from binance_f import RequestClient from binance_f.constant.test import * from binance_f.base.printobject import * from binance_f.model.constant import * request_client = RequestClient(api_key=g_api_key, secret_key=g_secret_key) result = request_client.get_balance() PrintMix.print_data(result)
class BinanceInterface(ExchangeInterface): def __init__(self, settings, logger, on_tick_callback=None): super().__init__(settings, logger, on_tick_callback) self.symbol = settings.SYMBOL self.client = RequestClient(api_key=settings.API_KEY, secret_key=settings.API_SECRET) self.ws = SubscriptionClient(api_key=settings.API_KEY, secret_key=settings.API_SECRET) self.orders = {} self.positions = {} self.candles: List[Candlestick] = [] self.last = 0 self.open = False self.listen_key = "" self.init() def init(self): self.logger.info("loading market data. this may take a moment") self.initOrders() self.initPositions() self.logger.info("got all data. subscribing to live updates.") self.listen_key = self.client.start_user_data_stream() self.ws.subscribe_user_data_event(self.listen_key, self.callback, self.error) subbarsIntervall = CandlestickInterval.MIN1 if self.settings.MINUTES_PER_BAR <= 60 else CandlestickInterval.HOUR1 self.ws.subscribe_candlestick_event(self.symbol, subbarsIntervall, self.callback, self.error) self.open = True self.lastUserDataKeep = time.time() self.logger.info("ready to go") def callback(self, data_type: 'SubscribeMessageType', event: 'any'): gotTick = False # refresh userdata every 15 min if self.lastUserDataKeep < time.time() - 15 * 60: self.lastUserDataKeep = time.time() self.client.keep_user_data_stream() # TODO: implement! (update bars, orders and account) if data_type == SubscribeMessageType.RESPONSE: pass # what to do herE? elif data_type == SubscribeMessageType.PAYLOAD: if event.eventType == "kline": # {'eventType': 'kline', 'eventTime': 1587064627164, 'symbol': 'BTCUSDT', # 'data': <binance_f.model.candlestickevent.Candlestick object at 0x0000016B89856760>} if event.symbol == self.symbol: candle: Candlestick = event.data if len(self.candles) > 0: if candle.startTime <= self.candles[ 0].startTime and candle.startTime > self.candles[ -1].startTime: # somewhere inbetween to replace for idx in range(0, len(self.candles)): if candle.startTime == self.candles[ idx].startTime: self.candles[idx] = candle break elif candle.startTime > self.candles[0].startTime: self.candles.insert(0, candle) gotTick = True else: self.candles.append(candle) gotTick = True elif (event.eventType == "ACCOUNT_UPDATE"): # {'eventType': 'ACCOUNT_UPDATE', 'eventTime': 1587063874367, 'transactionTime': 1587063874365, # 'balances': [<binance_f.model.accountupdate.Balance object at 0x000001FAF470E100>,...], # 'positions': [<binance_f.model.accountupdate.Position object at 0x000001FAF470E1C0>...]} usdBalance = 0 for b in event.balances: bal: Balance = b if bal.asset == "USDT": usdBalance = bal.walletBalance for p in event.positions: pos: Position = p if pos.symbol not in self.positions.keys(): self.positions[pos.symbol] = AccountPosition( pos.symbol, avgEntryPrice=float(pos.entryPrice), quantity=float(pos.amount), walletBalance=usdBalance if "USDT" in pos.symbol else 0) else: accountPos = self.positions[pos.symbol] accountPos.quantity = float(pos.amount) accountPos.avgEntryPrice = float(pos.entryPrice) if "USDT" in pos.symbol: accountPos.walletBalance = usdBalance elif (event.eventType == "ORDER_TRADE_UPDATE"): # {'eventType': 'ORDER_TRADE_UPDATE', 'eventTime': 1587063513592, 'transactionTime': 1587063513589, # 'symbol': 'BTCUSDT', 'clientOrderId': 'web_ybDNrTjCi765K3AvOMRK', 'side': 'BUY', 'type': 'LIMIT', # 'timeInForce': 'GTC', 'origQty': 0.01, 'price': 6901.0, 'avgPrice': 0.0, 'stopPrice': 0.0, # 'executionType': 'NEW', 'orderStatus': 'NEW', 'orderId': 2705199704, 'lastFilledQty': 0.0, # 'cumulativeFilledQty': 0.0, 'lastFilledPrice': 0.0, 'commissionAsset': None, 'commissionAmount': None, # 'orderTradeTime': 1587063513589, 'tradeID': 0, 'bidsNotional': 138.81, 'asksNotional': 0.0, # 'isMarkerSide': False, 'isReduceOnly': False, 'workingType': 'CONTRACT_PRICE'} sideMulti = 1 if event.side == 'BUY' else -1 order: Order = Order(orderId=event.clientOrderId, stop=event.stopPrice, limit=event.price, amount=event.origQty * sideMulti) order.exchange_id = event.orderId #FIXME: how do i know stop triggered on binance? #order.stop_triggered = order.executed_amount = event.cumulativeFilledQty * sideMulti order.executed_price = event.avgPrice order.tstamp = event.transactionTime order.execution_tstamp = event.orderTradeTime prev: Order = self.orders[ order. exchange_id] if order.exchange_id in self.orders.keys( ) else None if prev is not None: if prev.tstamp > order.tstamp or abs( prev.executed_amount) > abs(order.executed_amount): # already got newer information, probably the info of the stop order getting # triggered, when i already got the info about execution self.logger.info("ignoring delayed update for %s " % (prev.id)) if order.stop_price is None: order.stop_price = prev.stop_price if order.limit_price is None: order.limit_price = prev.limit_price prev = order if not prev.active and prev.execution_tstamp == 0: prev.execution_tstamp = datetime.utcnow().timestamp() self.orders[order.exchange_id] = prev self.logger.info("received order update: %s" % (str(order))) else: self.logger.warn("Unknown Data in websocket callback") if gotTick and self.on_tick_callback is not None: self.on_tick_callback() # got something new def error(self, e: 'BinanceApiException'): self.exit() self.logger.error(e.error_code + e.error_message) def initOrders(self): apiOrders = self.client.get_open_orders() for o in apiOrders: order = self.convertOrder(o) if order.active: self.orders[order.exchange_id] = order def convertOrder(self, apiOrder: binance_f.model.Order) -> Order: direction = 1 if apiOrder.side == OrderSide.BUY else -1 order = Order(orderId=apiOrder.clientOrderId, amount=apiOrder.origQty * direction, limit=apiOrder.price, stop=apiOrder.stopPrice) order.executed_amount = apiOrder.executedQty * direction order.executed_price = apiOrder.avgPrice order.active = apiOrder.status in ["NEW", "PARTIALLY_FILLED"] order.exchange_id = apiOrder.orderId return order def initPositions(self): balance = self.client.get_balance() usdBalance = 0 for bal in balance: if bal.asset == "USDT": usdBalance = bal.balance api_positions = self.client.get_position() self.positions[self.symbol] = AccountPosition(self.symbol, 0, 0, 0) if api_positions is not None: for pos in api_positions: self.positions[pos.symbol] = AccountPosition( pos.symbol, avgEntryPrice=pos.entryPrice, quantity=pos.positionAmt, walletBalance=usdBalance if "USDT" in pos.symbol else 0) self.logger.info("starting with %.2f in wallet and pos %.2f @ %.2f" % (self.positions[self.symbol].walletBalance, self.positions[self.symbol].quantity, self.positions[self.symbol].avgEntryPrice)) def exit(self): self.ws.unsubscribe_all() self.open = False def internal_cancel_order(self, order: Order): if order.exchange_id in self.orders.keys(): self.orders[order.exchange_id].active = False self.client.cancel_order(symbol=self.symbol, origClientOrderId=order.id) def internal_send_order(self, order: Order): order_type = OrderType.MARKET if order.limit_price is not None: if order.stop_price is not None: order_type = OrderType.STOP else: order_type = OrderType.LIMIT else: order_type = OrderType.STOP_MARKET resultOrder: binance_f.model.Order = self.client.post_order( symbol=self.symbol, side=OrderSide.BUY if order.amount > 0 else OrderSide.SELL, ordertype=order_type, timeInForce=TimeInForce.GTC, quantity=abs(order.amount), price=order.limit_price, stopPrice=order.stop_price, newClientOrderId=order.id) order.exchange_id = resultOrder.orderId def internal_update_order(self, order: Order): self.cancel_order(order) # stupid binance can't update orders self.send_order(order) def get_orders(self) -> List[Order]: return list(self.orders.values()) def get_bars(self, timeframe_minutes, start_offset_minutes) -> List[Bar]: tf = CandlestickInterval.MIN1 if timeframe_minutes <= 60 else CandlestickInterval.HOUR1 bars = self.client.get_candlestick_data(symbol=self.symbol, interval=tf, limit=1000) return self._aggregate_bars(reversed(bars), timeframe_minutes, start_offset_minutes) def recent_bars(self, timeframe_minutes, start_offset_minutes) -> List[Bar]: return self._aggregate_bars(self.bars, timeframe_minutes, start_offset_minutes) def _aggregate_bars(self, bars, timeframe_minutes, start_offset_minutes) -> List[Bar]: subbars = [] for b in bars: subbars.append(self.convertBar(b)) return process_low_tf_bars(subbars, timeframe_minutes, start_offset_minutes) def convertBar(self, apiBar: binance_f.model.candlestick.Candlestick): return Bar(tstamp=apiBar.openTime / 1000, open=apiBar.open, high=apiBar.high, low=apiBar.low, close=apiBar.close, volume=apiBar.volume) def get_instrument(self, symbol=None): if symbol is None: symbol = self.symbol instr: binance_f.model.exchangeinformation.ExchangeInformation = self.client.get_exchange_information( ) for symb in instr.symbols: if symb.symbol == symbol: baseLength = len(symb.baseAsset) lotSize = 0 tickSize = 0 for filter in symb.filters: if filter['filterType'] == 'LOT_SIZE': lotSize = filter['stepSize'] if filter['filterType'] == 'PRICE_FILTER': tickSize = filter['tickSize'] return Symbol( symbol=symb.symbol, isInverse=symb.baseAsset != symb.symbol[:baseLength], lotSize=lotSize, tickSize=tickSize, makerFee=0.02, takerFee=0.04) return None def get_position(self, symbol=None): if symbol is None: symbol = self.symbol return self.positions[symbol] if symbol in self.positions.keys( ) else None def is_open(self): return self.open def check_market_open(self): return self.open # TODO: is that the best we can do? def update_account(self, account: Account): pass
class Binance: def __init__(self): self.client = RequestClient(api_key=BINANCE_API_KEY, secret_key=BINANCE_SECRET_KEY) def get_wallet_value(self) -> float: res = self.client.get_balance() for e in res: if e.asset == 'USDT': return e.withdrawAvailable return 0 def buy(self): print("creating order") market_price = self.get_mark_price() quantity = calc_percentage(TOTAL_USDT_INVESTMENT_PERCENTAGE, self.get_wallet_value()) / market_price self.client.change_initial_leverage(SYMBOL, LEVERAGE) try: self.client.change_margin_type( SYMBOL, FuturesMarginType.CROSSED if MARGIN_TYPE == 'CROSS' else FuturesMarginType.ISOLATED) except Exception: pass order = self.client.post_order(symbol=SYMBOL, side=OrderSide.BUY, ordertype=OrderType.MARKET, positionSide=PositionSide.BOTH, quantity=int(quantity)) print( f"order created successfully: market_price: {market_price}, quantity: {quantity}" ) print(f"{PrintBasic.print_obj(order)}") if STOP_LOSS_PERCENTAGE is not None: # STOP LOSS stop_price = market_price - calc_percentage( market_price, int(STOP_LOSS_PERCENTAGE)) try: self.stop_loss(price=float("%.4f" % stop_price)) print( f"stop loss order created successfully: stop_price: {stop_price}" ) except Exception: print( f"something went wrong, could not create stop loss order") if TAKE_PROFIT_PERCENTAGE is not None: # TAKE PROFIT take_profit = market_price + calc_percentage( market_price, int(TAKE_PROFIT_PERCENTAGE)) try: self.take_profit(price=float("%.4f" % take_profit)) print( f"take profit order created successfully: stop_price: {take_profit}" ) except Exception: print( f"something went wrong, could not create take profit order" ) def stop_loss(self, price: float): res = self.client.post_order(symbol=SYMBOL, stopPrice=price, side=OrderSide.SELL, ordertype=OrderType.STOP_MARKET, positionSide=PositionSide.BOTH, closePosition=True, newOrderRespType=OrderRespType.RESULT) print(f"{PrintBasic.print_obj(res)}") return res def take_profit(self, price: float): res = self.client.post_order(symbol=SYMBOL, stopPrice=price, side=OrderSide.SELL, ordertype=OrderType.TAKE_PROFIT_MARKET, positionSide=PositionSide.BOTH, closePosition=True, newOrderRespType=OrderRespType.RESULT) print(f"{PrintBasic.print_obj(res)}") return res def get_mark_price(self, symbol: str = SYMBOL) -> float: return self.client.get_mark_price(symbol).markPrice
class BinanceInterface(ExchangeInterface): def __init__(self, settings, logger, on_tick_callback=None): super().__init__(settings, logger, on_tick_callback) self.symbol: str = settings.SYMBOL self.client = RequestClient(api_key=settings.API_KEY, secret_key=settings.API_SECRET, url="https://fapi.binance.com") self.ws = BinanceWebsocket(wsURL="wss://fstream.binance.com/ws", api_key=settings.API_KEY, api_secret=settings.API_SECRET, logger=logger, callback=self.callback) # for binance the key is the internal id (not the exchange id) cause we can't update order but have to cancel # and reopen with same id. that leads to different exchange id, but we need to know its the same. self.orders = {} self.positions = {} self.symbol_object: Symbol = None self.candles: List[Candlestick] = [] self.last = 0 self.listen_key = "" self.lastUserDataKeep = None self.wantedResponses = 0 # needed to wait for realtime if self.is_open(): self.init() def init(self): self.logger.info("loading market data. this may take a moment") self.initOrders() self.initPositions() self.symbol_object = self.get_instrument() self.logger.info("got all data. subscribing to live updates.") self.listen_key = self.client.start_user_data_stream() self.lastUserDataKeep = time.time() self.wantedResponses = 2 subInt = CandlestickInterval.MIN1 if self.settings.MINUTES_PER_BAR <= 60 else CandlestickInterval.HOUR1 self.ws.subscribe_candlestick_event(self.symbol.lower(), subInt) self.ws.subscribe_user_data_event(self.listen_key) waitingTime = 0 while self.wantedResponses > 0 and waitingTime < 100: waitingTime += 1 time.sleep(0.1) if self.wantedResponses > 0: self.logger.error("got no response to subscription. outa here") self.ws.exit() else: self.logger.info("ready to go") def callback(self, data_type: 'SubscribeMessageType', event: 'any'): gotTick = False fromAccount = False # refresh userdata every 5 min if self.lastUserDataKeep < time.time() - 5 * 60: self.lastUserDataKeep = time.time() self.client.keep_user_data_stream() if data_type == SubscribeMessageType.RESPONSE: self.wantedResponses -= 1 # tell the waiting init that we got it. otherwise we might be too fast elif data_type == SubscribeMessageType.PAYLOAD: if event.eventType == "kline": # {'eventType': 'kline', 'eventTime': 1587064627164, 'symbol': 'BTCUSDT', # 'data': <binance_f.model.candlestickevent.Candlestick object at 0x0000016B89856760>} if event.symbol == self.symbol: candle: Candlestick = event.data if len(self.candles) > 0: if self.candles[ 0].startTime >= candle.startTime > self.candles[ -1].startTime: # somewhere inbetween to replace for idx in range(0, len(self.candles)): if candle.startTime == self.candles[ idx].startTime: self.candles[idx] = candle break elif candle.startTime > self.candles[0].startTime: self.candles.insert(0, candle) gotTick = True else: self.candles.append(candle) gotTick = True self.last = self.candles[0].close elif event.eventType == "ACCOUNT_UPDATE": # {'eventType': 'ACCOUNT_UPDATE', 'eventTime': 1587063874367, 'transactionTime': 1587063874365, # 'balances': [<binance_f.model.accountupdate.Balance object at 0x000001FAF470E100>,...], # 'positions': [<binance_f.model.accountupdate.Position object at 0x000001FAF470E1C0>...]} usdBalance = 0 gotTick = True fromAccount = True for b in event.balances: bal: Balance = b if bal.asset == "USDT": usdBalance = bal.walletBalance for p in event.positions: pos: Position = p if pos.symbol not in self.positions.keys(): self.positions[pos.symbol] = AccountPosition( symbol=pos.symbol, avgEntryPrice=float(pos.entryPrice), quantity=float(pos.amount), walletBalance=usdBalance if "USDT" in pos.symbol else 0) else: accountPos = self.positions[pos.symbol] accountPos.quantity = float(pos.amount) accountPos.avgEntryPrice = float(pos.entryPrice) if "USDT" in pos.symbol: accountPos.walletBalance = usdBalance elif event.eventType == "ORDER_TRADE_UPDATE": # {'eventType': 'ORDER_TRADE_UPDATE', 'eventTime': 1587063513592, 'transactionTime': 1587063513589, # 'symbol': 'BTCUSDT', 'clientOrderId': 'web_ybDNrTjCi765K3AvOMRK', 'side': 'BUY', 'type': 'LIMIT', # 'timeInForce': 'GTC', 'origQty': 0.01, 'price': 6901.0, 'avgPrice': 0.0, 'stopPrice': 0.0, # 'executionType': 'NEW', 'orderStatus': 'NEW', 'orderId': 2705199704, 'lastFilledQty': 0.0, # 'cumulativeFilledQty': 0.0, 'lastFilledPrice': 0.0, 'commissionAsset': None, 'commissionAmount': None, # 'orderTradeTime': 1587063513589, 'tradeID': 0, 'bidsNotional': 138.81, 'asksNotional': 0.0, # 'isMarkerSide': False, 'isReduceOnly': False, 'workingType': 'CONTRACT_PRICE'} gotTick = True fromAccount = True sideMulti = 1 if event.side == 'BUY' else -1 order: Order = Order( orderId=event.clientOrderId, stop=event.stopPrice if event.stopPrice > 0 else None, limit=event.price if event.price > 0 else None, amount=event.origQty * sideMulti) order.exchange_id = event.orderId # trigger of a stoplimit in Binance means "update for order -> expired" then "update -> as limit" order.stop_triggered = event.type == "LIMIT" and event.stopPrice > 0 order.executed_amount = event.cumulativeFilledQty * sideMulti order.executed_price = event.avgPrice order.tstamp = event.transactionTime order.execution_tstamp = event.orderTradeTime / 1000 order.active = event.orderStatus in ["NEW", "PARTIALLY_FILLED"] prev: Order = self.orders[ order.id] if order.id in self.orders.keys() else None if prev is not None: if prev.tstamp > order.tstamp or abs( prev.executed_amount) > abs(order.executed_amount): # already got newer information, probably the info of the stop order getting # triggered, when i already got the info about execution self.logger.info("ignoring delayed update for %s " % prev.id) if order.stop_price is None: order.stop_price = prev.stop_price if order.limit_price is None: order.limit_price = prev.limit_price prev = order if not prev.active and prev.execution_tstamp == 0: prev.execution_tstamp = datetime.utcnow().timestamp() self.orders[order.id] = prev self.logger.info("received order update: %s" % (str(order))) else: self.logger.warn("Unknown Data in websocket callback") if gotTick and self.on_tick_callback is not None: self.on_tick_callback( fromAccountAction=fromAccount) # got something new def initOrders(self): apiOrders = self.client.get_open_orders() for o in apiOrders: order = self.convertOrder(o) if order.active: self.orders[order.id] = order def resyncOrders(self): self.orders = {} self.initOrders() @staticmethod def convertOrder(apiOrder: binance_f.model.Order) -> Order: direction = 1 if apiOrder.side == OrderSide.BUY else -1 order = Order( orderId=apiOrder.clientOrderId, amount=apiOrder.origQty * direction, limit=apiOrder.price if apiOrder.price > 0 else None, stop=apiOrder.stopPrice if apiOrder.stopPrice > 0 else None) order.executed_amount = apiOrder.executedQty * direction order.executed_price = apiOrder.avgPrice order.active = apiOrder.status in ["NEW", "PARTIALLY_FILLED"] order.exchange_id = apiOrder.orderId return order def initPositions(self): balance = self.client.get_balance() usdBalance = 0 for bal in balance: if bal.asset == "USDT": usdBalance = bal.balance api_positions = self.client.get_position() self.positions[self.symbol] = AccountPosition( self.symbol, avgEntryPrice=0, quantity=0, walletBalance=usdBalance if "USDT" in self.symbol else 0) if api_positions is not None: for pos in api_positions: self.positions[pos.symbol] = AccountPosition( pos.symbol, avgEntryPrice=pos.entryPrice, quantity=pos.positionAmt, walletBalance=usdBalance if "USDT" in pos.symbol else 0) self.logger.info("starting with %.2f in wallet and pos %.2f @ %.2f" % (self.positions[self.symbol].walletBalance, self.positions[self.symbol].quantity, self.positions[self.symbol].avgEntryPrice)) def exit(self): self.ws.exit() self.client.close_user_data_stream() def internal_cancel_order(self, order: Order): if order.id in self.orders.keys(): self.orders[order.id].active = False self.client.cancel_order(symbol=self.symbol, origClientOrderId=order.id) def internal_send_order(self, order: Order): if order.stop_price is not None and ( self.last - order.stop_price) * order.amount >= 0: order.stop_price = None # already triggered if order.limit_price is not None: order.limit_price = round(order.limit_price, self.symbol_object.pricePrecision) if order.stop_price is not None: order.stop_price = round(order.stop_price, self.symbol_object.pricePrecision) order_type = OrderType.STOP else: order_type = OrderType.LIMIT elif order.stop_price is not None: order.stop_price = round(order.stop_price, self.symbol_object.pricePrecision) order_type = OrderType.STOP_MARKET else: order_type = OrderType.MARKET order.amount = round(order.amount, self.symbol_object.quantityPrecision) quantityFormat = "{:." + str( self.symbol_object.quantityPrecision) + "f}" priceFormat = "{:." + str(self.symbol_object.pricePrecision) + "f}" # yes have to send the price and quantity in as str (although it wants float) cause otherwise it converts it # inernally and that sometimes f**k up the precision (0.023 -> 0.02299999999) resultOrder: binance_f.model.Order = self.client.post_order( symbol=self.symbol, side=OrderSide.BUY if order.amount > 0 else OrderSide.SELL, ordertype=order_type, timeInForce=TimeInForce.GTC if order_type in [OrderType.LIMIT, OrderType.STOP] else None, quantity=quantityFormat.format(abs(order.amount)), price=priceFormat.format(order.limit_price) if order.limit_price is not None else None, stopPrice=priceFormat.format(order.stop_price) if order.stop_price is not None else None, newClientOrderId=order.id) order.exchange_id = resultOrder.orderId def internal_update_order(self, order: Order): self.cancel_order(order) # stupid binance can't update orders self.on_tick_callback(True) # triggers a reset of the tick-delay. # otherwise we risk a tick to be calced after the cancel, before the new order self.send_order(order) def get_orders(self) -> List[Order]: return list(self.orders.values()) def get_bars(self, timeframe_minutes, start_offset_minutes) -> List[Bar]: tf = CandlestickInterval.MIN1 if timeframe_minutes <= 60 else CandlestickInterval.HOUR1 bars = self.client.get_candlestick_data(symbol=self.symbol, interval=tf, limit=1000) subbars = [] for b in reversed(bars): subbars.append(self.convertBar(b)) return process_low_tf_bars(subbars, timeframe_minutes, start_offset_minutes) def recent_bars(self, timeframe_minutes, start_offset_minutes) -> List[Bar]: subbars = [] for b in self.candles: subbars.append(self.convertBarevent(b)) return process_low_tf_bars(subbars, timeframe_minutes, start_offset_minutes) @staticmethod def convertBar(apiBar: binance_f.model.candlestick.Candlestick): return Bar(tstamp=apiBar.openTime / 1000, open=float(apiBar.open), high=float(apiBar.high), low=float(apiBar.low), close=float(apiBar.close), volume=float(apiBar.volume)) @staticmethod def convertBarevent(apiBar: binance_f.model.candlestickevent.Candlestick): return Bar(tstamp=apiBar.startTime / 1000, open=float(apiBar.open), high=float(apiBar.high), low=float(apiBar.low), close=float(apiBar.close), volume=float(apiBar.volume)) @staticmethod def barArrayToBar(b): return Bar(tstamp=b[0] / 1000, open=float(b[1]), high=float(b[2]), low=float(b[3]), close=float(b[4]), volume=float(b[5])) def get_instrument(self, symbol=None): if symbol is None: symbol = self.symbol instr: binance_f.model.exchangeinformation.ExchangeInformation = self.client.get_exchange_information( ) for symb in instr.symbols: if symb.symbol == symbol: baseLength = len(symb.baseAsset) lotSize = 1 tickSize = 1 for filterIt in symb.filters: if filterIt['filterType'] == 'LOT_SIZE': lotSize = float(filterIt['stepSize']) if filterIt['filterType'] == 'PRICE_FILTER': tickSize = float(filterIt['tickSize']) return Symbol( symbol=symb.symbol, isInverse=symb.baseAsset != symb.symbol[:baseLength], lotSize=lotSize, tickSize=tickSize, makerFee=0.02, takerFee=0.04, pricePrecision=symb.pricePrecision, quantityPrecision=symb.quantityPrecision) return None def get_position(self, symbol=None): if symbol is None: symbol = self.symbol return self.positions[symbol] if symbol in self.positions.keys( ) else None def is_open(self): return not self.ws.exited def check_market_open(self): return self.is_open() def update_account(self, account: Account): pos = self.positions[self.symbol] account.open_position = pos account.equity = pos.walletBalance account.usd_equity = account.equity
def get_balanceAvailableUSDT(): request_client = RequestClient(api_key=g_api_key, secret_key=g_secret_key) result = request_client.get_balance() acc_df = pd.DataFrame([acc.__dict__ for acc in result]) return acc_df.withdrawAvailable[0]
def get_balance(): request_client = RequestClient(api_key=g_api_key, secret_key=g_secret_key) result = request_client.get_balance() # PrintMix.print_data(result) return result
class Main(QMainWindow, form_class) : def __init__(self) : super().__init__() self.setupUi(self) self.api_key = keys() self.request_client = RequestClient(api_key=self.api_key.api_key(), secret_key=self.api_key.secret_key()) # self.File_class = File_class('2020_05') self.init_vars() self.init_status_bools() self.init_signals_bools() self.init_update_threads() self.init_trade_threads() def init_vars(self): self.my_money = self.get_balance() self.now_price = 0 self.macd_hist = 0 self.macd_hist_prev = 0 self.macd_12 = 0 self.macd_26 = 0 self.RSI = 0 self.ATR_band_15_Top = 0 self.ATR_band_15_bottom = 0 self.moving_average_15m_7 = 0 self.moving_average_15m_20 = 0 self.candleStickArrFor1m = [] self.candleStickArrFor1m_NP = [] self.moving_average_15m_7_constant = 0.002 self.RSI_is_it_below_X_constant = 35 self.RSI_is_it_above_X_constant = 65 def init_status_bools(self): self.MACD_is_it_above_X = False self.MACD_is_it_below_X = False self.macd_golden_bool = False self.macd_dead_bool = False self.RSI_is_it_below_X = False self.RSI_is_it_above_X = False self.ATR_band_rising = False self.ATR_band_falling = False self.touching_15m_20ma = False def init_signals_bools(self): self.macd_enter_long_signal =False self.macd_enter_short_signal = False self.macd_exit_short_signal = False self.macd_exit_long_signal = False self.ATR_long_signal = False self.ATR_short_signal = False self.touching_15m_7ma_signal = False self.Short_liquidate_signal = False self.Long_liquidate_signal = False def init_trade_threads(self): threading.Timer(0.5, self.no_position_thread).start() def init_update_threads(self): threading.Timer(0.01, self.update_candlestickArrFor1m_per1s_thr).start() threading.Timer(0.01, self.update_candlestickArrFor15m_per1s_thr).start() threading.Timer(5, self.update_MACDhist_thr).start() threading.Timer(5, self.update_RSI_thr).start() threading.Timer(5, self.update_ATR_thr).start() threading.Timer(5, self.update_touching_15m_20ma).start() threading.Timer(5, self.checking_RSI_thr).start() threading.Timer(2, self.check_bools_and_update_signal_thr).start() threading.Timer(2, self.update_UI_thr).start() #self.main_thr() def update_candlestickArrFor15m_per1s_thr(self): while True: try: tmp_Arr = self.request_client.get_candlestick_data(symbol="BTCUSDT", interval=CandlestickInterval.MIN15, startTime=None, endTime=self.request_client.get_servertime(), limit=50) self.newCandleStickArr_15m = tmp_Arr except Exception as e: print(f'에러 : {e}') time.sleep(3) def update_candlestickArrFor1m_per1s_thr(self): while True: try: self.candleStickArrFor1m = self.request_client.get_candlestick_data(symbol="BTCUSDT", interval=CandlestickInterval.MIN1, startTime=None, endTime=self.request_client.get_servertime(), limit=50) trash_Arr = [] for stick in self.candleStickArrFor1m: trash_Arr.append(float(stick.close)) self.candleStickArrFor1m_NP = np.array(trash_Arr, dtype='f8') self.now_price = float(self.candleStickArrFor1m[-1].close) except Exception as e: print(f'에러 : {e}') print("인터넷 연결 / 서버 확인 필요") time.sleep(1) def update_MACDhist_thr(self): #macd 값들 ta라이브러리로 넘파이 배열을 넣어서 전역변수에 넣어준다. while True: macd_12Arr, macd_26Arr, macd_histArr = ta.MACD(self.candleStickArrFor1m_NP, fastperiod=12, slowperiod=26, signalperiod=9) self.macd_12 = float(macd_12Arr[-1]) self.macd_26 = float(macd_26Arr[-1]) self.macd_hist = float(macd_histArr[-1]) self.macd_hist_prev = float(macd_histArr[-2]) self.checking_MACDhist() time.sleep(0.5) def update_RSI_thr(self): while True: self.RSI = float(ta.RSI(self.candleStickArrFor1m_NP, timperiod=14)[-1]) time.sleep(0.5) def update_ATR_thr(self): while True: self.checking_ATR() # 상방 돌파 if self.now_price >= self.ATR_band_15_Top: self.ATR_band_rising = True self.ATR_band_falling = False # 하방 돌파 elif self.now_price <= self.ATR_band_15_bottom: self.ATR_band_falling = True self.ATR_band_rising = False # 중앙 elif self.now_price < self.ATR_band_15_Top and self.now_price > self.ATR_band_15_bottom: self.ATR_band_rising = False self.ATR_band_falling = False time.sleep(0.5) def update_touching_15m_20ma(self): while True: if self.now_price > self.moving_average_15m_7 - (self.moving_average_15m_7 * self.moving_average_15m_7_constant) and self.now_price < self.moving_average_15m_7 + (self.moving_average_15m_7 * self.moving_average_15m_7_constant): self.touching_15m_20ma = True else: self.touching_15m_20ma = False time.sleep(0.05) def checking_MACDhist(self): macd_arr, macdsignal_arr, macdhist_arr = ta.MACD(self.candleStickArrFor1m_NP, fastperiod=12, slowperiod=26, signalperiod=9) if macdhist_arr[-2] < 0 and macdhist_arr[-1] > 0: self.macd_golden_bool = True self.macd_dead_bool = False elif macdhist_arr[-2] > 0 and macdhist_arr[-1] < 0: self.macd_dead_bool = True self.macd_golden_bool = False else: self.macd_dead_bool = False self.macd_golden_bool = False if macd_arr[-1] < 0 and macdsignal_arr[-1] < 0: self.MACD_is_it_above_X = False self.MACD_is_it_below_X = True elif macd_arr[-1] > 0 and macdsignal_arr[-1] > 0: self.MACD_is_it_above_X = True self.MACD_is_it_below_X = False def checking_RSI_thr(self): while(1): if self.RSI < self.RSI_is_it_below_X_constant: self.RSI_is_it_below_X = True self.RSI_is_it_above_X = False print(f'{self.RSI} 모드로 15분간 대기') time.sleep(60*15) elif self.RSI >= self.RSI_is_it_below_X_constant and self.RSI <= self.RSI_is_it_above_X_constant: self.RSI_is_it_below_X = False self.RSI_is_it_above_X = False elif self.RSI > self.RSI_is_it_above_X_constant: self.RSI_is_it_below_X = False self.RSI_is_it_above_X = True print(f'{self.RSI} 모드로 15분간 대기') time.sleep(60*15) time.sleep(0.5) def checking_ATR(self): price_high = [] price_low = [] price_close = [] for stick in self.newCandleStickArr_15m: price_high.append(float(stick.high)) price_low.append(float(stick.low)) price_close.append(float(stick.close)) price_high_np = np.array(price_high, dtype='f8') price_low_np = np.array(price_low, dtype='f8') price_close_np = np.array(price_close, dtype='f8') real = ta.ATR(price_high_np, price_low_np, price_close_np, timeperiod=15) sum_15m_20 = 0 for price in self.newCandleStickArr_15m[-20:]: sum_15m_20 += float(price.close) avg_15m_20 = sum_15m_20 / 20 high = avg_15m_20 + (float(real[-1]) * 2) low = avg_15m_20 - (float(real[-1]) * 2) sum_15m_7 = 0 for price in self.newCandleStickArr_15m[-7:]: sum_15m_7 += float(price.close) avg_15m_7 = sum_15m_7 / 7 self.ATR_band_15_Top = high #20일 이평선 기준으로 2 ATR을 빼고 더한것 self.ATR_band_15_bottom = low self.moving_average_15m_7 = avg_15m_7 #7일 이평선에 닿으면 청산 self.moving_average_15m_20 = avg_15m_20 def check_bools_and_update_signal_thr(self): #시그널을 끄는것은 트레이드쓰레드가 할 것 while(1): if self.MACD_is_it_above_X == True and self.macd_dead_bool == True and self.RSI_is_it_above_X == True: self.macd_enter_short_signal = True if self.MACD_is_it_below_X == True and self.macd_golden_bool == True and self.RSI_is_it_below_X == True: self.macd_enter_long_signal = True if self.MACD_is_it_above_X == True and self.macd_golden_bool == True: self.macd_exit_short_signal = True if self.MACD_is_it_below_X == True and self.macd_dead_bool == True: self.macd_exit_long_signal = True if self.ATR_band_rising == True: self.ATR_long_signal = True if self.ATR_band_falling == True: self.ATR_short_signal = True if self.touching_15m_20ma == True: self.touching_15m_7ma_signal = True if self.RSI < (self.RSI_is_it_below_X_constant - 5) == True: self.Short_liquidate_signal = True if self.RSI > (self.RSI_is_it_above_X_constant + 5) == True: self.Long_liquidate_signal = True time.sleep(0.05) def update_UI_thr(self): while True: self._now_price.setText(f'현재가 : {str(self.now_price)}') self._macd_12.setText(f'macd(12) : {str(self.macd_12)}') self._macd_26.setText(f'macd(26) : {str(self.macd_26)}') self._macd_hist.setText(f'macd_hist : {str(self.macd_hist)}') self._macd_hist_prev.setText(f'macd_hist_prev : {str(self.macd_hist_prev)}') self._RSI.setText(f'RSI : {str(self.RSI)}') self._ATR_band_15_Top.setText(f'ATR_Top : {str(self.moving_average_15m_20)} {str(self.ATR_band_15_Top)}') self._ATR_band_15_bottom.setText(f'ATR_bottom : {str(self.ATR_band_15_bottom)}') self._moving_average_15m_7.setText(f'mv_7m : {int(self.moving_average_15m_7)} {int(self.moving_average_15m_7 + (self.moving_average_15m_7 * self.moving_average_15m_7_constant))} {int(self.moving_average_15m_7 - (self.moving_average_15m_7 * self.moving_average_15m_7_constant))}') self._my_money.setText(f'{str(self.my_money)}') #signals UI update if self.MACD_is_it_above_X == True: self._MACD_is_it_above_X.setChecked(True) else: self._MACD_is_it_above_X.setChecked(False) if self.MACD_is_it_below_X == True: self._MACD_is_it_below_X.setChecked(True) else: self._MACD_is_it_below_X.setChecked(False) if self.macd_golden_bool == True: self._macd_goldencross_bool.setChecked(True) else: self._macd_goldencross_bool.setChecked(False) if self.macd_dead_bool == True: self._macd_deadcross_bool.setChecked(True) else: self._macd_deadcross_bool.setChecked(False) if self.RSI_is_it_above_X == True: self._RSI_is_it_above_X.setChecked(True) else: self._RSI_is_it_above_X.setChecked(False) if self.RSI_is_it_below_X == True: self._RSI_is_it_below_X.setChecked(True) else: self._RSI_is_it_below_X.setChecked(False) if self.ATR_band_rising == True: self._ATR_band_rising.setChecked(True) else: self._ATR_band_rising.setChecked(False) if self.ATR_band_falling == True: self._ATR_band_falling.setChecked(True) else: self._ATR_band_falling.setChecked(False) if self.touching_15m_20ma == True: self._touching_15m_7ma.setChecked(True) else: self._touching_15m_7ma.setChecked(False) #signals UI update if self.macd_enter_short_signal == True: self._macd_enter_short_signal.setChecked(True) else: self._macd_enter_short_signal.setChecked(False) if self.macd_enter_long_signal == True: self._macd_enter_long_signal.setChecked(True) else: self._macd_enter_long_signal.setChecked(False) if self.macd_exit_short_signal == True: self._macd_exit_short_signal.setChecked(True) else: self._macd_exit_short_signal.setChecked(False) if self.macd_exit_long_signal == True: self._macd_exit_long_signal.setChecked(True) else: self._macd_exit_long_signal.setChecked(False) if self.ATR_long_signal == True: self._ATR_long_signal.setChecked(True) else: self._ATR_long_signal.setChecked(False) if self.ATR_short_signal == True: self._ATR_short_signal.setChecked(True) else: self._ATR_short_signal.setChecked(False) if self.touching_15m_7ma_signal == True: self._touching_15m_7ma_signal.setChecked(True) else: self._touching_15m_7ma_signal.setChecked(False) if self.Long_liquidate_signal == True: self._Long_liquidate_signal.setChecked(True) else: self._Long_liquidate_signal.setChecked(False) if self.Short_liquidate_signal == True: self._Short_liquidate_signal.setChecked(True) else: self._Short_liquidate_signal.setChecked(False) time.sleep(0.05) def no_position_thread(self): self.del_signal() flag = 1 while flag == 1: self._now_position.setText('no_position_thread') if self.macd_enter_long_signal == True: print(f"{datetime.now()} : {self.now_price} : macd_enter_long_signal BUY") self.post_order("BUY") threading.Timer(1, self.macd_long_thread).start() flag = 0 elif self.macd_enter_short_signal == True: print(f"{datetime.now()} : {self.now_price} : macd_enter_short_signal SELL") self.post_order("SELL") threading.Timer(1, self.macd_short_thread).start() flag = 0 elif self.ATR_long_signal == True: print(f"{datetime.now()} : {self.now_price} : ATR_long_signal BUY") self.post_order("BUY") threading.Timer(1, self.ATR_long_thread).start() flag = 0 elif self.ATR_short_signal == True: print(f"{datetime.now()} : {self.now_price} : ATR_short_signal SELL") self.post_order("SELL") threading.Timer(1, self.ATR_short_thread).start() flag = 0 #이 포지션에선 필요없는 것 if self.macd_exit_long_signal == True: self.macd_exit_long_signal = False if self.macd_exit_short_signal == True: self.macd_exit_short_signal = False if self.touching_15m_7ma_signal == True: self.touching_15m_7ma_signal = False if self.Long_liquidate_signal == True: self.Long_liquidate_signal = False if self.Short_liquidate_signal == True: self.Short_liquidate_signal = False time.sleep(0.05) def macd_long_thread(self): self.del_signal() flag = 1 while flag == 1: self._now_position.setText(f'macd_long_thread') if self.macd_exit_long_signal == True: print(f"{datetime.now()} : {self.now_price} : macd_exit_long_signal 이므로 손절") self.post_order("SELL") threading.Timer(1, self.no_position_thread).start() flag = 0 elif self.Long_liquidate_signal == True: print(f"{datetime.now()} : {self.now_price} : Long_liquidate_signal 이므로 포지션 청산") self.post_order("SELL") threading.Timer(1, self.no_position_thread).start() flag = 0 elif self.ATR_long_signal == True: print(f"{datetime.now()} : {self.now_price} : ATR_long_signal") threading.Timer(1, self.ATR_long_thread).start() flag = 0 elif self.ATR_short_signal == True: print(f"{datetime.now()} : {self.now_price} : ATR_short_signal SELL") self.post_order("SELL") self.post_order("SELL") threading.Timer(1, self.ATR_short_thread).start() flag = 0 # 이 포지션에선 필요없는 것 if self.macd_enter_long_signal == True: self.macd_enter_long_signal = False if self.macd_exit_short_signal == True: self.macd_exit_short_signal = False if self.touching_15m_7ma_signal == True: self.touching_15m_7ma_signal = False if self.Short_liquidate_signal == True: self.Short_liquidate_signal = False time.sleep(0.05) def macd_short_thread(self): self.del_signal() flag = 1 while flag == 1: self._now_position.setText(f'macd_short_thread') if self.macd_exit_short_signal == True: print(f"{datetime.now()} : {self.now_price} : macd_exit_short_signal 이므로 손절") self.post_order("BUY") threading.Timer(1, self.no_position_thread).start() flag = 0 elif self.Short_liquidate_signal == True: print(f"{datetime.now()} : {self.now_price} : Short_liquidate_signal 이므로 포지션 청산") self.post_order("BUY") threading.Timer(1, self.no_position_thread).start() flag = 0 elif self.ATR_long_signal == True: print(f"{datetime.now()} : {self.now_price} : ATR_long_signal BUY") self.post_order("BUY") self.post_order("BUY") threading.Timer(1, self.ATR_long_thread).start() flag = 0 elif self.ATR_short_signal == True: print(f"{datetime.now()} : {self.now_price} : ATR_short_signal") threading.Timer(1, self.ATR_short_thread).start() flag = 0 # 이 포지션에선 필요없는 것 if self.macd_enter_short_signal == True: self.macd_enter_short_signal = False if self.macd_exit_long_signal == True: self.macd_exit_long_signal = False if self.touching_15m_7ma_signal == True: self.touching_15m_7ma_signal = False if self.Long_liquidate_signal == True: self.Long_liquidate_signal = False time.sleep(0.05) def ATR_long_thread(self): self.del_signal() flag = 1 while flag == 1: self._now_position.setText(f'ATR_long_thread') if self.touching_15m_7ma_signal == True: print(f"{datetime.now()} : {self.now_price}에 self.touching_15m_7_signal이 {self.touching_15m_7ma_signal}이므로 SELL") self.post_order("SELL") threading.Timer(1, self.no_position_thread).start() flag = 0 # 이 포지션에선 필요없는 것 if self.macd_enter_long_signal == True: self.macd_enter_long_signal = False if self.macd_enter_short_signal == True: self.macd_enter_short_signal = False if self.macd_exit_long_signal == True: self.macd_exit_long_signal = False if self.macd_exit_short_signal == True: self.macd_exit_short_signal = False if self.ATR_long_signal == True: self.ATR_long_signal = False if self.ATR_short_signal == True: self.ATR_short_signal = False if self.Long_liquidate_signal == True: self.Long_liquidate_signal = False if self.Short_liquidate_signal == True: self.Short_liquidate_signal = False time.sleep(0.05) def ATR_short_thread(self): self.del_signal() flag = 1 while flag == 1: self._now_position.setText(f'ATR_short_thread') if self.touching_15m_7ma_signal == True: print(f"{datetime.now()} : {self.now_price} : touching_15m_7ma_signal BUY") self.post_order("BUY") threading.Timer(1, self.no_position_thread).start() flag = 0 # 이 포지션에선 필요없는 것 if self.macd_enter_long_signal == True: self.macd_enter_long_signal = False if self.macd_enter_short_signal == True: self.macd_enter_short_signal = False if self.macd_exit_long_signal == True: self.macd_exit_long_signal = False if self.macd_exit_short_signal == True: self.macd_exit_short_signal = False if self.ATR_long_signal == True: self.ATR_long_signal = False if self.ATR_short_signal == True: self.ATR_short_signal = False if self.Long_liquidate_signal == True: self.Long_liquidate_signal = False if self.Short_liquidate_signal == True: self.Short_liquidate_signal = False time.sleep(0.05) def get_balance(self): result = self.request_client.get_balance() return result[0].balance def del_signal(self): self.macd_enter_long_signal = False self.macd_enter_short_signal = False self.macd_exit_long_signal = False self.macd_exit_short_signal = False self.ATR_long_signal = False self.ATR_short_signal = False self.Long_liquidate_signal = False self.Short_liquidate_signal = False self.touching_15m_7ma_signal = False def post_order(self, side, order_type="MARKET", quantity=0.001): try: ''' if side == "BUY" and order_type == "MARKET": result = self.request_client.post_order(symbol="BTCUSDT", side=OrderSide.BUY, ordertype=OrderType.MARKET, quantity=quantity) elif side == "SELL" and order_type == "MARKET": result = self.request_client.post_order(symbol="BTCUSDT", side=OrderSide.SELL, ordertype=OrderType.MARKET, quantity=quantity) ''' self.my_money = self.get_balance() except Exception as e: print(e) def debuging_print(self): print(f"{datetime.now()} :" f" self.macd_enter_long_signal : {self.macd_enter_long_signal}" f" self.macd_enter_short_signal : {self.macd_enter_short_signal}" f" self.macd_exit_short_signal : {self.macd_exit_short_signal} " f" self.macd_exit_long_signal : {self.macd_exit_long_signal} " f" self.ATR_long_signal : {self.ATR_long_signal} " f" self.ATR_short_signal : {self.ATR_short_signal} " f" self.touching_15m_7ma_signal : {self.touching_15m_7ma_signal} " f" self.Short_liquidate_signal : {self.Short_liquidate_signal} " f" self.Long_liquidate_signal : {self.Long_liquidate_signal} ")