6.4 KiB
QuantDinger Signal & Execution Standard (SSOT)
Version: 1.0
Status: Current
Scope: All IndicatorStrategy flows (indicator Python saved as strategies) for backtest and live
Implementation: BacktestService, TradingExecutor, validate_code_safety / verifyCode
Guide: STRATEGY_DEV_GUIDE.md
1. Purpose
The platform runs many different strategies. Without a single contract, teams see:
- Different meanings of
buy/sellacross scripts; - Backtests filling on closed bar → next open while live trades on forming bars + immediate exits;
- Indicator exits plus
# @strategy trailingEnabled→ duplicate closes andinvalid amount (0.0)rejects.
This document is the single source of truth for what authors write and how engines interpret it.
New strategies SHOULD comply; existing ones SHOULD migrate per §9.
2. Terms
| Term | Meaning |
|---|---|
| Signal bar | Bar where a boolean flag becomes true (timestamp = that bar’s close) |
| Fill bar | Bar where execution is anchored (default: bar after signal bar) |
| Edge trigger | true only on false→true transition vs previous bar |
| Repaint | Conditions on the forming bar flip as price updates |
| Exit owner | One primary close path: indicator signals or engine risk, not both narrow rules |
3. Choose a strategy form first
| Form | Use when | Avoid when |
|---|---|---|
A. Two-way buy / sell |
Simple crossovers; symmetric reversal | Separate tp/sl per side, state machines, “close only” semantics |
B. Four-way open_long / close_long / open_short / close_short |
State machines, touch exits, explicit queue alignment | — (recommended default for new strategies) |
C. ScriptStrategy on_bar + ctx |
Position-aware bots, scaling, cooldowns | Pure vectorized signals on full df |
Platform default for new listings: Form B (four-way).
4. Signal output rules (IndicatorStrategy)
4.1 Universal MUST
- MUST start mutations with
df = df.copy(). - MUST define
output(name,plots; optionalsignalsfor markers only). - MUST execution columns: length
len(df), dtype bool afterfillna(False).astype(bool). - MUST edge-trigger execution columns (§4.3) unless documented otherwise.
- MUST NOT use
shift(-1)or any look-ahead. - MUST NOT mix form A and form B execution columns as fill drivers (may keep
buy/sellall-false for legacy UI).
4.2 Form A: buy / sell
tradeDirection |
buy=True |
sell=True |
|---|---|---|
long |
open long | close long |
short |
close short | open short |
both |
open long; close short first if short | open short; close long first if long |
MUST NOT treat buy as “close short only” under both. Use form B close_* for flat-only exits.
4.3 Form B: Four-way (recommended)
| Column | Meaning |
|---|---|
open_long |
Open / add long |
close_long |
Close long |
open_short |
Open / add short |
close_short |
Close short |
MUST: all four columns exist as bool.
SHOULD: same-bar priority close before open; avoid simultaneous open_long and open_short on one bar.
Fills follow explicit four-way semantics (not buy/sell both-mode remapping).
4.4 tradeDirection
long / short / both filters which legs are active; it does not replace four-way columns.
5. Exit ownership
Each strategy MUST declare one owner in comments or docs:
| Mode | Script | # @strategy |
|---|---|---|
Indicator exits exit_owner: indicator |
close_* or equivalent explicit signal exits |
trailingEnabled false; do not rely on SL/TP/trailing |
Engine exits exit_owner: engine |
entries only, or structural reverse close_* signals |
SL/TP/trailing as needed |
MUST NOT combine tight indicator tp/sl with tight trailingEnabled on the same leg.
The current backend supports only exit_owner: indicator and exit_owner: engine. Do not emit exit_owner: layered; a mixed “indicator exits + engine backup” mode needs an explicit product/runtime change first.
6. Execution contract (backtest ↔ live)
6.1 Signal timing
| Setting | Standard |
|---|---|
signal_mode |
confirmed |
exit_signal_mode |
confirmed |
Forming-bar evaluation is for display/research only by default.
6.2 Fill timing
Default: next_bar_open (signal on bar t, fill on bar t+1 open ± slippage).
6.3 Same-bar ordering
Priority: close_* > reduce_* > open_* > add_*.
Document flip mode R1 (close on bar t, open on t+1, recommended) or R2 (same-bar flip).
6.4 Close sizing
Sync positions → resolve size from DB and exchange → retry once if zero → fail with log if still zero.
7. Configuration
Use # @strategy for defaults; leverage and credentials stay in product UI.
Optional header block:
# --- QuantDinger execution contract (v1) ---
# signal_form: four_way
# exit_owner: indicator
# flip_mode: R1
# @strategy tradeDirection both
# @strategy trailingEnabled false
8. Release checklist
- Form A/B/C chosen and documented
- Exit owner declared
confirmedmodes for live- Backtest fills reviewed (signal bar vs fill bar)
- Pilot live run without systematic zero-amount closes
9. Migration tiers
P0: both + merged tp/sl in buy/sell + trailing → fix first.
P1: Touch logic + large live/backtest drift → four-way + confirmed + edge.
P2: Simple two-way → keep A, add contract header.
Optional badge: “Contract v1 certified”.
10. Implementation map
| Rule | Code |
|---|---|
| Four-way | TradingExecutor._execute_indicator_with_prices, BacktestService |
| Two-way both | _indicator_both_mode only for buy/sell normalization |
| Confirmed bars | signal_mode, exit_signal_mode |
| Close retry | PendingOrderWorker, resolve_reduce_only_quantity |
| Sandbox | safe_exec.py |
11. Revision history
| Version | Date | Notes |
|---|---|---|
| 1.0 | 2026-05 | Initial standard |