Strategy file: macdMomentumMM.js Market: spot
Description
MACD momentum spot market maker. Enters long when a MACD histogram bullish crossover occurs while price is below its 20-period SMA, then exits when the histogram crosses back above a configurable exit threshold.
How it works
The Moving Average Convergence-Divergence indicator (Appel 1979) is the difference between two EMAs: MACD = EMA_fast - EMA_slow, with a 'signal line' = EMA(MACD). Crossovers of MACD over its signal line are the canonical momentum-shift trigger. Brock, Lakonishok & LeBaron (1992) found significant predictive content in MACD-style dual-EMA rules over long equity samples, and Park & Irwin (2007) survey 95+ studies showing technical indicator profitability persists in early periods of inefficient markets — directly applicable to crypto. The strategy goes long on bullish MACD crossovers above zero with a slope confirmation and scales out on the opposite signal or a configurable take-profit. Best on trending pairs; degrades in chop.
Capital & Period
Order sizing and candle timeframe
| Key | Type | Default | Description |
|---|---|---|---|
PERIOD |
select | 1 |
Candle timeframe Gunbot feeds the strategy. 1–3 min suits highly liquid spot pairs (BTC, ETH). 5 min is a balanced default. 15 min reduces noise and trade frequency on lower-volume pairs. |
TRADING_LIMIT |
range (range 10..5000) | 50 |
Gunbot-level order-size cap in quote currency. Keep equal to the Per-order size below unless you have a specific reason to differ. Hard cap on a single buy order regardless of strategy logic. |
MACD_TL |
range (range 10..5000) | 50 |
Quote-currency notional placed on each entry order. Raise for larger lots (fewer trades, more fee per turn); lower for smaller lots (finer position building). |
MACD_CAPITAL_ALLOC |
range (range 100..50000) | 500 |
Per-pair capital cap in quote currency. The strategy treats min(balance, this) as 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 %. |
MACD_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. |
MACD_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. |
MACD_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). |
MACD Signal
MACD fast/slow/signal periods and histogram thresholds
| Key | Type | Default | Description |
|---|---|---|---|
MACD_FAST |
range (range 3..30) | 12 |
Period for the fast EMA in the MACD calculation. Shorter fast periods make the MACD line more sensitive to recent price changes, generating more signals. Classic default is 12. |
MACD_SLOW |
range (range 10..100) | 26 |
Period for the slow EMA in the MACD calculation. The difference between fast and slow EMAs forms the MACD line. Classic default is 26. Increasing this smooths the MACD trend; decreasing it makes it more reactive. |
MACD_SIGNAL_PERIOD |
range (range 3..30) | 9 |
Period for the EMA applied to the MACD line to produce the signal line. Histogram = MACD - signal. Shorter signal periods produce more frequent crossovers; longer periods filter more noise. Classic default is 9. |
MACD_MIN_DIP |
range (range 0..2) | 0.1 |
Minimum percentage by which price must be below its 20-period SMA for an entry signal to qualify. This secondary filter prevents MACD entries at or above the rolling average price, ensuring entries have at least a small dip context. |
MACD_EXIT_THRESH |
range (range -0.001..0.001) | 0 |
MACD histogram value above which an exit is allowed. 0 = exit when histogram turns positive (bullish momentum confirmed). Negative values exit earlier while histogram is still negative but recovering; positive values wait for stronger confirmation. |
Exits & DCA
Take-profit, time-stop and DCA layering
| Key | Type | Default | Description |
|---|---|---|---|
MACD_GAIN |
range (range 0.05..5) | 0.12 |
Minimum percentage gain above break-even (including fees) required before a sell is allowed. Acts as a take-profit floor — raise to insist on larger winners; lower to exit more readily on small recoveries. |
MACD_MAX_HOLD |
range (range 10..500) | 150 |
Maximum cycles a position may be held before a timed exit is attempted. Acts as a time-stop to prevent stale bags from blocking capital indefinitely. The exit is only fired when price is above break-even; otherwise it waits. |
MACD_MAX_DCA |
range (range 1..8) | 3 |
Maximum number of DCA (cost-averaging) buy layers allowed on a single position. Each layer is placed when price drops a further DCA_STEP from the previous entry. Caps maximum exposure on a declining position. |
MACD_DCA_STEP |
range (range 0.05..5) | 0.25 |
Percentage drop from the last entry price required to trigger the next DCA layer. Smaller values average down aggressively; larger values wait for a more substantial pullback before adding. |
MACD_DCA_DECAY |
range (range 0.3..1) | 0.75 |
Multiplier applied to order size at each successive DCA layer (e.g. 0.75 means each layer is 75% the size of the previous). Values below 1 pyramid down (smaller additions on deeper dips); use 1 for equal-size DCA layers. |
Risk & Safety
Exposure caps and drawdown circuit breakers
| Key | Type | Default | Description |
|---|---|---|---|
MACD_MAX_EXP |
range (range 20..100) | 80 |
Hard cap on total exposure as a percentage of allocated capital. Further buys are blocked once this threshold is reached. Lower for more cautious positioning; higher to allow heavier accumulation on dips. |
MACD_MAX_DD |
range (range 2..30) | 8 |
Peak-to-trough equity drop that triggers a strategy pause. When tripped, no new orders are placed until the condition clears or is manually reset. Set conservatively — this is a last-line safety stop. |
MACD_DAILY_LOSS |
range (range 1..20) | 3 |
Maximum percentage of allocated capital that may be lost in a calendar day before the strategy pauses. Resets at UTC midnight. Use to cap worst-case daily drawdown during adverse sessions. |
MACD_CONSEC_LOSS_MAX |
range (range 2..10) | 4 |
Number of consecutive losing trades that triggers a trading pause. Helps prevent the strategy from rapidly compounding losses during a streak of adverse signals. Pair reactivates after a configurable cooldown or manual reset. |
Fees
Fee tier and display settings
| Key | Type | Default | Description |
|---|---|---|---|
MACD_FEE |
range (range 0..0.5) | 0.1 |
Taker fee per side as a percentage. Used internally for break-even and profit-target calculations. Set to your actual exchange tier; defaults assume retail-tier taker on Bybit/Binance. |
MACD_SP |
range (range 0..8) | 4 |
Number of decimal places used to format prices in logs and the sidebar. Purely cosmetic — does not affect order rounding (Gunbot handles tick size). Use more decimals for low-priced pairs (SHIB, PEPE) and fewer for 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 internal model state and every decision gate; use only when investigating a problem. |
WARMUP_CYCLES |
range (range 0..30) | 5 |
How many cycles the strategy collects candle data before placing any orders. Indicators need a few cycles to stabilise; trading too early gives noisy values. Raise this on slower timeframes or after restarting a cold pair. 0 disables the warmup. |
MIN_ORDER_QUOTE |
range (range 1..25) | 5 |
Floor for order notional in quote currency. The runtime always uses max(this, exchange-minimum), so setting below the exchange minimum has no effect. Raise to enforce a larger per-trade size or to prevent dust-order rejections. |
BE_GUARD |
boolean | False |
When ON, blocks any sell priced below the position break-even while holding a long. Prevents stop-loss and TTL exit paths from realising a loss — the pair holds until price recovers. OFF = sells below break-even are permitted (standard stop-loss behaviour). Recommended ON for spot accumulation. |
BE_GUARD_BLOCK_MARKET_SELLS |
boolean | False |
When OFF, market sells and closeMarket bypass the break-even guard (soft mode), preventing BE_GUARD from silently swallowing urgent stop-loss exits. When ON (legacy), BE_GUARD blocks all sell types including market orders. |
NO_POST_ONLY |
boolean | False |
When ON, the strategy submits regular limit orders instead of post-only. Enable only if your exchange rejects post-only flags or you do not benefit from a maker rebate. The strategy auto-disables post-only on PancakeSwap and Aster regardless of this toggle. |
MACD_TRACE |
boolean | False |
Writes per-cycle JSONL trace to gunbot_logs/quantroduction/. Captures every gate decision, indicator value, order placement and fill event for forensic analysis. Zero overhead when OFF. |
VERBOSE_LOGS |
boolean | False |
When ON, the full Quantroduction dashboard (config snapshot + runtime state) re-emits every VERBOSE_INTERVAL_MIN minutes for audit. When OFF, 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 and noisier; higher = quieter. Has no effect when VERBOSE_LOGS is OFF. |
MACD_TEST_MODE |
boolean | False |
Loosens all entry gates for rapid signal generation on 1 m candles. Useful for verifying strategy logic in a paper-trade environment. Turn OFF for production — loose gates will fire far more frequently than intended. |
IS_MARGIN_STRAT |
boolean | False |
When ON, treats this pair as a futures contract: routes sells through closeMarket/closeLimit, rebinds balance fields to availableMargin/currentQty, and blocks new long entries while a short is open. Default OFF (spot). The strategy logic itself is identical — the adapter sits between the strategy and Gunbot's order methods. |
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 |
|---|---|---|---|
PORTFOLIO_INCLUDE |
boolean | True |
Whether this pair contributes to the shared portfolio exposure cap below. YES means a buy here is gated on Portfolio exposure cap. NO means this pair trades independently of the portfolio cap. |
PORTFOLIO_EXP_BUDGET |
range (range 0..1) | 0.8 |
Total portfolio exposure ceiling expressed as a fraction of equity (0.80 = 80%). When running portfolio exposure across every pair that opted in hits this number, no new buys are placed anywhere in the budget. Set to 0 to disable the portfolio gate. |
PAIR_EXP_BUDGET |
range (range 0..1) | 0.6 |
Per-pair exposure ceiling expressed as a fraction of equity (0.60 = 60%). Independent of portfolio cap. Set to 0 to disable. |
NO_CLOSE_MARKET |
boolean | False |
On exchanges that don't expose closeMarket reliably, set this to true to force the compat wrapper to route every close through sellMarket / buyMarket instead. |
CONSEC_RESET_CYCLES |
range (range 0..500) | 0 |
If the consecutive-loss circuit breaker is tripped, automatically clear it after this many cycles. 0 disables auto-reset (manual reset only). |
DISABLE_BREAKER_WINDDOWN |
boolean | False |
By default, when a breaker trips the strategy cancels open orders and tries to scratch out any small inventory. Set to true to disable this and just freeze in place until you reset manually. |
RESET_BREAKER_ONCE |
boolean | False |
Set to true once to clear any tripped circuit breaker (consecutive-loss / daily-loss). The strategy auto-clears this flag after the reset fires so you don't have to remove it. |
RESET_STATS_ONCE |
boolean | False |
Set to true once to zero out wins, losses, consecutive losses, peak equity, max drawdown, trade count, and daily-loss lock. Total PnL is preserved. The flag auto-clears after the reset fires. |
QUANTRODUCTION_TRACE |
boolean | False |
Master switch for the diagnostic tracer. When on, every gate decision and breaker event is recorded so you can see exactly why the strategy did or did not act this cycle. |
TRACE_ALL |
boolean | False |
When the tracer is on, this widens it to capture every event (placement, fill, gate). When off, only meaningful state changes are captured. |
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
- Appel, G. (1979). The Moving Average Convergence-Divergence Trading Method. Signalert Corp.
- Brock, W., Lakonishok, J. & LeBaron, B. (1992). Simple technical trading rules and the stochastic properties of stock returns. Journal of Finance 47(5), 1731–1764.
- Park, C.-H. & Irwin, S. H. (2007). What do we know about the profitability of technical analysis? Journal of Economic Surveys 21(4), 786–826.
Configuration playbook
- Full Tier 1-3 stack: see
OPERATOR_GUIDE.mdquick-start - Standalone pair (not in portfolio):
"PORTFOLIO_INCLUDE": false, "PAIR_EXP_BUDGET": 0.20 - Clear stuck breaker:
"RESET_BREAKER_ONCE": true - Clear historical loss counters:
"RESET_STATS_ONCE": true(preserves total PnL) - Enable JSONL tracer:
"TRACE_ALL": true - Soft BE_GUARD:
"BE_GUARD": true, "BE_GUARD_BLOCK_MARKET_SELLS": false