This commit is contained in:
dosangyoon
2022-07-29 14:28:27 +09:00
parent b06a89eb2f
commit 794e2ea5f7
1082 changed files with 4441 additions and 446000 deletions

651
stock/analysis/Analyzer.py Normal file
View File

@@ -0,0 +1,651 @@
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 is not 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 > 540:
index = 540 # 최대 540일치 그래프 확인
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
candle_stick = go.Candlestick(x=df_stock.DATE, open=df_stock.open, high=df_stock.high, low=df_stock.low, close=df_stock.close, increasing_line_color='red', decreasing_line_color='blue')
avg5 = go.Scatter(x=df_stock.DATE, y=df_stock.avg5, name="avg5", line_color='#000000')
avg20 = go.Scatter(x=df_stock.DATE, y=df_stock.avg20, name="avg20", line_color='#f84c43')
avg60 = go.Scatter(x=df_stock.DATE, y=df_stock.avg60, name="avg60", line_color='#f89543')
avg120 = go.Scatter(x=df_stock.DATE, y=df_stock.avg120, name="avg120", line_color='#0ed604')
candle_data = [candle_stick, avg5, avg20, avg60, avg120]
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=3, cols=1, subplot_titles=('차트', '거래량', 'Stochastic'))
for trace in candle_data:
fig.append_trace(trace, 1, 1)
for trace in volume_data:
fig.append_trace(trace, 2, 1)
for trace in stochastic_data:
fig.append_trace(trace, 3, 1)
fig.update_layout(height=1200, xaxis_rangeslider_visible=False)
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 is not 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, last_index):
# 260 (= 52 * 5)일 중 가장 찾은 금액과 가장 높았던 금액 중 현재가의 위치 계산
close = stock[last_index]['close']
top = close
bottom = close
for i in range(1, 260):
if i > len(stock) or not stock[-i]:
break
if top < stock[-i]["close"]:
top = stock[-i]["close"]
if bottom > stock[-i]["close"]:
bottom = stock[-i]["close"]
if top-close == 0:
energy = 100.0
else:
energy = round((close-bottom) / (top-close), 2)
return energy
def writeFile(self, fig, state, buy_price, bolingerband_score, stochastic_score, positionalEnergy, item_name, item_code):
if state != "":
fileName = self.status_path
fileName = "%s/%s__p(%.2f)__b(%.2f)__s(%.2f)__%d__%s_%s.html" % (fileName, 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/b(%.2f)__p(%.2f)__s(%.2f)__%s__%d__%s_%s.html" % (fileName, 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/s(%.2f)__b(%.2f)__p(%.2f)__%s__%d__%s_%s.html" % (fileName, 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.5:
fileName = self.positionalEnergy_path
fileName = "%s/p(%.2f)__b(%.2f)__s(%.2f)__%s__%d__%s_%s.html" % (fileName, 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 is not 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 and positionalEnergy < 0.3:
# 종목 상태 체크 분석
state, buy_price = self.analyzeFinalScore(last_index, STOCK, STOCHASTIC)
# 스토케스틱이 20이하이어야 하며, 볼린저밴드 0.3 보다 작으며, 위치에너지도 0.3보다 낮다면,
if stochastic_score < 20 and bolingerband_score < 0.3 and positionalEnergy < 0.3 and len(STOCK) > 5:
state = '1_' + state
# 종가가 240일선 아래라면 매수한다.
if STOCK[last_index]['close'] < STOCK[last_index]['avg240']:
state = '2_' + state
# 볼린저밴드 하단에 부딪혔다면,
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']:
state = '3_' + state
# (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']):
state = '4_' + state
if (STOCHASTIC[last_index]['slow_k'] < 15 and
STOCHASTIC[last_index-1]['slow_k'] < STOCHASTIC[last_index-1]['slow_d'] and
STOCHASTIC[last_index]['slow_d'] < STOCHASTIC[last_index]['slow_k']):
state = '5_' + state
if (STOCHASTIC[last_index]['slow_k'] < 15 and
STOCHASTIC[last_index - 1]['slow_k'] < STOCHASTIC[last_index - 1]['slow_d'] and
STOCHASTIC[last_index]['slow_d'] < STOCHASTIC[last_index]['slow_k']):
state = '6_' + state
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, 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()
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 is not 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...")

View File

@@ -0,0 +1,898 @@
import os
import time
import shutil
import matplotlib.pyplot as plt
import datetime
import sqlite3
from datetime import datetime
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 stockpredictor.analysis.Common import Common
from stockpredictor.analysis.Stochastic import Stochastic
from stockpredictor.analysis.BolingerBand import BolingerBand
from stockpredictor.analysis.IchimokuCloud import IchimokuCloud
from stockpredictor.analysis.RSI import RSI
from stockpredictor.crawler.sQLite.MovingAverage import MovingAverage
class AnalyzerSqlite:
PROJECT_HOME = None
stochastic = None
bolingerBand = None
ichimokuCloud = None
rsi = 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.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):
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']))
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']))
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')
avg4 = go.Scatter(x=ymd, y=avg4, name="avg4", line_color='#085F1B')
avg5 = go.Scatter(x=ymd, y=avg5, name="avg5", line_color='#000000')
avg6 = go.Scatter(x=ymd, y=avg6, name="avg6", line_color='#698D09')
avg10 = go.Scatter(x=ymd, y=avg10, name="avg10", line_color='#041366')
avg12 = go.Scatter(x=ymd, y=avg12, name="avg12", line_color='#6C2507')
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='#8013ED')
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, avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg240, avg300, bolinger_upper, bolinger_lower, changeLine, baseLine]
#candle_data = [candle_stick, bolinger_upper, bolinger_lower, changeLine, baseLine]
volume = go.Bar(x=ymd, y=volume, name="volume")
volume_data = [volume]
# stochastic
rsi = go.Scatter(x=ymd, y=rsi, name="rsi", line_color='#8B4513')
rsis = go.Scatter(x=ymd, y=rsis, name="rsis", line_color='#4169E1')
rsi_data = [rsi, rsis]
# stochastic
slow_k = go.Scatter(x=ymd, y=stochastic_slow_k, name="Slow%K", line_color='#8B4513')
slow_d = go.Scatter(x=ymd, y=stochastic_slow_d, name="Slow%D", line_color='#4169E1')
stochastic_data = [slow_k, slow_d]
fig = subplots.make_subplots(rows=4, cols=1,
subplot_titles=('차트', '거래량', 'rsi', 'Stochastic'),
row_heights=[1200, 500, 500, 500])
for trace in candle_data:
fig.append_trace(trace, 1, 1)
for trace in volume_data:
fig.append_trace(trace, 2, 1)
for trace in rsi_data:
fig.append_trace(trace, 3, 1)
for trace in stochastic_data:
fig.append_trace(trace, 4, 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("weekly_4주선_48주선_상향돌파")
self.makeDir("weekly_종가_12주선_상향돌파")
self.makeDir("weekly_rsi_20이하")
self.makeDir("weekly_rsi_rsis_위로_올라옴")
self.makeDir("weekly_BB하단_내려옴")
self.makeDir("monthly_6월선_36월선_상향돌파")
self.makeDir("monthly_종가_12월선_상향돌파")
self.makeDir("monthly_rsi_30이하")
self.makeDir("monthly_rsi_rsis_위로_올라옴")
self.makeDir("monthly_BB하단_내려옴")
self.makeDir("daily_rsi_10이하")
self.makeDir("daily_거래량_7배_이상_상승")
self.makeDir("daily_BB하단_내려옴")
self.makeDir("daily_종가_60일선_상향돌파")
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, %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], state, 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 += ' avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300, '
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 += ' 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 = [], [], [], [], []
avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300 = [], [], [], [], [], [], [], [], [], [], [], [], [], []
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 = [], []
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])
avg4.append(price[6])
avg5.append(price[7])
avg6.append(price[8])
avg10.append(price[9])
avg12.append(price[10])
avg20.append(price[11])
avg36.append(price[12])
avg40.append(price[13])
avg48.append(price[14])
avg60.append(price[15])
avg120.append(price[16])
avg200.append(price[17])
avg240.append(price[18])
avg300.append(price[19])
bolingerband_upper.append(price[20])
bolingerband_lower.append(price[21])
bolingerband_middle.append(price[22])
ichimokucloud_changeLine.append(price[23])
ichimokucloud_baseLine.append(price[24])
ichimokucloud_leadingSpan1.append(price[25])
ichimokucloud_leadingSpan2.append(price[26])
stochastic_fast_k.append(price[27])
stochastic_slow_k.append(price[28])
stochastic_slow_d.append(price[29])
rsi.append(price[30])
rsis.append(price[31])
stock = {
"ymd": ymd,
"close": close, "open": open, "high": high, "low": low, "volume": volume,
"avg4": avg4, "avg5": avg5, "avg6": avg6, "avg10": avg10, "avg12": avg12, "avg20": avg20, "avg36": avg36, "avg40": avg40, "avg48": avg48, "avg60": avg60, "avg120": avg120, "avg200": avg200, "avg300": avg300,
"avg240": avg240,
"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
}
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:
# 종목 상태 체크 분석
# [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가 32 이하인 경우 (30이 이하로 떨어지는 것 미리 확인)
if len(stock_monthly['close']) > 1:
if stock_weekly['rsi'][0] is not None:
if stock_weekly['rsi'][0] <= 20:
type = "weekly_rsi_20이하"
final_status += " " + type
final_status_count += 1
self.writeFile(type, CODE, NAME, top, stock_weekly, status)
# rsi가 40 이하이고, rsis 아래에서 위로 올라올 때
if len(stock_weekly['close']) > 60:
if stock_weekly['rsi'][0] is not None and stock_weekly['rsis'][0] is not None and stock_weekly['rsi'][1] is not None and stock_weekly['rsis'][1] is not None:
if stock_weekly['rsi'][0] < 40:
if stock_weekly['rsi'][0] > stock_weekly['rsis'][0] and stock_weekly['rsi'][1] <= stock_weekly['rsis'][1]:
type = "weekly_rsi_rsis_위로_올라옴"
#final_status += " " + type
#final_status_count += 1
self.writeFile(type, CODE, NAME, top, stock_weekly, status)
if len(stock_weekly['volume']) > 6:
# BB 하단에 부딪힘
for c_index in range(1, 5):
if stock_weekly['bolingerband_lower'][c_index+1] is not None and stock_weekly['close'][c_index] <= (stock_weekly['bolingerband_lower'][c_index+1]):
type = "weekly_BB하단_내려옴"
final_status += " " + type
final_status_count += 1
self.writeFile(type, CODE, NAME, top, stock_weekly, status)
break
# [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_weekly, 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_weekly, status)
# RSI가 32 이하인 경우 (30이 이하로 떨어지는 것 미리 확인)
if len(stock_monthly['close']) > 1:
if stock_monthly['rsi'][0] is not None:
if stock_monthly['rsi'][0] <= 30:
type = "monthly_rsi_30이하"
final_status += " " + type
final_status_count += 1
self.writeFile(type, CODE, NAME, top, stock_weekly, status)
# rsi가 rsis 아래에서 위로 올라올 때
if len(stock_monthly['close']) > 60:
if stock_monthly['rsi'][0] is not None and stock_monthly['rsis'][0] is not None and stock_monthly['rsi'][1] is not None and stock_monthly['rsis'][1] is not None:
if stock_monthly['rsi'][0] < 40:
if stock_monthly['rsi'][0] > stock_monthly['rsis'][0] and stock_monthly['rsi'][1] <= stock_monthly['rsis'][1]:
type = "monthly_rsi_rsis_위로_올라옴"
#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
# 3) daily
if len(stock_daily['volume']) > 5:
# RSI가 10 이하인 경우 (10이 이하로 떨어지는 것 미리 확인)
if stock_daily['rsi'][0] is not None:
if stock_daily['rsi'][0] <= 10:
type = "daily_rsi_10이하"
final_status += " " + type
final_status_count += 1
self.writeFile(type, CODE, NAME, top, stock_weekly, status)
# 거래량이 7배 이상 상승한 경우
for c_index in range(1, 5):
if stock_daily['volume'][c_index] > (stock_daily['volume'][c_index+1]) * 5:
type = "daily_거래량_7배_이상_상승"
final_status += " " + type
final_status_count += 1
self.writeFile(type, CODE, NAME, top, stock_daily, status)
break
# BB 하단에 내려옴
for c_index in range(1, 5):
if stock_daily['bolingerband_lower'][c_index+1] is None:
break
if stock_daily['close'][c_index] <= (stock_daily['bolingerband_lower'][c_index + 1]):
type = "daily_BB하단_내려옴"
final_status += " " + type
final_status_count += 1
self.writeFile(type, CODE, NAME, top, stock_daily, status)
break
# 종가가 60일선을 상향 돌파함
if len(stock_daily['close']) > 60:
if stock_daily['close'][1] is not None and stock_daily['avg60'][1] is not None and stock_daily['close'][0] is not None and stock_daily['avg60'][0] is not None:
if stock_daily['close'][1] <= stock_daily['avg60'][1] and stock_daily['close'][0] > stock_daily['avg60'][0]:
type = "daily_종가_60일선_상향돌파"
final_status += " " + type
final_status_count += 1
self.writeFile(type, CODE, NAME, top, stock_daily, status)
if final_status_count > 0:
type = "final"
self.writeFile(type, CODE, NAME, top, stock_daily, final_status, final_status_count)
return
def get_moving_average(self, stock):
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_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]['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 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],
"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,
"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
}
)
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, 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, 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)")
# 키 생성
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.ichimokuCloud.analyze(stock)
self.stochastic.analyze(stock)
self.bolingerBand.analyze(stock)
self.rsi.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 += " avg4, avg5, avg6, avg10, avg12, avg20, avg36, avg40, avg48, avg60, avg120, avg200, avg240, avg300, "
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 += " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
cursor.execute(sql, (
stock["CODE"], stock["NAME"], price['ymd'], price['close'], price['diff'], price['open'], price['high'], price['low'], price['volume'],
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['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'],))
else:
sql = "UPDATE " + stockAnalysisTableName + " SET close=?, diff=?, open=?, high=?, low=?, volume=?, "
sql += " avg4=?, avg5=?, avg6=?, avg10=?, avg12=?, avg20=?, avg36=?, avg40=?, avg48=?, avg60=?, avg120=?, avg200=?, avg240=?, avg300=?, "
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 += " WHERE CODE=? and ymd=?"
cursor.execute(sql,
(price['close'], price['diff'], price['open'], price['high'], price['low'], price['volume'],
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['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'],
stock["CODE"], price['ymd'],))
break
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 '
#if result is not None:
# sql += ' limit 300'
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],
"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,
"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})
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 '
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],
"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,
"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})
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")
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.findCandidate(outPath)
print("time : %6.2f" % (time.time() - start))
print("done...")

View File

@@ -0,0 +1,40 @@
import pandas as pd
from stockpredictor.analysis.Common import Common
import plotly.graph_objs as go
from plotly import tools, subplots
import plotly.io as po
class BolingerBand:
common = None
def __init__(self):
self.common = Common()
return
def apply(self, df, n=13, t=2):
# 입력받은 값이 dataframe이라는 것을 정의해줌
df = pd.DataFrame(df)
max20 = df["close"].rolling(window=n).mean()
stddev = df["close"].rolling(window=n).std()
upper = max20 + (stddev * t) # 상단 볼리저 밴드
lower = max20 - (stddev * t) # 하단 볼리저 밴드
middle = (upper + lower ) / 2
# dataframe에 컬럼 추가
df = df.assign(upper=upper, middle=middle, lower=lower)
return df
def analyze(self, stock):
df = pd.DataFrame()
df = df.from_dict(stock['PRICE'])
df = self.apply(df)
for i in range(len(df.upper)):
stock['PRICE'][i]['bolingerband_upper'] = df.upper.values[i]
stock['PRICE'][i]['bolingerband_middle'] = df.middle.values[i]
stock['PRICE'][i]['bolingerband_lower'] = df.lower.values[i]
return

View File

@@ -0,0 +1,560 @@
class Common:
# 상향
def checkUpward(self, type, data):
check = True
if type is not None:
for i in range(len(data)-1):
# 만약 이전이 이후보다 크다면, 상승이 아님
if data[i][type] > data[i+1][type]:
check = False
break
else:
for i in range(len(data)-1):
# 만약 이전이 이후보다 크다면, 상승이 아님
if data[i] > data[i+1]:
check = False
break
return check
# 하향
def checkDownward(self, type, data):
check = True
for i in range(len(data)-1):
# 만약 이전이 이후보다 작다면, 하락이 아님
if data[i][type] < data[i+1][type]:
check = False
break
return check
# 상향 돌파
def checkUpwardBreakthrough(self, type1, type2, data):
if (type1 in data[0] and type1 in data[1] and type1 in data[2] and
type2 in data[0] and type2 in data[1] and type2 in data[2]):
if ((data[0][type1] < data[1][type1] < data[2][type1]) and
(data[0][type1] < data[0][type2] and data[2][type1] > data[2][type2])):
return True
return False
# 하향 돌파
def checkDownwardBreakthrough(self, type1, type2, data):
if (type1 in data[0] and type1 in data[1] and type1 in data[2] and
type2 in data[0] and type2 in data[1] and type2 in data[2]):
if ((data[0][type1] > data[1][type1] > data[2][type1]) and
(data[0][type1] > data[0][type2] and data[2][type1] < data[2][type2])):
return True
return False
def getStochasticScore(self, stock, i):
score = 0
if (stock[i - 1]['slow_k'] < stock[i]['slow_k'] and
stock[i]['slow_d'] < stock[i]['slow_k']):
if stock[i]['slow_k'] < 5:
score = 8
elif 5 <= stock[i]['slow_k'] < 10:
score = 7
elif 10 <= stock[i]['slow_k'] < 15:
score = 6
elif 15 <= stock[i]['slow_k'] < 20:
score = 5
elif 20 <= stock[i]['slow_k'] < 30:
score = 4
elif 30 <= stock[i]['slow_k'] < 40:
score = 3
elif 40 <= stock[i]['slow_k'] < 50:
score = 2
else:
score = 1
if (stock[i - 1]['slow_k'] > stock[i]['slow_k'] and
stock[i - 1]['slow_k'] > stock[i - 1]['slow_d'] and
stock[i]['slow_k'] < stock[i]['slow_d']):
if stock[i]['slow_k'] > 90:
score = -6
elif 90 >= stock[i]['slow_k'] > 80:
score = -5
elif 80 >= stock[i]['slow_k'] > 70:
score = -4
elif 70 >= stock[i]['slow_k'] > 60:
score = -3
elif 60 >= stock[i]['slow_k'] > 50:
score = -2
else:
score = -1
return score
def getBolingerBandScore(self, stock, i):
return 0
def getIchimokuCloudScore(self, stock, i):
score = 0
if stock[i - 1]['leadingSpan1'] != 0 and stock[i - 1]['leadingSpan2'] != 0:
# 후행스팬 > 선행스펜 일때, 후행스펜 > 어제 주가 > 선행스팬 이고, 오늘 주가 > 후행스팬 < 선행스팬 이라면, 매수 2점
if (stock[i - 1]['leadingSpan2'] > stock[i - 1]['leadingSpan1'] and stock[i]['leadingSpan2'] > stock[i]['leadingSpan1']):
if (stock[i - 1]['leadingSpan2'] > stock[i - 1]['close'] > stock[i - 1]['leadingSpan1'] and
stock[i]['close'] > stock[i]['leadingSpan2'] > stock[i - 1]['leadingSpan1']):
score = 2
# 후행스팬 > 선행스펜 일때, 후행스펜 > 선행스팬 > 어제 주가 이고, 오늘 주가 > 후행스팬 < 선행스팬 이라면, 매수 4점
if (stock[i - 1]['leadingSpan2'] > stock[i - 1]['leadingSpan1'] and stock[i]['leadingSpan2'] > stock[i]['leadingSpan1']):
if (stock[i - 1]['leadingSpan2'] > stock[i - 1]['leadingSpan1'] > stock[i - 1]['close'] and
stock[i]['close'] > stock[i]['leadingSpan2'] > stock[i - 1]['leadingSpan1']):
score = 4
# 선행스팬 > 후행스팬 일때, 선행스팬 > 어제 주가 > 후행스팬 이고, 오늘 주가 > 선행스팬 < 후행스팬 이라면, 매수 1점
if (stock[i - 1]['leadingSpan1'] > stock[i - 1]['leadingSpan2'] and stock[i]['leadingSpan1'] > stock[i]['leadingSpan2']):
if (stock[i - 1]['leadingSpan1'] > stock[i - 1]['close'] > stock[i - 1]['leadingSpan2'] and
stock[i]['close'] > stock[i]['leadingSpan1'] > stock[i - 1]['leadingSpan2']):
score = 1
# 선행스팬 > 후행스팬 일때, 선행스팬 > 후행스팬 > 어제 주가 이고, 오늘 주가 > 선행스팬 < 후행스팬 이라면, 매수 3점
if (stock[i - 1]['leadingSpan1'] > stock[i - 1]['leadingSpan2'] and stock[i]['leadingSpan1'] > stock[i]['leadingSpan2']):
if (stock[i - 1]['leadingSpan1'] > stock[i - 1]['leadingSpan2'] > stock[i - 1]['close'] and
stock[i]['close'] > stock[i]['leadingSpan1'] > stock[i - 1]['leadingSpan2']):
score = 3
# 어제는 주가가 선행이나 후행스팬 위에 있었지만, 오늘은 두 스팬 모두 아래로 내려왔을 때 매도
if (stock[i - 1]['close'] > stock[i - 1]['leadingSpan1'] or stock[i - 1]['close'] > stock[i - 1]['leadingSpan2']):
if (stock[i]['close'] < stock[i]['leadingSpan1'] and stock[i]['close'] < stock[i]['leadingSpan2']):
score = -1
return score
# YANGBONG
# 어제 음봉 이후 장대양봉이었다면, 매수
def checkLongYangBongAfterUmBong(self, stock, i):
if i > 0:
if stock[i-1]['close'] < stock[i-1]['open']: # 어제가 음봉인지 체크
if stock[i]['open'] < stock[i]['close'] and stock[i]['close'] == stock[i]['high']: # 오늘 장대양봉인지 체크
if stock[i-1]['volume']*2 < stock[i]['volume']: # 어제 거래량 보다 두배 이상일 때
return "UMYANG_"
return ""
def checkDoji(self, stock, i):
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 도지인지 체크한다
if i > 2:
# 하락 추세이고
if stock[i - 1]['close'] < stock[i - 2]['close']:
# 그저께와 어제가 음봉인지 체크
if stock[i-2]['close'] < stock[i-2]['open'] and stock[i-1]['close'] < stock[i-1]['open']:
# 도지 체크
if stock[i]['open'] == stock[i]['close'] and stock[i]['low'] < stock[i]['close'] < stock[i]['high']:
return "DOJI_"
return ""
def checkGravestone(self, stock, i):
# 상승 추세이고, 어제 양봉이고, 오늘 그레이브스톤인지 체크한다
if i > 2:
# 상승 추세이고
if stock[i-2]['close'] < stock[i - 1]['close']:
# 어제 양봉인지 체크
if stock[i-1]['open'] < stock[i-1]['close']:
# 오늘 그레이브스톤인지 체크한다
if stock[i]['open'] == stock[i]['close'] == stock[i]['low'] and stock[i]['low'] < stock[i]['high']:
return "GRAVESTONE_"
return ""
# 하락 추세에서 드레곤플라이가 나오면 매수
def checkDragonfly(self, stock, i):
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 드레곤플라이인지 체크한다
if i > 1:
# 하락 추세이고
if stock[i - 1]['close'] < stock[i - 2]['close']:
# 그저께와 어제가 음봉인지 체크
if stock[i - 2]['close'] < stock[i - 2]['open'] and stock[i - 1]['close'] < stock[i - 1]['open']:
# 오늘 드레곤플라이인지 체크한다
if stock[i]['open'] == stock[i]['close'] == stock[i]['high'] and stock[i]['low'] < stock[i]['high']:
return "DRAGONEFLY_"
return ""
def checkHammer(self, stock, i):
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 해머인지 체크한다
if i > 1:
# 하락 추세이고
if stock[i - 1]['close'] < stock[i - 2]['close']:
# 그저께와 어제가 음봉인지 체크
if stock[i - 2]['close'] < stock[i - 2]['open'] and stock[i - 1]['close'] < stock[i - 1]['open']:
# 오늘 해머인지 체크한다
if stock[i]['open'] < stock[i]['close']:
if (stock[i]['close'] - stock[i]['open']) * 2 < stock[i]['open'] - stock[i]['low']:
# 윗꼬리가 몸통보다 짧아야 한다.
if stock[i]['high'] - stock[i]['close'] < stock[i]['close'] - stock[i]['open']:
return "HAMMER_"
if stock[i]['close'] < stock[i]['open']:
if (stock[i]['open'] - stock[i]['close']) * 2 < stock[i]['close'] - stock[i]['low']:
# 윗꼬리가 몸통보다 짧아야 한다.
if stock[i]['high'] - stock[i]['open'] < stock[i]['open'] - stock[i]['close']:
return "HAMMER_"
return ""
def checkHangingman(self, stock, i):
# 상승 추세이고, 어제 양봉이고, 오늘 행잉맨인지 체크한다
if i > 2:
# 상승 추세이고
if stock[i-2]['close'] < stock[i - 1]['close']:
# 어제 양봉인지 체크
if stock[i-1]['open'] < stock[i-1]['close']:
# 오늘 해머인지 체크한다
if stock[i]['open'] < stock[i]['close']:
if (stock[i]['close'] - stock[i]['open']) * 2 < stock[i]['open'] - stock[i]['low']:
# 윗꼬리가 몸통보다 짧아야 한다.
if stock[i]['high'] - stock[i]['close'] < stock[i]['close'] - stock[i]['open']:
return "HANGINGMAN_"
if stock[i]['close'] < stock[i]['open']:
if (stock[i]['open'] - stock[i]['close']) * 2 < stock[i]['close'] - stock[i]['low']:
# 윗꼬리가 몸통보다 짧아야 한다.
if stock[i]['high'] - stock[i]['open'] < stock[i]['open'] - stock[i]['close']:
return "HANGINGMAN_"
return ""
def checkEngulfingHigh(self, stock, i):
# 하락 추세에서 상승 장악형인지 체크
if i > 2:
# 하락 추세이고
if stock[i - 1]['close'] < stock[i - 2]['close']:
# 그저께와 어제가 음봉인지 체크
if stock[i - 2]['close'] < stock[i - 2]['open'] and stock[i - 1]['close'] < stock[i - 1]['open']:
# 오늘 상승장악형인지 체크
if stock[i]['open'] < stock[i]['close']:
if stock[i-1]['open'] < stock[i]['close'] and stock[i]['open'] < stock[i-1]['close']:
return "ENHIGH_"
return ""
def checkEngulfingLow(self, stock, i):
# 상승 추세에서 하락 장악형인지 체크
if i > 2:
# 상승 추세이고
if stock[i - 2]['close'] < stock[i - 1]['close']:
# 어제 양봉인지 체크
if stock[i - 1]['open'] < stock[i - 1]['close']:
# 오늘 하락장악형인지 체크
if stock[i]['close'] < stock[i]['open']:
if stock[i-1]['close'] < stock[i]['open'] and stock[i]['close'] < stock[i-1]['open']:
return "ENLOW_"
return ""
def checkHaramiHigh(self, stock, i):
# # 하락 추세에서 상승포아형인지 체크
if i > 2:
# 하락 추세이고
if stock[i - 1]['close'] < stock[i - 2]['close']:
# 그저께와 어제가 음봉인지 체크
if stock[i - 2]['close'] < stock[i - 2]['open'] and stock[i - 1]['close'] < stock[i - 1]['open']:
# 오늘 상승포아형인지 체크
if stock[i]['open'] < stock[i]['close']:
if stock[i-1]['close'] < stock[i]['low'] and stock[i]['high'] < stock[i-1]['open']:
return "HAHIGH_"
return ""
def checkHaramiLow(self, stock, i):
# 상승 추세에서 하락 포아형인지 체크
if i > 2:
# 상승 추세이고
if stock[i - 2]['close'] < stock[i - 1]['close']:
# 어제 양봉인지 체크
if stock[i - 1]['open'] < stock[i - 1]['close']:
# 오늘 하락포아형인지 체크
if stock[i]['close'] < stock[i]['open']:
if stock[i-1]['open'] < stock[i]['low'] and stock[i]['high'] < stock[i-1]['close']:
return "HALOW_"
return ""
def checkPiercing(self, stock, i):
# 하락 추세에서 관통형인지 체크
if i > 2:
# 하락 추세이고
if stock[i - 1]['close'] < stock[i - 2]['close']:
# 그저께와 어제가 음봉인지 체크
if stock[i - 2]['close'] < stock[i - 2]['open'] and stock[i - 1]['close'] < stock[i - 1]['open']:
# 오늘 관통형인지 체크
if stock[i]['open'] < stock[i]['close']:
if stock[i]['open'] < stock[i-1]['low'] and (stock[i-1]['close'] + stock[i-1]['open'])/2 < stock[i]['close'] < stock[i-1]['close']:
return "PIERCING_"
return ""
def checkDarkCloud(self, stock, i):
# 상승 추세에서 흑운형인지 체크
if i > 2:
# 상승 추세이고
if stock[i - 2]['close'] < stock[i - 1]['close']:
# 어제 양봉인지 체크
if stock[i - 1]['open'] < stock[i - 1]['close']:
# 오늘 흑운형인지 체크
if stock[i]['close'] < stock[i]['open']:
if stock[i-1]['high'] < stock[i]['open'] and stock[i-1]['open'] < stock[i]['close'] < (stock[i-1]['open'] + stock[i-1]['close'])/2:
return "DARKCLOUD_"
return ""
def checkMorningstar(self, stock, i):
# 하락 추세에서 샛별인지 체크
if i > 3:
# 하락 추세이고
if stock[i-1]['close'] < stock[i-2]['close']:
# 그저께와 어제가 음봉인지 체크
if stock[i - 2]['close'] < stock[i - 2]['open'] and stock[i - 1]['close'] < stock[i - 1]['open']:
# 오늘 샛별인지 체크
# 어제 갭 체크
if stock[i-1]['open'] < stock[i - 2]['close'] and stock[i-1]['close'] < stock[i - 2]['close']:
# 오늘 시가가 어제 종가보다 높으며 양봉
if stock[i-1]['close'] < stock[i]['open'] < stock[i]['close']:
return "MORNINGSTAR_"
return ""
def checkEveningstar(self, stock, i):
# 상승 추세에서 저녁별형인지 체크
if i > 3:
# 상승 추세이고
if stock[i-2]['close'] < stock[i-1]['close']:
# 어제 양봉인지 체크
if stock[i-1]['open'] < stock[i-1]['close']:
# 오늘 저녁별형인지 체크
# 어제 갭 체크
if stock[i-2]['close'] < stock[i-1]['open'] and stock[i-2]['close'] < stock[i-1]['close']:
# 오늘 시가가 어제 종가보다 낮으며 음봉
if stock[i]['close'] < stock[i-1]['open'] < stock[i-1]['close']:
return "EVENINGSTAR_"
return ""
def checkAllUpperCross(self, stock, i):
if i > 10:
if stock[i]['avg5'] < stock[i]['close'] and stock[i]['avg20'] < stock[i]['close'] and stock[i]['avg60'] < stock[i]['close'] and stock[i]['avg120'] < stock[i]['close']:
for j in range(1, 6):
if stock[i-j]['close'] < stock[i-j]['avg5'] and stock[i-j]['close'] < stock[i-j]['avg20'] and stock[i-j]['close'] < stock[i-j]['avg60'] and stock[i-j]['close'] < stock[i-j]['avg120']:
return "ALLUPPER_"
return ""
def check_golded_cross(self, stock, i):
if i > 1:
# 60 -> 120
# 오늘 지수는 120 < 60 < 20 < 5
# 어제 지수는 60 < 120 이었다.
# 60, 20, 5일선 모두 어제 보다 오늘이 더 높다 (상승중)
# 5일과 20일선은 상승 중이며, 60일선이 120일 선을 뚫고 올라온 순간인지 체크함 (삼성전자 2021-07-29)
# 이때 바로 매수하지 않는다.
# 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다.
if stock[i]['avg120'] < stock[i]['avg60'] < stock[i]['avg20'] < stock[i]['avg5']:
if stock[i-1]['avg120'] > stock[i-1]['avg60']:
if (stock[i-1]['avg60'] < stock[i]['avg60'] and stock[i-1]['avg20'] < stock[i]['avg20'] and stock[i-1]['avg5'] < stock[i]['avg5']):
return "GOLDEN#1_"
# 20 -> 120: 5일과 20일, 60일선은 상승 중이며, 20일선이 120일 선을 뚫고 올라온 순간인지 체크 (SK 2021-12-09, 나노스 2021-02-04)
# 어제는 60일선 < 20일선 < 120일선 < 5일선이지만, 오늘은 60일선 < 120일선 < 20일선 < 5일선
# 이때 바로 매수하지 않는다.
# 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다.
if stock[i]['avg60'] < stock[i]['avg120'] < stock[i]['avg20'] < stock[i]['avg5']:
if stock[i-1]['avg60'] < stock[i-1]['avg20'] < stock[i]['avg120'] < stock[i]['avg5']:
if (stock[i-1]['avg60'] < stock[i]['avg60'] and stock[i-1]['avg20'] < stock[i]['avg20'] and stock[i-1]['avg5'] < stock[i]['avg5']):
return "GOLDEN#2_"
# 20 -> 120: 5일과 20일, 60일선은 상승 중이며, 20일선이 120일 선을 뚫고 올라온 순간인지 체크 (갤럭시아머니트리 2021-02-08)
# 어제는 60일선 < 120일선 < 5일선 < 20일선이지만, 오늘은 60일선 < 120일선 < 20일선 < 5일선
if stock[i]['avg60'] < stock[i]['avg120'] < stock[i]['avg20'] < stock[i]['avg5']:
if stock[i-1]['avg60'] < stock[i-1]['avg20'] < stock[i]['avg120'] < stock[i]['avg5']:
if (stock[i-1]['avg60'] < stock[i]['avg60'] and stock[i-1]['avg20'] < stock[i]['avg20'] and stock[i-1]['avg5'] < stock[i]['avg5']):
return "GOLDEN#3_"
return ""
def check_bearmarket_buying(self, stock, stochastic, i):
if i > 1:
# 5일선 상승 시점 확인 (SK 2020년 3월 24일)
# 어제는 5일선 < 20일선 < 120일선 < 60일선이며, 오늘은 20일선 < 120일선 < 60일선
# 어제와 오늘 모두 20일, 60일, 120일선은 모두 하락이다.
# 어제 종가보다 오늘 종가가 높고, 종가는 5일선 위에 올라왔다.
# 오늘 slow_k는 30 이하이며, 어제는 slow_d가 높았지만, 오늘은 slow_k가 더 높음
if (stock[i-1]['avg5'] < stock[i-1]['avg20'] < stock[i-1]['avg120'] < stock[i]['avg60']) and (stock[i]['avg20'] < stock[i-1]['avg120'] < stock[i]['avg60']):
if stock[i]['avg120'] < stock[i-1]['avg120'] and stock[i]['avg60'] < stock[i-1]['avg60'] and stock[i]['avg20'] < stock[i-1]['avg20']:
if stock[i-1]['close'] <= stock[i]['close'] and stock[i]['avg5'] <= stock[i]['close']:
if (stochastic[i]['slow_k'] < 30 and (stochastic[i-1]['slow_k'] < stochastic[i-1]['slow_d'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k'])):
return "BEARMARKET#1_"
# 5일선 상승 시점 확인 (원풍물산 2020년 3월 24일, NHN한국사이버결제 2018년 11월 2일)
# 어제는 5일선 < 20일선 < 60일선 < 120일선이며, 오늘은 20일선 < 60일선 < 120일선
# 어제와 오늘 모두 20일, 60일, 120일선은 모두 하락이다.
# 어제 종가보다 오늘 종가가 높고, 종가는 5일선 위에 올라왔다.
# 오늘 slow_k는 30 이하이며, 어제는 slow_d가 높았지만, 오늘은 slow_k가 더 높음
if (stock[i-1]['avg5'] < stock[i-1]['avg20'] < stock[i-1]['avg60'] < stock[i]['avg120']) and (stock[i]['avg20'] < stock[i-1]['avg60'] < stock[i]['avg120']):
if stock[i]['avg120'] < stock[i-1]['avg120'] and stock[i]['avg60'] < stock[i-1]['avg60'] and stock[i]['avg20'] < stock[i-1]['avg20']:
if stock[i-1]['close'] <= stock[i]['close'] and stock[i]['avg5'] <= stock[i]['close']:
if (stochastic[i]['slow_k'] < 30 and (stochastic[i-1]['slow_k'] < stochastic[i-1]['slow_d'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k'])):
return "BEARMARKET#2_"
return ""
def check_stochastic(self, stock, stochastic, i):
if i > 2:
# 스토케스틱이 15 이하인 경우
# 어제보다 slow_k가 상승했고, 오늘 slow_k가 slow_d 위에 있는 경우,
if stochastic[i]['slow_k'] < 15:
if stochastic[i-1]['slow_k'] < stochastic[i]['slow_k'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k']:
return "STOCHASTIC_"
return ""
def check_stochastic_buying(self, stock, stochastic, ichimoku, i):
if i > 3:
# 삼성전자 2020년 11월 4일
# 어제는 slow_K가 Slow_d 아래였지만, 오늘은 slow_K가 Slow_d 보다 높다.
# 에제의 slow_k는 20보다 작고, 오늘의 slow_K는 30보다 작다
# 1일전이나, 2, 3일전의 종가가 일목균형표 내의 선행스팬1 아래 존재하며,오늘 고가는 선행스팬1 위에 존재한다.
# 그저께 시가보다 어제의 시가가, 어제의 시가보다는 오늘의 시가가 높다.
if (stochastic[i-1]['slow_k'] < stochastic[i-1]['slow_d'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k']):
if (stochastic[i - 1]['slow_k'] < 20 and stochastic[i]['slow_k'] < 30):
if ((stock[i-3]['close'] < ichimoku[i-3]['leadingSpan1'] or stock[i-2]['close'] < ichimoku[i-2]['leadingSpan1'] or stock[i-1]['close'] < ichimoku[i-1]['leadingSpan1']) and ichimoku[i-1]['leadingSpan1'] < stock[i-1]['high']):
if stock[i-2]['open'] < stock[i-1]['open'] < stock[i]['open']:
return "STOCHASTIC#1_"
# 스토케스틱이 15 이하인 경우
# 어제보다 slow_k가 상승했고, 오늘 slow_k가 slow_d 위에 있는 경우,
# 오늘 종가가 5일선 위에 있는 경우
if stochastic[i]['slow_k'] < 15:
if stochastic[i - 1]['slow_k'] < stochastic[i]['slow_k'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k']:
if stock[i]['avg5'] < stock[i]['close']:
return "STOCHASTIC#2_"
return ""
def check_Dolpa(self, stock, i, avg1, avg2):
upper_index = 0
if len(stock) > 2:
if stock[i-1]["avg"+avg1] < stock[i-1]["avg"+avg2] and stock[i]["avg"+avg1] > stock[i]["avg"+avg2]:
return avg1+"_"+avg2+"_"
return ""
def check_Dolpa_Jiji(self, stock, i, day='20'):
upper_index = 0
if len(stock) > 5:
for idx in range(1, 5):
# day선을 돌파하는 양봉이고, 종가가 최고가 보다 100 이내이어야 한다.
if stock[-idx]['open'] < stock[-idx]["avg"+day] < stock[-idx]['close'] and stock[-idx]['high'] - 100 <= stock[-idx]['close']:
upper_index = idx
break
if upper_index != 0:
for cidx in range(1, upper_index):
# 해당일의 종가보다 현재의 시가가 높거나 같아야 하며, 현재가는 양봉이어야 한다.
if stock[-upper_index]['close'] <= stock[-cidx]['open'] and stock[-cidx]['open'] < stock[-cidx]['close']:
# 해당 기준일 선은 상승이어야 한다.
if stock[-upper_index]['avg'+day] < stock[-cidx]['avg'+day]:
return day + "_"
return ""
def check_Dolpa_Jiji_20(self, stock, i):
"""
top: 이전 5일선이 20일선 위에 있을 때 최고가
top일 체크 사항 (20일 < 5일선)
5일선이 20일 선으로 내려왔다가 다시 20일선 위로 올라왔고, top < 오늘 시가 + 100
top < 시가 < 종가 라면 다음날 매수한다.
# https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_80
"""
if len(stock) > 61:
if stock[i]['avg20'] < stock[i]['close'] and stock[i]['avg20'] < stock[i]['open']:
if stock[i]['avg5'] < stock[i]['avg20']:
index1 = -1
for j in range(1, 61):
if stock[i-j]['avg20'] < stock[i-j]['avg5']:
index1 = j
break
top = 0
for j in range(index1+1, 61):
if stock[i - j]['open'] < stock[i - j]['close']:
if top < stock[i - j]['close']:
top = stock[i - j]['close']
else:
if top < stock[i - j]['open']:
top = stock[i - j]['open']
if stock[i-j]['avg5'] < stock[i-j]['avg20']:
break
return "5-20_"
return ""
def check_Danta1(self, stock, i):
"""
어제 상한가 혹은 상승양봉이 나온다.
오늘 상승 출발을 해야 하며 상승 음봉이 나온다
- 어제 종가 = 어제 상한가 < 종가 < 시가 < 상한가
https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_109
만약 다음날 시작초가가 오늘 종가보다 높게 상승으로 출발한다면 매수를 한다.
손절가는 오늘 최저가이다.
"""
if stock[i-1]['open'] < stock[i-1]['close'] == stock[i-1]['high']:
if stock[i-1]['close'] < stock[i]['close'] < stock[i]['open'] < stock[i]['high']:
return "danta1_"
return ""
def check_Danta2(self, stock, i):
"""
쐐기, 수렴, 깃대 패턴 확인
# https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_144
상단 추세선을 돌파하면 매수를 한다.
"""
price_10 = round(stock[i]["close"] / 10)
if stock[-i]["open"] < stock[-i]["close"]:
top = stock[-i]["close"]
bottom = stock[-i]["open"]
else:
top = stock[-i]["open"]
bottom = stock[-i]["close"]
if len(stock) > 21:
for i in range(2, 21):
if stock[-i]["open"] < stock[-i]["close"]:
if top < stock[-i]["close"]:
top = stock[-i]["close"]
if stock[-i]["open"] < bottom:
bottom = stock[-i]["open"]
else:
if top < stock[-i]["open"]:
top = stock[-i]["open"]
if stock[-i]["close"] < bottom:
bottom = stock[-i]["close"]
if top - bottom < price_10:
return "danta2_"
return ""
def check_RightArrange(self, stock, i):
"""
어제는 정배열이 아니었는데, 오늘은 정배열인 경우
"""
if len(stock) > 2:
if not (stock[i - 1]["avg120"] < stock[i - 1]["avg60"] < stock[i - 1]["avg20"] < stock[i - 1]["avg5"]) and (stock[i]["avg120"] < stock[i]["avg60"] < stock[i]["avg20"] < stock[i]["avg5"]):
return "arrange_"
return ""
def checkHigherUmbong(self, stock, i):
# 음봉인데 어제보다 종가가 더 높은 경우
# 이 경우 정배열 상태인지도 함께 체크를 한다.
if len(stock) > 3:
# 어제는 거래량이 터진 양봉이다.
if stock[i-1]['open'] < stock[i-1]['close'] and 5*stock[i-2]['volume'] < stock[i-1]['volume']:
# 오늘은 음봉인데, 오늘 종가는 어제 시가보다는 높다
if stock[i]['close'] < stock[i]['open'] and stock[i-1]['open'] < stock[i]['close']:
return "HIGHERUMBONG_"
return ""
def check_W1Rise(self, stock, i, limit):
if len(stock) > 5:
rate = round((stock[i]["close"] - stock[i-4]["close"]) / stock[i-4]["close"],2)
if rate >= limit:
return "1w("+str(rate)+")_"
return ""
def check_D1Fall(self, stock, i, limit):
if len(stock) > 2:
# 1000, 900, (900 - 1000) / 900 = -0.111
# 1000, 800, (800 - 1000) / 800 = -0.25
rate = round((stock[i]["close"] - stock[i-1]["close"]) / stock[i-1]["close"], 2)
if rate <= limit:
return "1d("+str(rate)+")_"
return ""

482
stock/analysis/Common.py Normal file
View File

@@ -0,0 +1,482 @@
class Common:
# 상향
def checkUpward(self, type, data):
check = True
if type is not None:
for i in range(len(data)-1):
# 만약 이전이 이후보다 크다면, 상승이 아님
if data[i][type] > data[i+1][type]:
check = False
break
else:
for i in range(len(data)-1):
# 만약 이전이 이후보다 크다면, 상승이 아님
if data[i] > data[i+1]:
check = False
break
return check
# 하향
def checkDownward(self, type, data):
check = True
for i in range(len(data)-1):
# 만약 이전이 이후보다 작다면, 하락이 아님
if data[i][type] < data[i+1][type]:
check = False
break
return check
# 상향 돌파
def checkUpwardBreakthrough(self, type1, type2, data):
if (type1 in data[0] and type1 in data[1] and type1 in data[2] and
type2 in data[0] and type2 in data[1] and type2 in data[2]):
if ((data[0][type1] < data[1][type1] < data[2][type1]) and
(data[0][type1] < data[0][type2] and data[2][type1] > data[2][type2])):
return True
return False
# 하향 돌파
def checkDownwardBreakthrough(self, type1, type2, data):
if (type1 in data[0] and type1 in data[1] and type1 in data[2] and
type2 in data[0] and type2 in data[1] and type2 in data[2]):
if ((data[0][type1] > data[1][type1] > data[2][type1]) and
(data[0][type1] > data[0][type2] and data[2][type1] < data[2][type2])):
return True
return False
# YANGBONG
# 어제 음봉 이후 장대양봉이었다면, 매수
def checkLongYangBongAfterUmBong(self, stock):
if len(stock['close']) > 2:
if stock['close'][1] < stock['open'][1]: # 어제가 음봉인지 체크
if stock['open'][0] < stock['close'][0] and stock['close'][0] == stock['high'][0]: # 오늘 장대양봉인지 체크
if stock['volume'][1]*2 < stock['volume'][0]: # 어제 거래량 보다 두배 이상일 때
return "UMYANG_"
return ""
def checkDoji(self, stock):
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 도지인지 체크한다
if len(stock['close']) > 2:
# 하락 추세이고
if stock['close'][1] < stock['close'][2]:
# 그저께와 어제가 음봉인지 체크
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
# 도지 체크
if stock['open'][0] == stock['close'][0] and stock['low'][0] < stock['close'][0] < stock['high'][0]:
return "DOJI_"
return ""
def checkGravestone(self, stock):
# 상승 추세이고, 어제 양봉이고, 오늘 그레이브스톤인지 체크한다
if len(stock['close']) > 2:
# 상승 추세이고
if stock['close'][2] < stock['close'][1]:
# 어제 양봉인지 체크
if stock['open'][1] < stock['close'][1]:
# 오늘 그레이브스톤인지 체크한다
if stock['open'][0] == stock['close'][0] == stock['low'][0] and stock['low'][0] < stock['high'][0]:
return "GRAVESTONE_"
return ""
# 하락 추세에서 드레곤플라이가 나오면 매수
def checkDragonfly(self, stock):
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 드레곤플라이인지 체크한다
if len(stock['close']) > 2:
# 하락 추세이고
if stock['close'][1] < stock['close'][1]:
# 그저께와 어제가 음봉인지 체크
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
# 오늘 드레곤플라이인지 체크한다
if stock['open'][0] == stock['close'][0] == stock['high'][0] and stock['low'][0] < stock['high'][0]:
return "DRAGONEFLY_"
return ""
def checkHammer(self, stock):
# 하락 추세이고, 그저께, 어제 음봉이고, 오늘 해머인지 체크한다
if len(stock['close']) > 2:
# 하락 추세이고
if stock['close'][1] < stock['close'][1]:
# 그저께와 어제가 음봉인지 체크
if stock['close'][2] < stock['open'][2] and stock[1]['close'] < stock[1]['open']:
# 오늘 해머인지 체크한다
if stock['open'][0] < stock['close'][0]:
if (stock['close'][0] - stock['open'][0]) * 2 < stock['open'][0] - stock['low'][0]:
# 윗꼬리가 몸통보다 짧아야 한다.
if stock['high'][0] - stock['close'][0] < stock['close'][0] - stock['open'][0]:
return "HAMMER_"
if stock['close'][0] < stock['open'][0]:
if (stock['open'][0] - stock['close'][0]) * 2 < stock['close'][0] - stock['low'][0]:
# 윗꼬리가 몸통보다 짧아야 한다.
if stock['high'][0] - stock['open'][0] < stock['open'][0] - stock['close'][0]:
return "HAMMER_"
return ""
def checkHangingman(self, stock):
# 상승 추세이고, 어제 양봉이고, 오늘 행잉맨인지 체크한다
if len(stock['close']) > 2:
# 상승 추세이고
if stock['close'][2] < stock['close'][1]:
# 어제 양봉인지 체크
if stock['open'][1] < stock['close'][1]:
# 오늘 해머인지 체크한다
if stock['open'][0] < stock['close'][0]:
if (stock['close'][0] - stock['open'][0]) * 2 < stock['open'][0] - stock['low'][0]:
# 윗꼬리가 몸통보다 짧아야 한다.
if stock['high'][0] - stock['close'][0] < stock['close'][0] - stock['open'][0]:
return "HANGINGMAN_"
if stock['close'][0] < stock['open'][0]:
if (stock['open'][0] - stock['close'][0]) * 2 < stock['close'][0] - stock['low'][0]:
# 윗꼬리가 몸통보다 짧아야 한다.
if stock['high'][0] - stock['open'][0] < stock['open'][0] - stock['close'][0]:
return "HANGINGMAN_"
return ""
def checkEngulfingHigh(self, stock):
# 하락 추세에서 상승 장악형인지 체크
if len(stock['close']) > 2:
# 하락 추세이고
if stock['close'][1] < stock['close'][2]:
# 그저께와 어제가 음봉인지 체크
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
# 오늘 상승장악형인지 체크
if stock['open'][0] < stock['close'][0]:
if stock['open'][1] < stock['close'][0] and stock['open'][0] < stock['close'][1]:
return "ENHIGH_"
return ""
def checkEngulfingLow(self, stock):
# 상승 추세에서 하락 장악형인지 체크
if len(stock['close']) > 2:
# 상승 추세이고
if stock['close'][2] < stock['close'][1]:
# 어제 양봉인지 체크
if stock['open'][1] < stock['close'][1]:
# 오늘 하락장악형인지 체크
if stock['close'][0] < stock['open'][0]:
if stock['close'][1] < stock['open'][0] and stock['close'][0] < stock['open'][1]:
return "ENLOW_"
return ""
def checkHaramiHigh(self, stock):
# # 하락 추세에서 상승포아형인지 체크
if len(stock['close']) > 2:
# 하락 추세이고
if stock['close'][1] < stock['close'][2]:
# 그저께와 어제가 음봉인지 체크
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
# 오늘 상승포아형인지 체크
if stock['open'][0] < stock['close'][0]:
if stock['close'][1] < stock['low'][0] and stock['high'][0] < stock['open'][1]:
return "HAHIGH_"
return ""
def checkHaramiLow(self, stock):
# 상승 추세에서 하락 포아형인지 체크
if len(stock['close']) > 2:
# 상승 추세이고
if stock['close'][2] < stock['close'][1]:
# 어제 양봉인지 체크
if stock['open'][1] < stock['close'][1]:
# 오늘 하락포아형인지 체크
if stock['close'][0] < stock['open'][0]:
if stock['open'][1] < stock['low'][0] and stock['high'][0] < stock['close'][1]:
return "HALOW_"
return ""
def checkPiercing(self, stock):
# 하락 추세에서 관통형인지 체크
if len(stock['close']) > 2:
# 하락 추세이고
if stock['close'][1] < stock['close'][2]:
# 그저께와 어제가 음봉인지 체크
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
# 오늘 관통형인지 체크
if stock['open'][0] < stock['close'][0]:
if stock['open'][0] < stock['low'][1] and (stock['close'][1] + stock['open'][1])/2 < stock['close'][0] < stock['close'][1]:
return "PIERCING_"
return ""
def checkDarkCloud(self, stock):
# 상승 추세에서 흑운형인지 체크
if len(stock['close']) > 2:
# 상승 추세이고
if stock['close'][2] < stock['close'][1]:
# 어제 양봉인지 체크
if stock['open'][1] < stock['close'][1]:
# 오늘 흑운형인지 체크
if stock['close'][0] < stock['open'][0]:
if stock['high'][1] < stock['open'][0] and stock['open'][1] < stock['close'][0] < (stock['open'][1] + stock['close'][1])/2:
return "DARKCLOUD_"
return ""
def checkMorningstar(self, stock):
# 하락 추세에서 샛별인지 체크
if len(stock['close']) > 2:
# 하락 추세이고
if stock['close'][1] < stock['close'][2]:
# 그저께와 어제가 음봉인지 체크
if stock['close'][2] < stock['open'][2] and stock['close'][1] < stock['open'][1]:
# 오늘 샛별인지 체크
# 어제 갭 체크
if stock['open'][1] < stock['close'][2] and stock['close'][1] < stock['close'][2]:
# 오늘 시가가 어제 종가보다 높으며 양봉
if stock['close'][1] < stock['open'][0] < stock['close'][0]:
return "MORNINGSTAR_"
return ""
def checkEveningstar(self, stock):
# 상승 추세에서 저녁별형인지 체크
if len(stock['close']) > 2:
# 상승 추세이고
if stock['close'][2] < stock['close'][1]:
# 어제 양봉인지 체크
if stock['open'][1] < stock['close'][1]:
# 오늘 저녁별형인지 체크
# 어제 갭 체크
if stock['close'][2] < stock['open'][1] and stock['close'][2] < stock['close'][1]:
# 오늘 시가가 어제 종가보다 낮으며 음봉
if stock['close'][0] < stock['open'][1] < stock['close'][1]:
return "EVENINGSTAR_"
return ""
def checkAllUpperCross(self, stock):
if len(stock['close']) > 10:
if stock['avg5'][0] < stock['close'][0] and stock['avg20'][0] < stock['close'][0] and stock['avg60'][0] < stock['close'][0] and stock['avg120'][0] < stock['close'][0]:
for j in range(1, 6):
if stock['close'][j] < stock['avg5'][j] and stock['close'][j] < stock['avg20'][j] and stock['close'][j] < stock['avg60'][j] and stock['close'][j] < stock['avg120'][j]:
return "ALLUPPER_"
return ""
def check_golded_cross(self, stock):
if len(stock['close']) > 1:
# 60 -> 120
# 오늘 지수는 120 < 60 < 20 < 5
# 어제 지수는 60 < 120 이었다.
# 60, 20, 5일선 모두 어제 보다 오늘이 더 높다 (상승중)
# 5일과 20일선은 상승 중이며, 60일선이 120일 선을 뚫고 올라온 순간인지 체크함 (삼성전자 2021-07-29)
# 이때 바로 매수하지 않는다.
# 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다.
if stock['avg120'][0] < stock['avg60'][0] < stock['avg20'][0] < stock['avg5'][0]:
if stock['avg120'][1] > stock['avg60'][1]:
if (stock['avg60'][1] < stock['avg60'][0] and stock['avg20'][1] < stock['avg20'][0] and stock['avg5'][1] < stock['avg5'][0]):
return "GOLDEN#1_"
# 20 -> 120: 5일과 20일, 60일선은 상승 중이며, 20일선이 120일 선을 뚫고 올라온 순간인지 체크 (SK 2021-12-09, 나노스 2021-02-04)
# 어제는 60일선 < 20일선 < 120일선 < 5일선이지만, 오늘은 60일선 < 120일선 < 20일선 < 5일선
# 이때 바로 매수하지 않는다.
# 이 시점 이후로 5일선이 20일선을 하방으로 뚫었다가 다시 20일선을 상방으로 뚫는 순간 매수를 시도한다.
if stock['avg60'][0] < stock['avg120'][0] < stock['avg20'][0] < stock['avg5'][0]:
if stock['avg60'][1] < stock['avg20'][1] < stock['avg120'][0] < stock['avg5'][0]:
if (stock['avg60'][1] < stock['avg60'][0] and stock['avg20'][1] < stock['avg20'][0] and stock['avg5'][1] < stock['avg5'][0]):
return "GOLDEN#2_"
# 20 -> 120: 5일과 20일, 60일선은 상승 중이며, 20일선이 120일 선을 뚫고 올라온 순간인지 체크 (갤럭시아머니트리 2021-02-08)
# 어제는 60일선 < 120일선 < 5일선 < 20일선이지만, 오늘은 60일선 < 120일선 < 20일선 < 5일선
if stock['avg60'][0] < stock['avg120'][0] < stock['avg20'][0] < stock['avg5'][0]:
if stock['avg60'][1] < stock['avg20'][1] < stock['avg120'][0] < stock['avg5'][0]:
if (stock['avg60'][1] < stock['avg60'][0] and stock['avg20'][1] < stock['avg20'][0] and stock['avg5'][1] < stock['avg5'][0]):
return "GOLDEN#3_"
return ""
def check_bearmarket_buying(self, stock):
if len(stock['close']) > 1:
# 5일선 상승 시점 확인 (SK 2020년 3월 24일)
# 어제는 5일선 < 20일선 < 120일선 < 60일선이며, 오늘은 20일선 < 120일선 < 60일선
# 어제와 오늘 모두 20일, 60일, 120일선은 모두 하락이다.
# 어제 종가보다 오늘 종가가 높고, 종가는 5일선 위에 올라왔다.
# 오늘 slow_k는 30 이하이며, 어제는 slow_d가 높았지만, 오늘은 slow_k가 더 높음
if (stock['avg5'][1] < stock['avg20'][1] < stock['avg120'][1] < stock['avg60'][0]) and (stock['avg20'][0] < stock['avg120'][1] < stock['avg60'][0]):
if stock['avg120'][0] < stock['avg120'][1] and stock['avg60'][0] < stock['avg60'][1] and stock['avg20'][0] < stock['avg20'][1]:
if stock['close'][1] <= stock['close'][0] and stock['avg5'][0] <= stock['close'][0]:
if (stock['stochastic_slow_k'][0] < 30 and (stock['stochastic_slow_k'][1] < stock['stochastic_slow_d'][1] and stock['stochastic_slow_d'][0] < stock['stochastic_slow_k'][0])):
return "BEARMARKET#1_"
# 5일선 상승 시점 확인 (원풍물산 2020년 3월 24일, NHN한국사이버결제 2018년 11월 2일)
# 어제는 5일선 < 20일선 < 60일선 < 120일선이며, 오늘은 20일선 < 60일선 < 120일선
# 어제와 오늘 모두 20일, 60일, 120일선은 모두 하락이다.
# 어제 종가보다 오늘 종가가 높고, 종가는 5일선 위에 올라왔다.
# 오늘 slow_k는 30 이하이며, 어제는 slow_d가 높았지만, 오늘은 slow_k가 더 높음
if (stock['avg5'][1] < stock['avg20'][1] < stock['avg60'][1] < stock['avg120'][0]) and (stock['avg20'][0] < stock['avg60'][1] < stock['avg120'][0]):
if stock['avg120'][0] < stock['avg120'][1] and stock['avg60'][0] < stock['avg60'][1] and stock['avg20'][0] < stock['avg20'][1]:
if stock['close'][1] <= stock['close'][0] and stock['avg5'][0] <= stock['close'][0]:
if (stock['stochastic_slow_k'][0] < 30 and (stock['stochastic_slow_k'][1] < stock['stochastic_slow_d'][1] and stock['stochastic_slow_d'][0] < stock['stochastic_slow_k'][0])):
return "BEARMARKET#2_"
return ""
def check_stochastic(self, stock):
if len(stock['close']) > 60:
# 스토케스틱이 15 이하인 경우
# 어제보다 slow_k가 상승했고, 오늘 slow_k가 slow_d 위에 있는 경우,
if stock['stochastic_slow_k'][0] is not None and stock['stochastic_slow_k'][0] < 15:
if stock['stochastic_slow_k'][1] < stock['stochastic_slow_k'][0] and stock['stochastic_slow_d'][0] < stock['stochastic_slow_k'][0]:
return "STOCHASTIC_"
return ""
def check_stochastic_buying(self, stock, stochastic, ichimoku, i):
if len(stock['close']) > 60:
# 삼성전자 2020년 11월 4일
# 어제는 slow_K가 Slow_d 아래였지만, 오늘은 slow_K가 Slow_d 보다 높다.
# 에제의 slow_k는 20보다 작고, 오늘의 slow_K는 30보다 작다
# 1일전이나, 2, 3일전의 종가가 일목균형표 내의 선행스팬1 아래 존재하며,오늘 고가는 선행스팬1 위에 존재한다.
# 그저께 시가보다 어제의 시가가, 어제의 시가보다는 오늘의 시가가 높다.
if (stochastic[i-1]['slow_k'] < stochastic[i-1]['slow_d'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k']):
if (stochastic[i - 1]['slow_k'] < 20 and stochastic[i]['slow_k'] < 30):
if ((stock[i-3]['close'] < ichimoku[i-3]['leadingSpan1'] or stock[i-2]['close'] < ichimoku[i-2]['leadingSpan1'] or stock[i-1]['close'] < ichimoku[i-1]['leadingSpan1']) and ichimoku[i-1]['leadingSpan1'] < stock[i-1]['high']):
if stock[i-2]['open'] < stock[i-1]['open'] < stock[i]['open']:
return "STOCHASTIC#1_"
# 스토케스틱이 15 이하인 경우
# 어제보다 slow_k가 상승했고, 오늘 slow_k가 slow_d 위에 있는 경우,
# 오늘 종가가 5일선 위에 있는 경우
if stochastic[i]['slow_k'] < 15:
if stochastic[i - 1]['slow_k'] < stochastic[i]['slow_k'] and stochastic[i]['slow_d'] < stochastic[i]['slow_k']:
if stock[i]['avg5'] < stock[i]['close']:
return "STOCHASTIC#2_"
return ""
def check_Dolpa(self, stock, i, avg1, avg2):
upper_index = 0
if len(stock['close']) > 2:
if stock[i-1]["avg"+avg1] < stock[i-1]["avg"+avg2] and stock[i]["avg"+avg1] > stock[i]["avg"+avg2]:
return avg1+"_"+avg2+"_"
return ""
def check_Dolpa_Jiji(self, stock, day='20'):
upper_index = 0
if len(stock['close']) > 5:
for idx in range(1, 5):
# day선을 돌파하는 양봉이고, 종가가 최고가 보다 100 이내이어야 한다.
if stock['open'][idx] < stock["avg"+day][idx] < stock['close'][idx] and stock['high'][idx] - 100 <= stock['close'][idx]:
upper_index = idx
break
if upper_index != 0:
for cidx in range(1, upper_index):
# 해당일의 종가보다 현재의 시가가 높거나 같아야 하며, 현재가는 양봉이어야 한다.
if stock['close'][upper_index] <= stock['open'][cidx] and stock['open'][cidx] < stock['close'][cidx]:
# 해당 기준일 선은 상승이어야 한다.
if stock['avg'+day][upper_index] < stock['avg'+day][cidx]:
return day + "_"
return ""
def check_Dolpa_Jiji_20(self, stock):
"""
top: 이전 5일선이 20일선 위에 있을 때 최고가
top일 체크 사항 (20일 < 5일선)
5일선이 20일 선으로 내려왔다가 다시 20일선 위로 올라왔고, top < 오늘 시가 + 100
top < 시가 < 종가 라면 다음날 매수한다.
# https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_80
"""
if len(stock['close']) > 61:
if stock['avg20'][0] < stock['close'][0] and stock['avg20'][0] < stock['open'][0]:
if stock['avg5'][0] < stock['avg20'][0]:
index1 = -1
for j in range(1, 61):
if stock['avg20'][j] < stock['avg5'][j]:
index1 = j
break
top = 0
for j in range(index1+1, 61):
if stock['open'][j] < stock['close'][j]:
if top < stock['close'][j]:
top = stock['close'][j]
else:
if top < stock['open'][j]:
top = stock['open'][j]
if stock['avg5'][j] < stock['avg20'][j]:
break
return "5-20_"
return ""
def check_Danta1(self, stock):
"""
어제 상한가 혹은 상승양봉이 나온다.
오늘 상승 출발을 해야 하며 상승 음봉이 나온다
- 어제 종가 = 어제 상한가 < 종가 < 시가 < 상한가
https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_109
만약 다음날 시작초가가 오늘 종가보다 높게 상승으로 출발한다면 매수를 한다.
손절가는 오늘 최저가이다.
"""
if stock['open'][1] < stock['close'][1] == stock['high'][1]:
if stock['close'][1] < stock['close'][0] < stock['open'][0] < stock['high'][0]:
return "danta1_"
return ""
def check_Danta2(self, stock):
"""
쐐기, 수렴, 깃대 패턴 확인
# https://docs.google.com/presentation/d/1MVuaeRNljqLCdn4dPZmvVdtl2Ab09Zwg/edit#slide=id.gc7b796e645_0_144
상단 추세선을 돌파하면 매수를 한다.
"""
price_10 = round(stock["close"][0] / 10)
if stock["open"][0] < stock["close"][0]:
top = stock["close"][0]
bottom = stock["open"][0]
else:
top = stock["open"][0]
bottom = stock["close"][0]
if len(stock) > 21:
for i in range(2, 21):
if stock["open"][i] < stock["close"][i]:
if top < stock["close"][i]:
top = stock["close"][i]
if stock["open"][i] < bottom:
bottom = stock["open"][i]
else:
if top < stock["open"][i]:
top = stock["open"][i]
if stock["close"][i] < bottom:
bottom = stock["close"][i]
if top - bottom < price_10:
return "danta2_"
return ""
def check_RightArrange(self, stock):
"""
어제는 정배열이 아니었는데, 오늘은 정배열인 경우
"""
if len(stock['close']) > 2:
if (not (stock["avg120"][1] < stock["avg60"][1] < stock["avg20"][1] < stock["avg5"][1] < stock["close"][1]) and
stock["avg120"][0] < stock["avg60"][0] < stock["avg20"][0] < stock["avg5"][0] < stock["close"][0]):
if stock["avg5"][1] < stock["avg5"][0]:
return "arrange_"
return ""
def checkHigherUmbong(self, stock):
# 음봉인데 어제보다 종가가 더 높은 경우
# 이 경우 정배열 상태인지도 함께 체크를 한다.
if len(stock['close']) > 3:
# 어제는 거래량이 터진 양봉이다.
if stock['open'][1] < stock['close'][1] and 5*stock['volume'][2] < stock['volume'][1]:
# 오늘은 음봉인데, 오늘 종가는 어제 시가보다는 높다
if stock['close'][0] < stock['open'][0] and stock['open'][1] < stock['close'][0]:
return "HIGHERUMBONG_"
return ""
def check_W1Rise(self, stock, limit):
if len(stock['close']) > 5:
rate = round((stock["close"][0] - stock["close"][4]) / stock["close"][4],2)
if rate >= limit:
return "1w("+str(rate)+")_"
return ""
def check_D1Fall(self, stock, limit):
if len(stock['close']) > 2:
# 1000, 900, (900 - 1000) / 900 = -0.111
# 1000, 800, (800 - 1000) / 800 = -0.25
rate = round((stock["close"][0] - stock["close"][1]) / stock["close"][1], 2)
if rate <= limit:
return "1d("+str(rate)+")_"
return ""

View File

@@ -0,0 +1,106 @@
import pandas as pd
from stockpredictor.analysis.Common import Common
# graph: https://plotly.com/python/candlestick-charts/
# https://www.nanumtrading.com/fx-%EB%B0%B0%EC%9A%B0%EA%B8%B0/%EC%B0%A8%ED%8A%B8-%EB%B3%B4%EC%A1%B0%EC%A7%80%ED%91%9C-%EC%9D%B4%ED%95%B4/06-%EC%9D%BC%EB%AA%A9%EA%B7%A0%ED%98%95%ED%91%9C/
# 일목균형표 - 매매기법 알아보기 !: https://layhope.tistory.com/222
class IchimokuCloud:
common = None
def __init__(self):
self.common = Common()
return
# c=9, b=26, l=52
def apply(self, df, c=9, b=26, l=52):
# 입력받은 값이 dataframe이라는 것을 정의해줌
df = pd.DataFrame(df)
# 1. 전환선 = (과거 9일 동안 최고가 + 최저가) / 2
# 당일을 포함한 9일 동안의 최고가와 최저가의 중간 값을 평균으로 나타낸다.
changeLine = (df.high.rolling(c).max() + df.low.rolling(c).min()) / 2
# 2. 기준선 = 과거 26일 동안 최고가 + 최저가) / 2
# 당일을 포함한 26일 동안의 최고가와 최저가의 중간 값을 평균으로 나타낸다.
baseLine = (df.high.rolling(b).max() + df.low.rolling(b).min()) / 2
# 3. 선행스팬 1 = ((기준선 + 전환선) / 2)를 26일 선행하여 배치
# 전환선과 기준선의 평균값을 구해 당일 포함 26일 앞으로 이동시킨 선 (중-단기 구간의 힘을 보여줌)
leadingSpan1 = (changeLine + baseLine) / 2
move_LeadingSpan1 = list(leadingSpan1.values)
for i in range(b - 1):
move_LeadingSpan1.insert(0, None)
# 4. 선행스팬 2 = ((최근 52일 동안 최고가 + 최저가) / 2)를 26일 선행하여 배치
# 당일을 포함한 52일 동안의 최고가와 최저가의 평균을 26일 앞으로 이동시킨 선 (장기으로 형성된 선이기 때문에 가장 느리게 변함)
leadingSpan2 = (df.high.rolling(l).max() + df.low.rolling(l).min()) / 2
move_LeadingSpan2 = list(leadingSpan2.values)
for i in range(l - 1):
move_LeadingSpan2.insert(0, None)
# leadingSpan2에 맞추어 뒤로 빈 row를 채운다.
data = {"DATE": [], "close": [], "diff": [], "open": [], "high": [], "low": [], "volume": [], "avg5": [], "avg10": [], "avg20": [], "avg60": [], "avg120": [], "avg200": [], "avg240": []}
for i in range(len(move_LeadingSpan2) - len(df)):
#df = df.append({"DATE": None, "close": None, "diff": None, "open": None, "high": None, "low": None, "volume": None,"avg3": None, "avg5": None, "avg7": None, "avg10": None, "avg20": None, "avg30": None, "avg60": None,"avg90": None, "avg100": None, "avg120": None, "avg150": None, "avg180": None, "avg200": None,"avg240": None}, ignore_index=True)
data["DATE"].append(None)
data["close"].append(None)
data["diff"].append(None)
data["open"].append(None)
data["high"].append(None)
data["low"].append(None)
data["volume"].append(None)
data["avg5"].append(None)
data["avg10"].append(None)
data["avg20"].append(None)
data["avg60"].append(None)
data["avg120"].append(None)
data["avg200"].append(None)
data["avg240"].append(None)
df_temp = pd.DataFrame(data)
df = pd.concat([df, df_temp])
move_changeLine = list(changeLine.values)
for i in range(len(move_LeadingSpan2) - len(move_changeLine)):
move_changeLine.append(None)
move_baseLine = list(baseLine.values)
for i in range(len(move_LeadingSpan2) - len(move_baseLine)):
move_baseLine.append(None)
for i in range(len(move_LeadingSpan2) - len(move_baseLine)):
move_LeadingSpan1.append(None)
# dataframe에 컬럼 추가
df = df.assign(changeLine=pd.Series(move_changeLine), baseLine=pd.Series(move_baseLine), leadingSpan1=pd.Series(move_LeadingSpan1), leadingSpan2=pd.Series(move_LeadingSpan2))
return df
# 일목균형표의 구성을 훑어보면 주가를 선행과 후행으로 과거의 주가를 통해 미래 혹은 현재의 주식의 가격의 추세를 예측해보려는 지표라는 것을 이해할 수 있다.
# 또한 구름층의 색을 통해서 주식의 추세를 손쉽게 확인할 수 있을 것 같다는 것도 이해할 수 있다면 끝 !
def analyze(self, stock):
df = pd.DataFrame()
df = df.from_dict(stock['PRICE'])
df = self.apply(df)
diff = len(df.changeLine) - len(stock['PRICE'])
"""
lastDay = stock['PRICE'][len(stock['PRICE']) - 1]['ymd']
tmpLastDay = datetime.datetime.strptime(lastDay, "%Y.%m.%d")
for i in range(diff):
nextDay = tmpLastDay + datetime.timedelta(days=(i + 1))
stock['PRICE'].append(
{"ymd": nextDay.strftime("%Y-%m-%d"), "close": 0, "diff": 0, "open": 0, "high": 0, "low": 0, "volume": 0,
"avg3": 0, "avg5": 0, "avg7": 0, "avg10": 0, "avg20": 0, "avg30": 0, "avg60": 0, "avg90": 0, "avg100": 0,
"avg120": 0, "avg150": 0, "avg180": 0, "avg200": 0, "avg240": 0})
"""
for i in range(len(df.changeLine)-diff):
stock['PRICE'][i]['ichimokucloud_changeLine'] = df.changeLine.values[i]
stock['PRICE'][i]['ichimokucloud_baseLine'] = df.baseLine.values[i]
stock['PRICE'][i]['ichimokucloud_leadingSpan1'] = df.leadingSpan1.values[i]
stock['PRICE'][i]['ichimokucloud_leadingSpan2'] = df.leadingSpan2.values[i]
return
if __name__ == "__main__":
ichimokuCloud = IchimokuCloud()

81
stock/analysis/MACD.py Normal file
View File

@@ -0,0 +1,81 @@
import pandas as pd
from stockpredictor.analysis.Common import Common
# [청송촌놈] 파생을 알아야 시장이 보인다. 청송이 종목 고르는법! https://www.youtube.com/watch?v=weABtgZDeGg
# 6. Pandas와 Plotly를 이용한 MACD 차트 그리기 https://excelsior-cjh.tistory.com/110
# 첫번째. MACD 지표를 이용한 차트분석: https://post.naver.com/viewer/postView.nhn?volumeNo=7435935&memberNo=32471429
# MACD (Moving Average Conver gence Divergence)
# 빨간 네모박스권으로 MACD가 MACD-Sign 을 골든크로스하며, 상승하였을때, 주가는 상승추세를 유지하며, MACD가 MACD-Sign(분홍색)을 데드크로스 할때 주가는 하락의 추세를 보이게 됩니다.
# 즉, MSCD가 0이상에서 MACD-Sign 위에서 상승하는 그림이어야
class MACD:
common = None
def __init__(self):
self.common = Common()
return
# macd 0선 위에서 매수를 한다. 0이하는 절대 처다보지 않는다.
def apply(self, df, short=12, long=26, t=9):
# 입력받은 값이 dataframe이라는 것을 정의해줌
df = pd.DataFrame(df)
# MACD 관련 수식
ma_12 = df.close.ewm(span=short).mean() # 단기(12) EMA(지수이동평균)
ma_26 = df.close.ewm(span=long).mean() # 장기(26) EMA
macd = ma_12 - ma_26 # MACD
macds = macd.ewm(span=t).mean() # Signal
macdo = macd - macds # Oscillator
#df = df.assign(macd=macd, macds=macds, macdo=macdo).dropna()
df = df.assign(macd=macd, macds=macds, macdo=macdo)
return df
"""
# 기존 stock에 삽입
# macd 0선 위에서 매수를 한다. 0이하는 절대 처다보지 않는다.
def analyze(self, stock):
df = pd.DataFrame()
df = df.from_dict(stock['PRICE'])
df = self.apply(df)
for i in range(len(df.macd)):
stock['PRICE'][i]['macd_buy'] = 0
stock['PRICE'][i]['macd'] = df.macd.values[i]
stock['PRICE'][i]['macds'] = df.macds.values[i]
stock['PRICE'][i]['macdo'] = df.macdo.values[i]
if df.macd.values[i] > 0 and self.common.checkUpward('close', stock['PRICE'][i - 4: i + 1]):
stock['PRICE'][i]['macd_buy'] = df.macd.values[i]
return
"""
def analyze(self, stock):
results = []
df = pd.DataFrame()
df = df.from_dict(stock['PRICE'])
df = self.apply(df)
for i in range(len(df.macd)):
result = {'DATE':stock['PRICE'][i]['DATE'],
'macd': df.macd.values[i],
'macds': df.macds.values[i],
'macdo': df.macdo.values[i],
'macd_buy': 0}
# MACD가 3일 전부터 상승이라면 매수
if df.macd.values[i - 1] < df.macd.values[i]:
# 어제는 MACD-Sign이 MACD 위에 있지만, 오늘은 MACD가 MACD-Sign 위로 올라오면 매수
if df.macd.values[i-1] < df.macds.values[i-1] and df.macd.values[i] > df.macds.values[i]:
result['macd_buy'] = 1
# MACD가 3일 전부터 하락이라면 매도
if df.macd.values[i - 1] > df.macd.values[i]:
# 어제는 MACD가 MACD-Sign 위에 있지만, 오늘은 MACD-Sign이 MACD 위로 올라오면 매도
if df.macd.values[i - 1] > df.macds.values[i - 1] and df.macd.values[i] < df.macds.values[i]:
result['macd_buy'] = -1
results.append(result)
return results

55
stock/analysis/RSI.py Normal file
View File

@@ -0,0 +1,55 @@
import pandas as pd
from stockpredictor.analysis.Common import Common
import numpy as np
# [청송촌놈] 파생을 알아야 시장이 보인다. 청송이 종목 고르는법! https://www.youtube.com/watch?v=weABtgZDeGg
# 6. Pandas와 Plotly를 이용한 MACD 차트 그리기 https://excelsior-cjh.tistory.com/110
# 첫번째. MACD 지표를 이용한 차트분석: https://post.naver.com/viewer/postView.nhn?volumeNo=7435935&memberNo=32471429
# MACD (Moving Average Conver gence Divergence)
# 빨간 네모박스권으로 MACD가 MACD-Sign 을 골든크로스하며, 상승하였을때, 주가는 상승추세를 유지하며, MACD가 MACD-Sign(분홍색)을 데드크로스 할때 주가는 하락의 추세를 보이게 됩니다.
# 즉, MSCD가 0이상에서 MACD-Sign 위에서 상승하는 그림이어야
class RSI:
common = None
def __init__(self):
self.common = Common()
return
def apply(sefl, df, period=14, window=9):
df = pd.DataFrame(df)
# df.diff를 통해 (기준일 종가 - 기준일 전일 종가)를 계산하여 0보다 크면 증가분을 감소했으면 0을 넣어줌
U = np.where(df.close.diff(1) > 0, df.close.diff(1), 0)
# df.diff를 통해 (기준일 종가 - 기준일 전일 종가)를 계산하여 0보다 작으면 감소분을 증가했으면 0을 넣어줌
D = np.where(df.close.diff(1) < 0, df.close.diff(1) * (-1), 0)
# AU, period=14일 동안의 U의 평균
AU = pd.DataFrame(U).rolling(window=period, min_periods=period).mean()
# AD, period=14일 동안의 D의 평균
AD = pd.DataFrame(D).rolling(window=period, min_periods=period).mean()
rsi = AU.div(AD + AU) * 100
rsis = rsi.rolling(window=window).mean()
df = df.assign(rsi=rsi, rsis=rsis)
return df
def analyze(self, stock):
"""
RSI 값이 100에 접근하면 ㄷ 이상의 주가 상승을 기대하기 어렵고, 0에 접근하면 더 이상 하락을 기대하기 어렵다.
70이상이면 과매수 구간이라 할 수 있고, 30 이하면 과매도 구간이라 볼 수 있다.
따라서 과매수 구간에서는 매도 준비를, 과매도 구간에서는 매수 준비를 해야 한다.
"""
df = pd.DataFrame()
df = df.from_dict(stock['PRICE'])
df = self.apply(df)
for i in range(len(df.rsi)):
stock['PRICE'][i]['rsi'] = df.rsi.values[i]
stock['PRICE'][i]['rsis'] = df.rsis.values[i]

View File

@@ -0,0 +1,147 @@
import pandas as pd
from stockpredictor.analysis.Common import Common
# 6. Pandas와 Plotly를 이용한 MACD 차트 그리기: https://excelsior-cjh.tistory.com/111
# 스토캐스틱 슬로우(Stochastics Slow)를 이용한 간단한 매매기법: https://bagal.tistory.com/124
# Stochastic Slow, 스토캐스틱을 통한 주식 매매법 정리: https://m.blog.naver.com/PostView.nhn?blogId=kangyh427&logNo=220957146041&proxyReferer=https:%2F%2Fwww.google.com%2F
# 차트분석 2편 스토캐스틱을 활용한 매수타이밍 포착!: https://m.post.naver.com/viewer/postView.nhn?volumeNo=7446693&memberNo=32471429
class Stochastic:
common = None
def __init__(self):
self.common = Common()
return
# 일자(n,m,t)에 따른 Stochastic(KDJ)의 값을 구하기 위해 함수형태로 만듬
# n=15 (%k), m=5 (%d), t=3
# n=5 (%k), m=3 (%d), t=3
# n=14 (%k), m=3 (%d), t=3
def apply(self, df, n=12, m=5, t=5):
# 입력받은 값이 dataframe이라는 것을 정의해줌
df = pd.DataFrame(df)
# n일중 최고가
ndays_high = df.high.rolling(n).max()
# n일중 최저가
ndays_low = df.low.rolling(n).min()
# Fast%K 계산
# n(15)일 동안의 최고가(high)와 최저가(low) 사이 중 현재 종가(close)의 상대적 위치를 판단하는 값
# 가격이 지속적으로 상승하고 있다면 Stochastic 값은 100에 가까워 지며, 반대로 지속적으로 하락하고 있다면 Stochastic 값은 0에 가까워 지는 경향을 나타낸다.
fast_k = ((df.close - ndays_low) / (ndays_high - ndays_low)) * 100
# Fast%D (=Slow%K) 계산
# m(5)일간의 Fast%K의 이동평균 값
# 기본값으로 5일을 설정하며 Fast%K 값을 일반화하는 역할을 한다.
#slow_k = fast_k.ewm(span=m).mean()
slow_k = fast_k.rolling(m).mean()
# Slow%D 계산
# t(3)일간의 Slow%K의 이동평균 값
# 기본값으로 3일을 설정하며 Slow%K 값을 일반화 하는 역할을 한다.
#slow_d = slow_k.ewm(span=t).mean()
slow_d = slow_k.rolling(t).mean()
# dataframe에 컬럼 추가
#df = df.assign(fast_k=fast_k, slow_k=slow_k, slow_d=slow_d).dropna()
df = df.assign(fast_k=fast_k, slow_k=slow_k, slow_d=slow_d)
return df
"""
def analyze(self, stock):
df = pd.DataFrame()
df = df.from_dict(stock['PRICE'])
df = self.apply(df)
for i in range(len(df.fast_k)):
stock['PRICE'][i]['stochastic_buy'] = 0
if "fast_k" not in stock['PRICE'][i]:
stock['PRICE'][i]['fast_k'] = -1
stock['PRICE'][i]['slow_k'] = -1
stock['PRICE'][i]['slow_d'] = -1
i_ = i + len(stock['PRICE']) - len(df.fast_k)
stock['PRICE'][i_]['fast_k'] = df.fast_k.values[i]
stock['PRICE'][i_]['slow_k'] = df.slow_k.values[i]
stock['PRICE'][i_]['slow_d'] = df.slow_d.values[i]
# 0: 중립, 1: 매수, -1: 매도
stock['PRICE'][i_]['stochastic_buy'] = 0
stock['PRICE'][i_]['direction'] = ''
if i_ > 2:
if ('slow_k' in stock['PRICE'][i_-1] and 'slow_k' in stock['PRICE'][i_]):
# %k선이 %d선을 상향 돌파하면 매수 신호
if (self.common.checkUpward('slow_k', stock['PRICE'][i_-2:i_+1]) and
stock['PRICE'][i_-1]['slow_k'] < stock['PRICE'][i_-1]['slow_d'] and
stock['PRICE'][i_]['slow_k'] > stock['PRICE'][i_]['slow_d']):
if stock['PRICE'][i_]['slow_k'] < 5:
stock['PRICE'][i_]['stochastic_buy'] = 8
elif 5 <= stock['PRICE'][i_]['slow_k'] < 10:
stock['PRICE'][i_]['stochastic_buy'] = 7
elif 10 <= stock['PRICE'][i_]['slow_k'] < 15:
stock['PRICE'][i_]['stochastic_buy'] = 6
elif 15 <= stock['PRICE'][i_]['slow_k'] < 20:
stock['PRICE'][i_]['stochastic_buy'] = 5
elif 20 <= stock['PRICE'][i_]['slow_k'] < 30:
stock['PRICE'][i_]['stochastic_buy'] = 4
elif 30 <= stock['PRICE'][i_]['slow_k'] < 40:
stock['PRICE'][i_]['stochastic_buy'] = 3
elif 40 <= stock['PRICE'][i_]['slow_k'] < 50:
stock['PRICE'][i_]['stochastic_buy'] = 2
else:
stock['PRICE'][i_]['stochastic_buy'] = 1
if self.common.checkUpward('slow_k', stock['PRICE'][i_ - 1:i_ + 1]):
stock['PRICE'][i_]['direction'] = 'up'
if (self.common.checkDownward('slow_k', stock['PRICE'][i_ - 2:i_ + 1]) and
stock['PRICE'][i_-1]['slow_k'] > stock['PRICE'][i_-1]['slow_d'] and
stock['PRICE'][i_]['slow_k'] < stock['PRICE'][i_]['slow_d']):
if stock['PRICE'][i_]['slow_k'] > 90:
stock['PRICE'][i_]['stochastic_buy'] = -6
elif 90 >= stock['PRICE'][i_]['slow_k'] > 80:
stock['PRICE'][i_]['stochastic_buy'] = -5
elif 80 >= stock['PRICE'][i_]['slow_k'] > 70:
stock['PRICE'][i_]['stochastic_buy'] = -4
elif 70 >= stock['PRICE'][i_]['slow_k'] > 60:
stock['PRICE'][i_]['stochastic_buy'] = -3
elif 60 >= stock['PRICE'][i_]['slow_k'] > 50:
stock['PRICE'][i_]['stochastic_buy'] = -2
else:
stock['PRICE'][i_]['stochastic_buy'] = -1
if self.common.checkDownward('slow_k', stock['PRICE'][i_ - 1:i_ + 1]):
stock['PRICE'][i_]['direction'] = 'down'
results = []
for day in stock['PRICE']:
results.append({'DATE': day['DATE'],
'fast_k': day['fast_k'],
'slow_k': day['slow_k'],
'slow_d': day['slow_d'],
'stochastic_buy': day['stochastic_buy']})
return results
"""
def analyze(self, stock):
df = pd.DataFrame()
df = df.from_dict(stock['PRICE'])
df = self.apply(df)
for i in range(len(df.fast_k)):
if "stochastic_fast_k" not in stock['PRICE'][i]:
stock['PRICE'][i]['stochastic_fast_k'] = -1
stock['PRICE'][i]['stochastic_slow_k'] = -1
stock['PRICE'][i]['stochastic_slow_d'] = -1
stock['PRICE'][i]['stochastic_fast_k'] = df.fast_k.values[i]
stock['PRICE'][i]['stochastic_slow_k'] = df.slow_k.values[i]
stock['PRICE'][i]['stochastic_slow_d'] = df.slow_d.values[i]
return
if __name__ == "__main__":
stochastic = Stochastic()