Praeger Reuchlin
Signal Reference

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

signaldispatchesnotes
start_botall regime-gated componentsNormal operating mode — respects activeInRegimes per component
gridGrid MakerCancel existing → buy ladder → sell ladder from position → flip-flop
main_mmMain Market MakerWide spread, larger allocation
fast_mmFast Market MakerTight spread, active book
slow_mmSlow Market MakerWide spread, patient fills
dip_buyerDip BuyerLaddered trailing limit buys below anchor
min_orderMin OrderSingle permanent anchor bid
hedgeHedge (options)Buys put/call protection
tp / sell_ladderSell LadderRatcheting partial TP ladder — price-triggered trailing sells
threshold_tpThreshold TPMarket sell X% of position when position > threshold
ladderScaled OrderOne-shot scaled entry — params forwarded directly
min_sizeLimit OrderSingle limit order — params forwarded
options_long_callOptions Open (call)
options_long_putOptions 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.

ComponentUses 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.
On every new regime: fire a regime-only payload first to update 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:

ParamDefaultDescription
anchorPrice00 = use live mid at startup (self-centering)
buyDepthPct4% below anchor for bottom of buy ladder
spacingPct0.1% between levels
allocation10Total USD across all buy levels (snapped to $10 increments)
pongMinOffset0.05%Min distance from fill price to pong (% or absolute)
minPosition0Floor — sell pongs skipped if at/below this. Redundant if reduce_only is active (it is).
sellInLosstrueAllow sell pongs below avg entry price
cancelOnFillPause0ms to wait after buy fill before placing sell pong (adverse-move guard)
driftThresholdPct0Auto-reprice when mid moves this % from anchor. 0 = disabled. Example: 3 = reprice if price drifts >3%.
repriceCooldownMins60Minimum 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.

ParamDefaultDescription
allocationContracts per side (regime + options multipliers applied)
spreadTotal price width of the order book (% or abs)
orderCountOrders per side
pongMinOffsetMin distance from fill to pong (% or abs)
pongMaxOffsetMax distance — trailing pong trails between min and max
minPosition0Floor long contracts — no buys placed when position is at or below this
maxPosition0Ceiling long contracts — suppresses new buy pings when position ≥ this (0 = no ceiling)
sellInLosstrueAllow sell pongs below avg entry price
inventorySkewFactor0Skew bid/ask sizes towards inventory balance. 0 = symmetric; 0.5 = moderate skew.
targetPosition0Desired inventory level (USD notional). Skew leans towards this target.
baseVolatility0Baseline annualised vol for spread scaling. 0 = static spread.
fundingSkewFactor0Adjusts quotes in response to funding rate. Positive = lean short when funding high.
maxQuoteLifetime0Cancel and repost orders older than this (e.g. "4h"). 0 = never expire.
cancelOnFillPause0Cancel all pings for N seconds after a fill (cool-off). 0 = disabled.
driftThresholdPct0% price drift from anchor before auto-reprice. 0 = disabled.
repriceCooldownMins60Min 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.

ParamDefaultDescription
threshold200Position size (USD contracts) that triggers a sell
sellPct25% of total position to sell per trigger
minPosition0Floor — never sell below this
cooldownMins60Minutes between consecutive sells
checkInterval60Seconds between position polls
tagthreshold_tpOrder 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

RegimeDescriptionTypical components
CALMLow vol, range-boundAll components active
TRANSITIONBreakout or breakdown formingGrid + fast MM, TP active
STRESSElevated vol, directional moveMin order + TP only
BLACKExtreme event / flash crashHedge only or all stopped
AFTERMATHPost-event recoveryDip 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.

KeyDefaultDescription
enabledfalseEnable automatic regime detection
symbolBTC-PERPETUALInstrument to fetch candles for
candleResolution"60"Candle size in minutes
candleCount48Candles fetched per poll
overridePriorityfalseIf true, classifier can override manually set regime
deescalateAfterPolls3Consecutive polls below threshold before downgrading
thresholds.calm.maxVol0.60Max annualised vol for CALM
thresholds.transition.maxVol1.00Max annualised vol for TRANSITION
thresholds.stress.maxVol1.50Max annualised vol for STRESS
blackVol1.50Vol threshold for BLACK (must also meet blackDrawdown)
blackDrawdown0.15Drawdown threshold for BLACK (15% from recent HWM)
extremeFundingRate0.00048h 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.

MetricTargetFormulaDescription
RCG>3.0PnL / carryRealized Convex Gains — how many times over you earned your carry costs
UTR<0.30days below HWM / daysUnderwater Time Ratio — fraction of days equity was below all-time high
CDI<0.05carry / (avgEquity × days)Carry Drag Index — daily carry as fraction of equity
CPC>0.50top-20% days / all positive daysConvex Payoff Concentration — how concentrated the gains are in the best days
CPS>3.0RCG × (1−UTR) × e−CDIComposite Performance Score — blended rank. Primary sort key.
Health Index>0.70weighted blend0–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

FlagDefaultDescription
--symbolBTC-PERPETUALInstrument
--resolution60Candle resolution in minutes
--days30Days of history to fetch
--balance1.0Starting balance in BTC
--strategydipdip or grid
--gridParameter sweep: param=v1:v2:v3,param2=v1:v2
--regimePath to regime CSV with columns ts,regime
--testnetoffUse testnet candle data
--verboseoffPrint progress every 100 candles

Dip strategy flags --strategy dip

FlagDefaultDescription
--dip0.005Buy at N% below candle close
--tp0.010Take profit at N% above close
--size10Order size in USD

Grid strategy flags --strategy grid

FlagDefaultDescription
--anchor0Anchor price — 0 = first candle close
--depth4Buy ladder depth in %
--spacing0.1% spacing between levels
--alloc1000Total USD allocation across all buy levels
--pong0.1Ping-pong spread: % above buy fill for sell pong (below sell fill for buy pong)

Output metrics

MetricDescription
RCGRealized Convex Gains — realized PnL relative to carry cost. Higher is better.
CPSComposite Performance Score — blended rank across all metrics. Primary sort key.
UTRUnderwater Ratio — fraction of candles where equity was below the high-water mark. Lower is better.
CDICarry Drag Index — implied carry cost relative to gains. Lower is better. Note: options premium not modelled.
CPCConvex Payoff Concentration — how concentrated gains are in the best candles. Higher signals convexity.
RCG and CPS do not include options premium. CDI is understated when running a combined grid + options strategy — treat backtest metrics as directional signal, not absolute.

API Endpoints

MethodPathDescription
POST/api/webhookMain signal entry point
GET/api/healthProcess health — uptime, regime, activeBots, dbOk, stagingMode
GET/api/stateFull state snapshot (JSON)
GET/api/bot-configRead bot config from local.json
POST/api/bot-configWrite bot config to local.json
POST/api/restartRestart process via pm2
GET/api/consistency-checkCheck for orphaned orders
POST/api/cancel-bot-ordersCancel all open orders for a bot
GET/api/metrics?days=NConvexity metrics from portfolio_daily (also triggers WS push)
GET/api/regime-changes?limit=NRegime change log with vol, funding, outcomePct
GET/api/fills-heatmap?days&bucket&tagFill count/volume bucketed by price level, per side
GET/api/component-sharpe?days=NAnnualised Sharpe per bot component (tag prefix)
GET/api/adverse-selection?days&horizonHours&symbolAdverse selection score — requires Deribit candle fetch (~1s)
GET/api/equity-history?hours=NEquity snapshots for chart zoom (max 720h)
Praeger Reuchlin
Building dreams