This commit is contained in:
dsyoon
2023-04-16 10:05:54 +09:00
parent 745e455b11
commit c37e26673c
2 changed files with 188 additions and 165 deletions

View File

@@ -262,35 +262,7 @@ class AnalyzerSqlite:
return energy1, energy2
def makeDir(self, dir_code, dir_name):
if os.path.isdir(self.outPath + "/" + dir_code+"_"+dir_name):
os.rmdir(self.outPath + "/" + dir_code+"_"+dir_name)
os.mkdir(self.outPath + "/" + dir_code+"_"+dir_name)
return
def makeDirectory(self, outPath):
self.outPath = outPath
if os.path.isdir(outPath):
shutil.rmtree(outPath)
os.mkdir(outPath)
self.makeDir("0", "final")
self.makeDir("1", "monthly_macd_-300이하")
self.makeDir("2", "monthly_rsi_20이하")
self.makeDir("3", "monthly_EV하단위로_올라옴")
self.makeDir("11", "daily_macd_-1000이하")
self.makeDir("12", "daily_weekly_monthly_rsi_10_20_30이하")
self.makeDir("13", "daily_이전에_없던_거래량")
self.makeDir("14", "daily_이격도")
self.makeDir("15", "daily_낙폭과대")
self.makeDir("16", "daily_EV하단_내려옴")
self.makeDir("17", "daily_BB하단_내려옴")
self.makeDir("18", "daily_rsi_20이하")
return
def writeFile(self, dir_code, dir_name, CODE, NAME, top, stock, state, final_status_count=-1):
def writeFile(self, dir_name, CODE, NAME, top, stock, state, final_status_count=-1):
# 3년 이내 한번이라도 영업이익이 났는지 체크를 함
fnguide = None
if CODE in self.fnguide:
@@ -304,14 +276,10 @@ class AnalyzerSqlite:
if check:
fig = self.draw(stock)
title = "%s (%s), %d, %s 차트 (<a href=\"https://alphasquare.co.kr/home/stock/financial-information?code=%s\">URL1</a>, <a href=\"https://www.tradingview.com/chart/jJ8zOXz0/?symbol=KRX:%s\">URL2</a>)" % (NAME, CODE, stock['close'][0], dir_code+"_"+dir_name, CODE, CODE)
title = "%s (%s), %d, %s 차트 (<a href=\"https://alphasquare.co.kr/home/stock/financial-information?code=%s\">URL1</a>, <a href=\"https://www.tradingview.com/chart/jJ8zOXz0/?symbol=KRX:%s\">URL2</a>)" % (NAME, CODE, stock['close'][0], dir_name, CODE, CODE)
fig['layout'].update(title=title)
fileName = self.outPath + "/" + dir_code+"_"+dir_name
if dir_code in ("0", "6", "26"):
fileName = "%s/%s_%s_%s_%s_%s.html" % (fileName, str(final_status_count), top, NAME.replace(" ", ""), CODE, state)
else:
fileName = self.outPath + "/" + dir_name
fileName = "%s/%s_%s_%s_%s.html" % (fileName, state, NAME.replace(" ", ""), CODE, top)
po.write_html(fig, file=fileName, auto_open=False)
return
@@ -427,6 +395,37 @@ class AnalyzerSqlite:
return stock
def makeDir(self, dir_name):
if os.path.isdir(self.outPath + "/" + dir_name):
os.rmdir(self.outPath + "/" + dir_name)
os.mkdir(self.outPath + "/" + dir_name)
return
def makeDirectory(self, outPath):
self.outPath = outPath
if os.path.isdir(outPath):
shutil.rmtree(outPath)
os.mkdir(outPath)
self.makeDir("final")
self.makeDir("monthly_macd_n이하")
self.makeDir("monthly_rsi_n이하")
self.makeDir("weekly_macd_n이하")
self.makeDir("weekly_rsi_n이하")
self.makeDir("daily_macd_n이하")
self.makeDir("daily_rsi_n이하")
self.makeDir("daily_이전에_없던_거래량")
self.makeDir("daily_이격도")
self.makeDir("daily_낙폭과대")
self.makeDir("daily_EV하단_내려옴")
self.makeDir("daily_BB하단_내려옴")
return
# 후보 찾기
def findCandidates(self, outPath):
self.makeDirectory(outPath)
@@ -460,161 +459,113 @@ class AnalyzerSqlite:
stock_weekly = self.getStockData(stockAnalysisWeeklyTableName, CODE)
stock_monthly = self.getStockData(stockAnalysisMonthlyTableName, CODE)
status = ""
final_status = ""
final_status_count = 0
# 거래량이 100만 이상이고, 종가가 1천원 이상인지 체크 (https://happpy-rich.tistory.com/94)
if stock_weekly['volume'][0] > 100000 and stock_weekly['close'][0] > 1000:
# 거래량이 10만 이상이고, 종가가 1천원 이상인지 체크 (https://happpy-rich.tistory.com/94)
if stock_daily['volume'][0] > 100000 and stock_daily['close'][0] > 1000:
# 종목 상태 체크 분석
# Monthly 체크
if len(stock_monthly['volume']) > 100:
# MACD가 -300 이하에서 macd가 macds 위로 올라온 경우
if len(stock_monthly['close']) > 1:
if stock_monthly['macd'][1] is not None and stock_monthly['macds'][1] is not None and stock_monthly['macd'][0] is not None and stock_monthly['macds'][0] is not None:
if stock_monthly['macd'][0] <= -300:
if stock_monthly['macd'][1] < stock_monthly['macds'][1] and stock_monthly['macds'][0] < stock_monthly['macds'][0]:
dir_code = "1"
dir_name = "monthly_macd_-300이하"
check = self.common.check_macd(stock_monthly, -300)
if check:
dir_name = "monthly_macd_n이하"
final_status_count += 1
status = "{:.2f}".format(stock_monthly['macd'][0]) + "_" + status
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_monthly, status)
log = "{:.2f}".format(stock_monthly['macd'][0])
self.writeFile(dir_name, CODE, NAME, top, stock_monthly, log)
# RSI가 20 이하인 경우, rsi가 rsis위로 올라온 경우
if len(stock_monthly['close']) > 1:
if stock_monthly['rsi'][0] is not None and stock_monthly['rsi'][1] is not None and stock_monthly['rsis'][0] is not None and stock_monthly['rsis'][1] is not None:
if stock_monthly['rsi'][0] <= 20:
if stock_monthly['rsi'][1] < stock_monthly['rsis'][1] and stock_monthly['rsis'][0] < stock_monthly['rsi'][0]:
dir_code = "2"
dir_name = "monthly_rsi_20이하"
check = self.common.check_rsi(stock_monthly, 20)
if check:
dir_name = "monthly_rsi_n이하"
final_status_count += 1
status = "{:.2f}".format(stock_monthly['rsi'][0]) + "_" + status
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_monthly, status)
log = "{:.2f}".format(stock_monthly['rsi'][0])
self.writeFile(dir_name, CODE, NAME, top, stock_monthly, log)
if len(stock_monthly['volume']) > 5:
if stock_monthly['envelope_lower'][0] is not None and stock_monthly['envelope_lower'][1] is not None:
# env 하단에 부딪힘
if stock_monthly['close'][1] < stock_monthly['envelope_lower'][1] and stock_monthly['envelope_lower'][0] < stock_monthly['close'][0]:
if stock_daily['close'][1] < stock_daily['avg5'][1] and stock_daily['avg5'][0] < stock_daily['close'][0]:
dir_code = "3"
dir_name = "monthly_EV하단위로_올라옴"
status = str(top) + "_" + status
# Weekly 체크
if len(stock_weekly['volume']) > 100:
# MACD가 -300 이하에서 macd가 macds 위로 올라온 경우
check = self.common.check_macd(stock_weekly, -300)
if check:
dir_name = "weekly_macd_n이하"
final_status_count += 1
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_monthly, status)
log = "{:.2f}".format(stock_weekly['macd'][0])
self.writeFile(dir_name, CODE, NAME, top, stock_weekly, log)
# RSI가 20 이하인 경우, rsi가 rsis위로 올라온 경우
check = self.common.check_rsi(stock_weekly, 20)
if check:
dir_name = "weekly_rsi_n이하"
final_status_count += 1
log = "{:.2f}".format(stock_weekly['rsi'][0])
self.writeFile(dir_name, CODE, NAME, top, stock_weekly, log)
# 2) daily
if len(stock_daily['volume']) > 100:
# MSCD -500 이하인 경우
if stock_daily['macd'][1] is not None and stock_daily['macds'][1] is not None and stock_daily['macd'][0] is not None and stock_daily['macds'][0] is not None:
if stock_daily['macd'][0] <= -1000:
if stock_daily['macd'][1] < stock_daily['macds'][1] and stock_daily['macds'][0] < stock_daily['macds'][0]:
dir_code = "11"
dir_name = "daily_macd_-1000이하"
check = self.common.check_macd(stock_daily, -1000)
if check:
dir_name = "daily_macd_n이하"
final_status_count += 1
status = "{:.2f}".format(stock_daily['macd'][0]) + "_" + status
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, status)
log = "{:.2f}".format(stock_daily['macd'][0])
self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
# daily_weekly_monthly_rsi_10_20_30이하
if len(stock_monthly['close']) > 1:
if stock_monthly['rsi'][0] is not None and stock_weekly['rsi'][0] is not None and stock_daily['rsi'][0] is not None:
if stock_monthly['rsi'][0] < 30 and stock_weekly['rsi'][0] < 20 and stock_daily['rsi'][0] < 10:
dir_code = "12"
dir_name = "daily_weekly_monthly_rsi_10_20_30이하"
status = str(top) + "_" + status
# RSI가 10 이하인 경우
check = self.common.check_rsi(stock_daily, 10)
if check:
dir_name = "daily_rsi_n이하"
final_status_count += 1
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, status)
log = "{:.2f}".format(stock_daily['macd'][0])
self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
# 52주 200일 기준 평균 + 50% 보다 높은 거래량의 경우
c_index = 200
if len(stock_daily['volume']) < c_index:
c_index = len(stock_daily['volume'])
max_volume = max(stock_daily['volume'][1:c_index])
if max_volume < stock_daily['volume'][0]:
dir_code = "13"
check, log = self.common.check_volume(stock_daily)
if check:
dir_name = "daily_이전에_없던_거래량"
final_status_count += 1
status = "{:.2f}".format((stock_daily['volume'][0] - max_volume)/max_volume)
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, status)
self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
# daily_이격도
if (98<stock_daily['disparity_avg5'][0]<102 and 98<stock_daily['disparity_avg10'][0]<102 and
98<stock_daily['disparity_avg20'][0]<102 and 98<stock_daily['disparity_avg60'][0]<102 and
98<stock_daily['disparity_avg120'][0]<102):
if stock_daily['close'][1] < stock_daily['avg5'][1] and stock_daily['avg5'][0] < stock_daily['close'][0]:
if stock_daily['stochastic_slow_k'][0] < 20 and (stock_daily['stochastic_slow_k'][1] < stock_daily['stochastic_slow_d'][1] and stock_daily['stochastic_slow_d'][0] < stock_monthly['stochastic_slow_k'][0]):
dir_code = "14"
check = self.common.check_disparity(stock_daily)
if check:
dir_name = "daily_이격도"
status = str(top) + "_" + status
log = str(top)
final_status_count += 1
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, status)
self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
# daily_낙폭과대 (60% 이상 하락)
c_index = 52*5
if len(stock_daily['close']) < c_index:
c_index = len(stock_daily['close'])
for idx in range(1,c_index):
if stock_daily['close'][idx-1] < int(stock_daily['close'][idx]/3):
c_index = idx
location = (max(stock_daily['close'][1:c_index]) - stock_daily['close'][0]) / max(stock_daily['close'][1:c_index])
if location > 0.6:
if stock_daily['stochastic_slow_k'][0] is not None and stock_daily['stochastic_slow_k'][1] is not None and stock_daily['stochastic_slow_d'][1] is not None and stock_daily['stochastic_slow_d'][0] is not None and stock_monthly['stochastic_slow_k'][0] is not None:
if stock_daily['stochastic_slow_k'][0] < 20 and (stock_daily['stochastic_slow_k'][1] < stock_daily['stochastic_slow_d'][1] and stock_daily['stochastic_slow_d'][0] < stock_monthly['stochastic_slow_k'][0]):
dir_code = "15"
check = self.common.check_excessive_drop(stock_daily)
if check:
dir_name = "daily_낙폭과대"
final_status_count += 1
status = str(top) + "_" + status
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, status)
log = str(top)
self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
if len(stock_daily['volume']) > 5:
if stock_daily['envelope_lower'][0] is not None and stock_daily['envelope_lower'][1] is not None:
# ev 하단에 부딪힘
if stock_daily['close'][1] < stock_daily['avg5'][1] and stock_daily['avg5'][0] < stock_daily['close'][0]:
check = False
for c in range(1, 4):
if stock_daily['close'][c] < stock_daily['envelope_lower'][c]:
check = True
break
# daily_EV하단_내려옴
check = self.common.check_under_EV_Low(stock_daily)
if check:
dir_code = "16"
dir_name = "daily_EV하단_내려옴"
status = str(top) + "_" + status
log = str(top)
final_status_count += 1
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, status)
self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
if len(stock_daily['volume']) > 5:
if stock_daily['bolingerband_lower'][0] is not None and stock_daily['bolingerband_lower'][1] is not None:
# bb 하단에 부딪힘
if stock_daily['close'][1] < stock_daily['avg5'][1] and stock_daily['avg5'][0] < stock_daily['close'][0]:
check = False
for c in range(1, 4):
if stock_daily['close'][c] < stock_daily['bolingerband_lower'][c]:
check = True
break
check = self.common.check_under_BB_Low(stock_daily)
if check:
dir_code = "17"
dir_name = "daily_BB하단_내려옴"
status = str(top) + "_" + status
log = str(top)
final_status_count += 1
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, status)
self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
# RSI가 20 이하인 경우, rsi가 rsis위로 올라온 경우
if len(stock_daily['close']) > 1:
if stock_daily['rsi'][0] is not None and stock_daily['rsi'][1] is not None and \
stock_daily['rsis'][0] is not None and stock_daily['rsis'][1] is not None:
if stock_daily['rsi'][0] <= 20:
if stock_daily['rsi'][1] < stock_daily['rsis'][1] and stock_daily['rsis'][0] < stock_daily['rsi'][0]:
dir_code = "18"
dir_name = "daily_rsi_20이하"
final_status_count += 1
status = "{:.2f}".format(stock_daily['rsi'][0]) + "_" + status
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, status)
if final_status_count >= 5:
dir_code = "0"
dir_name = "final"
self.writeFile(dir_code, dir_name, CODE, NAME, top, stock_daily, final_status, final_status_count)
self.writeFile(dir_name, CODE, NAME, top, stock_daily, final_status, final_status_count)
return

View File

@@ -480,3 +480,75 @@ class Common:
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
# 거래량 체크
# 52주 200일 기준 평균 + 50% 보다 높은 거래량의 경우
def check_volume(self, stock):
c_index = 200
if len(stock['volume']) < c_index:
c_index = len(stock['volume'])
max_volume = max(stock['volume'][1:c_index])
if max_volume < stock['volume'][0]:
log = "{:.2f}".format((stock['volume'][0] - max_volume)/max_volume)
return True, log
return False, ""
# 이격도 체크
def check_disparity(self, stock):
if (98 < stock['disparity_avg5'][0] < 102 and 98 < stock['disparity_avg10'][0] < 102 and 98 < stock['disparity_avg20'][0] < 102 and 98 < stock['disparity_avg60'][0] < 102 and 98 < stock['disparity_avg120'][0] < 102):
if stock['close'][1] < stock['avg5'][1] and stock['avg5'][0] < stock['close'][0]:
if stock['stochastic_slow_k'][0] < 20:
if stock['stochastic_slow_k'][1] < stock['stochastic_slow_d'][1] and stock['stochastic_slow_d'][0] < stock['stochastic_slow_k'][0]:
return True
return False
# 낙폭 과대 체크
def check_excessive_drop(self, stock):
c_index = 52 * 5
if len(stock['close']) < c_index:
c_index = len(stock['close'])
for idx in range(1, c_index):
if stock['close'][idx - 1] < int(stock['close'][idx] / 3):
c_index = idx
location = (max(stock['close'][1:c_index]) - stock['close'][0]) / max(
stock['close'][1:c_index])
if location > 0.6:
if stock['stochastic_slow_k'][0] is not None and stock['stochastic_slow_k'][1] is not None and stock['stochastic_slow_d'][1] is not None and stock['stochastic_slow_d'][0] is not None and stock['stochastic_slow_k'][0] is not None:
if stock['stochastic_slow_k'][0] < 20 and (stock['stochastic_slow_k'][1] < stock['stochastic_slow_d'][1] and stock['stochastic_slow_d'][0] < stock['stochastic_slow_k'][0]):
return True
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'][1] < stock['avg5'][1] and stock['avg5'][0] < stock['close'][0]:
for c in range(1, 4):
if stock['close'][c] < stock['envelope_lower'][c]:
return True
return False
def check_under_BB_Low(self, stock):
if stock['bolingerband_lower'][0] is not None and stock['bolingerband_lower'][1] is not None:
# bb 하단에 부딪힘
if stock['close'][1] < stock['avg5'][1] and stock['avg5'][0] < stock['close'][0]:
check = False
for c in range(1, 4):
if stock['close'][c] < stock['bolingerband_lower'][c]:
return True
return False