183 lines
6.4 KiB
Markdown
183 lines
6.4 KiB
Markdown
# 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](./STRATEGY_DEV_GUIDE.md)
|
||
|
||
---
|
||
|
||
## 1. Purpose
|
||
|
||
The platform runs **many different strategies**. Without a single contract, teams see:
|
||
|
||
- Different meanings of `buy`/`sell` across scripts;
|
||
- Backtests filling on **closed bar → next open** while live trades on **forming bars + immediate exits**;
|
||
- Indicator exits plus `# @strategy trailingEnabled` → duplicate closes and `invalid 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
|
||
|
||
1. **MUST** start mutations with `df = df.copy()`.
|
||
2. **MUST** define `output` (`name`, `plots`; optional `signals` for markers only).
|
||
3. **MUST** execution columns: length `len(df)`, dtype bool after `fillna(False).astype(bool)`.
|
||
4. **MUST** edge-trigger execution columns (§4.3) unless documented otherwise.
|
||
5. **MUST NOT** use `shift(-1)` or any look-ahead.
|
||
6. **MUST NOT** mix form A and form B execution columns as fill drivers (may keep `buy`/`sell` all-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:
|
||
|
||
```python
|
||
# --- 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
|
||
- `confirmed` modes 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 |
|