diff --git a/src/prompts.rs b/src/prompts.rs index e1d8180..8e3cbbf 100644 --- a/src/prompts.rs +++ b/src/prompts.rs @@ -52,6 +52,10 @@ sma, ema, wma, rsi, std_dev, sum, highest, lowest, atr, supertrend, adx, bollinger_upper, bollinger_lower — applied to any candle field (open/high/low/close/volume) with configurable period and optional offset. +These are FuncNames used INSIDE `{{"kind":"func","name":"...","period":N}}` expressions. +`atr`, `adx`, and `supertrend` use OHLC internally and ignore the `field` parameter. +To use ADX as a trend-strength filter: `{{"kind":"compare","left":{{"kind":"func","name":"adx","period":14}},"op":">","right":{{"kind":"literal","value":"25"}}}}` + ### Composed indicators (apply_func) Apply rolling functions to arbitrary expressions: EMA of EMA, Hull MA (WMA of expression), VWAP (sum of close*volume / sum of volume), standard deviation of returns, etc. @@ -146,6 +150,8 @@ Common mistakes to NEVER make: - `"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"}}`. +- Every Expr object MUST have a `"kind"` field. `{{"field":"close"}}` is WRONG — missing `"kind"`. + CORRECT: `{{"kind":"field","field":"close"}}`. The `"kind"` is never optional. - `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`. @@ -154,6 +160,9 @@ Common mistakes to NEVER make: - `bollinger_upper` and `bollinger_lower` are FUNC NAMES, not Expr kinds. To compare close to the upper band: `{{"kind":"compare","left":{{"kind":"field","field":"close"}},"op":">","right":{{"kind":"func","name":"bollinger_upper","period":20}}}}` NEVER write `{{"kind":"bollinger_upper",...}}` — `bollinger_upper` is not an Expr kind. +- `adx` is a FUNC NAME, not a Condition kind. To filter for strong trends (ADX > 25): + `{{"kind":"compare","left":{{"kind":"func","name":"adx","period":14}},"op":">","right":{{"kind":"literal","value":"25"}}}}` + NEVER write `{{"kind":"adx",...}}` — `adx` is not a Condition kind, it is a FuncName used inside `{{"kind":"func",...}}`. - `roc` (rate of change), `hma` (Hull MA), `vwap`, `macd`, `cci`, `stoch` are NOT supported. Use `sma`, `ema`, `wma`, `rsi`, `atr`, `adx`, `supertrend`, `std_dev`, `sum`, `highest`, `lowest`, `bollinger_upper`, `bollinger_lower` only. @@ -324,6 +333,95 @@ Common mistakes to NEVER make: }} ``` +### Example 4 — MACD crossover (composed from primitives) + +MACD has no native support, but can be composed from `func` and `apply_func`. +The MACD line is `EMA(12) - EMA(26)`; the signal line is `EMA(9)` of the MACD line. + +```json +{{ + "type": "rule_based", + "candle_interval": "4h", + "rules": [ + {{ + "comment": "Buy: MACD line crosses above signal line", + "when": {{ + "kind": "all_of", + "conditions": [ + {{"kind": "position", "state": "flat"}}, + {{ + "kind": "cross_over", + "left": {{ + "kind": "bin_op", "op": "sub", + "left": {{"kind": "func", "name": "ema", "period": 12}}, + "right": {{"kind": "func", "name": "ema", "period": 26}} + }}, + "right": {{ + "kind": "apply_func", "name": "ema", "period": 9, + "expr": {{ + "kind": "bin_op", "op": "sub", + "left": {{"kind": "func", "name": "ema", "period": 12}}, + "right": {{"kind": "func", "name": "ema", "period": 26}} + }} + }} + }} + ] + }}, + "then": {{"side": "buy", "quantity": "0.001"}} + }}, + {{ + "comment": "Sell: MACD crosses below signal, OR 2% stop-loss, OR 72-bar time exit", + "when": {{ + "kind": "all_of", + "conditions": [ + {{"kind": "position", "state": "long"}}, + {{ + "kind": "any_of", + "conditions": [ + {{ + "kind": "cross_under", + "left": {{ + "kind": "bin_op", "op": "sub", + "left": {{"kind": "func", "name": "ema", "period": 12}}, + "right": {{"kind": "func", "name": "ema", "period": 26}} + }}, + "right": {{ + "kind": "apply_func", "name": "ema", "period": 9, + "expr": {{ + "kind": "bin_op", "op": "sub", + "left": {{"kind": "func", "name": "ema", "period": 12}}, + "right": {{"kind": "func", "name": "ema", "period": 26}} + }} + }} + }}, + {{ + "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"}} + }} + ] +}} +``` + +Key pattern: `apply_func` wraps any `Expr` tree, enabling EMA-of-expression (signal line), +WMA-of-expression (Hull MA), or std_dev-of-returns. There is NO native `macd` func name — +always compose it as `bin_op(sub, func(ema,12), func(ema,26))` as shown above. + ## Anti-patterns to avoid - Don't use the same indicator for both entry and exit (circular logic)