Backtesting.py 사용법
backtesting.py -1
유투브 참고
https://www.youtube.com/watch?v=e4ytbIm2Xg0&ab_channel=ChadThackray
https://kernc.github.io/backtesting.py/doc/backtesting/backtesting.html#backtesting.backtesting.Order.limit
import pandas as pd
import numpy as np
import seaborn as sns
from datetime import datetime
import matplotlib.pyplot as plt
ticker = 'KODEX 200'
start_date = datetime(2010, 1, 1)
df = pd.read_pickle("../crawling/data/etf_price.pickle")
df.columns = ['NAV','Open','High','Low','Close','Volume','Amount','base','ticker','code','Date']
df['Date'] = pd.to_datetime(df['Date'])
cond = (df['ticker']==ticker) & (df['Date']>start_date)
data = df[cond].reset_index(drop=True)
data = data[['Date','Open','High','Low','Close','Volume']].set_index('Date')
for col in data.columns:
data[col] = pd.to_numeric(data[col]).astype('float')
data.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 3396 entries, 2010-01-04 to 2023-10-06
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Open 3396 non-null float64
1 High 3396 non-null float64
2 Low 3396 non-null float64
3 Close 3396 non-null float64
4 Volume 3396 non-null float64
dtypes: float64(5)
memory usage: 159.2 KB
Strategy
- 전략을 설정하여 백테스트
- RSI 조건으로 매수, 매도
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
class MyStrategy(Strategy):
upper_bound = 67
lower_bound = 33
rsi_window=14
def init(self):
self.rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
if crossover(self.rsi, self.upper_bound):
self.position.close()
elif crossover(self.lower_bound, self.rsi):
self.buy()
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats = bt.run()
print(stats)
bt.plot()
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 47.290931
Equity Final [$] 11091864.24
Equity Peak [$] 17417832.28
Return [%] 10.918642
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 0.771926
Volatility (Ann.) [%] 13.341861
Sharpe Ratio 0.057857
Sortino Ratio 0.08228
Calmar Ratio 0.01836
Max. Drawdown [%] -42.044703
Avg. Drawdown [%] -3.154887
Max. Drawdown Duration 2031 days 00:00:00
Avg. Drawdown Duration 76 days 00:00:00
# Trades 20
Win Rate [%] 70.0
Best Trade [%] 11.105913
Worst Trade [%] -25.035937
Avg. Trade [%] 0.821925
Max. Trade Duration 450 days 00:00:00
Avg. Trade Duration 120 days 00:00:00
Profit Factor 1.551886
Expectancy [%] 1.124611
SQN 0.228145
_strategy MyStrategy
_equity_curve ...
_trades Size EntryB...
dtype: object
Optimizer
지표의 하이퍼파라미터를 최적화
- 파라미터 : RSI upper_bound, lower_bound, rsi_window
- 최적화 조건 설정 : optim_func
- 결과 저장 : bt.plot(filnename설정)
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
def optim_func(series):
if series['# Trades'] < 30: #최소 트레이드 회수 제한
return -1
return series['Equity Final [$]'] / series['Exposure Time [%]']
#return series['Sharpe Ratio']
class MyStrategy(Strategy):
upper_bound = 70
lower_bound = 30
rsi_window=14
def init(self):
self.rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
if crossover(self.rsi, self.upper_bound):
self.position.close()
elif crossover(self.lower_bound, self.rsi):
self.buy()
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats = bt.optimize(
upper_bound=range(50, 85, 1),
lower_bound=range(10, 45, 1),
rsi_window=range(10,30,1),
maximize = optim_func, #'Sharpe Ratio',
constraint = lambda param : param.upper_bound > param.lower_bound,
max_tries=100 # randomly simulations, 오버피팅 방지..
)
lower_bound = stats['_strategy'].lower_bound
upper_bound = stats['_strategy'].upper_bound
rsi_window = stats['_strategy'].rsi_window
print(lower_bound,"-", upper_bound,"-", rsi_window)
print(stats)
bt.plot()
#filename있으면 저장을합니다.
# bt.plot(filename=f'plots/{datetime.now().strftime("%Y%m%d")}_{lower_bound}_{upper_bound}.html')
/Users/byeongsikbu/opt/anaconda3/envs/dct/lib/python3.10/site-packages/backtesting/backtesting.py:1375: UserWarning: For multiprocessing support in `Backtest.optimize()` set multiprocessing start method to 'fork'.
warnings.warn("For multiprocessing support in `Backtest.optimize()` "
0%| | 0/9 [00:00<?, ?it/s]
36 - 53 - 16
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 25.912839
Equity Final [$] 12215340.55
Equity Peak [$] 12568343.2
Return [%] 22.153406
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 1.495976
Volatility (Ann.) [%] 11.227801
Sharpe Ratio 0.133239
Sortino Ratio 0.192884
Calmar Ratio 0.046011
Max. Drawdown [%] -32.513102
Avg. Drawdown [%] -3.777746
Max. Drawdown Duration 1934 days 00:00:00
Avg. Drawdown Duration 136 days 00:00:00
# Trades 39
Win Rate [%] 64.102564
Best Trade [%] 7.401241
Worst Trade [%] -9.27555
Avg. Trade [%] 0.854879
Max. Trade Duration 105 days 00:00:00
Avg. Trade Duration 33 days 00:00:00
Profit Factor 1.859875
Expectancy [%] 0.920323
SQN 0.928929
_strategy MyStrategy(upper...
_equity_curve ...
_trades Size EntryB...
dtype: object
display(stats['_strategy'])
display(stats['_trades'])
<Strategy MyStrategy(upper_bound=53,lower_bound=36,rsi_window=16)>
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 466 | 20 | 44 | 21427.77 | 22180.0 | 350539.18 | 0.035105 | 2010-02-01 | 2010-03-09 | 36 days |
1 | 474 | 87 | 110 | 21803.52 | 22150.0 | 164231.52 | 0.015891 | 2010-05-10 | 2010-06-14 | 35 days |
2 | 392 | 278 | 304 | 26813.52 | 27075.0 | 102500.16 | 0.009752 | 2011-02-14 | 2011-03-23 | 37 days |
3 | 1 | 432 | 444 | 22234.38 | 23880.0 | 1645.62 | 0.074012 | 2011-09-27 | 2011-10-14 | 17 days |
4 | 419 | 398 | 444 | 25280.46 | 23880.0 | -586792.74 | -0.055397 | 2011-08-05 | 2011-10-14 | 70 days |
5 | 1 | 604 | 614 | 23997.90 | 25500.0 | 1502.10 | 0.062593 | 2012-06-05 | 2012-06-20 | 15 days |
6 | 385 | 588 | 614 | 25991.88 | 25500.0 | -189373.80 | -0.018924 | 2012-05-11 | 2012-06-20 | 40 days |
7 | 413 | 631 | 643 | 23782.47 | 24550.0 | 316989.89 | 0.032273 | 2012-07-13 | 2012-07-31 | 18 days |
8 | 407 | 704 | 726 | 24919.74 | 25250.0 | 134415.82 | 0.013253 | 2012-10-29 | 2012-11-28 | 30 days |
9 | 395 | 803 | 808 | 26016.93 | 26600.0 | 230312.65 | 0.022411 | 2013-03-25 | 2013-04-01 | 7 days |
10 | 411 | 813 | 841 | 25601.10 | 25880.0 | 114627.90 | 0.010894 | 2013-04-08 | 2013-05-20 | 42 days |
11 | 427 | 858 | 887 | 24884.67 | 24620.0 | -113014.09 | -0.010636 | 2013-06-13 | 2013-07-24 | 41 days |
12 | 422 | 1016 | 1029 | 24909.72 | 25670.0 | 320838.16 | 0.030521 | 2014-02-05 | 2014-02-24 | 19 days |
13 | 425 | 1078 | 1084 | 25480.86 | 26160.0 | 288634.50 | 0.026653 | 2014-05-07 | 2014-05-15 | 8 days |
14 | 435 | 1177 | 1206 | 25571.04 | 25025.0 | -237527.40 | -0.021354 | 2014-10-01 | 2014-11-13 | 43 days |
15 | 442 | 1231 | 1237 | 24604.11 | 25035.0 | 190453.38 | 0.017513 | 2014-12-18 | 2014-12-29 | 11 days |
16 | 456 | 1242 | 1247 | 24288.48 | 24890.0 | 274293.12 | 0.024766 | 2015-01-07 | 2015-01-14 | 7 days |
17 | 446 | 1342 | 1416 | 25455.81 | 24085.0 | -611381.26 | -0.053851 | 2015-06-04 | 2015-09-17 | 105 days |
18 | 443 | 1456 | 1462 | 24243.39 | 24740.0 | 219998.23 | 0.020484 | 2015-11-17 | 2015-11-25 | 8 days |
19 | 478 | 1500 | 1518 | 22915.74 | 23770.0 | 408336.28 | 0.037278 | 2016-01-21 | 2016-02-19 | 29 days |
20 | 474 | 1575 | 1590 | 23982.87 | 24435.0 | 214309.62 | 0.018852 | 2016-05-16 | 2016-06-07 | 22 days |
21 | 379 | 1885 | 1908 | 30571.02 | 31190.0 | 234593.42 | 0.020247 | 2017-08-14 | 2017-09-15 | 32 days |
22 | 368 | 1972 | 1977 | 32129.13 | 33070.0 | 346240.16 | 0.029284 | 2017-12-22 | 2018-01-03 | 12 days |
23 | 386 | 2003 | 2023 | 31507.89 | 32460.0 | 367514.46 | 0.030218 | 2018-02-08 | 2018-03-13 | 33 days |
24 | 412 | 2089 | 2137 | 30380.64 | 29860.0 | -214503.68 | -0.017137 | 2018-06-20 | 2018-08-28 | 69 days |
25 | 444 | 2165 | 2202 | 27760.41 | 27525.0 | -104522.04 | -0.008480 | 2018-10-12 | 2018-12-04 | 53 days |
26 | 437 | 2305 | 2328 | 27945.78 | 27330.0 | -269095.86 | -0.022035 | 2019-05-09 | 2019-06-12 | 34 days |
27 | 455 | 2366 | 2388 | 26262.42 | 26250.0 | -5651.10 | -0.000473 | 2019-08-05 | 2019-09-05 | 31 days |
28 | 418 | 2502 | 2540 | 28536.96 | 25890.0 | -1106429.28 | -0.092756 | 2020-02-25 | 2020-04-20 | 55 days |
29 | 358 | 2672 | 2676 | 30270.42 | 32220.0 | 697949.64 | 0.064405 | 2020-11-02 | 2020-11-06 | 4 days |
30 | 276 | 2869 | 2934 | 41753.34 | 39885.0 | -515661.84 | -0.044747 | 2021-08-17 | 2021-11-23 | 98 days |
31 | 293 | 2940 | 2945 | 37620.09 | 39975.0 | 689988.63 | 0.062597 | 2021-12-01 | 2021-12-08 | 7 days |
32 | 311 | 2978 | 3021 | 37580.01 | 36925.0 | -203708.11 | -0.017430 | 2022-01-25 | 2022-04-01 | 66 days |
33 | 335 | 3047 | 3063 | 34363.59 | 35240.0 | 293597.35 | 0.025504 | 2022-05-10 | 2022-06-02 | 23 days |
34 | 363 | 3070 | 3103 | 32474.82 | 32500.0 | 9140.34 | 0.000775 | 2022-06-14 | 2022-07-29 | 45 days |
35 | 379 | 3131 | 3163 | 31147.17 | 29690.0 | -552267.43 | -0.046783 | 2022-09-08 | 2022-10-28 | 50 days |
36 | 381 | 3209 | 3214 | 29513.91 | 31345.0 | 697645.29 | 0.062042 | 2023-01-03 | 2023-01-10 | 7 days |
37 | 361 | 3365 | 3375 | 33050.97 | 34010.0 | 346209.83 | 0.029017 | 2023-08-21 | 2023-09-04 | 14 days |
38 | 381 | 3394 | 3395 | 32299.47 | 32060.0 | -91238.07 | -0.007414 | 2023-10-05 | 2023-10-06 | 1 days |
hemtmap
최적화 결과를 히트맵으로 표현하여, 어떤 파라미터가 유용했는지 시각적으로 확인가능
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
return series['Sharpe Ratio']
class MyStrategy(Strategy):
upper_bound = 70
lower_bound = 30
rsi_window=14
def init(self):
self.rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
if crossover(self.rsi, self.upper_bound):
self.position.close()
elif crossover(self.lower_bound, self.rsi):
self.buy()
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats, heatmap = bt.optimize(
upper_bound=range(55, 85, 5),
lower_bound=range(10, 45, 5),
rsi_window=14,
maximize = optim_func,#'Sharpe Ratio',
constraint = lambda param : param.upper_bound > param.lower_bound,
return_heatmap=True
)
#print(heatmap)
hm = heatmap.groupby(['upper_bound', 'lower_bound']).mean().unstack()
#print(hm)
sns.heatmap(hm, annot=True, cmap='viridis')
plt.show()
lower_bound = stats['_strategy'].lower_bound
upper_bound = stats['_strategy'].upper_bound
rsi_window = stats['_strategy'].rsi_window
print("best :", lower_bound,"-", upper_bound,"-", rsi_window)
print(stats)
bt.plot()
display(stats['_trades'])
0%| | 0/9 [00:00<?, ?it/s]
best : 15 - 80 - 14
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 16.872792
Equity Final [$] 21689992.91
Equity Peak [$] 21689992.91
Return [%] 116.899929
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 5.913693
Volatility (Ann.) [%] 7.728531
Sharpe Ratio 0.765177
Sortino Ratio 1.343684
Calmar Ratio 0.611046
Max. Drawdown [%] -9.67799
Avg. Drawdown [%] -1.620457
Max. Drawdown Duration 163 days 00:00:00
Avg. Drawdown Duration 16 days 00:00:00
# Trades 3
Win Rate [%] 100.0
Best Trade [%] 56.254118
Worst Trade [%] 13.836122
Avg. Trade [%] 29.464016
Max. Trade Duration 506 days 00:00:00
Avg. Trade Duration 280 days 00:00:00
Profit Factor NaN
Expectancy [%] 30.694484
SQN 1.966811
_strategy MyStrategy(upper...
_equity_curve ...
_trades Size EntryBa...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 413 | 1188 | 1316 | 24188.28 | 27535.0 | 1382195.36 | 0.138361 | 2014-10-20 | 2015-04-24 | 186 days |
1 | 510 | 1399 | 1741 | 22304.52 | 27210.0 | 2501794.80 | 0.219932 | 2015-08-25 | 2017-01-12 | 506 days |
2 | 665 | 2520 | 2620 | 20866.65 | 32605.0 | 7806002.75 | 0.562541 | 2020-03-20 | 2020-08-13 | 146 days |
자체 내장되어 있는 heatmap을 사용하는 것도 가능
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
return series['Sharpe Ratio']
class MyStrategy(Strategy):
upper_bound = 70
lower_bound = 30
rsi_window=14
def init(self):
self.rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
if crossover(self.rsi, self.upper_bound):
self.position.close()
elif crossover(self.lower_bound, self.rsi):
self.buy()
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats, heatmap = bt.optimize(
upper_bound=range(55, 85, 5),
lower_bound=range(10, 45, 5),
rsi_window=range(10, 20, 2),
maximize = optim_func,#'Sharpe Ratio',
constraint = lambda param : param.upper_bound > param.lower_bound,
return_heatmap=True
)
#print(heatmap)
hm = heatmap.groupby(['upper_bound', 'lower_bound']).mean().unstack()
#print(hm)
# sns.heatmap(hm, annot=True, cmap='viridis')
# plt.show()
plot_heatmaps(heatmap, agg='mean')
lower_bound = stats['_strategy'].lower_bound
upper_bound = stats['_strategy'].upper_bound
rsi_window = stats['_strategy'].rsi_window
print("best :", lower_bound,"-", upper_bound,"-", rsi_window)
print(stats)
bt.plot()
display(stats['_trades'])
0%| | 0/9 [00:00<?, ?it/s]
best : 15 - 80 - 14
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 16.872792
Equity Final [$] 21689992.91
Equity Peak [$] 21689992.91
Return [%] 116.899929
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 5.913693
Volatility (Ann.) [%] 7.728531
Sharpe Ratio 0.765177
Sortino Ratio 1.343684
Calmar Ratio 0.611046
Max. Drawdown [%] -9.67799
Avg. Drawdown [%] -1.620457
Max. Drawdown Duration 163 days 00:00:00
Avg. Drawdown Duration 16 days 00:00:00
# Trades 3
Win Rate [%] 100.0
Best Trade [%] 56.254118
Worst Trade [%] 13.836122
Avg. Trade [%] 29.464016
Max. Trade Duration 506 days 00:00:00
Avg. Trade Duration 280 days 00:00:00
Profit Factor NaN
Expectancy [%] 30.694484
SQN 1.966811
_strategy MyStrategy(upper...
_equity_curve ...
_trades Size EntryBa...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 413 | 1188 | 1316 | 24188.28 | 27535.0 | 1382195.36 | 0.138361 | 2014-10-20 | 2015-04-24 | 186 days |
1 | 510 | 1399 | 1741 | 22304.52 | 27210.0 | 2501794.80 | 0.219932 | 2015-08-25 | 2017-01-12 | 506 days |
2 | 665 | 2520 | 2620 | 20866.65 | 32605.0 | 7806002.75 | 0.562541 | 2020-03-20 | 2020-08-13 | 146 days |
multiple strategies
- 다른 시간대의 지표를 활용하는 전략
- 예를들어, rsi는 weekly, ma는 daily를 사용하는 전략
- resample_apply
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps, resample_apply
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
return series['Sharpe Ratio']
class MyStrategy(Strategy):
upper_bound = 70
lower_bound = 30
rsi_window=14
def init(self):
self.daily_rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
self.weekly_rsi = resample_apply(
"W-FRI", talib.RSI, self.data.Close, self.rsi_window)
def next(self):
if (crossover(self.daily_rsi, self.upper_bound)
and self.weekly_rsi[-1] > self.upper_bound):
self.position.close()
elif (crossover(self.lower_bound, self.daily_rsi)
and self.weekly_rsi[-1] < self.lower_bound
and self.daily_rsi[-1] < self.weekly_rsi[-1]):
self.buy()
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats = bt.run()
# stats, heatmap = bt.optimize(
# upper_bound=range(55, 85, 5),
# lower_bound=range(10, 45, 5),
# rsi_window=range(10, 20, 2),
# maximize = optim_func,#'Sharpe Ratio',
# constraint = lambda param : param.upper_bound > param.lower_bound,
# return_heatmap=True
# )
#print(heatmap)
#hm = heatmap.groupby(['upper_bound', 'lower_bound']).mean().unstack()
#print(hm)
# sns.heatmap(hm, annot=True, cmap='viridis')
# plt.show()
#plot_heatmaps(heatmap, agg='mean')
# lower_bound = stats['_strategy'].lower_bound
# upper_bound = stats['_strategy'].upper_bound
# rsi_window = stats['_strategy'].rsi_window
# print("best :", lower_bound,"-", upper_bound,"-", rsi_window)
print(stats)
bt.plot()
display(stats['_trades'])
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 47.055359
Equity Final [$] 13323664.95
Equity Peak [$] 14628839.95
Return [%] 33.236649
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 2.152193
Volatility (Ann.) [%] 10.862179
Sharpe Ratio 0.198136
Sortino Ratio 0.291684
Calmar Ratio 0.109967
Max. Drawdown [%] -19.571261
Avg. Drawdown [%] -3.781761
Max. Drawdown Duration 3859 days 00:00:00
Avg. Drawdown Duration 157 days 00:00:00
# Trades 2
Win Rate [%] 100.0
Best Trade [%] 21.092631
Worst Trade [%] 10.046459
Avg. Trade [%] 15.437495
Max. Trade Duration 1984 days 00:00:00
Avg. Trade Duration 1179 days 00:00:00
Profit Factor NaN
Expectancy [%] 15.569545
SQN 3.716169
_strategy MyStrategy
_equity_curve ...
_trades Size EntryBa...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 444 | 408 | 1751 | 22519.95 | 27270.0 | 2109022.20 | 0.210926 | 2011-08-22 | 2017-01-26 | 1984 days |
1 | 415 | 3142 | 3395 | 29133.15 | 32060.0 | 1214642.75 | 0.100465 | 2022-09-27 | 2023-10-06 | 374 days |
Reversal Starategies
- long, short을 함께 하는 전략
- buy : 롱포지션, sell : 숏포지션
- close : 롱이든 숏이든 포지션 청산하는 것
- is_long, is_short : 포지션이 롱인지, 숏인지 확인 필요
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps, resample_apply
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
return series['Sharpe Ratio']
class MyStrategy(Strategy):
upper_bound = 70
lower_bound = 30
rsi_window=14
def init(self):
self.daily_rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
if crossover(self.daily_rsi, self.upper_bound) :
if self.position.is_long:
#print(self.position.size)
#print(self.position.pl_pct)
self.position.close() # sell long positions 그런데, short position일 때도, 포지션을 청산함 >> 조심해야함
self.sell() # buy short positions
elif crossover(self.lower_bound, self.daily_rsi):
if self.position.is_short or not self.position:
self.position.close() # sell short positions 마찬가지로 조심해야함
self.buy() # buy long positions
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats = bt.run()
print(stats)
bt.plot()
display(stats['_trades'])
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 99.26384
Equity Final [$] 8422052.5
Equity Peak [$] 20604231.69
Return [%] -15.779475
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] -1.266248
Volatility (Ann.) [%] 20.348414
Sharpe Ratio 0.0
Sortino Ratio 0.0
Calmar Ratio 0.0
Max. Drawdown [%] -64.240721
Avg. Drawdown [%] -3.076568
Max. Drawdown Duration 2478 days 00:00:00
Avg. Drawdown Duration 55 days 00:00:00
# Trades 27
Win Rate [%] 70.37037
Best Trade [%] 15.154307
Worst Trade [%] -42.439203
Avg. Trade [%] -0.635676
Max. Trade Duration 453 days 00:00:00
Avg. Trade Duration 185 days 00:00:00
Profit Factor 1.037715
Expectancy [%] 0.132015
SQN -0.157789
_strategy MyStrategy
_equity_curve ...
_trades Size EntryB...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 475 | 25 | 183 | 21036.99 | 24225.0 | 1514304.75 | 0.151543 | 2010-02-08 | 2010-09-28 | 232 days |
1 | -476 | 183 | 399 | 24176.55 | 25080.0 | -430042.20 | -0.037369 | 2010-09-28 | 2011-08-08 | 314 days |
2 | 441 | 399 | 526 | 25130.16 | 26780.0 | 727579.44 | 0.065652 | 2011-08-08 | 2012-02-10 | 186 days |
3 | -441 | 526 | 589 | 26726.44 | 25520.0 | 532040.04 | 0.045140 | 2012-02-10 | 2012-05-14 | 94 days |
4 | 482 | 589 | 737 | 25571.04 | 26215.0 | 310388.72 | 0.025183 | 2012-05-14 | 2012-12-13 | 213 days |
5 | -483 | 737 | 859 | 26162.57 | 24610.0 | 749891.31 | 0.059343 | 2012-12-13 | 2013-06-14 | 183 days |
6 | 543 | 859 | 920 | 24659.22 | 25790.0 | 614013.54 | 0.045856 | 2013-06-14 | 2013-09-10 | 88 days |
7 | -544 | 920 | 1079 | 25738.42 | 25195.0 | 295620.48 | 0.021113 | 2013-09-10 | 2014-05-08 | 240 days |
8 | 566 | 1079 | 1136 | 25245.39 | 26850.0 | 908209.26 | 0.063561 | 2014-05-08 | 2014-07-30 | 83 days |
9 | -568 | 1136 | 1178 | 26796.30 | 25110.0 | 957818.40 | 0.062930 | 2014-07-30 | 2014-10-02 | 64 days |
10 | 643 | 1178 | 1289 | 25160.22 | 26100.0 | 604278.54 | 0.037352 | 2014-10-02 | 2015-03-18 | 167 days |
11 | -644 | 1289 | 1350 | 26047.80 | 24935.0 | 716643.20 | 0.042721 | 2015-03-18 | 2015-06-16 | 90 days |
12 | 700 | 1350 | 1447 | 24984.87 | 25330.0 | 241591.00 | 0.013814 | 2015-06-16 | 2015-11-04 | 141 days |
13 | -701 | 1447 | 1582 | 25279.34 | 23885.0 | 977432.34 | 0.055157 | 2015-11-04 | 2016-05-25 | 203 days |
14 | 782 | 1582 | 1726 | 23932.77 | 26265.0 | 1823803.86 | 0.097449 | 2016-05-25 | 2016-12-21 | 210 days |
15 | -783 | 1726 | 1885 | 26212.47 | 30510.0 | -3364965.99 | -0.163950 | 2016-12-21 | 2017-08-14 | 236 days |
16 | 561 | 1885 | 1920 | 30571.02 | 32405.0 | 1028862.78 | 0.059991 | 2017-08-14 | 2017-10-11 | 58 days |
17 | -562 | 1920 | 2003 | 32340.19 | 31445.0 | 503096.78 | 0.027680 | 2017-10-11 | 2018-02-08 | 120 days |
18 | 593 | 2003 | 2238 | 31507.89 | 28640.0 | -1700658.77 | -0.091021 | 2018-02-08 | 2019-01-28 | 354 days |
19 | -595 | 2238 | 2306 | 28582.72 | 27320.0 | 751318.40 | 0.044178 | 2019-01-28 | 2019-05-10 | 102 days |
20 | 648 | 2306 | 2396 | 27374.64 | 27280.0 | -61326.72 | -0.003457 | 2019-05-10 | 2019-09-19 | 132 days |
21 | -650 | 2396 | 2506 | 27225.44 | 27325.0 | -64714.00 | -0.003657 | 2019-09-19 | 2020-03-02 | 165 days |
22 | 644 | 2506 | 2570 | 27379.65 | 28965.0 | 1020965.40 | 0.057902 | 2020-03-02 | 2020-06-04 | 94 days |
23 | -645 | 2570 | 2870 | 28907.07 | 41175.0 | -7912814.85 | -0.424392 | 2020-06-04 | 2021-08-18 | 440 days |
24 | 260 | 2870 | 3174 | 41257.35 | 32435.0 | -2293811.00 | -0.213837 | 2021-08-18 | 2022-11-14 | 453 days |
25 | -261 | 3174 | 3394 | 32370.13 | 32235.0 | 35268.93 | 0.004175 | 2022-11-14 | 2023-10-05 | 325 days |
26 | 262 | 3394 | 3395 | 32299.47 | 32060.0 | -62741.14 | -0.007414 | 2023-10-05 | 2023-10-06 | 1 days |
Take Profits and Stop Losses
- 수익실현, 스탑로스 설정
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps, resample_apply
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
return series['Sharpe Ratio']
class MyStrategy(Strategy):
upper_bound = 70
lower_bound = 30
rsi_window=14
def init(self):
self.daily_rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
price = self.data.Close[-1] # recent value
if crossover(self.daily_rsi, self.upper_bound) :
self.position.close()
elif crossover(self.lower_bound, self.daily_rsi):
self.buy(tp=1.05*price, sl = 0.97*price) # sl : stop loss
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats = bt.run()
bt.plot()
# print(stats)
# display(stats['_trades'])
Size
- 매수비중 조절
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps, resample_apply
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
return series['Sharpe Ratio']
class MyStrategy(Strategy):
upper_bound = 70
lower_bound = 30
rsi_window=14
position_size=1
def init(self):
self.daily_rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
price = self.data.Close[-1] # recent value
if crossover(self.daily_rsi, self.upper_bound) :
self.position.close()
# elif crossover(self.lower_bound, self.daily_rsi):
# self.buy(size=0.1) # fractions shares
elif self.lower_bound > self.daily_rsi[-1]:
self.buy(size=self.position_size) # fractions shares
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats = bt.run()
bt.plot()
print(stats)
display(stats['_trades'])
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 48.468787
Equity Final [$] 10169269.44
Equity Peak [$] 10210942.37
Return [%] 1.692694
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 0.124633
Volatility (Ann.) [%] 0.362115
Sharpe Ratio 0.34418
Sortino Ratio 0.518956
Calmar Ratio 0.087826
Max. Drawdown [%] -1.419085
Avg. Drawdown [%] -0.056055
Max. Drawdown Duration 647 days 00:00:00
Avg. Drawdown Duration 31 days 00:00:00
# Trades 137
Win Rate [%] 84.671533
Best Trade [%] 42.892663
Worst Trade [%] -21.383705
Avg. Trade [%] 5.458696
Max. Trade Duration 453 days 00:00:00
Avg. Trade Duration 144 days 00:00:00
Profit Factor 5.164782
Expectancy [%] 5.918196
SQN 5.235569
_strategy MyStrategy
_equity_curve ...
_trades Size Entry...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 98 | 183 | 20761.44 | 24225.0 | 3463.56 | 0.166827 | 2010-05-26 | 2010-09-28 | 125 days |
1 | 1 | 87 | 183 | 21803.52 | 24225.0 | 2421.48 | 0.111059 | 2010-05-10 | 2010-09-28 | 141 days |
2 | 1 | 26 | 183 | 20741.40 | 24225.0 | 3483.60 | 0.167954 | 2010-02-09 | 2010-09-28 | 231 days |
3 | 1 | 25 | 183 | 21036.99 | 24225.0 | 3188.01 | 0.151543 | 2010-02-08 | 2010-09-28 | 232 days |
4 | 1 | 409 | 526 | 22284.48 | 26780.0 | 4495.52 | 0.201733 | 2011-08-23 | 2012-02-10 | 171 days |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
132 | 1 | 2873 | 3174 | 40500.84 | 32435.0 | -8065.84 | -0.199152 | 2021-08-23 | 2022-11-14 | 448 days |
133 | 1 | 2872 | 3174 | 40781.40 | 32435.0 | -8346.40 | -0.204662 | 2021-08-20 | 2022-11-14 | 451 days |
134 | 1 | 2870 | 3174 | 41257.35 | 32435.0 | -8822.35 | -0.213837 | 2021-08-18 | 2022-11-14 | 453 days |
135 | 1 | 3395 | 3395 | 32124.12 | 32060.0 | -64.12 | -0.001996 | 2023-10-06 | 2023-10-06 | 0 days |
136 | 1 | 3394 | 3395 | 32299.47 | 32060.0 | -239.47 | -0.007414 | 2023-10-05 | 2023-10-06 | 1 days |
137 rows × 10 columns
barssince
- position을 x일간 유지할 수 있도록 해줌
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps, resample_apply, barssince
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
return series['Sharpe Ratio']
class MyStrategy(Strategy):
upper_bound = 70
lower_bound = 30
rsi_window=14
position_size=0.2
def init(self):
self.daily_rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
price = self.data.Close[-1] # recent value
if (self.daily_rsi[-1] > self.upper_bound and
barssince(self.daily_rsi < self.upper_bound)==3):
self.position.close()
# elif crossover(self.lower_bound, self.daily_rsi):
# self.buy(size=0.1) # fractions shares
elif self.lower_bound > self.daily_rsi[-1]:
self.buy(size=self.position_size) # fractions shares
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats = bt.run()
bt.plot()
print(stats)
display(stats['_trades'])
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 61.513545
Equity Final [$] 14904859.33
Equity Peak [$] 18202293.18
Return [%] 49.048593
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 3.005825
Volatility (Ann.) [%] 12.129955
Sharpe Ratio 0.247802
Sortino Ratio 0.372816
Calmar Ratio 0.108518
Max. Drawdown [%] -27.698991
Avg. Drawdown [%] -2.020104
Max. Drawdown Duration 760 days 00:00:00
Avg. Drawdown Duration 40 days 00:00:00
# Trades 135
Win Rate [%] 85.925926
Best Trade [%] 45.531971
Worst Trade [%] -22.292634
Avg. Trade [%] 6.372823
Max. Trade Duration 779 days 00:00:00
Avg. Trade Duration 271 days 00:00:00
Profit Factor 5.544472
Expectancy [%] 6.904557
SQN 2.777349
_strategy MyStrategy
_equity_curve ...
_trades Size Entry...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 49 | 98 | 187 | 20761.44 | 24545.0 | 185394.44 | 0.182240 | 2010-05-26 | 2010-10-04 | 131 days |
1 | 58 | 87 | 187 | 21803.52 | 24545.0 | 159005.84 | 0.125736 | 2010-05-10 | 2010-10-04 | 147 days |
2 | 77 | 26 | 187 | 20741.40 | 24545.0 | 292877.20 | 0.183382 | 2010-02-09 | 2010-10-04 | 237 days |
3 | 95 | 25 | 187 | 21036.99 | 24545.0 | 333260.95 | 0.166754 | 2010-02-08 | 2010-10-04 | 238 days |
4 | 2 | 604 | 739 | 23997.90 | 26540.0 | 5084.20 | 0.105930 | 2012-06-05 | 2012-12-17 | 195 days |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
130 | 37 | 2901 | 3395 | 39138.12 | 32060.0 | -261890.44 | -0.180850 | 2021-10-06 | 2023-10-06 | 730 days |
131 | 47 | 2900 | 3395 | 39163.17 | 32060.0 | -333848.99 | -0.181374 | 2021-10-05 | 2023-10-06 | 731 days |
132 | 56 | 2873 | 3395 | 40500.84 | 32060.0 | -472687.04 | -0.208411 | 2021-08-23 | 2023-10-06 | 774 days |
133 | 70 | 2872 | 3395 | 40781.40 | 32060.0 | -610498.00 | -0.213857 | 2021-08-20 | 2023-10-06 | 777 days |
134 | 87 | 2870 | 3395 | 41257.35 | 32060.0 | -800169.45 | -0.222926 | 2021-08-18 | 2023-10-06 | 779 days |
135 rows × 10 columns
strategies
strategy : SAR, MACD
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps, resample_apply, barssince
class MyStrategy(Strategy):
position_size=0.2
def init(self):
#Parabolic SAR 지표 계산
self.sar = self.I(talib.SAR, self.data.High, self.data.Low, acceleration=0.02, maximum=0.2)
# MACD 지표 계산
self.macd_line, self.signal_line, self.macd_hist = self.I(talib.MACD,
self.data.Close, fastperiod=12, slowperiod=26, signalperiod=9)
def next(self):
price = self.data.Close[-1] # recent value
# 매수
if (self.data.Close > self.sar and self.macd_hist > 0):
self.buy(size=self.position_size)#, sl=0.95*price, tp=1.05*price)
# 매도
elif (self.sar > self.data.Close and self.macd_hist < 0):
self.position.close()
bt = Backtest(data, MyStrategy, cash=1000000, commission=0.002)
stats = bt.run()
bt.plot()
print(stats)
display(stats['_trades'])
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 55.742049
Equity Final [$] 1068093.2
Equity Peak [$] 1201059.73
Return [%] 6.80932
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 0.490022
Volatility (Ann.) [%] 8.184861
Sharpe Ratio 0.059869
Sortino Ratio 0.084004
Calmar Ratio 0.019785
Max. Drawdown [%] -24.76673
Avg. Drawdown [%] -2.160225
Max. Drawdown Duration 4547 days 00:00:00
Avg. Drawdown Duration 236 days 00:00:00
# Trades 977
Win Rate [%] 38.689867
Best Trade [%] 13.92485
Worst Trade [%] -9.015166
Avg. Trade [%] 0.024117
Max. Trade Duration 70 days 00:00:00
Avg. Trade Duration 22 days 00:00:00
Profit Factor 1.064758
Expectancy [%] 0.083349
SQN 0.555778
_strategy MyStrategy
_equity_curve E...
_trades Size Entry...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 52 | 70 | 22439.79 | 22935.0 | 495.21 | 0.022068 | 2010-03-19 | 2010-04-14 | 26 days |
1 | 1 | 51 | 70 | 22389.69 | 22935.0 | 545.31 | 0.024355 | 2010-03-18 | 2010-04-14 | 27 days |
2 | 1 | 50 | 70 | 22209.33 | 22935.0 | 725.67 | 0.032674 | 2010-03-17 | 2010-04-14 | 28 days |
3 | 1 | 49 | 70 | 22038.99 | 22935.0 | 896.01 | 0.040656 | 2010-03-16 | 2010-04-14 | 29 days |
4 | 1 | 48 | 70 | 22214.34 | 22935.0 | 720.66 | 0.032441 | 2010-03-15 | 2010-04-14 | 30 days |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
972 | 2 | 3377 | 3389 | 34093.05 | 33005.0 | -2176.10 | -0.031914 | 2023-09-06 | 2023-09-22 | 16 days |
973 | 3 | 3376 | 3389 | 34108.08 | 33005.0 | -3309.24 | -0.032341 | 2023-09-05 | 2023-09-22 | 17 days |
974 | 4 | 3375 | 3389 | 34078.02 | 33005.0 | -4292.08 | -0.031487 | 2023-09-04 | 2023-09-22 | 18 days |
975 | 5 | 3374 | 3389 | 33551.97 | 33005.0 | -2734.85 | -0.016302 | 2023-09-01 | 2023-09-22 | 21 days |
976 | 6 | 3373 | 3389 | 33697.26 | 33005.0 | -4153.56 | -0.020544 | 2023-08-31 | 2023-09-22 | 22 days |
977 rows × 10 columns
stats['_trades']
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 387 | 156 | 460 | 25821.54 | 15530.0 | -3982825.98 | -0.398564 | 2021-08-18 | 2022-11-14 | 453 days |
1 | 399 | 680 | 681 | 15075.09 | 14920.0 | -61880.91 | -0.010288 | 2023-10-05 | 2023-10-06 | 1 days |
MACD strategy
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
return series['Sharpe Ratio']
class MyStrategy(Strategy):
fast_period = 12
slow_period = 26
signal_period = 9
def init(self):
self.macd_line, self.signal_line, self.macd_hist = self.I(talib.MACD,
self.data.Close, fastperiod=self.fast_period, slowperiod=self.slow_period, signalperiod=self.signal_period)
def next(self):
if crossover(self.signal_line, self.macd_line):
self.position.close()
elif crossover(self.macd_line, self.signal_line):
self.buy()
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats, heatmap = bt.optimize(
fast_period=range(9, 25, 3),
slow_period=range(20, 50, 3),
signal_period=range(8, 25, 3),
maximize = optim_func,#'Sharpe Ratio',
constraint = lambda param : param.fast_period < param.slow_period,
return_heatmap=True
)
#print(heatmap)
hm = heatmap.groupby(['fast_period', 'slow_period']).mean().unstack()
#print(hm)
# sns.heatmap(hm, annot=True, cmap='viridis')
# plt.show()
plot_heatmaps(heatmap, agg='mean')
fast_period = stats['_strategy'].fast_period
slow_period = stats['_strategy'].slow_period
signal_period = stats['_strategy'].signal_period
print("best :", fast_period,"-", slow_period,"-", signal_period)
print(stats)
bt.plot()
display(stats['_trades'])
best : 18 - 20 - 11
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 53.739694
Equity Final [$] 15981353.49
Equity Peak [$] 17669529.69
Return [%] 59.813535
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 3.540232
Volatility (Ann.) [%] 11.569808
Sharpe Ratio 0.305989
Sortino Ratio 0.452202
Calmar Ratio 0.141107
Max. Drawdown [%] -25.088971
Avg. Drawdown [%] -3.49389
Max. Drawdown Duration 2195 days 00:00:00
Avg. Drawdown Duration 125 days 00:00:00
# Trades 108
Win Rate [%] 38.888889
Best Trade [%] 15.241274
Worst Trade [%] -7.939919
Avg. Trade [%] 0.43567
Max. Trade Duration 77 days 00:00:00
Avg. Trade Duration 24 days 00:00:00
Profit Factor 1.439068
Expectancy [%] 0.515966
SQN 1.09744
_strategy MyStrategy(fast_...
_equity_curve ...
_trades Size Entry...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 457 | 32 | 74 | 21858.63 | 22860.0 | 457626.09 | 0.045811 | 2010-02-18 | 2010-04-20 | 61 days |
1 | 476 | 104 | 124 | 21958.83 | 21960.0 | 556.92 | 0.000053 | 2010-06-04 | 2010-07-02 | 28 days |
2 | 458 | 131 | 153 | 22790.49 | 22700.0 | -41444.42 | -0.003971 | 2010-07-13 | 2010-08-12 | 30 days |
3 | 444 | 171 | 195 | 23446.80 | 24505.0 | 469840.80 | 0.045132 | 2010-09-07 | 2010-10-14 | 37 days |
4 | 428 | 211 | 218 | 25400.70 | 25000.0 | -171499.60 | -0.015775 | 2010-11-05 | 2010-11-16 | 11 days |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
103 | 481 | 3214 | 3237 | 31407.69 | 32825.0 | 681726.11 | 0.045126 | 2023-01-10 | 2023-02-14 | 35 days |
104 | 493 | 3265 | 3287 | 31978.83 | 32905.0 | 456601.81 | 0.028962 | 2023-03-27 | 2023-04-26 | 30 days |
105 | 479 | 3304 | 3323 | 33882.63 | 34230.0 | 166390.23 | 0.010252 | 2023-05-23 | 2023-06-21 | 29 days |
106 | 472 | 3341 | 3355 | 34739.34 | 34275.0 | -219168.48 | -0.013366 | 2023-07-17 | 2023-08-04 | 18 days |
107 | 482 | 3374 | 3390 | 33551.97 | 33105.0 | -215439.54 | -0.013322 | 2023-09-01 | 2023-09-25 | 24 days |
108 rows × 10 columns
strategy : SAR
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps
def optim_func(series):
if series['# Trades'] < 3: #최소 트레이드 회수 제한
return -1
#return series['Equity Final [$]'] / series['Exposure Time [%]']
# return series['Sharpe Ratio']
return series['Equity Final [$]']
class MyStrategy(Strategy):
acceleration = 0.02
maximum=0.2
def init(self):
self.sar = self.I(talib.SAR, self.data.High, self.data.Low, acceleration=self.acceleration, maximum=self.maximum)
def next(self):
if crossover(self.data.Close, self.sar):
self.buy()
elif crossover(self.sar, self.data.Close):
self.position.close()
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats, heatmap = bt.optimize(
acceleration=0.02, #np.arange(0.01, 0.05, 0.01).tolist(),
maximum=0.2, #np.arange(0.1, 0.25, 0.01).tolist(),
maximize = optim_func,#'Sharpe Ratio',
#constraint = lambda param : param.fast_period < param.slow_period,
return_heatmap=True
)
#print(heatmap)
hm = heatmap.groupby(['acceleration', 'maximum']).mean().unstack()
#print(hm)
# sns.heatmap(hm, annot=True, cmap='viridis')
# plt.show()
plot_heatmaps(heatmap, agg='mean')
acceleration = stats['_strategy'].acceleration
maximum = stats['_strategy'].maximum
print("best :", acceleration,"-", maximum)
print(stats)
bt.plot()
display(stats['_trades'])
0%| | 0/1 [00:00<?, ?it/s]
best : 0.02 - 0.2
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 58.186101
Equity Final [$] 11100358.3
Equity Peak [$] 12748303.25
Return [%] 11.003583
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 0.777651
Volatility (Ann.) [%] 10.952246
Sharpe Ratio 0.071004
Sortino Ratio 0.102097
Calmar Ratio 0.023512
Max. Drawdown [%] -33.074381
Avg. Drawdown [%] -5.483589
Max. Drawdown Duration 3638 days 00:00:00
Avg. Drawdown Duration 332 days 00:00:00
# Trades 150
Win Rate [%] 35.333333
Best Trade [%] 15.752356
Worst Trade [%] -6.348752
Avg. Trade [%] 0.069374
Max. Trade Duration 55 days 00:00:00
Avg. Trade Duration 18 days 00:00:00
Profit Factor 1.10156
Expectancy [%] 0.130881
SQN 0.259607
_strategy MyStrategy(accel...
_equity_curve ...
_trades Size Entry...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 435 | 11 | 15 | 22935.78 | 22175.0 | -330939.30 | -0.033170 | 2010-01-19 | 2010-01-25 | 6 days |
1 | 451 | 29 | 38 | 21402.72 | 21250.0 | -68876.72 | -0.007136 | 2010-02-12 | 2010-02-26 | 14 days |
2 | 434 | 43 | 68 | 22079.07 | 23170.0 | 473463.62 | 0.049410 | 2010-03-08 | 2010-04-12 | 35 days |
3 | 431 | 71 | 74 | 23346.60 | 22860.0 | -209724.60 | -0.020842 | 2010-04-15 | 2010-04-20 | 5 days |
4 | 425 | 76 | 82 | 23176.26 | 22910.0 | -113160.50 | -0.011488 | 2010-04-22 | 2010-04-30 | 8 days |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
145 | 336 | 3264 | 3285 | 31973.82 | 33470.0 | 502716.48 | 0.046794 | 2023-03-24 | 2023-04-24 | 31 days |
146 | 337 | 3302 | 3320 | 33326.52 | 34555.0 | 413997.76 | 0.036862 | 2023-05-19 | 2023-06-16 | 28 days |
147 | 337 | 3332 | 3336 | 34543.95 | 33340.0 | -405731.15 | -0.034853 | 2023-07-04 | 2023-07-10 | 6 days |
148 | 325 | 3340 | 3355 | 34609.08 | 34275.0 | -108576.00 | -0.009653 | 2023-07-14 | 2023-08-04 | 21 days |
149 | 329 | 3372 | 3388 | 33857.58 | 33715.0 | -46908.82 | -0.004211 | 2023-08-30 | 2023-09-21 | 22 days |
150 rows × 10 columns
SMA, RSI
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps, resample_apply, barssince
class MyStrategy(Strategy):
rsi_window=14
short_period=60
long_period=120
upper_bound=65
lower_bound=45
def init(self):
self.rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
self.ma_short = self.I(talib.SMA, self.data.Close, self.short_period)
self.ma_long = self.I(talib.SMA, self.data.Close, self.long_period)
def next(self):
# 매수
if (self.rsi < self.lower_bound and self.ma_short > self.ma_long):
self.buy()
# 매도
elif (self.rsi > self.upper_bound and self.ma_short < self.ma_long):
self.position.close()
bt = Backtest(data, MyStrategy, cash=1000000, commission=0.002)
stats = bt.run()
bt.plot()
print(stats)
display(stats['_trades'])
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 77.532391
Equity Final [$] 1325733.23
Equity Peak [$] 1866205.41
Return [%] 32.573323
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 2.114367
Volatility (Ann.) [%] 15.595352
Sharpe Ratio 0.135577
Sortino Ratio 0.19576
Calmar Ratio 0.058773
Max. Drawdown [%] -35.97514
Avg. Drawdown [%] -3.603827
Max. Drawdown Duration 2222 days 00:00:00
Avg. Drawdown Duration 108 days 00:00:00
# Trades 13
Win Rate [%] 61.538462
Best Trade [%] 19.740756
Worst Trade [%] -6.503583
Avg. Trade [%] 2.474779
Max. Trade Duration 777 days 00:00:00
Avg. Trade Duration 310 days 00:00:00
Profit Factor 4.40458
Expectancy [%] 2.711425
SQN 1.122675
_strategy MyStrategy
_equity_curve ...
_trades Size EntryB...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 45 | 124 | 651 | 22003.92 | 25890.0 | 174873.60 | 0.176609 | 2010-07-02 | 2012-08-10 | 770 days |
1 | 46 | 692 | 918 | 25450.80 | 25420.0 | -1416.80 | -0.001210 | 2012-10-11 | 2013-09-06 | 330 days |
2 | 44 | 958 | 1062 | 26117.13 | 26400.0 | 12446.28 | 0.010831 | 2013-11-08 | 2014-04-10 | 153 days |
3 | 1 | 1182 | 1279 | 24829.56 | 25625.0 | 795.44 | 0.032036 | 2014-10-10 | 2015-03-04 | 145 days |
4 | 45 | 1104 | 1279 | 25801.50 | 25625.0 | -7942.50 | -0.006841 | 2014-06-16 | 2015-03-04 | 261 days |
5 | 44 | 1323 | 1430 | 26332.56 | 24620.0 | -75352.64 | -0.065036 | 2015-05-07 | 2015-10-12 | 158 days |
6 | 45 | 1470 | 1529 | 24423.75 | 24460.0 | 1631.25 | 0.001484 | 2015-12-07 | 2016-03-08 | 92 days |
7 | 44 | 1566 | 1626 | 24579.06 | 25210.0 | 27761.36 | 0.025670 | 2016-04-29 | 2016-07-27 | 89 days |
8 | 44 | 1659 | 2157 | 25350.60 | 30355.0 | 220193.60 | 0.197408 | 2016-09-13 | 2018-09-28 | 745 days |
9 | 48 | 2268 | 2340 | 28116.12 | 27890.0 | -10853.76 | -0.008042 | 2019-03-15 | 2019-06-28 | 105 days |
10 | 48 | 2446 | 2568 | 27930.75 | 27305.0 | -30036.00 | -0.022404 | 2019-12-02 | 2020-06-02 | 183 days |
11 | 42 | 2648 | 3171 | 31067.01 | 31375.0 | 12935.58 | 0.009914 | 2020-09-23 | 2022-11-09 | 777 days |
12 | 41 | 3247 | 3395 | 31993.86 | 32060.0 | 2711.74 | 0.002067 | 2023-02-28 | 2023-10-06 | 220 days |
import talib
from backtesting import Backtest, Strategy
from backtesting.lib import crossover, plot_heatmaps, resample_apply, barssince
class MyStrategy(Strategy):
short_period=60
long_period=120
rsi_window = 14
upper_bound = 70
lower_bound = 30
position_size = 1
def init(self):
self.ma_short = self.I(talib.SMA, self.data.Close, self.short_period)
self.ma_long = self.I(talib.SMA, self.data.Close, self.long_period)
self.rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
def next(self):
price = self.data.Close[-1]
# 매수
if (self.ma_short > self.ma_long and self.rsi < self.lower_bound):
# if self.position.is_short or not self.position:
# self.position.close()
self.buy(sl=0.8*price)
# 매도
elif (self.ma_short < self.ma_long and self.rsi > self.upper_bound):
# if self.position.is_long:
self.position.close()
# self.sell()
bt = Backtest(data, MyStrategy, cash=10000000, commission=0.002)
stats, heatmap = bt.optimize(
short_period=range(5, 45, 10), #np.arange(0.01, 0.05, 0.01).tolist(),
long_period=range(20, 80, 10), #np.arange(0.1, 0.25, 0.01).tolist(),
rsi_window=range(10, 22, 2),
maximize = 'Equity Final [$]', #'Sharpe Ratio',
constraint = lambda param : param.short_period < param.long_period,
return_heatmap=True,
max_tries=100
)
#print(heatmap)
hm = heatmap.groupby(['short_period', 'long_period']).mean().unstack()
#print(hm)
# sns.heatmap(hm, annot=True, cmap='viridis')
# plt.show()
plot_heatmaps(heatmap, agg='mean')
short_period = stats['_strategy'].short_period
long_period = stats['_strategy'].long_period
rsi_window = stats['_strategy'].rsi_window
print("best :", short_period,"-", long_period, "-", rsi_window)
print(stats)
bt.plot()
display(stats['_trades'])
0%| | 0/9 [00:00<?, ?it/s]
best : 5 - 70 - 14
Start 2010-01-04 00:00:00
End 2023-10-06 00:00:00
Duration 5023 days 00:00:00
Exposure Time [%] 97.438163
Equity Final [$] 14697467.84
Equity Peak [$] 20285067.84
Return [%] 46.974678
Buy & Hold Return [%] 41.23734
Return (Ann.) [%] 2.898779
Volatility (Ann.) [%] 17.463108
Sharpe Ratio 0.165994
Sortino Ratio 0.242547
Calmar Ratio 0.071368
Max. Drawdown [%] -40.61749
Avg. Drawdown [%] -3.581652
Max. Drawdown Duration 2205 days 00:00:00
Avg. Drawdown Duration 96 days 00:00:00
# Trades 1
Win Rate [%] 100.0
Best Trade [%] 47.040478
Worst Trade [%] 47.040478
Avg. Trade [%] 47.040478
Max. Trade Duration 4897 days 00:00:00
Avg. Trade Duration 4897 days 00:00:00
Profit Factor NaN
Expectancy [%] 47.040478
SQN NaN
_strategy MyStrategy(short...
_equity_curve ...
_trades Size EntryBa...
dtype: object
Size | EntryBar | ExitBar | EntryPrice | ExitPrice | PnL | ReturnPct | EntryTime | ExitTime | Duration | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 458 | 87 | 3395 | 21803.52 | 32060.0 | 4697467.84 | 0.470405 | 2010-05-10 | 2023-10-06 | 4897 days |
댓글남기기