init
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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'
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user