diff --git a/AnalyzerSqlite.py b/AnalyzerSqlite.py
new file mode 100644
index 0000000..f969c97
--- /dev/null
+++ b/AnalyzerSqlite.py
@@ -0,0 +1,1008 @@
+import os
+import time
+import shutil
+import matplotlib.pyplot as plt
+import datetime
+import sqlite3
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+from matplotlib import rc
+import pandas as pd
+
+rc('font', family='AppleGothic')
+plt.rcParams['axes.unicode_minus'] = False
+
+import plotly.graph_objs as go
+from plotly import subplots
+import plotly.io as po
+
+from stock.analysis.Common import Common
+from stock.analysis.Stochastic import Stochastic
+from stock.analysis.BolingerBand import BolingerBand
+from stock.analysis.IchimokuCloud import IchimokuCloud
+from stock.analysis.RSI import RSI
+from stock.analysis.MACD import MACD
+from stock.analysis.Envelope import Envelope
+from stock.analysis.MFI import MFI
+from stock.analysis.MovingAverage import MovingAverage
+
+class AnalyzerSqlite:
+ stochastic = None
+ bolingerBand = None
+ ichimokuCloud = None
+ rsi = None
+ macd = None
+ envelope = None
+ mfi = None
+
+ topCompany = None
+ fnguide = None
+
+ common = None
+ stockFileName = None
+ analyzedFileName = None
+
+ moving_avg = None
+
+ def __init__(self, stockFileName=None):
+ self.common = Common()
+
+ self.stochastic = Stochastic()
+ self.bolingerBand = BolingerBand()
+ self.ichimokuCloud = IchimokuCloud()
+ self.rsi = RSI()
+ self.macd = MACD()
+ self.envelope = Envelope()
+ self.mfi = MFI()
+
+ if stockFileName is not None:
+ self.stockFileName = stockFileName
+ self.topCompany = self.getTopCompany(stockFileName, 2000)
+ self.fnguide = self.readFnguide(stockFileName)
+
+ return
+
+ def getTopCompany(self, stockFileName, top):
+ conn = sqlite3.connect(stockFileName)
+ cursor = conn.cursor()
+
+ sql = "select DISTINCT CODE, NAME from fnguide order by total_ownership_interest desc limit " + str(top)
+ cursor.execute(sql)
+ result = cursor.fetchall()
+
+ top_company = {}
+ for idx, item in enumerate(result):
+ top_company[item[0]] = (idx+1, item[1])
+
+ cursor.close()
+ conn.close()
+ return top_company
+
+ def readFnguide(self, fnguideFileName):
+ conn = sqlite3.connect(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"
+
+ sql = "SELECT CODE, NAME, ymd, business_profits, business_profits_ratio, debt_ratio, ROA, ROE, EPS, BPS, DPS, PER, PBR FROM fnguide "
+ sql += " WHERE (ymd=? or ymd=? or ymd=?) and type=''"
+ sql += " order by code, ymd desc"
+ cursor.execute(sql, (year1,year2,year3))
+ result = cursor.fetchall()
+
+ fnguide = {}
+ for item in result:
+ if item[0] not in fnguide:
+ fnguide[item[0]] = []
+
+ fnguide[item[0]].append(
+ {'NAME': item[1],
+ 'ymd': item[2],
+ 'business_profits': item[3],
+ 'business_profits_ratio': item[4],
+ 'debt_ratio': item[5],
+ 'ROA': item[6],
+ 'ROE': item[7],
+ 'EPS': item[8],
+ 'BPS': item[9],
+ 'DPS': item[10],
+ 'PER': item[11],
+ 'PBR': item[12]})
+
+ cursor.close()
+ conn.close()
+ return fnguide
+
+ def draw(self, stock):
+ # 참고) https://sjblog1.tistory.com/45
+
+ ymd = list(reversed(stock['ymd']))
+ open = list(reversed(stock['open']))
+ close = list(reversed(stock['close']))
+ high = list(reversed(stock['high']))
+ low = list(reversed(stock['low']))
+ volume = list(reversed(stock['volume']))
+ avg3 = list(reversed(stock['avg3']))
+ avg4 = list(reversed(stock['avg4']))
+ avg5 = list(reversed(stock['avg5']))
+ avg6 = list(reversed(stock['avg6']))
+ avg10 = list(reversed(stock['avg10']))
+ avg12 = list(reversed(stock['avg12']))
+ avg20 = list(reversed(stock['avg20']))
+ avg36 = list(reversed(stock['avg36']))
+ avg40 = list(reversed(stock['avg40']))
+ avg48 = list(reversed(stock['avg48']))
+ avg60 = list(reversed(stock['avg60']))
+ avg120 = list(reversed(stock['avg120']))
+ avg240 = list(reversed(stock['avg240']))
+ avg480 = list(reversed(stock['avg480']))
+ disparity_avg5 = list(reversed(stock['disparity_avg5']))
+ disparity_avg10 = list(reversed(stock['disparity_avg10']))
+ disparity_avg20 = list(reversed(stock['disparity_avg20']))
+ disparity_avg60 = list(reversed(stock['disparity_avg60']))
+ disparity_avg120 = list(reversed(stock['disparity_avg120']))
+ macd = list(reversed(stock['macd']))
+ macdo = list(reversed(stock['macdo']))
+ macds = list(reversed(stock['macds']))
+ rsi = list(reversed(stock['rsi']))
+ rsis = list(reversed(stock['rsis']))
+ stochastic_slow_k = list(reversed(stock['slow_k']))
+ stochastic_slow_d = list(reversed(stock['slow_d']))
+ bolingerband_upper = list(reversed(stock['upper']))
+ bolingerband_lower = list(reversed(stock['lower']))
+ bolingerband_middle = list(reversed(stock['middle']))
+ envelope_upper = list(reversed(stock['envelope_upper']))
+ envelope_lower = list(reversed(stock['envelope_lower']))
+ ichimokucloud_changeLine = list(reversed(stock['ichimokucloud_changeLine']))
+ ichimokucloud_baseLine = list(reversed(stock['ichimokucloud_baseLine']))
+ ichimokucloud_laggingSpan = [laggingSpan if -1 < laggingSpan else None for laggingSpan in stock['ichimokucloud_laggingSpan']]
+ ichimokucloud_laggingSpan = list(reversed(ichimokucloud_laggingSpan))
+ ichimokucloud_leadingSpan1 = list(reversed(stock['ichimokucloud_leadingSpan1']))
+ ichimokucloud_leadingSpan2 = list(reversed(stock['ichimokucloud_leadingSpan2']))
+ trend = list(reversed(stock['trend']))
+
+ # general
+ candle_stick = go.Candlestick(x=ymd, open=open, high=high, low=low, close=close, increasing_line_color='red', decreasing_line_color='blue')
+ #avg3 = go.Scatter(x=ymd, y=avg3, name="avg3", line_color='#085F1B')
+ #avg4 = go.Scatter(x=ymd, y=avg4, name="avg4", line_color='#085F1B')
+ avg5 = go.Scatter(x=ymd, y=avg5, name="avg5", line_color='#F73B13')
+ #avg6 = go.Scatter(x=ymd, y=avg6, name="avg6", line_color='#698D09')
+ avg10 = go.Scatter(x=ymd, y=avg10, name="avg10", line_color='#8013ED')
+ #avg12 = go.Scatter(x=ymd, y=avg12, name="avg12", line_color='#000000')
+ avg20 = go.Scatter(x=ymd, y=avg20, name="avg20", line_color='#0A86F4')
+ #avg36 = go.Scatter(x=ymd, y=avg36, name="avg36", line_color='#370557')
+ #avg40 = go.Scatter(x=ymd, y=avg40, name="avg40", line_color='#041366')
+ #avg48 = go.Scatter(x=ymd, y=avg48, name="avg48", line_color='#7A1E66')
+ avg60 = go.Scatter(x=ymd, y=avg60, name="avg60", line_color='#f89543')
+ avg120 = go.Scatter(x=ymd, y=avg120, name="avg120", line_color='#0ed604')
+ avg240 = go.Scatter(x=ymd, y=avg240, name="avg240", line_color='#FF00F7')
+ avg480 = go.Scatter(x=ymd, y=avg480, name="avg480", line_color='#00FF49')
+ bolinger_upper = go.Scatter(x=ymd, y=bolingerband_upper, name="bol_upper", line_color='#8B4513')
+ bolinger_lower = go.Scatter(x=ymd, y=bolingerband_lower, name="bol_lower", line_color='#8B4513')
+ env_upper = go.Scatter(x=ymd, y=envelope_upper, name="env_upper", line_color='#FF33A2')
+ env_lower = go.Scatter(x=ymd, y=envelope_lower, name="env_lower", line_color='#FF33A2')
+ changeLine = go.Scatter(x=ymd, y=ichimokucloud_changeLine, name="changeLine", line_color='#000000')
+ baseLine = go.Scatter(x=ymd, y=ichimokucloud_baseLine, name="baseLine", line_color='#FF0000')
+ laggingSpan = go.Scatter(x=ymd, y=ichimokucloud_laggingSpan, name='laggingSpan', line_color='#B50ABB')
+ leadingSpan1 = go.Scatter(x=ymd, y=ichimokucloud_leadingSpan1, name='leadingSpan1', line_color='black')
+ leadingSpan2 = go.Scatter(x=ymd, y=ichimokucloud_leadingSpan2, name='leadingSpan2', line_color='black')
+ trend = go.Scatter(x=ymd, y=trend, name="trend", line_color='#574e4c')
+
+
+ candle_data = [candle_stick, trend, avg5, avg20, avg60, avg120, avg240, avg480, bolinger_upper, bolinger_lower, changeLine, baseLine, laggingSpan]
+ #candle_data = [candle_stick, trend, avg5, avg10, avg20, avg60, avg120, avg240, bolinger_upper, bolinger_lower, env_upper, env_lower, changeLine, baseLine]
+ #candle_data = [avg5, avg20, trend, changeLine, baseLine, laggingSpan, candle_stick]
+
+ volume = go.Bar(x=ymd, y=volume, marker_color='red', name="volume")
+ volume_data = [volume]
+
+ disparity_avg5 = go.Scatter(x=ymd, y=disparity_avg5, name="disparity_avg5", line_color='#8F8203')
+ disparity_avg10 = go.Scatter(x=ymd, y=disparity_avg10, name="disparity_avg10", line_color='#089B5B')
+ disparity_avg20 = go.Scatter(x=ymd, y=disparity_avg20, name="disparity_avg20", line_color='#ff00ff')
+ disparity_avg60 = go.Scatter(x=ymd, y=disparity_avg60, name="disparity_avg60", line_color='#1469F4')
+ disparity_avg120 = go.Scatter(x=ymd, y=disparity_avg120, name="disparity_avg120", line_color='#000000')
+ disparity_data = [disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120]
+
+ # macd
+ macd_line = go.Scatter(x=ymd, y=macd, line=dict(color='red', width=2), name='macd')
+ macd_s_line = go.Scatter(x=ymd, y=macds, line=dict(dash='dashdot', color='black', width=2), name='macds')
+ macd_o_line = go.Bar(x=ymd, y=macdo, marker_color='purple', name='macdo')
+ macd_data = [macd_line, macd_s_line, macd_o_line]
+
+ # stochastic
+ rsi_line = go.Scatter(x=ymd, y=rsi, line=dict(color='red', width=2), name='rsi')
+ rsis_line = go.Scatter(x=ymd, y=rsis, line=dict(dash='dashdot', color='black', width=2), name='rsis')
+ rsi_data = [rsi_line, rsis_line]
+
+ # stochastic
+ stochastic_slow_k_line = go.Scatter(x=ymd, y=stochastic_slow_k, line=dict(color='red', width=2), name='slow_k')
+ stochastic_slow_d_line = go.Scatter(x=ymd, y=stochastic_slow_d, line=dict(dash='dashdot', color='black', width=2), name='slow_d')
+ stochastic_data = [stochastic_slow_k_line, stochastic_slow_d_line]
+
+ fig = subplots.make_subplots(
+ rows=6, cols=1,
+ subplot_titles=("MACD", "스토캐스틱", "RSI", "이격도", "거래량", '캔들'),
+ # specs=[[{}], [{}], [{}], [{}], [{}], [{}]],
+ shared_xaxes=True, horizontal_spacing=0.03, vertical_spacing=0.01,
+ row_heights=[200, 200, 200, 200, 200, 800]
+ )
+ 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 disparity_data:
+ fig.append_trace(trace, 4, 1)
+ for trace in volume_data:
+ fig.append_trace(trace, 5, 1)
+ for trace in candle_data:
+ fig.append_trace(trace, 6, 1)
+
+ fig.update_layout(height=1900, xaxis_rangeslider_visible=False)
+
+ return fig
+
+ def getPositionalEnergy(self, close):
+ # 260 (= 52 * 5)일 중 가장 찾은 금액과 가장 높았던 금액 중 현재가의 위치 계산
+
+ top = close[0]
+ bottom = close[0]
+
+ for i in range(1, 260):
+ if i >= len(close):
+ break
+ if top < close[i]:
+ top = close[i]
+ if bottom > close[i]:
+ bottom = close[i]
+
+ if top-close[0] == 0:
+ energy1 = 100.0
+ else:
+ energy1 = round((close[0]-bottom) / (top-close[0]), 2)
+
+ energy2 = round((close[0] / top), 2)
+
+ return energy1, energy2
+
+ def writeSummary(self, param):
+ bull = list(reversed(param['bull']))
+ bear = list(reversed(param['bear']))
+ even = list(reversed(param['even']))
+ ymd = [i for i in range(len(bull))]
+
+ bull_line = go.Scatter(x=ymd, y=bull, name="bull", line_color='#FF33A2')
+ bear_line = go.Scatter(x=ymd, y=bear, name="bear", line_color='#1469F4')
+ even_line = go.Scatter(x=ymd, y=even, name="even", line_color='#8B4513')
+
+ line_data = [bull_line, bear_line, even_line]
+
+ fig = subplots.make_subplots(
+ rows=1, cols=1,
+ subplot_titles=("주식 상황"),
+ shared_xaxes=True, horizontal_spacing=0.03, vertical_spacing=0.01,
+ row_heights=[800]
+ )
+ for trace in line_data:
+ fig.append_trace(trace, 1, 1)
+
+ fig.update_layout(height=810, xaxis_rangeslider_visible=False)
+ sum = param['bull'][0] + param['bear'][0] + param['even'][0]
+ title = "[Summary] bull: %d (%.2f), bear: %d (%.2f), even: %d (%.2f)" % (param['bull'][0], param['bull'][0]/sum, param['bear'][0], param['bear'][0]/sum, param['even'][0], param['even'][0]/sum)
+ fig['layout'].update(title=title)
+
+ fileName = "%s/summary.html" % (self.outPath)
+ po.write_html(fig, file=fileName, auto_open=False)
+
+ return
+
+ def writeFile(self, dir_name, CODE, NAME, top, stock, state):
+ # 3년 이내 한번이라도 영업이익이 났는지 체크를 함
+ fnguide = None
+ if CODE in self.fnguide:
+ fnguide = self.fnguide[CODE]
+ check = True
+ if fnguide:
+ check = False
+ for item in fnguide:
+ if item['business_profits'] > 0:
+ check = True
+
+ if check:
+ fig = self.draw(stock)
+ title = "%s (%s), %d, %s 차트 (URL1, URL2, URL3)" % (NAME, CODE, stock['close'][0], dir_name, CODE, CODE, CODE)
+ fig['layout'].update(title=title)
+
+ fileName = self.outPath + "/" + dir_name
+ fileName = "%s/%s_%s_%s_%s_%s.html" % (fileName, datetime.today().strftime("%Y%m%d"), state, top, NAME.replace(" ", ""), CODE)
+ po.write_html(fig, file=fileName, auto_open=False)
+ return
+
+ def checkVolume(self, p_volume, volume):
+ if 0 < p_volume <= 10000 and p_volume * 700 < volume:
+ return True
+ if 10000 < p_volume <= 50000 and p_volume * 40 < volume:
+ return True
+ if 50000 < p_volume <= 100000 and p_volume * 25 < volume:
+ return True
+ if 100000 < p_volume <= 200000 and p_volume * 15 < volume:
+ return True
+ if 200000 < p_volume <= 700000 and p_volume * 13 < volume:
+ return True
+ if 700000 < p_volume <= 1000000 and p_volume * 10 < volume:
+ return True
+ if 5000000 < p_volume <= 5000000 and p_volume * 5 < volume:
+ return True
+ if 5000000 < p_volume and p_volume * 4 < volume:
+ return True
+ return False
+
+ def getStockData(self, TableName, CODE):
+ conn = sqlite3.connect(self.stockFileName)
+ cursor = conn.cursor()
+
+ sql = 'SELECT ymd, close, open, high, low, volume, '
+ sql += ' avg3, avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300, avg360, avg480, avg720, avg1440, '
+ sql += ' disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120, disparity_avg240, disparity_avg480, '
+ sql += ' bolingerband_upper, bolingerband_lower, bolingerband_middle, bolingerband_width, bolingerband_pb, '
+ sql += ' envelope_upper, envelope_lower, envelope_middle, '
+ sql += ' ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_laggingSpan, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2, '
+ sql += ' stochastic_fast_k, stochastic_slow_k, stochastic_slow_d, '
+ sql += ' rsi, rsis, '
+ sql += ' macd, macds, macdo, '
+ sql += ' mfi, '
+ sql += ' trend '
+ sql += ' FROM ' + TableName + ' where CODE=? order by ymd desc limit 512 '
+ cursor.execute(sql, (CODE,))
+ prices = cursor.fetchall()
+
+ cursor.close()
+ conn.close()
+
+ ymd = []
+ close, open, high, low, volume = [], [], [], [], []
+ avg3, avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300, avg360, avg480, avg720, avg1440 = [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []
+ disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120, disparity_avg240, disparity_avg480 = [], [], [], [], [], [], []
+ bolingerband_upper, bolingerband_lower, bolingerband_middle, bolingerband_width, bolingerband_pb = [], [], [], [], []
+ envelope_upper, envelope_lower, envelope_middle = [], [], []
+ ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_laggingSpan, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2 = [], [], [], [], []
+ stochastic_fast_k, stochastic_slow_k, stochastic_slow_d = [], [], []
+ rsi, rsis = [], []
+ macd, macds, macdo = [], [], []
+ mfi = []
+ trend = []
+
+ for price in prices:
+ ymd.append(price[0])
+ close.append(price[1])
+ open.append(price[2])
+ high.append(price[3])
+ low.append(price[4])
+ volume.append(price[5])
+ avg3.append(price[6])
+ avg4.append(price[7])
+ avg5.append(price[8])
+ avg6.append(price[9])
+ avg10.append(price[10])
+ avg12.append(price[11])
+ avg20.append(price[12])
+ avg36.append(price[13])
+ avg40.append(price[14])
+ avg48.append(price[15])
+ avg60.append(price[16])
+ avg120.append(price[17])
+ avg200.append(price[18])
+ avg240.append(price[19])
+ avg300.append(price[20])
+ avg360.append(price[21])
+ avg480.append(price[22])
+ avg720.append(price[23])
+ avg1440.append(price[24])
+ disparity_avg5.append(price[25])
+ disparity_avg10.append(price[26])
+ disparity_avg20.append(price[27])
+ disparity_avg60.append(price[28])
+ disparity_avg120.append(price[29])
+ disparity_avg240.append(price[30])
+ disparity_avg480.append(price[31])
+ bolingerband_upper.append(price[32])
+ bolingerband_lower.append(price[33])
+ bolingerband_middle.append(price[34])
+ bolingerband_width.append(price[35])
+ bolingerband_pb.append(price[36])
+ envelope_upper.append(price[37])
+ envelope_lower.append(price[38])
+ envelope_middle.append(price[39])
+ ichimokucloud_changeLine.append(price[40])
+ ichimokucloud_baseLine.append(price[41])
+ ichimokucloud_laggingSpan.append(price[42])
+ ichimokucloud_leadingSpan1.append(price[43])
+ ichimokucloud_leadingSpan2.append(price[44])
+ stochastic_fast_k.append(price[45])
+ stochastic_slow_k.append(price[46])
+ stochastic_slow_d.append(price[47])
+ rsi.append(price[48])
+ rsis.append(price[49])
+ macd.append(price[50])
+ macds.append(price[51])
+ macdo.append(price[52])
+ mfi.append(price[53])
+ trend.append(price[54])
+
+ stock = {
+ "ymd": ymd,
+ "close": close, "open": open, "high": high, "low": low, "volume": volume,
+ "avg3": avg3, "avg4": avg4, "avg5": avg5, "avg6": avg6, "avg10": avg10, "avg12": avg12, "avg20": avg20, "avg36": avg36, "avg40": avg40, "avg48": avg48, "avg60": avg60, "avg120": avg120, "avg200": avg200, "avg240": avg240, "avg300": avg300, "avg360": avg360, "avg480": avg480, "avg720": avg720, "avg1440": avg1440,
+ "disparity_avg5": disparity_avg5, "disparity_avg10": disparity_avg10, "disparity_avg20": disparity_avg20, "disparity_avg60": disparity_avg60, "disparity_avg120": disparity_avg120, "disparity_avg240": disparity_avg240, "disparity_avg480": disparity_avg480,
+ "upper": bolingerband_upper, "lower": bolingerband_lower, "middle": bolingerband_middle, "width": bolingerband_width, "pb": bolingerband_pb,
+ "envelope_upper": envelope_upper, "envelope_lower": envelope_lower, "envelope_middle": envelope_middle,
+ "ichimokucloud_changeLine": ichimokucloud_changeLine, "ichimokucloud_baseLine": ichimokucloud_baseLine, "ichimokucloud_laggingSpan": ichimokucloud_laggingSpan, "ichimokucloud_leadingSpan1": ichimokucloud_leadingSpan1, "ichimokucloud_leadingSpan2": ichimokucloud_leadingSpan2,
+ "fast_k": stochastic_fast_k, "slow_k": stochastic_slow_k, "slow_d": stochastic_slow_d,
+ "rsi": rsi, "rsis": rsis,
+ "macd": macd, "macds": macds, "macdo": macdo,
+ "mfi": mfi,
+ "trend": trend
+ }
+
+ 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("monthly_env_하단_rsi_50")
+
+ self.makeDir("weekly_BB하단_내려옴")
+
+ self.makeDir("daily_이전에_없던_거래량")
+ self.makeDir("daily_final_candidate")
+ self.makeDir("daily_bol_candidate")
+ self.makeDir('daily_5_20')
+
+ return
+
+ # 후보 찾기
+ def findCandidates(self, outPath):
+ self.makeDirectory(outPath)
+
+ stockTableName = 'stock'
+ stockAnalysisTableName = 'stock_analysis'
+ stockAnalysisWeeklyTableName = 'stock_analysis_weekly'
+ stockAnalysisMonthlyTableName = 'stock_analysis_monthly'
+
+ conn = sqlite3.connect(self.stockFileName)
+ cursor = conn.cursor()
+
+ cursor.execute('SELECT distinct code, name FROM ' + stockTableName + ' order by code')
+ #cursor.execute('select CODE, NAME, max(ymd) as ymd from ' + fnguideTableName + ' where type != "E" group by 1 order by total_assets desc')
+ items = cursor.fetchall()
+
+ cursor.close()
+ conn.close()
+
+ # 상승 종목 개수
+ param = {'bull': [], 'bear': [], 'even': []}
+ for i in range(60):
+ param['bull'].append(0)
+ param['bear'].append(0)
+ param['even'].append(0)
+ for idx, item in enumerate(items):
+ CODE = item[0]
+ stock_daily = self.getStockData(stockAnalysisTableName, CODE)
+ for c in range(len(stock_daily['open'])):
+ if c >= 60:
+ break
+ if stock_daily['open'][c] < stock_daily['close'][c]:
+ param['bull'][c] += 1
+ elif stock_daily['close'][c] < stock_daily['open'][c]:
+ param['bear'][c] += 1
+ else:
+ param['even'][c] += 1
+ self.writeSummary(param)
+
+
+ for idx, item in enumerate(items):
+ CODE = item[0]
+ NAME = item[1]
+ print("#", idx, ", CODE: ", CODE, ", NAME: ", NAME)
+
+ top = "0"
+ if CODE in self.topCompany:
+ top = str(self.topCompany[CODE][0])
+
+ stock_daily = self.getStockData(stockAnalysisTableName, CODE)
+ stock_weekly = self.getStockData(stockAnalysisWeeklyTableName, CODE)
+ stock_monthly = self.getStockData(stockAnalysisMonthlyTableName, CODE)
+
+ count = 0
+ # 거래량이 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']) > 40:
+
+ # ENV 하단 상향 돌파
+ check = self.common.check_env_lower_rsi(stock_monthly)
+ if check:
+ count += 1
+ dir_name = "monthly_env_하단_rsi_50"
+ log = "RSI_" + "{:.2f}".format(stock_monthly['rsi'][0])
+ self.writeFile(dir_name, CODE, NAME, top, stock_monthly, log)
+
+
+ # Weekly 체크
+ if len(stock_weekly['volume']) > 40:
+
+ # 볼린저 밴드 하단 아래
+ check = self.common.check_under_BB_Low(stock_weekly)
+ if check:
+ count += 1
+ dir_name = "weekly_BB하단_내려옴"
+ log = "BB_" + str(top)
+ self.writeFile(dir_name, CODE, NAME, top, stock_weekly, log)
+
+ # 2) daily
+ if len(stock_daily['volume']) > 100:
+
+ # 52주 200일 기준 평균 + 50% 보다 높은 거래량의 경우
+ check, log = self.common.check_volume(stock_daily)
+ if check:
+ count += 1
+ dir_name = "daily_이전에_없던_거래량"
+ log = "이전없던거래량_" + log
+ self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
+
+ check = self.common.check_optimal_buy_timeing(param, stock_daily)
+ if check:
+ count += 1
+ dir_name = "daily_final_candidate"
+ log = str(count) + "_" + dir_name + "_"
+ self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
+
+ check = self.common.buy_stock_candidate(param, stock_daily)
+ if check:
+ count += 1
+ dir_name = "daily_bol_candidate"
+ log = str(count) + "_" + dir_name + "_"
+ self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
+
+ check = self.common.buy_stock_dail5_5_20(stock_daily)
+ if check:
+ count += 1
+ dir_name = "daily_5_20"
+ log = str(count) + "_" + dir_name + "_"
+ self.writeFile(dir_name, CODE, NAME, top, stock_daily, log)
+ return
+
+ def get_moving_average(self, stock):
+ q_3 = MovingAverage(3)
+ q_4 = MovingAverage(4)
+ q_5 = MovingAverage(5)
+ q_6 = MovingAverage(6)
+ q_10 = MovingAverage(10)
+ q_12 = MovingAverage(12)
+ q_20 = MovingAverage(20)
+ q_30 = MovingAverage(30)
+ q_36 = MovingAverage(36)
+ q_40 = MovingAverage(40)
+ q_48 = MovingAverage(48)
+ q_60 = MovingAverage(60)
+ q_120 = MovingAverage(120)
+ q_200 = MovingAverage(200)
+ q_240 = MovingAverage(240)
+ q_300 = MovingAverage(300)
+ q_360 = MovingAverage(360)
+ q_480 = MovingAverage(480)
+ q_720 = MovingAverage(720)
+ q_1440 = MovingAverage(1440)
+
+ for i in range(len(stock)):
+ q_3.enqueue(stock[i]['close'])
+ q_4.enqueue(stock[i]['close'])
+ q_5.enqueue(stock[i]['close'])
+ q_6.enqueue(stock[i]['close'])
+ q_10.enqueue(stock[i]['close'])
+ q_12.enqueue(stock[i]['close'])
+ q_20.enqueue(stock[i]['close'])
+ q_30.enqueue(stock[i]['close'])
+ q_36.enqueue(stock[i]['close'])
+ q_40.enqueue(stock[i]['close'])
+ q_48.enqueue(stock[i]['close'])
+ q_60.enqueue(stock[i]['close'])
+ q_120.enqueue(stock[i]['close'])
+ q_200.enqueue(stock[i]['close'])
+ q_240.enqueue(stock[i]['close'])
+ q_300.enqueue(stock[i]['close'])
+ q_360.enqueue(stock[i]['close'])
+ q_480.enqueue(stock[i]['close'])
+ q_720.enqueue(stock[i]['close'])
+ q_1440.enqueue(stock[i]['close'])
+
+ stock[i]['avg3'] = q_3.avg()
+ stock[i]['avg4'] = q_4.avg()
+ stock[i]['avg5'] = q_5.avg()
+ stock[i]['avg6'] = q_6.avg()
+ stock[i]['avg10'] = q_10.avg()
+ stock[i]['avg12'] = q_12.avg()
+ stock[i]['avg20'] = q_20.avg()
+ stock[i]['avg30'] = q_30.avg()
+ stock[i]['avg36'] = q_36.avg()
+ stock[i]['avg40'] = q_40.avg()
+ stock[i]['avg48'] = q_48.avg()
+ stock[i]['avg60'] = q_60.avg()
+ stock[i]['avg120'] = q_120.avg()
+ stock[i]['avg200'] = q_200.avg()
+ stock[i]['avg240'] = q_240.avg()
+ stock[i]['avg300'] = q_300.avg()
+ stock[i]['avg360'] = q_360.avg()
+ stock[i]['avg480'] = q_480.avg()
+ stock[i]['avg720'] = q_720.avg()
+ stock[i]['avg1440'] = q_1440.avg()
+
+ return
+
+ def get_disparity(self, stock):
+ for i in range(len(stock)):
+ stock[i]['disparity_avg5'] = 100 * (stock[i]["open"] / stock[i]["avg5"])
+ stock[i]['disparity_avg10'] = 100 * (stock[i]["open"] / stock[i]["avg10"])
+ stock[i]['disparity_avg20'] = 100 * (stock[i]["open"] / stock[i]["avg20"])
+ stock[i]['disparity_avg60'] = 100 * (stock[i]["open"] / stock[i]["avg60"])
+ stock[i]['disparity_avg120'] = 100 * (stock[i]["open"] / stock[i]["avg120"])
+ stock[i]['disparity_avg240'] = 100 * (stock[i]["open"] / stock[i]["avg240"])
+ stock[i]['disparity_avg480'] = 100 * (stock[i]["open"] / stock[i]["avg480"])
+
+ return
+
+ def convertFormat(self, weekDict):
+ previous_close = 0
+ stock_price = []
+ for ts in weekDict['open']:
+ stock_price.append(
+ {
+ "ymd": ts.strftime("%Y.%m.%d"),
+ "close": weekDict['close'][ts],
+ "diff": weekDict['close'][ts] - previous_close,
+ "open": weekDict['open'][ts],
+ "high": weekDict['high'][ts],
+ "low": weekDict['low'][ts],
+ "volume": weekDict['volume'][ts],
+ "avg3": -1,
+ "avg4": -1,
+ "avg5": -1,
+ "avg6": -1,
+ "avg10": -1,
+ "avg12": -1,
+ "avg20": -1,
+ "avg30": -1,
+ "avg36": -1,
+ "avg40": -1,
+ "avg48": -1,
+ "avg60": -1,
+ "avg120": -1,
+ "avg200": -1,
+ "avg240": -1,
+ "avg300": -1,
+ "avg360": -1,
+ "avg480": -1,
+ "avg720": -1,
+ "avg1440": -1,
+ "disparity_avg5": -1,
+ "disparity_avg10": -1,
+ "disparity_avg20": -1,
+ "disparity_avg60": -1,
+ "disparity_avg120": -1,
+ "disparity_avg240": -1,
+ "disparity_avg480": -1,
+ "bolingerband_upper": -1,
+ "bolingerband_lower": -1,
+ "bolingerband_middle": -1,
+ "bolingerband_width": -1,
+ "bolingerband_pb": -1,
+ "ichimokucloud_changeLine": -1,
+ "ichimokucloud_baseLine": -1,
+ "ichimokucloud_leadingSpan1": -1,
+ "ichimokucloud_leadingSpan2": -1,
+ "stochastic_fast_k": -1,
+ "stochastic_slow_k": -1,
+ "stochastic_slow_d": -1,
+ "rsi": -1,
+ "rsis": -1,
+ "macd": -1,
+ "macds": -1,
+ "macdo": -1,
+ "mfi": -1,
+ "trend": -1
+ }
+ )
+ previous_close = weekDict['close'][ts]
+
+ return stock_price
+
+ def analyzeAdditionalInfo(self, stock, cursor, type=None):
+ if type==None:
+ stockAnalysisTableName = 'stock_analysis'
+ else:
+ stockAnalysisTableName = 'stock_analysis_' + type
+
+ # 테이블 생성
+ cursor.execute("CREATE TABLE IF NOT EXISTS " + stockAnalysisTableName + " (CODE text, NAME text, ymd text, close REAL, diff REAL, open REAL, high REAL, low REAL, volume REAL, avg3 REAL, avg4 REAL, avg5 REAL, avg6 REAL, avg10 REAL, avg12 REAL, avg20 REAL, avg36 REAL, avg40 REAL, avg48 REAL, avg60 REAL, avg120 REAL, avg200 REAL, avg240 REAL, avg300 REAL, avg360 REAL, avg480 REAL, avg720 REAL, avg1440 REAL, disparity_avg5 REAL, disparity_avg10 REAL, disparity_avg20 REAL, disparity_avg60 REAL, disparity_avg120 REAL, disparity_avg240 REAL, disparity_avg480, bolingerband_upper REAL, bolingerband_lower REAL, bolingerband_middle REAL, bolingerband_width REAL, bolingerband_pb REAL, envelope_upper REAL, envelope_lower REAL, envelope_middle REAL, ichimokucloud_changeLine REAL, ichimokucloud_baseLine REAL, ichimokucloud_laggingSpan REAL, ichimokucloud_leadingSpan1 REAL, ichimokucloud_leadingSpan2 REAL, stochastic_fast_k REAL, stochastic_slow_k REAL, stochastic_slow_d REAL, rsi REAL, rsis REAL, macd REAL, macds REAL, macdo REAL, mfi REAL, trend REAL)")
+
+ # 키 생성
+ create_key = "CREATE INDEX IF NOT EXISTS " + stockAnalysisTableName + "_idx on " + stockAnalysisTableName + " (CODE, ymd) "
+ cursor.execute(create_key)
+
+ # 이동 평균 계산
+ stock["PRICE"] = sorted(stock["PRICE"], key=lambda x: x['ymd'])
+ self.get_moving_average(stock["PRICE"])
+
+ # 이동 평균을 이용한 이격도 계산
+ self.get_disparity(stock["PRICE"])
+
+ self.ichimokuCloud.analyze(stock)
+ self.stochastic.analyze(stock)
+ self.bolingerBand.analyze(stock)
+ self.envelope.analyze(stock)
+ self.rsi.analyze(stock)
+ self.macd.analyze(stock)
+ self.mfi.analyze(stock)
+
+ close_list = [price['close'] for price in stock['PRICE']]
+ for i, price in enumerate(stock['PRICE']):
+ price['trend'] = stock['PRICE'][i]['avg120']
+
+ sorted_stock = sorted(stock["PRICE"], key=lambda x: x['ymd'], reverse=True)
+ for price in sorted_stock:
+ cursor.execute('SELECT * FROM ' + stockAnalysisTableName + ' WHERE CODE=? and ymd=?', (stock['CODE'], price['ymd'],))
+ result = cursor.fetchone()
+ if result == None:
+ sql = "INSERT INTO " + stockAnalysisTableName + "(CODE, NAME, ymd, close, diff, open, high, low, volume, "
+ sql += " avg3, avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300, avg360, avg480, avg720, avg1440, "
+ sql += " disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120, disparity_avg240, disparity_avg480, "
+ sql += " bolingerband_upper, bolingerband_lower, bolingerband_middle, bolingerband_width, bolingerband_pb, "
+ sql += " envelope_upper, envelope_lower, envelope_middle, "
+ sql += " ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_laggingSpan, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2, "
+ sql += " stochastic_fast_k, stochastic_slow_k, stochastic_slow_d, "
+ sql += " rsi, rsis, macd, macds, macdo, "
+ sql += " mfi, "
+ sql += " trend) "
+ sql += " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
+
+ cursor.execute(sql, (
+ stock["CODE"], stock["NAME"], price['ymd'], price['close'], price['diff'], price['open'], price['high'], price['low'], price['volume'],
+ price['avg3'], price['avg4'], price['avg5'], price['avg6'], price['avg10'], price['avg12'], price['avg20'], price['avg36'], price['avg40'], price['avg48'], price['avg60'], price['avg120'], price['avg200'], price['avg240'], price['avg300'], price['avg360'], price['avg480'], price['avg720'], price['avg1440'],
+ price['disparity_avg5'], price['disparity_avg10'], price['disparity_avg20'], price['disparity_avg60'], price['disparity_avg120'], price['disparity_avg240'], price['disparity_avg480'],
+ price['bolingerband_upper'], price['bolingerband_lower'], price['bolingerband_middle'], price['bolingerband_width'], price['bolingerband_pb'],
+ price['envelope_upper'], price['envelope_lower'], price['envelope_middle'],
+ price['ichimokucloud_changeLine'], price['ichimokucloud_baseLine'], price['ichimokucloud_laggingSpan'], price['ichimokucloud_leadingSpan1'], price['ichimokucloud_leadingSpan2'],
+ price['stochastic_fast_k'], price['stochastic_slow_k'], price['stochastic_slow_d'],
+ price['rsi'], price['rsis'], price['macd'], price['macds'], price['macdo'],
+ price['mfi'],
+ price['trend'],
+ ))
+
+ else:
+ sql = "UPDATE " + stockAnalysisTableName + " SET close=?, diff=?, open=?, high=?, low=?, volume=?, "
+ sql += " avg3=?, avg4=?, avg5=?, avg6=?, avg10=?, avg12=?, avg20=?, avg36=?, avg40=?, avg48=?, avg60=?, avg120=?, avg200=?, avg240=?, avg300=?, avg360=?, avg480=?, avg720=?, avg1440=?, "
+ sql += " disparity_avg5=?, disparity_avg10=?, disparity_avg20=?, disparity_avg60=?, disparity_avg120=?, disparity_avg240=?, disparity_avg480=?, "
+ sql += " bolingerband_upper=?, bolingerband_lower=?, bolingerband_middle=?, bolingerband_width=?, bolingerband_pb=?,"
+ sql += " envelope_upper=?, envelope_lower=?, envelope_middle=?, "
+ sql += " ichimokucloud_changeLine=?, ichimokucloud_baseLine=?, ichimokucloud_laggingSpan=?, ichimokucloud_leadingSpan1=?, ichimokucloud_leadingSpan2=?, "
+ sql += " stochastic_fast_k=?, stochastic_slow_k=?, stochastic_slow_d=?, "
+ sql += " rsi=?, rsis=?, "
+ sql += " macd=?, macds=?, macdo=?, "
+ sql += " mfi=?, "
+ sql += " trend=? "
+ sql += " WHERE CODE=? and ymd=?"
+
+ cursor.execute(sql,
+ (price['close'], price['diff'], price['open'], price['high'], price['low'], price['volume'],
+ price['avg3'], price['avg4'], price['avg5'], price['avg6'], price['avg10'], price['avg12'], price['avg20'], price['avg36'], price['avg40'], price['avg48'], price['avg60'], price['avg120'], price['avg200'], price['avg240'], price['avg300'], price['avg360'], price['avg480'], price['avg720'], price['avg1440'],
+ price['disparity_avg5'], price['disparity_avg10'], price['disparity_avg20'], price['disparity_avg60'], price['disparity_avg120'], price['disparity_avg240'], price['disparity_avg480'],
+ price['bolingerband_upper'], price['bolingerband_lower'], price['bolingerband_middle'], price['bolingerband_width'], price['bolingerband_pb'],
+ price['envelope_upper'], price['envelope_lower'], price['envelope_middle'],
+ price['ichimokucloud_changeLine'], price['ichimokucloud_baseLine'], price['ichimokucloud_laggingSpan'], price['ichimokucloud_leadingSpan1'], price['ichimokucloud_leadingSpan2'],
+ price['stochastic_fast_k'], price['stochastic_slow_k'], price['stochastic_slow_d'],
+ price['rsi'], price['rsis'],
+ price['macd'], price['macds'], price['macdo'],
+ price['mfi'],
+ price['trend'],
+ stock["CODE"], price['ymd'],))
+ break
+
+ cursor.execute("commit",)
+ return
+
+ def setItem(self, item):
+ return {
+ "ymd": item[0],
+ "close": item[1],
+ "diff": item[2],
+ "open": item[3],
+ "high": item[4],
+ "low": item[5],
+ "volume": item[6],
+ "avg3": -1,
+ "avg4": -1,
+ "avg5": -1,
+ "avg6": -1,
+ "avg10": -1,
+ "avg12": -1,
+ "avg20": -1,
+ "avg36": -1,
+ "avg40": -1,
+ "avg48": -1,
+ "avg60": -1,
+ "avg120": -1,
+ "avg200": -1,
+ "avg240": -1,
+ "avg300": -1,
+ "avg360": -1,
+ "avg480": -1,
+ "avg720": -1,
+ "avg1440": -1,
+ "disparity_avg5": -1,
+ "disparity_avg10": -1,
+ "disparity_avg20": -1,
+ "disparity_avg60": -1,
+ "disparity_avg120": -1,
+ "disparity_avg240": -1,
+ "disparity_avg480": -1,
+ "bolingerband_upper": -1,
+ "bolingerband_lower": -1,
+ "bolingerband_middle": -1,
+ "bolingerband_width": -1,
+ "bolingerband_pb": -1,
+ "envelope_upper": -1,
+ "envelope_lower": -1,
+ "envelope_middle": -1,
+ "ichimokucloud_changeLine": -1,
+ "ichimokucloud_baseLine": -1,
+ "ichimokucloud_laggingSpan": -1,
+ "ichimokucloud_leadingSpan1": -1,
+ "ichimokucloud_leadingSpan2": -1,
+ "stochastic_fast_k": -1,
+ "stochastic_slow_k": -1,
+ "stochastic_slow_d": -1,
+ "rsi": -1,
+ "rsis": -1,
+ "macd": -1,
+ "macds": -1,
+ "macdo": -1,
+ "mfi": -1,
+ "trend": -1
+ }
+
+ def analyzeDaily(self):
+ stockTableName = 'stock'
+
+ conn = sqlite3.connect(self.stockFileName)
+ cursor = conn.cursor()
+
+ cursor.execute('SELECT distinct code, name FROM ' + stockTableName + ' order by code')
+ items = cursor.fetchall()
+
+ for rowid, item in enumerate(items):
+
+ stock = {"CODE": item[0], "NAME": item[1], "PRICE":[]}
+ print("Daily # :", rowid, ", CODE: ", stock['CODE'], ", NAME: ", stock['NAME'])
+
+ sql = 'SELECT ymd, close, diff, open, high, low, volume FROM ' + stockTableName + ' where CODE=? order by ymd desc '
+ sql += ' limit 500'
+ cursor.execute(sql, (stock['CODE'],))
+ items = cursor.fetchall()
+
+ items_reverse = reversed(items)
+ for item in items_reverse:
+ stock['PRICE'].append( self.setItem(item) )
+
+ self.analyzeAdditionalInfo(stock, cursor)
+
+ conn.commit()
+ cursor.close()
+ conn.close()
+
+ return
+
+
+ def analyzeGrouping(self, type):
+ stockTableName = 'stock'
+
+ conn = sqlite3.connect(self.stockFileName)
+ cursor = conn.cursor()
+
+ cursor.execute('SELECT distinct code, name FROM ' + stockTableName + ' order by code')
+ items = cursor.fetchall()
+
+ for rowid, item in enumerate(items):
+ stock = {"CODE": item[0], "NAME": item[1], "PRICE": []}
+ print(type, "# :", rowid, ", CODE: ", stock['CODE'], ", NAME: ", stock['NAME'])
+
+ sql = 'SELECT ymd, close, diff, open, high, low, volume FROM ' + stockTableName + ' where CODE=? order by ymd desc '
+ #sql += ' limit 350'
+ cursor.execute(sql, (stock['CODE'],))
+ items = cursor.fetchall()
+
+ items_reverse = reversed(items)
+ for item in items_reverse:
+ stock['PRICE'].append(
+ self.setItem(item)
+ )
+
+ agg_dict = {'open': 'first',
+ 'high': 'max',
+ 'low': 'min',
+ 'close': 'last',
+ 'volume': 'sum'}
+
+ df = pd.DataFrame(stock['PRICE'])
+ df['ymd'] = pd.to_datetime(df['ymd'])
+ df.set_index('ymd', inplace=True)
+
+ if type == "weekly":
+ condition="W"
+ else:
+ condition='M'
+ df_group = df.resample(condition).agg(agg_dict)
+ df_group = df_group.dropna()
+ df_group.merge(df, )
+ stock['PRICE'] = self.convertFormat(df_group.to_dict())
+
+ self.analyzeAdditionalInfo(stock, cursor, type)
+
+ conn.commit()
+ cursor.close()
+ conn.close()
+
+ return
+
+
+if __name__ == "__main__":
+
+ start = time.time()
+ PROJECT_HOME = '.'
+
+ RESOURCE_PATH = os.path.join(PROJECT_HOME, 'resources')
+ stockFileName = os.path.join(RESOURCE_PATH, 'stock.db')
+ analyzer = AnalyzerSqlite(stockFileName)
+
+ #analyzer.analyzeDaily()
+ #analyzer.analyzeGrouping("weekly")
+ #analyzer.analyzeGrouping("monthly")
+
+ # HTML 출력
+ outPath = os.path.join(PROJECT_HOME, "resources", "analysis")
+ if not os.path.isdir(outPath):
+ os.mkdir(outPath)
+ day = datetime.today().strftime("%Y%m%d")
+ before_7_day = datetime.today() + relativedelta(days=-7)
+ dayList = os.listdir(outPath)
+ for dayDir in dayList:
+ if dayDir[0] != '.' and dayDir < before_7_day.strftime("%Y%m%d"):
+ if os.path.exists(os.path.join(outPath, dayDir)) and os.path.isdir(os.path.join(outPath, dayDir)):
+ shutil.rmtree(os.path.join(outPath, dayDir))
+
+ outPath = os.path.join(outPath, day)
+
+ if os.path.isdir(outPath):
+ shutil.rmtree(outPath)
+ os.mkdir(outPath)
+ print("print to Html...")
+ analyzer.findCandidates(outPath)
+
+
+ print("time : %6.2f 초" % (time.time() - start))
+ print("done...")
\ No newline at end of file
diff --git a/stock/analysis/Common.py b/stock/analysis/Common.py
index a3ac56b..1a4a025 100644
--- a/stock/analysis/Common.py
+++ b/stock/analysis/Common.py
@@ -4,39 +4,39 @@ from stock.analysis.MovingAverage import MovingAverage
class Common:
# 상향
- def getDirection(self, stock, idx, until=10):
+ def getDirection(self, data, idx, until=10):
up, down = 0, 0
for i in range(idx, idx-(until+1), -1):
- if stock[i - 1] < stock[i]:
+ if data[i - 1] < data[i]:
up += 1
- if stock[i - 1] > stock[i]:
+ if data[i - 1] > data[i]:
down += 1
if down < up:
- if stock[idx - 3] < stock[idx]:
+ if data[idx - 3] < data[idx]:
return 'UP'
elif up < down:
- if stock[idx - 3] > stock[idx]:
+ if data[idx - 3] > data[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]):
+ 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 ((stock[0][type1] < stock[1][type1] < stock[2][type1]) and
- (stock[0][type1] < stock[0][type2] and stock[2][type1] > stock[2][type2])):
+ 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, 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]):
+ 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 ((stock[0][type1] > stock[1][type1] > stock[2][type1]) and
- (stock[0][type1] > stock[0][type2] and stock[2][type1] < stock[2][type2])):
+ 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
@@ -488,8 +488,8 @@ class Common:
return False
def check_env_lower_rsi(self, stock):
- if stock['close'][-2] < stock['envelope_lower'][-2] and stock['envelope_lower'][-1] < stock['close'][-1]:
- if stock['rsi'][-1] < 50:
+ 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
@@ -521,9 +521,9 @@ class Common:
def check_volume(self, stock):
c_index = 200
- max_volume = max(stock['volume'][-c_index:-1])
- if 0 < max_volume*2 < stock['volume'][-1] and stock['close'][-2] < stock['close'][-1]:
- log = "{:.2f}".format(stock['volume'][-1]/max_volume)
+ 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, ""
@@ -569,28 +569,74 @@ class Common:
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
- i = len(stock['ymd']) - 1
- check = False
- if 130 < i:
- for c in range(5):
- # 추세가 상승 중일 때 매수의 관점 (소추세가 하락해 있을 때 매수의 기회)
- if np.average(stock['avg60'][i - c - 3:i - c]) < stock['avg60'][i - c]:
- if stock['avg60'][i - c] < stock['avg20'][i - c]:
- if stock['slow_d'][i - c] is not None and stock['slow_k'][i - c] is not None and stock['slow_k'][i - c - 1] is not None and stock['slow_k'][i - c] is not None and stock['slow_k'][i - c] is not None:
- if stock['slow_d'][i - c] < stock['slow_k'][i - c] and stock['slow_k'][i - c - 1] < stock['slow_k'][i - c] and stock['slow_k'][i - c] < 50:
- if stock['close'][i - c] < stock['last_middle'][i - c] - (stock['last_middle'][i - c] - stock['last_min'][i - c]) * 0.5:
- check = True
+ 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
- # 거래량은 6시간 중 가장 많은 것보다 1.5배 이상 많고, 종가는 3시간 중에서 가장 높을 때
- if np.max(stock['volume'][i - c - 120:i - c - 2]) * 1.5 < stock['volume'][i - c] and np.max(stock['close'][i - c - 20: i - c]) < stock['close'][i - c]:
- if stock['open'][i - c - 1] < stock['close'][i - c - 1] and stock['close'][i - c - 1] - stock['open'][i - c - 1] < stock['close'][i - c] - stock['open'][i - c]:
- # 양봉이고 몸통이 3/4 이상일 때
- if stock['open'][i - c] < stock['close'][i - c] and stock['high'][i - c] - stock['close'][i - c] < (stock['close'][i - c] - stock['open'][i - c]) * 0.25:
- check = True
return check
+ def buy_stock_candidate(self, param, stock):
+ check = False
+ if len(stock['trend']) < 10:
+ return check
+
+ if stock['upper'][0] < stock['high'][0]:
+ # 볼린저 밴드 width 로 매수 시점
+ for i in range(10):
+ if stock['width'][i+1] < 20 and 20 < stock['width'][i]:
+ check = True
+ break
+ # 볼린저 밴드 %B와 MFI로 매수 시점
+ if 80 < stock['pb'][i+1] and 80 < stock['mfi'][i]:
+ check = True
+
+ return check
# 낙폭 과대 체크
def check_excessive_drop(self, stock):
@@ -609,9 +655,9 @@ class Common:
return False
def check_under_BB_Low(self, stock):
- if stock['lower'][-1] is not None and stock['lower'][-2] is not None:
+ if stock['lower'][0] is not None and stock['lower'][1] is not None:
# bb 하단에 부딪힘
- if stock['close'][-1] < stock['lower'][-1]:
+ if stock['close'][0] < stock['lower'][0]:
return True
return False
@@ -662,3 +708,11 @@ class Common:
return obv
return -1
+
+ def buy_stock_dail5_5_20(self, stock_daily):
+
+ if stock_daily['avg5'][1] < stock_daily['avg20'][1] and stock_daily['avg20'][0] < stock_daily['avg5'][1]:
+ if stock_daily['avg20'][0] < stock_daily['avg5'][0] < stock_daily['avg40'][0]:
+ return True
+
+ return False
\ No newline at end of file