Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/deniszidbaev-cmyk/polyclaw-trading/llms.txt

Use this file to discover all available pages before exploring further.

Every pipeline stage communicates exclusively through versioned JSON files persisted under runtime/. No stage may read from memory that a previous stage wrote, and no stage may skip validation of its upstream input. This section describes the envelope format, every validation rule, freshness constants, atomic write mechanics, and the complete stage-to-file mapping.

JSON envelope

Every runtime file that crosses a stage boundary uses this envelope:
{
  "schema_version": 1,
  "stage": "signal",
  "generated_at": "2026-01-01T12:00:00Z",
  "data": {}
}
FieldTypeDescription
schema_versionintegerMust be exactly 1. Any other value raises ContractError.
stagestringThe name of the stage that produced this file (e.g., "fetch", "signal").
generated_atstringUTC ISO 8601 timestamp with Z suffix, rounded to the nearest second.
dataobjectStage-specific payload; validated by each stage’s own validator function.

Validation rules (fail-closed)

The contract validator in src/polyclaw_engine/contracts.py applies the following checks in order. Any failure raises ContractError and prevents the stage from continuing.
RuleWhat triggers it
Invalid JSONFile cannot be parsed as JSON.
Duplicate keysThe same key appears more than once in a JSON object.
Non-finite numbersAny NaN, Infinity, or -Infinity value anywhere in data.
Wrong schema_versionValue is anything other than 1.
Missing envelope fieldsAny of schema_version, stage, generated_at, or data is absent.
Wrong stagestage does not match the value expected by the reading stage.
Malformed timestampgenerated_at is not a valid ISO 8601 string with a timezone offset.
Future timestampgenerated_at is more than 60 seconds in the future.
StalenessAge of generated_at exceeds the reading stage’s max_age_sec threshold.
Bad price boundsAny Polymarket probability price is outside [0.0, 1.0].
Bad OHLC geometrylow > high, or open/close outside [low, high].
Unsupported timeframestimeframes object does not contain exactly "5m" and "15m".
Non-monotonic timestampsCandle or price-history timestamps are not strictly increasing.
Non-string object keysAny JSON object contains a non-string key.
Unsupported typesdata tree contains a value type other than bool, None, str, int, float, list, or dict.
Validation is fail-closed by design. There is no coercion, repair, or fallback for malformed, stale, or semantically invalid data. The pipeline halts and exits with code 2 on the first ContractError.

Freshness thresholds

Each reading stage enforces a maximum age on its upstream contract file. These constants are configurable via environment variables but the defaults are:
ConstantEnv varDefaultApplied by
MAX_DECISION_AGE_SECMAX_DECISION_AGE_SEC600 svalidate, execute, report reading decisions
MAX_MARKET_AGE_SECMAX_MARKET_AGE_SEC600 scandles reading market snapshot
MAX_CANDLE_AGE_SECMAX_CANDLE_AGE_SEC1200 sanalyze reading candles; signal reading analysis
MAX_BANKROLL_AGE_SECMAX_BANKROLL_AGE_SEC600 svalidate reading bankroll state

Atomic writes

Every runtime file is written atomically to prevent partial reads by the next stage. The sequence is:
  1. Create a unique temporary sibling in the same directory, named .{filename}.{pid}.{uuid4hex}.tmp, opened with exclusive creation (x mode).
  2. Write the serialized JSON, call handle.flush(), then os.fsync(handle.fileno()).
  3. Call os.replace(temporary, destination) — atomic on POSIX; atomic on Windows (NTFS).
  4. In a finally block, attempt to unlink the temporary file if os.replace did not complete.
JSON is serialized with indent=2, sort_keys=True, ensure_ascii=False, and allow_nan=False.

Stage-to-file mapping

The table below shows every runtime file that each stage reads and writes. All paths are relative to RUNTIME_DIR (default: runtime/).
StageReadsWrites
fetch(none; optionally reads --fixture for offline use)market_snapshot.json
candlesmarket_snapshot.json (stage: fetch, max age: MAX_MARKET_AGE_SEC)candles.json
analyzecandles.json (stage: candles, max age: MAX_CANDLE_AGE_SEC)market_analysis.json
signalmarket_analysis.json (stage: analyze, max age: MAX_CANDLE_AGE_SEC)decisions.json, manual_review.json
bankrolltrade_registry.json (optional)bankroll_state.json, risk_state.json, trade_registry.json (if initialized from default)
validatedecisions.json (stage: signal, max age: MAX_DECISION_AGE_SEC), bankroll_state.json (stage: bankroll, max age: MAX_BANKROLL_AGE_SEC), risk_state.json (stage: bankroll, max age: MAX_BANKROLL_AGE_SEC), trade_registry.json (stage: bankroll or execute)decisions.json (updated, stage: validate), manual_review.json
executedecisions.json (stage: validate, max age: MAX_DECISION_AGE_SEC), bankroll_state.json (stage: bankroll), risk_state.json (stage: bankroll), trade_registry.json (stage: bankroll or execute)executions/YYYY-MM-DD.json (append), trade_registry.json, execution_status.json, manual_review.json (on failure)
reportdecisions.json (stage: validate, max age: MAX_DECISION_AGE_SEC), bankroll_state.json (stage: bankroll), risk_state.json (stage: bankroll), execution_status.json (stage: execute)runtime_snapshot.json (terminal output also printed to stdout)

Special runtime files

Beyond the main pipeline files, one additional file carries non-executable HOLD records across stages:

runtime/manual_review.json

Written (and overwritten) by the signal, validate, and execute stages. Contains all HOLD decisions that are not executable. Each item includes:
{
  "schema_version": 1,
  "stage": "signal",
  "generated_at": "2026-01-01T12:00:00Z",
  "data": {
    "items": [
      {
        "market_id": "...",
        "question": "Will BTC exceed $100k?",
        "signal": "HOLD",
        "candidate_signal": "YES",
        "confidence": 0.63,
        "blocking_reasons": ["candle quality below automatic execution threshold"],
        "suggested_action": "Wait for clean EMA, RSI, candle, quality, and timeframe alignment, then rerun the cycle.",
        "executable": false
      }
    ]
  }
}
All records in this file must have signal = "HOLD" and executable = false. Any other values cause ContractError. manual_review.json is also overwritten by the validate stage (after risk sizing) and, when execution failures or blocked decisions occur, by the execute stage. The stage field in the envelope will reflect the most recent writer (signal, validate, or execute).

Data payload schemas

Each stage’s data object is validated by a dedicated validator in contracts.py. Key per-stage requirements are: market_snapshot.json (data.markets) — each market must have market_id (unique, non-empty), question, end_date (ISO timestamp), boolean state flags (active, closed, resolved, accepting_orders), yes_price/no_price in [0, 1], non-negative liquidity_usd/volume_24h_usd, non-empty yes_token_id/no_token_id, a valid semantic_mapping object, and a non-empty price_history list with strictly increasing integer timestamps. candles.json (data.markets) — each market must have exactly "5m" and "15m" keys in timeframes. Each candle within a timeframe must have timestamp (UTC bucket-aligned to the interval), valid OHLC geometry, a positive integer sample_count, and a boolean complete flag. Candle timestamps must be strictly monotonic. market_analysis.json (data.markets) — each market’s timeframe entry must have a direction of "bullish", "bearish", or "neutral", a metrics object, a patterns object, and a candle_quality object with a score in [0, 1]. Each market must also have a market_quality object with a score in [0, 1]. decisions.json (data.decisions) — each decision must have a unique decision_id, unique market_id, signal of "YES", "NO", or "HOLD", confidence in [0, 1], a risk_validation object with a boolean approved, and a blocking_reasons list of non-empty strings. For executable (YES/NO) decisions, entry_price must be strictly inside (0, 1), and approved decisions must also have positive amount_usd, max_entry_price, and valid planned_exits with stop_price < entry_price < take_profit_price. bankroll_state.json — must have equity_usd, working_bankroll_usd, reserve_usd, free_balance_usd, open_exposure_usd, drawdown_pct, risk_mode ("normal", "reduced", or "critical"), and risk_multiplier. All numeric fields must be finite and non-negative. risk_state.json — must have day_key, limits, remaining, risk_mode, and risk_multiplier. Both limits and remaining must be objects with non-negative numeric values; remaining.trades must be an integer.
The execute stage re-reads fresh bankroll, risk, registry, expiry, semantic mapping, pattern, quality, and cooldown state immediately before launching the PolyClaw subprocess. A stale or missing upstream file at execution time causes a ContractError abort, not a silent skip.

Build docs developers (and LLMs) love