From 292c1018597e56faf0e7d9011c8e19e708198fd6 Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Mon, 9 Mar 2026 13:09:01 +0200 Subject: [PATCH] docs(prompts): add DSL expression kind reference and three working examples Shows correct usage of rsi/bollinger/ema_trend condition shortcuts, entry_price and bars_since_entry ExprKind values, and func/cross_over/bin_op expressions. Also calls out common model mistakes (rsi as ExprKind, bars_since_entry as FuncName, expr_field) and adds a note that spot strategies are long-only. Co-Authored-By: Claude Sonnet 4.6 --- src/prompts.rs | 183 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/src/prompts.rs b/src/prompts.rs index f7446ac..b9d1a28 100644 --- a/src/prompts.rs +++ b/src/prompts.rs @@ -119,6 +119,188 @@ When I share results from previous iterations, use them to guide your next strat - **Condition audit shows one condition always true/false**: That condition is redundant or broken. Remove it or adjust its parameters. +## Critical: expression kinds (common mistakes) + +These are the ONLY valid values for `"kind"` inside an `Expr` object: +`literal`, `field`, `func`, `bin_op`, `apply_func`, `unary_op`, `bars_since`, +`entry_price`, `position_quantity`, `unrealised_pnl`, `bars_since_entry`, `balance` + +Common mistakes to NEVER make: +- `"kind": "rsi"` inside an Expr is WRONG. `rsi` is a *Condition* kind, not an Expr. + To use RSI value in a `compare` expression use: `{{"kind":"func","name":"rsi","period":14}}` +- `"kind": "bars_since_entry"` is a valid standalone Expr (no extra fields needed). + Do NOT put `"bars_since_entry"` as a `"name"` inside `{{"kind":"func",...}}` — that is WRONG. +- `"kind": "expr_field"` does NOT exist. Use `{{"kind":"field","field":"close"}}`. +- `rsi`, `adx`, `supertrend` are NOT valid inside `apply_func`. Use only `apply_func` + with `ApplyFuncName` values: `highest`, `lowest`, `sma`, `ema`, `wma`, `std_dev`, `sum`, + `bollinger_upper`, `bollinger_lower`. + +## Working examples + +### Example 1 — EMA crossover with trend filter and position exits + +```json +{{ + "type": "rule_based", + "candle_interval": "1h", + "rules": [ + {{ + "comment": "Buy: EMA9 crosses above EMA21 while price is above EMA50", + "when": {{ + "kind": "all_of", + "conditions": [ + {{"kind": "position", "state": "flat"}}, + {{"kind": "ema_crossover", "fast_period": 9, "slow_period": 21, "direction": "above"}}, + {{"kind": "ema_trend", "period": 50, "direction": "above"}} + ] + }}, + "then": {{"side": "buy", "quantity": "0.001"}} + }}, + {{ + "comment": "Sell: EMA9 crosses below EMA21, OR 2% stop-loss, OR 72-bar time exit", + "when": {{ + "kind": "all_of", + "conditions": [ + {{"kind": "position", "state": "long"}}, + {{ + "kind": "any_of", + "conditions": [ + {{"kind": "ema_crossover", "fast_period": 9, "slow_period": 21, "direction": "below"}}, + {{ + "kind": "compare", + "left": {{"kind": "field", "field": "close"}}, + "op": "<", + "right": {{"kind": "bin_op", "op": "mul", "left": {{"kind": "entry_price"}}, "right": {{"kind": "literal", "value": "0.98"}}}} + }}, + {{ + "kind": "compare", + "left": {{"kind": "bars_since_entry"}}, + "op": ">=", + "right": {{"kind": "literal", "value": "72"}} + }} + ] + }} + ] + }}, + "then": {{"side": "sell", "quantity": "0.001"}} + }} + ] +}} +``` + +### Example 2 — RSI mean-reversion with Bollinger band confirmation + +```json +{{ + "type": "rule_based", + "candle_interval": "4h", + "rules": [ + {{ + "comment": "Buy: RSI below 35 AND price below lower Bollinger band", + "when": {{ + "kind": "all_of", + "conditions": [ + {{"kind": "position", "state": "flat"}}, + {{"kind": "rsi", "period": 14, "threshold": "35", "comparison": "below"}}, + {{"kind": "bollinger", "period": 20, "band": "below_lower"}} + ] + }}, + "then": {{"side": "buy", "quantity": "0.001"}} + }}, + {{ + "comment": "Sell: RSI recovers above 55, OR 3% stop-loss, OR 48-bar time exit", + "when": {{ + "kind": "all_of", + "conditions": [ + {{"kind": "position", "state": "long"}}, + {{ + "kind": "any_of", + "conditions": [ + {{"kind": "rsi", "period": 14, "threshold": "55", "comparison": "above"}}, + {{ + "kind": "compare", + "left": {{"kind": "field", "field": "close"}}, + "op": "<", + "right": {{"kind": "bin_op", "op": "mul", "left": {{"kind": "entry_price"}}, "right": {{"kind": "literal", "value": "0.97"}}}} + }}, + {{ + "kind": "compare", + "left": {{"kind": "bars_since_entry"}}, + "op": ">=", + "right": {{"kind": "literal", "value": "48"}} + }} + ] + }} + ] + }}, + "then": {{"side": "sell", "quantity": "0.001"}} + }} + ] +}} +``` + +### Example 3 — ATR breakout with ATR-based stop-loss + +```json +{{ + "type": "rule_based", + "candle_interval": "1h", + "rules": [ + {{ + "comment": "Buy: close crosses above 20-bar high while EMA50 confirms uptrend", + "when": {{ + "kind": "all_of", + "conditions": [ + {{"kind": "position", "state": "flat"}}, + {{"kind": "ema_trend", "period": 50, "direction": "above"}}, + {{ + "kind": "cross_over", + "left": {{"kind": "field", "field": "close"}}, + "right": {{"kind": "func", "name": "highest", "field": "high", "period": 20, "offset": 1}} + }} + ] + }}, + "then": {{"side": "buy", "quantity": "0.001"}} + }}, + {{ + "comment": "Sell: 2-ATR stop-loss below entry price, OR 48-bar time exit", + "when": {{ + "kind": "all_of", + "conditions": [ + {{"kind": "position", "state": "long"}}, + {{ + "kind": "any_of", + "conditions": [ + {{ + "kind": "compare", + "left": {{"kind": "field", "field": "close"}}, + "op": "<", + "right": {{ + "kind": "bin_op", "op": "sub", + "left": {{"kind": "entry_price"}}, + "right": {{ + "kind": "bin_op", "op": "mul", + "left": {{"kind": "func", "name": "atr", "period": 14}}, + "right": {{"kind": "literal", "value": "2.0"}} + }} + }} + }}, + {{ + "kind": "compare", + "left": {{"kind": "bars_since_entry"}}, + "op": ">=", + "right": {{"kind": "literal", "value": "48"}} + }} + ] + }} + ] + }}, + "then": {{"side": "sell", "quantity": "0.001"}} + }} + ] +}} +``` + ## Anti-patterns to avoid - Don't use the same indicator for both entry and exit (circular logic) @@ -128,6 +310,7 @@ When I share results from previous iterations, use them to guide your next strat - Don't create strategies with more than 5-6 conditions — overfitting risk - Don't ignore fees — a strategy needs to overcome 0.1% per round trip - Always gate buy rules with position state "flat" and sell rules with "long" +- Never add a short-entry (sell when flat) rule — spot markets are long-only "## ) }