d698544f30
CI/CD / build-and-push (push) Successful in 2m21s
Adds run_legacy_scan() that executes once at startup before the trading loop:
1. Re-keys every open DB position using the current market_family_key()
2. Groups by new family key; KEEP = highest edge_net, CLOSE_RECOMMENDED = sibling
3. Manifold re-query for positions whose family key changed; if corrected
probability contradicts the trade direction → CLOSE_RECOMMENDED
4. Logs full report (KEEP / REVIEW / CLOSE_RECOMMENDED) before any closures
5. In paper mode: auto-closes all CLOSE_RECOMMENDED positions
For the existing Ohio bug:
- Democrats win Ohio governor (629557): CLOSE_RECOMMENDED
family changed ohio-democrat-2026 → ohio-gubernatorial-2026
Manifold re-query confirms prob=0.05 contradicts BUY_YES (inversion bug)
$X returned to cash at break-even
- Republicans win Ohio governor (629558): KEEP
higher edge_net (0.349 > 0.247)
Infrastructure:
- schema.sql: closed_at TIMESTAMPTZ, close_reason TEXT on trades
- db.py: all open-position queries filter WHERE closed_at IS NULL
+ close_paper_position(market_id, reason)
- paper.py: close_legacy_position(market_id, reason) → float
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
6.1 KiB
SQL
111 lines
6.1 KiB
SQL
-- Polymarket Bot Database Schema
|
|
|
|
CREATE TABLE IF NOT EXISTS trades (
|
|
id TEXT PRIMARY KEY,
|
|
market_id TEXT NOT NULL,
|
|
question TEXT NOT NULL,
|
|
direction TEXT NOT NULL, -- BUY_YES | BUY_NO
|
|
size_usdc DOUBLE PRECISION,
|
|
entry_price DOUBLE PRECISION,
|
|
shares DOUBLE PRECISION,
|
|
fee_usdc DOUBLE PRECISION,
|
|
net_cost DOUBLE PRECISION,
|
|
timestamp TIMESTAMPTZ NOT NULL,
|
|
reasoning TEXT,
|
|
paper BOOLEAN DEFAULT TRUE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS metrics_daily (
|
|
id SERIAL PRIMARY KEY,
|
|
timestamp TIMESTAMPTZ NOT NULL,
|
|
total_trades INTEGER,
|
|
total_deployed DOUBLE PRECISION,
|
|
total_fees DOUBLE PRECISION,
|
|
total_pnl DOUBLE PRECISION,
|
|
win_rate DOUBLE PRECISION,
|
|
avg_edge DOUBLE PRECISION,
|
|
sharpe_ratio DOUBLE PRECISION,
|
|
calibration_score DOUBLE PRECISION,
|
|
paper_mode BOOLEAN DEFAULT TRUE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS markets (
|
|
id TEXT PRIMARY KEY,
|
|
condition_id TEXT,
|
|
question TEXT NOT NULL,
|
|
category TEXT,
|
|
end_date TEXT,
|
|
active BOOLEAN DEFAULT TRUE,
|
|
last_seen TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS signals (
|
|
id SERIAL PRIMARY KEY,
|
|
market_id TEXT NOT NULL,
|
|
timestamp TIMESTAMPTZ NOT NULL,
|
|
polymarket_price DOUBLE PRECISION,
|
|
estimated_prob DOUBLE PRECISION,
|
|
edge DOUBLE PRECISION,
|
|
confidence DOUBLE PRECISION,
|
|
direction TEXT,
|
|
acted_on BOOLEAN DEFAULT FALSE
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_trades_timestamp ON trades(timestamp DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_trades_market ON trades(market_id);
|
|
CREATE INDEX IF NOT EXISTS idx_metrics_timestamp ON metrics_daily(timestamp DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_signals_timestamp ON signals(timestamp DESC);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
-- Phase 1 migrations: edge neto real
|
|
--
|
|
-- spread_estimate and commission are HEURISTICS, not exact Polymarket exchange
|
|
-- costs. spread_estimate ≈ estimated half-spread for medium-liquidity markets.
|
|
-- commission = COMMISSION_RATE (0.02) * size_usdc — mirrors Polymarket taker fee.
|
|
-- edge_net = edge_gross - spread_estimate - commission/size_usdc
|
|
-- = edge_gross - 0.02 - 0.02 (always 0.04 deduction at current rates)
|
|
--
|
|
-- These are stored per-trade so we can audit whether the model's cost assumptions
|
|
-- were met in practice once markets resolve.
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS edge_gross DOUBLE PRECISION;
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS edge_net DOUBLE PRECISION;
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS prior_prob DOUBLE PRECISION;
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS final_prob DOUBLE PRECISION;
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS mid_price DOUBLE PRECISION;
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS spread_estimate DOUBLE PRECISION;
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS commission DOUBLE PRECISION;
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS family_key TEXT;
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
-- Phase 2 / Phase 5 migrations: market families + observability
|
|
--
|
|
-- Signals table extended so each evaluated market carries its audit trail:
|
|
-- skip_reason — why the market was not traded ("edge_net", "family",
|
|
-- "gnews_priority", "regime", "prior_extreme", etc.)
|
|
-- passed_gross — True if edge_gross alone met regime_min_edge
|
|
-- passed_net — True if edge_net met regime_min_edge (the actual gate)
|
|
-- family_key — market family slug (e.g. "texas-republican-2026")
|
|
-- regime_min_edge — threshold that applied to this market/category
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
ALTER TABLE signals ADD COLUMN IF NOT EXISTS edge_gross DOUBLE PRECISION;
|
|
ALTER TABLE signals ADD COLUMN IF NOT EXISTS edge_net DOUBLE PRECISION;
|
|
ALTER TABLE signals ADD COLUMN IF NOT EXISTS family_key TEXT;
|
|
ALTER TABLE signals ADD COLUMN IF NOT EXISTS regime_min_edge DOUBLE PRECISION;
|
|
ALTER TABLE signals ADD COLUMN IF NOT EXISTS skip_reason TEXT;
|
|
ALTER TABLE signals ADD COLUMN IF NOT EXISTS passed_gross BOOLEAN;
|
|
ALTER TABLE signals ADD COLUMN IF NOT EXISTS passed_net BOOLEAN;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_signals_market ON signals(market_id);
|
|
CREATE INDEX IF NOT EXISTS idx_trades_family ON trades(family_key);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
-- Position lifecycle: legacy scan can close erroneous paper positions.
|
|
-- closed_at IS NULL → position is open (all open-position queries filter this).
|
|
-- closed_at NOT NULL → position closed; close_reason explains why.
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS closed_at TIMESTAMPTZ;
|
|
ALTER TABLE trades ADD COLUMN IF NOT EXISTS close_reason TEXT;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_trades_closed ON trades(closed_at) WHERE closed_at IS NOT NULL;
|