662 lines
27 KiB
Python
662 lines
27 KiB
Python
import json
|
|
import os
|
|
import time
|
|
import shutil
|
|
from stockpredictor.analysis.Common import Common
|
|
from stockpredictor.analysis.Stochastic import Stochastic
|
|
from stockpredictor.analysis.BolingerBand import BolingerBand
|
|
import matplotlib.pyplot as plt
|
|
import datetime
|
|
import sqlite3
|
|
from datetime import datetime
|
|
|
|
from matplotlib import rc
|
|
|
|
rc('font', family='AppleGothic')
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
|
|
import pandas as pd
|
|
import plotly.graph_objs as go
|
|
from plotly import tools, subplots
|
|
import plotly.io as po
|
|
|
|
class Analyzer:
|
|
tableName = 'stock'
|
|
PROJECT_HOME = None
|
|
|
|
stocks = None
|
|
candidate = None
|
|
|
|
stochastic = None
|
|
|
|
common = None
|
|
inFileName = None
|
|
fnguideFileName = None
|
|
|
|
fnguide = {}
|
|
|
|
def __init__(self, PROJECT_HOME, inFileName, fnguideFileName):
|
|
self.PROJECT_HOME = PROJECT_HOME
|
|
self.inFileName = inFileName
|
|
self.fnguideFileName = fnguideFileName
|
|
|
|
self.stocks = []
|
|
self.candidate = []
|
|
|
|
self.common = Common()
|
|
|
|
self.stochastic = Stochastic()
|
|
self.bolingerBand = BolingerBand()
|
|
|
|
self.readFnguide()
|
|
return
|
|
|
|
def readFnguide(self):
|
|
conn = sqlite3.connect(self.fnguideFileName)
|
|
cursor = conn.cursor()
|
|
|
|
today = datetime.today()
|
|
year1 = str(today.year - 1) + ".12.01"
|
|
year2 = str(today.year - 2) + ".12.01"
|
|
year3 = str(today.year - 3) + ".12.01"
|
|
|
|
rowid = 1
|
|
cursor.execute('SELECT * FROM fnguide WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
while result != None:
|
|
if result[2] == "227950":
|
|
print (1)
|
|
data = json.loads(result[2])
|
|
self.fnguide[result[0]] = True
|
|
|
|
if (year1 in data and year2 in data and year3 in data):
|
|
if (data[year1]['영업이익'] < 0 or data[year2]['영업이익'] < 0 or data[year3]['영업이익'] < 0):
|
|
# and 3년 연속 영업이익이 적자이면 매수하지 않는다.
|
|
# or: 3년 중 1번이라도 영업이익이 적자이면 매수하지 않는다.
|
|
self.fnguide[result[0]] = False
|
|
if (data[year1]['영업이익'] < -100):
|
|
# 전년 영억적자가 100억 이상이면 매수하지 않는다.
|
|
self.fnguide[result[0]] = False
|
|
|
|
rowid += 1
|
|
cursor.execute('SELECT * FROM fnguide WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
|
|
cursor.close()
|
|
conn.close()
|
|
return
|
|
|
|
def draw(self, stock):
|
|
|
|
last_index = self.get_last_index(stock)
|
|
if last_index > 300:
|
|
index = 300 # 최대 300일치 그래프 확인
|
|
df_stock = pd.DataFrame(stock["PRICE"][len(stock["PRICE"]) - index:])
|
|
df_stochastic = pd.DataFrame(stock["STOCHASTIC"][len(stock["STOCHASTIC"]) - index:last_index+1])
|
|
else:
|
|
index = last_index
|
|
df_stock = pd.DataFrame(stock["PRICE"][:index+1])
|
|
df_stochastic = pd.DataFrame(stock["STOCHASTIC"][:index+1])
|
|
|
|
# general
|
|
volume = go.Bar(x=df_stock.DATE, y=df_stock['volume'], name="volume")
|
|
volume_data = [volume]
|
|
|
|
# stochastic
|
|
slow_k = go.Scatter(x=df_stochastic.DATE, y=df_stochastic['slow_k'], name="Slow%K", line_color='#8B4513')
|
|
slow_d = go.Scatter(x=df_stochastic.DATE, y=df_stochastic['slow_d'], name="Slow%D", line_color='#4169E1')
|
|
stochastic_data = [slow_k, slow_d]
|
|
|
|
fig = subplots.make_subplots(rows=2, cols=1, subplot_titles=('거래량', 'Stochastic'))
|
|
|
|
for trace in volume_data:
|
|
fig.append_trace(trace, 1, 1)
|
|
for trace in stochastic_data:
|
|
fig.append_trace(trace, 2, 1)
|
|
|
|
fig.update_layout(height=800)
|
|
|
|
return fig
|
|
|
|
def get_last_index(self, stock):
|
|
for i in range(0, len(stock['PRICE'])):
|
|
if (stock['PRICE'][i]['close'] == 0 and stock['PRICE'][i]['open'] == 0 and stock['PRICE'][i]['volume'] == 0):
|
|
return i-1
|
|
return len(stock['PRICE']) - 1
|
|
|
|
def analyzeStochastic(self):
|
|
conn = sqlite3.connect(self.inFileName)
|
|
cursor = conn.cursor()
|
|
|
|
# 기존 분석 데이터를 모두 지움
|
|
cursor.execute('update ' + self.tableName + ' set STOCHASTIC = ""')
|
|
|
|
rowid = 1
|
|
cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
while result != None:
|
|
stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2])}
|
|
results = self.stochastic.analyze(stock)
|
|
text = json.dumps(results, ensure_ascii=False)
|
|
cursor.execute("UPDATE " + self.tableName + " SET STOCHASTIC=? WHERE CODE=?", (text, stock["CODE"]))
|
|
|
|
print("#analyzeStochastic", rowid, stock['NAME'])
|
|
rowid += 1
|
|
cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
|
|
conn.commit()
|
|
cursor.close()
|
|
conn.close()
|
|
return
|
|
|
|
def analyzeFinalScore(self, last_index, STOCK, STOCHASTIC):
|
|
"""
|
|
매수 조건
|
|
#0. 최소 매수 조건은 거래량은 20만건, 종가는 2천원 이상인 종목이어야 한다.
|
|
#1. 골든크로스: 5일선, 20일선, 60일선, 120일선이 순서대로 나열되는 순간
|
|
"""
|
|
i = last_index
|
|
buy_price = 0
|
|
count = 0
|
|
for idx in range(i, i-5, -1):
|
|
if idx-1 < 0:
|
|
break
|
|
buy_price += STOCK[idx-1]['close'] - STOCK[idx]['low']
|
|
count += 1
|
|
if count == 0:
|
|
buy_price = STOCK[i]['close']
|
|
else:
|
|
# 종가 - 최저가의 최근 3일 평균 가격을 산정한다.
|
|
buy_price = round(STOCK[i]['close'] - (buy_price/count))
|
|
|
|
status = ""
|
|
if STOCK[i]['volume'] > 100000 and STOCK[i]['close'] > 2000:
|
|
# 거래량이 100만 이상이고, 종가가 1천원 이상인지 체크 (https://happpy-rich.tistory.com/94)
|
|
|
|
# 정배열 체크
|
|
temp_status = self.common.check_RightArrange(STOCK, i)
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
# 20일선 돌파
|
|
temp_status = self.common.check_Dolpa_Jiji(STOCK, i, '20')
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
# 60일선 돌파
|
|
temp_status = self.common.check_Dolpa_Jiji(STOCK, i, '60')
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
# 120일선 돌파
|
|
temp_status = self.common.check_Dolpa_Jiji(STOCK, i, '120')
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
# 240일선 돌파
|
|
temp_status = self.common.check_Dolpa_Jiji(STOCK, i, '240')
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
# 20일선 지지 매수가 추천
|
|
temp_status = self.common.check_Dolpa_Jiji_20(STOCK, i)
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
# 음봉인데 어제보다 종가가 더 높은 경우
|
|
# 이 경우 정배열 상태인지도 함께 체크를 한다.
|
|
higher_umbong_status = self.common.checkHigherUmbong(STOCK, i)
|
|
if higher_umbong_status != "":
|
|
status += higher_umbong_status
|
|
|
|
"""
|
|
# 단타 #1
|
|
temp_status = self.common.check_Danta1(STOCK, i)
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
# 단타 #2
|
|
temp_status = self.common.check_Danta2(STOCK, i)
|
|
if temp_status != "":
|
|
status += temp_status
|
|
|
|
all_upper_cross_status = self.common.checkAllUpperCross(STOCK, i)
|
|
if all_upper_cross_status != "":
|
|
status += all_upper_cross_status
|
|
|
|
# 1주일 동안 몇 10% 이상 오른 종목
|
|
W1Rise = self.common.check_W1Rise(STOCK, i, 0.1)
|
|
if W1Rise != "":
|
|
status += W1Rise
|
|
|
|
# 1일 동안 몇 10% 이상 내린 종목
|
|
W1Fall = self.common.check_D1Fall(STOCK, i, -0.1)
|
|
if W1Fall != "":
|
|
status += W1Fall
|
|
"""
|
|
|
|
# GOLDENCROSS#1은 바로 매수하지 않고, 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다.
|
|
# GOLDENCROSS#2은 바로 매수 가능
|
|
# GOLDENCROSS#3은 바로 매수 가능
|
|
golden_cross_status = self.common.check_golded_cross(STOCK, i)
|
|
if golden_cross_status != "":
|
|
status += golden_cross_status
|
|
|
|
"""
|
|
# BUYINGBEARMARKET#1은 바로 매수 가능
|
|
# BUYINGBEARMARKET#2은 바로 매수 가능
|
|
bearmarket_buying_status = self.common.check_bearmarket_buying(STOCK, STOCHASTIC, i)
|
|
if bearmarket_buying_status != "":
|
|
status += bearmarket_buying_status
|
|
"""
|
|
|
|
# STOCHASTIC
|
|
stochastic_status = self.common.check_stochastic(STOCK, STOCHASTIC, i)
|
|
if stochastic_status != "":
|
|
status += stochastic_status
|
|
|
|
# YANGBONG
|
|
"""
|
|
longYangBongAfterUmBong_status = self.common.checkLongYangBongAfterUmBong(STOCK, i)
|
|
# 어제 음봉 이후 장대양봉이었다면,
|
|
if longYangBongAfterUmBong_status != "":
|
|
status += longYangBongAfterUmBong_status
|
|
"""
|
|
|
|
# Doji
|
|
doji_status = self.common.checkDoji(STOCK, i)
|
|
# 하락 추세에서 도지가 나오면 매수
|
|
if doji_status != "":
|
|
status += doji_status
|
|
|
|
"""---------------------------------
|
|
# Gravestone
|
|
gravestone_status = self.common.checkGravestone(STOCK, i)
|
|
# 상승 추세에서 그레이브스톤이 나오면 매도
|
|
if gravestone_status != "":
|
|
status += gravestone_status
|
|
---------------------------------"""
|
|
|
|
"""
|
|
# Dragonfly
|
|
dragonfly_status = self.common.checkDragonfly(STOCK, i)
|
|
# 하락 추세에서 드레곤플라이가 나오면 매수
|
|
if dragonfly_status != "":
|
|
status += dragonfly_status
|
|
|
|
# Hammer
|
|
hammer_status = self.common.checkHammer(STOCK, i)
|
|
# 하락 추세에서 해머가 나오면 매수
|
|
if hammer_status != "":
|
|
status += hammer_status
|
|
"""
|
|
|
|
"""---------------------------------
|
|
# Hangingman
|
|
hangingman_status = self.common.checkHangingman(STOCK, i)
|
|
# 상승 추세에서 행잉맨이 나오면 매도
|
|
if hangingman_status != "":
|
|
status += hangingman_status
|
|
---------------------------------"""
|
|
|
|
"""
|
|
# 상승장악형 (Engulfing) - 다음 날도 양봉이라면 매수
|
|
engulfing_status = self.common.checkEngulfingHigh(STOCK, i)
|
|
# 하락 추세에서 상승장악형이 나오면 매수
|
|
if engulfing_status != "":
|
|
status += engulfing_status
|
|
"""
|
|
|
|
"""---------------------------------
|
|
# 하락장악형 (Engulfing)
|
|
engulfing_status = self.common.checkEngulfingLow(STOCK, i)
|
|
# 상승 추세에서 하락장악형이 나오면 매도
|
|
if engulfing_status != "":
|
|
status += engulfing_status
|
|
---------------------------------"""
|
|
|
|
"""
|
|
# 상승 포아형 (Harami)
|
|
harami_status = self.common.checkHaramiHigh(STOCK, i)
|
|
# 하락 추세에서 상승포아형이 나오면 매수
|
|
if harami_status != "":
|
|
status += harami_status
|
|
"""
|
|
|
|
"""---------------------------------
|
|
# 하락 포아형 (Harami)
|
|
harami_status = self.common.checkHaramiLow(STOCK, i)
|
|
# 상승 추세에서 하락포아형이 나오면 매도
|
|
if harami_status != "":
|
|
status += harami_status
|
|
---------------------------------"""
|
|
|
|
"""
|
|
# 관통형 (piercing)
|
|
piercing_status = self.common.checkPiercing(STOCK, i)
|
|
# 하락 추세에서 관통형이 나오면 매수
|
|
if piercing_status != "":
|
|
status += piercing_status
|
|
"""
|
|
|
|
"""---------------------------------
|
|
# 흑운형 (Dark-cloud)
|
|
darkcloud_status = self.common.checkDarkCloud(STOCK, i)
|
|
# 상승 추세에서 흑운형이 나오면 매도
|
|
if darkcloud_status != "":
|
|
status += darkcloud_status
|
|
---------------------------------"""
|
|
|
|
"""
|
|
# 샛별 (Morning start)
|
|
morningstar_status = self.common.checkMorningstar(STOCK, i)
|
|
# 하락 추세에서 샛별형이 나오면 매수
|
|
if morningstar_status != "":
|
|
status += morningstar_status
|
|
"""
|
|
|
|
"""---------------------------------
|
|
# 저녁별 (Evening start)
|
|
eveningstar_status = self.common.checkEveningstar(STOCK, i)
|
|
# 상승 추세에서 저녁별형이 나오면 매도
|
|
if eveningstar_status != "":
|
|
status += eveningstar_status
|
|
---------------------------------"""
|
|
|
|
|
|
return status, buy_price
|
|
|
|
def getPositionalEnergy(self, stock, i):
|
|
# 350일 중 가장 찾은 금액과 가장 높았던 금액 중 현재가의 위치 계산
|
|
|
|
top = stock[i]['close']
|
|
bottom = stock[i]['close']
|
|
price = stock[i]['close']
|
|
|
|
for i in range(8, 540):
|
|
if i > len(stock) or not stock[-i]:
|
|
break
|
|
|
|
if top < stock[-i]["close"]:
|
|
top = stock[-i]["close"]
|
|
|
|
if stock[-i]["close"] < bottom:
|
|
bottom = stock[-i]["close"]
|
|
|
|
if top - bottom == 0:
|
|
energy = 100
|
|
else:
|
|
energy = round(((price - bottom) / (top - bottom)), 2)
|
|
|
|
return energy
|
|
|
|
def writeFile(self, fig, state, isbuy, buy_price, bolingerband_score, stochastic_score, positionalEnergy, item_name, item_code):
|
|
|
|
if state != "":
|
|
fileName = self.status_path
|
|
fileName = "%s/%d__%s__p(%.2f)__b(%.2f)__s(%.2f)__%d__%s_%s.html" % (fileName, isbuy, state, positionalEnergy, bolingerband_score, stochastic_score, buy_price, item_name.replace(" ", ""), item_code)
|
|
po.write_html(fig, file=fileName, auto_open=False)
|
|
|
|
if bolingerband_score < 0.15:
|
|
fileName = self.bolingerband_path
|
|
fileName = "%s/%d__b(%.2f)__p(%.2f)__s(%.2f)__%s__%d__%s_%s.html" % (fileName, isbuy, bolingerband_score, positionalEnergy, stochastic_score, state, buy_price, item_name.replace(" ", ""), item_code)
|
|
po.write_html(fig, file=fileName, auto_open=False)
|
|
|
|
if stochastic_score < 15:
|
|
fileName = self.stochastic_path
|
|
fileName = "%s/%d__s(%.2f)__b(%.2f)__p(%.2f)__%s__%d__%s_%s.html" % (fileName, isbuy, stochastic_score, bolingerband_score, positionalEnergy, state, buy_price, item_name.replace(" ", ""), item_code)
|
|
po.write_html(fig, file=fileName, auto_open=False)
|
|
|
|
if positionalEnergy < 0.15:
|
|
fileName = self.positionalEnergy_path
|
|
fileName = "%s/%d__p(%.2f)__b(%.2f)__s(%.2f)__%s__%d__%s_%s.html" % (fileName, isbuy, positionalEnergy, bolingerband_score, stochastic_score, state, buy_price, item_name.replace(" ", ""), item_code)
|
|
po.write_html(fig, file=fileName, auto_open=False)
|
|
|
|
if isbuy > 0:
|
|
fileName = self.outPath
|
|
fileName = "%s/%d__p(%.2f)__b(%.2f)__s(%.2f)__%s__%d__%s_%s.html" % (fileName, isbuy, positionalEnergy, bolingerband_score, stochastic_score, state, buy_price, item_name.replace(" ", ""), item_code)
|
|
po.write_html(fig, file=fileName, auto_open=False)
|
|
|
|
return
|
|
|
|
def makeDirectory(self, outPath):
|
|
self.outPath = outPath
|
|
if os.path.isdir(outPath):
|
|
shutil.rmtree(outPath)
|
|
os.mkdir(outPath)
|
|
|
|
self.stochastic_path = outPath + "/stochastic"
|
|
if os.path.isdir(self.stochastic_path):
|
|
os.rmdir(self.stochastic_path)
|
|
os.mkdir(self.stochastic_path)
|
|
|
|
self.bolingerband_path = outPath + "/bolingerband"
|
|
if os.path.isdir(self.bolingerband_path):
|
|
os.rmdir(self.bolingerband_path)
|
|
os.mkdir(self.bolingerband_path)
|
|
|
|
self.positionalEnergy_path = outPath + "/positionalEnergy"
|
|
if os.path.isdir(self.positionalEnergy_path):
|
|
os.rmdir(self.positionalEnergy_path)
|
|
os.mkdir(self.positionalEnergy_path)
|
|
|
|
self.status_path = outPath + "/status"
|
|
if os.path.isdir(self.status_path):
|
|
os.rmdir(self.status_path)
|
|
os.mkdir(self.status_path)
|
|
|
|
return
|
|
|
|
# 그래프 출력
|
|
def analyzeToHtml(self, outPath):
|
|
self.makeDirectory(outPath)
|
|
|
|
conn = sqlite3.connect(self.inFileName)
|
|
cursor = conn.cursor()
|
|
rowid = 1
|
|
cursor.execute('SELECT CODE, NAME, PRICE, STOCHASTIC, BOLINGERBAND FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
|
|
# 최근 20일간 +-종목 개수 체크
|
|
inde_check = []
|
|
for check_index in range(365):
|
|
inde_check.append([0,0])
|
|
|
|
while result != None:
|
|
item_code = result[0]
|
|
item_name = result[1]
|
|
print("#html", rowid, item_name)
|
|
|
|
# 부실 기업은 매수하지 않고 그냥 넘긴다.
|
|
# kospi 지수와 kosdak 지수도 그냥 넘긴다.
|
|
#if ((item_code in self.fnguide and not self.fnguide[item_code]) or (item_code == "KOSPI" or item_code == "KOSDAK") or result[3] == ''):
|
|
if ((item_code in self.fnguide and not self.fnguide[item_code]) or (item_code == "KOSPI" or item_code == "KOSDAK")):
|
|
rowid += 1
|
|
# 다음 종목을 가져옴
|
|
cursor.execute('SELECT CODE, NAME, PRICE, STOCHASTIC, BOLINGERBAND FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
continue
|
|
|
|
result_3 = result[3]
|
|
if result[3] != result[3]:
|
|
result_3 = result[3].replace("NaN", "0")
|
|
|
|
if result[3]==None or (result[2]=="[]" or result[3]=='' or result[4] == ''):
|
|
rowid += 1
|
|
cursor.execute('SELECT CODE, NAME, PRICE, STOCHASTIC, BOLINGERBAND FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
continue
|
|
|
|
stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2]), "STOCHASTIC": json.loads(result_3), "BOLINGERBAND": json.loads(result[4])}
|
|
|
|
last_index = self.get_last_index(stock)
|
|
STOCK = stock['PRICE']
|
|
STOCHASTIC = stock['STOCHASTIC']
|
|
BOLINGERBAND = stock['BOLINGERBAND']
|
|
|
|
stochastic_score = STOCHASTIC[last_index]['slow_k']
|
|
|
|
# upper: 20, lower: 10
|
|
# 14 = (14-10) / (20-10) = 0.4
|
|
# 18 = (18-10) / (20-10) = 0.8
|
|
# 5 = (5-10) / (20-10) = -0.5
|
|
if BOLINGERBAND[last_index]['upper'] == BOLINGERBAND[last_index]['lower']:
|
|
bolingerband_score = 0
|
|
else:
|
|
bolingerband_score = round(((STOCK[last_index]['close']-BOLINGERBAND[last_index]['lower'])/(BOLINGERBAND[last_index]['upper']-BOLINGERBAND[last_index]['lower'])), 2)
|
|
|
|
# 위치 에너지
|
|
positionalEnergy = self.getPositionalEnergy(STOCK, last_index)
|
|
if STOCK[last_index]['volume'] > 100000 and STOCK[last_index]['close'] > 1000:
|
|
# 종목 상태 체크 분석
|
|
state, buy_price = self.analyzeFinalScore(last_index, STOCK, STOCHASTIC)
|
|
|
|
isbuy = 0
|
|
|
|
# 스토케스틱이 20이하이어야 하며, 볼린저밴드 0.3 보다 작으며, 위치에너지도 0.2보다 낮다면,
|
|
if stochastic_score < 30 and bolingerband_score < 0.3 and positionalEnergy < 0.3:
|
|
isbuy = 1
|
|
|
|
# 위치에너지가 낮거나 240일선 아래에 있는 상태에서 상태값을 갖는 경우 매수한다.
|
|
if state != "":
|
|
isbuy = 2
|
|
|
|
# 종가가 240일선 아래라면 매수한다.
|
|
if STOCK[last_index]['close'] < STOCK[last_index]['avg240']:
|
|
if state == "":
|
|
isbuy = 3
|
|
else:
|
|
isbuy = 4
|
|
|
|
if len(STOCK) > 5:
|
|
# 볼린저밴드 하단에 부딪혔다면,
|
|
"""
|
|
if (STOCK[last_index-2]['low'] <= BOLINGERBAND[last_index-2]['lower'] <= STOCK[last_index-2]['high'] or
|
|
STOCK[last_index-3]['low'] <= BOLINGERBAND[last_index-3]['lower'] <= STOCK[last_index-3]['high'] or
|
|
STOCK[last_index-4]['low'] <= BOLINGERBAND[last_index-4]['lower'] <= STOCK[last_index-4]['high']):
|
|
"""
|
|
if (STOCK[last_index - 2]['low'] <= BOLINGERBAND[last_index - 2]['lower'] <= STOCK[last_index - 2]['high'] or
|
|
STOCK[last_index - 3]['low'] <= BOLINGERBAND[last_index - 3]['lower'] <= STOCK[last_index - 3]['high']):
|
|
# 어제 양봉이거나
|
|
# 음봉이라면 그저깨 종가보다 어제 시가가 높거나 같고 그저깨 시가보다 어제 종가가 높다.
|
|
# 음봉이라면 그저깨 시가보다 어제 시가가 높거나 같고 그저깨 종가보다 어제 종가가 높다.
|
|
if STOCK[last_index-1]['open'] < STOCK[last_index-1]['close'] or (
|
|
(STOCK[last_index-2]['close'] <= STOCK[last_index-1]['open'] and STOCK[last_index-2]['open'] < STOCK[last_index-1]['close']) or
|
|
(STOCK[last_index-2]['open'] <= STOCK[last_index-1]['open'] and STOCK[last_index-2]['close'] < STOCK[last_index-1]['close'])):
|
|
# (KOSPI: 2011년 8월 11일)
|
|
# 오늘 양봉이어야 한다.
|
|
if STOCK[last_index]['open'] < STOCK[last_index]['close']:
|
|
isbuy = 5
|
|
# (KOSPI: 2011년 9월 26일)
|
|
# 오늘 음봉이라면, 오늘 시가는 어제 종가보다 높아야 하고, 오늘 종가는 어제 시가보다 높아야 한다.
|
|
if (STOCK[last_index]['close'] < STOCK[last_index]['open']) and (STOCK[last_index-1]['close'] < STOCK[last_index]['open'] and STOCK[last_index-1]['open'] < STOCK[last_index]['close']):
|
|
isbuy = 5
|
|
|
|
if isbuy==4 and stochastic_score < 30 and bolingerband_score < 0.3 and positionalEnergy < 0.3:
|
|
isbuy = 9
|
|
|
|
fig = self.draw(stock)
|
|
title = "%s (%s), %s, buy_price (%d), stochastic(%.2f), bolingerband(%.2f), positionalEnergy(%.2f) 차트" % (item_name, item_code, state, buy_price, stochastic_score, bolingerband_score, positionalEnergy)
|
|
fig['layout'].update(title=title)
|
|
|
|
self.writeFile(fig, state, isbuy, buy_price, bolingerband_score, stochastic_score, positionalEnergy, item_name, item_code)
|
|
|
|
for check_index in range(365):
|
|
if len(STOCK) > check_index and STOCK[last_index-check_index]['open'] < STOCK[last_index-check_index]['close']:
|
|
inde_check[check_index][0] += 1
|
|
else:
|
|
inde_check[check_index][1] += 1
|
|
|
|
rowid += 1
|
|
cursor.execute('SELECT CODE, NAME, PRICE, STOCHASTIC, BOLINGERBAND FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
|
|
outFp = open("inout.cvs", mode='w')
|
|
for check_index in range(365):
|
|
idx = 365 - check_index - 1
|
|
outFp.write("%d,%d,%d,%4.2f\n" % (idx, inde_check[idx][0], inde_check[idx][1], inde_check[idx][0]*100/(inde_check[idx][0]+inde_check[idx][1])))
|
|
outFp.close()
|
|
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
return
|
|
|
|
def analyze(self):
|
|
conn = sqlite3.connect(self.inFileName)
|
|
cursor = conn.cursor()
|
|
|
|
# 기존 분석 데이터를 모두 지움
|
|
cursor.execute('update ' + self.tableName + ' set STOCHASTIC = "", BOLINGERBAND = ""')
|
|
|
|
rowid = 1
|
|
cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
while result != None:
|
|
stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2])}
|
|
|
|
try:
|
|
results_STOCHASTIC = self.stochastic.analyze(stock)
|
|
text_STOCHASTIC = json.dumps(results_STOCHASTIC, ensure_ascii=False)
|
|
|
|
results_BolingerBand = self.bolingerBand.analyze(stock)
|
|
text_BOLINGERBAND = json.dumps(results_BolingerBand, ensure_ascii=False)
|
|
except:
|
|
print("#", rowid, stock['NAME'])
|
|
rowid += 1
|
|
cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
continue
|
|
|
|
cursor.execute("UPDATE " + self.tableName + " SET STOCHASTIC=?, BOLINGERBAND=? WHERE CODE=?", (text_STOCHASTIC, text_BOLINGERBAND, stock["CODE"]))
|
|
print("#", rowid, stock['NAME'])
|
|
rowid += 1
|
|
cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
|
|
result = cursor.fetchone()
|
|
|
|
conn.commit()
|
|
cursor.close()
|
|
conn.close()
|
|
return
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
start = time.time()
|
|
PROJECT_HOME = os.path.join(os.path.dirname(os.path.join(os.path.dirname(os.path.join(os.path.dirname(__file__))))))
|
|
|
|
inFileName = PROJECT_HOME + '/resources/stock.db'
|
|
inFnguideFileName = PROJECT_HOME + '/resources/fnguide.db'
|
|
analyzer = Analyzer(PROJECT_HOME, inFileName, inFnguideFileName)
|
|
|
|
# 분석 & update DB
|
|
"""
|
|
#print ("analyze Stochastic...")
|
|
analyzer.analyzeStochastic()
|
|
"""
|
|
|
|
#######
|
|
# analyzer.analyze()
|
|
#######
|
|
|
|
day = datetime.today().strftime("%Y%m%d")
|
|
|
|
# HTML 출력
|
|
outPath = PROJECT_HOME + "/resources/analysis/"+day
|
|
if os.path.isdir(outPath):
|
|
shutil.rmtree(outPath)
|
|
os.mkdir(outPath)
|
|
print("print to Html...")
|
|
analyzer.analyzeToHtml(outPath)
|
|
|
|
|
|
# 파일 출력
|
|
#print("print to File...")
|
|
#outFileName = PROJECT_HOME + '/resources/analysis/'+day+'.json'
|
|
#analyzer.analyzeToFile(outFileName)
|
|
|
|
print("time : %6.2f 초" % (time.time() - start))
|
|
|
|
print("done...")
|