눌림목 전략
눌림목 전략은 기본적으로 큰 거래량을 동반하면서 상승한 종목이 눌림 (하락) 을 받고, 다시 상승할 때 매수 하는전략이다. 코드를 보면서 이해를 해보겠다.
매수 전략
Capital = x['시가총액'].values
Rate = x["시총변화율"].values
PER = x["PER"].values
minimum_cap = config.minimum_cap * 100000000000 #(1000억)
maximum_cap = config.maximum_cap * 100000000000
idx = np.where((Capital > minimum_cap) & (Rate > config.w1) & ((Rate < 30)) &(PER>0))[0]
먼저 x 에 대해 이해를 해보겠다. x 는 현재 날짜의 코스피/코스닥 모든 종목의 정보를 담고 있다. 예를 들어, x를 출력하면 다음과 같이 뜬다.
티커 PER PBR 시가총액 시총변화율 거래량
054950 0.4 1.0 30000000 4.5 % 1000000
033220 1.4 0.4 50000000 -3.5 % 3000000
013200 0.7 1.5 60000000 8.5 % 80000000
078889 0.5 2.5 70000000 5.5 %
'
'
'
321032 0.6 18.0 10000000 10.5 %
따라서 x의 총 행의 개수는 코스피/코스닥 모든 종목을 합한 숫자가 나올것이다. 대략 2000개 정도 나온다.
idx는 필터링을 통해 내가 원하는 종목만 뽑아오게 된다. 여기선 최소 시가총액 minimum_cap을 넘겨야 하고, Rate는 시총변화율 (어제 종가 기준 현재 종가에 대해 변화율) 을 뜻하는데, 이것이 config.w1이라는 상수를 넘겨야 하며, 30보단 작아야한다. Rate는 당연히 30보다 작을수 밖에 없는데, 가끔 가격 정보가 잘못 입력되어 30을 초과하는 경우가 생겨 넣었다. 이렇게 되면, 저 조건을 통과한 종목들만 살아남게 될 것이다.
필자는 w1을 (5,10,15,20,25) 을 모두 테스트해서 좋은 값을 뽑아냈고, 그 값은 10이다. 또한 거래를 자주하고 싶어서 minimum_cap 은 0 으로 설정하였다.
따라서, 어떤 날 종목을 봤을 때 10퍼센트 이상의 상승을 가지고 있는 종목들이 필터링 될것이다. 다음은 거래량을 동반하는지 다시 한 번 필터링을 해야할 것이다.
먼저, idx를 통해 얻어낸 것들을 얻어내기 위해 다음과 같은 코드를 적용한다.
x_test = x.iloc[idx]
이제 x_test 는 강한 상승을 얻은 종목들로 구성될 것이다. 다음은 거래량을 동반하는지 살펴볼 것이다. x_test 에 포함된 각 종목들에 대해, 지난 n일간에 거래량과 지난 m일 간의 고점을 조사한다.
for rank,ticker in enumerate(x_test["티커"].values):
highest_price = price_ticker["종가"].iloc[date_idx-int(config.w2):date_idx].max()
max_trades = price_ticker["거래량"].iloc[date_idx-int(config.w3):date_idx].max()
cur_price = price_ticker["종가"].iloc[date_idx]
cur_trades = price_ticker["거래량"].iloc[date_idx]
highest_price는 현재 날짜 (date_idx) 에서 부터config.w2전날 까지 고점(.max()) 을 의미한다.max_trades는 현재 날짜 (date_idx) 에서 부터config.w3전날 까지 거래량 최대(.max()) 을 의미한다.cur_price는 현재 날짜 (date_idx) 의 종가를 의미한다.cur_trades는 무엇을 의미하겠는가? 현재 날짜의 거래량을 의미한다.
이제, 10퍼센트 이상의 상승을 가진 종목들이, 강한 거래량과 고점도 넘었는지 필터링 하기 위해 다음과 같이 코드를 작성해준다.
if cur_price > highest_price and cur_trades > max_trades:
myStock.interesting_stocks[ticker] = [cur_price,cur_trades,config.w5,highest_price]
식을 해석하면, 현재 가격이 지난 config.w2 전날 까지 고점을 넘은 경우에, 그리고 (and) 현재 거래량이 config.w3 까지의 거래량 최대를 넘었는가? 를 의미한다.
필자는 w2와 w3을 모두 20으로 설정하였다. 이 조건을 만족한 종목들을 관심종목 사전에 넣어둔다. 코드는 어려워 보이지만 간단하다. 관심종목티커를 myStock.interesing_stocks 에 넣어두는 것 뿐이다. 필자는 여기에 조건을 만족했을 때의 종가, 거래량 등을 넣어두었다.
이렇게 넣어두면, 나중에 이 정보를 그대로 가져올 수 있다. 예를 들어 POSCO홀딩스 (‘005490’) 이 관심종목에 들어갔다면, myStock.interesing_stocks['005490'] 에 상승을 이루었을 때의 종가, 거래량 등이 들어가게 되는 것이다.
이제 마지막 단계인데, 이 관심종목으로부터 눌림으로부터의 반등이 나오는지 확인하고, 관심 종목을 매수 리스트로 옮길 것이다.
# 관심종목 하나하나 살펴본다.
for ticker in myStock.interesting_stocks.copy().keys():
at_time_trade = myStock.interesting_stocks[ticker][2]
at_time_price = myStock.interesting_stocks[ticker][1]
at_time_trade 는 관심종목에 올랐을 때, 강한 상승을 이루었을 때의 거래량을, at_time_price 는 그때의 가격을 의미한다. 모두 myStock.interesting_stocks[ticker] 에 정보가 담겨있는 것을 알 수 있다.
필자는 다음과 같은 조건일 때 관심 종목을 매수 리스트로 옮겼다. 조건은 다음과 같다.
# rate 는 오늘의 시총 변화량이다. 우리가 주식 어플에서 보는 그 숫자.
condition = rate < config.w6 and rate > config.w7 and price < at_time_price #and price > at_time_support_price
if condition :
myStock.target_upper[ticker] = config.upper
myStock.target_lower[ticker] = config.lower
recommend_indexes.append(np.where(watch_idx == True)[0].item())
condition 이 그것인데, 현재 rate 가 config.w6 보다 작고, config.w7 보다 크면서, 관심 종목에 올랐을 때의 가격보다 작을 때 종목을 매수 리스트로 옮긴다.
필자는 confdig.w6 은 수익률에 크게 연관을 주지 않으며, config.w7 은 0 일 때 수익률이 좋다는 것을 발견하였다. 그래서 config.w6 은 30으로, config.w7 은 0 으로 설정하였다.
즉, 관심종목에 올라와 있는 종목의 가격이 at_time_price 보다 낮으면서 (눌림) 상승을 이루기 시작했을 때 (rate > config.w7 ) 매수를 하는 것이다.
매도 전략
필자는 처음의 경우에 단순하게 다음과 같이 매도를 하였다.
if (rate < config.lower or rate > config.upper or 보유하고 있는 일수 > config.hold):
매도
즉, 내가 설정한 하한보다 작거나, 상한보다 크거나 (목표 달성), 보유일이 내가 설정한 config.hold 보다 높을 때 매도를 하였다.
이때 rate 는 종가기준이다. 하지만, 목표 수익의 경우 그날의 종가 기준이 아닌 고가 기준으로 하면 성공 확률을 올릴 수 있지 않을까? 왜냐하면 주식을 사고, trailing 기법을 이용해서 기준을 정해두기만 한다면, 고가 근처에서 파는 것은 가능하다고 판단했기 때문이다. 따라서, 다음과 같이 가운데 조건의 rate 를 hieghest_rate 로 바꿔 매도 전략을 수정할 수 있다.
if (rate < config.lower or highest_rate > config.upper or 보유하고 있는 일수 > config.hold):
매도
하지만 또 생각이 든것이, 내가 종가 까지 기다리다가 config.lower 를 확 넘어버린다면 실제 테스트와 괴리가 생길것이라고 판단하였다. 그래서, 안전하게 아예 시가에서의 rate를 보고 , 이 시가에서의 rate가 config.lower 보다 작으면 팔아버리는 편이 안전하다고 판단하였다. 따라서, 코드를 다음과 같이 첫번째 조건을 rate 를 start_rate로 수정하였다.
if (start_rate < config.lower or highest_rate > config.upper or 보유하고 있는 일수 > config.hold):
매도
마지막으로 바꾼것은, 내가 주식을 보유하고 있는데, 이미 수익을 보고있는 경우에 어떡할 것인가를 생각해보았다. 가령 난 이미 2~3 % 의 수익을 보고 있는데, config.upper 가 4로 설정되어 팔지 않는다면 괜히 욕심을 부리다가 손해를 볼 수 있겠다는 생각이 들었다. 따라서, 줄 때 먹자 라는 마인드로, 그날의 종가 기준으로 어느정도 수익을 보고있다면 그냥 팔아치우자라는 생각이 들었다.
따라서 식은 다음과 같이 바뀐다.
condition_A = cur - buyDateIdx >= config.hold
condition_B = start_rate < config.lower
condition_C = highest_rate >= config.upper
condition_D = last_rate > config.first_upper
if (condition_A or condition_B or condition_C or condition_D):
매도
condition_A는 내가 보유하고있는 주식의 최대 저장 일 수condition_B는 내가 아침 9시에 장 시작직후 보는 수익률이, 설정한config.lower보다 작을 경우 매도condition_C는 장 중에, 설정한config.upper를 돌파한 경우 바로 매도 (이건 매수하자마자 설정해놔야하는 것이 되겠다.)condition_D는 내가 장이 끝나고 보는 수익률이, 설정한config.fisrt_upper보다 큰 경우
이렇게 하면, 내가 실제 매수,매도를 하는것과 유사하게 하면서도 시가 ~ 종가 사이에 벌어나는 일을 최대한 커버할 수 있겠다는 생각이 들었다.
결과
2020년 2월 24일부터 8월 까지의 결과는 다음과 같다.
2405 번의 승, 881번의 패 , 승률 : 73 %
이겼을 때, 시스템 매매대로 팔아치웠다고 가정했을 때, 이겼을 때의 평균 수익률을 U%,
손실이 났을 때의 평균수익률을 L% 라고 하자.
종목당 100만을 들어간다고 한다면, 이길 때 U 만원, 질 때 -L 만원을 잃고, 이것에 대한 승률이 73 % 이므로 평균 수익률을 계산할 수 있다.
73/100 * U - 27/100 * L 을 계산 해보면, 0.89 가 나온다.
이를 E = 0.89 라고 표기하자.
즉, 한 번 거래할때 기대 수익이 0.89 만원이며, 나는 총 거래를 3200여번 정도를 했으므로, 총 수익은 E * 3200 = 2800 여 만원이 나온다.
종목 당 200만원을 투입한다면? 2800 * 2 만원이 될 것이다.
마무리
꽤나 고무적인 결과인 것 같으면서도, 실제로 매매할 때 발생하는 일을 까먹진 않았는지 확인해보는 작업이 필요하다. 또한, 기대수익 E 를 어떻게 하면 최대화할 수 있을지 고민해봐야하는데, 지금 백 테스트상으로는 무조건 종가에 매수를 하기 때문에, 이것을 이용한다면 E 를 더 키울 수 있지 않을까라는 생각이다.
가령, 관심종목리스트는 왠만하면 항상 가지고 있기 때문에 관심종목 중 어떤 종목이 종가에 조건을 만족할 것 같으면 미리 매수하는 방법이 있다. 간단한 방법으로는 관심종목 중에 + 가 찍히고 있는것들을 미리 매수하는 방법이 있다.