232 lines
6.8 KiB
Python
232 lines
6.8 KiB
Python
"""
|
|
Paper Trading Runner
|
|
Run live paper trading with real-time data
|
|
"""
|
|
import sys
|
|
import os
|
|
import time
|
|
import signal
|
|
|
|
# Add project root to path (go up 3 levels: scripts -> crypto_trading -> TradingAgents)
|
|
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
sys.path.insert(0, project_root)
|
|
|
|
from crypto_trading.src.paper_trading.paper_trading_engine import PaperTradingEngine, OrderSide
|
|
|
|
|
|
# ============================================================================
|
|
# EXAMPLE STRATEGIES
|
|
# ============================================================================
|
|
|
|
class SimpleMovingAverageStrategy:
|
|
"""Simple moving average crossover for paper trading."""
|
|
|
|
def __init__(self, short_window=20, long_window=50):
|
|
self.short_window = short_window
|
|
self.long_window = long_window
|
|
self.price_history = {}
|
|
|
|
def __call__(self, engine, symbol, current_price):
|
|
"""Execute strategy logic."""
|
|
# Initialize price history for symbol
|
|
if symbol not in self.price_history:
|
|
self.price_history[symbol] = []
|
|
|
|
# Add current price
|
|
self.price_history[symbol].append(current_price)
|
|
|
|
# Keep only needed history
|
|
if len(self.price_history[symbol]) > self.long_window:
|
|
self.price_history[symbol] = self.price_history[symbol][-self.long_window:]
|
|
|
|
# Need enough data
|
|
if len(self.price_history[symbol]) < self.long_window:
|
|
return None
|
|
|
|
# Calculate moving averages
|
|
prices = self.price_history[symbol]
|
|
short_ma = sum(prices[-self.short_window:]) / self.short_window
|
|
long_ma = sum(prices[-self.long_window:]) / self.long_window
|
|
|
|
# Golden cross - buy signal
|
|
if short_ma > long_ma and symbol not in engine.positions:
|
|
return OrderSide.BUY
|
|
|
|
# Death cross - sell signal
|
|
elif short_ma < long_ma and symbol in engine.positions:
|
|
return OrderSide.SELL
|
|
|
|
return None
|
|
|
|
|
|
class MomentumStrategy:
|
|
"""Momentum-based strategy."""
|
|
|
|
def __init__(self, lookback=10, threshold=0.05):
|
|
self.lookback = lookback
|
|
self.threshold = threshold
|
|
self.price_history = {}
|
|
|
|
def __call__(self, engine, symbol, current_price):
|
|
"""Execute strategy logic."""
|
|
# Initialize
|
|
if symbol not in self.price_history:
|
|
self.price_history[symbol] = []
|
|
|
|
self.price_history[symbol].append(current_price)
|
|
|
|
if len(self.price_history[symbol]) > self.lookback + 1:
|
|
self.price_history[symbol] = self.price_history[symbol][-(self.lookback + 1):]
|
|
|
|
if len(self.price_history[symbol]) < self.lookback:
|
|
return None
|
|
|
|
# Calculate momentum
|
|
momentum = (self.price_history[symbol][-1] - self.price_history[symbol][-self.lookback]) / self.price_history[symbol][-self.lookback]
|
|
|
|
# Strong momentum - buy
|
|
if momentum > self.threshold and symbol not in engine.positions:
|
|
return OrderSide.BUY
|
|
|
|
# Momentum reversal - sell
|
|
elif momentum < -self.threshold and symbol in engine.positions:
|
|
return OrderSide.SELL
|
|
|
|
return None
|
|
|
|
|
|
class RSIStrategy:
|
|
"""RSI mean reversion strategy."""
|
|
|
|
def __init__(self, period=14, oversold=30, overbought=70):
|
|
self.period = period
|
|
self.oversold = oversold
|
|
self.overbought = overbought
|
|
self.price_history = {}
|
|
|
|
def calculate_rsi(self, prices):
|
|
"""Calculate RSI."""
|
|
if len(prices) < self.period + 1:
|
|
return 50
|
|
|
|
gains = []
|
|
losses = []
|
|
|
|
for i in range(1, self.period + 1):
|
|
change = prices[-i] - prices[-i-1]
|
|
if change > 0:
|
|
gains.append(change)
|
|
losses.append(0)
|
|
else:
|
|
gains.append(0)
|
|
losses.append(abs(change))
|
|
|
|
avg_gain = sum(gains) / self.period
|
|
avg_loss = sum(losses) / self.period
|
|
|
|
if avg_loss == 0:
|
|
return 100
|
|
|
|
rs = avg_gain / avg_loss
|
|
rsi = 100 - (100 / (1 + rs))
|
|
|
|
return rsi
|
|
|
|
def __call__(self, engine, symbol, current_price):
|
|
"""Execute strategy logic."""
|
|
if symbol not in self.price_history:
|
|
self.price_history[symbol] = []
|
|
|
|
self.price_history[symbol].append(current_price)
|
|
|
|
if len(self.price_history[symbol]) > self.period + 10:
|
|
self.price_history[symbol] = self.price_history[symbol][-(self.period + 10):]
|
|
|
|
rsi = self.calculate_rsi(self.price_history[symbol])
|
|
|
|
# Oversold - buy
|
|
if rsi < self.oversold and symbol not in engine.positions:
|
|
return OrderSide.BUY
|
|
|
|
# Overbought - sell
|
|
elif rsi > self.overbought and symbol in engine.positions:
|
|
return OrderSide.SELL
|
|
|
|
return None
|
|
|
|
|
|
# ============================================================================
|
|
# MAIN
|
|
# ============================================================================
|
|
|
|
def main():
|
|
"""Run paper trading."""
|
|
print("\n" + "="*80)
|
|
print(" CRYPTO PAPER TRADING - LIVE SIMULATION")
|
|
print("="*80)
|
|
print("\nThis runs live paper trading with real-time market data.")
|
|
print("No real money is at risk - this is a simulation.\n")
|
|
|
|
print("Requirements:")
|
|
print(" ✓ CCXT installed (pip install ccxt)")
|
|
print(" ✓ Internet connection")
|
|
print(" ✓ Press Ctrl+C to stop gracefully\n")
|
|
|
|
# Configuration
|
|
print("Configuration:")
|
|
print(" Exchange: Binance")
|
|
print(" Symbols: BTC/USDT, ETH/USDT")
|
|
print(" Initial Capital: $10,000")
|
|
print(" Update Interval: 60 seconds")
|
|
print(" Strategy: MA Crossover (20/50)")
|
|
print()
|
|
|
|
input("Press Enter to start paper trading...")
|
|
|
|
# Create engine
|
|
engine = PaperTradingEngine(
|
|
exchange_id='binance',
|
|
initial_capital=10000,
|
|
commission_rate=0.001,
|
|
max_position_size=0.20,
|
|
max_daily_loss=0.05,
|
|
stop_loss_pct=0.15,
|
|
take_profit_pct=0.30,
|
|
update_interval=60 # 1 minute updates
|
|
)
|
|
|
|
# Set strategy
|
|
strategy = SimpleMovingAverageStrategy(short_window=20, long_window=50)
|
|
engine.set_strategy(strategy)
|
|
|
|
# Handle graceful shutdown
|
|
def signal_handler(sig, frame):
|
|
print("\n\nReceived interrupt signal...")
|
|
engine.stop()
|
|
sys.exit(0)
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
# Start paper trading
|
|
symbols = ['BTC/USDT', 'ETH/USDT']
|
|
engine.start(symbols)
|
|
|
|
# Keep main thread alive
|
|
try:
|
|
while engine.is_running:
|
|
time.sleep(1)
|
|
except KeyboardInterrupt:
|
|
engine.stop()
|
|
|
|
print("\nPaper trading stopped.")
|
|
print("Data saved to: ./paper_trading_data/\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except Exception as e:
|
|
print(f"\nError: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|