매번 이리저리 변수들을 바꿔서 백테스트를 하다 대충 우상향 하는 곡선을 뽑아내고
나온 결과물들을 곰곰히 살펴보면 결국엔 기존 전략들과 별반 차이가 없는 구조를 발견하게 되는 일이 많았다.
결국 어떤 스토리, 철학을 가지고 진입을 하느냐가 중요한 것 같은데
그런 측면에서 그동안 미루어왔던 캔들 유형에 따른 투자방법을 한번 생각해보기로 했다.
캔들 유형이 매우 다양해서 그 유형들의 특성들을 다 공부하고
이를 수식화하는 것은 단기간에 어려울 것같고
그냥 데이터베이스에 담긴 OHLC 자료를 TA-Lib을 이용해서 어떤 캔들 유형인지 판단,
해당 유형의 캔들이 출현하였을 때 단기적으로 투자한다고 가정할 경우
유형별 수익성, 복리투자를 가정한 자본, MDD 등을 산출해볼 것이다.
그런데 생각할수록 '패턴인식'이라니... 너무 거창한 느낌이다.
TA-Lib의 functions에서 'pattern recognition'으로 분류되어 있긴 한데...
아무래도 결과는 초라하게 나와서 실망할 것 같은 불안감이 엄습해온다.
아무튼 1차적으로 구성한 전체 코드는 아래와 같다.
- candlestick pattern recognition에 사용할 일봉 데이터 준비 및 읽어오기
- TA-Lib에서의 전체 'Pattern Recognition'을 변수에 저장 (총 60개)
- 일자별로 전체 Patern recognition 중 어떤 유형에 해당되는지 판단하여 해당 유형명에 해당하는 칼럼에 기록한다. 이 경우 통상 -100 (Negative), 0(N/A), 100 (Positive)으로 표시되나, 간혹 -200, 200과 같이 표시되는 값들이 있는데 일단 무시하기로 했다
- 특정 유형이 출현하였을 때 100 이상이면 Long 포지션, -100이면 Short 포지션, 당일 종가 진입 - 익일 종가 청산으로 단순화하여 유형별 투자성과를 집계
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
|
import pandas as pd
import sqlite3
import numpy as np
import talib
import warnings
warnings.filterwarnings("ignore")
pd.set_option('display.max_columns', 10000)
pd.set_option('display.max_rows', 10000)
pd.set_option('display.max_colwidth', 0)
pd.options.display.expand_frame_repr = False
class Talib_main():
def __init__(self):
# TA-Lib에서 인식가능한 전체 pattern list를 가져온다.
self.candle_names = talib.get_function_groups()['Pattern Recognition']
# OHLC Database에서 특정 table을 DF으로 반환
def load_db1_table(self, table_name):
con = sqlite3.connect('C:/backtest_db.db')
df = pd.read_sql("SELECT * FROM '" + table_name + "'", con, index_col='index')
return df
# name에 해당하는 캔들유형에 해당하는지 여부를 판단
def get_candle_pattern(self, df, name):
df[name] = getattr(talib, name)(df['open'], df['high'], df['low'], df['close'])
return df
# 전체 캔들유형을 검사하여 DF로 반환
def get_all_candlestick(self, df):
df['total_score'] = 0
df[f'plr'] = df['close'].shift(-1) / df['close'] - 1 # 익일 종가 / 당일 종가
for i in self.candle_names:
df = self.get_candle_pattern(df, i)
df['total_score'] = df['total_score'] + df[i]
total_candle_count = len(df)
print(f'전체캔들수:{total_candle_count}')
return df
# 캔들유형별 당일 종가 진입 후 익일 종가 청산 시 Long/short포지션의 성과 산출
def candlestick_plr(self, df, name, thres):
con1 = df[name] > thres
fdb1 = df[con1]
ttc1 = len(fdb1)
con2 = df[name] < thres
fdb2 = df[con2]
ttc2 = len(fdb2)
result = {'name': name}
# condition1 - Long포지션
result['l_count'] = ttc1
plr = fdb1['plr'].sum()
equity = (fdb1['plr'] + 1).cumprod()
equity = equity.fillna(method='pad')
dd = equity / equity.cummax() - 1
win = np.sum(fdb1['plr'] > 0)
win_ratio = win / ttc1
result['l_plr'] = round(plr, 4)
if ttc1 == 0:
result['l_eq'] = 0
result['l_mdd'] = 0
else:
result['l_eq'] = round(equity.iat[-1], 4)
result['l_mdd'] = round(dd.min(), 4)
result['l_wr'] = round(win_ratio, 4)
# condition2 - short포지션
result['s_count'] = ttc2
plr2 = - fdb2['plr'].sum()
equity2 = (- fdb2['plr'] + 1).cumprod()
equity2 = equity2.fillna(method='pad')
dd2 = equity2 / equity2.cummax() - 1
win2 = np.sum(- fdb2['plr'] > 0)
win_ratio2 = win2 / ttc2
result['s_plr'] = round(plr2, 4)
if ttc2 == 0:
result['s_eq'] = 0
result['s_mdd'] = 0
else:
result['s_eq'] = round(equity2.iat[-1], 4)
result['s_mdd'] = round(dd2.min(), 4)
result['s_wr'] = round(win_ratio2, 4)
return result
# 전체 캔들에 대한 성과 산출
def all_candlestick_plr(self, df):
thres = 0
all_result = {}
for candle_name in self.candle_names:
ret = self.candlestick_plr(df, candle_name, thres)
all_result[len(all_result)] = ret
return all_result
if __name__ == "__main__":
main = Talib_main()
table_name = 'OOOOOOOOO'
df = main.load_db1_table(table_name)
df2 = main.get_all_candlestick(df)
df2.to_csv(f'all_cs_{table_name}.csv')
# all_candlestick_plr 실행 후 결과값 dictionary -> dataframe화
results = main.all_candlestick_plr(df2)
df_ret = pd.DataFrame(results).transpose()
df_ret = df_ret.fillna(0)
print(df_ret)
|
cs |
일단 실행결과는 여기까지 나오는 걸 확인했다.
이제 하나하나 뜯어서 검증해봐야겠다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
name l_count l_plr l_eq l_mdd l_wr s_count s_plr s_eq s_mdd s_wr
0 CDL2CROWS 0 0.0000 0.0000 0.0000 0.0000 3 0.0046 1.0043 -0.0138 0.3333
1 CDL3BLACKCROWS 0 0.0000 0.0000 0.0000 0.0000 0 -0.0000 0.0000 0.0000 0.0000
2 CDL3INSIDE 61 -0.0146 0.9815 -0.0909 0.5082 38 0.1627 1.1673 -0.0436 0.4474
3 CDL3LINESTRIKE 1 0.0023 1.0023 0.0000 1.0000 1 0.0030 1.0030 0.0000 1.0000
4 CDL3OUTSIDE 71 -0.0743 0.9229 -0.1230 0.5352 75 -0.0200 0.9632 -0.1909 0.4533
5 CDL3STARSINSOUTH 0 0.0000 0.0000 0.0000 0.0000 0 -0.0000 0.0000 0.0000 0.0000
6 CDL3WHITESOLDIERS 1 -0.0133 0.9867 0.0000 0.0000 0 -0.0000 0.0000 0.0000 0.0000
7 CDLABANDONEDBABY 1 0.0085 1.0085 0.0000 1.0000 0 -0.0000 0.0000 0.0000 0.0000
8 CDLADVANCEBLOCK 0 0.0000 0.0000 0.0000 0.0000 29 -0.0179 0.9790 -0.0872 0.4138
9 CDLBELTHOLD 437 0.1528 1.1219 -0.2059 0.5469 413 -0.5380 0.5560 -0.5043 0.4383
10 CDLBREAKAWAY 0 0.0000 0.0000 0.0000 0.0000 0 -0.0000 0.0000 0.0000 0.0000
......
|
cs |
'시스템트레이딩' 카테고리의 다른 글
무지성 종가베팅 - 개선판 (0) | 2021.09.01 |
---|---|
무지성 종가베팅 (0) | 2021.08.31 |
변화하는 시장 (0) | 2021.08.16 |
주문사고 (1) | 2021.07.15 |
코스닥150ETF 전략재개 (0) | 2021.07.12 |