Home

/

Library

/

simulator.md

Simulator (dry‑run execution)

Simulator (dry‑run execution)

What it is

The Simulator is a dry‑run execution engine that listens to live opportunity signals, runs the same safety checks you would use for real trading, and records “virtual” trades in the database. It never places live orders, but it can call real exchange APIs for validation (orderbook, market status, balance checks) to mimic live behavior as closely as possible.

The Simulator is designed for:

  • validating end‑to‑end execution logic
  • running “paper trading” loops for hours/days
  • tracking profitability and timing assumptions before going live

High‑level flow

  1. Opportunity signals are produced by the ingest loop (e.g., Time Alignment Approx).
  2. Each signal emits an execution intent when net edge ≥ the user’s threshold.
  3. The Simulator consumes each intent and applies safety checks.
  4. If all checks pass, it records a simulated trade with fake execution + settlement timing.
  5. Trades, balance changes, and P&L are persisted for review.

What drives it

Inputs

  • Execution intents generated from app/data/ingest.py
  • User profile thresholds from user_profiles (min edge, max leg, daily cap)
  • Exchange secrets from user_secrets (encrypted)

Primary logic

  • Simulator core: app/orders/simulator.py
  • Orchestration and persistence: app/orders/engine.py
  • Exchange validation: app/orders/validators.py
  • API endpoints: app/api/http.py

Safety checks (simulated)

For every intent, the following checks run:

  • Has at least one order
  • Net edge ≥ user minimum
  • Instrument IDs present (Polymarket token ID + Kalshi market ticker)
  • Exchange keys present
  • Exchange validation via live orderbooks (dry‑run)
  • Sufficient balance vs. starting fake USD
  • Daily cap not exceeded

Exchange validation (dry‑run)

The simulator can call real exchange APIs for validation only:

  • Kalshi: uses kalshi_python_sync client; validates market status and balance
  • Polymarket: uses py-clob-client; validates orderbook, size, and balance

If those checks fail, the trade is skipped and logged with details.

Fake execution + settlement

When a simulated trade passes validation:

  • executed_at is set to “now”
  • settled_at is set to now + a small delay
  • Partial fills are simulated for marginal edges

These timestamps are recorded so the system can be used to analyze timing and queue behavior.

Simulated trade states

UI trade status is derived from timestamps:

  • queued: current time is before executed_at
  • executed: after executed_at but before settled_at
  • settled: after settled_at

Trades also track:

  • requested_size, matched_size, fill_pct
  • settlement_delay_s

Persistence

Two tables capture all simulator data:

simulator_runs

  • One row per simulator session
  • Stores thresholds, balances, and status

simulator_trades

  • One row per simulated trade
  • Stores checks, validation details, and timing data

Migrations:

  • migrations/014_create_simulator_tables.sql
  • migrations/015_add_simulator_trade_fields.sql
  • migrations/016_add_simulator_run_labels.sql
  • migrations/017_add_simulator_run_filters.sql
  • migrations/018_add_simulator_trade_decisions.sql

API endpoints

All endpoints require an authenticated user.

  • POST /simulator/start

    • Start a simulator run
    • Payload: { starting_usd, opportunity_key, opportunity_keys, duration_minutes, label, notes }
  • POST /simulator/stop

    • Stop the current run (or pass run_id to stop a specific simulator)
  • GET /simulator/status

    • Current simulator status + recent trades
  • GET /simulator/runs

    • History of simulator runs
  • GET /simulator/runs/{run_id}/trades

    • Trades for a specific run
  • GET /simulator/trades/{trade_id}

    • Full details for one simulated trade
  • GET /simulator/active

    • Active simulators for the current user
  • GET /simulator/report?hours=24&max_gap_s=120&min_edge_cents=3

    • 24h comparison of simulator trades vs. Success spans
  • Socket events:

    • simulator:trade emits when a simulated trade is recorded
  • GET /execution/intents/{intent_id}

    • Raw intent + any linked trade

Required configuration

User profile

Set via Profile UI (or DB):

  • min_net_edge_cents
  • max_leg_usd
  • daily_cap_usd

Exchange secrets

Saved per user (encrypted at rest):

Kalshi

  • api_key (required)
  • extra.private_key or extra.private_key_path (required)
  • Optional: extra.use_demo, extra.host

Polymarket

  • extra.private_key (required)
  • Optional: extra.funder, extra.host

UI

Simulator controls live on the /simulator page:

  • Start / Stop controls
  • Current status (balance, pnl, trade count)
  • Trade list with detail modal
  • Recent run history

Multiple simulators + duration

  • You can run up to 5 simulators per user simultaneously.
  • Each simulator can target a single opportunity key or a list of keys.
  • If duration_minutes is 0 or omitted, the simulator runs until stopped, with a hard 7‑day max.

Success vs execution intent decisions

  • Every execution intent logs a simulator decision (decision_source = intent).
  • Every success span start logs a simulator decision (decision_source = success_span), even if no trade is taken. This shows which opportunities were considered and why they were skipped or eligible.

Labeling runs

You can attach a short label and optional notes when starting a simulator run. These are stored on the run and shown in the run history list.

What it is not (yet)

  • No live orders
  • No execution re‑tries
  • No smart routing
  • No automated liquidity management

Those will be layered in after we confirm safe, predictable behavior under dry‑run conditions.