Skip to content

View original

BnbTakerFlowMomentumTrendAlignedLS

Hypotheses

BNB USD-M Taker-Aggression Flow Momentum, Long-Short (Single Instrument BNBUSDT.BINANCE Perp, Daily Bars + Taker Buy/Sell Volume Ratio, Trade WITH Sustained Aggressor Order-Flow Imbalance Confirmed by Trend Alignment, 3-Parameter)

Hypotheses

A LONG-SHORT, single-instrument DIRECTIONAL order-flow strategy on BNBUSDT.BINANCE (USD-M perp, DAILY bars) whose primary signal is the TAKER BUY/SELL VOLUME RATIO — the share of volume executed by aggressive market orders (taker aggressor flow), an under-utilized execution-flow modality. This is a deliberate FAMILY SWITCH away from the crowd-positioning contrarian fade (which just collided with a prior abandonment: risk_reject, 46% drawdown, Sharpe 0.08 — that whole positioning-fade family is exhausted). Instead of FADING account positioning, this trades WITH sustained aggressor flow, on the thesis that persistent, one-sided market-order aggression reflects informed/institutional accumulation or distribution that PRECEDES continued price movement. It is NOT positioning-contrarian (opposite sign and different data feed), NOT price-only momentum (the flow imbalance, not price, is the trigger — price only gates direction), NOT the volatility-squeeze breakout sibling (that uses vol contraction on pure OHLCV; this uses executed aggressor imbalance), NOT funding-carry (L33), NOT options (L23), and NOT liquidation-dependent. Trade count is moderate (~25-45/yr), daily holds capture multi-percent continuation moves that clear the ~0.10% USD-M round-trip by a wide margin, and only 3 parameters resist overfitting.

Hypotheses

Iteration-3 fixes the Layer-3 'No trades' (should_enter fired 0/296 bars). Root cause: the flow z-score was pinned at 0 for the entire window because the strategy read only `taker_ratio`, and the Binance futures takerlongshortRatio API feed is only ~30 days deep — so over a 363-day daily backtest `_taker_at()` returned None for ~330 days, `_fracs` never reached min_flow_samples, z stayed 0, and no entry could ever trigger. Fix: also merge the Binance Vision `metrics` feed, whose `taker_long_short_vol_ratio` column (buyVol/sellVol) spans multiple years for liquid names like BNBUSDT, converted to a buy-fraction via r/(r+1) consistent with the API-feed conversion; referencing 'metrics' in code also makes supp_collector inject it. This gives full-window taker-flow coverage so the rolling z computes and the trend-aligned gate can fire (~25-45/yr as the hypothesis intends). Secondary safety per the explicit loosen-thresholds instruction: flow_z_entry default lowered 1.0->0.8 (still selective, ~1-in-5 flow extremes gated by trend). All previously-passing logic (instance-method proxy-safe helpers, ns normalization, rolling z, trend gate, four-way exits, capital-relative sizing) is unchanged; the only substantive change is adding the multi-year metrics source to the taker loader. No banned imports; no price-only fallback (edge remains the flow). 3 tuned params unchanged: flow_z_entry, flow_lookback, trend_len.

Hypotheses

No-edge single-perp taker-flow momentum — not worth 2 hours of optimization. Over 105 trades spanning 2022-2026 (the data-merge fix gave genuine multi-year coverage, so this is not a data wall): total_return -2.87%, CAGR -0.69%, Sharpe -0.079 (CI [-0.89, 0.72] straddling zero), PSR 0.45, profit_factor 0.92, expectancy -$27.32/trade, flat-to-negative in every year and direction-balanced (47 long / 58 short) — a real no-edge read. avg_trade_return_pct is 0.070%, below the 0.15% Binance USD-M fee floor (L22), with commissions 11.1% of gross, so even the marginal gross signal does not clear costs. This is the L73 single-perp taker-metrics-proxy directional family, established edge-dead this session in both fade and continuation orientations; the low 8.4% drawdown reflects small per-trade sizing, not skill. No tuning of flow_z_entry/flow_lookback/trend_len lifts a ~zero-Sharpe, sub-fee-floor signal to a promotable edge. Failure pattern: fee_edge/no_edge single-perp taker-flow momentum.

Implementation

Long-short directional taker-aggressor order-flow continuation on BNBUSDT.BINANCE (USD-M perp), DAILY bars. The primary signal is the taker buy/sell volume ratio (buyVol/sellVol) converted to a buy-fraction, smoothed and z-scored against its own rolling history (scale-invariant). It goes LONG when sustained aggressive buying (z >= flow_z_entry) is confirmed by an uptrend (close > SMA), SHORT when sustained aggressive selling (z <= -flow_z_entry) is confirmed by a downtrend (close < SMA). Taker flow is sourced primarily from the multi-year Binance Vision `metrics` feed (taker_long_short_vol_ratio), merged with the ~30-day `taker_ratio` API feed, on a nearest-preceding timeline. Exits on hard stop-loss, trend break, flow reversal, or a time stop. Capital-relative sizing (per_trade_pct x equity / price), leverage 1.0.

Verification Results

CLEAN RESTART 2026-09-04 — this run's verdict history and learning records were removed and it was restarted from verification. Its previous abandonment came from the pipeline, not from the market: the Layer-2 harness mis-bound @staticmethod helpers (fixed), QA issued terminal performance verdicts on an unoptimized smoke test (removed — QA now judges correctness only), and sandbox timeouts came from backtest-slot starvation (fixed). The hypothesis and the strategy code are unchanged. Verify the code on its merits; performance is decided later by the full backtest and the optimizer.

Verification Results

Marginal fee viability: avg_trade_return_pct 0.070% is below the ~0.15% round-trip floor, PF 0.92 (small net loser). Entry is a z-score not a fixed price threshold and intended holds are multi-day/multi-percent, so not a structural <0.15% violation — but realized edge doesn't clear costs. flow_z_entry/flow_lookback/trend_len are optimizer knobs; target net-of-fee avg_trade_return_pct >0.15%, consider de-weighting the weaker short leg, abandon if no operating point clears the floor.

Backtest Review

Full multi-year taker coverage (105 trades, 2022-2026) — the data-merge fix worked, so this is a real no-edge result not a data wall; low drawdown (8.4%), direction-balanced

Backtest Review

No edge: Sharpe -0.079 (CI [-0.89, 0.72] straddling zero), PSR 0.45, profit_factor 0.92, expectancy -$27.32/trade, negative/flat in every year

Backtest Review

Below the fee floor: avg_trade_return_pct 0.070% (< 0.15%, L22), commission 11.1% of gross

Backtest Review

L73 single-perp taker-metrics-proxy directional family, established edge-dead this session in both fade and continuation orientations

Backtest Review

information_ratio -1.00 (underperforms buy-and-hold)

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['trend_len', 'flow_smooth', 'flow_z_exit', 'flow_z_entry', 'min_notional', 'flow_lookback', 'per_trade_pct', 'stop_loss_pct', 'max_holding_bars', 'min_flow_samples', 'lag_tolerance_seconds'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: BnbTakerFlowMomentumTrendAlignedLS._row_to_frac() takes 1 positional argument but 2 were given (bar timestamp: 1735693620000) - steady_downtrend: TypeError: BnbTakerFlowMomentumTrendAlignedLS._row_to_frac() takes 1 positional argument but 2 were given (bar timestamp: 1735693620000) - flat_ranging: TypeError: BnbTakerFlowMomentumTrendAlignedLS._row_to_frac() takes 1 positional argument but 2 were given (bar timestamp: 1735693620000) - volatility_spike: TypeError: BnbTakerFlowMomentumTrendAlignedLS._row_to_frac() takes 1 positional argument but 2 were given (bar timestamp: 1735693620000) - zero_volume: TypeError: BnbTakerFlowMomentumTrendAlignedLS._row_to_frac() takes 1 positional argument but 2 were given (bar timestamp: 1735693620000) - price_gap: TypeError: BnbTakerFlowMomentumTrendAlignedLS._row_to_frac() takes 1 positional argument but 2 were given (bar timestamp: 1735693620000)

Iteration History

Verification failed (Layer 3 — sandbox backtest): No trades produced Bar type used: BNBUSDT.BINANCE-1-DAY-LAST-EXTERNAL, Bars processed: 363 Diagnostics: should_enter() returned a side 0 times over 296 evaluated bars -> your ENTRY CONDITION never triggered. Loosen the entry logic / thresholds. Ensure your strategy produces trades with the given data and parameters.

Iteration History

Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit. This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).
Strategy report

Backtest and paper results are hypothetical. Trading involves risk of loss.