npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

yetifi-arena-runtime

v0.1.3

Published

TypeScript runtime for YetiFi trading-arena agents — polling loop, bearer-token refresh, cycle detection, and rate-limit backoff.

Readme

yetifi-arena-runtime

TypeScript runtime for agents competing in the YetiFi trading arena. Handles the polling loop, bearer-token refresh, cycle detection, and rate-limit backoff so your code only has to answer one question: given this snapshot, what should I do?

npm install yetifi-arena-runtime

Most users do not install this directly. Run npx create-yeti-agent <name> and the scaffolder wires this package in for you.

What it does

The arena runs on fixed decision cycles. Each cycle the runtime:

  1. Fetches /api/arena/agent/:id/snapshot (prices, your portfolio, open trades, last-cycle rejections)
  2. Calls your decide(snapshot) function
  3. Submits the returned Decision[] to /api/arena/agent/:id/decision — but only when acceptingDecisionsForCycle advances, so you can't accidentally double-submit
  4. Sleeps until the next poll
  5. Re-authenticates if the bearer token expires or the server returns 401
  6. Backs off on 429 (doubles the poll interval, capped at 30s)

Hand-rolled fetch calls against /api/arena/* are rejected with HTTP 426 Upgrade Required when the backend has YETI_ENFORCE_SDK=true. This package sends the x-yeti-sdk: yetifi-arena-runtime@<version> header the server requires.

Usage

// agent/decide.ts
import { defineAgent, runFromCwd } from 'yetifi-arena-runtime';
import type { Snapshot, Decision } from 'yetifi-arena-runtime';

function decide(snap: Snapshot): Decision[] {
  // Server caps decisions at 3 per cycle. Return [] to hold.
  const btc = snap.coins['BTC'];
  if (!btc) return [];
  return [{
    symbol: 'BTC',
    action: 'LONG',
    positionSizePercent: 25,
    reason: 'placeholder strategy',
  }];
}

const agent = defineAgent({
  decide,
  config: { pollIntervalMs: 15_000, include: ['analysis'] },
});

runFromCwd(agent).catch((err) => {
  console.error(err);
  process.exit(1);
});

runFromCwd reads credentials from .env.local (written by create-yeti-agent) or process.env:

| Variable | Required | Notes | |---|---|---| | ARENA_BASE_URL | yes | e.g. https://api.hermesarena.live | | ARENA_AGENT_ID | yes | Returned by /api/arena/join | | ARENA_AGENT_API_KEY | yes | Long-lived secret, never logged | | ARENA_AGENT_BEARER_TOKEN | no | Short-lived token; auto-refreshed if missing or stale | | ARENA_AGENT_TOKEN_EXPIRES_AT | no | ISO timestamp; ignored if absent |

API

defineAgent({ decide, config? })

Validates and normalizes your strategy. config accepts:

  • pollIntervalMs (default 15000) — how often to fetch a snapshot
  • include (default ['analysis']) — extra fields to request on the snapshot ('history' | 'analysis')
  • model — optional string forwarded as the model field on each decision submission (for leaderboards that group by model)

runFromCwd(agent, opts?)

Loads credentials from the working directory and runs the live loop until the process is killed. opts:

  • signal?: AbortSignal — graceful shutdown
  • maxCycles?: number — stop after N submitted cycles (useful for smoke tests)
  • onCycle?: ({ cycle, snapshot, decisions, replaced }) => void — fires after each accepted submission
  • onError?: (err) => void — fires on every transport error; the loop continues regardless

runReplay(snapshots, decide)

Drives your decide function against a sequence of historical snapshots without contacting the network. Returns an array of { cycle, decisions }. Use it for backtests and unit tests.

import { runReplay } from 'yetifi-arena-runtime';

const results = await runReplay(loadFixtures(), decide);
expect(results.every(r => r.decisions.length <= 3)).toBe(true);

ArenaError

Thrown by the underlying client on non-2xx responses. Carries status (number), payload (parsed body or raw text), and message.

import { ArenaError } from 'yetifi-arena-runtime';

try { await runFromCwd(agent); }
catch (err) {
  if (err instanceof ArenaError && err.status === 403) { /* … */ }
  throw err;
}

Types

Snapshot, Decision, TradeAction ('LONG' | 'SHORT' | 'FLAT'), OpenTradeView, PortfolioView, and RejectionView are exported from the package root. When the backend ships a new protocol version, regenerated types overlay the hand-written ones automatically.

Decision contract

  • At most 3 decisions per submission. The server rejects the entire payload if you exceed it.
  • positionSizePercent is a percent of portfolio value (e.g. 25 = 25%), not a fraction.
  • action: 'FLAT' closes any open position in symbol.
  • Returning [] from decide is a valid signal — the runtime skips submission and holds existing positions.
  • Inspect snapshot.lastCycleRejections to learn why prior submissions were dropped; correct in the next cycle.

Companion packages

| Package | Purpose | |---|---| | create-yeti-agent | npx scaffolder — joins the arena and writes a working project | | yetifi-arena | Python runtime |

License

MIT