import json import os import shutil from stockpredictor.analysis.Common import Common from stockpredictor.analysis.MACD import MACD from stockpredictor.analysis.RSI import RSI from stockpredictor.analysis.Stochastic import Stochastic from stockpredictor.analysis.IchimokuCloud import IchimokuCloud 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 macd = None rsi = None stochastic = None ichimokuCloud = 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.macd = MACD() self.rsi = RSI() self.stochastic = Stochastic() self.ichimokuCloud = IchimokuCloud() 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): if (data[year1]['영업이익'] > 0 and data[year1]['당기순이익'] > 0): self.fnguide[result[0]] = True if (year2 in data): if (data[year2]['영업이익'] > 0 and data[year2]['당기순이익'] > 0): self.fnguide[result[0]] = True if (year3 in data): if (data[year3]['영업이익'] > 0 and data[year3]['당기순이익'] > 0): self.fnguide[result[0]] = True else: self.fnguide[result[0]] = False else: if (data[year1]['영업이익'] > data[year2]['영업이익']): self.fnguide[result[0]] = True else: self.fnguide[result[0]] = False else: self.fnguide[result[0]] = False else: 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_macd = pd.DataFrame(stock["MACD"][len(stock["MACD"]) - index:last_index+1]) df_stochastic = pd.DataFrame(stock["STOCHASTIC"][len(stock["STOCHASTIC"]) - index:last_index+1]) df_rsi = pd.DataFrame(stock["RSI"][len(stock["RSI"]) - index:last_index+1]) df_ichimoku = pd.DataFrame(stock["ICHIMOKU"][len(stock["ICHIMOKU"]) - index:]) else: index = last_index df_stock = pd.DataFrame(stock["PRICE"][:index+1]) df_macd = pd.DataFrame(stock["MACD"][:index+1]) df_stochastic = pd.DataFrame(stock["STOCHASTIC"][:index+1]) df_ichimoku = pd.DataFrame(stock["ICHIMOKU"][:index+1]) df_rsi = pd.DataFrame(stock["RSI"][:index+1]) # general volume = go.Bar(x=df_stock.DATE, y=df_stock['volume'], name="volume") volume_data = [volume] leadingSpan1 = go.Scatter(x=df_ichimoku.DATE, y=df_ichimoku['leadingSpan1'], name="선행스팬", line_color='#8B4513') leadingSpan2 = go.Scatter(x=df_ichimoku.DATE, y=df_ichimoku['leadingSpan2'], name="후행스팬", line_color='#4169E1') cnadle = go.Candlestick(x=df_stock.DATE, open=df_stock.open, high=df_stock.high, low=df_stock.low, close=df_stock.close, increasing_line_color= 'red', decreasing_line_color= 'blue') ichimokuCloud_data = [leadingSpan1, leadingSpan2, cnadle] # macd macd = go.Scatter(x=df_macd.DATE, y=df_macd['macd'], name="MACD", line_color='#8B4513') macd_signal = go.Scatter(x=df_macd.DATE, y=df_macd['macds'], name="MACD Signal", line_color='#4169E1') oscillator = go.Bar(x=df_macd.DATE, y=df_macd['macdo'], name="oscillator") macd_data = [macd, macd_signal, oscillator] # 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] # rsi rsi = go.Scatter(x=df_macd.DATE, y=df_rsi['rsi'], name="RSI", line_color='#8B4513') rsi_signal = go.Scatter(x=df_macd.DATE, y=df_rsi['rsis'], name="RSI Signal", line_color='#4169E1') rsi_data = [rsi, rsi_signal] fig = subplots.make_subplots(rows=5, cols=1, subplot_titles=('MACD', 'Stochastic', 'RSI', '거래량', '일목균형표')) for trace in macd_data: fig.append_trace(trace, 1, 1) for trace in stochastic_data: fig.append_trace(trace, 2, 1) for trace in rsi_data: fig.append_trace(trace, 3, 1) for trace in volume_data: fig.append_trace(trace, 4, 1) for trace in ichimokuCloud_data: fig.append_trace(trace, 5, 1) fig.update_layout(height=2000) 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 analyzeMACD(self): conn = sqlite3.connect(self.inFileName) cursor = conn.cursor() # 기존 분석 데이터를 모두 지움 cursor.execute('update ' + self.tableName + ' set MACD = ""') 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.macd.analyze(stock) text = json.dumps(results, ensure_ascii=False) cursor.execute("UPDATE " + self.tableName + " SET MACD=? WHERE CODE=?", (text, stock["CODE"])) print("#analyzeMACD", 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 analyzeRSI(self): conn = sqlite3.connect(self.inFileName) cursor = conn.cursor() # 기존 분석 데이터를 모두 지움 cursor.execute('update ' + self.tableName + ' set RSI = ""') rowid = 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() while result != None: prices = json.loads(result[2]) for price in prices: del price['diff'] stock = {"CODE": result[0], "NAME": result[1], "PRICE": prices} results = self.rsi.analyze(stock) text = json.dumps(results, ensure_ascii=False) cursor.execute("UPDATE " + self.tableName + " SET RSI=? WHERE CODE=?", (text, stock["CODE"])) print("#analyzeRSI", 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 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 analyzeIchimokuCloud(self): conn = sqlite3.connect(self.inFileName) cursor = conn.cursor() # 기존 분석 데이터를 모두 지움 cursor.execute('update ' + self.tableName + ' set ICHIMOKU = ""') 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.ichimokuCloud.analyze(stock) text = json.dumps(results, ensure_ascii=False) cursor.execute("UPDATE " + self.tableName + " SET ICHIMOKU=? WHERE CODE=?", (text, stock["CODE"])) print("#analyzeIchimokuCloud", 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, MACD, STOCHASTIC, ICHIMOKU, RSI): """ [매도] 1. MACD 1) MACD가 시그널설을 하향 돌파하면 매도한다. 2) MACD가 기준선 (0) 아래에 있는 한 주가는 하향추세이거나 또는 상승하지 않는다. 3) 시그널 추세가 하향인 한 매수 신호가 나올 때까지 주식을 보유하지 않는다. 4) 하락형 다이버전스가 발생하면 적극 매도를 검토한다. 2. Stochasic: %K선이 %D선을 하향 돌파하면 매도 신호로 보되 다음 사항들이 일치하면 매도한다. 1) 스토캐스틱 지표가 하락추세를 보이고, 50 이하에 있어야 한다. 2) 스토캐스틱 지표가 고점이 낮아지는 하락파동에 있어야 한다. 3) 주가가 5일 또는 20일 이동 평균선 아래에 있어야 한다. 4) MACD 지표가 하락으로 전환하거나 또는 최소한 상승을 멈추어야 한다. 5) 하락형 다이버전스가 발생하면 매도를 검토한다. 3. rsi 1) rsi가 하향이고 70이하로 떨어지면 매도, 2) rsi가 하향이고 50이하로 떨어지면 매도, 3) rsi가 하향이고 30이하로 떨어지면 단기매도, [매수] 1. MACD 1) MACD가 시그널설을 상향 돌파하면 매수한다. 2) MACD가 기준선 (0) 위에 있는 한 주가는 상승추세이거나 또는 하락하지 않는다. 3) 시그널 추세가 상승하는 한 매도 신호가 나올 때까지 주식을 보유한다. 4) 상승형 다이버전스가 발생하면 적극 매수를 검토한다. 2. Stochasic: %K선이 %D선을 상향 돌파하면 매수 신호로 보되 다음 사항들이 일치하면 매수한다. 1) 스토캐스틱 지표가 상승추세를 보이고, 50 이상에 있어야 한다. 2) 스토캐스틱 지표가 저점을 높이는 상승파동에 있어야 한다. 3) 주가가 5일 또는 20일 이동 평균선 위에 있어야 한다. 4) MACD 지표가 상승으로 전환하거나 또는 최소한 하락을 멈추어야 한다. 5) 상승형 다이버전스가 발생하면 매수를 검토한다. 3. rsi 1) 상향이고 30을 돌파하면 매수, 2) rsi가 상향이고 40을 돌파하면 매수, 3) rsi가 상향이고 70을 돌파하면 단기매수, """ i = last_index # 매수금액을 구 # 이전 3일 동안의 어제종가-오늘저가의 평균을 구함 --> (종가-시가)/3 # 그래서 오늘 종가에 구한 평균값을 더해서 내일 종목을 매수함 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: buy_price = round(STOCK[i]['close'] - (buy_price/count)) stochastic_score = self.common.getStochasticScore(STOCHASTIC, i) """ if STOCK[i]['volume'] > 10000: if MACD[i - 1]['macd'] < MACD[i]['macd']: if MACD[i - 1]['macd'] < MACD[i - 1]['macds'] and MACD[i]['macd'] > MACD[i]['macds']: return True,buy_price, stochastic_score if stochastic_score > 0: return True, buy_price, stochastic_score """ if STOCHASTIC[i]['slow_k'] < 10 and self.common.checkLongYangBongAfterUmBong(STOCK, i): return 'STOCHASTIC_YANGBONG', buy_price if STOCHASTIC[i]['slow_k'] < 10: return 'STOCHASTIC', buy_price if self.common.checkLongYangBongAfterUmBong(STOCK, i): return 'YANGBONG', buy_price return "", buy_price def analyzeToFile(self, outFileName): conn = sqlite3.connect(self.inFileName) cursor = conn.cursor() outfp = open(outFileName, "w", encoding="utf-8") 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])} macd = json.loads(result[3]) stochastic = json.loads(result[4]) ichimokuCloud = json.loads(result[5]) rsi = json.loads(result[6]) last_index = self.get_last_index(stock) lastStock = stock['PRICE'] state, buy_price = self.analyzeFinalScore(last_index, lastStock, macd, stochastic, ichimokuCloud, rsi) if state != "": # self.macd.draw(stock) print(stock['CODE'], stock['NAME'], str(buy_price), stochastic[last_index]['slow_k'], macd[last_index]['macd'], rsi[last_index]['rsi_buy'], ichimokuCloud[last_index]['ichimoku_buy']) outfp.write("%s\t%s\t%s\t%d\t%s\t%s\t%s\n"%(stock['CODE'], stock['NAME'], str(buy_price), stochastic[last_index]['slow_k'], rsi[last_index]['rsi_buy'], macd[last_index]['macd'], ichimokuCloud[last_index]['ichimoku_buy'])) print("#file", rowid, stock['NAME']) rowid += 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() cursor.close() conn.close() outfp.close() return # 그래프 출력 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] """ if (item_code in self.fnguide and not self.fnguide[item_code]): rowid += 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) result = cursor.fetchone() continue """ stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2]), "MACD": json.loads(result[3]), "STOCHASTIC": json.loads(result[4]), "ICHIMOKU": json.loads(result[5]), "RSI": json.loads(result[6])} last_index = self.get_last_index(stock) STOCK = stock['PRICE'] MACD = stock['MACD'] STOCHASTIC = stock['STOCHASTIC'] ICHIMOKU = stock['ICHIMOKU'] RSI = stock['RSI'] state, buy_price = self.analyzeFinalScore(last_index, STOCK, MACD, STOCHASTIC, ICHIMOKU, RSI) stochastic_score = STOCHASTIC[last_index]['slow_k'] macd_score = MACD[last_index]['macd'] rsi_score = RSI[last_index]['rsi'] ichimoku_score = ICHIMOKU[last_index]['ichimoku_buy'] if state != "": fig = self.draw(stock) title = "%s (%s), %s, buy_price (%d), stochastic(%.3f), rsi(%.3f), macd(%.3f), ichimoku(%d)) 차트" % (item_name, item_code, state, buy_price, stochastic_score, rsi_score, macd_score, ichimoku_score) fig['layout'].update(title=title) fileName = "%s/%s__%.3f__%.3f__%s.html" % (outPath, state, stochastic_score, rsi_score, item_name.replace(" ", "")) po.write_html(fig, file=fileName, auto_open=False) else: if RSI[last_index]['rsi_buy'] == 1 and STOCK[last_index]['volume'] > 10000: fig = self.draw(stock) title = "%s (%s) buy_price (%d), stochastic(%.3f), rsi(%.3f), macd(%.3f), ichimoku(%d)) 차트"%(item_name, item_code, buy_price, stochastic_score, rsi_score, macd_score, ichimoku_score) fig['layout'].update(title=title) fileName = "%s/%.3f__%.3f__%s.html"%(tmp_path, stochastic_score, rsi_score, item_name.replace(" ", "")) po.write_html(fig, file=fileName, auto_open=False) print ("#html", rowid, stock['NAME']) 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 ICHIMOKU = "", MACD = "", STOCHASTIC = "", RSI = ""') 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_ICHIMOKU = self.ichimokuCloud.analyze(stock) text_ICHIMOKU = json.dumps(results_ICHIMOKU, ensure_ascii=False) results_MACD = self.macd.analyze(stock) text_MACD = json.dumps(results_MACD, ensure_ascii=False) results_STOCHASTIC = self.stochastic.analyze(stock) text_STOCHASTIC = json.dumps(results_STOCHASTIC, ensure_ascii=False) results_RSI = self.rsi.analyze(stock) text_RSI = json.dumps(results_RSI, 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 ICHIMOKU=?, MACD=?, STOCHASTIC=?, RSI=? WHERE CODE=?", (text_ICHIMOKU,text_MACD,text_STOCHASTIC,text_RSI, 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__": PROJECT_HOME = "../.." inFileName = PROJECT_HOME + '/resources/stock.db' inFnguideFileName = PROJECT_HOME + '/resources/fnguide.db' analyzer = Analyzer(PROJECT_HOME, inFileName, inFnguideFileName) # 분석 & update DB """ #print ("analyze IchimokuCloud...") analyzer.analyzeIchimokuCloud() #print ("analyze MACD...") analyzer.analyzeMACD() #print ("analyze Stochastic...") analyzer.analyzeStochastic() #print ("analyze RSI...") analyzer.analyzeRSI() """ ###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("done...")