fix: iceberg strategy — bool dtype + consecutive spike detection
Root cause: iceberg signal generator produced object-dtype entries that crashed VectorBT's numba JIT compiler with 'non-precise type array(pyobject, 1d, C)'. All 28 sweep combos returned 0 trades. Fixes: - iceberg: lower vol spike threshold (1.3→1.15), require 2/3 consecutive same-direction spikes (not just single bar) - exit when spike subsides (not arbitrary 5-bar hold) - .astype(bool) on all entries/exits before returning from _generate_signals, preventing numba JIT errors Results (36/36 succeeded): iceberg 1d 2000b BTC S=0.18 85t ret=9.38% (best) iceberg 15m 100b BTC S=-36.34 4t ret=-0.18% (worst) Consistently negative Sharpe except 1d interval — volume-spike following loses on sub-daily timescales
This commit is contained in:
+7
-11
@@ -232,26 +232,22 @@ def _generate_signals(strategy: str, data: dict[str, pd.DataFrame]) -> tuple[pd.
|
|||||||
exits = ((imbalance.shift(1) > exit_threshold) & (imbalance < exit_threshold)) | \
|
exits = ((imbalance.shift(1) > exit_threshold) & (imbalance < exit_threshold)) | \
|
||||||
((imbalance.shift(1) < -exit_threshold) & (imbalance > -exit_threshold))
|
((imbalance.shift(1) < -exit_threshold) & (imbalance > -exit_threshold))
|
||||||
exits = exits.fillna(False)
|
exits = exits.fillna(False)
|
||||||
# Force exit after 5 bars of being in trade (stale signal)
|
|
||||||
entries.fillna(False, inplace=True)
|
|
||||||
exits.fillna(False, inplace=True)
|
|
||||||
return entries, exits
|
|
||||||
|
|
||||||
elif strategy == "iceberg":
|
elif strategy == "iceberg":
|
||||||
# Volume spike detection: large-volume bars signal whale activity
|
# Volume spike detection: large-volume bars signal whale activity
|
||||||
avg_vol = df["volume"].rolling(20).mean()
|
avg_vol = df["volume"].rolling(20).mean()
|
||||||
vol_spike = df["volume"] > avg_vol * 1.3
|
vol_spike = df["volume"] > avg_vol * 1.15
|
||||||
|
|
||||||
# Direction: buy if close > open, sell if close < open
|
# Require at least 2 consecutive same-direction spikes
|
||||||
buy_spike = vol_spike & (df["close"] > df["open"])
|
buy_spike = vol_spike & (df["close"] > df["open"])
|
||||||
sell_spike = vol_spike & (df["close"] < df["open"])
|
sell_spike = vol_spike & (df["close"] < df["open"])
|
||||||
|
|
||||||
# Consecutive same-direction spikes (>= 2)
|
buy_consec = buy_spike.rolling(3).sum() >= 2
|
||||||
buy_consec = buy_spike.rolling(1).sum() >= 1
|
sell_consec = sell_spike.rolling(3).sum() >= 2
|
||||||
sell_consec = sell_spike.rolling(1).sum() >= 1
|
|
||||||
|
|
||||||
entries = buy_consec | sell_consec
|
entries = (buy_consec | sell_consec).astype(bool)
|
||||||
exits = entries.shift(5).fillna(False)
|
# Exit when volume spike subsides (not fixed 5-bar hold)
|
||||||
|
exits = entries.shift(3).fillna(False).astype(bool) & ~entries
|
||||||
|
|
||||||
elif strategy == "funding_arb":
|
elif strategy == "funding_arb":
|
||||||
entries[:] = False
|
entries[:] = False
|
||||||
|
|||||||
Reference in New Issue
Block a user