import json import os import time import shutil from stockpredictor.analysis.Common import Common from stockpredictor.analysis.Stochastic import Stochastic import matplotlib.pyplot as plt import datetime import sqlite3 from datetime import datetime from matplotlib import rc rc('font', family='AppleGothic') plt.rcParams['axes.unicode_minus'] = False import pandas as pd import plotly.graph_objs as go from plotly import tools, subplots import plotly.io as po class Analyzer: tableName = 'stock' PROJECT_HOME = None stocks = None candidate = None stochastic = None common = None inFileName = None fnguideFileName = None fnguide = {} def __init__(self, PROJECT_HOME, inFileName, fnguideFileName): self.PROJECT_HOME = PROJECT_HOME self.inFileName = inFileName self.fnguideFileName = fnguideFileName self.stocks = [] self.candidate = [] self.common = Common() self.stochastic = Stochastic() self.readFnguide() return def readFnguide(self): conn = sqlite3.connect(self.fnguideFileName) cursor = conn.cursor() today = datetime.today() year1 = str(today.year - 1) + ".12.01" year2 = str(today.year - 2) + ".12.01" year3 = str(today.year - 3) + ".12.01" rowid = 1 cursor.execute('SELECT * FROM fnguide WHERE rowid=?', (rowid,)) result = cursor.fetchone() while result != None: data = json.loads(result[2]) self.fnguide[result[0]] = True if (year1 in data and year2 in data and year3 in data): if (data[year1]['영업이익'] < 0 and data[year2]['영업이익'] < 0 and data[year3]['영업이익'] < 0): # 3년 연속 영업이익이 적자이면 매수하지 않는다. self.fnguide[result[0]] = False if (data[year1]['영업이익'] < -100): # 전년 영억적자가 100억 이상이면 매수하지 않는다. self.fnguide[result[0]] = False rowid += 1 cursor.execute('SELECT * FROM fnguide WHERE rowid=?', (rowid,)) result = cursor.fetchone() cursor.close() conn.close() return def draw(self, stock): last_index = self.get_last_index(stock) if last_index > 300: index = 300 # 최대 300일치 그래프 확인 df_stock = pd.DataFrame(stock["PRICE"][len(stock["PRICE"]) - index:]) df_stochastic = pd.DataFrame(stock["STOCHASTIC"][len(stock["STOCHASTIC"]) - index:last_index+1]) else: index = last_index df_stock = pd.DataFrame(stock["PRICE"][:index+1]) df_stochastic = pd.DataFrame(stock["STOCHASTIC"][:index+1]) # general volume = go.Bar(x=df_stock.DATE, y=df_stock['volume'], name="volume") volume_data = [volume] # stochastic slow_k = go.Scatter(x=df_stochastic.DATE, y=df_stochastic['slow_k'], name="Slow%K", line_color='#8B4513') slow_d = go.Scatter(x=df_stochastic.DATE, y=df_stochastic['slow_d'], name="Slow%D", line_color='#4169E1') stochastic_data = [slow_k, slow_d] fig = subplots.make_subplots(rows=2, cols=1, subplot_titles=('거래량', 'Stochastic')) for trace in volume_data: fig.append_trace(trace, 1, 1) for trace in stochastic_data: fig.append_trace(trace, 2, 1) fig.update_layout(height=800) return fig def get_last_index(self, stock): for i in range(0, len(stock['PRICE'])): if (stock['PRICE'][i]['close'] == 0 and stock['PRICE'][i]['open'] == 0 and stock['PRICE'][i]['volume'] == 0): return i-1 return len(stock['PRICE']) - 1 def analyzeStochastic(self): conn = sqlite3.connect(self.inFileName) cursor = conn.cursor() # 기존 분석 데이터를 모두 지움 cursor.execute('update ' + self.tableName + ' set STOCHASTIC = ""') rowid = 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() while result != None: stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2])} results = self.stochastic.analyze(stock) text = json.dumps(results, ensure_ascii=False) cursor.execute("UPDATE " + self.tableName + " SET STOCHASTIC=? WHERE CODE=?", (text, stock["CODE"])) print("#analyzeStochastic", rowid, stock['NAME']) rowid += 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() conn.commit() cursor.close() conn.close() return def analyzeFinalScore(self, last_index, STOCK, STOCHASTIC): """ 매수 조건 #0. 최소 매수 조건은 거래량은 20만건, 종가는 2천원 이상인 종목이어야 한다. #1. 골든크로스: 5일선, 20일선, 60일선, 120일선이 순서대로 나열되는 순간 """ i = last_index buy_price = 0 count = 0 for idx in range(i, i-5, -1): if idx-1 < 0: break buy_price += STOCK[idx-1]['close'] - STOCK[idx]['low'] count += 1 if count == 0: buy_price = STOCK[i]['close'] else: # 종가 - 최저가의 최근 3일 평균 가격을 산정한다. buy_price = round(STOCK[i]['close'] - (buy_price/count)) status = "" if STOCK[i]['volume'] > 100000 and STOCK[i]['close'] > 2000: # 거래량이 100만 이상이고, 종가가 1천원 이상인지 체크 (https://happpy-rich.tistory.com/94) # 정배열 체크 temp_status = self.common.check_RightArrange(STOCK, i) if temp_status != "": #if STOCHASTIC[i]['slow_k'] < 40: status += temp_status # 20일선 돌파 temp_status = self.common.check_Dolpa_Jiji(STOCK, i, '20') if temp_status != "": #if STOCHASTIC[i]['slow_k'] < 40: status += temp_status # 60일선 돌파 temp_status = self.common.check_Dolpa_Jiji(STOCK, i, '60') if temp_status != "": #if STOCHASTIC[i]['slow_k'] < 40: status += temp_status # 120일선 돌파 temp_status = self.common.check_Dolpa_Jiji(STOCK, i, '120') if temp_status != "": #if STOCHASTIC[i]['slow_k'] < 40: status += temp_status # 240일선 돌파 temp_status = self.common.check_Dolpa_Jiji(STOCK, i, '240') if temp_status != "": #if STOCHASTIC[i]['slow_k'] < 40: status += temp_status # 270일선 돌파 temp_status = self.common.check_highest_270(STOCK, i) if temp_status != "": # if STOCHASTIC[i]['slow_k'] < 40: status += temp_status # 20일선 지지 매수가 추천 temp_status = self.common.check_Dolpa_Jiji_20(STOCK, i) if temp_status != "": # if STOCHASTIC[i]['slow_k'] < 40: status += temp_status # 단타 #1 temp_status = self.common.check_Danta1(STOCK, i) if temp_status != "": # if STOCHASTIC[i]['slow_k'] < 40: status += temp_status # 단타 #2 temp_status = self.common.check_Danta2(STOCK, i) if temp_status != "": # if STOCHASTIC[i]['slow_k'] < 40: status += temp_status all_upper_cross_status = self.common.checkAllUpperCross(STOCK, i) if all_upper_cross_status != "": status += all_upper_cross_status # 1주일 동안 몇 10% 이상 오른 종목 W1Rise = self.common.check_W1Rise(STOCK, i, 0.1) if W1Rise != "": status += W1Rise # 1일 동안 몇 10% 이상 내리 종목 W1Fall = self.common.check_D1Fall(STOCK, i, -0.1) if W1Fall != "": status += W1Fall # GOLDENCROSS#1은 바로 매수하지 않고, 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다. # GOLDENCROSS#2은 바로 매수 가능 # GOLDENCROSS#3은 바로 매수 가능 golden_cross_status = self.common.check_golded_cross(STOCK, i) if golden_cross_status != "": status += golden_cross_status # BUYINGBEARMARKET#1은 바로 매수 가능 # BUYINGBEARMARKET#2은 바로 매수 가능 bearmarket_buying_status = self.common.check_bearmarket_buying(STOCK, STOCHASTIC, i) if bearmarket_buying_status != "": status += bearmarket_buying_status # STOCHASTIC stochastic_status = self.common.check_stochastic(STOCK, STOCHASTIC, i) if stochastic_status != "": status += stochastic_status # YANGBONG if self.common.checkLongYangBongAfterUmBong(STOCK, i): # 어제 음봉 이후 장대양봉이었다면, status += 'YANGBONG_' return status, buy_price # 그래프 출력 def analyzeToHtml(self, outPath): tmp_path = outPath + "/tmp" if os.path.isdir(tmp_path): os.rmdir(tmp_path) os.mkdir(tmp_path) conn = sqlite3.connect(self.inFileName) cursor = conn.cursor() rowid = 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() while result != None: item_code = result[0] item_name = result[1] print("#html", rowid, item_name) # 부실 기업은 매수하지 않고 그냥 넘긴다. # kospi 지수와 kosdak 지수도 그냥 넘긴다. if ((item_code in self.fnguide and not self.fnguide[item_code]) or (item_code == "KOSPI" or item_code == "KOSDAK") or result[3] == ''): rowid += 1 # 다음 종목을 가져옴 cursor.execute('SELECT CODE, NAME, PRICE, STOCHASTIC FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() continue result_3 = result[3] if result[3] != result[3]: result_3 = result[3].replace("NaN", "0") if result[3]==None: rowid += 1 cursor.execute('SELECT CODE, NAME, PRICE, STOCHASTIC FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() continue stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2]), "STOCHASTIC": json.loads(result_3)} last_index = self.get_last_index(stock) STOCK = stock['PRICE'] STOCHASTIC = stock['STOCHASTIC'] stochastic_score = STOCHASTIC[last_index]['slow_k'] if stochastic_score < 50: # 종목 상태 체크 분석 state, buy_price = self.analyzeFinalScore(last_index, STOCK, STOCHASTIC) if state != "": fig = self.draw(stock) title = "%s (%s), %s, buy_price (%d), stochastic(%.3f) 차트" % (item_name, item_code, state, buy_price, stochastic_score) fig['layout'].update(title=title) fileName = "%s/%s__%.3f__%s_%s.html" % (outPath, state, stochastic_score, item_name.replace(" ", ""), item_code) po.write_html(fig, file=fileName, auto_open=False) else: if STOCK[last_index]['volume'] > 1000000: fig = self.draw(stock) title = "%s (%s) buy_price (%d), stochastic(%.3f) 차트"%(item_name, item_code, buy_price, stochastic_score) fig['layout'].update(title=title) fileName = "%s/%.3f__%s_%s.html"%(tmp_path, stochastic_score, item_name.replace(" ", ""), item_code) po.write_html(fig, file=fileName, auto_open=False) """ try: stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2]), "STOCHASTIC": json.loads(result_3)} last_index = self.get_last_index(stock) STOCK = stock['PRICE'] STOCHASTIC = stock['STOCHASTIC'] stochastic_score = STOCHASTIC[last_index]['slow_k'] if stochastic_score < 50: # 종목 상태 체크 분석 state, buy_price = self.analyzeFinalScore(last_index, STOCK, STOCHASTIC) if state != "": fig = self.draw(stock) title = "%s (%s), %s, buy_price (%d), stochastic(%.3f) 차트" % (item_name, item_code, state, buy_price, stochastic_score) fig['layout'].update(title=title) fileName = "%s/%s__%.3f__%s_%s.html" % (outPath, state, stochastic_score, item_name.replace(" ", ""), item_code) po.write_html(fig, file=fileName, auto_open=False) else: if STOCK[last_index]['volume'] > 1000000: fig = self.draw(stock) title = "%s (%s) buy_price (%d), stochastic(%.3f) 차트"%(item_name, item_code, buy_price, stochastic_score) fig['layout'].update(title=title) fileName = "%s/%.3f__%s_%s.html"%(tmp_path, stochastic_score, item_name.replace(" ", ""), item_code) po.write_html(fig, file=fileName, auto_open=False) except: print ("error") """ rowid += 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() cursor.close() conn.close() return def analyze(self): conn = sqlite3.connect(self.inFileName) cursor = conn.cursor() # 기존 분석 데이터를 모두 지움 cursor.execute('update ' + self.tableName + ' set STOCHASTIC = ""') rowid = 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() while result != None: stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2])} try: results_STOCHASTIC = self.stochastic.analyze(stock) text_STOCHASTIC = json.dumps(results_STOCHASTIC, ensure_ascii=False) except: print("#", rowid, stock['NAME']) rowid += 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() continue cursor.execute("UPDATE " + self.tableName + " SET STOCHASTIC=? WHERE CODE=?", (text_STOCHASTIC, stock["CODE"])) print("#", rowid, stock['NAME']) rowid += 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() conn.commit() cursor.close() conn.close() return if __name__ == "__main__": start = time.time() PROJECT_HOME = "../.." inFileName = PROJECT_HOME + '/resources/stock.db' inFnguideFileName = PROJECT_HOME + '/resources/fnguide.db' analyzer = Analyzer(PROJECT_HOME, inFileName, inFnguideFileName) # 분석 & update DB """ #print ("analyze Stochastic...") analyzer.analyzeStochastic() """ ###analyzer.analyze() day = datetime.today().strftime("%Y%m%d") # HTML 출력 outPath = PROJECT_HOME + "/resources/analysis/"+day if os.path.isdir(outPath): shutil.rmtree(outPath) os.mkdir(outPath) print("print to Html...") analyzer.analyzeToHtml(outPath) # 파일 출력 #print("print to File...") #outFileName = PROJECT_HOME + '/resources/analysis/'+day+'.json' #analyzer.analyzeToFile(outFileName) print("time : %6.2f 초", (time.time() - start)) print("done...")