diff --git a/stockpredictor/analysis/Analyzer.py b/stockpredictor/analysis/Analyzer.py index 26aecee..28853c0 100644 --- a/stockpredictor/analysis/Analyzer.py +++ b/stockpredictor/analysis/Analyzer.py @@ -4,6 +4,7 @@ import time import shutil from stockpredictor.analysis.Common import Common from stockpredictor.analysis.Stochastic import Stochastic +from stockpredictor.analysis.BolingerBand import BolingerBand import matplotlib.pyplot as plt import datetime import sqlite3 @@ -45,6 +46,7 @@ class Analyzer: self.common = Common() self.stochastic = Stochastic() + self.bolingerBand = BolingerBand() self.readFnguide() return @@ -459,7 +461,7 @@ class Analyzer: cursor = conn.cursor() # 기존 분석 데이터를 모두 지움 - cursor.execute('update ' + self.tableName + ' set STOCHASTIC = ""') + cursor.execute('update ' + self.tableName + ' set STOCHASTIC = "", BOLINGERBAND = ""') rowid = 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) @@ -470,6 +472,9 @@ class Analyzer: try: results_STOCHASTIC = self.stochastic.analyze(stock) text_STOCHASTIC = json.dumps(results_STOCHASTIC, ensure_ascii=False) + + results_BolingerBand = self.bolingerBand.analyze(stock) + text_BOLINGERBAND = json.dumps(results_BolingerBand, ensure_ascii=False) except: print("#", rowid, stock['NAME']) rowid += 1 @@ -477,7 +482,7 @@ class Analyzer: result = cursor.fetchone() continue - cursor.execute("UPDATE " + self.tableName + " SET STOCHASTIC=? WHERE CODE=?", (text_STOCHASTIC, stock["CODE"])) + cursor.execute("UPDATE " + self.tableName + " SET STOCHASTIC=?, BOLINGERBAND=? WHERE CODE=?", (text_STOCHASTIC, text_BOLINGERBAND, stock["CODE"])) print("#", rowid, stock['NAME']) rowid += 1 cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) @@ -503,7 +508,7 @@ if __name__ == "__main__": analyzer.analyzeStochastic() """ - ###analyzer.analyze() + analyzer.analyze() day = datetime.today().strftime("%Y%m%d") diff --git a/stockpredictor/analysis/Analyzer_1.py b/stockpredictor/analysis/Analyzer_1.py deleted file mode 100644 index f42043d..0000000 --- a/stockpredictor/analysis/Analyzer_1.py +++ /dev/null @@ -1,432 +0,0 @@ -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...") diff --git a/stockpredictor/analysis/BolingerBand.py b/stockpredictor/analysis/BolingerBand.py new file mode 100644 index 0000000..e981b90 --- /dev/null +++ b/stockpredictor/analysis/BolingerBand.py @@ -0,0 +1,54 @@ +import pandas as pd +from stockpredictor.analysis.Common import Common +import plotly.graph_objs as go +from plotly import tools, subplots +import plotly.io as po + +class BolingerBand: + + common = None + + def __init__(self): + self.common = Common() + return + + def apply(self, df, n=10, m=6, t=6): + # 입력받은 값이 dataframe이라는 것을 정의해줌 + df = pd.DataFrame(df) + + max20 = df["close"].rolling(window=20).mean() + stddev = df["close"].rolling(window=20).std() + upper = max20 + (stddev * 2) # 상단 볼리저 밴드 + lower = max20 - (stddev * 2) # 하단 볼리저 밴드 + middle = (upper + lower ) / 2 + + # dataframe에 컬럼 추가 + #df = df.assign(fast_k=fast_k, slow_k=slow_k, slow_d=slow_d).dropna() + df = df.assign(upper=upper, middle=middle, lower=lower) + + return df + + def analyze(self, stock): + df = pd.DataFrame() + df = df.from_dict(stock['PRICE']) + df = self.apply(df) + + for i in range(len(df.upper)): + stock['PRICE'][i]['upper'] = df.upper.values[i] + stock['PRICE'][i]['middle'] = df.middle.values[i] + stock['PRICE'][i]['lower'] = df.lower.values[i] + + # 0: 중립, 1: 매수, -1: 매도 + stock['PRICE'][i]['bolingerband_buy'] = 0 + + if i > 0: + stock['PRICE'][i]['bolingerband_buy'] = self.common.getBolingerBandScore(stock['PRICE'], i) + + results = [] + for day in stock['PRICE']: + results.append({'DATE': day['DATE'], + 'upper': day['upper'], + 'middle': day['middle'], + 'lower': day['lower'], + 'bolingerband_buy': day['bolingerband_buy']}) + return results \ No newline at end of file diff --git a/stockpredictor/analysis/Common.py b/stockpredictor/analysis/Common.py index d7d2bef..25af5fe 100644 --- a/stockpredictor/analysis/Common.py +++ b/stockpredictor/analysis/Common.py @@ -89,6 +89,9 @@ class Common: return score + def getBolingerBandScore(self, stock, i): + return 0 + def getIchimokuCloudScore(self, stock, i): score = 0 diff --git a/stockpredictor/analysis/Common_1.py b/stockpredictor/analysis/Common_1.py deleted file mode 100644 index 05d9dd9..0000000 --- a/stockpredictor/analysis/Common_1.py +++ /dev/null @@ -1,364 +0,0 @@ - -class Common: - - # 상향 - def checkUpward(self, type, data): - check = True - if type != None: - for i in range(len(data)-1): - # 만약 이전이 이후보다 크다면, 상승이 아님 - if data[i][type] > data[i+1][type]: - check = False - break - else: - for i in range(len(data)-1): - # 만약 이전이 이후보다 크다면, 상승이 아님 - if data[i] > data[i+1]: - check = False - break - return check - - # 하향 - def checkDownward(self, type, data): - check = True - for i in range(len(data)-1): - # 만약 이전이 이후보다 작다면, 하락이 아님 - if data[i][type] < data[i+1][type]: - check = False - break - return check - - # 상향 돌파 - def checkUpwardBreakthrough(self, type1, type2, data): - if (type1 in data[0] and type1 in data[1] and type1 in data[2] and - type2 in data[0] and type2 in data[1] and type2 in data[2]): - - if ((data[0][type1] < data[1][type1] < data[2][type1]) and - (data[0][type1] < data[0][type2] and data[2][type1] > data[2][type2])): - return True - return False - - # 하향 돌파 - def checkDownwardBreakthrough(self, type1, type2, data): - if (type1 in data[0] and type1 in data[1] and type1 in data[2] and - type2 in data[0] and type2 in data[1] and type2 in data[2]): - - if ((data[0][type1] > data[1][type1] > data[2][type1]) and - (data[0][type1] > data[0][type2] and data[2][type1] < data[2][type2])): - return True - return False - - - def getStochasticScore(self, stock, i): - score = 0 - - if (stock[i - 1]['slow_k'] < stock[i]['slow_k'] and - stock[i]['slow_d'] < stock[i]['slow_k']): - if stock[i]['slow_k'] < 5: - score = 8 - elif 5 <= stock[i]['slow_k'] < 10: - score = 7 - elif 10 <= stock[i]['slow_k'] < 15: - score = 6 - elif 15 <= stock[i]['slow_k'] < 20: - score = 5 - elif 20 <= stock[i]['slow_k'] < 30: - score = 4 - elif 30 <= stock[i]['slow_k'] < 40: - score = 3 - elif 40 <= stock[i]['slow_k'] < 50: - score = 2 - else: - score = 1 - - if (stock[i - 1]['slow_k'] > stock[i]['slow_k'] and - stock[i - 1]['slow_k'] > stock[i - 1]['slow_d'] and - stock[i]['slow_k'] < stock[i]['slow_d']): - if stock[i]['slow_k'] > 90: - score = -6 - elif 90 >= stock[i]['slow_k'] > 80: - score = -5 - elif 80 >= stock[i]['slow_k'] > 70: - score = -4 - elif 70 >= stock[i]['slow_k'] > 60: - score = -3 - elif 60 >= stock[i]['slow_k'] > 50: - score = -2 - else: - score = -1 - - return score - - def getIchimokuCloudScore(self, stock, i): - score = 0 - - if stock[i - 1]['leadingSpan1'] != 0 and stock[i - 1]['leadingSpan2'] != 0: - - # 후행스팬 > 선행스펜 일때, 후행스펜 > 어제 주가 > 선행스팬 이고, 오늘 주가 > 후행스팬 < 선행스팬 이라면, 매수 2점 - if (stock[i - 1]['leadingSpan2'] > stock[i - 1]['leadingSpan1'] and stock[i]['leadingSpan2'] > stock[i]['leadingSpan1']): - if (stock[i - 1]['leadingSpan2'] > stock[i - 1]['close'] > stock[i - 1]['leadingSpan1'] and - stock[i]['close'] > stock[i]['leadingSpan2'] > stock[i - 1]['leadingSpan1']): - score = 2 - - # 후행스팬 > 선행스펜 일때, 후행스펜 > 선행스팬 > 어제 주가 이고, 오늘 주가 > 후행스팬 < 선행스팬 이라면, 매수 4점 - if (stock[i - 1]['leadingSpan2'] > stock[i - 1]['leadingSpan1'] and stock[i]['leadingSpan2'] > stock[i]['leadingSpan1']): - if (stock[i - 1]['leadingSpan2'] > stock[i - 1]['leadingSpan1'] > stock[i - 1]['close'] and - stock[i]['close'] > stock[i]['leadingSpan2'] > stock[i - 1]['leadingSpan1']): - score = 4 - - # 선행스팬 > 후행스팬 일때, 선행스팬 > 어제 주가 > 후행스팬 이고, 오늘 주가 > 선행스팬 < 후행스팬 이라면, 매수 1점 - if (stock[i - 1]['leadingSpan1'] > stock[i - 1]['leadingSpan2'] and stock[i]['leadingSpan1'] > stock[i]['leadingSpan2']): - if (stock[i - 1]['leadingSpan1'] > stock[i - 1]['close'] > stock[i - 1]['leadingSpan2'] and - stock[i]['close'] > stock[i]['leadingSpan1'] > stock[i - 1]['leadingSpan2']): - score = 1 - - # 선행스팬 > 후행스팬 일때, 선행스팬 > 후행스팬 > 어제 주가 이고, 오늘 주가 > 선행스팬 < 후행스팬 이라면, 매수 3점 - if (stock[i - 1]['leadingSpan1'] > stock[i - 1]['leadingSpan2'] and stock[i]['leadingSpan1'] > stock[i]['leadingSpan2']): - if (stock[i - 1]['leadingSpan1'] > stock[i - 1]['leadingSpan2'] > stock[i - 1]['close'] and - stock[i]['close'] > stock[i]['leadingSpan1'] > stock[i - 1]['leadingSpan2']): - score = 3 - - # 어제는 주가가 선행이나 후행스팬 위에 있었지만, 오늘은 두 스팬 모두 아래로 내려왔을 때 매도 - if (stock[i - 1]['close'] > stock[i - 1]['leadingSpan1'] or stock[i - 1]['close'] > stock[i - 1]['leadingSpan2']): - if (stock[i]['close'] < stock[i]['leadingSpan1'] and stock[i]['close'] < stock[i]['leadingSpan2']): - score = -1 - - return score - - def checkLongYangBongAfterUmBong(self, stock, i): - if i > 0: - if stock[i-1]['open'] > stock[i-1]['close']: # 어제가 음봉인지 체크 - if stock[i]['open'] < stock[i]['close'] and stock[i]['close'] == stock[i]['high']: # 오늘 장대양봉인지 체크 - if stock[i-1]['volume']*2 < stock[i]['volume']: # 어제 거래량 보다 두배 이상일 때 - return True - return False - - def checkAllUpperCross(self, stock, i): - if i > 10: - if stock[i]['avg5'] < stock[i]['close'] and stock[i]['avg20'] < stock[i]['close'] and stock[i]['avg60'] < stock[i]['close'] and stock[i]['avg120'] < stock[i]['close']: - for j in range(1, 6): - if stock[i-j]['close'] < stock[i-j]['avg5'] and stock[i-j]['close'] < stock[i-j]['avg20'] and stock[i-j]['close'] < stock[i-j]['avg60'] and stock[i-j]['close'] < stock[i-j]['avg120']: - return "ALLUPPER_" - return "" - - def check_golded_cross(self, stock, i): - if i > 1: - # 60 -> 120 - # 오늘 지수는 120 < 60 < 20 < 5 - # 어제 지수는 60 < 120 이었다. - # 60, 20, 5일선 모두 어제 보다 오늘이 더 높다 (상승중) - # 5일과 20일선은 상승 중이며, 60일선이 120일 선을 뚫고 올라온 순간인지 체크함 (삼성전자 2021-07-29) - # 이때 바로 매수하지 않는다. - # 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다. - if stock[i]['avg120'] < stock[i]['avg60'] < stock[i]['avg20'] < stock[i]['avg5']: - if stock[i-1]['avg120'] > stock[i-1]['avg60']: - if (stock[i-1]['avg60'] < stock[i]['avg60'] and stock[i-1]['avg20'] < stock[i]['avg20'] and stock[i-1]['avg5'] < stock[i]['avg5']): - 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[i]['avg60'] < stock[i]['avg120'] < stock[i]['avg20'] < stock[i]['avg5']: - if stock[i-1]['avg60'] < stock[i-1]['avg20'] < stock[i]['avg120'] < stock[i]['avg5']: - if (stock[i-1]['avg60'] < stock[i]['avg60'] and stock[i-1]['avg20'] < stock[i]['avg20'] and stock[i-1]['avg5'] < stock[i]['avg5']): - return "GOLDEN#2_" - - # 20 -> 120: 5일과 20일, 60일선은 상승 중이며, 20일선이 120일 선을 뚫고 올라온 순간인지 체크 (갤럭시아머니트리 2021-02-08) - # 어제는 60일선 < 120일선 < 5일선 < 20일선이지만, 오늘은 60일선 < 120일선 < 20일선 < 5일선 - if stock[i]['avg60'] < stock[i]['avg120'] < stock[i]['avg20'] < stock[i]['avg5']: - if stock[i-1]['avg60'] < stock[i-1]['avg20'] < stock[i]['avg120'] < stock[i]['avg5']: - if (stock[i-1]['avg60'] < stock[i]['avg60'] and stock[i-1]['avg20'] < stock[i]['avg20'] and stock[i-1]['avg5'] < stock[i]['avg5']): - return "GOLDEN#3_" - - return "" - - def check_bearmarket_buying(self, stock, stochastic, i): - if i > 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[i-1]['avg5'] < stock[i-1]['avg20'] < stock[i-1]['avg120'] < stock[i]['avg60']) and (stock[i]['avg20'] < stock[i-1]['avg120'] < stock[i]['avg60']): - if stock[i]['avg120'] < stock[i-1]['avg120'] and stock[i]['avg60'] < stock[i-1]['avg60'] and stock[i]['avg20'] < stock[i-1]['avg20']: - if stock[i-1]['close'] <= stock[i]['close'] and stock[i]['avg5'] <= stock[i]['close']: - if (stochastic[i]['slow_k'] < 30 and (stochastic[i-1]['slow_k'] < stochastic[i-1]['slow_d'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k'])): - 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[i-1]['avg5'] < stock[i-1]['avg20'] < stock[i-1]['avg60'] < stock[i]['avg120']) and (stock[i]['avg20'] < stock[i-1]['avg60'] < stock[i]['avg120']): - if stock[i]['avg120'] < stock[i-1]['avg120'] and stock[i]['avg60'] < stock[i-1]['avg60'] and stock[i]['avg20'] < stock[i-1]['avg20']: - if stock[i-1]['close'] <= stock[i]['close'] and stock[i]['avg5'] <= stock[i]['close']: - if (stochastic[i]['slow_k'] < 30 and (stochastic[i-1]['slow_k'] < stochastic[i-1]['slow_d'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k'])): - return "BEARMARKET#2_" - return "" - - def check_stochastic(self, stock, stochastic, i): - if i > 2: - # 스토케스틱이 15 이하인 경우 - # 어제보다 slow_k가 상승했고, 오늘 slow_k가 slow_d 위에 있는 경우, - 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']: - return "STOCHASTIC_" - return "" - - def check_stochastic_buying(self, stock, stochastic, ichimoku, i): - if i > 3: - # 삼성전자 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_highest_270(self, stock, i): - # 270일 기준으로 최고가를 기록하는 순간 매수를 시도한다. - # https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_27 - upper_index = 0 - if len(stock) > 271: - top = 0 - for idx in range(2, 271): - # 최근 270일 중 최고가를 구한다. - if top < stock[-idx]['close']: - top = stock[-idx]['close'] - - if top < stock[i]['close']: - return "highest_" - return "" - - def check_Dolpa_Jiji(self, stock, i, day='20'): - upper_index = 0 - if len(stock) > 5: - for idx in range(1, 5): - # day선을 돌파하는 양봉이고, 종가가 최고가 보다 100 이내이어야 한다. - if stock[-idx]['open'] < stock[-idx]["avg"+day] < stock[-idx]['close'] and stock[-idx]['high'] - 100 <= stock[-idx]['close']: - upper_index = idx - break - if upper_index != 0: - for cidx in range(1, upper_index): - # 해당일의 종가보다 현재의 시가가 높거나 같아야 하며, 현재가는 양봉이어야 한다. - if stock[-upper_index]['close'] <= stock[-cidx]['open'] and stock[-cidx]['open'] < stock[-cidx]['close']: - # 해당 기준일 선은 상승이어야 한다. - if stock[-upper_index]['avg'+day] < stock[-cidx]['avg'+day]: - return day + "_" - return "" - - def check_Dolpa_Jiji_20(self, stock, i): - """ - 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) > 61: - if stock[i]['avg20'] < stock[i]['close'] and stock[i]['avg20'] < stock[i]['open']: - if stock[i]['avg5'] < stock[i]['avg20']: - index1 = -1 - for j in range(1, 61): - if stock[i-j]['avg20'] < stock[i-j]['avg5']: - index1 = j - break - top = 0 - for j in range(index1+1, 61): - if stock[i - j]['open'] < stock[i - j]['close']: - if top < stock[i - j]['close']: - top = stock[i - j]['close'] - else: - if top < stock[i - j]['open']: - top = stock[i - j]['open'] - if stock[i-j]['avg5'] < stock[i-j]['avg20']: - break - return "5-20_" - return "" - - def check_Danta1(self, stock, i): - """ - 어제 상한가 혹은 상승양봉이 나온다. - 오늘 상승 출발을 해야 하며 상승 음봉이 나온다 - - 어제 종가 = 어제 상한가 < 종가 < 시가 < 상한가 - https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_109 - - 만약 다음날 시작초가가 오늘 종가보다 높게 상승으로 출발한다면 매수를 한다. - 손절가는 오늘 최저가이다. - """ - if stock[i-1]['open'] < stock[i-1]['close'] == stock[i-1]['high']: - if stock[i-1]['close'] < stock[i]['close'] < stock[i]['open'] < stock[i]['high']: - return "danta1_" - return "" - - def check_Danta2(self, stock, i): - """ - 쐐기, 수렴, 깃대 패턴 확인 - # https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_144 - - 상단 추세선을 돌파하면 매수를 한다. - """ - price_10 = round(stock[i]["close"] / 10) - if stock[-i]["open"] < stock[-i]["close"]: - top = stock[-i]["close"] - bottom = stock[-i]["open"] - else: - top = stock[-i]["open"] - bottom = stock[-i]["close"] - - if len(stock) > 21: - for i in range(2, 21): - if stock[-i]["open"] < stock[-i]["close"]: - if top < stock[-i]["close"]: - top = stock[-i]["close"] - if stock[-i]["open"] < bottom: - bottom = stock[-i]["open"] - else: - if top < stock[-i]["open"]: - top = stock[-i]["open"] - if stock[-i]["close"] < bottom: - bottom = stock[-i]["close"] - - if top - bottom < price_10: - return "danta2_" - return "" - - def check_RightArrange(self, stock, i): - """ - 어제는 정배열이 아니었는데, 오늘은 정배열인 경우 - """ - if len(stock) > 2: - if (not (stock[i-1]["avg120"] < stock[i-1]["avg60"] < stock[i-1]["avg20"] < stock[i-1]["avg5"]) and - (stock[i]["avg120"] < stock[i]["avg60"] < stock[i]["avg20"] < stock[i]["avg5"])): - return "arrange_" - return "" - - def check_W1Rise(self, stock, i, limit): - if len(stock) > 5: - rate = round((stock[i]["close"] - stock[i-4]["close"]) / stock[i-4]["close"],2) - if rate >= limit: - return "1w("+str(rate)+")_" - return "" - - def check_D1Fall(self, stock, i, limit): - if len(stock) > 2: - # 1000, 900, (900 - 1000) / 900 = -0.111 - # 1000, 800, (800 - 1000) / 800 = -0.25 - rate = round((stock[i]["close"] - stock[i-1]["close"]) / stock[i-1]["close"], 2) - if rate <= limit: - return "1d("+str(rate)+")_" - return "" \ No newline at end of file diff --git a/stockpredictor/crawler/pandas/Crawler.py b/stockpredictor/crawler/pandas/Crawler.py new file mode 100644 index 0000000..8f27257 --- /dev/null +++ b/stockpredictor/crawler/pandas/Crawler.py @@ -0,0 +1,2 @@ +# https://grand-unified-engine.tistory.com/21 +# https://github.com/FinanceData/FinanceDataReader