# lib
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns;sns.set()
'tableau-colorblind10')
plt.style.use(
from sklearn.preprocessing import StandardScaler, MinMaxScaler, normalize
# homemade
from features import trnd_scan, tautil
from labeling import labeling
from triple_barrier import get_barrier, make_rt
from backtest import print_round_trip_stats, round_trip
머신러닝을 이용한 트레이딩: (6) 매매 규칙
Trading Rules
trading
Trading rules: 매수 진입만 허용
- Inputs
- 진입(Enter) 규칙 : 모멘텀 시그널과 기술적 분석
- 청산(Exit) 규칙: 다이나믹 트리플(익절, 손절, 최대보유) 청산
- outputs
- 각 매매의 성공/실패 여부
import warnings
='ignore') warnings.filterwarnings(action
= pd.read_csv('C:data/market_samsung.csv')
market_df = market_df.rename(columns={market_df.columns[0]:'Date'})
market_df = pd.to_datetime(market_df.Date)
market_df.index ='Date',inplace=True)
market_df.drop(columns=True)
market_df.dropna(inplace
= market_df.close['2010':'2020'] close
매매 규칙
진입 규칙
Momentum Prediction
= pd.read_csv('C:data/momentum_signals.csv')
signals = pd.to_datetime(signals['Date'])
signals.index ='Date',inplace=True) signals.drop(columns
= signals['signals'].loc['2010':'2020'] signals
= normalize
scaler = MinMaxScaler()
scaler2 = pd.Series(scaler2.fit_transform(normalize(signals.values.reshape(-1,1),axis=0)).reshape((-1,)),
signals =signals.index).rename('signals') index
=50)[2]
plt.hist(signals,bins'Distribution of momentum signals')
plt.title( plt.show()
= [0, 0.3] thresholds
=[]
enter_ml_listfor h in thresholds:
>h].index) enter_ml_list.append(signals.loc[signals
for i in range(len(thresholds)):
=(10,3))
plt.figure(figsize=0.5)
plt.plot(close, alpha'Enter points ML Prediction (Option {})'.format(i+1))
plt.title(='^',linewidth=0,alpha=0.3)
plt.plot(close.loc[enter_ml_list[i]],marker'price','Long signals from momentum classifier'])
plt.legend([ plt.show()
Tech. Analysis Long/short decision
open = market_df.open['2010':'2020']
= tautil.RSIIndicator(open,14).rsi().dropna()
rsi long = (rsi>=50) & (rsi<70)
= rsi.loc[long].index enter_ta
=(10,3))
plt.figure(figsize=0.5)
plt.plot(close, alpha'Enter points TA')
plt.title(='^',linewidth=0,alpha=0.3)
plt.plot(close.loc[enter_ta],marker'price','Long signals from rsi'])
plt.legend([ plt.show()
= [enter_ta]
enter_list 1]& enter_ta).sort_values().drop_duplicates()) enter_list.append((enter_ml_list[
for i in range(len(thresholds)):
=(10,3))
plt.figure(figsize=0.5)
plt.plot(close, alpha'Enter points (Option {})'.format(i+1))
plt.title(='^',linewidth=0,alpha=0.3)
plt.plot(close.loc[enter_list[i]],marker'price','Enter points'])
plt.legend([ plt.show()
=(10,3))
plt.figure(figsize=0.5)
plt.plot(close, alpha'Position Enter points'.format(i+1))
plt.title(1]],marker='^',linewidth=0,alpha=0.3)
plt.plot(close.loc[enter_list['price','Enter points'])
plt.legend([ plt.show()
청산 규칙
# no Rule (benchmark)
= [1000,1000]
pt_sl_bm = [1, 0]
max_holding_bm = [pt_sl_bm,max_holding_bm] no_exit_rule
#dynamic target rule
= [60, 0]
max_holding = market_df.close['2009':'2020']
close_ = close_.pct_change(1).to_frame()
changes for i in range(2,max_holding[0]+1):
= changes.join(close_.pct_change(i).rename('close {}'.format(i)))
changes = changes.abs().dropna().mean(axis=1)['2010':] dynamic_target
=[]
barrier_exit_list1], [1,1], max_holding, target = dynamic_target)) #dynamic
barrier_exit_list.append(get_barrier(close, enter_list[
=[]
rts_exit_listfor i in range(len(barrier_exit_list)):
rts_exit_list.append(make_rt(close,barrier_exit_list[i].dropna()))
=(10,2))
plt.figure(figsize'Dynamic exit rule returns')
plt.title(0].ret)
plt.plot(barrier_exit_list['Returns of dynamic exit target rate']) plt.legend([
<matplotlib.legend.Legend at 0x1fe01fa7148>
결과
벤치마크
- 매번 매매시
= get_barrier(close, close.index, no_exit_rule[0], no_exit_rule[1]) #no rule barrier_bm
= make_rt(close,barrier_bm.dropna()) rts_bm
'Benchmark',years=11) round_trip.get_df_ann_sr(rts_bm,
Benchmark | |
---|---|
avg_n_bets_per_year | 243.272727 |
win_ratio | 0.518835 |
annualized_sharpe_ratio | 0.537080 |
= pd.concat([round_trip.get_df_ann_sr(rts_bm,'No Rule')], axis=1)
result_df for i in range(len(rts_exit_list)):
= result_df.join(round_trip.get_df_ann_sr(rts_exit_list[i],'Enter & Exit Rule'))
result_df
result_df
No Rule | Enter & Exit Rule | |
---|---|---|
avg_n_bets_per_year | 243.272727 | 105.000000 |
win_ratio | 0.518835 | 0.590988 |
annualized_sharpe_ratio | 0.537080 | 1.800316 |
최적 청산 규칙 파라미터
#dynamic target rule
# different maximum holding
= market_df.close['2009':'2020']
close_ = np.arange(20,260,10)
rolling = [20,60,120,260]
mhs = pd.DataFrame()
win_ratios
for mh in mhs:
= [mh, 0]
max_holding = []
dynamic_targets for j in rolling:
for i in range(2,j+1):
= close_.pct_change(1).to_frame()
changes = changes.join(close_.pct_change(i).rename('close {}'.format(i)))
changes = changes.abs().dropna().mean(axis=1)['2010':]
dynamic_target
dynamic_targets.append(dynamic_target)
=[]
barrier_exit_list_rollingfor i in range(len(dynamic_targets)):
1], [1,1], max_holding, target = dynamic_targets[i])) #dynamic
barrier_exit_list_rolling.append(get_barrier(close, enter_list[
=[]
rts_exit_listfor i in range(len(barrier_exit_list_rolling)):
rts_exit_list.append(make_rt(close,barrier_exit_list_rolling[i].dropna()))
= pd.concat([round_trip.get_df_ann_sr(rts_bm,'No Rule')], axis=1)
result_df for i in range(len(rts_exit_list)):
= result_df.join(round_trip.get_df_ann_sr(rts_exit_list[i],'{}'.format(rolling[i])))
result_df
'Max. holding {} days'.format(mh)] = result_df.T.win_ratio
win_ratios[ win_ratios
Max. holding 20 days | Max. holding 60 days | Max. holding 120 days | Max. holding 260 days | |
---|---|---|---|---|
No Rule | 0.518835 | 0.518835 | 0.518835 | 0.518835 |
20 | 0.541054 | 0.576857 | 0.572169 | 0.575130 |
30 | 0.564014 | 0.573898 | 0.570069 | 0.570441 |
40 | 0.532872 | 0.559689 | 0.553155 | 0.550562 |
50 | 0.555363 | 0.588591 | 0.589455 | 0.587727 |
60 | 0.564991 | 0.605195 | 0.605195 | 0.603463 |
70 | 0.562392 | 0.605195 | 0.600866 | 0.595671 |
80 | 0.557192 | 0.600000 | 0.584416 | 0.586147 |
90 | 0.555459 | 0.599134 | 0.598787 | 0.601732 |
100 | 0.563258 | 0.587522 | 0.600520 | 0.599653 |
110 | 0.555459 | 0.575022 | 0.595486 | 0.604510 |
120 | 0.561525 | 0.585281 | 0.611785 | 0.620451 |
130 | 0.560659 | 0.593560 | 0.608014 | 0.620209 |
140 | 0.559792 | 0.601739 | 0.607485 | 0.623151 |
150 | 0.555844 | 0.579496 | 0.578261 | 0.597391 |
160 | 0.545927 | 0.574413 | 0.581010 | 0.600174 |
170 | 0.558925 | 0.598432 | 0.598082 | 0.625981 |
180 | 0.551127 | 0.586297 | 0.594266 | 0.615451 |
190 | 0.548918 | 0.594805 | 0.601386 | 0.629116 |
200 | 0.548918 | 0.591342 | 0.606586 | 0.623050 |
210 | 0.535065 | 0.596886 | 0.612987 | 0.627706 |
220 | 0.555844 | 0.614187 | 0.628571 | 0.643290 |
230 | 0.552768 | 0.595506 | 0.605195 | 0.624567 |
240 | 0.561525 | 0.618387 | 0.629887 | 0.647569 |
250 | 0.559792 | 0.609714 | 0.625543 | 0.644097 |
=(15,6))
plt.figure(figsize"Exit rules")
plt.title(
plt.plot(win_ratios)
plt.legend(win_ratios)'win ratio')
plt.ylabel('Rolling days for calculating target rate')
plt.xlabel( plt.show()
매매 결과
= get_barrier(close, enter_list[1], [1,1], max_holding, target = dynamic_target)
barrier = make_rt(close,barrier.dropna()) rts
'Chose for 2nd model') round_trip.get_df_ann_sr(rts,
Chose for 2nd model | |
---|---|
avg_n_bets_per_year | 104.818182 |
win_ratio | 0.644097 |
annualized_sharpe_ratio | 3.122372 |
print_round_trip_stats(rts)
Summary stats | All trades | Long trades |
---|---|---|
Total number of round_trips | 1153.00 | 1153.00 |
Percent profitable | 0.64 | 0.64 |
Winning round_trips | 742.00 | 742.00 |
Losing round_trips | 410.00 | 410.00 |
Even round_trips | 1.00 | 1.00 |
PnL stats | All trades | Long trades |
---|---|---|
Total profit | $1562060.00 | $1562060.00 |
Gross profit | $2900830.00 | $2900830.00 |
Gross loss | $-1338770.00 | $-1338770.00 |
Profit factor | $2.17 | $2.17 |
Avg. trade net profit | $1354.78 | $1354.78 |
Avg. winning trade | $3909.47 | $3909.47 |
Avg. losing trade | $-3265.29 | $-3265.29 |
Ratio Avg. Win:Avg. Loss | $1.20 | $1.20 |
Largest winning trade | $15720.00 | $15720.00 |
Largest losing trade | $-19350.00 | $-19350.00 |
Duration stats | All trades | Long trades |
---|---|---|
Avg duration | 98 days 20:41:25.342584562 | 98 days 20:41:25.342584562 |
Median duration | 51 days 00:00:00 | 51 days 00:00:00 |
Longest duration | 264 days 00:00:00 | 264 days 00:00:00 |
Shortest duration | 1 days 00:00:00 | 1 days 00:00:00 |
Return stats | All trades | Long trades |
---|---|---|
Avg returns all round_trips | 0.22% | 0.22% |
Avg returns winning | 1.38% | 1.38% |
Avg returns losing | -1.22% | -1.22% |
Median returns all round_trips | 0.18% | 0.18% |
Median returns winning | 1.06% | 1.06% |
Median returns losing | -0.99% | -0.99% |
Largest winning trade | 8.69% | 8.69% |
Largest losing trade | -9.44% | -9.44% |
Symbol stats | Asset |
---|---|
Avg returns all round_trips | 0.22% |
Avg returns winning | 1.38% |
Avg returns losing | -1.22% |
Median returns all round_trips | 0.18% |
Median returns winning | 1.06% |
Median returns losing | -0.99% |
Largest winning trade | 8.69% |
Largest losing trade | -9.44% |
'C:data/barrier.csv') barrier.to_csv(
'C:data/barrier_bm.csv') barrier_bm.to_csv(