← Strategy index

Strategy file: inventoryBalancer.js Market: spot

Description

Avellaneda-Stoikov / GLT infinite-horizon spot rebalancer. Continuously rebalances toward a 50/50 base/quote split using the A-S reservation price and optimal spread. Spot.

How it works

The Avellaneda-Stoikov (2008) model derives optimal MM quotes by maximising the expected exponential utility of terminal wealth subject to an inventory penalty. The reservation price is r(s,q,t) = s - q·γ·σ²·(T-t) where s = mid, q = current inventory, γ = risk aversion, σ = volatility. The optimal half-spread is δ ≈ (γ·σ²·(T-t))/2 + (1/γ)·ln(1 + γ/k) where k governs the arrival rate of market orders against the quote. The strategy uses the infinite-horizon GLT (Guéant-Lehalle-Tapia 2013) variant which drops the dependence on terminal time T, yielding a stationary quoting rule that continuously rebalances inventory toward a target split. This is the workhorse model of academic MM literature.

Capital & Period

Order sizing and candle timeframe

Key Type Default Description
PERIOD select 5 Candle timeframe Gunbot feeds the strategy. 1-3 min for very liquid spot (BTC, ETH). 5 min is a balanced default. 15 min reduces noise and trade frequency on lower-volume pairs.
TRADING_LIMIT range (range 100..5000) 900 Gunbot-level order size cap in quote currency. The strategy also reads its own Per-order limit below; keep these two equal unless you know why they should differ. Hard cap on a single buy order from Gunbot core regardless of strategy logic.
IB_TL range (range 100..5000) 900 Quote-currency notional placed on each rebalance order. Raise for larger lots (fewer trades, more fee per turn); lower for smaller lots (more trades, finer rebalancing).
IB_CAPITAL_ALLOC range (range 500..50000) 9090 Per-pair capital cap in quote currency. The strategy treats min(baseBalance, this) as the working capital, so values above your actual quote balance are ignored. Set to the realistic per-pair allocation you want this strategy to manage.

Allocation

How much of THIS pair’s wallet the strategy may use, what it must start with, and the hard config gate. Trading is BLOCKED until Wallet allocation % is set AND Allocation confirmed is ON. Optional grid splits an order into laddered post-only rungs for better fills.

Key Type Default Description
ALLOC_PCT range (range 1..100) 25 Percentage of THIS pair’s wallet balance the strategy may deploy. Effective cap = min(wallet × this %, the absolute Capital allocation). Every order is clamped to it and can never exceed it. REQUIRED — trading is blocked until this is set (>0) and Allocation confirmed is ON.
ALLOC_CONFIRMED boolean False Explicit acknowledgement that the allocation above is correct for this pair. The strategy will NOT place any entry order until this is ON. A clear "ALLOCATION CONFIGURED" line is logged once it is; otherwise an "ALLOCATION NOT CONFIGURED — refusing to trade" line is logged each cycle.
ALLOC_SPLIT_TOL range (range 0..100) 15 How far the starting base/quote split may deviate from what this strategy needs before it refuses to start. Long-only strategies want mostly quote/cash; two-sided market-makers want ~50/50 base/quote; futures want free margin. Mismatch beyond this % blocks trading (unless Enforce start inventory is OFF).
ALLOC_ENFORCE_SPLIT boolean True When ON, the strategy refuses to trade until the starting base/quote split is within tolerance of what it needs (archetype-aware). When OFF, a mismatch is logged as a warning but trading proceeds.
ALLOC_AUTO_REBALANCE boolean True Two-sided market-makers only. ON by default: if the pair starts off the target base/quote split, the strategy places a single bounded market order on start to reach it (e.g. buys ~half the allocation into base for a 50/50 maker), then begins normal trading. Set to OFF to instead stay blocked and log the exact amount to buy/sell manually. Long-only and futures strategies ignore this.
ALLOC_RESERVE_PCT range (range 0..50) 0 A buffer, as % of wallet, kept BELOW the allocation cap and never deployed. Use to leave headroom for fees/slippage. Effective cap = min(wallet × allocation %, absolute cap) − wallet × this %.
IB_GRID boolean False When ON, an entry order is split into several laddered post-only rungs across a price band instead of one order — often gets better average fills. Cumulative size is still clamped to the allocation. OFF = single order.
IB_GRID_LEVELS range (range 2..10) 3 Number of laddered rungs to split an entry into when "Split entries into a grid" is ON. More rungs = finer fills but more orders.
IB_GRID_SPAN_PCT range (range 0.1..5) 0.5 Price band width, as % from the reference price, across which the grid rungs are spread. e.g. 0.5 places rungs from the reference price down to 0.5% below it (for buys).

A-S Model

Avellaneda-Stoikov / GLT parameters

Key Type Default Description
IB_GAMMA range (range 200..3000) 1000 Risk-aversion coefficient in the A-S reservation/spread formulas (return-space convention). Higher γ = wider quoted spread AND a stronger pull on the reservation price when inventory is non-neutral, so the strategy leans harder against accumulating inventory. Lower γ = tighter quotes and smaller inventory lean. Tune in tandem with κ.
IB_KAPPA range (range 500..8000) 3000 Order-arrival decay parameter from A-S. Larger κ → tighter floor on the constant-spread component (≈ 2/κ in return space, scaled by mid). Decrease κ to widen quotes uniformly across volatility regimes; increase κ to compress to a minimum spread. Default 3000 ≈ 6 bps floor.
IB_TAU range (range 0.25..4) 1 Horizon scaling factor. τ=1.0 corresponds to the steady-state GLT (infinite-horizon) interpretation used by this implementation. Scale up to amplify both spread components; scale down to compress. Co-tunes with γ; doubling τ at fixed γ has the same first-order effect as doubling γ.
IB_VOL_LB range (range 10..100) 30 Number of candles used by the Parkinson high-low volatility estimator. Longer lookback = smoother σ but slower reaction to regime shifts. 30 candles ≈ 30-150 minutes depending on PERIOD. Raise on very noisy pairs.
IB_EWMA range (range 0.85..0.99) 0.94 EWMA decay applied on top of the Parkinson estimate. Lower λ = faster adaptation (more weight on the latest reading); higher λ = smoother, more persistent σ. 0.94 ≈ 17-candle half-life (RiskMetrics-style).
IB_MIN_SP range (range 2..30) 8 Informational floor for the sidebar "captured spread" metric. Does NOT clamp actual quote prices — the quoting math runs as configured regardless. Used only to highlight in the GUI when the computed spread falls below this value.

Rebalance & Exits

When to trade and exit rules

Key Type Default Description
IB_REBAL range (range 3..20) 8 Strategy only acts when |exposure - 50%| exceeds this. 8% means trades fire when the quote-asset weight is below 42% or above 58%. Lower = more frequent rebalances and more fees; higher = lazier rebalancing with larger inventory excursions between trades.
IB_SIZE range (range 1..15) 4 Fraction of total equity put through each rebalance trade. 4% = roughly 12 trades to flatten a fully one-sided book. Higher = faster correction but more market impact; lower = finer increments.
IB_GAIN range (range 0..2) 0.15 Take-profit floor for the rebalance sell side: a sell only fires once mid is at least this percentage above the position break-even. Prevents scratching at a loss; set to 0 to disable this gate (then only IB_REBAL governs sells).
IB_MAX_DCA range (range 1..15) 6 Maximum number of consecutive buy rebalances allowed without an intervening sell. Caps how deep the strategy can average down when price keeps falling. Lower = stricter inventory cap; higher = more averaging-down headroom on persistent dips.
IB_COMPOUND range (range 0..100) 30 Percentage of realised profit that is added back to the working capital (per-trade size grows over time). 0 = harvest everything to cash, no compounding. 100 = reinvest all PnL.

Risk & Safety

Exposure caps and drawdown circuit breakers

Key Type Default Description
IB_MAX_EXP range (range 30..95) 70 Hard cap on quote-asset weight: further buys are blocked above this exposure. Caps how much of your capital ends up as the held asset. Lower for more cautious positioning; higher to allow heavier accumulation on dips.
IB_MAX_DD range (range 2..30) 12 Peak-to-trough equity drop that triggers a strategy pause. When tripped, no new orders are placed until equity recovers (no auto-reset on time). Set conservatively — this is a last-line safety stop.

Fees

Fee tier (informational, for P&L bookkeeping)

Key Type Default Description
IB_FEE range (range 0..0.2) 0.1 Taker fee for your exchange tier, as a percentage. Used in P&L bookkeeping inside the strategy and to size break-even targets. Get the exact number from your exchange fee schedule for your VIP/maker tier; defaults assume retail tier on Bybit/Binance.
IB_MAKER_FEE range (range -0.05..0.1) 0.02 Maker fee for your tier, as a percentage. Set to your actual fee; if you get a rebate, use the Maker rebate variant instead.
IB_SP range (range 2..8) 4 Number of decimal places used to format prices in logs and the sidebar. Purely display — does not affect order rounding (Gunbot handles tick size automatically). Set higher for low-priced pairs (SHIB, etc.) and lower for high-priced pairs (BTC, ETH).

Runtime & Exchange

Logging, warmup, exchange safety toggles

Key Type Default Description
LOG_LEVEL select NORMAL Controls how much the strategy writes to Gunbot logs. SILENT keeps only errors and circuit-breaker messages. NORMAL writes one summary line per cycle plus order events — recommended for live trading. DEBUG adds the internal model state and every decision; use only when investigating a problem.
WARMUP_CYCLES range (range 0..30) 5 How many cycles the strategy collects data before placing any orders. Indicators (ATR, vol, etc.) need a few cycles to stabilise; trading too early gives noisy values. Raise this on slower timeframes (15m candles or longer) or after restarting an empty pair. 0 disables the warmup.
MIN_ORDER_QUOTE range (range 1..25) 5 Floor for order notional in quote currency (typically USDT). The runtime always uses max(this, exchange-minimum), so setting this lower than the exchange minimum has no effect. Raise it if dust orders are getting rejected or you want to enforce a larger per-trade size.
BE_GUARD boolean True When ON, blocks any sell or position-close priced below the position break-even while holding a long. Prevents stop-loss, scratch, max-drawdown and cap-reduce paths from realising a loss — the pair will hold the position until price recovers above break-even. Off = sells below break-even are permitted (standard stop-loss behaviour). Recommended ON for spot accumulation; consider OFF on highly leveraged futures where forced closes are needed.
NO_POST_ONLY boolean False When ON, the strategy uses regular limit orders instead of post-only. Turn this on only if your exchange does not support post-only flags or rejects them, or if you do not benefit from a maker rebate. The strategy auto-applies this on PancakeSwap and Aster regardless of this toggle. OFF (default) is correct for every standard CEX.
IB_TRACE boolean False When ON, writes a structured JSONL audit trail to gunbot_logs/quantroduction/-.jsonl AND echoes every gate, compute, quote, order and fill decision to the main console log. Useful for verifying literature fidelity, debugging mis-fills and post-hoc analysis of strategy behaviour. Default OFF (zero overhead).
VERBOSE_LOGS boolean False When ON, the full Quantroduction × Gunbot dashboard (every config value + state snapshot) re-emits every VERBOSE_INTERVAL_MIN minutes for forensic audit. When OFF (default), the dashboard only fires once per Gunbot restart per pair.
VERBOSE_INTERVAL_MIN range (range 5..240) 30 How often the dashboard re-emits when VERBOSE_LOGS is ON. Lower = more frequent / noisier; higher = quieter. Has no effect when VERBOSE_LOGS is OFF.
BE_GUARD_BLOCK_MARKET_SELLS boolean True Default ON (legacy). Set OFF to enable SOFT BE_GUARD: market sells and closeMarket pass through (treated as urgent / stop-loss). Only limit sells below break-even remain blocked. Prevents BE_GUARD from silently swallowing stop-loss exits.
SCRATCH_LIVENESS_MIN range (range 0..120) 0 When >0: if the pair holds inventory and hasn't filled in N minutes AND the bid is at break-even+1bp, force a market exit to rotate capital. 0 = disabled. Typical: 30.
CONSEC_RESET_CYCLES range (range 60..2880) 480 After this many cycles without any order activity, auto-reset consecutiveLosses to 0. Default 480 cycles ≈ 2h at 15s/cycle. Prevents a 3-loss streak from killing the pair for the entire session.
DRIFT_ATR_FRAC range (range 0..0.5) 0 When >0: scale drift-requote threshold to this fraction of the recent 10-candle high-low range (clamped 2-100 bps). 0 = use fixed *_STALE_DRIFT_BPS. Typical: 0.05 = 5% of recent range. Adapts drift detection to per-pair volatility.
SKEW_QTY_MAX range (range 1..5) 2.5 Maximum ask:bid qty ratio when inventory is heavily skewed. 2.5 means a heavily-bagged pair quotes up to 2.5x ask qty vs bid qty to drain inventory faster. Set 1.0 to disable (symmetric quoting).
SKEW_QTY_TARGET range (range 0.1..0.9) 0.5 Target inventory fraction of pair equity. 0.5 = 50/50 balanced base/quote. Skew kicks in proportionally as actual exposure deviates from target.
PORTFOLIO_INCLUDE boolean True When ON (default), this pair participates in the shared PORTFOLIO_EXP_BUDGET — its exposure counts toward portfolio total and bids pause when budget is exceeded. When OFF, the pair stands alone (use PAIR_EXP_BUDGET for own cap).
PORTFOLIO_EXP_BUDGET range (range 0..1) 0 Cap on total portfolio inventory as fraction of total allocated equity (sum across PORTFOLIO_INCLUDE pairs). 0 = disabled. Typical: 0.6 = 60% inventory cap across the included portfolio. When exceeded, bids pause but exits remain active.
PAIR_EXP_BUDGET range (range 0..1) 0 This pair's own exposure cap as fraction of pair equity (inventory / pair allocated capital). Applies independently of portfolio budget. 0 = disabled. Typical: 0.2 = 20% per-pair cap.
DISABLE_BREAKER_WINDDOWN boolean False When OFF (default), an active breaker (3 consecutive losses or daily loss limit) actively frees capital: cancels open orders and scratch-sells inventory IF profitable (bid >= BE+1bp). When ON, legacy halt-and-hold behaviour.
TRACE_ALL boolean False Master switch for the Quantroduction tracer. When ON, writes detailed per-cycle JSONL to gunbot_logs/quantroduction/ AND emits a verbose console summary. Equivalent to setting every _TRACE to true. Useful for diagnosing silent gates and breakers. Disk overhead ~1MB/day/pair.
IB_STALE_CYCLES range (range 1..30) 3 Cancel and re-quote resting orders after this many cycles to avoid stale quotes.
IB_CROSS_PROT_BPS range (range 0..10) 1 Buffer in bps to keep quotes outside the touch so post-only orders are not rejected.
IB_DISABLE_CROSS_PROT boolean False Disable the cross-protection clamp (advanced).

Safety & Tuning (v1.0-beta)

Universal safety + tuning knobs added in v1.0-beta. These were previously hidden from the chart page even though the strategy reads them. Setting any of these here will be applied per-pair.

Key Type Default Description
IB_PERIOD string 1 Candle period in minutes used for this strategy's indicators. Common values: '1', '5', '15'. Some strategies need a long history to warm up indicators.
STRICT_LITERATURE boolean False Master switch. When ON, the strategy disables every operator-grade safety override (BE_GUARD, cross-protection floors, BE ask clamps, drift-ATR scaling, asymmetric sizing, scratch liveness, breaker wind-down) so it runs as faithful as we can make it to the cited academic paper. Use this for paper-comparison backtests or academic research. WARNING: in strict mode the strategy can take real losses (papers prove optimality only in expectation, not per-trade).
NO_CLOSE_MARKET toggle False When true, the strategy NEVER falls back to a market sell to close a held inventory — only post-only limits. Use to prevent any taker fee on exits. Default false (allow market close when necessary).
QUANTRODUCTION_TRACE toggle False When true, this single pair writes detailed cycle events to gunbot_logs/quantroduction/-.jsonl. Use TRACE_ALL to enable across all pairs at once; this knob overrides per-pair. Default false.
RESET_BREAKER_ONCE toggle False One-shot: set true and Gunbot will clear all tripped risk breakers (consec_losses, daily_loss, max_dd) on this pair on the next cycle, then re-arm them. The setting auto-clears itself after firing. Use when a breaker tripped on a now-stale condition.
RESET_STATS_ONCE toggle False One-shot: set true and Gunbot will clear customStratStore counters (wins, losses, totalPnL, peakEquity, maxDD, dailyPnL, dailyLossLimit) on this pair on the next cycle. The setting auto-clears itself after firing. Useful after a recovery / regime change.

Laddered Quoting

Spread each side of the quote into a stack of post-only rungs instead of one full-size order.

Key Type Default Description
IB_LAYERS range (range 1..8) 4 Number of laddered orders to place on each side of the book per cycle. 1 = single quote at the top. 4 = stack of four rungs walking deeper into the book. Higher numbers improve scale-in / scale-out but require larger MIN_ORDER_QUOTE per rung.
IB_LAYER_SPAN_BPS range (range 0..50) 10 How far the deepest rung sits below the top bid (or above the top ask), in basis points. The rungs are spaced linearly across this band.

Portfolio & Runtime (per-pair overrides)

Per-pair runtime knobs. Override any of these on a single pair without changing the strategy defaults.

Key Type Default Description
SPREAD_PNL_JUMP_GUARD range (range 0.1..1) 0.5 Realized-PnL deltas larger than this fraction of pair equity are treated as deposits/withdrawals/data-glitches and skipped. Default 0.5 means a single delta over 50% of equity is ignored. Lower for tighter glitch detection on small accounts.

References & further reading

Configuration playbook