Skip to content

feat(signals): Kronos forecast and crypto funding/OI, shadow-scored (#144) - #150

Merged
sudoTomas merged 4 commits into
mainfrom
feat/kronos-funding-signals
Nov 4, 2026
Merged

sudoTomas merged 4 commits into
mainfrom
feat/kronos-funding-signals

Conversation

@sudoTomas

Copy link
Copy Markdown
Owner

What changed

Two new Sources, both shadow signals: computed and journaled on every decide of a Bot that subscribes, scored against later bars, and kept out of the decide prompt unless a per-Bot toggle is on (off by default).

  • kronos (src/trader/data/kronos.py, KronosSource): sends the symbol's last 400 1h bars to a Kronos-small sidecar over HTTP. The sidecar runs each path as its own batch row with sample_count=1, so the N sampled paths stay separate instead of being averaged. The trader summarises them as share of paths up, plus median, p10 and p90 return TRADER_SIGNAL_HORIZON_BARS (24) bars ahead. The model code is pinned by commit and the weights by Hugging Face revision; the trader journals that revision with each forecast.
  • crypto_derivatives (src/trader/data/derivatives.py, CryptoDerivativesSource): BTC and ETH USDT-perp funding rate, open interest and its 24h change, and mark-vs-index basis from Binance, Bybit and OKX through ccxt public endpoints (no keys; ccxt added as a dependency). Fetched on each tick and never stored as market data. A venue that fails drops out of the reading. Replay reads nothing from it, because today's venue numbers are not point-in-time.
  • Journal: new column decisions.signals (migration 0019) holds {source: {version, as_of, in_prompt, reading}} for every reading, in the prompt or not. Replay steps record it too.
  • Toggle: set_signal_prompt(name, source, in_prompt) on services, API (POST /bots/{name}/signal-prompt), MCP and CLI (trader bot signal NAME kronos --in-prompt). Each flip is a new, audited Bot version. Bot Flip briefs into the decide lane (was EAI-192) #1 is refused: it follows Settings and subscribes journal-only with TRADER_SHADOW_SIGNALS_ENABLED.
  • Scoring: signal_scores (GET /signals/scores, MCP get_signal_scores, trader signal-scores) scores the journal against bars fetched for that call only:
    • Kronos band coverage (target 80%) and directional hit rate, per symbol and overall.
    • Rank IC of funding, OI change and basis against the forward return.
  • Sidecar: infra/kronos/{Dockerfile,server.py} plus a compose service under the kronos profile. It has mem_limit: 2g, a GPU reservation and a port bound to localhost only. Runbook: docs/runbooks/shadow-signals.md.
  • Docs: settings in ops/config.py (regenerated docs/configuration.md and .env.example), the "Shadow signal" glossary entry, and an architecture note.

Acceptance criteria

  • Sidecar memory stays under 2 GB: the compose service has a 2 GB hard cap, and GET /health reports peak_rss_mb. The runbook also says to check docker stats and nvidia-smi on the unified-memory box. This needs the operator to run it; CI has not run it.
  • Inference time per symbol logged: the sidecar prints one JSON kronos_forecast line per call with inference_ms. The trader logs kronos_forecast and journals reading.inference_ms.
  • A report after two weeks of shadow data: the scoring tool and the criteria are in the runbook. The report itself can only be written after two weeks of data have been collected.
  • Per-Bot toggle, off by default: tested (test_the_toggle_*).
  • Trader works with the sidecar down: tested (test_the_sidecar_down_drops_only_its_reading).

How to switch it on

  1. Build and run the sidecar on the GPU box: KRONOS_BASE_IMAGE=nvcr.io/nvidia/pytorch:<tag>-py3 docker compose -f infra/docker-compose.yml --profile kronos up -d --build kronos.
  2. Set TRADER_KRONOS_URL (http://kronos:8095 or http://localhost:8095).
  3. Set TRADER_SHADOW_SIGNALS_ENABLED=true for Bot Flip briefs into the decide lane (was EAI-192) #1, or subscribe a store-defined Bot to kronos / crypto_derivatives.
  4. After two weeks, run trader signal-scores --days 14. Only then consider trader bot signal <bot> kronos --in-prompt, through the decide-change gate.

With nothing set, this deploys with no change to any prompt or decision.

Tests

tests/test_shadow_signals.py: 22 tests covering the clients (HTTP and ccxt mocked), the Sources (point-in-time Kronos bars), context and journal, the Bot spec and toggle, scoring, and the toggle and scores through every surface. Full suite: 953 tests, ruff clean, config reference --check clean.

Closes #144

https://claude.ai/code/session_01UDNLNu854ULHbHSL59UJKu

#144)

Two new Sources, journaled on every decide and scored before any Bot reads them.

- kronos: the symbol's last 400 1h bars go to a Kronos-small sidecar
  (infra/kronos/, its own GPU container, TRADER_KRONOS_URL); N sampled paths
  come back un-averaged and the journal keeps share up, median, p10 and p90
  at TRADER_SIGNAL_HORIZON_BARS. Weights and model code pinned by revision.
  The trader decides as usual when the sidecar is down.
- crypto_derivatives: BTC/ETH perp funding, open interest and its 24h change,
  and mark-vs-index basis from Binance/Bybit/OKX via ccxt public endpoints.
  Fetch-on-tick; reads nothing in replay.
- decisions.signals (migration 0019) records every reading, in the prompt or
  not. A subscription reaches the prompt only with params.in_prompt (off by
  default); set_signal_prompt on services, API, MCP and CLI flips it as an
  audited Bot version. Bot #1 subscribes journal-only with
  TRADER_SHADOW_SIGNALS_ENABLED.
- signal_scores: Kronos band coverage and hit rate, funding/OI/basis rank IC
  against the forward return, bars fetched on use.
- Runbook docs/runbooks/shadow-signals.md, glossary, architecture, settings.

Claude-Session: https://claude.ai/code/session_01UDNLNu854ULHbHSL59UJKu
kronos and crypto_derivatives slices now carry an availability time: a
Kronos forecast is available once the last bar it saw has closed, a live
derivatives reading when it is fetched (replay still reads none). Both get
a look-ahead case in tests/test_point_in_time.py: Kronos with a bar planted
at the step, crypto_derivatives asserting a replay step reads nothing while
a live read serves it.

Claude-Session: https://claude.ai/code/session_01UDNLNu854ULHbHSL59UJKu
Both sidecars sit side by side in compose; SOURCE_NAMES, build_sources, the
prompt lanes and the point-in-time cases keep both sides.

Claude-Session: https://claude.ai/code/session_01UDNLNu854ULHbHSL59UJKu
@sudoTomas
sudoTomas merged commit ec810d3 into main Nov 4, 2026
1 check passed
@sudoTomas
sudoTomas deleted the feat/kronos-funding-signals branch November 4, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kronos forecast and crypto funding/OI, shadow-scored

1 participant