Files
DeepStock/HTS_etf.py
dsyoon b07248637e init
2023-12-20 09:26:09 +09:00

477 lines
24 KiB
Python

import os
import time
import pandas as pd
import sqlite3
from datetime import datetime
from hts.HTS import HTS
from hts.OrderType import OrderType
from hts.OrderChecker import OrderChecker
from stock.util.TelegramBot import TelegramBot
from stock.analysis.StockStatus import StockStatus
from stock.analysis.Common import Common
from stock.analysis.Stochastic import Stochastic
from stock.analysis.RSI import RSI
from stock.analysis.MACD import MACD
from stock.analysis.IchimokuCloud import IchimokuCloud
from statsmodels.tsa.seasonal import seasonal_decompose
from hts.BuySellChecker import BuySellChecker
class HTS_etf(HTS):
RESOURCE_PATH = None
SELL_GAP = None
buy_count = None
orderChecker = None
buySellChecker = None
bot = None
stockStatus = None
common = None
stochastic = None
rsi = None
macd = None
ichimokuCloud = None
def __init__(self, RESOURCE_PATH):
super().__init__(RESOURCE_PATH)
self.RESOURCE_PATH = RESOURCE_PATH
self.bot = TelegramBot()
self.stockStatus = StockStatus(RESOURCE_PATH)
self.common = Common()
self.stochastic = Stochastic()
self.rsi = RSI()
self.macd = MACD()
self.ichimokuCloud = IchimokuCloud()
self.buySellChecker = BuySellChecker(self.RESOURCE_PATH)
return
def getBallance(self, stock_code):
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:
return jangoDic[code]['장부가'], jangoDic[code]['평가금액'], jangoDic[code]['평가손익']
return 0, 0, 0
def sellStocks(self, stock_code=None, stock_name=None, bs_sell_price=None, sell_count=0):
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:
if bs_sell_price is not None:
if jangoDic[code]['매도가능'] > 0:
if jangoDic[code]['평가손익'] < -0.6 or 1.0 < jangoDic[code]['평가손익']:
if sell_count == 0:
self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], bs_sell_price)
else:
self.requestOrder(OrderType.sell, code[1:], sell_count, bs_sell_price)
self.bot.sendMsg("Profit {:.2f}, {} ({})".format(jangoDic[code]['평가손익'], stock_code, stock_name))
check = True
else:
if jangoDic[code]['매도가능'] > 0:
if jangoDic[code]['평가손익'] < -0.6 or 2.0 < jangoDic[code]['평가손익']:
if sell_count == 0:
self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], jangoDic[code]['현재가'])
else:
self.requestOrder(OrderType.sell, code[1:], sell_count, jangoDic[code]['현재가'])
self.bot.sendMsg("Profit {:.2f}, {} ({})".format(jangoDic[code]['평가손익'], stock_code, stock_name))
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 jangoDic[code]['매도가능'] > 0:
if without_loss:
if jangoDic[code]['장부가'] * 0.07 < jangoDic[code]['장부가'] - final_price:
sell_price = jangoDic[code]['장부가']
if code == "A" + stock_code:
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
else:
max_price = max(jangoDic[code]['장부가'], final_price)
sell_price = (int(max_price) - int(max_price) % 5) + 5
if code == "A" + stock_code:
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 analyze(self, result):
# 기본 캔들 정보
open = result["open"]
close = result["close"]
high = result["high"]
low = result["low"]
volume = result["volume"]
if "volume_down" in result:
volume_down = result["volume_down"]
if "volume_up" in result:
volume_up = result["volume_up"]
if "volume_updown_diff" in result:
volume_updown_diff = result["volume_updown_diff"]
# 이동 평균
close_df = pd.DataFrame(close)
avg5_list = close_df.rolling(window=5).mean().fillna(close[0]).values.tolist()
avg5 = [item[0] for item in avg5_list]
avg20_list = close_df.rolling(window=20).mean().fillna(close[0]).values.tolist()
avg20 = [item[0] for item in avg20_list]
avg30_list = close_df.rolling(window=30).mean().fillna(close[0]).values.tolist()
avg30 = [item[0] for item in avg30_list]
avg60_list = close_df.rolling(window=60).mean().fillna(close[0]).values.tolist()
avg60 = [item[0] for item in avg60_list]
avg120_list = close_df.rolling(window=120).mean().fillna(close[0]).values.tolist()
avg120 = [item[0] for item in avg120_list]
avg240_list = close_df.rolling(window=240).mean().fillna(close[0]).values.tolist()
avg240 = [item[0] for item in avg240_list]
avg480_list = close_df.rolling(window=480).mean().fillna(close[0]).values.tolist()
avg480 = [item[0] for item in avg480_list]
avg1500_list = close_df.rolling(window=1500).mean().fillna(close[0]).values.tolist()
avg1500 = [item[0] for item in avg1500_list]
size = int(len(close) / 8)
pos = round(size / 2)
close_temp = close + [close[-1]] * pos
decomposition_results = seasonal_decompose(close_temp, model='multiplicative', period=size)
trend = decomposition_results.trend[:-pos]
trend_df = pd.DataFrame(trend).fillna(close[0])
trend_avg_list = trend_df.rolling(window=20).mean().values.tolist()
trend_avg = [item[0] for item in trend_avg_list]
open_df = pd.DataFrame(close)
disparity_avg5_list = (open_df / close_df.rolling(window=5).mean()).values.tolist()
disparity_avg5 = [item[0] for item in disparity_avg5_list]
disparity_avg20_list = (open_df / close_df.rolling(window=20).mean()).values.tolist()
disparity_avg20 = [item[0] for item in disparity_avg20_list]
disparity_avg30_list = (open_df / close_df.rolling(window=30).mean()).values.tolist()
disparity_avg30 = [item[0] for item in disparity_avg30_list]
disparity_avg60_list = (open_df / close_df.rolling(window=60).mean()).values.tolist()
disparity_avg60 = [item[0] for item in disparity_avg60_list]
disparity_avg120_list = (open_df / close_df.rolling(window=120).mean()).values.tolist()
disparity_avg120 = [item[0] for item in disparity_avg120_list]
disparity_avg240_list = (open_df / close_df.rolling(window=240).mean()).values.tolist()
disparity_avg240 = [item[0] for item in disparity_avg240_list]
disparity_avg480_list = (open_df / close_df.rolling(window=480).mean()).values.tolist()
disparity_avg480 = [item[0] for item in disparity_avg480_list]
disparity_avg1500_list = (open_df / close_df.rolling(window=1500).mean()).values.tolist()
disparity_avg1500 = [item[0] for item in disparity_avg1500_list]
# 볼린져 밴드
df = pd.DataFrame(close)
max20 = df.rolling(window=20).mean()
stddev20 = df.rolling(window=20).std()
upper_df = max20 + (stddev20 * 2) # 상단 볼린저 밴드
lower_df = max20 - (stddev20 * 2) # 하단 볼린저 밴드
middle_df = (upper_df + lower_df) / 2
upper_limit_df = upper_df - (upper_df - lower_df) * 0.1
lower_limit_df = (upper_df - lower_df) * 0.15 + lower_df
upper, lower, middle, upper_limit, lower_limit = [], [], [], [], []
for i in range(len(upper_df)):
if i < 10:
upper.append(upper_df.values[0][0])
lower.append(lower_df.values[0][0])
middle.append(middle_df.values[0][0])
upper_limit.append(upper_limit_df.values[0][0])
lower_limit.append(lower_limit_df.values[0][0])
else:
upper.append(upper_df.values[i][0])
lower.append(lower_df.values[i][0])
middle.append(middle_df.values[i][0])
upper_limit.append(upper_limit_df.values[i][0])
lower_limit.append(lower_limit_df.values[i][0])
upper, lower = [], []
for i in range(len(upper_df)):
if i < 10:
upper.append(upper_df.values[0][0])
lower.append(lower_df.values[0][0])
else:
upper.append(upper_df.values[i][0])
lower.append(lower_df.values[i][0])
point_temp = result["ymd"]
STOCK = []
if "volume_up" in result and "volume_updown_diff" in result:
for i in range(len(open)):
STOCK.append({'volume': volume[i], 'volume_down': volume_down[i], 'volume_up': volume_up[i], 'volume_updown_diff': volume_updown_diff[i], 'close': close[i], 'open': open[i], 'high': high[i], 'low': low[i],
'avg5': avg5[i], 'avg20': avg20[i], 'avg60': avg60[i], 'avg120': avg120[i], 'avg240': avg240[i], 'avg480': avg480[i], 'avg1500': avg1500[i]})
else:
for i in range(len(open)):
STOCK.append({'volume': volume[i], 'close': close[i], 'open': open[i], 'high': high[i], 'low': low[i],
'avg5': avg5[i], 'avg20': avg20[i], 'avg60': avg60[i], 'avg120': avg120[i], 'avg240': avg240[i], 'avg480': avg480[i], 'avg1500': avg1500[i]})
# stochastic
stochastic_df = self.stochastic.apply(STOCK, n=30, m=5, t=5)
fast_k = stochastic_df['fast_k'].values.tolist()
slow_k = stochastic_df['slow_k'].values.tolist()
slow_d = stochastic_df['slow_d'].values.tolist()
# macd
#macd_df = self.macd.apply(STOCK, short=12, long=26, t=9)
macd_df = self.macd.apply(STOCK, short=5, long=20, t=5)
macd = macd_df['macd'].values.tolist()
macds = macd_df['macds'].values.tolist()
macdo = macd_df['macdo'].values.tolist()
# rsi
rsi_df = self.rsi.apply(STOCK, period=30, window=5)
rsi = rsi_df['rsi'].values.tolist()
rsis = rsi_df['rsis'].values.tolist()
# ichimokuCloud
ichimokuCloud_df = self.ichimokuCloud.apply(STOCK, c=9, b=26, l=52)
ichimokuCloud_df = ichimokuCloud_df[:len(ichimokuCloud_df) - 51]
changeLine = ichimokuCloud_df['changeLine'].values.tolist()
baseLine = ichimokuCloud_df['baseLine'].values.tolist()
laggingSpan = ichimokuCloud_df['laggingSpan'].values.tolist()
leadingSpan1 = ichimokuCloud_df['leadingSpan1'].values.tolist()
leadingSpan2 = ichimokuCloud_df['leadingSpan2'].values.tolist()
# 결과
if "volume_up" in result and "volume_updown_diff" in result:
temp = {
"ymd": point_temp,
"open": open, "high": high, "low": low, "close": close, "volume": volume, "volume_down": volume_down,
"volume_up": volume_up, "volume_updown_diff": volume_updown_diff,
"trend": trend, "trend_avg": trend_avg,
"avg5": avg5, "avg20": avg20, "avg60": avg60, "avg120": avg120, "avg240": avg240, "avg480": avg480,
"avg1500": avg1500,
"disparity_avg5": disparity_avg5, "disparity_avg20": disparity_avg20,
"disparity_avg30": disparity_avg30, "disparity_avg60": disparity_avg60,
"disparity_avg120": disparity_avg120, "disparity_avg240": disparity_avg240,
"disparity_avg480": disparity_avg480, "disparity_avg1500": disparity_avg1500,
"upper": upper, "lower": lower, 'middle': middle, 'upper_limit': upper_limit,
'lower_limit': lower_limit,
"macd": macd, "macds": macds, "macdo": macdo,
"fast_k": fast_k, "slow_k": slow_k, "slow_d": slow_d,
"rsi": rsi, "rsis": rsis,
"changeLine": changeLine, "baseLine": baseLine, "laggingSpan": laggingSpan,
"leadingSpan1": leadingSpan1, "leadingSpan2": leadingSpan2,
}
else:
temp = {
"ymd": point_temp,
"open": open, "high": high, "low": low, "close": close, "volume": volume,
"trend": trend, "trend_avg": trend_avg,
"avg5": avg5, "avg20": avg20, "avg60": avg60, "avg120": avg120, "avg240": avg240, "avg480": avg480,
"avg1500": avg1500,
"disparity_avg5": disparity_avg5, "disparity_avg20": disparity_avg20,
"disparity_avg30": disparity_avg30, "disparity_avg60": disparity_avg60,
"disparity_avg120": disparity_avg120, "disparity_avg240": disparity_avg240,
"disparity_avg480": disparity_avg480, "disparity_avg1500": disparity_avg1500,
"upper": upper, "lower": lower, 'middle': middle, 'upper_limit': upper_limit,
'lower_limit': lower_limit,
"macd": macd, "macds": macds, "macdo": macdo,
"fast_k": fast_k, "slow_k": slow_k, "slow_d": slow_d,
"rsi": rsi, "rsis": rsis,
"changeLine": changeLine, "baseLine": baseLine, "laggingSpan": laggingSpan,
"leadingSpan1": leadingSpan1, "leadingSpan2": leadingSpan2,
}
data = pd.DataFrame(temp)
df_final_time = pd.DatetimeIndex(point_temp)
data.index = df_final_time
data = data.fillna(-1)
return data
def makeTickData(self, data, mins=30):
result = {"check": set(),
"ymd": [],
"open": [],
"close": [],
"high": [],
"low": [],
"volume": [],
"label": []}
for i in range(mins, len(data['ymd']) + 1):
result["check"].add(data['ymd'][i - 1])
result["ymd"].append(data['ymd'][i - 1])
result["open"].append(data['open'][i - mins])
result["close"].append(data['close'][i - 1])
result["high"].append(max(data['high'][i - mins: i]))
result["low"].append(min(data['low'][i - mins: i]))
result["volume"].append(sum(data['volume'][i - mins: i]))
return result
def makeTickData1(self, data, mins=5):
result = {
"ymd": [],
"open": [], "close": [], "high": [], "low": [], "volume": [], "volume_up": [], "volume_down": [], "volume_updown_diff": []
}
for i in range(mins, len(data['ymd'])+1):
result["ymd"].append(data['ymd'][i-1])
result["open"].append(data['open'][i-mins])
result["close"].append(data['close'][i-1])
result["high"].append(max(data['high'][i - mins: i]))
result["low"].append(min(data['low'][i - mins: i]))
result["volume"].append(sum(data['volume'][i - mins: i]))
up = [data['volume'][i - mins + c] for c in range(len(data['volume'][i - mins: i])) if data['open'][i - mins + c] < data['close'][i - mins + c]]
down = [data['volume'][i - mins + c] for c in range(len(data['volume'][i - mins: i])) if data['close'][i - mins + c] < data['open'][i - mins + c]]
result["volume_up"].append(sum(up))
result["volume_down"].append(sum(down))
result["volume_updown_diff"].append(sum(up) - sum(down))
return result
def makeTickData2(self, data, mins=5):
result = {
"ymd": [],
"open": [], "close": [], "high": [], "low": [], "volume": [], "volume_up": [], "volume_down": [], "volume_updown_diff": []
}
for i in range(mins, len(data['ymd'])+1, mins):
result["ymd"].append(data['ymd'][i-1])
result["open"].append(data['open'][i-mins])
result["close"].append(data['close'][i-1])
result["high"].append(max(data['high'][i - mins: i]))
result["low"].append(min(data['low'][i - mins: i]))
result["volume"].append(data['volume'][i-1])
if data['open'][i-1] < data['close'][i-1]:
result["volume_up"].append(data['volume'][i-1])
result["volume_down"].append(0)
elif data['close'][i-1] < data['open'][i-1]:
result["volume_down"].append(-1*data['volume'][i-1])
result["volume_up"].append(0)
else:
result["volume_up"].append(0)
result["volume_down"].append(0)
up = [data['volume'][i - mins + c] for c in range(len(data['volume'][i - mins: i])) if data['close'][i - mins + c] < data['open'][i - mins + c]]
down = [data['volume'][i - mins + c] for c in range(len(data['volume'][i - mins: i])) if data['close'][i - mins + c] < data['open'][i - mins + c]]
result["volume_updown_diff"].append(sum(up) - sum(down))
return result
def getDisparityLimit(self, ticker, RESOURCE_PATH):
conn = sqlite3.connect(os.path.join(RESOURCE_PATH, 'hts.db'))
cursor = conn.cursor()
cursor.execute('SELECT disparity_avg5, disparity_avg20, disparity_avg60, disparity_avg120, disparity_avg240, disparity_avg480, disparity_avg1500 FROM hts WHERE CODE=? order by ymd, hms', (ticker['stock_code'],))
disparity = {
'avg': {},
"limit_top_1": {"avg5": None, "avg20": None, "avg60": None, "avg120": None, "avg240": None, "avg480": None, "avg1500": None},
"limit_bottom_1": {"avg5": None, "avg20": None, "avg60": None, "avg120": None, "avg240": None, "avg480": None, "avg1500": None},
"limit_top_3": {"avg5": None, "avg20": None, "avg60": None, "avg120": None, "avg240": None, "avg480": None, "avg1500": None},
"limit_bottom_3": {"avg5": None, "avg20": None, "avg60": None, "avg120": None, "avg240": None, "avg480": None, "avg1500": None}
}
avg = {"avg5": [], "avg20": [], "avg60": [], "avg120": [], "avg240": [], "avg480": [], "avg1500": []}
db_result = cursor.fetchall()
for rows in db_result:
avg["avg5"].append(float(rows[0]))
avg["avg20"].append(float(rows[1]))
avg["avg60"].append(float(rows[2]))
avg["avg120"].append(float(rows[3]))
avg["avg240"].append(float(rows[4]))
avg["avg480"].append(float(rows[5]))
avg["avg1500"].append(float(rows[6]))
cursor.close()
conn.close()
disparity['avg'] = avg
disparity_1500 = sorted(list(set(avg['avg1500'])), reverse=True)
disparity_480 = sorted(list(set(avg['avg480'])), reverse=True)
disparity_240 = sorted(list(set(avg['avg240'])), reverse=True)
disparity_120 = sorted(list(set(avg['avg120'])), reverse=True)
disparity_60 = sorted(list(set(avg['avg60'])), reverse=True)
disparity_20 = sorted(list(set(avg['avg20'])), reverse=True)
disparity_5 = sorted(list(set(avg['avg5'])), reverse=True)
poses = [1, 3]
for pos in poses:
disparity['limit_top_'+str(pos)]['avg1500'] = disparity_1500[pos]
disparity['limit_bottom_'+str(pos)]['avg1500'] = disparity_1500[len(disparity_1500)-pos]
disparity['limit_top_'+str(pos)]['avg480'] = disparity_480[pos]
disparity['limit_bottom_'+str(pos)]['avg480'] = disparity_480[len(disparity_480)-pos]
disparity['limit_top_'+str(pos)]['avg240'] = disparity_240[pos]
disparity['limit_bottom_'+str(pos)]['avg240'] = disparity_240[len(disparity_240)-pos]
disparity['limit_top_'+str(pos)]['avg120'] = disparity_120[pos]
disparity['limit_bottom_'+str(pos)]['avg120'] = disparity_120[len(disparity_120)-pos]
disparity['limit_top_'+str(pos)]['avg60'] = disparity_60[pos]
disparity['limit_bottom_'+str(pos)]['avg60'] = disparity_60[len(disparity_60)-pos]
disparity['limit_top_'+str(pos)]['avg20'] = disparity_20[pos]
disparity['limit_bottom_'+str(pos)]['avg20'] = disparity_20[len(disparity_20)-pos]
disparity['limit_top_'+str(pos)]['avg5'] = disparity_5[pos]
disparity['limit_bottom_'+str(pos)]['avg5'] = disparity_5[len(disparity_5)-pos]
return disparity
def buyRealTime(self, stock, data, data_signal, MAX_BUY_PRICE, BUY_LIST):
self.orderChecker = OrderChecker(self.RESOURCE_PATH, stock['stock_code'])
# 매도를 체크한다.
check = self.sellStocks(stock_code=stock['stock_code'], stock_name=stock['stock_name'])
# jangoDic[code]['장부가'], jangoDic[code]['평가금액'], jangoDic[code]['평가손익'],
buy_avg, amount, profit = self.getBallance(stock['stock_code'])
if check or buy_avg == 0:
BUY_LIST['buy_avg'] = 0
BUY_LIST['buy_count'] = 0
BUY_LIST['buy_list'].clear()
time.sleep(0.1)
# 사야 할 시점과 팔아야 할 시점을 체크한다.
bsLine1 = self.buySellChecker.checkTransaction1(stock['stock_code'], MAX_BUY_PRICE, data, data_signal, BUY_LIST, isRealTime=True)
if 'sell_price' in bsLine1:
sell_price = bsLine1['sell_price'][-1]
sell_count = bsLine1['sell_count'][-1]
sell_type = bsLine1['sell_type'][-1]
if 0 < sell_price:
check = self.sellStocks(stock_code=stock['stock_code'], stock_name=stock['stock_name'], bs_sell_price=sell_price)
if check:
self.orderChecker.sell(datetime.today().strftime('%Y%m%d'), stock['stock_code'])
BUY_LIST['buy_avg'] = 0
BUY_LIST['buy_count'] = 0
BUY_LIST['buy_list'].clear()
self.bot.sendMsg("Profit {:.2f}, {} ({})".format(profit, stock['stock_code'], stock['stock_name']))
if 'buy_price' in bsLine1:
buy_price = bsLine1['buy_price'][-1]
buy_count = int(bsLine1['buy_count'][-1])
if buy_price > 0:
# 매수를 요청 한다.
orderNum = self.requestOrder(OrderType.buy, stock['stock_code'], buy_count, buy_price)
self.orderChecker.buy(datetime.today().strftime('%Y%m%d'), "A" + stock['stock_code'], buy_count, buy_price, orderNum)
self.bot.post(stock['stock_code'], stock['stock_name'], "[BUY] ", buy_price, buy_count, data['rsi'][-1], -1)
# 미체결 기록을 가져와서 10분 이상 된 매수 주문을 취소 한다.
ORDER_LIST = self.requestOrderList()
orderListToCancel = self.orderChecker.cancel(datetime.today().strftime('%Y%m%d'), "A" + stock['stock_code'], ORDER_LIST, mins=3)
if len(orderListToCancel) > 0:
self.cancelOrderList(orderListToCancel)
return True