-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
295 lines (234 loc) · 9.22 KB
/
main.py
File metadata and controls
295 lines (234 loc) · 9.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
"""
Main runner for quantitative trading strategies.
Integrates quantStrat strategies with the framework's execution engine.
Directory structure:
home/
├── framework/ # Framework code
├── quantStrat/ # Strategies
└── main.py
"""
import sys
from pathlib import Path
# Add both directories to Python path
PROJECT_ROOT = Path(__file__).parent
FRAMEWORK_PATH = PROJECT_ROOT / 'framework'
QUANT_STRAT_PATH = PROJECT_ROOT / 'quantStrat'
sys.path.insert(0, str(FRAMEWORK_PATH))
sys.path.insert(0, str(QUANT_STRAT_PATH))
import pandas as pd
from typing import Optional
# Framework imports
from strategy_base import Strategy
from config import IBConfig, TradingMode
from test_strat import (
run_simulated_backtest,
run_ibkr_backtest,
run_live_trading
)
# QuantStrat imports
from moving_average_strategy import MovingAverageCrossover
from mean_reversion_strategy import MeanReversion
from momentum_strategy import TrendMomentum
from strategy_manager import StrategyManager
class FrameworkAdapter(Strategy):
"""
Adapter to make quantStrat strategies compatible with framework.
Converts TradingSignal objects to BUY/SELL/HOLD strings.
"""
def __init__(self, quant_strategy, name: Optional[str] = None):
super().__init__(name or quant_strategy.name)
self.quant_strategy = quant_strategy
self.long_window = self._extract_long_window()
def _extract_long_window(self) -> int:
"""Extract the longest lookback period from the strategy"""
strategy = self.quant_strategy
# Check for common window attributes
if hasattr(strategy, 'long_window'):
return strategy.long_window
elif hasattr(strategy, 'window'):
return strategy.window
elif hasattr(strategy, 'rsi_period'):
return max(
getattr(strategy, 'rsi_period', 14),
getattr(strategy, 'macd_slow', 26)
)
return 30 # Default fallback
def on_data(self, symbol: str, data: pd.DataFrame):
"""Process new market data"""
return self.generate_signals(symbol, data)
def generate_signals(self, symbol: str, data: pd.DataFrame) -> str:
"""Generate trading signals using the quantStrat strategy"""
try:
signal = self.quant_strategy.generate_signal(data)
if signal is None:
return 'HOLD'
# Convert SignalType enum to string
return signal.signal.name # Returns 'BUY', 'SELL', or 'HOLD'
except Exception as e:
print(f"⚠️ Error generating signal: {e}")
return 'HOLD'
class ConsensusAdapter(Strategy):
"""
Adapter for using StrategyManager's consensus signal.
Combines multiple strategies with weighted voting.
"""
def __init__(self, strategy_manager: StrategyManager, min_confidence: float = 0.6):
super().__init__("Consensus_Strategy")
self.strategy_manager = strategy_manager
self.min_confidence = min_confidence
# Get the longest window from all strategies
self.long_window = max(
(self._get_window(s) for s in strategy_manager.strategies),
default=30
)
def _get_window(self, strategy) -> int:
"""Helper to extract window from any strategy"""
for attr in ['long_window', 'window', 'rsi_period']:
if hasattr(strategy, attr):
return getattr(strategy, attr)
return 30
def on_data(self, symbol: str, data: pd.DataFrame):
return self.generate_signals(symbol, data)
def generate_signals(self, symbol: str, data: pd.DataFrame) -> str:
"""Generate consensus signal from multiple strategies"""
try:
consensus = self.strategy_manager.get_consensus_signal(data)
if consensus is None:
return 'HOLD'
# Only act on high-confidence signals
if consensus.confidence < self.min_confidence:
return 'HOLD'
return consensus.signal.name # 'BUY', 'SELL', or 'HOLD'
except Exception as e:
print(f"⚠️ Error generating consensus: {e}")
return 'HOLD'
def print_menu():
"""Display strategy and mode selection menu"""
print("\n" + "=" * 70)
print("QUANTITATIVE TRADING SYSTEM")
print("=" * 70)
print("\n📊 AVAILABLE STRATEGIES:")
print(" 1. Moving Average Crossover (MA 10/30)")
print(" 2. Mean Reversion (Bollinger Bands 20, 2σ)")
print(" 3. Trend Momentum (RSI 14 + MACD 12/26/9)")
print(" 4. Consensus Strategy (Combines all above with confidence voting)")
print("\n🎯 TRADING MODES:")
print(" Backtest:")
print(" 1. Simulated Backtest (fast, synthetic data)")
print(" 2. IBKR Backtest (real historical data)")
print(" Trading:")
print(" 3. Paper Trading (live simulation)")
print(" 4. Live Trading (⚠️ real money)")
print("=" * 70 + "\n")
def get_strategy_choice() -> Strategy:
"""Prompt user to select a strategy"""
print("Select Strategy:")
print(" [1] Moving Average Crossover")
print(" [2] Mean Reversion")
print(" [3] Trend Momentum")
print(" [4] Consensus (All strategies)")
choice = input("\nEnter choice (1-4): ").strip()
if choice == '1':
print("📈 Using Moving Average Crossover (10/30)")
quant_strat = MovingAverageCrossover(short_window=10, long_window=30)
return FrameworkAdapter(quant_strat)
elif choice == '2':
print("📊 Using Mean Reversion (BB 20, 2σ)")
quant_strat = MeanReversion(window=20, num_std=2.0)
return FrameworkAdapter(quant_strat)
elif choice == '3':
print("🚀 Using Trend Momentum (RSI + MACD)")
quant_strat = TrendMomentum(rsi_period=14)
return FrameworkAdapter(quant_strat)
elif choice == '4':
print("🎯 Using Consensus Strategy")
manager = StrategyManager()
manager.add_strategy(MovingAverageCrossover(10, 30))
manager.add_strategy(MeanReversion(20, 2.0))
manager.add_strategy(TrendMomentum(14))
return ConsensusAdapter(manager, min_confidence=0.6)
else:
print("Invalid choice. Defaulting to Moving Average Crossover.")
quant_strat = MovingAverageCrossover(short_window=10, long_window=30)
return FrameworkAdapter(quant_strat)
def get_mode_choice() -> int:
"""Prompt user to select trading mode"""
print("\nSelect Trading Mode:")
print(" [1] Simulated Backtest")
print(" [2] IBKR Backtest")
print(" [3] Paper Trading")
print(" [4] Live Trading")
choice = input("\nEnter choice (1-4): ").strip()
try:
mode = int(choice)
if mode not in [1, 2, 3, 4]:
raise ValueError()
return mode
except:
print("Invalid choice. Defaulting to Simulated Backtest.")
return 1
def get_symbols() -> list:
"""Prompt user for trading symbols"""
symbol_input = input("\nEnter symbol(s) (comma-separated, default: AAPL): ").strip()
if symbol_input:
symbols = [s.strip().upper() for s in symbol_input.split(',')]
else:
symbols = ['AAPL']
print(f"✓ Trading symbols: {', '.join(symbols)}")
return symbols
def main():
"""Main entry point"""
print_menu()
# Get user selections
strategy = get_strategy_choice()
mode = get_mode_choice()
symbols = get_symbols()
# Initialize config
config = IBConfig()
config.load_from_env_file(script_dir=FRAMEWORK_PATH)
# Execute based on mode
if mode == 1:
# Mode 1: Simulated Backtest
print("\n🔬 Running Simulated Backtest...")
run_simulated_backtest(
strategy,
symbols,
periods=200,
start_date='2024-01-01'
)
elif mode == 2:
# Mode 2: IBKR Backtest
print("\n📊 Running IBKR Backtest...")
print("⚠️ This will start IB Gateway to fetch historical data")
confirm = input("Continue? (y/n): ").strip().lower()
if confirm == 'y':
run_ibkr_backtest(
strategy,
symbols,
config,
duration='6 M',
bar_size='1 day'
)
else:
print("Cancelled")
elif mode == 3:
# Mode 3: Paper Trading
print("\n📄 Starting Paper Trading...")
run_live_trading(strategy, symbols, config, TradingMode.PAPER)
elif mode == 4:
# Mode 4: Live Trading
print("\n⚠️ WARNING: LIVE TRADING WITH REAL MONEY ⚠️")
confirm = input("Type 'YES' to confirm: ").strip()
if confirm == 'YES':
run_live_trading(strategy, symbols, config, TradingMode.LIVE)
else:
print("Cancelled - Live trading not confirmed")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n👋 Exiting...")
except Exception as e:
print(f"\n❌ Error: {e}")
import traceback
traceback.print_exc()