You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# Local dev UX: if name/description not provided, derive from code variables.
543
560
ifnotnameornotdescription:
544
561
meta=_extract_indicator_meta_from_code(code)
@@ -902,6 +919,7 @@ def _err_stream():
902
919
- Environment: browser-side Pyodide–style sandbox **or** API verify sandbox: **no network**, no file I/O, no subprocess.
903
920
- **`pd` and `np` are already available.** Do **not** write `import pandas` / `import numpy`. Avoid any `import` unless unavoidable; never import `os`, `sys`, `requests`, `socket`, `subprocess`, `threading`, `sqlite3`, `multiprocessing`, or other I/O/network modules.
904
921
- Do **not** use: `eval`, `exec`, `compile`, `open`, `__import__`, `getattr`/`setattr`/`delattr` on untrusted names, `globals`, `vars`, `dir`, or meta-programming to escape the sandbox. `locals()` is allowed if needed to assemble `output` (backtest/verify allow it); avoid `globals()`.
- `df['buy']` — True on bars where a **new** long entry signal is allowed (edge-triggered).
946
-
- `df['sell']` — True on bars where a **new** exit / short entry signal is allowed (per product semantics).
963
+
- `df['open_long']`, `df['close_long']`, `df['open_short']`, `df['close_short']` — all bool, length `len(df)`.
964
+
- Use an `edge(s)` helper: `s & ~s.shift(1).fillna(False)` on each raw condition.
965
+
- On trend flip bars you MAY set both `close_*` and opposing `open_*` true (flip_mode R2); for tp/sl-only exits use `close_*` alone without mixing tp/sl into `buy`/`sell`.
- `df['buy']` / `df['sell']` — edge-triggered; `tradeDirection both` maps buy→open long (flip short first), sell→open short (flip long first). Do **not** use buy/sell for “close only” exits when `both` — use four-way `close_*`.
972
+
973
+
Rules (both forms):
949
974
950
975
- Same **index and length** as `df`; dtype boolean (use `.astype(bool)` after fillna).
951
-
- **Edge-trigger (mandatory)** unless the user explicitly asks for repeated signals on consecutive bars:
- Signals represent **confirmation on bar close**; the engine fills on the **next bar open** (live-like). Do not implement intrabar lookahead (e.g. do not use the same bar’s `high` to validate a signal that assumes you bought at that bar’s `open` unless the user clearly wants that research mode).
976
+
- **Edge-trigger (mandatory)** unless the user explicitly asks for repeated signals on consecutive bars.
977
+
- Signals represent **confirmation on bar close**; the engine fills on the **next bar open** (live-like). Do not implement intrabar lookahead unless the user clearly wants research mode.
956
978
- Fill NaN from indicators before comparisons; replace division-by-zero (`replace(0, np.nan)` then fill).
979
+
- If you use four-way columns, you do **not** need `df['buy']`/`df['sell']` unless `output['signals']` chart markers require them (markers can use open_long/open_short only).
- `tradeDirection`: exactly `long`, `short`, or `both`.
1009
1032
1033
+
**`tradeDirection both` execution semantics:** `df['buy']` → open long (close short first if short); `df['sell']` → open short (close long first if long). Do not document `buy` as a separate close-short column. If the strategy uses in-code tp/sl on `high`/`low` touches, prefer **not** also setting `trailingEnabled true` unless the user explicitly wants engine trailing — see `docs/STRATEGY_DEV_GUIDE.md`.
1034
+
1010
1035
**Do not** put `leverage` in `@strategy`; users set leverage in the IDE backtest panel.
1011
1036
1012
1037
**Do not** emit `signalTiming`; the product fixes fills to next bar open.
@@ -1035,58 +1060,21 @@ def _err_stream():
1035
1060
"""
1036
1061
1037
1062
def_template_code() ->str:
1038
-
# Fallback template that follows the project expectations.
+"\n\nReturn one full replacement script: same QuantDinger rules (my_indicator_name/description, df = df.copy(), declared @param values must be read via params.get(...), df['buy']/df['sell'], output dict, list lengths == len(df)). "
1106
+
+"\n\nReturn one full replacement script: same QuantDinger rules (my_indicator_name/description, df = df.copy(), declared @param values must be read via params.get(...), four-way OR buy/sell execution columns, output dict, list lengths == len(df)). "
1119
1107
"Python only — no markdown, no prose outside the code."
0 commit comments