1011 lines
45 KiB
Python
1011 lines
45 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
|
|
import copy
|
|
|
|
rc('font', family='AppleGothic')
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
|
|
import plotly.graph_objs as go
|
|
from plotly import tools, 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.crawler.MovingAverage import MovingAverage
|
|
|
|
class AnalyzerSqlite:
|
|
PROJECT_HOME = None
|
|
|
|
stochastic = None
|
|
bolingerBand = None
|
|
ichimokuCloud = None
|
|
rsi = None
|
|
macd = None
|
|
|
|
topCompany = None
|
|
fnguide = None
|
|
|
|
common = None
|
|
stockFileName = None
|
|
analyzedFileName = None
|
|
|
|
moving_avg = None
|
|
|
|
def __init__(self, PROJECT_HOME, stockFileName):
|
|
self.PROJECT_HOME = PROJECT_HOME
|
|
self.stockFileName = stockFileName
|
|
|
|
self.common = Common()
|
|
|
|
self.stochastic = Stochastic()
|
|
self.bolingerBand = BolingerBand()
|
|
self.ichimokuCloud = IchimokuCloud()
|
|
self.rsi = RSI()
|
|
self.macd = MACD()
|
|
|
|
self.topCompany = self.getTopCompany(stockFileName, 2000)
|
|
self.fnguide = self.readFnguide(stockFileName)
|
|
|
|
return
|
|
|
|
def getTopCompany(self, fnguideFileName, top):
|
|
conn = sqlite3.connect(fnguideFileName)
|
|
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']))
|
|
avg300 = list(reversed(stock['avg300']))
|
|
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['stochastic_slow_k']))
|
|
stochastic_slow_d = list(reversed(stock['stochastic_slow_d']))
|
|
bolingerband_upper = list(reversed(stock['bolingerband_upper']))
|
|
bolingerband_lower = list(reversed(stock['bolingerband_lower']))
|
|
ichimokucloud_changeLine = list(reversed(stock['ichimokucloud_changeLine']))
|
|
ichimokucloud_baseLine = list(reversed(stock['ichimokucloud_baseLine']))
|
|
|
|
# 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='#6C2507')
|
|
#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='#f84c43')
|
|
#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')
|
|
#avg300 = go.Scatter(x=ymd, y=avg300, name="avg300", line_color='#00FF49')
|
|
bolinger_upper = go.Scatter(x=ymd, y=bolingerband_upper, name="upper", line_color='#8B4513')
|
|
bolinger_lower = go.Scatter(x=ymd, y=bolingerband_lower, name="lower", line_color='#8B4513')
|
|
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')
|
|
|
|
#candle_data = [candle_stick, avg3, avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg240, avg300, bolinger_upper, bolinger_lower, changeLine, baseLine]
|
|
candle_data = [candle_stick, avg5, avg10, avg20, avg60, avg120, avg240, bolinger_upper, bolinger_lower, changeLine, baseLine]
|
|
#candle_data = [candle_stick, bolinger_upper, bolinger_lower, changeLine, baseLine]
|
|
|
|
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, 1200]
|
|
)
|
|
for trace in macd_data:
|
|
fig.append_trace(trace, 1, 1)
|
|
for trace in stochastic_data:
|
|
fig.append_trace(trace, 2, 1)
|
|
for trace in rsi_data:
|
|
fig.append_trace(trace, 3, 1)
|
|
for trace in volume_data:
|
|
fig.append_trace(trace, 4, 1)
|
|
for trace in disparity_data:
|
|
fig.append_trace(trace, 5, 1)
|
|
for trace in candle_data:
|
|
fig.append_trace(trace, 6, 1)
|
|
|
|
fig.update_layout(height=2200, 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 makeDir(self, type):
|
|
if os.path.isdir(self.outPath + "/" + type):
|
|
os.rmdir(self.outPath + "/" + type)
|
|
os.mkdir(self.outPath + "/" + type)
|
|
return
|
|
|
|
def makeDirectory(self, outPath):
|
|
self.outPath = outPath
|
|
if os.path.isdir(outPath):
|
|
shutil.rmtree(outPath)
|
|
os.mkdir(outPath)
|
|
self.makeDir("final")
|
|
|
|
self.makeDir("monthly_6월선_36월선_상향돌파")
|
|
self.makeDir("monthly_종가_12월선_상향돌파")
|
|
self.makeDir("monthly_macd_-300이하")
|
|
self.makeDir("monthly_rsi_20이하")
|
|
self.makeDir("monthly_BB하단_내려옴")
|
|
|
|
self.makeDir("weekly_4주선_48주선_상향돌파")
|
|
self.makeDir("weekly_종가_12주선_상향돌파")
|
|
self.makeDir("weekly_rsi_15이하")
|
|
|
|
self.makeDir("daily_macd_-500이하")
|
|
self.makeDir("daily_rsi_10이하")
|
|
self.makeDir("daily_이전에_없던_거래량")
|
|
self.makeDir("daily_이격도")
|
|
self.makeDir("daily_weekly_monthly_rsi_10_20_30이하")
|
|
self.makeDir("daily_낙폭과대")
|
|
|
|
|
|
return
|
|
|
|
def writeFile(self, type, CODE, NAME, top, stock, state, final_status_count=-1):
|
|
# 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/jJ8zOXz0/?symbol=KRX:%s\">URL2</a>)" % (NAME, CODE, stock['close'][0], type, CODE, CODE)
|
|
fig['layout'].update(title=title)
|
|
|
|
fileName = self.outPath + "/" + str(type)
|
|
if final_status_count == -1:
|
|
fileName = "%s/%s_%s_%s_%s.html" % (fileName, top, NAME.replace(" ", ""), CODE, state)
|
|
else:
|
|
fileName = "%s/%s_%s_%s_%s_%s.html" % (fileName, str(final_status_count), top, NAME.replace(" ", ""), CODE, state)
|
|
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, '
|
|
sql += ' disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120, '
|
|
sql += ' bolingerband_upper, bolingerband_lower, bolingerband_middle, '
|
|
sql += ' ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2, '
|
|
sql += ' stochastic_fast_k, stochastic_slow_k, stochastic_slow_d, '
|
|
sql += ' rsi, rsis, '
|
|
sql += ' macd, macds, macdo '
|
|
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 = [], [], [], [], [], [], [], [], [], [], [], [], [], [], []
|
|
disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120 = [], [], [], [], []
|
|
bolingerband_upper, bolingerband_lower, bolingerband_middle = [], [], []
|
|
ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2 = [], [], [], []
|
|
stochastic_fast_k, stochastic_slow_k, stochastic_slow_d = [], [], []
|
|
rsi, rsis = [], []
|
|
macd, macds, macdo = [], [], []
|
|
|
|
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])
|
|
disparity_avg5.append(price[21])
|
|
disparity_avg10.append(price[22])
|
|
disparity_avg20.append(price[23])
|
|
disparity_avg60.append(price[24])
|
|
disparity_avg120.append(price[25])
|
|
bolingerband_upper.append(price[26])
|
|
bolingerband_lower.append(price[27])
|
|
bolingerband_middle.append(price[28])
|
|
ichimokucloud_changeLine.append(price[29])
|
|
ichimokucloud_baseLine.append(price[30])
|
|
ichimokucloud_leadingSpan1.append(price[31])
|
|
ichimokucloud_leadingSpan2.append(price[32])
|
|
stochastic_fast_k.append(price[33])
|
|
stochastic_slow_k.append(price[34])
|
|
stochastic_slow_d.append(price[35])
|
|
rsi.append(price[36])
|
|
rsis.append(price[37])
|
|
macd.append(price[38])
|
|
macds.append(price[39])
|
|
macdo.append(price[40])
|
|
|
|
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,
|
|
"disparity_avg5": disparity_avg5, "disparity_avg10": disparity_avg10, "disparity_avg20": disparity_avg20, "disparity_avg60": disparity_avg60, "disparity_avg120": disparity_avg120,
|
|
"bolingerband_upper": bolingerband_upper, "bolingerband_lower": bolingerband_lower,
|
|
"bolingerband_middle": bolingerband_middle,
|
|
"ichimokucloud_changeLine": ichimokucloud_changeLine, "ichimokucloud_baseLine": ichimokucloud_baseLine,
|
|
"ichimokucloud_leadingSpan1": ichimokucloud_leadingSpan1,
|
|
"ichimokucloud_leadingSpan2": ichimokucloud_leadingSpan2,
|
|
"stochastic_fast_k": stochastic_fast_k, "stochastic_slow_k": stochastic_slow_k, "stochastic_slow_d": stochastic_slow_d,
|
|
"rsi": rsi, "rsis": rsis,
|
|
"macd": macd, "macds": macds, "macdo": macdo
|
|
}
|
|
|
|
return stock
|
|
|
|
# 후보 찾기
|
|
def findCandidate(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')
|
|
items = cursor.fetchall()
|
|
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
for idx, item in enumerate(items):
|
|
CODE = item[0]
|
|
NAME = item[1]
|
|
print (idx, CODE, NAME)
|
|
print("Analysis # :", 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)
|
|
|
|
status = ""
|
|
final_status = ""
|
|
final_status_count = 0
|
|
|
|
# 거래량이 100만 이상이고, 종가가 1천원 이상인지 체크 (https://happpy-rich.tistory.com/94)
|
|
if stock_weekly['volume'][0] > 100000 and stock_weekly['close'][0] > 1000:
|
|
|
|
# 종목 상태 체크 분석
|
|
|
|
|
|
# [Monthly]
|
|
# 20주선이 40주 선을 상향 돌파함
|
|
if len(stock_monthly['close']) > 40:
|
|
if (stock_monthly['avg6'][1] is not None and stock_monthly['avg36'][1] is not None and
|
|
stock_monthly['avg6'][0] is not None and stock_monthly['avg36'][0] is not None):
|
|
if stock_monthly['avg6'][1] <= stock_monthly['avg36'][1] and stock_monthly['avg6'][0] > stock_monthly['avg36'][0]:
|
|
type = "monthly_6월선_36월선_상향돌파"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_monthly, status)
|
|
|
|
# 종가가 20주선을 상향 돌파함
|
|
if len(stock_monthly['close']) > 2:
|
|
if stock_monthly['close'][1] is not None and stock_monthly['avg12'][1] is not None and stock_monthly['close'][0] is not None and stock_monthly['avg12'][0] is not None:
|
|
if stock_monthly['close'][1] <= stock_monthly['avg12'][1] and stock_monthly['close'][0] > stock_monthly['avg12'][0]:
|
|
type = "monthly_종가_12월선_상향돌파"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_monthly, status)
|
|
|
|
# MACD가 0 이하인 경우
|
|
if len(stock_monthly['close']) > 1:
|
|
if stock_monthly['macd'][0] is not None:
|
|
if stock_monthly['macd'][0] <= -300:
|
|
type = "monthly_macd_-300이하"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_monthly, status)
|
|
|
|
# RSI가 20 이하인 경우
|
|
if len(stock_monthly['close']) > 1:
|
|
if stock_monthly['rsi'][0] is not None:
|
|
if stock_monthly['rsi'][0] <= 20:
|
|
type = "monthly_rsi_20이하"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_monthly, status)
|
|
|
|
if len(stock_monthly['volume']) > 5:
|
|
# BB 하단에 부딪힘
|
|
for c_index in range(1, 5):
|
|
if stock_monthly['bolingerband_lower'][c_index+1] is None:
|
|
break
|
|
if stock_monthly['close'][c_index] <= (stock_monthly['bolingerband_lower'][c_index+1]):
|
|
type = "monthly_BB하단_내려옴"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_monthly, status)
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# [Weekly]
|
|
# 정배열 체크
|
|
temp_status = self.common.check_RightArrange(stock_weekly)
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
# 4주선이 48주 선을 상향 돌파함
|
|
if len(stock_weekly['close']) > 40:
|
|
if (stock_weekly['avg4'][1] is not None and stock_weekly['avg48'][1] is not None and
|
|
stock_weekly['avg4'][0] is not None and stock_weekly['avg48'][0] is not None):
|
|
if stock_weekly['avg4'][1] <= stock_weekly['avg48'][1] and stock_weekly['avg4'][0] > stock_weekly['avg48'][0]:
|
|
type = "weekly_4주선_48주선_상향돌파"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_weekly, status)
|
|
|
|
# 종가가 20주선을 상향 돌파함
|
|
if len(stock_weekly['close']) > 2:
|
|
if stock_weekly['close'][1] is not None and stock_weekly['avg12'][1] is not None and stock_weekly['close'][0] is not None and stock_weekly['avg12'][0] is not None:
|
|
if stock_weekly['close'][1] <= stock_weekly['avg12'][1] and stock_weekly['close'][0] > stock_weekly['avg12'][0]:
|
|
type = "weekly_종가_12주선_상향돌파"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_weekly, status)
|
|
|
|
# RSI가 15 이하인 경우
|
|
if len(stock_monthly['close']) > 1:
|
|
if stock_weekly['rsi'][0] is not None:
|
|
if stock_weekly['rsi'][0] <= 15:
|
|
type = "weekly_rsi_15이하"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_weekly, status)
|
|
|
|
|
|
|
|
|
|
|
|
# 3) daily
|
|
if len(stock_daily['volume']) > 5:
|
|
# RSI가 10 이하인 경우
|
|
if stock_daily['macd'][0] is not None:
|
|
if stock_daily['macd'][0] <= -500:
|
|
type = "daily_macd_-500이하"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_daily, status)
|
|
|
|
# 2년 중 최대 거래량인 경우
|
|
c_index = 520
|
|
if len(stock_daily['volume']) < c_index:
|
|
c_index = len(stock_daily['volume'])
|
|
if max(stock_daily['volume'][1:c_index]) < stock_daily['volume'][0]:
|
|
type = "daily_이전에_없던_거래량"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_daily, status)
|
|
|
|
# daily_이격도
|
|
if (98<stock_daily['disparity_avg5'][0]<102 and 98<stock_daily['disparity_avg10'][0]<102 and
|
|
98<stock_daily['disparity_avg20'][0]<102 and 98<stock_daily['disparity_avg60'][0]<102 and
|
|
98<stock_daily['disparity_avg120'][0]<102):
|
|
type = "daily_이격도"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_daily, status)
|
|
|
|
# daily_weekly_monthly_rsi_10_20_30이하
|
|
if len(stock_monthly['close']) > 1:
|
|
if stock_monthly['rsi'][0] is not None and stock_weekly['rsi'][0] is not None and stock_daily['rsi'][0] is not None:
|
|
if stock_monthly['rsi'][0] <= 30 and stock_weekly['rsi'][0] <= 20 and stock_daily['rsi'][0] <= 10:
|
|
type = "daily_weekly_monthly_rsi_10_20_30이하"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
self.writeFile(type, CODE, NAME, top, stock_daily, status)
|
|
|
|
# daily_낙폭과대 (50% 이상 하락)
|
|
c_index = 520
|
|
if len(stock_daily['close']) < c_index:
|
|
c_index = len(stock_daily['close'])
|
|
location = (max(stock_daily['close'][1:c_index]) - stock_daily['close'][0]) / max(stock_daily['close'][1:c_index])
|
|
if location > 0.5:
|
|
type = "daily_낙폭과대"
|
|
final_status += " " + type
|
|
final_status_count += 1
|
|
status = "{:.2f}".format(location) + "_" + status
|
|
self.writeFile(type, CODE, NAME, top, stock_daily, status)
|
|
|
|
if final_status_count >= 5:
|
|
type = "final"
|
|
self.writeFile(type, CODE, NAME, top, stock_daily, final_status, final_status_count)
|
|
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_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)
|
|
|
|
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_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'])
|
|
|
|
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]['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()
|
|
|
|
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"])
|
|
|
|
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,
|
|
"avg36": -1,
|
|
"avg40": -1,
|
|
"avg48": -1,
|
|
"avg60": -1,
|
|
"avg120": -1,
|
|
"avg200": -1,
|
|
"avg240": -1,
|
|
"avg300": -1,
|
|
"disparity_avg5": -1,
|
|
"disparity_avg10": -1,
|
|
"disparity_avg20": -1,
|
|
"disparity_avg60": -1,
|
|
"disparity_avg120": -1,
|
|
"bolingerband_upper": -1,
|
|
"bolingerband_lower": -1,
|
|
"bolingerband_middle": -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
|
|
}
|
|
)
|
|
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, disparity_avg5 REAL, disparity_avg10 REAL, disparity_avg20 REAL, disparity_avg60 REAL, disparity_avg120, bolingerband_upper REAL, bolingerband_lower REAL, bolingerband_middle REAL, ichimokucloud_changeLine REAL, ichimokucloud_baseLine 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)")
|
|
|
|
# 키 생성
|
|
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.rsi.analyze(stock)
|
|
self.macd.analyze(stock)
|
|
|
|
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, "
|
|
sql += " disparity_avg5, disparity_avg10, disparity_avg20, disparity_avg60, disparity_avg120, "
|
|
sql += " bolingerband_upper, bolingerband_lower, bolingerband_middle, "
|
|
sql += " ichimokucloud_changeLine, ichimokucloud_baseLine, ichimokucloud_leadingSpan1, ichimokucloud_leadingSpan2, "
|
|
sql += " stochastic_fast_k, stochastic_slow_k, stochastic_slow_d, "
|
|
sql += " rsi, rsis, macd, macds, macdo) "
|
|
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['disparity_avg5'], price['disparity_avg10'], price['disparity_avg20'], price['disparity_avg60'], price['disparity_avg120'],
|
|
price['bolingerband_upper'], price['bolingerband_lower'], price['bolingerband_middle'],
|
|
price['ichimokucloud_changeLine'], price['ichimokucloud_baseLine'], 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'],))
|
|
|
|
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=?, "
|
|
sql += " disparity_avg5=?, disparity_avg10=?, disparity_avg20=?, disparity_avg60=?, disparity_avg120=?, "
|
|
sql += " bolingerband_upper=?, bolingerband_lower=?, bolingerband_middle=?, "
|
|
sql += " ichimokucloud_changeLine=?, ichimokucloud_baseLine=?, ichimokucloud_leadingSpan1=?, ichimokucloud_leadingSpan2=?, "
|
|
sql += " stochastic_fast_k=?, stochastic_slow_k=?, stochastic_slow_d=?, "
|
|
sql += " rsi=?, rsis=?, "
|
|
sql += " macd=?, macds=?, macdo=? "
|
|
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['disparity_avg5'], price['disparity_avg10'], price['disparity_avg20'], price['disparity_avg60'], price['disparity_avg120'],
|
|
price['bolingerband_upper'], price['bolingerband_lower'], price['bolingerband_middle'], price['ichimokucloud_changeLine'], price['ichimokucloud_baseLine'],
|
|
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'],
|
|
stock["CODE"], price['ymd'],))
|
|
break
|
|
|
|
cursor.execute("commit",)
|
|
return
|
|
|
|
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 350'
|
|
cursor.execute(sql, (stock['CODE'],))
|
|
items = cursor.fetchall()
|
|
|
|
items_reverse = reversed(items)
|
|
for item in items_reverse:
|
|
stock['PRICE'].append(
|
|
{
|
|
"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,
|
|
"disparity_avg5": -1,
|
|
"disparity_avg10": -1,
|
|
"disparity_avg20": -1,
|
|
"disparity_avg60": -1,
|
|
"disparity_avg120": -1,
|
|
"bolingerband_upper": -1,
|
|
"bolingerband_lower": -1,
|
|
"bolingerband_middle": -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,
|
|
}
|
|
)
|
|
|
|
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(
|
|
{
|
|
"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,
|
|
"disparity_avg5": -1,
|
|
"disparity_avg10": -1,
|
|
"disparity_avg20": -1,
|
|
"disparity_avg60": -1,
|
|
"disparity_avg120": -1,
|
|
"bolingerband_upper": -1,
|
|
"bolingerband_lower": -1,
|
|
"bolingerband_middle": -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
|
|
}
|
|
)
|
|
|
|
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 = os.path.join(os.path.dirname(os.path.join(os.path.dirname(os.path.join(os.path.dirname(__file__))))))
|
|
|
|
stockFileName = PROJECT_HOME + '/resources/stock.db'
|
|
analyzer = AnalyzerSqlite(PROJECT_HOME, 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)):
|
|
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.findCandidate(outPath)
|
|
|
|
print("time : %6.2f 초" % (time.time() - start))
|
|
|
|
print("done...")
|
|
|