Files
DeepStock/HTS_stocks.py
dsyoon ced4175d38 init
2023-03-19 20:44:42 +09:00

362 lines
16 KiB
Python

import re
import os
import time
import math
import sqlite3
from datetime import datetime
from hts.HTS import HTS
from hts.OrderType import OrderType
from hts.BuySellChecker import BuySellChecker
from hts.OrderChecker import OrderChecker
from stock.util.SlackBot import SlackBot
from stock.analysis.StockStatus import StockStatus
class HTS_Stocks (HTS):
RESOURCE_PATH = None
orderChecker = None
buySellChecker = None
labelChecker = None
slackBot = None
stockStatus = None
analyzed_day = None
MAX_BUY_PRICE = None
conn_stock = None
cursor_stock = None
def __init__(self, RESOURCE_PATH):
super().__init__(RESOURCE_PATH)
self.slackBot = SlackBot()
self.RESOURCE_PATH = RESOURCE_PATH
self.stockStatus = StockStatus(RESOURCE_PATH)
self.buySellChecker = BuySellChecker()
self.orderChecker = OrderChecker(self.RESOURCE_PATH, "STOCK")
self.analyzed_day = 120
return
def connect2StockDB(self):
self.conn_stock = sqlite3.connect(os.path.join(self.RESOURCE_PATH, "stock.db"))
self.cursor_stock = self.conn_stock.cursor()
return
def disconnectStockDB(self):
self.cursor_stock.close()
self.conn_stock.close()
return
def sellStocks(self, stock_code=None, bs_sell_price=None):
check = False
jangoDic = self.requstJango()
if jangoDic and len(jangoDic.keys()) > 0:
for code in jangoDic:
if stock_code is not None:
if code == "A"+stock_code and bs_sell_price is not None:
if jangoDic[code]['매도가능'] > 0:
#if 2 < jangoDic[code]['평가손익']:
# 2% 이상 시 수익 매도
if jangoDic[code]['평가손익'] < -5:
# 5% 손해면 매도함
self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], bs_sell_price)
self.slackBot.post_to_slack(code, jangoDic[code]['종목명'], "SELL", bs_sell_price, jangoDic[code]['매도가능'])
check = True
else:
continue
else:
if jangoDic[code]['매도가능'] > 0:
#if 3 < jangoDic[code]['평가손익']:
# 3% 이상 시 수익 매도
if jangoDic[code]['평가손익'] < -5:
# 5% 손해면 매도함
currentStock = self.currentStock(code[1:])
self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], currentStock['close'])
self.slackBot.post_to_slack(code, jangoDic[code]['종목명'], "SELL", currentStock['close'], jangoDic[code]['매도가능'])
check = True
return check
def getSellingPrice(self, log_time, stock_code, final_price, without_loss=False):
# final_price와 diff를 받으면, 해당 가격으로 그냥 매도한다는 의미
# final_price와 diff가 None이면 장부가와 final 중 max로 팔겠다는 의미
# final_price가 0이고 diff가 None이면 장부가로 팔겠다는 의미임
orderNum = None
jangoDic = self.requstJango()
if jangoDic and len(jangoDic.keys()) > 0:
for code in jangoDic:
if code == "A" + stock_code:
if jangoDic[code]['매도가능'] > 0:
if without_loss:
if final_price - jangoDic[code]['장부가'] > jangoDic[code]['장부가']*0.02:
orderNum = self.requestOrder(OrderType.sell, stock_code, jangoDic[code]['매도가능'], final_price)
return orderNum, log_time.strftime('%Y%m%d %H%M%S'), jangoDic[code]['매도가능'], final_price
else:
#max_price = max(jangoDic[code]['장부가'], final_price)
# 10% 이상 수익이어야 매도한다.
max_price = int(jangoDic[code]['장부가'] * 1.10)
if max_price <= final_price:
# 2% 이상 상승이면 매도 가능함
sell_price = (int(max_price) - int(max_price) % 2)
orderNum = self.requestOrder(OrderType.sell, stock_code, jangoDic[code]['매도가능'], sell_price)
return orderNum, log_time.strftime('%Y%m%d %H%M%S'), jangoDic[code]['매도가능'], sell_price
return orderNum, None, None, None
def getCompanyInfo(self):
self.cursor_stock.execute('SELECT distinct code, name FROM stock order by code')
all_stocks = self.cursor_stock.fetchall()
valid_company = dict()
self.cursor_stock.execute('select CODE, NAME, max(ymd) as ymd from fnguide where type != "E" and sales > 0 group by 1 order by sales desc')
items = self.cursor_stock.fetchall()
for i, item in enumerate(items):
if i < 1000:
valid_company[item[0]] = i
return all_stocks, valid_company
def getSlowK(self, stock_code):
slow_k, p_slow_k, slow_k_week, p_slow_k_week, slow_k_month, p_slow_k_month = -1, -1, -1, -1, -1, -1
self.cursor_stock.execute('select stochastic_slow_k, max(ymd) from stock_analysis where code=? group by 1 order by ymd desc', (stock_code,))
items = self.cursor_stock.fetchall()
if items is not None and len(items) > 1:
for i, item in enumerate(items):
if i == 0:
slow_k = item[0]
elif i == 1:
p_slow_k = item[0]
else:
break
self.cursor_stock.execute('select stochastic_slow_k, max(ymd) from stock_analysis_weekly where code=? group by 1 order by ymd desc', (stock_code, ))
items = self.cursor_stock.fetchall()
if items is not None and len(items)>1:
for i, item in enumerate(items):
if i == 0:
slow_k_week = item[0]
elif i == 1:
p_slow_k_week = item[0]
else:
break
self.cursor_stock.execute('select stochastic_slow_k, max(ymd) from stock_analysis_monthly where code=? group by 1 order by ymd desc', (stock_code, ))
items = self.cursor_stock.fetchall()
if items is not None and len(items)>1:
for i, item in enumerate(items):
if i == 0:
slow_k_month = item[0]
elif i == 1:
p_slow_k_month = item[0]
else:
break
if slow_k is None or p_slow_k is None:
slow_k, p_slow_k = -1, -1
if slow_k_week is None or p_slow_k_week is None:
slow_k_week, p_slow_k_week = -1, -1
if slow_k_month is None or p_slow_k_month is None:
slow_k_month, p_slow_k_month = -1, -1
return slow_k, p_slow_k, slow_k_week, p_slow_k_week, slow_k_month, p_slow_k_month
def getBuyCount(self, stock_code, valid_company, bs_buy_price, slow_k_kospi, p_slow_k_kospi, slow_k_week_kospi, p_slow_k_week_kospi, slow_k_month_kospi, p_slow_k_month_kospi):
base_price = 5000
if stock_code in valid_company:
if 0 < valid_company[stock_code] <= 250:
base_price = 20000
elif 250 < valid_company[stock_code] <= 500:
base_price = 10000
elif 500 < valid_company[stock_code] <= 1000:
base_price = 5000
elif 1000 < valid_company[stock_code] <= 1500:
base_price = 2000
else:
base_price = 1000
# kospi 상태 파악
type_kospi = {'day':1, 'week':1, 'month':1}
if slow_k_kospi < p_slow_k_kospi:
if slow_k_kospi < 20: type_kospi['day'] = 4
if 20 < slow_k_kospi < 30: type_kospi['day'] = 3
if 30 < slow_k_kospi < 40: type_kospi['day'] = 2
if slow_k_week_kospi < p_slow_k_week_kospi:
if slow_k_week_kospi < 20: type_kospi['week'] = 4
if 20 < slow_k_week_kospi < 30: type_kospi['week'] = 3
if 30 < slow_k_week_kospi < 40: type_kospi['week'] = 2
if slow_k_month_kospi < p_slow_k_month_kospi:
if slow_k_month_kospi < 20: type_kospi['month'] = 4
if 20 < slow_k_month_kospi < 30: type_kospi['month'] = 3
if 30 < slow_k_month_kospi < 40: type_kospi['month'] = 2
type_stock = {'day':1, 'week':1, 'month':1}
slow_k, p_slow_k, slow_k_week, p_slow_k_week, slow_k_month, p_slow_k_month = self.getSlowK(stock_code)
if slow_k < p_slow_k:
if slow_k < 20: type_stock['day'] = 4
if 20 < slow_k < 30: type_stock['day'] = 3
if 30 < slow_k < 40: type_stock['day'] = 2
if slow_k_week < p_slow_k_week:
if slow_k_week < 20: type_stock['week'] = 4
if 20 < slow_k_week < 30: type_stock['week'] = 3
if 30 < slow_k_week < 40: type_stock['week'] = 2
if slow_k_month < p_slow_k_month:
if slow_k_month < 20: type_stock['month'] = 4
if 20 < slow_k_month < 30: type_stock['month'] = 3
if 30 < slow_k_month < 40: type_stock['month'] = 2
if (type_kospi['day'] == 1 and type_kospi['week'] == 1 and type_kospi['month'] == 1 and
type_stock['day'] == 1 and type_stock['week'] == 1 and type_stock['month'] == 1):
return 0
max_price = math.log(type_kospi['day']*type_kospi['week']*type_kospi['month']*type_stock['day']*type_stock['week']*type_stock['month'], 1.3) * base_price
buy_count = int(math.floor(max_price / bs_buy_price))
return buy_count
def buyRealTime(self, today, n = 200):
print ("START...")
THIS_TIME = datetime.now()
slow_k_kospi, p_slow_k_kospi, slow_k_week_kospi, p_slow_k_week_kospi, slow_k_month_kospi, p_slow_k_month_kospi = self.getSlowK("^KS11")
all_stocks, valid_company = self.getCompanyInfo()
while datetime.strptime(today + " 070000", '%Y%m%d %H%M%S') < THIS_TIME < datetime.strptime(today + " 153100", '%Y%m%d %H%M%S'):
# 1515 까지만 매수를 시도한다.
if datetime.strptime(today + " 090000", '%Y%m%d %H%M%S') < THIS_TIME < datetime.strptime(today + " 151500", '%Y%m%d %H%M%S'):
# 매도를 체크한다.
self.sellStocks()
for idx, item in enumerate(all_stocks):
if THIS_TIME < datetime.strptime(today + " 090000", '%Y%m%d %H%M%S') or datetime.strptime(today + " 151500", '%Y%m%d %H%M%S') < THIS_TIME:
break
time.sleep(0.1)
stock_code = item[0]
stock_name = item[1]
if ((stock_name.lower().find('ch') >= 0 or stock_name.find('차이나') >= 0 or
stock_name.find('바이오') >= 0 or stock_name.find('제약') >= 0 or stock_name.find('약품') >= 0 or
stock_name.find('스팩') >= 0 or re.search("\d.*?호", stock_name) is not None) or
stock_code not in valid_company):
continue
print("%5d: %8s, %-50s" % (idx, stock_code, stock_name))
stock = self.stockStatus.fetchLastData(self.cursor_stock, stock_code, n)
try:
self.getRealTime_DailyCheck(today, stock_code, stock)
data = self.stockStatus.analyze(stock, self.analyzed_day)
except:
print("#ERROR:", stock_code, stock_name)
continue
# 분석일 데이터만 활용한다 (이전 데이터는 제거)
data.drop(data.index[:len(data) - self.analyzed_day], inplace=True)
bsLine, data = self.buySellChecker.checkTransactionWithEnvelope(data, stock_code, self.analyzed_day, isRealTime=False)
# 미체결 기록을 가져와서 10분 이상 된 매수 주문을 취소 한다.
ORDER_LIST = self.requestOrderList()
orderListToCancel = self.orderChecker.cancel(today, "A" + stock_code, ORDER_LIST, mins=10)
if len(orderListToCancel) > 0:
self.cancelOrderList(orderListToCancel)
# 다음 조건이면 매수한다.
if len(data) > 10 and max(bsLine['buy'][len(bsLine['buy']) - 1:]) > 1000:
if not self.orderChecker.exist(today, "A" + stock_code, hours=9):
last_index = len(bsLine['buy'])-1
if 0 < bsLine['buy'][last_index] < 200000:
bs_buy_price = bsLine['buy'][last_index]
bs_buy_weight = bsLine['buy_weight'][last_index]
buy_count = self.getBuyCount(stock_code, valid_company, bs_buy_price, slow_k_kospi, p_slow_k_kospi, slow_k_week_kospi, p_slow_k_week_kospi, slow_k_month_kospi, p_slow_k_month_kospi)
if buy_count > 0:
# 매수를 주문한다.
orderNum = self.requestOrder(OrderType.buy, stock_code, buy_count, bs_buy_price)
self.orderChecker.buy(today, "A" + stock_code, buy_count, bs_buy_price, orderNum)
# slackbot에 메시지를 보냄
self.slackBot.post_to_slack(stock_code, stock_name, "BUY", bsLine['buy'][len(bsLine['buy']) - 1], buy_count)
# 로그 출력
print("BUY", THIS_TIME.strftime('%Y%m%d %H%M%S'), orderNum, stock_code, stock_name, bs_buy_price, buy_count)
# 다음 조건이면 매도한다.
if len(data) > 10 and max(bsLine['sell'][len(bsLine['sell']) - 1:]) > 1000:
bs_sell_price = bsLine['sell'][len(bsLine['sell']) - 1]
check = self.sellStocks(stock_code, bs_sell_price)
if check:
# slackbot에 메시지를 보냄
self.slackBot.post_to_slack(stock_code, stock_name, "SELL", bsLine['sell'][len(bsLine['sell']) - 1], 'ALL')
# 로그 출력
print("SELL", THIS_TIME.strftime('%Y%m%d %H%M%S'), stock_code, stock_name, bs_sell_price)
"""
elif datetime.strptime(today + " 151530", '%Y%m%d %H%M%S') < THIS_TIME < datetime.strptime(today + " 151600", '%Y%m%d %H%M%S'):
# 3시 15분 30초부터 3시 16분 사이는 잔량을 매도한다.
####
# 손해 보지 않는 가격에 매도한다.
####
# 주문 리스트를 가져온다.
orderList = self.requestOrderList()
# 15:10:00 이후라면 모든 미체결 취소한다.
self.cancelOrderList(orderList)
for idx, item in enumerate(all_stocks):
stock_code = item[0]
stock_name = item[1]
# 매도 가격을 가져온다.
orderNum, sell_time, jango, sell_price = self.getSellingPrice(THIS_TIME, stock_code, final_price=-1, without_loss=True)
# 로그 출력
print("SELL", sell_time, stock_code, stock_name, -1, str(orderNum), jango, sell_price)
"""
time.sleep(3600)
THIS_TIME = datetime.now()
return
def updteTodayStock(self, db_filename, stock_code, today_str):
bsLine, data = self.labelChecker.makeCandidate(stock_code, today_str)
self.labelChecker.updateLabel(db_filename, stock_code, bsLine, data, today_str)
return
if __name__ == "__main__":
today = datetime.today()
PROJECT_HOME = os.getcwd()
RESOURCE_PATH = os.path.join(PROJECT_HOME, "resources")
hts = HTS_Stocks(RESOURCE_PATH)
hts.connect2DB("hts.db")
hts.connect2StockDB()
today_str = today.strftime('%Y%m%d')
hts.buyRealTime(today_str)
hts.disconnectStockDB()
hts.disconnect()
print ("done...")