This commit is contained in:
dsyoon
2021-02-18 08:10:56 +09:00
parent 9af08dbee2
commit 2f6b33f8f2
4 changed files with 152 additions and 37 deletions

View File

@@ -12,6 +12,8 @@ import sqlite3
from datetime import datetime from datetime import datetime
from matplotlib import rc from matplotlib import rc
import math
rc('font', family='AppleGothic') rc('font', family='AppleGothic')
plt.rcParams['axes.unicode_minus'] = False plt.rcParams['axes.unicode_minus'] = False
@@ -71,26 +73,14 @@ class Analyzer:
while result != None: while result != None:
data = json.loads(result[2]) data = json.loads(result[2])
self.fnguide[result[0]] = True self.fnguide[result[0]] = True
if (year1 in data):
if (data[year1]['영업이익'] > 0 and data[year1]['당기순이익'] > 0): if (year1 in data and year2 in data and year3 in data):
self.fnguide[result[0]] = True if (data[year1]['영업이익'] < 0 and data[year2]['영업이익'] < 0 and data[year3]['영업이익'] < 0):
if (year2 in data): # 3년 연속 영업이익이 적자이면 매수하지 않는다.
if (data[year2]['영업이익'] > 0 and data[year2]['당기순이익'] > 0): self.fnguide[result[0]] = False
self.fnguide[result[0]] = True if (data[year1]['영업이익'] < -100):
if (year3 in data): # 전년 영억적자가 100억 이상이면 매수하지 않는다.
if (data[year3]['영업이익'] > 0 and data[year3]['당기순이익'] > 0):
self.fnguide[result[0]] = True
else:
self.fnguide[result[0]] = False
else:
if (data[year1]['영업이익'] > data[year2]['영업이익']):
self.fnguide[result[0]] = True
else:
self.fnguide[result[0]] = False
else:
self.fnguide[result[0]] = False self.fnguide[result[0]] = False
else:
self.fnguide[result[0]] = False
rowid += 1 rowid += 1
cursor.execute('SELECT * FROM fnguide WHERE rowid=?', (rowid,)) cursor.execute('SELECT * FROM fnguide WHERE rowid=?', (rowid,))
@@ -314,6 +304,8 @@ class Analyzer:
1) 상향이고 30을 돌파하면 매수, 1) 상향이고 30을 돌파하면 매수,
2) rsi가 상향이고 40을 돌파하면 매수, 2) rsi가 상향이고 40을 돌파하면 매수,
3) rsi가 상향이고 70을 돌파하면 단기매수, 3) rsi가 상향이고 70을 돌파하면 단기매수,
시장 보다 강한 종목이란 (https://biz.sbs.co.kr/article/10000976754)
""" """
i = last_index i = last_index
@@ -343,14 +335,30 @@ class Analyzer:
if stochastic_score > 0: if stochastic_score > 0:
return True, buy_price, stochastic_score return True, buy_price, stochastic_score
""" """
if STOCHASTIC[i]['slow_k'] < 10 and self.common.checkLongYangBongAfterUmBong(STOCK, i):
return 'STOCHASTIC_YANGBONG', buy_price
if STOCHASTIC[i]['slow_k'] < 10:
return 'STOCHASTIC', buy_price
if self.common.checkLongYangBongAfterUmBong(STOCK, i):
return 'YANGBONG', buy_price
return "", buy_price status = ""
if STOCK[i]['volume'] > 100000 and STOCK[i]['close'] > 1000:
# 거래량이 10만 이상이고, 종가가 1천원 이상인지 체크 (https://happpy-rich.tistory.com/94)
if self.common.check_on_120_daysLine(STOCK, i):
# 특히 종목이 120일선을 뚫은 후 60일선이 상승세인지를 확인한다. (https://biz.sbs.co.kr/article/10000976754)
if STOCHASTIC[i]['slow_k'] < 70:
status += '120_'
if self.common.check_on_60_daysLine(STOCK, i):
if STOCHASTIC[i]['slow_k'] < 70:
status += '60_'
if self.common.check_on_20_daysLine(STOCK, i):
if STOCHASTIC[i]['slow_k'] < 70:
status += '20_'
if STOCHASTIC[i]['slow_k'] < 10:
# 스토캐스틱이 10 이하였다면,
status += 'STOCHASTIC_'
if self.common.checkLongYangBongAfterUmBong(STOCK, i):
# 어제 음봉 이후 장대양봉이었다면,
status += 'YANGBONG_'
return status, buy_price
def analyzeToFile(self, outFileName): def analyzeToFile(self, outFileName):
conn = sqlite3.connect(self.inFileName) conn = sqlite3.connect(self.inFileName)
@@ -403,14 +411,28 @@ class Analyzer:
while result != None: while result != None:
item_code = result[0] item_code = result[0]
item_name = result[1] item_name = result[1]
"""
if (item_code in self.fnguide and not self.fnguide[item_code]): # 부실 기업은 매수하지 않고 그냥 넘긴다.
# kospi 지수와 kosdak 지수도 그냥 넘긴다.
if ((item_code in self.fnguide and not self.fnguide[item_code]) or (item_code == "KOSPI" or item_code == "KOSDAK")):
rowid += 1 rowid += 1
# 다음 종목을 가져옴
cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,)) cursor.execute('SELECT * FROM ' + self.tableName + ' WHERE rowid=?', (rowid,))
result = cursor.fetchone() result = cursor.fetchone()
continue continue
"""
stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2]), "MACD": json.loads(result[3]), "STOCHASTIC": json.loads(result[4]), "ICHIMOKU": json.loads(result[5]), "RSI": json.loads(result[6])} result_3 = result[3]
result_4 = result[4]
result_5 = result[5]
result_6 = result[6]
if result[3] != result[3]: result_3 = result[3].replace("NaN", "0")
if result[4] != result[4]: result_4 = result[4].replace("NaN", "0")
if result[5] != result[5]: result_5 = result[5].replace("NaN", "0")
if result[6] != result[6]: result_6 = result[6].replace("NaN", "0")
if rowid == 2435:
print (1)
stock = {"CODE": result[0], "NAME": result[1], "PRICE": json.loads(result[2]), "MACD": json.loads(result_3), "STOCHASTIC": json.loads(result_4), "ICHIMOKU": json.loads(result_5), "RSI": json.loads(result_6)}
last_index = self.get_last_index(stock) last_index = self.get_last_index(stock)
STOCK = stock['PRICE'] STOCK = stock['PRICE']
@@ -432,7 +454,7 @@ class Analyzer:
fileName = "%s/%s__%.3f__%.3f__%s.html" % (outPath, state, stochastic_score, rsi_score, item_name.replace(" ", "")) fileName = "%s/%s__%.3f__%.3f__%s.html" % (outPath, state, stochastic_score, rsi_score, item_name.replace(" ", ""))
po.write_html(fig, file=fileName, auto_open=False) po.write_html(fig, file=fileName, auto_open=False)
else: else:
if RSI[last_index]['rsi_buy'] == 1 and STOCK[last_index]['volume'] > 10000: if RSI[last_index]['rsi_buy'] == 1 and STOCK[last_index]['volume'] > 100000:
fig = self.draw(stock) fig = self.draw(stock)
title = "%s (%s) buy_price (%d), stochastic(%.3f), rsi(%.3f), macd(%.3f), ichimoku(%d)) 차트"%(item_name, item_code, buy_price, stochastic_score, rsi_score, macd_score, ichimoku_score) title = "%s (%s) buy_price (%d), stochastic(%.3f), rsi(%.3f), macd(%.3f), ichimoku(%d)) 차트"%(item_name, item_code, buy_price, stochastic_score, rsi_score, macd_score, ichimoku_score)
fig['layout'].update(title=title) fig['layout'].update(title=title)

View File

@@ -132,3 +132,30 @@ class Common:
if stock[i-1]['volume']*2 < stock[i]['volume']: # 어제 거래량 보다 두배 이상일 때 if stock[i-1]['volume']*2 < stock[i]['volume']: # 어제 거래량 보다 두배 이상일 때
return True return True
return False return False
def check_on_20_daysLine(self, stock, i):
# 20일선 돌파를 체크할 때는 5일선 < 20일선 < 120일선 < 60일선 이어야 하며, 5일선은 상승중이어야 한다. (삼성전자 2020년 4월 6일)
if i > 0:
if stock[i-1]['avg20'] > stock[i-1]['close'] and stock[i]['avg20'] < stock[i]['close']:
if stock[i]['avg5'] < stock[i]['avg20'] < stock[i]['avg120'] < stock[i]['avg60']:
if stock[i-1]['avg5'] < stock[i]['avg5']:
return True
return False
def check_on_60_daysLine(self, stock, i):
# 60일선 돌파를 체크할 때는 20일선 < 5일선 < 60일 < 120일선 이어야 하며, 5일선과 20일선은 상승중이어야 한다. (삼성전자 2020년 5월 27일)
if i > 0:
if stock[i-1]['avg60'] > stock[i-1]['close'] and stock[i]['avg60'] < stock[i]['close']:
if stock[i]['avg20'] < stock[i]['avg5'] < stock[i]['avg60'] < stock[i]['avg120']:
if stock[i-1]['avg5'] < stock[i]['avg5'] and stock[i-1]['avg20'] < stock[i]['avg20']:
return True
return False
def check_on_120_daysLine(self, stock, i):
# 120일선 돌파를 체크할 때는 60일선이 상승 중이어야 한다.
if i > 0:
if stock[i-1]['avg120'] > stock[i-1]['close'] and stock[i]['avg120'] < stock[i]['close']:
if stock[i-1]['avg5'] < stock[i]['avg5'] and stock[i-1]['avg20'] < stock[i]['avg20'] and stock[i-1]['avg60'] < stock[i]['avg60']:
return True
return False

View File

@@ -19,8 +19,6 @@ print("[KOSPI 상장기업 재무제표 다운로드]")
crawler.crawl_fnguide(inFnguideFileName) crawler.crawl_fnguide(inFnguideFileName)
""" """
crawler = MetaCrawler() crawler = MetaCrawler()
print("\n[환율 (USD, JPY, EUR, CNY), 원유 (WTI), 국제금]") print("\n[환율 (USD, JPY, EUR, CNY), 원유 (WTI), 국제금]")
inFileName = PROJECT_HOME + '/resources/meta_1.db' inFileName = PROJECT_HOME + '/resources/meta_1.db'
@@ -44,6 +42,14 @@ crawler = StockCrawler()
crawler.crawl_etf_stocks(inFileName) crawler.crawl_etf_stocks(inFileName)
crawler.crawl_stocks(inFileName) crawler.crawl_stocks(inFileName)
print("\n[지수 저장]")
kospiFileName = PROJECT_HOME + '/resources/kospi.tsv'
kosdakFileName = PROJECT_HOME + '/resources/kosdak.tsv'
outFileName = PROJECT_HOME + '/resources/stock.db'
crawler = StockCrawler()
crawler.saveIndex("KOSPI", kospiFileName, outFileName)
crawler.saveIndex("KOSDAK", kosdakFileName, outFileName)
print("\n[종목 분석]") print("\n[종목 분석]")
# S: 분석까지 진행 # S: 분석까지 진행
inFileName = PROJECT_HOME + '/resources/stock.db' inFileName = PROJECT_HOME + '/resources/stock.db'

View File

@@ -223,12 +223,10 @@ class StockCrawler:
###print (df.head()) ###print (df.head())
# 한글로 된 컬럼명을 영어로 바꿔줌 # 한글로 된 컬럼명을 영어로 바꿔줌
df = df.rename(columns={'날짜': 'date', '종가': 'close', '전일비': 'diff', '시가': 'open', '고가': 'high', '저가': 'low', df = df.rename(columns={'날짜': 'date', '종가': 'close', '전일비': 'diff', '시가': 'open', '고가': 'high', '저가': 'low', '거래량': 'volume'})
'거래량': 'volume'})
# 데이터의 타입을 int형으로 바꿔줌 # 데이터의 타입을 int형으로 바꿔줌
df[['close', 'diff', 'open', 'high', 'low', 'volume']] = df[ df[['close', 'diff', 'open', 'high', 'low', 'volume']] = df[['close', 'diff', 'open', 'high', 'low', 'volume']].astype(int)
['close', 'diff', 'open', 'high', 'low', 'volume']].astype(int)
# 컬럼명 'date'의 타입을 date로 바꿔줌 # 컬럼명 'date'의 타입을 date로 바꿔줌
df['date'] = pd.to_datetime(df['date']) df['date'] = pd.to_datetime(df['date'])
@@ -350,3 +348,65 @@ class StockCrawler:
cursor.execute("UPDATE " + tableName + " SET PRICE=? WHERE CODE=?", (text, stock["CODE"])) cursor.execute("UPDATE " + tableName + " SET PRICE=? WHERE CODE=?", (text, stock["CODE"]))
return return
def saveIndex(self, code, inFileName, outFileName):
tableName = 'stock'
conn = sqlite3.connect(outFileName)
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS " + tableName + " (CODE text PRIMARY KEY, NAME text, PRICE text)")
stock = {"NAME": code, "CODE": code, "PRICE": []}
lastDay = ""
cursor.execute('SELECT * FROM ' + tableName + ' WHERE CODE=?', (stock["CODE"],))
result = cursor.fetchone()
if result != None:
stock["PRICE"] = json.loads(result[2])
lastDay = stock["PRICE"][len(stock["PRICE"]) - 1]["DATE"]
with open(inFileName, "r", encoding="utf-8") as inFp:
for line in inFp:
line = line.strip()
if line[0] == "#":
continue
arr = line.split("\t")
if arr[0] == lastDay:
cursor.close()
conn.close()
return
price = {"DATE": arr[0], "close": float(arr[1]), "diff": float(arr[6].replace("%", "")), "open": float(arr[2]), "high": float(arr[3]), "low": float(arr[4]), "volume": 0}
price['avg3'] = 0
price['avg5'] = 0
price['avg7'] = 0
price['avg10'] = 0
price['avg20'] = 0
price['avg30'] = 0
price['avg60'] = 0
price['avg90'] = 0
price['avg100'] = 0
price['avg120'] = 0
price['avg150'] = 0
price['avg180'] = 0
price['avg200'] = 0
price['avg240'] = 0
stock["PRICE"].append(price)
stock["PRICE"] = sorted(stock["PRICE"], key=lambda x: x['DATE'])
self.get_moving_avg(stock)
text = json.dumps(stock['PRICE'], ensure_ascii=False)
cursor.execute('SELECT * FROM ' + tableName + ' WHERE CODE=?', (stock["CODE"],))
result = cursor.fetchone()
if result == None:
cursor.execute("INSERT INTO " + tableName + "(CODE, NAME, PRICE, MACD, STOCHASTIC, ICHIMOKU, RSI) VALUES(?, ?, ?, ?, ?, ?, ?)", (stock["CODE"], stock["NAME"], text, "[{}]", "[{}]", "[{}]", "[{}]"))
else:
cursor.execute("UPDATE " + tableName + " SET PRICE=?, MACD=?, STOCHASTIC=?, ICHIMOKU=?, RSI=? WHERE CODE=?", (text, "[{}]", "[{}]", "[{}]", "[{}]", stock["CODE"]))
conn.commit()
cursor.close()
conn.close()
return