Repository navigation
Expand file tree
/
Copy pathbacktester.py
More file actions
44 lines (41 loc) · 1.87 KB
/
Copy pathbacktester.py
File metadata and controls
44 lines (41 loc) · 1.87 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
import numpy as np
import pandas as pd
class Backtester:
def __init__(self, data, strategy, initial_cash=100000, transaction_cost=0.001):
self.data = data.copy()
self.strategy = strategy
self.initial_cash = initial_cash
self.transaction_cost = transaction_cost
self.results = None
def run(self):
signals = self.strategy.generate_signals(self.data)
self.data['Signal'] = signals
self.data['Position'] = self.data['Signal'].shift().fillna(0)
self.data['Returns'] = self.data['Close'].pct_change().fillna(0)
self.data['Strategy_Returns'] = self.data['Returns'] * self.data['Position']
# Transaction costs
trades = self.data['Position'].diff().abs()
self.data['Strategy_Returns'] -= trades * self.transaction_cost
# Portfolio value
self.data['Equity'] = self.initial_cash * (1 + self.data['Strategy_Returns']).cumprod()
self.results = self.data
return self.data
def metrics(self, risk_free_rate=0.0):
df = self.results if self.results is not None else self.run()
total_return = df['Equity'].iloc[-1] / self.initial_cash - 1
returns = df['Strategy_Returns']
sharpe = (returns.mean() - risk_free_rate/252) / (returns.std() + 1e-8) * np.sqrt(252)
equity = df['Equity']
roll_max = equity.cummax()
drawdown = (equity - roll_max) / roll_max
max_drawdown = drawdown.min()
trades = df['Position'].diff().abs()
wins = ((returns > 0) & (trades > 0)).sum()
total_trades = (trades > 0).sum()
win_rate = wins / total_trades if total_trades > 0 else np.nan
return {
'Total Return': total_return,
'Sharpe Ratio': sharpe,
'Max Drawdown': max_drawdown,
'Win Rate': win_rate
}