Update Statergy: Don't Buy When RSI value in 1H in Above 50: import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from binance import Client, ThreadedWebsocketManager, ThreadedDepthCacheManager
import datetime
import time

# ----------------------------- Configuration -----------------------------

# Binance API credentials (Public API does not require API keys for data retrieval)
# If you plan to extend this script for live trading, provide your API_KEY and API_SECRET
API_KEY = ''
API_SECRET = ''

# Initialize Binance Client
client = Client(API_KEY, API_SECRET)

# Trading Parameters
SYMBOL = 'BTCUSDT'            # Trading pair
INTERVAL = Client.KLINE_INTERVAL_1HOUR  # Timeframe for candlesticks (1 hour)
START_DATE = '2020-01-01'     # Start date for historical data
END_DATE = '2023-10-01'       # End date for historical data

# Moving Averages Periods
SMA_SHORT_PERIOD = 50    # Short-term SMA (e.g., 50 hours)
SMA_LONG_PERIOD = 200    # Long-term SMA (e.g., 200 hours)

# Position Parameters
INITIAL_CAPITAL = 10000.0  # Starting with $10,000
TRADING_FEE = 0.001         # 0.1% trading fee per transaction

# ----------------------------- Data Collection -----------------------------

def get_historical_data(symbol, interval, start, end):
    """
    Fetches historical klines (candlestick data) from Binance.
    Handles Binance's limitation on the maximum number of data points per request.
    """
    klines = []
    start_ts = int(pd.to_datetime(start).timestamp() * 1000)
    end_ts = int(pd.to_datetime(end).timestamp() * 1000)
    limit = 1000  # Maximum limit per Binance API request
    
    print("Fetching historical data from Binance...")
    
    while True:
        temp_klines = client.get_klines(symbol=symbol, interval=interval, startTime=start_ts, endTime=end_ts, limit=limit)
        if not temp_klines:
            break
        klines.extend(temp_klines)
        last_ts = temp_klines[-1][0]
        if last_ts >= end_ts:
            break
        start_ts = last_ts + 1
        # To respect API rate limits
        time.sleep(0.5)
    
    # Convert to DataFrame
    data = pd.DataFrame(klines, columns=[
        'Open Time', 'Open', 'High', 'Low', 'Close', 'Volume',
        'Close Time', 'Quote Asset Volume', 'Number of Trades',
        'Taker Buy Base Asset Volume', 'Taker Buy Quote Asset Volume', 'Ignore'
    ])
    
    # Convert timestamp to datetime
    data['Open Time'] = pd.to_datetime(data['Open Time'], unit='ms')
    data['Close Time'] = pd.to_datetime(data['Close Time'], unit='ms')
    
    # Convert numerical columns to float
    numerical_cols = ['Open', 'High', 'Low', 'Close', 'Volume']
    data[numerical_cols] = data[numerical_cols].astype(float)
    
    # Set Open Time as index
    data.set_index('Open Time', inplace=True)
    
    return data

# Fetch data
data = get_historical_data(SYMBOL, INTERVAL, START_DATE, END_DATE)
print(f"Data fetched: {data.shape[0]} rows.")

# ----------------------------- Strategy Implementation -----------------------------

# Calculate Moving Averages
data['SMA_short'] = data['Close'].rolling(window=SMA_SHORT_PERIOD).mean()
data['SMA_long'] = data['Close'].rolling(window=SMA_LONG_PERIOD).mean()

# Drop rows with NaN values (due to SMA calculations)
data.dropna(inplace=True)

# Generate Signals
# When SMA_short crosses above SMA_long -> Buy Signal (1)
# When SMA_short crosses below SMA_long -> Sell Signal (-1)
data['Signal'] = 0
data['Signal'] = np.where(data['SMA_short'] > data['SMA_long'], 1, -1)

# Identify Points where Signal changes
data['Position'] = data['Signal'].diff()

# ----------------------------- Backtesting -----------------------------

# Initialize portfolio DataFrame
portfolio = pd.DataFrame(index=data.index)
portfolio['Close'] = data['Close']
portfolio['Signal'] = data['Signal']
portfolio['Position'] = data['Position']

# Initialize portfolio columns
portfolio['Holdings'] = 0.0      # Value of BTC held
portfolio['Cash'] = INITIAL_CAPITAL
portfolio['Total'] = INITIAL_CAPITAL
portfolio['BTC_Held'] = 0.0

for i in range(1, len(portfolio)):
    # Carry forward previous holdings and cash
    portfolio.at[portfolio.index[i], 'BTC_Held'] = portfolio.at[portfolio.index[i-1], 'BTC_Held']
    portfolio.at[portfolio.index[i], 'Cash'] = portfolio.at[portfolio.index[i-1], 'Cash']
    
    # Buy Signal
    if portfolio.at[portfolio.index[i], 'Position'] == 2:  # From -1 to +1
        # Buy BTC with all available cash
        buy_price = portfolio.at[portfolio.index[i], 'Close']
        btc_to_buy = (portfolio.at[portfolio.index[i], 'Cash'] * (1 - TRADING_FEE)) / buy_price
        portfolio.at[portfolio.index[i], 'BTC_Held'] += btc_to_buy
        portfolio.at[portfolio.index[i], 'Cash'] -= btc_to_buy * buy_price * (1 + TRADING_FEE)
        print(f"Buy on {portfolio.index[i]} at price {buy_price:.2f} USDT, BTC Bought: {btc_to_buy:.6f}")
        
    # Sell Signal
    elif portfolio.at[portfolio.index[i], 'Position'] == -2:  # From +1 to -1
        # Sell all BTC held
        sell_price = portfolio.at[portfolio.index[i], 'Close']
        btc_to_sell = portfolio.at[portfolio.index[i], 'BTC_Held']
        portfolio.at[portfolio.index[i], 'Cash'] += btc_to_sell * sell_price * (1 - TRADING_FEE)
        portfolio.at[portfolio.index[i], 'BTC_Held'] = 0.0
        print(f"Sell on {portfolio.index[i]} at price {sell_price:.2f} USDT, BTC Sold: {btc_to_sell:.6f}")
        
    # Update total portfolio value
    portfolio.at[portfolio.index[i], 'Total'] = (
        portfolio.at[portfolio.index[i], 'Cash'] +
        portfolio.at[portfolio.index[i], 'BTC_Held'] * portfolio.at[portfolio.index[i], 'Close']
    )

# Forward Fill any remaining data
portfolio['Total'].ffill(inplace=True)

# ----------------------------- Performance Evaluation -----------------------------

# Calculate returns
portfolio['Returns'] = portfolio['Total'].pct_change()

# Calculate cumulative returns
portfolio['Cumulative Returns'] = (1 + portfolio['Returns']).cumprod()

# Calculate Buy and Hold strategy for comparison
initial_price = data['Close'].iloc[0]
portfolio['Buy_Hold'] = INITIAL_CAPITAL * (data['Close'] / initial_price)

# ----------------------------- Visualization -----------------------------

plt.figure(figsize=(16, 12))

# Plot Price and Moving Averages with Signals
plt.subplot(3, 1, 1)
plt.plot(data['Close'], label='BTC/USDT Price', color='blue', alpha=0.5)
plt.plot(data['SMA_short'], label=f'SMA {SMA_SHORT_PERIOD} Hrs', color='green', alpha=0.9)
plt.plot(data['SMA_long'], label=f'SMA {SMA_LONG_PERIOD} Hrs', color='red', alpha=0.9)

# Plot Buy Signals
buy_signals = data[(data['Position'] == 2)]
plt.scatter(buy_signals.index, data['SMA_short'][buy_signals.index], marker='^', color='g', label='Buy Signal', s=100)

# Plot Sell Signals
sell_signals = data[(data['Position'] == -2)]
plt.scatter(sell_signals.index, data['SMA_short'][sell_signals.index], marker='v', color='r', label='Sell Signal', s=100)

plt.title('BTC/USDT Price with Moving Average Crossover Signals (1-Hour Timeframe)')
plt.xlabel('Date')
plt.ylabel('Price (USDT)')
plt.legend()
plt.grid(True)

# Plot Portfolio Value Over Time
plt.subplot(3, 1, 2)
plt.plot(portfolio['Total'], label='Moving Average Strategy', color='purple')
plt.plot(portfolio['Buy_Hold'], label='Buy & Hold', color='orange')
plt.title('Portfolio Value Over Time')
plt.xlabel('Date')
plt.ylabel('Portfolio Value (USDT)')
plt.legend()
plt.grid(True)

# Plot Cumulative Returns
plt.subplot(3, 1, 3)
plt.plot(portfolio['Cumulative Returns'], label='Strategy Cumulative Returns', color='purple')
plt.plot((portfolio['Buy_Hold'] / INITIAL_CAPITAL), label='Buy & Hold Cumulative Returns', color='orange')
plt.title('Cumulative Returns')
plt.xlabel('Date')
plt.ylabel('Cumulative Returns')
plt.legend()
plt.grid(True)

plt.tight_layout()
plt.show()

# ----------------------------- Performance Metrics -----------------------------

final_portfolio_value = portfolio['Total'].iloc[-1]
total_return = (final_portfolio_value / INITIAL_CAPITAL - 1) * 100

# Buy and Hold metrics
final_buy_hold = portfolio['Buy_Hold'].iloc[-1]
buy_hold_return = (final_buy_hold / INITIAL_CAPITAL - 1) * 100

# Calculate number of trades
num_buys = len(portfolio[portfolio['Position'] == 2])
num_sells = len(portfolio[portfolio['Position'] == -2])

# Print summary
print("\n--- Strategy Performance ---")
print(f"Initial Capital: ${INITIAL_CAPITAL:,.2f}")
print(f"Final Portfolio Value (Strategy): ${final_portfolio_value:,.2f}")
print(f"Total Return (Strategy): {total_return:.2f}%")
print(f"Final Portfolio Value (Buy & Hold): ${final_buy_hold:,.2f}")
print(f"Total Return (Buy & Hold): {buy_hold_return:.2f}%")
print(f"Number of Buy Signals Executed: {num_buys}")
print(f"Number of Sell Signals Executed: {num_sells}")

# Optional: Save the portfolio to a CSV file
# portfolio.to_csv('moving_average_crossover_portfolio_1H.csv')