Files
DeepStock/AnalyzerSqlite.py
dsyoon 5c97e3edc2 init
2024-11-13 19:19:32 +09:00

1035 lines
47 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 stochastic_data:
fig.append_trace(trace, 1, 1)
for trace in macd_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=1800, 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_3_5')
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_daily_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], 'type': '5~20'})
check = self.common.buy_stock_daily_3_5(stock_daily)
if check:
count += 1
dir_name = "daily_3_5"
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], 'type': '3~5'})
pStr = '[Stock Analysis]\n'
i = 0
for item in result:
pStr += " <{}> {} {} ({:.2f})\n".format(item['type'], item['ticker_code'], item['ticker_name'], item['close'])
i += 1
if i==100:
i = 0
self.bot.sendMsg(pStr)
pStr = ''
if i>0:
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...")