Signal Reference
Webhook payloads, component params, and anchorPrice flow for Megadrive / test-bot.
Webhook Payload
POST to /api/webhook with Content-Type: application/json.
{
"regime": "CALM", // optional — updates stored regime
"anchorPrice": 67000, // optional — persisted to SQLite
"signal": "grid,fast_mm", // single or comma-separated
"exchange": "deribit",
"symbol": "BTC-PERPETUAL",
"name": "test-bot",
"params": {} // optional — overrides forwarded to component
}
Regime-only update (persists anchor, no component started):
{
"regime": "CALM",
"anchorPrice": 67000,
"exchange": "deribit",
"symbol": "BTC-PERPETUAL",
"name": "test-bot"
}
Signal Routing
| signal | dispatches | notes |
|---|---|---|
start_bot | all regime-gated components | Normal operating mode — respects activeInRegimes per component |
grid | Grid Maker | Cancel existing → buy ladder → sell ladder from position → flip-flop |
main_mm | Main Market Maker | Wide spread, larger allocation |
fast_mm | Fast Market Maker | Tight spread, active book |
slow_mm | Slow Market Maker | Wide spread, patient fills |
dip_buyer | Dip Buyer | Laddered trailing limit buys below anchor |
min_order | Min Order | Single permanent anchor bid |
hedge | Hedge (options) | Buys put/call protection |
tp / sell_ladder | Sell Ladder | Ratcheting partial TP ladder — price-triggered trailing sells |
threshold_tp | Threshold TP | Market sell X% of position when position > threshold |
ladder | Scaled Order | One-shot scaled entry — params forwarded directly |
min_size | Limit Order | Single limit order — params forwarded |
options_long_call | Options Open (call) | |
options_long_put | Options Open (put) |
Multi-Signal
The signal field accepts a comma-separated list. Each token is dispatched sequentially in order.
{
"regime": "CALM",
"signal": "grid,fast_mm,tp",
"exchange": "deribit",
"symbol": "BTC-PERPETUAL",
"name": "test-bot"
}
Anchor Price
When anchorPrice is present, store.setRegime() persists it to SQLite (bot_state table). It survives restarts.
| Component | Uses anchorPrice? | Behaviour |
|---|---|---|
| Dip Buyer / Ladder | Yes — primary | Anchor is the centre of the buy ladder. Set once on regime change; ladder rebuilds from stored value on restart. |
| Grid Maker | No | Always anchors to live mid at startup (self-centering). Pass explicit anchorPrice param to override. |
| Market Makers (main/fast/slow) | No | Centres on live mid/bid/ask at startup via ticker fetch. |
| Take Profit | No | Works off current position and mark price only. |
| Hedge | No | Uses live spot from ticker. |
anchorPrice, then fire the components. Or combine both in one payload — regime + signal in the same POST.
Grid Maker
Anchor-based startup cleanup + symmetric flip-flop. On each start:
- Cancels all existing orders on the symbol (clean slate)
- Places a buy ladder from
anchor − spacingdown toanchor × (1 − buyDepthPct) - Reads live position; places sell ladder above anchor based on
floor((position − minPosition) / perLevel), capped to buy level count - Enters flip-flop: buy fill → sell pong; sell fill → buy pong; pong fill → new ping
| Param | Default | Description |
|---|---|---|
anchorPrice | 0 | 0 = use live mid at startup (self-centering) |
buyDepthPct | 4 | % below anchor for bottom of buy ladder |
spacingPct | 0.1 | % between levels |
allocation | 10 | Total USD across all buy levels (snapped to $10 increments) |
pongMinOffset | 0.05% | Min distance from fill price to pong (% or absolute) |
minPosition | 0 | Floor — sell pongs skipped if at/below this. Redundant if reduce_only is active (it is). |
sellInLoss | true | Allow sell pongs below avg entry price |
cancelOnFillPause | 0 | ms to wait after buy fill before placing sell pong (adverse-move guard) |
driftThresholdPct | 0 | Auto-reprice when mid moves this % from anchor. 0 = disabled. Example: 3 = reprice if price drifts >3%. |
repriceCooldownMins | 60 | Minimum minutes between automatic reprices |
Market Makers (main / fast / slow)
Ping-pong order book. Places a scaled buy ladder, watches fills, places sell pongs above each filled buy. Sell pong fills → new buy ping.
| Param | Default | Description |
|---|---|---|
allocation | — | Contracts per side (regime + options multipliers applied) |
spread | — | Total price width of the order book (% or abs) |
orderCount | — | Orders per side |
pongMinOffset | — | Min distance from fill to pong (% or abs) |
pongMaxOffset | — | Max distance — trailing pong trails between min and max |
minPosition | 0 | Floor long contracts — no buys placed when position is at or below this |
maxPosition | 0 | Ceiling long contracts — suppresses new buy pings when position ≥ this (0 = no ceiling) |
sellInLoss | true | Allow sell pongs below avg entry price |
inventorySkewFactor | 0 | Skew bid/ask sizes towards inventory balance. 0 = symmetric; 0.5 = moderate skew. |
targetPosition | 0 | Desired inventory level (USD notional). Skew leans towards this target. |
baseVolatility | 0 | Baseline annualised vol for spread scaling. 0 = static spread. |
fundingSkewFactor | 0 | Adjusts quotes in response to funding rate. Positive = lean short when funding high. |
maxQuoteLifetime | 0 | Cancel and repost orders older than this (e.g. "4h"). 0 = never expire. |
cancelOnFillPause | 0 | Cancel all pings for N seconds after a fill (cool-off). 0 = disabled. |
driftThresholdPct | 0 | % price drift from anchor before auto-reprice. 0 = disabled. |
repriceCooldownMins | 60 | Min minutes between auto-reprices. |
Dip Buyer
Places a ladder of trailing limit buy orders below current price, each spaced by toOffset. Uses stored anchorPrice as the ladder centre. Each fill is independent — no pong placed.
Sell Ladder tp / sell_ladder
Ratcheting partial TP monitor. Watches position and mark price; places scaled trailing-limit sell orders when price crosses TP thresholds. Resets on new position entry.
Threshold TP threshold_tp
Position-size watcher. When position exceeds threshold, sells sellPct% at market. No price triggers — just size-based trimming. Use when the grid has accumulated too large a position and you want to scale back automatically.
| Param | Default | Description |
|---|---|---|
threshold | 200 | Position size (USD contracts) that triggers a sell |
sellPct | 25 | % of total position to sell per trigger |
minPosition | 0 | Floor — never sell below this |
cooldownMins | 60 | Minutes between consecutive sells |
checkInterval | 60 | Seconds between position polls |
tag | threshold_tp | Order tag prefix |
Min Order
Single permanent anchor bid — a standing buy at a fixed level that refreshes after each fill. Ensures a baseline position is always being rebuilt even when all other components are idle.
Hedge (Options)
Opens an options position (put or call) sized as a % of equity. Strength 1–3 controls strike selection (ATM → OTM). Uses live spot from ticker unless overridden.
Regime States
| Regime | Description | Typical components |
|---|---|---|
CALM | Low vol, range-bound | All components active |
TRANSITION | Breakout or breakdown forming | Grid + fast MM, TP active |
STRESS | Elevated vol, directional move | Min order + TP only |
BLACK | Extreme event / flash crash | Hedge only or all stopped |
AFTERMATH | Post-event recovery | Dip buyer + grid to rebuild |
Each component has an activeInRegimes array in config. When start_bot fires, only components whose regimes include the current stored regime are started.
Vol Classifier Config
Automatic regime detection from realised vol + funding + drawdown. Set enabled: true to activate. See Runbooks → Regime Classifier for operational detail.
| Key | Default | Description |
|---|---|---|
enabled | false | Enable automatic regime detection |
symbol | BTC-PERPETUAL | Instrument to fetch candles for |
candleResolution | "60" | Candle size in minutes |
candleCount | 48 | Candles fetched per poll |
overridePriority | false | If true, classifier can override manually set regime |
deescalateAfterPolls | 3 | Consecutive polls below threshold before downgrading |
thresholds.calm.maxVol | 0.60 | Max annualised vol for CALM |
thresholds.transition.maxVol | 1.00 | Max annualised vol for TRANSITION |
thresholds.stress.maxVol | 1.50 | Max annualised vol for STRESS |
blackVol | 1.50 | Vol threshold for BLACK (must also meet blackDrawdown) |
blackDrawdown | 0.15 | Drawdown threshold for BLACK (15% from recent HWM) |
extremeFundingRate | 0.0004 | 8h rate that escalates to STRESS regardless of vol |
Options Multipliers
A second layer of buy/sell multipliers that stack on top of regime multipliers when an options position is open.
"optionsMultipliers": {
"put": { "buyMultiplier": 1.2, "sellMultiplier": 0.8 },
"call": { "buyMultiplier": 0.8, "sellMultiplier": 1.2 },
"none": { "buyMultiplier": 1.0, "sellMultiplier": 1.0 }
}
Effective multiplier = regime_mult × options_mult. A zero from the regime side is never un-gated.
Convexity Metrics Reference
Computed from portfolio_daily (90-day window, refreshed every 15 min). Carry is taken from the carry_cost column when present, otherwise from fees + funding.
| Metric | Target | Formula | Description |
|---|---|---|---|
RCG | >3.0 | PnL / carry | Realized Convex Gains — how many times over you earned your carry costs |
UTR | <0.30 | days below HWM / days | Underwater Time Ratio — fraction of days equity was below all-time high |
CDI | <0.05 | carry / (avgEquity × days) | Carry Drag Index — daily carry as fraction of equity |
CPC | >0.50 | top-20% days / all positive days | Convex Payoff Concentration — how concentrated the gains are in the best days |
CPS | >3.0 | RCG × (1−UTR) × e−CDI | Composite Performance Score — blended rank. Primary sort key. |
Health Index | >0.70 | weighted blend | 0–1 summary. ≥0.70 HIB · ≥0.50 MIB · <0.50 LIB |
Component Sharpe: annualised Sharpe per bot tag prefix. Zero-padded calendar days. formula: mean / std × √365.
Adverse Selection Score: −mean(signed post-fill return at horizon). Positive = being picked off. On-demand, requires Deribit candle fetch.
Backtest CLI
Run historical simulations from the command line against Deribit candle data.
node src/backtest/cli.js --strategy grid --days 30 --alloc 1000 --pong 0.2 node src/backtest/cli.js --strategy grid --days 60 --grid pong=0.1:0.2:0.3,spacing=0.05:0.1:0.2
Global flags
| Flag | Default | Description |
|---|---|---|
--symbol | BTC-PERPETUAL | Instrument |
--resolution | 60 | Candle resolution in minutes |
--days | 30 | Days of history to fetch |
--balance | 1.0 | Starting balance in BTC |
--strategy | dip | dip or grid |
--grid | — | Parameter sweep: param=v1:v2:v3,param2=v1:v2 |
--regime | — | Path to regime CSV with columns ts,regime |
--testnet | off | Use testnet candle data |
--verbose | off | Print progress every 100 candles |
Dip strategy flags --strategy dip
| Flag | Default | Description |
|---|---|---|
--dip | 0.005 | Buy at N% below candle close |
--tp | 0.010 | Take profit at N% above close |
--size | 10 | Order size in USD |
Grid strategy flags --strategy grid
| Flag | Default | Description |
|---|---|---|
--anchor | 0 | Anchor price — 0 = first candle close |
--depth | 4 | Buy ladder depth in % |
--spacing | 0.1 | % spacing between levels |
--alloc | 1000 | Total USD allocation across all buy levels |
--pong | 0.1 | Ping-pong spread: % above buy fill for sell pong (below sell fill for buy pong) |
Output metrics
| Metric | Description |
|---|---|
RCG | Realized Convex Gains — realized PnL relative to carry cost. Higher is better. |
CPS | Composite Performance Score — blended rank across all metrics. Primary sort key. |
UTR | Underwater Ratio — fraction of candles where equity was below the high-water mark. Lower is better. |
CDI | Carry Drag Index — implied carry cost relative to gains. Lower is better. Note: options premium not modelled. |
CPC | Convex Payoff Concentration — how concentrated gains are in the best candles. Higher signals convexity. |
API Endpoints
| Method | Path | Description |
|---|---|---|
POST | /api/webhook | Main signal entry point |
GET | /api/health | Process health — uptime, regime, activeBots, dbOk, stagingMode |
GET | /api/state | Full state snapshot (JSON) |
GET | /api/bot-config | Read bot config from local.json |
POST | /api/bot-config | Write bot config to local.json |
POST | /api/restart | Restart process via pm2 |
GET | /api/consistency-check | Check for orphaned orders |
POST | /api/cancel-bot-orders | Cancel all open orders for a bot |
GET | /api/metrics?days=N | Convexity metrics from portfolio_daily (also triggers WS push) |
GET | /api/regime-changes?limit=N | Regime change log with vol, funding, outcomePct |
GET | /api/fills-heatmap?days&bucket&tag | Fill count/volume bucketed by price level, per side |
GET | /api/component-sharpe?days=N | Annualised Sharpe per bot component (tag prefix) |
GET | /api/adverse-selection?days&horizonHours&symbol | Adverse selection score — requires Deribit candle fetch (~1s) |
GET | /api/equity-history?hours=N | Equity snapshots for chart zoom (max 720h) |