-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathlearning_strategy.py
More file actions
84 lines (63 loc) · 2.86 KB
/
Copy pathlearning_strategy.py
File metadata and controls
84 lines (63 loc) · 2.86 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
"""
Example Usage of LearningBacktester
This example demonstrates how to create a simple moving average crossover strategy
using the LearningBacktester framework.
Steps:
1. Define a strategy class that inherits from LearningBacktester
2. Implement the required abstract methods
3. Create an instance of the strategy
4. Run the backtest simulation
"""
import pandas as pd
import numpy as np
import asyncio
from quantjourney.backtesting.learning_backtester import LearningBacktester
from quantjourney.indicators.technical_indicators import TechnicalIndicators
from quantjourney.data.utils.data_logs import data_logger
logger = data_logger()
class LearningSimpleMovingAverageCrossover(LearningBacktester):
def __init__(self, config_file_path: str, **kwargs):
super().__init__(config_file_path, **kwargs)
self.ti = TechnicalIndicators()
self.sma_short_window = kwargs.get('sma_short_window', 50)
self.sma_long_window = kwargs.get('sma_long_window', 200)
def _generate_signals(self) -> pd.DataFrame:
sma_short = self.instruments_data.get_feature(f'SMA_{self.sma_short_window}')
sma_long = self.instruments_data.get_feature(f'SMA_{self.sma_long_window}')
# Create a mask for valid data points
valid_data = sma_short.notna() & sma_long.notna()
signals = pd.DataFrame(0, index=sma_short.index, columns=sma_short.columns)
signals[valid_data & (sma_short > sma_long)] = 1 # Buy signals
signals[valid_data & (sma_short < sma_long)] = -1 # Sell signals
# Forward fill only after the first valid signal for each instrument
first_valid = signals.abs().idxmax()
for col in signals.columns:
signals[col] = signals[col].loc[first_valid[col]:].fillna(method='ffill')
signals = signals.fillna(0) # Fill remaining NaNs with 0
if signals.empty:
logger.error("Signal generation failed.")
return pd.DataFrame()
logger.info(f"Generated signals for {len(signals.columns)} instruments.")
return signals
def _generate_positions(self, signals: pd.DataFrame) -> pd.DataFrame:
# Simple implementation: directly use signals as positions
return signals
def _generate_weights(self, positions: pd.DataFrame) -> pd.DataFrame:
# Equal weighting for all active positions
weights = positions.astype(bool).astype(float)
return weights.div(weights.sum(axis=1), axis=0).fillna(0)
async def run_backtest():
config = {
'backtest_name': 'Simple Moving Average Crossover',
'instruments': ['AAPL', 'GOOGL', 'MSFT'],
'initial_capital': 100000,
'trading_range': {'start': '2020-01-01', 'end': '2021-12-31'},
'indicators_config': [
{'function': 'SMA', 'price_cols': ['close'], 'params': {'window': 50}},
{'function': 'SMA', 'price_cols': ['close'], 'params': {'window': 200}}
]
}
strategy = LearningSimpleMovingAverageCrossover('quantjourney/backtesting/default_strategy.json', **config)
await strategy.run_simulation()
if __name__ == '__main__':
asyncio.run(run_backtest())