forked from arch_enjoyer/crypt_bot
176 lines
5.7 KiB
Python
176 lines
5.7 KiB
Python
import ccxt
|
|
import time
|
|
import pandas as pd
|
|
import numpy as np
|
|
import ta
|
|
import matplotlib.pyplot as plt
|
|
import os
|
|
from dotenv import load_dotenv
|
|
load_dotenv()
|
|
|
|
# Connect to Binance exchange
|
|
exchange = ccxt.binance({
|
|
'apiKey': os.getenv("API_KEY"),
|
|
'secret': os.getenv("API_SECRET"),
|
|
})
|
|
|
|
# Fetch historical data with pagination
|
|
def fetch_full_ohlcv(symbol, timeframe, since):
|
|
all_data = []
|
|
while True:
|
|
try:
|
|
data = exchange.fetch_ohlcv(symbol, timeframe, since=since, limit=1000)
|
|
print(f"Fetched {len(data)} records from {exchange.iso8601(since)}")
|
|
if not data:
|
|
break
|
|
all_data.extend(data)
|
|
since = data[-1][0] + 1
|
|
time.sleep(0.2)
|
|
except Exception as e:
|
|
print(f"Error fetching data: {e}")
|
|
break
|
|
return all_data
|
|
|
|
# Fetch historical data
|
|
timeframe = '1m'
|
|
symbol = 'BTC/USDC'
|
|
since = exchange.parse8601('2025-04-01T00:00:00Z')
|
|
historical_data = fetch_full_ohlcv(symbol, timeframe, since)
|
|
|
|
|
|
|
|
|
|
|
|
# Load historical data into DataFrame
|
|
df = pd.DataFrame(historical_data, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
|
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
|
|
|
|
virtual_balance = 50
|
|
open_trades = []
|
|
trade_log = []
|
|
|
|
def calculate_indicators(df):
|
|
df['rsi'] = ta.momentum.RSIIndicator(df['close'], window=14).rsi()
|
|
macd = ta.trend.MACD(df['close'])
|
|
df['macd'] = macd.macd()
|
|
df['signal'] = macd.macd_signal()
|
|
return df
|
|
|
|
df = calculate_indicators(df)
|
|
|
|
RISK_PER_TRADE = 0.3
|
|
STOP_LOSS = 1 # 100%
|
|
TAKE_PROFIT = 0.03 # 5%
|
|
|
|
|
|
# Open new trades with investment tracking
|
|
for index, row in df.iterrows():
|
|
current_price = row['close']
|
|
|
|
# Close existing trades
|
|
for trade in open_trades[:]:
|
|
if current_price >= trade['entry_price'] * (1 + TAKE_PROFIT) or \
|
|
current_price <= trade['entry_price'] * (1 - STOP_LOSS):
|
|
open_trades.remove(trade)
|
|
profit_pct = (current_price / trade['entry_price'] - 1)
|
|
trade_log.append({
|
|
'entry_time': trade['entry_time'],
|
|
'exit_time': row['timestamp'],
|
|
'profit': profit_pct * 100,
|
|
'success': profit_pct > 0,
|
|
'investment_usd': trade['size']
|
|
})
|
|
virtual_balance += trade['size'] * profit_pct
|
|
|
|
# Calculate available capital for new trades
|
|
allocated_capital = sum(trade['size'] for trade in open_trades)
|
|
available_capital = max(0, virtual_balance - allocated_capital)
|
|
|
|
# Open new trades with capital check
|
|
if available_capital > 0 and len(open_trades) < 15:
|
|
if row['rsi'] < 30 and row['macd'] > row['signal']:
|
|
position_size = min(
|
|
RISK_PER_TRADE * virtual_balance, # Normal risk amount
|
|
available_capital # Never exceed remaining balance
|
|
)
|
|
open_trades.append({
|
|
'entry_time': row['timestamp'],
|
|
'entry_price': current_price,
|
|
'size': position_size,
|
|
'investment_usd': position_size # Track investment in USD
|
|
})
|
|
|
|
|
|
print(open_trades)
|
|
# Calculate Performance Metrics
|
|
total_trades = len(trade_log)
|
|
winning_trades = sum(1 for trade in trade_log if trade['success'])
|
|
win_rate = (winning_trades / total_trades) * 100 if total_trades > 0 else 0
|
|
total_return = ((virtual_balance / 50) - 1) * 100
|
|
returns = [trade['profit'] for trade in trade_log]
|
|
sharpe_ratio = np.mean(returns) / np.std(returns) if len(returns) > 1 else 0
|
|
|
|
# Print Results
|
|
print(f"""
|
|
Backtest Results:
|
|
- Starting Capital: $50 USDC
|
|
- Ending Balance: ${virtual_balance:.2f} USDC
|
|
- Total Return: {total_return:.2f}%
|
|
- Win Rate: {win_rate:.2f}%
|
|
- Trades Executed: {total_trades}
|
|
- Sharpe Ratio: {sharpe_ratio:.2f}
|
|
""")
|
|
|
|
|
|
|
|
# Visualization
|
|
plt.figure(figsize=(16, 12))
|
|
|
|
# Price Chart
|
|
ax1 = plt.subplot(3, 1, 1)
|
|
plt.plot(df['timestamp'], df['close'], label='Price', color='black')
|
|
for trade in trade_log:
|
|
entry_idx = df[df['timestamp'] == trade['entry_time']].index[0]
|
|
exit_idx = df[df['timestamp'] == trade['exit_time']].index[0]
|
|
plt.plot([trade['entry_time'], trade['exit_time']],
|
|
[df.loc[entry_idx, 'close'], df.loc[exit_idx, 'close']],
|
|
color='green' if trade['success'] else 'red', alpha=0.3)
|
|
plt.scatter(trade['entry_time'], df.loc[entry_idx, 'close'],
|
|
marker='^', color='green', s=100)
|
|
plt.scatter(trade['exit_time'], df.loc[exit_idx, 'close'],
|
|
marker='v', color='red', s=100)
|
|
|
|
# Annotate investment amount
|
|
plt.text(trade['entry_time'], df.loc[entry_idx, 'close'],
|
|
f"${trade['investment_usd']:.2f}", fontsize=8, color='blue')
|
|
|
|
plt.title('Trading Strategy Performance')
|
|
plt.ylabel('Price')
|
|
|
|
# Plot open trades with orange and annotate investments
|
|
for trade in open_trades:
|
|
entry_idx = df[df['timestamp'] == trade['entry_time']].index[0]
|
|
plt.plot([trade['entry_time'], df['timestamp'].iloc[-1]],
|
|
[df.loc[entry_idx, 'close'], df.iloc[-1]['close']],
|
|
color='orange', alpha=0.3)
|
|
plt.scatter(trade['entry_time'], df.loc[entry_idx, 'close'],
|
|
marker='^', color='orange', s=100)
|
|
|
|
# Annotate investment amount for open trades
|
|
plt.text(trade['entry_time'], df.loc[entry_idx, 'close'],
|
|
f"${trade['investment_usd']:.2f}", fontsize=8, color='blue')
|
|
|
|
# Indicators
|
|
plt.subplot(3, 1, 2, sharex=ax1)
|
|
plt.plot(df['timestamp'], df['rsi'], label='RSI', color='purple')
|
|
plt.axhline(30, linestyle='--', color='green')
|
|
plt.axhline(70, linestyle='--', color='red')
|
|
plt.ylabel('RSI')
|
|
|
|
plt.subplot(3, 1, 3, sharex=ax1)
|
|
plt.plot(df['timestamp'], df['macd'], label='MACD', color='blue')
|
|
plt.plot(df['timestamp'], df['signal'], label='Signal', color='orange')
|
|
plt.ylabel('MACD')
|
|
|
|
plt.tight_layout()
|
|
plt.show() |