722 lines
35 KiB
Python
722 lines
35 KiB
Python
import numpy as np
|
|
from stock.analysis.MovingAverage import MovingAverage
|
|
|
|
class Common:
|
|
|
|
# 상향
|
|
def getDirection(self, stock, idx, until=10):
|
|
up, down = 0, 0
|
|
for i in range(idx, idx-(until+1), -1):
|
|
if stock[i - 1] < stock[i]:
|
|
up += 1
|
|
if stock[i - 1] > stock[i]:
|
|
down += 1
|
|
|
|
if down < up:
|
|
if stock[idx - 3] < stock[idx]:
|
|
return 'UP'
|
|
elif up < down:
|
|
if stock[idx - 3] > stock[idx]:
|
|
return 'DOWN'
|
|
return 'EVEN'
|
|
|
|
# 상향 돌파
|
|
def checkUpwardBreakthrough(self, type1, type2, stock):
|
|
if (type1 in stock[0] and type1 in stock[1] and type1 in stock[2] and
|
|
type2 in stock[0] and type2 in stock[1] and type2 in stock[2]):
|
|
|
|
if ((stock[0][type1] < stock[1][type1] < stock[2][type1]) and
|
|
(stock[0][type1] < stock[0][type2] and stock[2][type1] > stock[2][type2])):
|
|
return True
|
|
return False
|
|
|
|
# 하향 돌파
|
|
def checkDownwardBreakthrough(self, type1, type2, stock):
|
|
if (type1 in stock[0] and type1 in stock[1] and type1 in stock[2] and
|
|
type2 in stock[0] and type2 in stock[1] and type2 in stock[2]):
|
|
|
|
if ((stock[0][type1] > stock[1][type1] > stock[2][type1]) and
|
|
(stock[0][type1] > stock[0][type2] and stock[2][type1] < stock[2][type2])):
|
|
return True
|
|
return False
|
|
|
|
# YANGBONG
|
|
# 어제 음봉 이후 장대양봉이었다면, 매수
|
|
def checkLongYangBongAfterUmBong(self, stock):
|
|
if len(stock['close']) > 2:
|
|
if stock['close'][1] < stock['open'][1]: # 어제가 음봉인지 체크
|
|
if stock['open'][0] < stock['close'][0] and stock['close'][0] == stock['high'][0]: # 오늘 장대양봉인지 체크
|
|
if stock['volume'][1]*2 < stock['volume'][0]: # 어제 거래량 보다 두배 이상일 때
|
|
return "UMYANG_"
|
|
return ""
|
|
|
|
def checkDoji(self, stock):
|
|
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 도지인지 체크한다
|
|
if len(stock['close']) > 2:
|
|
# 하락 추세이고
|
|
if stock['close'][1] < stock['close'][2]:
|
|
# 그저께와 어제가 음봉인지 체크
|
|
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
|
|
# 도지 체크
|
|
if stock['open'][0] == stock['close'][0] and stock['low'][0] < stock['close'][0] < stock['high'][0]:
|
|
return "DOJI_"
|
|
return ""
|
|
|
|
def checkGravestone(self, stock):
|
|
# 상승 추세이고, 어제 양봉이고, 오늘 그레이브스톤인지 체크한다
|
|
if len(stock['close']) > 2:
|
|
# 상승 추세이고
|
|
if stock['close'][2] < stock['close'][1]:
|
|
# 어제 양봉인지 체크
|
|
if stock['open'][1] < stock['close'][1]:
|
|
# 오늘 그레이브스톤인지 체크한다
|
|
if stock['open'][0] == stock['close'][0] == stock['low'][0] and stock['low'][0] < stock['high'][0]:
|
|
return "GRAVESTONE_"
|
|
return ""
|
|
|
|
# 하락 추세에서 드레곤플라이가 나오면 매수
|
|
def checkDragonfly(self, stock):
|
|
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 드레곤플라이인지 체크한다
|
|
if len(stock['close']) > 2:
|
|
# 하락 추세이고
|
|
if stock['close'][1] < stock['close'][1]:
|
|
# 그저께와 어제가 음봉인지 체크
|
|
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
|
|
# 오늘 드레곤플라이인지 체크한다
|
|
if stock['open'][0] == stock['close'][0] == stock['high'][0] and stock['low'][0] < stock['high'][0]:
|
|
return "DRAGONEFLY_"
|
|
return ""
|
|
|
|
def checkHammer(self, stock):
|
|
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 해머인지 체크한다
|
|
if len(stock['close']) > 2:
|
|
# 하락 추세이고
|
|
if stock['close'][1] < stock['close'][1]:
|
|
# 그저께와 어제가 음봉인지 체크
|
|
if stock['close'][2] < stock['open'][2] and stock[1]['close'] < stock[1]['open']:
|
|
# 오늘 해머인지 체크한다
|
|
if stock['open'][0] < stock['close'][0]:
|
|
if (stock['close'][0] - stock['open'][0]) * 2 < stock['open'][0] - stock['low'][0]:
|
|
# 윗꼬리가 몸통보다 짧아야 한다.
|
|
if stock['high'][0] - stock['close'][0] < stock['close'][0] - stock['open'][0]:
|
|
return "HAMMER_"
|
|
if stock['close'][0] < stock['open'][0]:
|
|
if (stock['open'][0] - stock['close'][0]) * 2 < stock['close'][0] - stock['low'][0]:
|
|
# 윗꼬리가 몸통보다 짧아야 한다.
|
|
if stock['high'][0] - stock['open'][0] < stock['open'][0] - stock['close'][0]:
|
|
return "HAMMER_"
|
|
return ""
|
|
|
|
def checkHangingman(self, stock):
|
|
# 상승 추세이고, 어제 양봉이고, 오늘 행잉맨인지 체크한다
|
|
if len(stock['close']) > 2:
|
|
# 상승 추세이고
|
|
if stock['close'][2] < stock['close'][1]:
|
|
# 어제 양봉인지 체크
|
|
if stock['open'][1] < stock['close'][1]:
|
|
# 오늘 해머인지 체크한다
|
|
if stock['open'][0] < stock['close'][0]:
|
|
if (stock['close'][0] - stock['open'][0]) * 2 < stock['open'][0] - stock['low'][0]:
|
|
# 윗꼬리가 몸통보다 짧아야 한다.
|
|
if stock['high'][0] - stock['close'][0] < stock['close'][0] - stock['open'][0]:
|
|
return "HANGINGMAN_"
|
|
if stock['close'][0] < stock['open'][0]:
|
|
if (stock['open'][0] - stock['close'][0]) * 2 < stock['close'][0] - stock['low'][0]:
|
|
# 윗꼬리가 몸통보다 짧아야 한다.
|
|
if stock['high'][0] - stock['open'][0] < stock['open'][0] - stock['close'][0]:
|
|
return "HANGINGMAN_"
|
|
return ""
|
|
|
|
def checkEngulfingHigh(self, stock):
|
|
# 하락 추세에서 상승 장악형인지 체크
|
|
if len(stock['close']) > 2:
|
|
# 하락 추세이고
|
|
if stock['close'][1] < stock['close'][2]:
|
|
# 그저께와 어제가 음봉인지 체크
|
|
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
|
|
# 오늘 상승장악형인지 체크
|
|
if stock['open'][0] < stock['close'][0]:
|
|
if stock['open'][1] < stock['close'][0] and stock['open'][0] < stock['close'][1]:
|
|
return "ENHIGH_"
|
|
return ""
|
|
|
|
def checkEngulfingLow(self, stock):
|
|
# 상승 추세에서 하락 장악형인지 체크
|
|
if len(stock['close']) > 2:
|
|
# 상승 추세이고
|
|
if stock['close'][2] < stock['close'][1]:
|
|
# 어제 양봉인지 체크
|
|
if stock['open'][1] < stock['close'][1]:
|
|
# 오늘 하락장악형인지 체크
|
|
if stock['close'][0] < stock['open'][0]:
|
|
if stock['close'][1] < stock['open'][0] and stock['close'][0] < stock['open'][1]:
|
|
return "ENLOW_"
|
|
return ""
|
|
|
|
def checkHaramiHigh(self, stock):
|
|
# # 하락 추세에서 상승포아형인지 체크
|
|
if len(stock['close']) > 2:
|
|
# 하락 추세이고
|
|
if stock['close'][1] < stock['close'][2]:
|
|
# 그저께와 어제가 음봉인지 체크
|
|
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
|
|
# 오늘 상승포아형인지 체크
|
|
if stock['open'][0] < stock['close'][0]:
|
|
if stock['close'][1] < stock['low'][0] and stock['high'][0] < stock['open'][1]:
|
|
return "HAHIGH_"
|
|
return ""
|
|
|
|
def checkHaramiLow(self, stock):
|
|
# 상승 추세에서 하락 포아형인지 체크
|
|
if len(stock['close']) > 2:
|
|
# 상승 추세이고
|
|
if stock['close'][2] < stock['close'][1]:
|
|
# 어제 양봉인지 체크
|
|
if stock['open'][1] < stock['close'][1]:
|
|
# 오늘 하락포아형인지 체크
|
|
if stock['close'][0] < stock['open'][0]:
|
|
if stock['open'][1] < stock['low'][0] and stock['high'][0] < stock['close'][1]:
|
|
return "HALOW_"
|
|
return ""
|
|
|
|
def checkPiercing(self, stock):
|
|
# 하락 추세에서 관통형인지 체크
|
|
if len(stock['close']) > 2:
|
|
# 하락 추세이고
|
|
if stock['close'][1] < stock['close'][2]:
|
|
# 그저께와 어제가 음봉인지 체크
|
|
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
|
|
# 오늘 관통형인지 체크
|
|
if stock['open'][0] < stock['close'][0]:
|
|
if stock['open'][0] < stock['low'][1] and (stock['close'][1] + stock['open'][1])/2 < stock['close'][0] < stock['close'][1]:
|
|
return "PIERCING_"
|
|
return ""
|
|
|
|
def checkDarkCloud(self, stock):
|
|
# 상승 추세에서 흑운형인지 체크
|
|
if len(stock['close']) > 2:
|
|
# 상승 추세이고
|
|
if stock['close'][2] < stock['close'][1]:
|
|
# 어제 양봉인지 체크
|
|
if stock['open'][1] < stock['close'][1]:
|
|
# 오늘 흑운형인지 체크
|
|
if stock['close'][0] < stock['open'][0]:
|
|
if stock['high'][1] < stock['open'][0] and stock['open'][1] < stock['close'][0] < (stock['open'][1] + stock['close'][1])/2:
|
|
return "DARKCLOUD_"
|
|
return ""
|
|
|
|
def checkMorningstar(self, stock):
|
|
# 하락 추세에서 샛별인지 체크
|
|
if len(stock['close']) > 2:
|
|
# 하락 추세이고
|
|
if stock['close'][1] < stock['close'][2]:
|
|
# 그저께와 어제가 음봉인지 체크
|
|
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
|
|
# 오늘 샛별인지 체크
|
|
# 어제 갭 체크
|
|
if stock['open'][1] < stock['close'][2] and stock['close'][1] < stock['close'][2]:
|
|
# 오늘 시가가 어제 종가보다 높으며 양봉
|
|
if stock['close'][1] < stock['open'][0] < stock['close'][0]:
|
|
return "MORNINGSTAR_"
|
|
return ""
|
|
|
|
def checkEveningstar(self, stock):
|
|
# 상승 추세에서 저녁별형인지 체크
|
|
if len(stock['close']) > 2:
|
|
# 상승 추세이고
|
|
if stock['close'][2] < stock['close'][1]:
|
|
# 어제 양봉인지 체크
|
|
if stock['open'][1] < stock['close'][1]:
|
|
# 오늘 저녁별형인지 체크
|
|
# 어제 갭 체크
|
|
if stock['close'][2] < stock['open'][1] and stock['close'][2] < stock['close'][1]:
|
|
# 오늘 시가가 어제 종가보다 낮으며 음봉
|
|
if stock['close'][0] < stock['open'][1] < stock['close'][1]:
|
|
return "EVENINGSTAR_"
|
|
return ""
|
|
|
|
|
|
def checkAllUpperCross(self, stock):
|
|
if len(stock['close']) > 10:
|
|
if stock['avg5'][0] < stock['close'][0] and stock['avg20'][0] < stock['close'][0] and stock['avg60'][0] < stock['close'][0] and stock['avg120'][0] < stock['close'][0]:
|
|
for j in range(1, 6):
|
|
if stock['close'][j] < stock['avg5'][j] and stock['close'][j] < stock['avg20'][j] and stock['close'][j] < stock['avg60'][j] and stock['close'][j] < stock['avg120'][j]:
|
|
return "ALLUPPER_"
|
|
return ""
|
|
|
|
def check_golded_cross(self, stock):
|
|
if len(stock['close']) > 1:
|
|
# 60 -> 120
|
|
# 오늘 지수는 120 < 60 < 20 < 5
|
|
# 어제 지수는 60 < 120 이었다.
|
|
# 60, 20, 5일선 모두 어제 보다 오늘이 더 높다 (상승중)
|
|
# 5일과 20일선은 상승 중이며, 60일선이 120일 선을 뚫고 올라온 순간인지 체크함 (삼성전자 2021-07-29)
|
|
# 이때 바로 매수하지 않는다.
|
|
# 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다.
|
|
if stock['avg120'][0] < stock['avg60'][0] < stock['avg20'][0] < stock['avg5'][0]:
|
|
if stock['avg120'][1] > stock['avg60'][1]:
|
|
if (stock['avg60'][1] < stock['avg60'][0] and stock['avg20'][1] < stock['avg20'][0] and stock['avg5'][1] < stock['avg5'][0]):
|
|
return "GOLDEN#1_"
|
|
|
|
# 20 -> 120: 5일과 20일, 60일선은 상승 중이며, 20일선이 120일 선을 뚫고 올라온 순간인지 체크 (SK 2021-12-09, 나노스 2021-02-04)
|
|
# 어제는 60일선 < 20일선 < 120일선 < 5일선이지만, 오늘은 60일선 < 120일선 < 20일선 < 5일선
|
|
# 이때 바로 매수하지 않는다.
|
|
# 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다.
|
|
if stock['avg60'][0] < stock['avg120'][0] < stock['avg20'][0] < stock['avg5'][0]:
|
|
if stock['avg60'][1] < stock['avg20'][1] < stock['avg120'][0] < stock['avg5'][0]:
|
|
if (stock['avg60'][1] < stock['avg60'][0] and stock['avg20'][1] < stock['avg20'][0] and stock['avg5'][1] < stock['avg5'][0]):
|
|
return "GOLDEN#2_"
|
|
|
|
# 20 -> 120: 5일과 20일, 60일선은 상승 중이며, 20일선이 120일 선을 뚫고 올라온 순간인지 체크 (갤럭시아머니트리 2021-02-08)
|
|
# 어제는 60일선 < 120일선 < 5일선 < 20일선이지만, 오늘은 60일선 < 120일선 < 20일선 < 5일선
|
|
if stock['avg60'][0] < stock['avg120'][0] < stock['avg20'][0] < stock['avg5'][0]:
|
|
if stock['avg60'][1] < stock['avg20'][1] < stock['avg120'][0] < stock['avg5'][0]:
|
|
if (stock['avg60'][1] < stock['avg60'][0] and stock['avg20'][1] < stock['avg20'][0] and stock['avg5'][1] < stock['avg5'][0]):
|
|
return "GOLDEN#3_"
|
|
|
|
return ""
|
|
|
|
def check_bearmarket_buying(self, stock):
|
|
if len(stock['close']) > 1:
|
|
# 5일선 상승 시점 확인 (SK 2020년 3월 24일)
|
|
# 어제는 5일선 < 20일선 < 120일선 < 60일선이며, 오늘은 20일선 < 120일선 < 60일선
|
|
# 어제와 오늘 모두 20일, 60일, 120일선은 모두 하락이다.
|
|
# 어제 종가보다 오늘 종가가 높고, 종가는 5일선 위에 올라왔다.
|
|
# 오늘 slow_k는 30 이하이며, 어제는 slow_d가 높았지만, 오늘은 slow_k가 더 높음
|
|
if (stock['avg5'][1] < stock['avg20'][1] < stock['avg120'][1] < stock['avg60'][0]) and (stock['avg20'][0] < stock['avg120'][1] < stock['avg60'][0]):
|
|
if stock['avg120'][0] < stock['avg120'][1] and stock['avg60'][0] < stock['avg60'][1] and stock['avg20'][0] < stock['avg20'][1]:
|
|
if stock['close'][1] <= stock['close'][0] and stock['avg5'][0] <= stock['close'][0]:
|
|
if (stock['stochastic_slow_k'][0] < 30 and (stock['stochastic_slow_k'][1] < stock['stochastic_slow_d'][1] and stock['stochastic_slow_d'][0] < stock['stochastic_slow_k'][0])):
|
|
return "BEARMARKET#1_"
|
|
|
|
# 5일선 상승 시점 확인 (원풍물산 2020년 3월 24일, NHN한국사이버결제 2018년 11월 2일)
|
|
# 어제는 5일선 < 20일선 < 60일선 < 120일선이며, 오늘은 20일선 < 60일선 < 120일선
|
|
# 어제와 오늘 모두 20일, 60일, 120일선은 모두 하락이다.
|
|
# 어제 종가보다 오늘 종가가 높고, 종가는 5일선 위에 올라왔다.
|
|
# 오늘 slow_k는 30 이하이며, 어제는 slow_d가 높았지만, 오늘은 slow_k가 더 높음
|
|
if (stock['avg5'][1] < stock['avg20'][1] < stock['avg60'][1] < stock['avg120'][0]) and (stock['avg20'][0] < stock['avg60'][1] < stock['avg120'][0]):
|
|
if stock['avg120'][0] < stock['avg120'][1] and stock['avg60'][0] < stock['avg60'][1] and stock['avg20'][0] < stock['avg20'][1]:
|
|
if stock['close'][1] <= stock['close'][0] and stock['avg5'][0] <= stock['close'][0]:
|
|
if (stock['stochastic_slow_k'][0] < 30 and (stock['stochastic_slow_k'][1] < stock['stochastic_slow_d'][1] and stock['stochastic_slow_d'][0] < stock['stochastic_slow_k'][0])):
|
|
return "BEARMARKET#2_"
|
|
return ""
|
|
|
|
def check_stochastic(self, stock):
|
|
if len(stock['close']) > 60:
|
|
# 스토케스틱이 15 이하인 경우
|
|
# 어제보다 slow_k가 상승했고, 오늘 slow_k가 slow_d 위에 있는 경우,
|
|
if stock['stochastic_slow_k'][0] is not None and stock['stochastic_slow_k'][0] < 15:
|
|
if stock['stochastic_slow_k'][1] < stock['stochastic_slow_k'][0] and stock['stochastic_slow_d'][0] < stock['stochastic_slow_k'][0]:
|
|
return "STOCHASTIC_"
|
|
return ""
|
|
|
|
def check_stochastic_buying(self, stock, stochastic, ichimoku, i):
|
|
if len(stock['close']) > 60:
|
|
# 삼성전자 2020년 11월 4일
|
|
# 어제는 slow_K가 Slow_d 아래였지만, 오늘은 slow_K가 Slow_d 보다 높다.
|
|
# 에제의 slow_k는 20보다 작고, 오늘의 slow_K는 30보다 작다
|
|
# 1일전이나, 2, 3일전의 종가가 일목균형표 내의 선행스팬1 아래 존재하며,오늘 고가는 선행스팬1 위에 존재한다.
|
|
# 그저께 시가보다 어제의 시가가, 어제의 시가보다는 오늘의 시가가 높다.
|
|
if (stochastic[i-1]['slow_k'] < stochastic[i-1]['slow_d'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k']):
|
|
if (stochastic[i - 1]['slow_k'] < 20 and stochastic[i]['slow_k'] < 30):
|
|
if ((stock[i-3]['close'] < ichimoku[i-3]['leadingSpan1'] or stock[i-2]['close'] < ichimoku[i-2]['leadingSpan1'] or stock[i-1]['close'] < ichimoku[i-1]['leadingSpan1']) and ichimoku[i-1]['leadingSpan1'] < stock[i-1]['high']):
|
|
if stock[i-2]['open'] < stock[i-1]['open'] < stock[i]['open']:
|
|
return "STOCHASTIC#1_"
|
|
|
|
# 스토케스틱이 15 이하인 경우
|
|
# 어제보다 slow_k가 상승했고, 오늘 slow_k가 slow_d 위에 있는 경우,
|
|
# 오늘 종가가 5일선 위에 있는 경우
|
|
if stochastic[i]['slow_k'] < 15:
|
|
if stochastic[i - 1]['slow_k'] < stochastic[i]['slow_k'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k']:
|
|
if stock[i]['avg5'] < stock[i]['close']:
|
|
return "STOCHASTIC#2_"
|
|
return ""
|
|
|
|
def check_Dolpa(self, stock, i, avg1, avg2):
|
|
upper_index = 0
|
|
if len(stock['close']) > 2:
|
|
if stock[i-1]["avg"+avg1] < stock[i-1]["avg"+avg2] and stock[i]["avg"+avg1] > stock[i]["avg"+avg2]:
|
|
return avg1+"_"+avg2+"_"
|
|
return ""
|
|
|
|
def check_Dolpa_Jiji(self, stock, day='20'):
|
|
upper_index = 0
|
|
if len(stock['close']) > 5:
|
|
for idx in range(1, 5):
|
|
# day선을 돌파하는 양봉이고, 종가가 최고가 보다 100 이내이어야 한다.
|
|
if stock['open'][idx] < stock["avg"+day][idx] < stock['close'][idx] and stock['high'][idx] - 100 <= stock['close'][idx]:
|
|
upper_index = idx
|
|
break
|
|
if upper_index != 0:
|
|
for cidx in range(1, upper_index):
|
|
# 해당일의 종가보다 현재의 시가가 높거나 같아야 하며, 현재가는 양봉이어야 한다.
|
|
if stock['close'][upper_index] <= stock['open'][cidx] and stock['open'][cidx] < stock['close'][cidx]:
|
|
# 해당 기준일 선은 상승이어야 한다.
|
|
if stock['avg'+day][upper_index] < stock['avg'+day][cidx]:
|
|
return day + "_"
|
|
return ""
|
|
|
|
def check_Dolpa_Jiji_20(self, stock):
|
|
"""
|
|
top: 이전 5일선이 20일선 위에 있을 때 최고가
|
|
top일 체크 사항 (20일 < 5일선)
|
|
5일선이 20일 선으로 내려왔다가 다시 20일선 위로 올라왔고, top < 오늘 시가 + 100
|
|
top < 시가 < 종가 라면 다음날 매수한다.
|
|
# https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_80
|
|
"""
|
|
if len(stock['close']) > 61:
|
|
if stock['avg20'][0] < stock['close'][0] and stock['avg20'][0] < stock['open'][0]:
|
|
if stock['avg5'][0] < stock['avg20'][0]:
|
|
index1 = -1
|
|
for j in range(1, 61):
|
|
if stock['avg20'][j] < stock['avg5'][j]:
|
|
index1 = j
|
|
break
|
|
top = 0
|
|
for j in range(index1+1, 61):
|
|
if stock['open'][j] < stock['close'][j]:
|
|
if top < stock['close'][j]:
|
|
top = stock['close'][j]
|
|
else:
|
|
if top < stock['open'][j]:
|
|
top = stock['open'][j]
|
|
if stock['avg5'][j] < stock['avg20'][j]:
|
|
break
|
|
return "5-20_"
|
|
return ""
|
|
|
|
def check_Danta1(self, stock):
|
|
"""
|
|
어제 상한가 혹은 상승양봉이 나온다.
|
|
오늘 상승 출발을 해야 하며 상승 음봉이 나온다
|
|
- 어제 종가 = 어제 상한가 < 종가 < 시가 < 상한가
|
|
https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_109
|
|
|
|
만약 다음날 시작초가가 오늘 종가보다 높게 상승으로 출발한다면 매수를 한다.
|
|
손절가는 오늘 최저가이다.
|
|
"""
|
|
if stock['open'][1] < stock['close'][1] == stock['high'][1]:
|
|
if stock['close'][1] < stock['close'][0] < stock['open'][0] < stock['high'][0]:
|
|
return "danta1_"
|
|
return ""
|
|
|
|
def check_Danta2(self, stock):
|
|
"""
|
|
쐐기, 수렴, 깃대 패턴 확인
|
|
# https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_144
|
|
|
|
상단 추세선을 돌파하면 매수를 한다.
|
|
"""
|
|
price_10 = round(stock["close"][0] / 10)
|
|
if stock["open"][0] < stock["close"][0]:
|
|
top = stock["close"][0]
|
|
bottom = stock["open"][0]
|
|
else:
|
|
top = stock["open"][0]
|
|
bottom = stock["close"][0]
|
|
|
|
if len(stock) > 21:
|
|
for i in range(2, 21):
|
|
if stock["open"][i] < stock["close"][i]:
|
|
if top < stock["close"][i]:
|
|
top = stock["close"][i]
|
|
if stock["open"][i] < bottom:
|
|
bottom = stock["open"][i]
|
|
else:
|
|
if top < stock["open"][i]:
|
|
top = stock["open"][i]
|
|
if stock["close"][i] < bottom:
|
|
bottom = stock["close"][i]
|
|
|
|
if top - bottom < price_10:
|
|
return "danta2_"
|
|
return ""
|
|
|
|
def check_RightArrange(self, stock):
|
|
"""
|
|
어제는 정배열이 아니었는데, 오늘은 정배열인 경우
|
|
"""
|
|
if len(stock['close']) > 2:
|
|
if (not (stock["avg120"][1] < stock["avg60"][1] < stock["avg20"][1] < stock["avg5"][1] < stock["close"][1]) and
|
|
stock["avg120"][0] < stock["avg60"][0] < stock["avg20"][0] < stock["avg5"][0] < stock["close"][0]):
|
|
if stock["avg5"][1] < stock["avg5"][0]:
|
|
return "arrange_"
|
|
return ""
|
|
|
|
def checkHigherUmbong(self, stock):
|
|
# 음봉인데 어제보다 종가가 더 높은 경우
|
|
# 이 경우 정배열 상태인지도 함께 체크를 한다.
|
|
|
|
if len(stock['close']) > 3:
|
|
# 어제는 거래량이 터진 양봉이다.
|
|
if stock['open'][1] < stock['close'][1] and 5*stock['volume'][2] < stock['volume'][1]:
|
|
# 오늘은 음봉인데, 오늘 종가는 어제 시가보다는 높다
|
|
if stock['close'][0] < stock['open'][0] and stock['open'][1] < stock['close'][0]:
|
|
return "HIGHERUMBONG_"
|
|
|
|
return ""
|
|
|
|
|
|
def check_W1Rise(self, stock, limit):
|
|
if len(stock['close']) > 5:
|
|
rate = round((stock["close"][0] - stock["close"][4]) / stock["close"][4],2)
|
|
if rate >= limit:
|
|
return "1w("+str(rate)+")_"
|
|
return ""
|
|
|
|
def check_D1Fall(self, stock, limit):
|
|
if len(stock['close']) > 2:
|
|
# 1000, 900, (900 - 1000) / 900 = -0.111
|
|
# 1000, 800, (800 - 1000) / 800 = -0.25
|
|
rate = round((stock["close"][0] - stock["close"][1]) / stock["close"][1], 2)
|
|
if rate <= limit:
|
|
return "1d("+str(rate)+")_"
|
|
return ""
|
|
|
|
|
|
# macd가 n보다 낮은 지 체크
|
|
def check_macd(self, stock, n=-1000):
|
|
if stock['macd'][0] <= n:
|
|
if stock['macd'][1] < stock['macds'][1] and stock['macds'][0] < stock['macds'][0]:
|
|
return True,
|
|
return False
|
|
|
|
def check_rsi(self, stock, n=10):
|
|
if stock['macd'][0] <= n:
|
|
if stock['macd'][1] < stock['macds'][1] and stock['macds'][0] < stock['macds'][0]:
|
|
return True
|
|
return False
|
|
|
|
def check_env_lower_rsi(self, stock):
|
|
if stock['close'][1] < stock['envelope_lower'][1] and stock['envelope_lower'][0] < stock['close'][0]:
|
|
if stock['rsi'][0] < 50:
|
|
return True
|
|
return False
|
|
|
|
def check_env_upper(self, stock):
|
|
if stock['close'][1] < stock['envelope_upper'][1] and stock['envelope_upper'][0] < stock['close'][0]:
|
|
return True
|
|
return False
|
|
|
|
def check_env_upper_volume(self, stock):
|
|
c_index = 200
|
|
check = True
|
|
max_volume = max(stock['volume'][1:c_index + 1])
|
|
if 0 < max_volume < stock['volume'][0] and stock['close'][1] < stock['close'][0]:
|
|
check = True
|
|
|
|
c_index = 20
|
|
sum_volume = sum(stock['volume'][1:c_index + 1])
|
|
avg_volume = sum_volume / c_index
|
|
if (avg_volume * 5) < stock['volume'][0] and stock['close'][1] < stock['close'][0]:
|
|
check = True
|
|
|
|
if check and stock['close'][1] < stock['envelope_upper'][1] and stock['envelope_upper'][0] < stock['close'][0]:
|
|
return True
|
|
return False
|
|
|
|
|
|
# 거래량 체크
|
|
# 52주 200일 기준 평균 + 50% 보다 높은 거래량의 경우
|
|
def check_volume(self, stock):
|
|
c_index = 200
|
|
|
|
max_volume = max(stock['volume'][1:c_index+1])
|
|
if 0 < max_volume*2 < stock['volume'][0] and stock['close'][1] < stock['close'][0]:
|
|
log = "{:.2f}".format(stock['volume'][0]/max_volume)
|
|
return True, log
|
|
|
|
return False, ""
|
|
|
|
# 이격도 체크
|
|
def check_disparity(self, stock, type="daily"):
|
|
|
|
if (99 < stock['disparity_avg5'][0] < 101 and 98.7 < stock['disparity_avg10'][0] < 101.3 and
|
|
98.5 < stock['disparity_avg20'][0] < 101.5 and 98.3 < stock['disparity_avg60'][0] < 101.7 and
|
|
98 < stock['disparity_avg120'][0] < 102):
|
|
return True
|
|
|
|
return False
|
|
|
|
# 종가가 5일선_돌파
|
|
def check_5_moving_line(self, stock):
|
|
if (stock['close'][1] < stock['avg5'][1] and stock['avg5'][0] < stock['close'][0]):
|
|
if stock['volume'][1] < stock['volume'][0]:
|
|
if stock['open'][0] < stock['close'][0]:
|
|
return True
|
|
|
|
return False
|
|
|
|
# 5일선이 20일선_돌파
|
|
def check_5__20_moving_line(self, stock):
|
|
if (stock['avg5'][1] < stock['avg20'][1] and stock['avg20'][0] < stock['avg5'][0]):
|
|
if stock['volume'][1] < stock['volume'][0]:
|
|
if stock['open'][0] < stock['close'][0]:
|
|
return True
|
|
|
|
return False
|
|
|
|
# 20일선이 60일선_돌파
|
|
def check_20__60_moving_line(self, stock):
|
|
if (stock['avg20'][1] < stock['avg60'][1] and stock['avg60'][0] < stock['avg20'][0]):
|
|
if stock['volume'][1] < stock['volume'][0]:
|
|
if stock['open'][0] < stock['close'][0]:
|
|
return True
|
|
|
|
return False
|
|
|
|
def check_optimal_buy_timeing(self, param, stock):
|
|
check = False
|
|
if len(stock['trend']) < 10:
|
|
return check
|
|
for i in range(10):
|
|
if stock['rsi'][i] is None:
|
|
return check
|
|
for i in range(10):
|
|
if stock['slow_k'][i] is None:
|
|
return check
|
|
|
|
|
|
if (stock['close'][1] is None or stock['close'][0] is None or stock['rsi'][1] is None or stock['rsi'][0] is None):
|
|
return check
|
|
|
|
rise_rate = param['bull'][0] / (param['bull'][0]+param['bear'][0]+param['bull'][0])
|
|
if (
|
|
(stock['macd'][1] < stock['macd'][0] and stock['rsi'][0] < 80) or
|
|
(stock['rsi'][1] < stock['rsi'][0] and np.min(stock['rsi'][:3]) < 35) or
|
|
(stock['rsi'][0] < 35) or
|
|
0.7 <= rise_rate
|
|
):
|
|
# avg300 상승
|
|
if stock['avg300'][1] < stock['avg300'][0]:
|
|
# avg5 < trend
|
|
if stock['avg5'][0] < stock['trend'][0]:
|
|
# avg5 이전 3개 봉 위
|
|
if np.max(stock['avg5'][:3]) < stock['avg5'][0]:
|
|
buy_type = "trend"
|
|
check = True
|
|
|
|
# 상승 추세일 때
|
|
if (stock['macd'][1] < stock['macd'][0] and stock['macds'][0] < stock['macd'][0] or
|
|
stock['rsi'][1] < stock['rsi'][0]):
|
|
|
|
# rsi가 50을 상향 돌파할 때
|
|
if 0.9 <= rise_rate and np.max(stock['rsi'][:5]) < 50 and 50 < stock['rsi'][0]:
|
|
buy_type = "rsi"
|
|
check = True
|
|
|
|
# golden & 거래량
|
|
if stock['avg120'][0] < stock['avg60'][0] < stock['avg20'][0] < stock['avg5'][0]:
|
|
buy_type = "golden"
|
|
check = True
|
|
|
|
# rsi가 30보다 작은 후에 상승일 때
|
|
if np.min(stock['rsi'][:5]) < 30:
|
|
if stock['rsi'][1] < stock['rsi'][0]:
|
|
buy_type = "rsi"
|
|
check = True
|
|
|
|
if not(stock['rsi'][1] < stock['rsi'][0] and stock['rsis'][0] < stock['rsi'][0]):
|
|
check = False
|
|
|
|
return check
|
|
|
|
def buy_stock_candidate(self, param, stock):
|
|
check = False
|
|
if len(stock['trend']) < 10:
|
|
return check
|
|
|
|
if np.average(stock['trend'][1:21]) < stock['trend'][0] and np.average(stock['trend_k'][1:21]) < stock['trend_k'][0]:
|
|
# 1일 트렌드가 시그널 위로 상승 돌파 할 때
|
|
if stock['trend_k'][1] <= stock['trend_s'][1] and stock['trend_s'][0] < stock['trend_k'][0]:
|
|
|
|
# 1일 트렌드가 7일 트렌드보다 작을 때 (상승 초/중기 매수를 위해서)
|
|
if stock['trend_k'][0] < stock['trend'][0]:
|
|
# 상승을 체크하기 위해서 선행 스팬 활용
|
|
check = True
|
|
|
|
# 추세가 상승 중일 때 매수의 관점 (소추세가 하락해 있을 때 매수의 기회)
|
|
# macd가 0 이하에서 macd 매수 체크 (macd가 macds를 상승 돌파)
|
|
if stock['macd'][1] < stock['macds'][1] and stock['macds'][0] < stock['macd'][0] and stock['macd'][0] < np.min(stock['macd'][1:]) * 0.5:
|
|
if stock['avg120'][0] < stock['trend_k'][0]:
|
|
# slow_k가 50이하에서 상승 중이고 slow_d를 상승 돌파 할 때
|
|
check = True
|
|
|
|
# slow_k가 10이하에서 상승 중이고 slow_d를 상승 돌파 할 때
|
|
if stock['slow_k'][1] < stock['slow_d'][1] and stock['slow_d'][0] < stock['slow_k'][0] and stock['slow_k'][0] < 10:
|
|
if stock['avg120'][0] < stock['trend_k'][0]:
|
|
check = True
|
|
|
|
return check
|
|
|
|
# 낙폭 과대 체크
|
|
def check_excessive_drop(self, stock):
|
|
c_index = 200
|
|
if len(stock['close']) > c_index:
|
|
max_value = max(stock['close'][1:c_index+1])
|
|
if max_value * 0.4 < stock['close'][0] < max_value * 0.6:
|
|
return True, "{:.2f}".format(stock['close'][0]/max_value)
|
|
return False, ""
|
|
|
|
def check_under_EV_Low(self, stock):
|
|
if stock['envelope_lower'][0] is not None and stock['envelope_lower'][1] is not None:
|
|
# ev 하단에 부딪힘
|
|
if stock['close'][0] < stock['envelope_lower'][0]:
|
|
return True
|
|
return False
|
|
|
|
def check_under_BB_Low(self, stock):
|
|
if stock['lower'][0] is not None and stock['lower'][1] is not None:
|
|
# bb 하단에 부딪힘
|
|
if stock['close'][0] < stock['lower'][0]:
|
|
return True
|
|
return False
|
|
|
|
# OBV (최저점에서 누적 거래량) 체크
|
|
def check_obv(self, stock):
|
|
lowest_index = -1
|
|
p_price = 99999999999999
|
|
size = len(stock['close'])
|
|
for i in range(size):
|
|
if stock['low'][i] < p_price:
|
|
p_price = stock['low'][i]
|
|
lowest_index = i
|
|
|
|
if lowest_index > 600:
|
|
lowest_index = 600
|
|
|
|
obv = 0
|
|
obvs = []
|
|
for i in range(lowest_index, -1, -1):
|
|
if stock['open'][i] < stock['close'][i]:
|
|
obv += stock['volume'][i]
|
|
elif stock['close'][i] < stock['open'][i]:
|
|
obv -= stock['volume'][i]
|
|
|
|
obvs.append(obv)
|
|
|
|
q_5 = MovingAverage(5)
|
|
q_20 = MovingAverage(20)
|
|
q_60 = MovingAverage(60)
|
|
|
|
if len(obvs) > 60:
|
|
for i in range(len(obvs)):
|
|
q_5.enqueue(obvs[i])
|
|
q_20.enqueue(obvs[i])
|
|
q_60.enqueue(obvs[i])
|
|
|
|
stock['obv5'].append( q_5.avg() )
|
|
stock['obv20'].append( q_20.avg() )
|
|
stock['obv60'].append( q_60.avg() )
|
|
lowest_index -= 1
|
|
|
|
stock['obv5'] = list(reversed(stock['obv5']))
|
|
stock['obv20'] = list(reversed(stock['obv20']))
|
|
stock['obv60'] = list(reversed(stock['obv60']))
|
|
|
|
obvs = list(reversed(obvs))
|
|
if len(obvs) > 60 and obv > 0:
|
|
return obv
|
|
|
|
return -1
|