Download test_bb_width.py from SurgeSoftwareSolutions/intraday-btc-model-11yr: direct link, hf CLI and curl.
- Browser
- Download file 3.77 kB
-
https://huggingface.co/SurgeSoftwareSolutions/intraday-btc-model-11yr/resolve/main/test_bb_width.py
- Command line
-
hf download hf://SurgeSoftwareSolutions/intraday-btc-model-11yr/test_bb_width.py
-
curl -L -o test_bb_width.py https://huggingface.co/SurgeSoftwareSolutions/intraday-btc-model-11yr/resolve/main/test_bb_width.py
3.77 kB
| """ | |
| Test Bollinger Band Width Filter on 15-Minute Strategy | |
| ------------------------------------------------------ | |
| Goal: Avoid trading during "Squeezes" (low volatility) to reduce whipsaws. | |
| Hypothesis: Only entering when Band Width > Threshold (expansion) will improve performance. | |
| """ | |
| import pandas as pd | |
| import numpy as np | |
| import sys | |
| import os | |
| from typing import Dict, Any | |
| sys.path.append(os.path.dirname(os.path.abspath(__file__))) | |
| import config | |
| from core.backtester import Backtester | |
| DATA_FILE = os.path.join(config.DATA_DIR, "btc_15min_binance.csv") | |
| DF_15M = pd.read_csv(DATA_FILE) | |
| DF_15M['timestamp'] = pd.to_datetime(DF_15M['timestamp']) | |
| DF_15M.set_index('timestamp', inplace=True) | |
| def calculate_bb_width(df, period=20, std_dev=2.0): | |
| """Calculate Bollinger Band Width.""" | |
| df = df.copy() | |
| sma = df['close'].rolling(period).mean() | |
| std = df['close'].rolling(period).std() | |
| upper = sma + (std * std_dev) | |
| lower = sma - (std * std_dev) | |
| # Width normalized by price level | |
| df['bb_width'] = (upper - lower) / sma | |
| return df['bb_width'] | |
| def run_strategy_with_bb_width(width_threshold): | |
| df = DF_15M.copy() | |
| # Indicators (MA 10/100 Baseline) | |
| df['fast'] = df['close'].ewm(span=10).mean() | |
| df['slow'] = df['close'].ewm(span=100).mean() | |
| df['bb_width'] = calculate_bb_width(df) | |
| # ATR for Stops | |
| tr = (df['high'] - df['low']).rolling(14).mean() | |
| df['atr'] = tr | |
| # Signals | |
| # Only signal if Crossover AND BB Width > Threshold | |
| signals = pd.Series(0, index=df.index) | |
| long_cond = (df['fast'] > df['slow']) & (df['bb_width'] > width_threshold) | |
| short_cond = (df['fast'] < df['slow']) & (df['bb_width'] > width_threshold) | |
| signals[long_cond] = 1 | |
| signals[short_cond] = -1 | |
| # Stops (Same as Baseline) | |
| avg_atr_pct = (df['atr'] / df['close']).mean() | |
| if pd.isna(avg_atr_pct): avg_atr_pct = 0.005 | |
| stop_dist = avg_atr_pct * 3.0 | |
| class DummySignal: | |
| def get_params(self): return {"model": f"BBW_{width_threshold}"} | |
| backtester = Backtester( | |
| data=df, | |
| features=df, | |
| signals=signals, | |
| signal_model=DummySignal(), | |
| e_base=1000.0, | |
| leverage=2.0, | |
| risk_fraction=0.02, | |
| stop_distance=stop_dist, | |
| tp_distance=stop_dist * 3.0, | |
| d_max=0.40, | |
| commission=0.0, | |
| slippage=0.0001, | |
| verbose=False | |
| ) | |
| results = backtester.run() | |
| m = results['metrics'] | |
| total_days = (df.index[-1] - df.index[0]).days | |
| trades_per_day = m['num_trades'] / total_days if total_days > 0 else 0 | |
| return { | |
| 'threshold': width_threshold, | |
| 'return': m['total_return_pct'], | |
| 'trades': m['num_trades'], | |
| 'trades_per_day': trades_per_day, | |
| 'win_rate': m['win_rate'], | |
| 'sharpe': m['sharpe_ratio'], | |
| 'drawdown': m['max_drawdown_pct'] | |
| } | |
| def test_bb_width(): | |
| print("\n" + "="*70) | |
| print("TESTING BOLLINGER BAND WIDTH FILTER (15-Minute)") | |
| print("="*70) | |
| print("Baseline: MA 10/100 | Stop 3.0ATR | 2x Leverage") | |
| print("-" * 70) | |
| print(f"{'BB Width >':<15} {'Return':<10} {'Trades':<10} {'Win Rate':<10} {'Drawdown':<10} {'Sharpe':<10}") | |
| print("-" * 70) | |
| # Width usually ranges from 0.005 (squeeze) to 0.05+ (expansion) on 15m | |
| thresholds = [0.0, 0.005, 0.01, 0.015, 0.02] | |
| for t in thresholds: | |
| r = run_strategy_with_bb_width(t) | |
| print(f"{t:<15} {r['return']:>8.2f}% {r['trades']:>9} {r['win_rate']:>9.1f}% {r['drawdown']:>9.1f}% {r['sharpe']:>9.2f}") | |
| print("-" * 70) | |
| if __name__ == "__main__": | |
| test_bb_width() | |