Compare commits

...

2 Commits

Author SHA1 Message Date
75c95f7935 feat: add triple-Supertrend consensus flip as strategy family 7
Adds awareness of the multi-Supertrend any_of flip pattern (based on
the reference strategy at swym/assets/reference/supertrend-triple.json,
itself a DSL port of the popular TradingView triple-Supertrend script).

- prompts.rs: add strategy family 7 (Supertrend consensus flip) with
  guidance on any_of vs all_of, period/multiplier tuning, and the
  always-in-market / reverse-as-stop-loss trade-off
- prompts.rs: add risk management exception for always-in-market flip
  strategies (reverse: true means the opposite signal is the stop)
- prompts.rs: add Example 7 — correctly gated 2-rule triple-Supertrend
  flip with position state guards to prevent unintended scale-ins

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:40:15 +02:00
6601da21cc feat: add reverse flag and symmetric short support to DSL
Update scout's schema and system prompt to reflect two upstream swym
changes from 2026-03-10:

- b535207: symmetric short quantity fix — buy-to-cover now correctly
  uses position_qty (executor was broken; scout's DSL patterns were
  already correct and will now work as intended)

- 6f58949: reverse flag on Action — new optional "reverse": true field
  that submits position_qty + configured_qty when an opposite position
  is open, closing it and opening a new one in the opposite direction
  in a single order (flip-through-zero)

Changes:
- dsl-schema.json: add "reverse" boolean to Action definition
- prompts.rs: add "Reverse / flip-through-zero" capability section
  and Example 6 (2-rule EMA flip strategy) to FUTURES_SHORT_EXAMPLES

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:28:54 +02:00
2 changed files with 155 additions and 1 deletions

View File

@@ -74,6 +74,11 @@
{ "$ref": "#/definitions/SizingFixedUnits" }, { "$ref": "#/definitions/SizingFixedUnits" },
{ "$ref": "#/definitions/Expr" } { "$ref": "#/definitions/Expr" }
] ]
},
"reverse": {
"type": "boolean",
"default": false,
"description": "Flip-through-zero flag (futures only). When true and an opposite position is currently open, the submitted order quantity becomes position_qty + configured_qty, closing the existing position and immediately opening a new one in the opposite direction in a single order. When flat the flag has no effect and configured_qty is used as normal. Omit or set false for standard close-only behaviour."
} }
} }
}, },

View File

@@ -121,6 +121,24 @@ CRITICAL — the `"method"` vs `"kind"` distinction:
`multiplier` field. Only `amount` is accepted alongside `method`. `multiplier` field. Only `amount` is accepted alongside `method`.
- NEVER add extra fields to SizingMethod objects — they use `additionalProperties: false`. - NEVER add extra fields to SizingMethod objects — they use `additionalProperties: false`.
### Reverse / flip-through-zero (futures only)
Setting `"reverse": true` on a rule action enables a single-order position flip on futures.
When an opposite position is open, quantity = `position_qty + configured_qty`, which closes
the existing position and opens a new one in the opposite direction in one order (fees split
proportionally). When flat the flag has no effect — `configured_qty` is used normally.
This lets you collapse a 4-rule long+short strategy (separate open/close for each leg) into
2 rules, reducing round-trip fees and keeping logic compact:
```json
{{"side": "sell", "quantity": {{"method": "percent_of_balance", "percent": "10", "asset": "usdc"}}, "reverse": true}}
```
Use `reverse` when you always want to be in a position — the signal flips you from long to
short (or vice versa) rather than first exiting and then re-entering separately. Do NOT use
`reverse` on spot markets (short selling is not supported there).
### Multi-timeframe ### Multi-timeframe
Any expression can reference a different timeframe via "timeframe" field. Any expression can reference a different timeframe via "timeframe" field.
Use higher timeframes as trend filters, lower timeframes for entry precision. Use higher timeframes as trend filters, lower timeframes for entry precision.
@@ -145,6 +163,13 @@ Use higher timeframes as trend filters, lower timeframes for entry precision.
6. **Composite / hybrid**: Combine families. Trend filter + mean-reversion entry. 6. **Composite / hybrid**: Combine families. Trend filter + mean-reversion entry.
Momentum confirmation + volatility sizing. Momentum confirmation + volatility sizing.
7. **Supertrend consensus flip (futures only)**: Use `any_of` across multiple
Supertrend configs (e.g. period=7/mul=1.5, period=10/mul=2.0, period=20/mul=3.0)
so that ANY flip triggers a long or short entry. Combine with `"reverse": true`
for an always-in-market approach where the opposite signal is the stop-loss.
Varying multiplier tightens/loosens the band; varying period controls sensitivity.
Risk: choppy markets generate many whipsaws — best on daily or 4h.
## Risk management (always include) ## Risk management (always include)
Every strategy MUST have: Every strategy MUST have:
@@ -152,6 +177,10 @@ Every strategy MUST have:
- A time-based exit: use bars_since_entry to avoid holding losers indefinitely - A time-based exit: use bars_since_entry to avoid holding losers indefinitely
- Reasonable position sizing: prefer ATR-based or percent-of-balance over fixed quantity - Reasonable position sizing: prefer ATR-based or percent-of-balance over fixed quantity
Exception: always-in-market flip strategies (using `"reverse": true`) do not need an
explicit stop-loss or time exit — the opposite signal acts as the stop. These are
only valid on futures. See Example 6 and Example 7.
{output_instructions} {output_instructions}
## Interpreting backtest results ## Interpreting backtest results
@@ -557,7 +586,127 @@ Key short-specific notes:
- Stop-loss for short = close > entry_price * (1 + stop_pct), e.g. `* 1.02` for 2% stop - Stop-loss for short = close > entry_price * (1 + stop_pct), e.g. `* 1.02` for 2% stop
- Take-profit for short = close < entry_price * (1 - target_pct), e.g. `* 0.97` for 3% target - Take-profit for short = close < entry_price * (1 - target_pct), e.g. `* 0.97` for 3% target
- Short exit uses `"side": "buy"` with `{"kind": "position_quantity"}` (same as long exit uses sell) - Short exit uses `"side": "buy"` with `{"kind": "position_quantity"}` (same as long exit uses sell)
- `percent_of_balance` for short entry uses `"usdc"` as the asset (the collateral currency)"##; - `percent_of_balance` for short entry uses `"usdc"` as the asset (the collateral currency)
### Example 6 — Futures flip-through-zero: 2-rule EMA trend-follower using `reverse`
When you always want to be in a position (long during uptrends, short during downtrends),
use `"reverse": true` to flip from one side to the other in a single order. This uses half
the round-trip fee count compared to a 4-rule separate-entry/exit approach.
```json
{
"type": "rule_based",
"candle_interval": "4h",
"rules": [
{
"comment": "Go long (or flip short→long): EMA9 crosses above EMA21 while above EMA50",
"when": {
"kind": "all_of",
"conditions": [
{"kind": "any_of", "conditions": [
{"kind": "position", "state": "flat"},
{"kind": "position", "state": "short"}
]},
{"kind": "ema_crossover", "fast_period": 9, "slow_period": 21, "direction": "above"},
{"kind": "ema_trend", "period": 50, "direction": "above"}
]
},
"then": {"side": "buy", "quantity": {"method": "percent_of_balance", "percent": "10", "asset": "usdc"}, "reverse": true}
},
{
"comment": "Go short (or flip long→short): EMA9 crosses below EMA21 while below EMA50",
"when": {
"kind": "all_of",
"conditions": [
{"kind": "any_of", "conditions": [
{"kind": "position", "state": "flat"},
{"kind": "position", "state": "long"}
]},
{"kind": "ema_crossover", "fast_period": 9, "slow_period": 21, "direction": "below"},
{"kind": "ema_trend", "period": 50, "direction": "below"}
]
},
"then": {"side": "sell", "quantity": {"method": "percent_of_balance", "percent": "10", "asset": "usdc"}, "reverse": true}
}
]
}
```
Key flip-strategy notes:
- Gate each rule on `flat OR opposite` (using `any_of`) so it fires both on initial entry and on flip
- `reverse: true` handles the flip math automatically — no need to size for `position_qty + new_qty`
- This pattern works best for trend-following where you want continuous market exposure
- Still add a time-based or ATR stop if you want a safety exit when the trend stalls
### Example 7 — Futures triple-Supertrend consensus flip
Multiple Supertrend instances with different period/multiplier combos act as a tiered
signal. `any_of` fires on the FIRST flip — the fastest line (7/1.5) reacts quickly,
the slowest (20/3.0) confirms strong trends. `reverse: true` makes it always-in-market:
the opposite signal is the stop-loss. No explicit stop or time exit needed.
Varying parameters to tune:
- Tighter multipliers (1.02.0) → more signals, more whipsaws
- Looser multipliers (2.54.0) → fewer signals, longer holds
- Try `all_of` instead of `any_of` to require consensus across all three (stronger filter)
```json
{{
"type": "rule_based",
"candle_interval": "4h",
"rules": [
{{
"comment": "LONG (or flip short→long): any Supertrend flips bullish",
"when": {{
"kind": "all_of",
"conditions": [
{{"kind": "any_of", "conditions": [
{{"kind": "position", "state": "flat"}},
{{"kind": "position", "state": "short"}}
]}},
{{
"kind": "any_of",
"conditions": [
{{"kind": "cross_over", "left": {{"kind": "field", "field": "close"}}, "right": {{"kind": "func", "name": "supertrend", "period": 7, "multiplier": "1.5"}}}},
{{"kind": "cross_over", "left": {{"kind": "field", "field": "close"}}, "right": {{"kind": "func", "name": "supertrend", "period": 10, "multiplier": "2.0"}}}},
{{"kind": "cross_over", "left": {{"kind": "field", "field": "close"}}, "right": {{"kind": "func", "name": "supertrend", "period": 20, "multiplier": "3.0"}}}}
]
}}
]
}},
"then": {{"side": "buy", "quantity": {{"method": "percent_of_balance", "percent": "5", "asset": "usdc"}}, "reverse": true}}
}},
{{
"comment": "SHORT (or flip long→short): any Supertrend flips bearish",
"when": {{
"kind": "all_of",
"conditions": [
{{"kind": "any_of", "conditions": [
{{"kind": "position", "state": "flat"}},
{{"kind": "position", "state": "long"}}
]}},
{{
"kind": "any_of",
"conditions": [
{{"kind": "cross_under", "left": {{"kind": "field", "field": "close"}}, "right": {{"kind": "func", "name": "supertrend", "period": 7, "multiplier": "1.5"}}}},
{{"kind": "cross_under", "left": {{"kind": "field", "field": "close"}}, "right": {{"kind": "func", "name": "supertrend", "period": 10, "multiplier": "2.0"}}}},
{{"kind": "cross_under", "left": {{"kind": "field", "field": "close"}}, "right": {{"kind": "func", "name": "supertrend", "period": 20, "multiplier": "3.0"}}}}
]
}}
]
}},
"then": {{"side": "sell", "quantity": {{"method": "percent_of_balance", "percent": "5", "asset": "usdc"}}, "reverse": true}}
}}
]
}}
```
Key Supertrend-specific notes:
- `supertrend` ignores `field` — it uses OHLC internally; omit the `field` param
- `multiplier` controls band width: lower = tighter, more reactive; higher = wider, more stable
- `any_of` → first flip triggers (responsive); `all_of` → all three must agree (conservative)
- Gate on position state to prevent re-entries scaling into an existing position"##;
/// Build the user message for the first iteration (no prior results). /// Build the user message for the first iteration (no prior results).
/// `prior_summary` contains a formatted summary of results from previous runs, if any. /// `prior_summary` contains a formatted summary of results from previous runs, if any.