import time import pandas as pd import psutil 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 stock_code = None stock_name = 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): 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]['평가손익'] < -1.0 or jangoDic[code]['장부가'] + 10 <= jangoDic[code]['현재가']: if jangoDic[code]['장부가'] + 10 <= jangoDic[code]['현재가']: # 1.5% 손해 혹은 2% 이상 시 수익 매도 self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], 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]['평가손익'] < -1.0 or jangoDic[code]['장부가'] + 10 <= jangoDic[code]['현재가']: if jangoDic[code]['장부가'] + 10 <= jangoDic[code]['현재가']: # 1.5% 손해 혹은 2% 이상 시 수익 매도 self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], jangoDic[code]['현재가']) self.bot.sendMsg("Profit {:.2f}, {} ({})".format(jangoDic[code]['평가손익'], stock_code, stock_name)) check = True else: if jangoDic[code]['매도가능'] > 0: #if jangoDic[code]['평가손익'] < -1.0 or jangoDic[code]['장부가'] + 10 <= jangoDic[code]['현재가']: if jangoDic[code]['장부가'] + 10 <= jangoDic[code]['현재가']: # 1.5% 손해 혹은 2% 이상 시 수익 매도 self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], jangoDic[code]['현재가']) self.bot.sendMsg("Profit {:.2f}, {} ({})".format(jangoDic[code]['평가손익'], stock_code, stock_name)) check = True return check """ def sellStocks(self, stock_code=None, stock_name=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: if bs_sell_price is not None: if jangoDic[code]['매도가능'] > 0: if jangoDic[code]['평가손익'] < -1.0 or 1.5 < jangoDic[code]['평가손익'] or self.SELL_GAP < jangoDic[code]['평가금액']-jangoDic[code]['매입금액']: # 1.5% 손해 혹은 2% 이상 시 수익 매도 self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], 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]['평가손익'] < -1.0 or 1.5 < jangoDic[code]['평가손익'] or self.SELL_GAP < jangoDic[code]['평가금액']-jangoDic[code]['매입금액']: # 1.5% 손해 혹은 2% 이상 시 수익 매도 self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], jangoDic[code]['현재가']) self.bot.sendMsg("Profit {:.2f}, {} ({})".format(jangoDic[code]['평가손익'], stock_code, stock_name)) check = True else: if jangoDic[code]['매도가능'] > 0: if jangoDic[code]['평가손익'] < -1.0 or 1.5 < jangoDic[code]['평가손익'] or self.SELL_GAP < jangoDic[code]['평가금액']-jangoDic[code]['매입금액']: # 1.5% 손해 혹은 2% 이상 시 수익 매도 self.requestOrder(OrderType.sell, code[1:], jangoDic[code]['매도가능'], 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 buyRealTime(self, stocks, today, MAX_PRICE=30000): #self.orderChecker = OrderChecker(self.RESOURCE_PATH, stock_code) BUY_LIST = { '122630': {'buy_count': 0, 'buy_avg': 0, 'buy_list': []}, '233740': {'buy_count': 0, 'buy_avg': 0, 'buy_list': []}, '251340': {'buy_count': 0, 'buy_avg': 0, 'buy_list': []}, '252670': {'buy_count': 0, 'buy_avg': 0, 'buy_list': []} } print("START...") THIS_TIME = datetime.now() #LAST_DATA = self.getLastData(stock_code, today) isFirst = 0 while datetime.strptime(today + " 060000", '%Y%m%d %H%M%S') < THIS_TIME < datetime.strptime(today + " 153100",'%Y%m%d %H%M%S'): if datetime.strptime(today + " 090100", '%Y%m%d %H%M%S') < THIS_TIME < datetime.strptime(today + " 151500", '%Y%m%d %H%M%S'): for stock in stocks: stock_code = stock['stock_code'] stock_name = stock['stock_name'] # 매도를 체크한다. check = self.sellStocks(stock_code, stock_name) # jangoDic[code]['장부가'], jangoDic[code]['평가금액'], jangoDic[code]['평가손익'], buy_avg, amount, profit = self.getBallance(stock_code) if check or buy_avg == 0: BUY_LIST[stock_code]['buy_avg'] = 0 BUY_LIST[stock_code]['buy_count'] = 0 BUY_LIST[stock_code]['buy_list'].clear() if isFirst < 4: if 0 < buy_avg: BUY_LIST[stock_code]['buy_avg'] = buy_avg if BUY_LIST is not None and len(BUY_LIST[stock_code]['buy_list']) == 0: BUY_LIST[stock_code]['buy_list'].append({'buy_ymd': datetime.now(), 'buy_price': buy_avg, 'buy_count': 0, 'buy_cut': 0, 'buy_type': ''}) self.bot.sendMsg("START... {} ({}) MAX_PRICE: {}".format(stock_code, stock_name, MAX_PRICE)) isFirst += 1 time.sleep(0.1) #result_m1 = self.getRealTime(stock_code, today, LAST_DATA) result_m1 =self.getRealTime(stock_code, today) result_tic_m1 = self.makeTickData1(result_m1, mins=1) data = self.analyze(result_tic_m1) result_tic_m30 = self.makeTickData2(result_tic_m1, mins=30) data_signal = self.analyze(result_tic_m30) #data.drop(data.index[:len(data) - analyzed_day], inplace=True) # 사야 할 시점과 팔아야 할 시점을 체크한다. bsLine1 = self.buySellChecker.checkTransaction1(stock_code, MAX_PRICE, data, data_signal, BUY_LIST[stock_code], isRealTime=True) if 'sell_price' in bsLine1: sell_price = bsLine1['sell_price'][-1] if 0 < sell_price: profit_rate = 1.002 if buy_avg * profit_rate < data['close'][-1]: check = self.sellStocks(stock_code, sell_price) if check: #self.orderChecker.sell(datetime.today().strftime('%Y%m%d'), stock_code) BUY_LIST[stock_code]['buy_avg'] = 0 BUY_LIST[stock_code]['buy_count'] = 0 BUY_LIST[stock_code]['buy_list'].clear() self.bot.sendMsg( "Profit {:.2f}, {} ({})".format(profit, stock_code, 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_code, buy_count, buy_price) #self.orderChecker.buy(today, "A" + stock_code, buy_count, buy_price, orderNum) #self.orderChecker.buy(datetime.today().strftime('%Y%m%d'), stock_code, buy_count, buy_price) self.bot.post(stock_code, stock_name, "[BUY] ", buy_price, buy_count, data['rsi'][-1], -1) """ # 미체결 기록을 가져와서 10분 이상 된 매수 주문을 취소 한다. ORDER_LIST = self.requestOrderList() orderListToCancel = self.orderChecker.cancel(today, "A" + stock_code, ORDER_LIST, mins=3) if len(orderListToCancel) > 0: self.cancelOrderList(orderListToCancel) """ if ((int(THIS_TIME.strftime("%M")) == 4) and 0 <= int(THIS_TIME.strftime("%S")) <= 59): #self.bot.alarm_live(stock_code, stock_name) vm = psutil.virtual_memory() vm_item = dict() vm_item['free'] = vm.available // (1024 * 1024) vm_item['idle'] = vm.available / vm.total * 100 self.bot.sendMsg("Alive... {} ({}) avg: {:.2f}, close: {:.2f}, mem: {:.1f}".format(stock_code, stock_name, buy_avg, data['close'][-1], vm_item['idle'])) time.sleep(60) THIS_TIME = datetime.now() return True