1019 lines
46 KiB
Python
1019 lines
46 KiB
Python
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
|
|
|
|
from stock.util.TelegramBot import TelegramBot
|
|
|
|
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
|
|
bot = None
|
|
|
|
def __init__(self, stockFileName=None):
|
|
self.common = Common()
|
|
self.bot = TelegramBot()
|
|
|
|
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 차트 (<a href=\"https://alphasquare.co.kr/home/stock/financial-information?code=%s\">URL1</a>, <a href=\"https://www.tradingview.com/chart?symbol=%s\">URL2</a>, <a href=\"https://www.investing.com/search/?q=%s\">URL3</a>)" % (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):
|
|
result = []
|
|
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)
|
|
result.append({'ticker_code': CODE, 'ticker_name': NAME,'close': stock_daily['close'][0]})
|
|
|
|
pStr = '[Stock Analysis]\n'
|
|
for item in result:
|
|
pStr += " {} {} ({:.2f})\n".format(item['ticker_code'], item['ticker_name'], item['close'])
|
|
self.bot.sendMsg(pStr)
|
|
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...") |