import logging import seaborn as sns import matplotlib.pyplot as plt import pandas as pd import datetime from cycles.utils.storage import Storage from cycles.Analysis.strategies import Strategy logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", handlers=[ logging.FileHandler("backtest.log"), logging.StreamHandler() ] ) config = { "start_date": "2025-03-01", "stop_date": datetime.datetime.today().strftime('%Y-%m-%d'), "data_file": "btcusd_1-min_data.csv" } config_strategy = { "bb_width": 0.05, "bb_period": 20, "rsi_period": 14, "trending": { "rsi_threshold": [30, 70], "bb_std_dev_multiplier": 2.5, }, "sideways": { "rsi_threshold": [40, 60], "bb_std_dev_multiplier": 1.8, }, "strategy_name": "MarketRegimeStrategy", # CryptoTradingStrategy "SqueezeStrategy": True } IS_DAY = False if __name__ == "__main__": # Load data storage = Storage(logging=logging) data = storage.load_data(config["data_file"], config["start_date"], config["stop_date"]) # Run strategy strategy = Strategy(config=config_strategy, logging=logging) processed_data = strategy.run(data.copy(), config_strategy["strategy_name"]) # Get buy and sell signals buy_condition = processed_data.get('BuySignal', pd.Series(False, index=processed_data.index)).astype(bool) sell_condition = processed_data.get('SellSignal', pd.Series(False, index=processed_data.index)).astype(bool) buy_signals = processed_data[buy_condition] sell_signals = processed_data[sell_condition] # Plot the data with seaborn library if processed_data is not None and not processed_data.empty: # Create a figure with two subplots, sharing the x-axis fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(16, 8), sharex=True) strategy_name = config_strategy["strategy_name"] # Plot 1: Close Price and Strategy-Specific Bands/Levels sns.lineplot(x=processed_data.index, y='close', data=processed_data, label='Close Price', ax=ax1) # Use standardized column names for bands if 'UpperBand' in processed_data.columns and 'LowerBand' in processed_data.columns: # Instead of lines, shade the area between upper and lower bands ax1.fill_between(processed_data.index, processed_data['LowerBand'], processed_data['UpperBand'], alpha=0.1, color='blue', label='Bollinger Bands') else: logging.warning(f"{strategy_name}: UpperBand or LowerBand not found for plotting.") # Add strategy-specific extra indicators if available if strategy_name == "CryptoTradingStrategy": if 'StopLoss' in processed_data.columns: sns.lineplot(x=processed_data.index, y='StopLoss', data=processed_data, label='Stop Loss', ax=ax1, linestyle='--', color='orange') if 'TakeProfit' in processed_data.columns: sns.lineplot(x=processed_data.index, y='TakeProfit', data=processed_data, label='Take Profit', ax=ax1, linestyle='--', color='purple') # Plot Buy/Sell signals on Price chart if not buy_signals.empty: ax1.scatter(buy_signals.index, buy_signals['close'], color='green', marker='o', s=20, label='Buy Signal', zorder=5) if not sell_signals.empty: ax1.scatter(sell_signals.index, sell_signals['close'], color='red', marker='o', s=20, label='Sell Signal', zorder=5) ax1.set_title(f'Price and Signals ({strategy_name})') ax1.set_ylabel('Price') ax1.legend() ax1.grid(True) # Plot 2: RSI and Strategy-Specific Thresholds if 'RSI' in processed_data.columns: sns.lineplot(x=processed_data.index, y='RSI', data=processed_data, label=f'RSI (' + str(config_strategy.get("rsi_period", 14)) + ')', ax=ax2, color='purple') if strategy_name == "MarketRegimeStrategy": # Get threshold values upper_threshold = config_strategy.get("trending", {}).get("rsi_threshold", [30,70])[1] lower_threshold = config_strategy.get("trending", {}).get("rsi_threshold", [30,70])[0] # Shade overbought area (upper) ax2.fill_between(processed_data.index, upper_threshold, 100, alpha=0.1, color='red', label=f'Overbought (>{upper_threshold})') # Shade oversold area (lower) ax2.fill_between(processed_data.index, 0, lower_threshold, alpha=0.1, color='green', label=f'Oversold (<{lower_threshold})') elif strategy_name == "CryptoTradingStrategy": # Shade overbought area (upper) ax2.fill_between(processed_data.index, 65, 100, alpha=0.1, color='red', label='Overbought (>65)') # Shade oversold area (lower) ax2.fill_between(processed_data.index, 0, 35, alpha=0.1, color='green', label='Oversold (<35)') # Plot Buy/Sell signals on RSI chart if not buy_signals.empty and 'RSI' in buy_signals.columns: ax2.scatter(buy_signals.index, buy_signals['RSI'], color='green', marker='o', s=20, label='Buy Signal (RSI)', zorder=5) if not sell_signals.empty and 'RSI' in sell_signals.columns: ax2.scatter(sell_signals.index, sell_signals['RSI'], color='red', marker='o', s=20, label='Sell Signal (RSI)', zorder=5) ax2.set_title('Relative Strength Index (RSI) with Signals') ax2.set_ylabel('RSI Value') ax2.set_ylim(0, 100) ax2.legend() ax2.grid(True) else: logging.info("RSI data not available for plotting.") # Plot 3: Strategy-Specific Indicators ax3.clear() # Clear previous plot content if any if 'BBWidth' in processed_data.columns: sns.lineplot(x=processed_data.index, y='BBWidth', data=processed_data, label='BB Width', ax=ax3) if strategy_name == "MarketRegimeStrategy": if 'MarketRegime' in processed_data.columns: sns.lineplot(x=processed_data.index, y='MarketRegime', data=processed_data, label='Market Regime (Sideways: 1, Trending: 0)', ax=ax3) ax3.set_title('Bollinger Bands Width & Market Regime') ax3.set_ylabel('Value') elif strategy_name == "CryptoTradingStrategy": if 'VolumeMA' in processed_data.columns: sns.lineplot(x=processed_data.index, y='VolumeMA', data=processed_data, label='Volume MA', ax=ax3) if 'volume' in processed_data.columns: sns.lineplot(x=processed_data.index, y='volume', data=processed_data, label='Volume', ax=ax3, alpha=0.5) ax3.set_title('Volume Analysis') ax3.set_ylabel('Volume') ax3.legend() ax3.grid(True) plt.xlabel('Date') fig.tight_layout() plt.show() else: logging.info("No data to plot.")