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
The async-job submit endpoints (`/backtests`, `/experiments/*`,
`/quick-trade/orders` idempotent replay) all return a 500 with
`TypeError: The view function did not return a valid response. ...
but it was a tuple.` because `envelope()` already returns a
`(jsonify, 200)` tuple, and the routes wrap it AGAIN as
`return envelope(...), 202` — producing a nested
`((Response, 200), 202)` Flask cannot decode.
Fix:
- Add `status=` kwarg to `envelope()` (defaults to 200), so callers
pass status through the helper instead of wrapping the result.
- Update 5 call sites to use `status=202` / drop the redundant `,200`.
Verified end-to-end: a SMA(10/30) crossover backtest on BTC/USDT 1D
now submits (HTTP 202), runs the indicator (`buy=2, sell=1` over 97
candles), reaches `_simulate_trading_new_format`, and returns a
`succeeded` job with totalReturn=6.39%, sharpeRatio=0.73,
maxDrawdown=-14.53%, 2 trades.
Also: replace the existing AGENT_QUICKSTART.md backtest example
(which used `output = {"signal": ...}` — never a supported shape)
with a working SMA-crossover script, plus a new "code parameter
contract" subsection that documents:
- the pre-bound exec environment (`df`, `np`, `pd`, `params`,
`call_indicator`, technical-indicator helpers like
SMA/EMA/RSI/MACD/BOLL/ATR/CROSSOVER/CROSSUNDER)
- the two supported signal shapes (2-way `df['buy']`/`df['sell']`,
4-way `df['open_long']`/`close_long`/`open_short`/`close_short`)
- a parameterized trend-pullback example with `# @param` declarations
|`call_indicator(...)`| callable | Invoke another saved indicator from this script |
139
+
|`SMA, EMA, RSI, MACD, BOLL, ATR, CROSSOVER, CROSSUNDER`| callables | Built-in technical helpers (see `app/services/backtest.py::_get_indicator_functions`) |
140
+
141
+
**Required output** — the script must add **either** of these to `df`:
142
+
143
+
| Style | Required columns | When to use |
144
+
|-------|------------------|-------------|
145
+
| 2-way (recommended) |`df['buy']`, `df['sell']` (boolean Series) | Most strategies — simple long-only or `trade_direction='both'`|
146
+
| 4-way (advanced) |`df['open_long']`, `df['close_long']`, `df['open_short']`, `df['close_short']` (boolean Series) | When you need explicit control over each leg |
0 commit comments