class OrderManager: def __init__(self): self.client = Client(api_key=os.getenv('API_KEY', ''), api_secret=os.getenv('SECRET_KEY', '')) def get_detailed_open_orders(self) -> List[DetailedOrder]: return [ DetailedOrder(**order) for order in (self.client.get_open_orders()) ] def get_open_orders_symbols(self) -> List[str]: return [ order.get_symbol() for order in (self.get_detailed_open_orders()) ] def get_open_orders(self) -> List[Order]: return [ order.get_order() for order in (self.get_detailed_open_orders()) ] def get_filled_order(self, symbol: str) -> List[DetailedOrder]: return [ DetailedOrder(**order) for order in (self.client.get_all_orders(symbol=symbol)) ] def is_order_filled(self, order: Order) -> bool: return self.get_order(order).is_cancelled def get_order(self, order: Order) -> DetailedOrder: order = self.client.get_all_orders(symbol=order.symbol, orderId=order.orderId)[0] return DetailedOrder(**order)
def test_check_Binance_valid_credentials(self): error_raised = False api_key = os.getenv('Binance_API_Key') api_secret = os.getenv('Binance_API_Secret') try: client = ClientBinance(api_key, api_secret, { "verify": True, "timeout": 20 }) client.get_all_orders(symbol='BNBBTC', limit=1) except: error_raised = True self.assertFalse(error_raised, "Invalid Bianance credentials")
class order_api(object): def __init__(self): self.client = Client("KCl8UvtnFcrlEgpqiwJlA0dr2SM3DuhKHHxiRtHA8oe7yl0JZYqas8oN2XNK0Lfz", "2wj46v8IAKpXoeigAmzfPg1VEjYi3oItNTeeNcEkDrK1HIev4nZHJr1Cd8tiJ5L0", {"verify": True, "timeout": 20}) def get_all_orders(self,symbol='VENETH',limit=10): return self.get_all_orders(symbol,limit) # def create_order(self, symbol='VENETH', side='BUY', type=ORDER_TYPE_LIMIT, timeInForce=TIME_IN_FORCE_GTC, quantity=100, price='0.00001'): # self.client.create_order(symbol,side,type,timeInForce,quantity,price) # def create_sell_limit_order(self): # self.order = client.order_limit_buy(symbol,quantity,price) # def create_market_order(self): # pass def create_test_order(self,symbol,quantity,price): return self.client.create_test_order(symbol=symbol,quantity=quantity,price=price,side='BUY',type='LIMIT',timeInForce='GTC') def get_all_orders(self,symbol='VENETH'): return self.client.get_all_orders(symbol=symbol) def get_open_orders(self,symbol='VENETH'): return self.client.get_open_orders(symbol=symbol) def cancel_order(self,symbol='VENETH',orderId=0): return self.client.cancel_order(symbol=symbol,orderId=orderId) def order_status(self,symbol='VENETH',orderId=0): return self.client.get_order(symbol=symbol,orderId=orderId)
class SystemClient: """Modified model of binance Client Model. Symbol is hardcoded due to task system rules, can be simply refactored. API Keys are hardcoded too, but can be simply refactored. """ def __init__(self, sys_name): self.name = sys_name self.client = Client(Config.API_KEY, Config.API_SECRET) self.client.API_URL = Config.API_URL def get_balance(self): self.balance = self.client.get_account()['balances'] return self.balance def get_trades(self): result_list = [] self.trades = self.client.get_all_orders(symbol='BTCUSDT') for trade in self.trades: result_dict = {} result_dict['OrderID'] = trade['orderId'] result_dict['OrderSymbol'] = trade['symbol'] result_dict['OrderSide'] = trade['side'] result_dict['ExecutionStatus'] = trade['status'] result_dict['OriginalVolume'] = trade['origQty'] result_dict['ExecutedVolume'] = trade['executedQty'] result_dict['time'] = datetime.fromtimestamp(trade['time'] / 1000) result_list.append(result_dict) return result_list
def start(client) -> None: """start of the program""" # gets server time in UNIX server_time = client.get_server_time() # makes server time readable to normal people readable_time = datetime.datetime.fromtimestamp( round(server_time["serverTime"]) / 1000).strftime('%Y-%m-%d %H:%M:%S') print(server_time["serverTime"], "-", readable_time) # loop forever while True: client = Client(api["neo 0.076 8h 21"]["pub"], api["neo 0.076 8h 21"]["priv"]) # gets last order created last_trade = client.get_all_orders(symbol="WBTCBTC", limit=1) # starts main function main(client, last_trade) # sleeps for 61 seconds for funzies sleep(61)
def binance_get_all_orders(chat_id, **params): full_api = getbinanceapi(chat_id)['binance_api'] api_key = full_api.split(':')[0].strip() api_secret = full_api.split(':')[1].strip() client = Client(api_key, api_secret) binance_timesync(client) try: js_info = client.get_all_orders(**params) print(js_info) return js_info except Exception as e: print(e)
class market_api(object): def __init__(self): self.client = Client(cfg.api_keys['key'],cfg.api_keys['secret'], {"verify": True, "timeout": 20}) def get_all_orders(self,symbol='VENETH'): return self.client.get_all_orders(symbol=symbol, requests_params={'timeout': 5}) def ping(self): return self.client.ping() def get_system_status(self): return self.client.get_system_status() def get_exchange_info(self): return self.client.get_exchange_info() def get_symbol_info(self,symbol='VENETH'): return self.client.get_symbol_info(symbol=symbol) def get_market_depth(self,symbol='VENETH'): return self.client.get_order_book(symbol=symbol) def get_latest_trades(self,symbol): return self.client.get_recent_trades(symbol=symbol) def get_historical_trades(self,symbol='VENETH'): return self.client.get_historical_trades(symbol=symbol) def get_aggregate_trades(self,symbol='VENETH'): return self.client.get_aggregate_trades(symbol=symbol) def get_tickers(self): return self.client.get_ticker() def get_all_prices(self): return self.client.get_all_tickers() def get_orderbook_tickers(self): return self.client.get_orderbook_tickers()
class BinanceClient(): def __init__(self, api_key=os.getenv("BINANCE_API_KEY"), api_secret=os.getenv("BINANCE_API_SECRET")): self._client = Client(api_key, api_secret) def get_orders(self, symbol): all_orders = self._client.get_all_orders(symbol=symbol) orders = [] for order in all_orders: if order['status'] == "FILLED": orders.append(order) return orders def get_base_pair(self, symbol): return symbol[:3] def get_klines(self, symbol): # Get price on order date # klines = client.get_historical_klines("ETHBTC", Client.KLINE_INTERVAL_30MINUTE, "1 Dec, 2017", "1 Jan, 2018") order_dt = datetime.datetime.utcfromtimestamp(order['time'] / 1000) order_dt_formatted = order_dt.strftime("%d %b, %Y") klines = self._client.get_historical_klines( pair, Client.KLINE_INTERVAL_30MINUTE, order_dt_formatted, order_dt_formatted) print(klines) # print ("order_datetime={order_datetime}".format(order_datetime=order_datetime)) def get_base_pair_total(self, base_pair): alt_pairs = ALT_PAIRS[base_pair] if base_pair in ALT_PAIRS else None # print (alt_pairs) total = 0.0 if alt_pairs is not None: for pair in alt_pairs: orders = self.get_orders(pair) for order in orders: total += float(order['origQty']) * float(order['price']) return total
def GET_ALL_ORDERS(LOG,univ,out_file): LOG.write(TimeStamp() + "GET_ALL_ORDERS\n") try: client = Client(api_key, api_secret) except BinanceAPIException as e: print(e.status_code) print(e.message) F = open(out_file,'w') F.write("symbol\torderId\tclientOrderId\tprice\torigQty\texecutedQty\tstatus\ttimeInForce\ttype\tside\tstopPrice\ticebergQty\ttime\tTS\n") for u in univ: if (u == "BTC"): continue try: sym = u + "BTC" #print(sym) data = client.get_all_orders(symbol=sym) if data is None: continue for T in data: TS = TimeStampUnixDateTime(T['time']) S = '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' % \ (u,T['orderId'],T['clientOrderId'],T['price'],T['origQty'], \ T['executedQty'],T['status'],T['timeInForce'],T['type'], \ T['side'],T['stopPrice'],T['icebergQty'],T['time'],TS) F.write(S) except BinanceAPIException as e: print(e.status_code) print(e.message) LOG.write(e.message + "\n") F.close()
class AuthAPI(AuthAPIBase): def __init__(self, api_key: str = '', api_secret: str = '', api_url: str = 'https://api.binance.com') -> None: """Binance API object model Parameters ---------- api_key : str Your Binance account portfolio API key api_secret : str Your Binance account portfolio API secret """ # options self.debug = False self.die_on_api_error = False valid_urls = [ 'https://api.binance.com/', 'https://testnet.binance.vision/api/' ] # validate Binance API if api_url not in valid_urls: raise ValueError('Binance API URL is invalid') if len(api_url) > 1 and api_url[-1] != '/': api_url = api_url + '/' # validates the api key is syntactically correct p = re.compile(r"^[A-z0-9]{64,64}$") if not p.match(api_key): self.handle_init_error('Binance API key is invalid') # validates the api secret is syntactically correct p = re.compile(r"^[A-z0-9]{64,64}$") if not p.match(api_secret): self.handle_init_error('Binance API secret is invalid') self.mode = 'live' # TODO: check if this needs to be set here self.api_url = api_url self.api_key = api_key self.api_secret = api_secret self.client = Client(self.api_key, self.api_secret, { 'verify': False, 'timeout': 20 }) def handle_init_error(self, err: str) -> None: if self.debug: raise TypeError(err) else: raise SystemExit(err) def getClient(self) -> Client: return self.client def getAccounts(self) -> pd.DataFrame: """Retrieves your list of accounts""" accounts = self.client.get_account() if 'balances' not in accounts: return pd.DataFrame(columns=[ 'index', 'id', 'currency', 'balance', 'hold', 'available', 'profile_id', 'trading_enabled' ]) df = pd.DataFrame(self.client.get_account()['balances']) df.columns = ['currency', 'available', 'hold'] df['index'] = df.index df['id'] = df.index df['balance'] = df['available'] df['profile_id'] = '' df['trading_enabled'] = True return df[[ 'index', 'id', 'currency', 'balance', 'hold', 'available', 'profile_id', 'trading_enabled' ]] def getAccount(self, account: int) -> pd.DataFrame: """Retrieves a specific account""" accounts = self.client.get_account() if 'balances' not in accounts: return pd.DataFrame(columns=[ 'index', 'id', 'currency', 'balance', 'hold', 'available', 'profile_id', 'trading_enabled' ]) df = pd.DataFrame(self.client.get_account()['balances']) df.columns = ['currency', 'available', 'hold'] df['index'] = df.index df['id'] = df.index df['balance'] = df['available'] df['profile_id'] = '' df['trading_enabled'] = True return df[df['id'] == account][[ 'index', 'id', 'currency', 'balance', 'hold', 'available', 'profile_id', 'trading_enabled' ]] def getFees(self, market: str = '') -> pd.DataFrame: if market != '': resp = self.client.get_trade_fee(symbol=market) if 'tradeFee' in resp and len(resp['tradeFee']) > 0: df = pd.DataFrame(resp['tradeFee'][0], index=[0]) df['usd_volume'] = None df.columns = [ 'maker_fee_rate', 'market', 'taker_fee_rate', 'usd_volume' ] return df[[ 'maker_fee_rate', 'taker_fee_rate', 'usd_volume', 'market' ]] return pd.DataFrame( columns=['maker_fee_rate', 'taker_fee_rate', 'market']) else: resp = self.client.get_trade_fee() if 'tradeFee' in resp: df = pd.DataFrame(resp['tradeFee']) df['usd_volume'] = None df.columns = [ 'maker_fee_rate', 'market', 'taker_fee_rate', 'usd_volume' ] return df[[ 'maker_fee_rate', 'taker_fee_rate', 'usd_volume', 'market' ]] return pd.DataFrame( columns=['maker_fee_rate', 'taker_fee_rate', 'market']) def getMakerFee(self, market: str = '') -> float: if market == '': fees = self.getFees() else: fees = self.getFees(market) if len(fees) == 0 or 'maker_fee_rate' not in fees: print( f"error: 'maker_fee_rate' not in fees (using {DEFAULT_MAKER_FEE_RATE} as a fallback)" ) return DEFAULT_MAKER_FEE_RATE if market == '': return fees else: return float(fees['maker_fee_rate'].to_string(index=False).strip()) def getTakerFee(self, market: str = '') -> float: if market == '': fees = self.getFees() else: fees = self.getFees(market) if len(fees) == 0 or 'taker_fee_rate' not in fees: print( f"error: 'taker_fee_rate' not in fees (using {DEFAULT_TAKER_FEE_RATE} as a fallback)" ) return DEFAULT_TAKER_FEE_RATE if market == '': return fees else: return float(fees['taker_fee_rate'].to_string(index=False).strip()) def __convertStatus(self, val: str) -> str: if val == 'filled': return 'done' else: return val def getOrders(self, market: str = '', action: str = '', status: str = 'all') -> pd.DataFrame: """Retrieves your list of orders with optional filtering""" # if market provided if market != '': # validates the market is syntactically correct if not self._isMarketValid(market): raise ValueError('Binance market is invalid.') # if action provided if action != '': # validates action is either a buy or sell if not action in ['buy', 'sell']: raise ValueError('Invalid order action.') # validates status is either open, pending, done, active, or all if not status in ['open', 'pending', 'done', 'active', 'all']: raise ValueError('Invalid order status.') resp = self.client.get_all_orders(symbol=market) if len(resp) > 0: df = pd.DataFrame(resp) else: df = pd.DataFrame() if len(df) == 0: return pd.DataFrame() df = df[[ 'time', 'symbol', 'side', 'type', 'executedQty', 'cummulativeQuoteQty', 'status' ]] df.columns = [ 'created_at', 'market', 'action', 'type', 'size', 'filled', 'status' ] df['created_at'] = df['created_at'].apply(lambda x: int(str(x)[:10])) df['created_at'] = df['created_at'].astype("datetime64[s]") df['size'] = df['size'].astype(float) df['filled'] = df['filled'].astype(float) df['action'] = df['action'].str.lower() df['type'] = df['type'].str.lower() df['status'] = df['status'].str.lower() df['price'] = df['filled'] / df['size'] # pylint: disable=unused-variable for k, v in df.items(): if k == 'status': df[k] = df[k].map(self.__convertStatus) if action != '': df = df[df['action'] == action] df = df.reset_index(drop=True) if status != 'all' and status != '': df = df[df['status'] == status] df = df.reset_index(drop=True) return df def marketBuy(self, market: str = '', quote_quantity: float = 0) -> list: """Executes a market buy providing a funding amount""" # validates the market is syntactically correct if not self._isMarketValid(market): raise ValueError('Binance market is invalid.') # validates quote_quantity is either an integer or float if not isinstance(quote_quantity, int) and not isinstance( quote_quantity, float): raise TypeError('The funding amount is not numeric.') try: current_price = self.getTicker(market)[1] base_quantity = np.divide(quote_quantity, current_price) df_filters = self.getMarketInfoFilters(market) step_size = float(df_filters.loc[df_filters['filterType'] == 'LOT_SIZE']['stepSize']) precision = int(round(-math.log(step_size, 10), 0)) # remove fees base_quantity = base_quantity - (base_quantity * self.getTradeFee(market)) # execute market buy stepper = 10.0**precision truncated = math.trunc(stepper * base_quantity) / stepper print('Order quantity after rounding and fees:', truncated) return self.client.order_market_buy(symbol=market, quantity=truncated) except Exception as err: ts = datetime.now().strftime("%d-%m-%Y %H:%M:%S") print(ts, 'Binance', 'marketBuy', str(err)) return [] def marketSell(self, market: str = '', base_quantity: float = 0) -> list: """Executes a market sell providing a crypto amount""" # validates the market is syntactically correct if not self._isMarketValid(market): raise ValueError('Binance market is invalid.') if not isinstance(base_quantity, int) and not isinstance( base_quantity, float): raise TypeError('The crypto amount is not numeric.') try: df_filters = self.getMarketInfoFilters(market) step_size = float(df_filters.loc[df_filters['filterType'] == 'LOT_SIZE']['stepSize']) precision = int(round(-math.log(step_size, 10), 0)) # remove fees base_quantity = base_quantity - (base_quantity * self.getTradeFee(market)) # execute market sell stepper = 10.0**precision truncated = math.trunc(stepper * base_quantity) / stepper print('Order quantity after rounding and fees:', truncated) return self.client.order_market_sell(symbol=market, quantity=truncated) except Exception as err: ts = datetime.now().strftime("%d-%m-%Y %H:%M:%S") print(ts, 'Binance', 'marketSell', str(err)) return [] def getTradeFee(self, market: str) -> float: resp = self.client.get_trade_fee(symbol=market, timestamp=self.getTime()) ### DEBUG ### if 'success' not in resp: print('*** getTradeFee(' + market + ') - missing "success" ***') print(resp) if 'tradeFee' not in resp: print('*** getTradeFee(' + market + ') - missing "tradeFee" ***') print(resp) else: if len(resp['tradeFee']) == 0: print('*** getTradeFee(' + market + ') - "tradeFee" empty ***') print(resp) else: if 'taker' not in resp['tradeFee'][0]: print('*** getTradeFee(' + market + ') - missing "trader" ***') print(resp) if resp['success']: return resp['tradeFee'][0]['taker'] else: return DEFAULT_TRADE_FEE_RATE def getMarketInfo(self, market: str) -> dict: # validates the market is syntactically correct if not self._isMarketValid(market): raise TypeError('Binance market required.') return self.client.get_symbol_info(symbol=market) def getMarketInfoFilters(self, market: str) -> pd.DataFrame: return pd.DataFrame( self.client.get_symbol_info(symbol=market)['filters']) def getTicker(self, market: str) -> tuple: # validates the market is syntactically correct if not self._isMarketValid(market): raise TypeError('Binance market required.') resp = self.client.get_symbol_ticker(symbol=market) if 'price' in resp: return (self.getTime().strftime('%Y-%m-%d %H:%M:%S'), float('{:.8f}'.format(float(resp['price'])))) now = datetime.today().strftime('%Y-%m-%d %H:%M:%S') return (now, 0.0) def getTime(self) -> datetime: """Retrieves the exchange time""" try: resp = self.client.get_server_time() epoch = int(str(resp['serverTime'])[0:10]) return datetime.fromtimestamp(epoch) except: return None
class BinanceAPI: def __init__(self, api_key, api_secret): API_KEY = api_key API_SECRET = api_secret self.client = Client(API_KEY, API_SECRET) def get_ticker(self, pair): try: value = self.client.get_ticker(symbol=pair) return value except Exception as e: print("Exception : " + str(e)) def get_current_price(self, pair): try: ticker = self.client.get_symbol_ticker(symbol=pair) value = ticker["price"] return float(value) except Exception as e: print("Exception : " + str(e)) def get_klines(self, pair, number): try: klines = pd.DataFrame(self.client.get_klines(symbol=pair, interval=Client.KLINE_INTERVAL_30MINUTE, limit=number),columns = ["OpenTime","Open","High","Low","Close","Volume","CloseTime","QuoteVolume","TradeCount","TakerVolume","TakerQuoteVolume","Ignore"]) value = klines[["OpenTime","Close","High","Low","Volume","QuoteVolume","TakerVolume","TakerQuoteVolume","TradeCount"]].copy() value.loc[:, "OpenTime"] = pd.to_datetime(value["OpenTime"].apply(lambda x: datetime.fromtimestamp(int(x/1000)))) value = value.set_index("OpenTime") return value except Exception as e: print("Exception : " + str(e)) def get_historical_klines(self, pair, start, end): try: klines = pd.DataFrame(self.client.get_historical_klines(start_str = start,end_str = end,symbol=pair, interval=Client.KLINE_INTERVAL_30MINUTE, limit=500),columns = ["OpenTime","Open","High","Low","Close","Volume","CloseTime","QuoteVolume","TradeCount","TakerVolume","TakerQuoteVolume","Ignore"]) value = klines[["OpenTime","Close","High","Low","Volume","QuoteVolume","TakerVolume","TakerQuoteVolume","TradeCount"]].copy() value.loc[:, "OpenTime"] = pd.to_datetime(value["OpenTime"].apply(lambda x: datetime.fromtimestamp(int(x/1000)))) value = value.set_index("OpenTime") return value except Exception as e: print("Exception : " + str(e)) def get_balance(self, symbol): try: value = self.client.get_asset_balance(asset=symbol) return value except Exception as e: print('Exception : {}'.format(e)) def get_free_balance(self, symbol): try: value = self.client.get_asset_balance(asset=symbol) return float(value["free"]) except Exception as e: print('Exception : {}'.format(e)) def get_futures_balance(self, symbol): try: value = self.client.futures_account_balance() balance = [balance["balance"] for balance in value if balance["asset"] == symbol] return float(str(*balance)) except Exception as e: print('Exception : {}'.format(e)) def create_limit_order(self, symbol, price, quantity, side_str): try: if side_str == "BUY": side = self.client.SIDE_BUY elif side_str == "SELL": side = self.client.SIDE_SELL order = self.client.order_limit( symbol=symbol, side=side, timeInForce=self.client.TIME_IN_FORCE_IOC, price=price, quantity=quantity) print(order) print("buy order created.\nSymbol:{0:5}\nPrice:{1:5}\nQuantity:{2:5}",symbol,price,quantity) except Exception as e: print("Exception : " + str(e)) def create_market_order(self, symbol, quantity, side_str): try: if side_str == "BUY": side = self.client.SIDE_BUY elif side_str == "SELL": side = self.client.SIDE_SELL order = self.client.order_market( symbol=symbol, side=side, quantity=quantity) print(order) print("buy order created.\nSymbol:{0:5}\nPrice:{1:5}\nQuantity:{2:5}",symbol,price,quantity) except Exception as e: print("Exception : " + str(e)) def create_test_order(self, symbol, quantity, side_str): try: if side_str == "BUY": side = self.client.SIDE_BUY elif side_str == "SELL": side = self.client.SIDE_SELL order = self.client.create_test_order( symbol=symbol, side=side, type=self.client.ORDER_TYPE_MARKET, quantity=quantity) print(order) print("buy order created.\nSymbol:{0:5}\nQuantity:{1:5}".format(symbol,quantity)) except Exception as e: print("Exception : " + str(e)) def create_futures_order(self, symbol, quantity, side_str): try: if side_str == "BUY": side = self.client.SIDE_BUY elif side_str == "SELL": side = self.client.SIDE_SELL order = self.client.futures_create_order( symbol=symbol, side=side, type=self.client.ORDER_TYPE_MARKET, quantity=quantity) #print(order) print("buy order created.\nSymbol:{0:5}\nQuantity:{1:5}".format(symbol,quantity)) except Exception as e: print("Exception : " + str(e)) def get_base_asset(self, symbol): try: return self.client.get_symbol_info(symbol)["baseAsset"]; except Exception as e: print("Exception : " + str(e)) def get_quote_asset(self, symbol): try: return self.client.get_symbol_info(symbol)["quoteAsset"]; except Exception as e: print("Exception : " + str(e)) def get_all_tickers(self): try: return self.client.get_all_tickers(); except Exception as e: print("Exception : " + str(e)) def get_all_orders(self): try: return self.client.get_all_orders(); except Exception as e: print("Exception : " + str(e))
class RmtSrvObj(BaseObj): def __init__(self, symbol, line_type, size=0, since='', debug=False, balance_min=0.01): self.rmt_srv_obj = Client(api_key, secret_key) self.balance_min = balance_min super(RmtSrvObj, self).__init__(symbol, line_type, size, since, debug) def get_kline(self): return self.rmt_srv_obj.get_klines(symbol=self.symbol, interval=self.type,limit=self.size) def get_kline_pd(self): return pd.DataFrame(self.get_kline(),columns=['open_time', 'open','high','low','close','volume','close_time', 'quote_asset_volume','number_of_trades','taker_buy_base_asset_volume','taker_buy_quote_asset_volume','ignore']) def balance(self, coin=''): balance = {'free': 0, 'frozen': 0} for item in self.account: if coin == item['asset']: balance = {'free': item['free'], 'frozen': item['locked']} break return balance """ 'balances': [{'asset': 'BTC', 'free': '0.00000000', 'locked': '0.00000000'}, {'asset': 'ETH', 'free': '0.43767306', 'locked': '2.74720975'}, {'asset': 'ICO', 'free': '0.00000000', 'locked': '0.00000000'}] """ def get_account(self): user = self.rmt_srv_obj.get_account() # print(user) regular_user = user['balances'] return regular_user def get_balance(self, coin): coin_info = {'coin': coin, 'free': 0, 'frozen': 0, 'balance': 0} for item in self.account: if item['asset'] == coin: coin_info['free'] = float(item['free']) coin_info['frozen'] = float(item['locked']) coin_info['balance'] = coin_info['free'] + coin_info['frozen'] break return coin_info def get_available_coins(self): coins = [] for item in self.account: free_coin = float(item['free']) frozen_coin = float(item['locked']) balance = free_coin + frozen_coin if balance > self.balance_min: coins.append({'coin': item['asset'], 'free': free_coin, 'frozen': frozen_coin, 'balance': balance}) return coins def get_all_orders(self): return self.rmt_srv_obj.get_all_orders(symbol=self.symbol) def get_order(self, order_id): order = self.rmt_srv_obj.get_order(symbol=self.symbol, orderId=order_id) if not order: return None new_order = {} if order['status'] == 'CANCELED': new_order['status'] = 'Cancelled' elif order['status'] == 'NEW': new_order['status'] = 'Not deal' elif order['status'] == 'PARTIALLY_FILLED': new_order['status'] = 'Part dealt' elif order['status'] == 'FILLED': new_order['status'] = 'Dealt' elif order['status'] == 'REJECTED': new_order['status'] = 'Rejected' elif order['status'] == 'EXPIRED': new_order['status'] = 'Expired' else: new_order['status'] = 'Error' if order['side'] == 'BUY': new_order['type'] = 'buy' else: new_order['type'] = 'sell' new_order['symbol'] = order['symbol'] new_order['timestamp'] = order['time']/1000 new_order['create_date'] = datetime.fromtimestamp(new_order['timestamp']).strftime("%Y-%m-%d %H:%M:%S") new_order['order_id'] = str(order['orderId']) new_order['amount'] = order['origQty'] new_order['avg_price'] = order['price'] new_order['deal_amount'] = order['executedQty'] new_order['price'] = order['price'] return new_order def buy(self, price, amount): self.debug('Buy order: pair(%s), price(%s), amount(%s)' % (self.symbol, price, amount)) ret = self.rmt_srv_obj.create_order(symbol=self.symbol, side=SIDE_BUY, type=ORDER_TYPE_LIMIT, timeInForce=TIME_IN_FORCE_GTC, price=price, quantity=amount) # self.debug(ret) try: if ret['orderId']: # self.debug('Return buy order ID: %s' % ret['orderId']) return ret['orderId'] else: self.debug('Place order failed') return None except Exception: self.debug('Error result: %s' % ret) return None def sell(self, price, amount): self.debug('Sell order: pair(%s), price(%s), amount(%s)' % (self.symbol, price, amount)) ret = self.rmt_srv_obj.create_order(symbol=self.symbol, side=SIDE_SELL, type=ORDER_TYPE_LIMIT, timeInForce=TIME_IN_FORCE_GTC, price=price, quantity=amount) # self.debug(ret) try: if ret['orderId']: # self.debug('Return sell order ID: %s' % ret['orderId']) return ret['orderId'] else: self.debug('Place order failed') return None except Exception: self.debug('Error result: %s' % ret) return None def cancel_orders(self): orders = self.rmt_srv_obj.get_open_orders(symbol=self.symbol) for order in orders: self.rmt_srv_obj.cancel_order(symbol=self.symbol, orderId=order['orderId'])
class FXConnector(Logger): ORDER_STATUS_NEW = 'NEW' ORDER_STATUS_PARTIALLY_FILLED = 'PARTIALLY_FILLED' ORDER_STATUS_FILLED = 'FILLED' ORDER_STATUS_CANCELED = 'CANCELED' ORDER_STATUS_PENDING_CANCEL = 'PENDING_CANCEL' ORDER_STATUS_REJECTED = 'REJECTED' ORDER_STATUS_EXPIRED = 'EXPIRED' SIDE_BUY = 'BUY' SIDE_SELL = 'SELL' ORDER_TYPE_LIMIT = 'LIMIT' ORDER_TYPE_MARKET = 'MARKET' ORDER_TYPE_STOP_LOSS = 'STOP_LOSS' ORDER_TYPE_STOP_LOSS_LIMIT = 'STOP_LOSS_LIMIT' ORDER_TYPE_TAKE_PROFIT = 'TAKE_PROFIT' ORDER_TYPE_TAKE_PROFIT_LIMIT = 'TAKE_PROFIT_LIMIT' ORDER_TYPE_LIMIT_MAKER = 'LIMIT_MAKER' TIME_IN_FORCE_GTC = 'GTC' # Good till cancelled TIME_IN_FORCE_IOC = 'IOC' # Immediate or cancel TIME_IN_FORCE_FOK = 'FOK' # Fill or kill ORDER_RESP_TYPE_ACK = 'ACK' ORDER_RESP_TYPE_RESULT = 'RESULT' ORDER_RESP_TYPE_FULL = 'FULL' def __init__(self, key=None, secret=None): super().__init__() self.__key = key self.__secret = secret self.client = Client(key, secret) self.bs: BinanceWebsocket = None # self.connection = None self.ticker_connection = None self.user_data_connection = None def listen_symbols(self, symbols, on_ticker_received, user_data_handler): self.bs = BinanceWebsocket(self.client) self.bs.start_ticker(symbols, on_ticker_received) self.bs.start_user_info(user_data_handler) self.logInfo('Ticker and User WS initialized') def start_listening(self): self.bs.start() self.logInfo('WS listening started') def stop_listening(self): self.bs.stop_sockets() self.logInfo('Socket stopped') @retry(**DEFAULT_RETRY_SETTINGS) def cancel_order(self, sym, id): return self.client.cancel_order(symbol=sym, orderId=id) @retry(**DEFAULT_RETRY_SETTINGS) def cancel_open_orders(self, sym): orders = self.get_open_orders(sym) if orders: for order_id in orders: self.client.cancel_order(symbol=sym, orderId=order_id) def get_server_time(self): return self.client.get_server_time() @retry(**DEFAULT_RETRY_SETTINGS) def get_open_orders(self, sym): return [o['orderId'] for o in self.client.get_open_orders(symbol=sym)] @retry(**DEFAULT_RETRY_SETTINGS) def get_all_orders(self, sym, limit=500): return { o['orderId']: { 'status': o['status'], 'price': o['price'], 'stop_price': o['stopPrice'], 'vol': o['origQty'], 'vol_exec': o['executedQty'] } for o in self.client.get_all_orders(symbol=sym, limit=limit) } @retry(**DEFAULT_RETRY_SETTINGS) def get_all_tickers(self): return self.client.get_all_tickers() @retry(**DEFAULT_RETRY_SETTINGS) def get_orderbook_tickers(self): return self.client.get_orderbook_tickers() @retry(**DEFAULT_RETRY_SETTINGS) def get_order_status(self, sym, id): return self.client.get_order(symbol=sym, orderId=id) # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY) def create_makret_order(self, sym, side, volume): return self.client.create_order( symbol=sym, side=side, type=FXConnector.ORDER_TYPE_MARKET, quantity=FXConnector.format_number(volume)) # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY) def create_limit_order(self, sym, side, price, volume): return self.client.create_order( symbol=sym, side=side, type=FXConnector.ORDER_TYPE_LIMIT, timeInForce=FXConnector.TIME_IN_FORCE_GTC, quantity=FXConnector.format_number(volume), price=FXConnector.format_number(price)) # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY) def create_stop_order(self, sym, side, stop_price, price, volume): return self.client.create_order( symbol=sym, side=side, type=FXConnector.ORDER_TYPE_STOP_LOSS_LIMIT, timeInForce=FXConnector.TIME_IN_FORCE_GTC, quantity=FXConnector.format_number(volume), stopPrice=FXConnector.format_number(stop_price), price=FXConnector.format_number(price)) # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY) def create_test_stop_order(self, sym, side, price, volume): return self.client.create_test_order( symbol=sym, side=side, type=FXConnector.ORDER_TYPE_STOP_LOSS_LIMIT, timeInForce=FXConnector.TIME_IN_FORCE_GTC, quantity=FXConnector.format_number(volume), stopPrice=FXConnector.format_number(price), price=FXConnector.format_number(price)) @retry(**DEFAULT_RETRY_SETTINGS) def get_balance(self, asset): bal = self.client.get_asset_balance(asset=asset) return float(bal['free']), float(bal['locked']) @retry(**DEFAULT_RETRY_SETTINGS) def get_all_balances(self, assets: dict): res = self.client.get_account() if 'balances' in res: for bal in res['balances']: if bal['asset'] in assets: assets[bal['asset']] = { 'f': float(bal['free']), 'l': float(bal['locked']) } @retry(**DEFAULT_RETRY_SETTINGS) def get_all_balances_dict(self): res = self.client.get_account() if 'balances' in res: return { bal['asset']: { 'f': float(bal['free']), 'l': float(bal['locked']) } for bal in res['balances'] } return {} @retry(**DEFAULT_RETRY_SETTINGS) def get_exchange_info(self): return self.client.get_exchange_info() # info = self.client.get_exchange_info() # # symbol_info = None # for s in info['symbols']: # if s['symbol'] == sym: # symbol_info = s # break # # props = {} # for f in symbol_info['filters']: # props.update(f) # # props.pop('filterType', None) # # return FXConnector.ExchangeInfo(**props) @classmethod def format_number(cls, num): return '{:.08f}'.format(num)
class BinanceOrders(ExchangeOrders): '''Handle order calls to binance.''' def __init__(self, config=config.binance): self.client = Client(config['api_key'], config['api_secret']) def buy_order(self, symbol, quantity, test=True): """ Place a market buy order via the binance API. Parameters: ------------ symbol: string A valid binance cryptocurreny symbol. quantity: int | float The amount of <symbol> to buy. test: boolean True ---> Simulate a buy via the Binance API to check authorization. """ if test: order = self.client.create_test_order(symbol=symbol, side=SIDE_BUY, type=ORDER_TYPE_MARKET, quantity=quantity) else: order = self.client.create_order(symbol=symbol, side=SIDE_BUY, type=ORDER_TYPE_MARKET, quantity=quantity) # return True if order else False return order def sell_order(self, symbol, quantity, test=True): """ Place a market buy order via the binance API. Parameters: ------------ symbol: string A valid binance cryptocurreny symbol. quantity: int | float The amount of <symbol> to buy. test: boolean True ---> Simulate a buy via the Binance API to check authorization. """ if test: order = self.client.create_test_order(symbol=symbol, side=SIDE_SELL, type=ORDER_TYPE_MARKET, quantity=quantity) else: order = self.client.create_order(symbol=symbol, side=SIDE_sell, type=ORDER_TYPE_MARKET, quantity=quantity) # return True if order else False return order def account_balance(self): """Check account balance for all cryptocurrencies.""" return self.client.get_account()['balances'] def coin_balance(self, symbol): """Check balance for a given cryptocurreny""" return self.client.get_asset_balance(symbol) def account_status(self): """Check for normal account status.""" status = self.client.get_account_status() if status['msg'] == 'Normal' and status['success']: return True else: return False def all_orders(self, symbol): """Get records of all orders.""" return self.client.get_all_orders(symbol=symbol) def trades(self, symbol): """Get records of all trades.""" return self.client.get_my_trades(symbol=symbol)
def enterSellPosition(): #here check conditions to Sell return def strategy(): return #-----------------------------------------------------------------------# # CONNECT TO WEBSCOKET symbol = 'btcusdt' interval = '1m' ws = websocket.WebSocketApp( f'wss://stream.binance.com:9443/ws/{symbol}@kline_{interval}', on_open=on_open, on_message=on_message, on_close=on_close, on_error=on_error) #-----------------------------------------------------------------------# # START RECEIVING INFO #ws.run_forever() orders = client.get_all_orders(symbol=symbol.upper()) quantity = float(orders[-1]['executedQty']) sell_order = self.client.create_order(symbol=symbol.upper(), side='SELL', type='MARKET', quantity=quantity) print(sell_order)
from pprint import pprint import configapi2 #import configapi2 #print(configapi2.API_KEY) api_key = configapi2.API_KEY api_secret = configapi2.SECRET_KEY client = Client(api_key, api_secret) ''' order = client.order_limit_buy( symbol='DOGEUSDT', quantity=450, price='0.03') pprint(order) ''' # 215750186 ''' result = client.cancel_order( symbol='DOGEUSDT', orderId=215750186) print(result) ''' orders = client.get_all_orders(symbol='DOGEUSDT') #orders = client.get_open_orders(symbol='DOGEUSDT') print('ALL ORDER:', orders)
data = pd.read_json("trades.txt") data.to_csv("Binance - trades.csv") #Trades - End orders = [] #Orders - Start for instr in instruments['symbols']: index = 0 recordcount = 500 while (recordcount == 500): order = client.get_all_orders(symbol=instr['symbol'], limit=500, orderId=index) if order != [] and order is not None: orders.extend(order) recordcount = len(order) index = int(order[recordcount - 1]['orderId']) + 1 else: recordcount = 0 for tr in orders: tr['time'] = dt.datetime.fromtimestamp(int(tr['time']) / 1000) with open('orders.txt', 'w') as outfile: json.dump(orders, outfile, default=myconverter)
class trade_realtime(): def __init__(self, api_key, api_secret, total_list=None): self.coin_list = total_list self.api_key = api_key self.api_secret = api_secret self.client = Client(api_key, api_secret) def get_time(self): server_time = self.client.get_server_time() # { # "serverTime": 1499827319559 # } return int(server_time['serverTime']) def get_exchange_status(self): return self.client.get_system_status() def get_coin_price(self, coin_list): # kwargs = {'data': coin} output_data = [] for coin in coin_list: price_d = ast.literal_eval( json.dumps(self.client.get_symbol_ticker(symbol=coin))) print(price_d) price = float(price_d['price']) output_data.append(price) return output_data def get_trade(self, start, end): output_data = [] for coin in self.coin_list: output_data.append( self.client.get_aggregate_trades(symbol=coin, startTime=start, endTime=end)) return output_data def get_kline(self, start, end): output_data = [] for coin in self.coin_list: output_data.append( self.client.get_klines(symbol=coin, interval=Client.KLINE_INTERVAL_5MINUTE, limit=500, startTime=start, endTime=end)) return output_data def get_historical_klines(self, symbol, interval, start, end): # init our list output_data = [] # setup the max limit limit = 500 timeframe = interval_to_milliseconds(interval) start_ts = start idx = 0 # it can be difficult to know when a symbol was listed on Binance so allow start time to be before list date symbol_existed = False while True: # fetch the klines from start_ts up to max 500 entries or the end_ts if set temp_data = self.client.get_klines(symbol=symbol, interval=interval, limit=limit, startTime=start_ts, endTime=end) # handle the case where our start date is before the symbol pair listed on Binance if not symbol_existed and len(temp_data): symbol_existed = True if symbol_existed: # append this loops data to our output data output_data += temp_data # update our start timestamp using the last value in the array and add the interval timeframe start_ts = temp_data[len(temp_data) - 1][0] + timeframe else: # it wasn't listed yet, increment our start date start_ts += timeframe idx += 1 # check if we received less than the required limit and exit the loop if len(temp_data) < limit: # exit the while loop break # sleep after every 3rd call to be kind to the API if idx % 3 == 0: time.sleep(1) return output_data def get_orderbook_ticker(self): pass def order_limit_buy(self, **params): return self.client.order_limit_buy(**params) def order_limit_sell(self, **params): return self.client.order_limit_sell(**params) def order_market_sell(self, **params): return self.client.order_market_sell(**params) def order_market_buy(self, **params): return self.client.order_market_buy(**params) def get_open_orders(self, **params): return self.client.get_open_orders(**params) def create_test_order(self, **params): self.client.create_test_order() def get_order(self, **params): self.client.get_order(self, **params) def get_all_orders(self, **params): self.client.get_all_orders(self, **params) def cancel_order(self, **params): self.client.cancel_order(self, **params) def get_account(self, **params): return (self.client.get_account(recvWindow=self.get_time())) def get_asset_balance(self, asset, **params): bal = self.client.get_asset_balance(asset=asset, recvWindow=self.get_time()) return ast.literal_eval(json.dumps(bal))['free'] def start_trade(): pass def get_kline_lag_time(self, coin, lookback_in_ms): # lookback = 2*60*1000 #5mins # rt.pred_coin_list=[coin] end_ts = self.get_time() # calendar.timegm(time.gmtime()) -lookback start_ts = end_ts - lookback_in_ms # print("start=",start_ts) # print("end=",end_ts) f = self.get_historical_klines(symbol=coin, interval=Client.KLINE_INTERVAL_30MINUTE, end=end_ts, start=start_ts) f = ast.literal_eval(json.dumps(f)) return f def getState(self, coin_list): features = np.empty((LOOK_BACK, 0), float) coin_f_index = [2, 3, 4, 5, 7] for coin in coin_list: coin_f = np.array(self.get_kline_lag_time(coin, LOOK_BACK_IN_MS), dtype=np.float).reshape(-1, 12) coin_f = coin_f[coin_f.shape[0] - LOOK_BACK:, coin_f_index] if (coin_f.shape[0] < 10): print("something is wrong with binance api,return shape=", coin_f.shape) return #print("coin_f shape ",coin_f.shape) #print("features shape ",features.shape) # COIN_FEATURE = np.concatenate((COIN_FEATURE, tmp), axis=0) features = np.hstack((features, coin_f)) # features = self.create_input(features,LOOK_BACK) # DF_FEATURES #reshape for tensorflow backend features = features.reshape(1, DF_FEATURES, 1, LOOK_BACK) print("create_predictive_input, features shape ", features.shape) # print("kline shape after modify",features.shape) # features = self.create_input(features ,LOOK_BACK) return features
class BinanceOperator: def __init__(self, api_key, secret): self.client = Client(api_key, secret) self.symbol_status = {} # self.base_url = 'https://api.binance.com' # def get_open_orders(self): # endpoint = '/api/v3/openOrders' # ts = int(time.time()*1000) # symbol = 'LTCBTC' # query = urlencode({'symbol': symbol, 'timestamp': ts}) # signature = hmac.new(self.api_secret.encode('utf-8'), query.encode('utf-8'), hashlib.sha256).hexdigest() # params = { # 'symbol': symbol, # 'timestamp': ts, # 'signature': signature, # } # header = {'X-MBX-APIKEY': self.api_key} # url = self.base_url + endpoint # response = requests.get(url, headers=header, params=params) # orders = json.loads(response.text) # return orders # def create_order(self, order): # endpoint = '/api/v3/order/test' # ts = int(time.time()*1000) # symbol = 'LTCBTC' # query = urlencode({'symbol': symbol, 'timestamp': ts}) # signature = hmac.new(self.api_secret.encode('utf-8'), query.encode('utf-8'), hashlib.sha256).hexdigest() # params = { # 'symbol': symbol, # 'timestamp': ts, # 'signature': signature, # } # header = {'X-MBX-APIKEY': self.api_key} # url = self.base_url + endpoint # response = requests.post(url, headers=header, params=params) # orders = json.loads(response.text) # return orders def create_market_buy(self, symbol, total): price = float(self.client.get_symbol_ticker(symbol=symbol)['price']) qty = self.estimate_quantity(symbol, total, price) response = self.client.order_market_buy(symbol=symbol, quantity=qty) self.symbol_status[symbol] = { 'status': 'long', 'quantity': qty, 'price': price } return response def create_market_sell(self, symbol): last_order = list( filter(lambda o: o['side'] == 'BUY', self.client.get_all_orders(symbol=symbol)))[-1] qty = last_order['executedQty'] buy_price = float(last_order['cummulativeQuoteQty']) response = self.client.order_market_sell(symbol=symbol, quantity=qty) # DO PnL estimation and save it if response['status'] == 'FILLED': sell_price = float(response['cummulativeQuoteQty']) profit = round((sell_price - buy_price) / buy_price, 4) logging.info('SOLD {}: {} @ {} - Profit: {}%\n'.format( symbol, qty, sell_price, profit * 10)) self.symbol_status[symbol] = { 'status': 'short', 'quantity': qty, 'price': sell_price } return response def estimate_quantity(self, symbol, total, current_price): info = requests.get('https://www.binance.com/api/v1/exchangeInfo') data = json.loads(info.text) sym = list(filter(lambda x: x['symbol'] == symbol, data['symbols']))[0] lot_info = list( filter(lambda x: x['filterType'] == 'LOT_SIZE', sym['filters']))[0] min_qty = float(lot_info['minQty']) max_qty = float(lot_info['maxQty']) step_size = lot_info['stepSize'] qty = round(total / current_price, 8) if qty >= min_qty: approximator = step_size.find('1') - 1 qty = math.floor(qty * 10**approximator) / float(10**approximator) return qty elif qty > max_qty: return max_qty else: return 0 def buying_allowed(self, symbol): # If symbol not traded yet, add to the symbol status dictionary if symbol not in self.symbol_status: self.symbol_status[symbol] = { 'status': 'short', 'quantity': 0, 'price': 0 } return True if self.symbol_status[symbol][ 'status'] == 'short' else False # COULD ALSO CHECK THAT QUANTITY IS SAME AS LAST BUY (?) def selling_allowed(self, symbol): return True if symbol in self.symbol_status and self.symbol_status[ symbol]['status'] == 'long' else False
def modify(): #Binance API client = Client(api_key, secret_key) balances = client.get_account().get('balances') prices = client.get_all_tickers() trades = [] for pair in pairs: trade = (client.get_all_orders(symbol=pair, limit=1000)) for order in trade: trades.append(order) #Excel Values workbook = openpyxl.load_workbook('trading.xlsx') Home = workbook.get_sheet_by_name('Home') DetailledHome = workbook.get_sheet_by_name('DetailledHome') Wallet = workbook.get_sheet_by_name('Wallet') values = workbook.get_sheet_by_name('Values') #Fill Wallet sheet for i in range(len(balances)): Wallet[f'A{i+2}'] = balances[i].get('asset') Wallet[f'B{i+2}'] = balances[i].get('free') Wallet[f'C{i+2}'] = balances[i].get('locked') Wallet['E1'] = "Last update at " + str( datetime.now().strftime("%H:%M:%S %d/%m/%Y")) #Fill DetailledHome sheet trade_id = [] for i in range(2, 10000): val = DetailledHome[f'B{i}'].value if val != None: trade_id.append(val) for order in trades: if trade_id.count(order.get('orderId')) == 0: DetailledHome[f'A{len(trade_id)+2}'] = order.get('symbol') DetailledHome[f'B{len(trade_id)+2}'] = order.get('orderId') DetailledHome[f'C{len(trade_id)+2}'] = order.get('orderListId') DetailledHome[f'D{len(trade_id)+2}'] = order.get('clientOrderId') DetailledHome[f'E{len(trade_id)+2}'] = order.get('price') DetailledHome[f'F{len(trade_id)+2}'] = order.get('origQty') DetailledHome[f'G{len(trade_id)+2}'] = order.get('executedQty') DetailledHome[f'H{len(trade_id)+2}'] = order.get( 'cummulativeQuoteQty') DetailledHome[f'I{len(trade_id)+2}'] = order.get('status') DetailledHome[f'J{len(trade_id)+2}'] = order.get('timeInForce') DetailledHome[f'K{len(trade_id)+2}'] = order.get('type') DetailledHome[f'L{len(trade_id)+2}'] = order.get('side') DetailledHome[f'M{len(trade_id)+2}'] = order.get('stopPrice') DetailledHome[f'N{len(trade_id)+2}'] = order.get('icebergQty') DetailledHome[f'O{len(trade_id)+2}'] = datetime.fromtimestamp( int(str(order.get('time'))[:-3])) DetailledHome[f'P{len(trade_id)+2}'] = order.get('time') DetailledHome[f'Q{len(trade_id)+2}'] = datetime.fromtimestamp( int(str(order.get('updateTime'))[:-3])) DetailledHome[f'R{len(trade_id)+2}'] = order.get('updateTime') DetailledHome[f'S{len(trade_id)+2}'] = order.get('isWorking') DetailledHome[f'T{len(trade_id)+2}'] = order.get( 'origQuoteOrderQty') trade_id.append(order.get('orderId')) else: index = trade_id.index(order.get('orderId')) DetailledHome[f'A{index+2}'] = order.get('symbol') DetailledHome[f'B{index+2}'] = order.get('orderId') DetailledHome[f'C{index+2}'] = order.get('orderListId') DetailledHome[f'D{index+2}'] = order.get('clientOrderId') DetailledHome[f'E{index+2}'] = order.get('price') DetailledHome[f'F{index+2}'] = order.get('origQty') DetailledHome[f'G{index+2}'] = order.get('executedQty') DetailledHome[f'H{index+2}'] = order.get('cummulativeQuoteQty') DetailledHome[f'I{index+2}'] = order.get('status') DetailledHome[f'J{index+2}'] = order.get('timeInForce') DetailledHome[f'K{index+2}'] = order.get('type') DetailledHome[f'L{index+2}'] = order.get('side') DetailledHome[f'M{index+2}'] = order.get('stopPrice') DetailledHome[f'N{index+2}'] = order.get('icebergQty') DetailledHome[f'O{index+2}'] = datetime.fromtimestamp( int(str(order.get('time'))[:-3])) DetailledHome[f'P{index+2}'] = order.get('time') DetailledHome[f'Q{index+2}'] = datetime.fromtimestamp( int(str(order.get('updateTime'))[:-3])) DetailledHome[f'R{index+2}'] = order.get('updateTime') DetailledHome[f'S{index+2}'] = order.get('isWorking') DetailledHome[f'T{index+2}'] = order.get('origQuoteOrderQty') DetailledHome['V1'] = "Last update at " + str( datetime.now().strftime("%H:%M:%S %d-%m-%Y")) #Feel Home sheet for i in range(len(trade_id)): Home[f'A{i+2}'] = f'=DetailledHome!O{i+2}' Home[f'B{i+2}'] = f'=DetailledHome!A{i+2}' Home[f'C{i+2}'] = f'=DetailledHome!L{i+2}' Home[f'D{i+2}'] = round( float(DetailledHome[f'F{i+2}'].value) * float(DetailledHome[f'E{i+2}'].value), 4) Home[f'E{i+2}'] = f'=DetailledHome!F{i+2}' Home[f'G{i+2}'] = f'=DetailledHome!E{i+2}' Home[f'H{i+2}'] = f'=DetailledHome!I{i+2}' Home['K1'] = '=DetailledHome!V1' #Fill Values sheet for i in range(len(prices)): values[f'A{i+2}'] = prices[i].get('symbol') values[f'B{i+2}'] = prices[i].get('price') values['D1'] = "Last update at " + str( datetime.now().strftime("%H:%M:%S %d-%m-%Y")) #Save Excel try: workbook.save('trading.xlsx') print('Saved !') except: print('Not saved because file is open...') pass
class BinBot(object): def __init__(self): self.client = Client(binance_config.api_key, binance_config.api_secret) self.fee = 0.0005 #if user has BNB tokens self.orderbook = self.get_orderbook_total() self.bit_rate = self.extract_btc_eth_rate(self.orderbook) self.update_orderbook() self.symbol_keys = self.get_keys(self.orderbook) self.current_btc, self.current_eth = self.update_account() def get_orderbook_total(self): """ Returns orderbook from binance package. """ return self.client.get_orderbook_tickers() def update_orderbook(self): """ Updates orderbook by analyzing exchange rates in BTC and removing coins in no_trade in binance_config. """ try: self.orderbook = self.get_orderbook_total() self.bit_rate = self.extract_btc_eth_rate(self.orderbook) self.orderbook = self.modify_orderbook(self.orderbook) except binexc.BinanceAPIException as e: print(e.status_code) print(e.message) time.sleep(10) self.update_orderbook() def update_account(self): """ Returns the amount of BTC or ETH in the account. """ flag = False #If local time compared to Binance NTP server time is offset by +1000ms then API will throw error while (flag == False): try: acc = self.client.get_account( recvWindow=binance_config.recv_window) flag = True except binexc.BinanceAPIException as e: print(str(e.status_code) + " : " + e.message) time.sleep(1) btc_eth = [] for k in acc['balances']: if (k['asset'] == 'BTC' or k['asset'] == 'ETH'): btc_eth.append(k['free']) if (len(btc_eth) == 2): break if (btc_eth[0] >= btc_eth[1]): return btc_eth[0], btc_eth[1] else: return btc_eth[1], btc_eth[0] def alt_asset_amount(self, symbol, symbol_length=3): """ Returns the total amount of an altcoin on the account. Keyword Arguments: symbol -- the symbol of the altcoin e.g. 'NEO' symbol_length -- 'NEO' will have symbol_length of 3 'LINK' will have symbol_length of 4 Work around for the API (default 3) """ acc = self.client.get_account(recvWindow=binance_config.recv_window) for k in acc['balances']: if (k['asset'] == symbol[0, symbol_length]): return k['free'] return "Symbol Not Found" def get_keys(self, orderbook): """ Returns symbols of all coins on Binance """ _keys = [] for k in orderbook: _keys.append(list(k.keys())[0]) return _keys def extract_btc_eth_rate(self, orderbook): """ Returns ETHBTC exchange rate """ for i in range(0, len(orderbook)): if (orderbook[i]['symbol'][0:6] == 'ETHBTC'): odbk = orderbook[i] odbk['btc_one'] = 1 / float(odbk['askPrice']) return orderbook[i] def modify_orderbook(self, orderbook): """ Helper function to modify orderbook to remove trading pairs that are not involved eg BTCUSDT """ ob_sorted = sorted(orderbook, key=lambda k: k['symbol']) ob_sorted = self.del_non_pair_coins(ob_sorted) ob_dict = self.transform_data_list(ob_sorted) return ob_dict def del_non_pair_coins(self, orders): """ Deletes coins that are no longer listed on Binance as well as coins listed on the binance_config.no_trade list """ i = 0 orders_to_return = [] while (i < len(orders) - 1): if (orders[i]['symbol'][0:3] == orders[i + 1]['symbol'][0:3]): if (orders[i]['symbol'] == 'ETC' or orders[i]['symbol'][-4:] == 'USDT' or orders[i]['symbol'][0:4] in binance_config.no_trade or orders[i]['symbol'][0:3] in binance_config.no_trade or orders[i]['symbol'][-3:] == 'BNB'): i += 1 elif (orders[i + 1]['symbol'][-4:] == 'USDT'): i += 1 else: orders_to_return.append(orders[i]) orders_to_return.append(orders[i + 1]) i += 2 else: i += 1 return orders_to_return def transform_data_list(self, orders): """ Transforms data from dictionary into list(BTC=0, ETH=1) """ transform = [] for i in range(0, len(orders), 2): transform.append( {orders[i]['symbol'][0:3]: [orders[i], orders[i + 1]]}) return transform def orderbook_btc_eth(self, orderbook): """ Looks for viable trading pairs in the BTC --> ETH direction """ btc_to_eth = {} btc_pos = 0 eth_pos = 1 for k in orderbook: try: btc_to_eth[list(k.keys())[0]] = 1 / float(k[list( k.keys())[0]][btc_pos]['askPrice']) * float(k[list( k.keys())[0]][eth_pos]['bidPrice']) except ZeroDivisionError as e: print(e) return sorted(btc_to_eth.items(), key=operator.itemgetter(1), reverse=True) def orderbook_eth_btc(self, orderbook): """ Looks for viable trading pairs in the ETH --> BTC direction """ eth_to_btc = {} btc_pos = 0 eth_pos = 1 for k in orderbook: try: eth_to_btc[list(k.keys())[0]] = 1 / float(k[list( k.keys())[0]][eth_pos]['askPrice']) * float(k[list( k.keys())[0]][btc_pos]['bidPrice']) except ZeroDivisionError as e: print(e) return sorted(eth_to_btc.items(), key=operator.itemgetter(1), reverse=True) #Without client validation - no google authenticator #symbol - string, quantity = int, price = string -- must be 0.0002 def order_buy_alt(self, _symbol, _quantity, _price, order_rank, attempt=1): """ Buys an altcoin using Binance package Keyword Arguments: _symbol -- String: Symbol name of trading pair eg NEOBTC _quantity -- Integer: Quantity to buy _price -- String: Price to buy at order_rank -- Integer: The order of buy/sell exection attempt -- Integer: Total of attempts to buy at the price """ try: self.client.create_order(symbol=_symbol, side=enum.SIDE_BUY, type=enum.ORDER_TYPE_LIMIT, timeInForce=enum.TIME_IN_FORCE_GTC, quantity=_quantity, price=_price, disable_validation=True) return True except binexc.BinanceAPIException as e: print(e.status_code, e.message, " | order_buy_alt") return False def order_sell_alt(self, _symbol, _quantity, _price, order_rank, attempt=1): """ Sells an altcoin using Binance package Keyword Arguments: _symbol -- String: Symbol name of trading pair eg NEOBTC _quantity -- Integer: Quantity to buy _price -- String: Price to buy at order_rank -- Integer: The order of buy/sell exection attempt -- Integer: Total of attempts to buy at the price """ try: self.client.create_order(symbol=_symbol, side=enum.SIDE_SELL, type=enum.ORDER_TYPE_LIMIT, timeInForce=enum.TIME_IN_FORCE_GTC, quantity=_quantity, price=_price, disable_validation=True) except binexc.BinanceAPIException as e: #print(e.message, e.status_code, " | order_sell_alt") if (order_rank is 4 or order_rank is 2): if (attempt <= 25): attempt += 1 self.order_sell_alt(_symbol, _quantity, _price, order_rank, attempt) time.sleep(0.02) else: print("Went to market price | order_sell_alt", e.message, e.status_code) self.order_sell_market(_symbol, _quantity) def order_sell_market(self, _symbol, _quantity): """Sells coins into either ETH or BTC at MARKET VALUE. Use for upgrades for flexibility of binbot Say the order you originally placed was bought before your order arrived on the binance servers. This will immediately sell your coins and depending on the fluctuation of the market could still result in a (most likely milder) profit. """ try: self.crypto_storage.client.create_order( symbol=_symbol, side=enum.SIDE_SELL, type=enum.ORDER_TYPE_MARKET, quantity=_quantity) except binexc.BinanceAPIException as e: print(e.status_code) print(e.message) self.order_sell_market(_symbol, _quantity) def test_order_buy_alt(self, _symbol, _quantity, _price): """ FOR TESTING BINBOT BEFORE YOU LOSE MONEY Important to test if your orders will execute in time. Buys an altcoin using Binance package Keyword Arguments: _symbol -- String: Symbol name of trading pair eg NEOBTC _quantity -- Integer: Quantity to buy _price -- String: Price to buy at order_rank -- Integer: The order of buy/sell exection attempt -- Integer: Total of attempts to buy at the price """ try: self.client.create_test_order(symbol=_symbol, side=enum.SIDE_BUY, type=enum.ORDER_TYPE_LIMIT, timeInForce=enum.TIME_IN_FORCE_GTC, quantity=_quantity, price=_price, disable_validation=True) except binexc.BinanceAPIException as e: print(e.status_code) print(e.message) def test_order_sell_alt(self, _symbol, _quantity, _price): """ FOR TESTING BINBOT BEFORE YOU LOSE MONEY Important to test if your orders will execute in time. Sells an altcoin using Binance package Keyword Arguments: _symbol -- String: Symbol name of trading pair eg NEOBTC _quantity -- Integer: Quantity to buy _price -- String: Price to buy at order_rank -- Integer: The order of buy/sell exection attempt -- Integer: Total of attempts to buy at the price """ try: self.client.create_test_order(symbol=_symbol, side=enum.SIDE_SELL, type=enum.ORDER_TYPE_LIMIT, timeInForce=enum.TIME_IN_FORCE_GTC, quantity=_quantity, price=_price, disable_validation=True) except binexc.BinanceAPIException as e: print(e.status_code) print(e.message) def hunt(self, trials=10000, sleep_time=0.1): """ This is the main function of BinBot. This function will search for arbitrage opportunities and execute orders if it finds an inefficient pair. Keyword Arguments: trials -- Integer: how many loops the bot will run (default 10,000) sleep_time -- Float: The Binance API (since I last checked) will only allow you to access it 18 times per second. So need a sleep time to avoid this problem. Remember the longer the sleep time the less likely your arbitrage oppurtunity will still be available. (default 0.1) """ num_runs = 0 pre_arbitrage_assets = self.load_arbitrage_assets() time.sleep(sleep_time) while (num_runs < trials): try: self.update_orderbook() except ConnectionError as e: print(e + "will suspend bot for 10 seconds") time.sleep(10) continue #Search for inefficiency orderbook_btc = self.orderbook_btc_eth(self.orderbook) orderbook_eth = self.orderbook_eth_btc(self.orderbook) if (orderbook_btc[0][1] - (self.fee * orderbook_btc[0][1]) > self.bit_rate['btc_one'] and orderbook_eth[0][1] - (self.fee * orderbook_eth[0][1]) > float(self.bit_rate['askPrice'])): #print('found' + orderbook_btc[0][0] + orderbook_eth[0][0] + str(num_runs)) num_runs += 1 purchase = [] for k in self.orderbook: if (list(k.keys())[0] == orderbook_btc[0][0]): purchase.insert(0, k) if (list(k.keys())[0] == orderbook_eth[0][0]): purchase.insert(1, k) btc_limit = binance_config.btc_trade_limit while (btc_limit > 0.001): if (self.determine_feasibility( orderbook_btc[0][0], orderbook_eth[0][0], purchase, btc_limit) is True): self.execute_trade(orderbook_btc[0][0], orderbook_eth[0][0], purchase, btc_limit) break else: btc_limit = btc_limit - 0.001 num_runs += 1 if (num_runs % 100 == 0): print(str(num_runs)) post_arbitrage_assets = self.load_arbitrage_assets() #Print results time_delta = datetime.datetime.now().replace( microsecond=0) - pre_arbitrage_assets['datetime'] print('Initial: BTC:', pre_arbitrage_assets['BTC'], 'ETH:', pre_arbitrage_assets['ETH'], 'BNB:', pre_arbitrage_assets['BNB']) print('After__: BTC:', post_arbitrage_assets['BTC'], 'ETH:', post_arbitrage_assets['ETH'], 'BNB:', post_arbitrage_assets['BNB']) print( 'Diff___: BTC:', float(post_arbitrage_assets['BTC']) - float(pre_arbitrage_assets['BTC']), 'ETH:', float(post_arbitrage_assets['ETH']) - float(pre_arbitrage_assets['ETH']), 'BNB:', float(post_arbitrage_assets['BNB']) - float(pre_arbitrage_assets['BNB']), 'TIME:', divmod(time_delta.total_seconds(), 60)) def determine_feasibility(self, btc_sym, eth_sym, purchase, btc_trade_limit): """ Determines if an arbitrage opportunity is profitable. """ if (btc_trade_limit / float(purchase[0][btc_sym][0]['askPrice']) <= float(purchase[0][btc_sym][0]['askQty']) and btc_trade_limit / float(purchase[0][btc_sym][0]['askPrice']) <= float(purchase[0][btc_sym][1]['bidQty'])): eth_capital = (btc_trade_limit / float(purchase[0][btc_sym][0]['askPrice'])) * float( purchase[0][btc_sym][1]['bidPrice']) if (eth_capital / float(purchase[1][eth_sym][1]['askPrice']) <= float(purchase[1][eth_sym][1]['askQty']) and eth_capital / float(purchase[1][eth_sym][1]['askPrice']) <= float(purchase[1][eth_sym][0]['bidQty'])): #and eth_capital / float(purchase[1][eth_sym][1]['askPrice']) >= 1): return True else: return False else: return False def execute_trade(self, btc_sym, eth_sym, purchase, btc_trade_limit): """ Executes trade in BTC-->ALT_1-->ETH-->ALT_2-->BTC order. Side note: Making threads will improve likelihood of success. This implementation is inefficient. """ amount_btc = math.floor(btc_trade_limit / float(purchase[0][btc_sym][0]['askPrice'])) eth_capital = (btc_trade_limit / float(purchase[0][btc_sym][0]['askPrice'])) * float( purchase[0][btc_sym][1]['bidPrice']) amount_eth = math.floor(eth_capital / float(purchase[1][eth_sym][1]['askPrice'])) if (amount_btc * float(purchase[0][btc_sym][0]['askPrice']) > 0.001 and amount_eth * float(purchase[1][eth_sym][0]['bidPrice']) > 0.001): if self.order_buy_alt( purchase[0][btc_sym][0]['symbol'], amount_btc, purchase[0][btc_sym][0]['askPrice'], 1) is True: print("1: " + purchase[0][btc_sym][0]['symbol'] + " " + str(amount_btc) + " " + purchase[0][btc_sym][0]['askPrice']) self.order_sell_alt(purchase[0][btc_sym][1]['symbol'], amount_btc, purchase[0][btc_sym][1]['bidPrice'], 2) print("2: " + purchase[0][btc_sym][1]['symbol'] + " " + str(amount_btc) + " " + purchase[0][btc_sym][1]['bidPrice']) if self.order_buy_alt( purchase[1][eth_sym][1]['symbol'], amount_eth, purchase[1][eth_sym][1]['askPrice'], 3) is True: print("3: " + purchase[1][eth_sym][1]['symbol'] + " " + str(amount_eth) + " " + purchase[1][eth_sym][1]['askPrice']) self.order_sell_alt(purchase[1][eth_sym][0]['symbol'], amount_eth, purchase[1][eth_sym][0]['bidPrice'], 4) print("4: " + purchase[1][eth_sym][0]['symbol'] + " " + str(amount_eth) + " " + purchase[1][eth_sym][0]['bidPrice']) self.remove_any_open_orders([ purchase[0][btc_sym][0]['symbol'], purchase[0][btc_sym][1]['symbol'], purchase[1][eth_sym][1]['symbol'], purchase[1][eth_sym][0]['symbol'] ]) def remove_any_open_orders(self, poss_orders=[]): """ For upgrading flexibility of BinBot. Removes any open orders. """ for order in poss_orders: open_orders = self.get_open_orders_symbol(_symbol=order) if len(open_orders) is not 0: self.order_sell_market(_symbol=open_orders[0]['symbol'], _quantity=open_orders[0]['origQty']) def check_server_time_difference(self): """ Checks the amount of time it takes for your packets to reach Binance servers and return to your computer. VERY IMPORTANT: If your packets take too long, your trades will not execute """ for i in range(0, 10): local_time_one = int(time.time() * 1000) server_time = self.client.get_server_time() diff_one = server_time['serverTime'] - local_time_one local_time_two = int(time.time() * 1000) diff_two = local_time_two - server_time['serverTime'] print("local1: %s server: %s local2: %s diff1: %s diff2: %s" % (local_time_one, server_time['serverTime'], local_time_two, diff_one, diff_two)) time.sleep(2) def get_specific_symbol(self, coin_sym='ETH', trade_currency='BTC'): """Returns a specific trading pair to see what price it is at in the orderbook. If no parameters are given, it will return the price of ETHBTC. Keyword Arguments: coin_sym -- This is the first symbol in the trading pair (default 'ETH') trade_currency -- This is the second symbol in the trading pair (default 'BTC') """ if trade_currency != 'BTC' and trade_currency != 'ETH' and trade_currency != 'BNB': print('Trade currency can only be BTC or ETH') return {} trade_pair = coin_sym + trade_currency for k in self.orderbook: if list(k.keys())[0] == coin_sym[0:3]: for pair in k[list(k.keys())[0]]: if pair['symbol'] == trade_pair: return pair print('Pair not in orderbook.') return {} def get_orderbook_symbol(self, _symbol='ETHBTC', _limit=10): """ Returns the orderbook (buy/sell prices) of a given symbol. Limit will hold the orderbook to only 10 prices of the buy/sell pair Returns: Dictionary of prices """ return self.client.get_order_book(symbol=_symbol, limit=_limit) def get_open_order_symbol(self, _symbol='ETHBTC'): """Get all open orders of a symbol """ try: return self.client.get_open_orders(symbol=_symbol) except binexc.BinanceAPIException as e: print(e.message) self.get_open_orders_symbol(_symbol) def get_past_orders(self, _symbol='ETHBTC'): """ Get the user's past orders. """ return self.client.get_all_orders(symbol=_symbol) def load_arbitrage_assets(self): """ Loads the amount of coins in the user's account """ flag = False btc_eth_bnb = {} while (flag is False): try: acc = self.client.get_account( recvWindow=binance_config.recv_window) flag = True except binexc.BinanceAPIException as e: print(str(e.status_code) + " : " + e.message) time.sleep(1) #acc['balances'] for cryptoasset in acc['balances']: if cryptoasset['asset'] == 'BTC': btc_eth_bnb['BTC'] = cryptoasset['free'] if cryptoasset['asset'] == 'ETH': btc_eth_bnb['ETH'] = cryptoasset['free'] if cryptoasset['asset'] == 'BNB': btc_eth_bnb['BNB'] = cryptoasset['free'] if len(btc_eth_bnb) is 3: break btc_eth_bnb['datetime'] = datetime.datetime.now().replace( microsecond=0) return btc_eth_bnb
elif count == 2: counter = 4 elif count == 3: counter = 3 elif count == 4: counter = 2 elif count == 5: counter = 1 elif count > 6: counter = 0 ## get completed orders and print index = 1 if count < 7: print("\033[1;36;40m" + 'completed ============ =========' + "\033[1;37;40m") all_orders = client.get_all_orders(symbol='BTCAUD', limit=100) for a in sorted(all_orders, reverse=True, key=lambda i: i['orderId']): if a['status'] == 'FILLED': aud = float(a['origQty']) * float(a['price']) print(' ' + str(index) + ': ' + "\033[1;32;40m" + str("${:,.2f}".format(aud)).rjust(7, ' ') + "\033[1;37;40m" + ' @ ' + "\033[1;35;40m" + str("${:,.0f}".format(float(a['price']))) + "\033[1;37;40m" + ' ' + "\033[1;37;44m" + str(a['status'])[0:7].center(9, ' ') + "\033[1;37;40m") index += 1 counter -= 1 elif a['status'] == 'PARTIALLY_FILLED': aud = float(a['origQty']) * float(a['price']) print(' ' + str(index) + ': ' + "\033[1;32;40m" + str("${:,.2f}".format(aud)).rjust(7, ' ') + "\033[1;37;40m" + ' @ ' + "\033[1;35;40m" + str("${:,.0f}".format(float(a['price']))) + "\033[1;37;40m" + ' ' + "\033[1;33;40m" + str(a['status'])[0:7].center(9, ' ') + "\033[1;37;40m") index += 1 counter -= 1 if counter == -1: break
class ApiClient: def __init__(self): self._client = None self._lock = threading.Lock() self._orders_since_run = [] def connect(self): self._client = Client(API_KEY, API_SECRET) def get_raw_client(self): return self._client def get_asset_balance(self, asset): if self._client is None: print("client is undefined") return return self._client.get_asset_balance(asset) def get_balance(self): balance = self._client.get_account() my_balance = list( filter( lambda coin: float(coin['locked']) != 0 or float(coin['free']) != 0, balance['balances'])) return my_balance def get_current_price(self, symbol): return float(self._client.get_symbol_ticker(symbol=symbol)['price']) def get_candles(self, symbol='ETHUSDT', interval='1m', limit=500): klines = self._client.get_klines(symbol=symbol, interval=interval, limit=limit) return [ BinanceCandleStick(a[1], a[2], a[3], a[4], a[5], a[8]) for a in klines ] def get_candles_dataframe(self, symbol='ETHUSDT', interval='1m', limit=500): klines = self._client.get_klines(symbol=symbol, interval=interval, limit=limit) arr = [] for candle in klines: obj = { 'Open': float(candle[1]), 'High': float(candle[2]), 'Low': float(candle[3]), 'Close': float(candle[4]), 'Volume': candle[5], 'Number Of Trades': candle[8] } arr.append(obj) return pd.DataFrame(arr) def get_close_prices_dataframe(self, symbol='ETHUSDT', interval='1m', limit=500): with self._lock: klines = self._client.get_klines(symbol=symbol, interval=interval, limit=limit) close_arr = [float(a[4]) for a in klines] np_arr = np.array(close_arr) data = {'Close': pd.Series(np_arr)} return pd.DataFrame(data) def get_all_orders(self, symbol='ETHUSDT'): return self._client.get_all_orders(symbol=symbol) def verify_limit_order(self, symbol, order_id): print(f"Verifying order ID {order_id}") order_recorded = False order_status = None while not order_recorded: try: time.sleep(2) order_status = self._client.get_order(symbol=symbol, orderId=order_id) except BinanceAPIException as e: print(f"get_order failed: {e}") time.sleep(5) except Exception as e: print(f"Unexpected error: {e}") return False if order_status is None: print("Order status is None!") return False while order_status['status'] != 'FILLED': try: order_status = self._client.get_order(symbol=symbol, orderId=order_id) time.sleep(1) except BinanceAPIException as e: print(e) time.sleep(2) except Exception as e: print(f"Unexpected error: {e}") return False return True def _execute_market_buy(self, symbol, quantity): api_res = None try: api_res = self._client.order_market_buy(symbol=symbol, quantity=quantity) print(f"API result: {api_res}") except BinanceAPIException as e: print(f"Binance error: {e} - Error code: - {e.code}") if e.code == -2010: # Account has insufficient balance for requested action print("No money.") return False except Exception as e: print(f"Unexpected error: {e}") return api_res def _execute_market_sell(self, symbol, quantity): api_res = None try: api_res = self._client.order_market_sell(symbol=symbol, quantity=quantity) print(f"API result: {api_res}") except BinanceAPIException as e: print(f"Binance error: {e} - Error code: - {e.code}") if e.code == -2010: # Account has insufficient balance for requested action print("No money.") return False except Exception as e: print(f"Unexpected error: {e}") return api_res def market_sell(self, symbol=None, quantity=None): if symbol is None or quantity is None: raise ValueError( "You MUST pass symbol and quantity. Be careful with this method!" ) current_balance = self.get_asset_balance( asset=symbol.replace('USDT', ''))['free'] print(f"Starting balance: {current_balance} {symbol}") if float(current_balance) < quantity: print( f"Cannot sell {quantity} when there is only {current_balance} available" ) return False api_res = False for i in range(5): api_res = self._execute_market_sell(symbol, quantity) if api_res: break else: print(f"Market sell failed. Attempt number {i+1}") if not api_res: print("Error sending market sell") return False print( f"Sell order for {quantity} {symbol} filled. Order ID: {api_res['orderId']}" ) after_sell_balance = self.get_asset_balance( asset=symbol.replace('USDT', ''))['free'] print(f"Balance after sell order: {after_sell_balance} {symbol}") if isclose(float(current_balance) - quantity, float(after_sell_balance), abs_tol=1): print("Market sell order success") return api_res def market_buy(self, symbol=None, quantity=None): if symbol is None or quantity is None: raise ValueError( "You MUST pass symbol and quantity. Be careful with this method!" ) current_balance = self.get_asset_balance( asset=symbol.replace('USDT', ''))['free'] print(f"Starting balance: {current_balance} {symbol}") api_res = False for i in range(5): api_res = self._execute_market_buy(symbol, quantity) if api_res: break else: print(f"Market buy failed. Attempt number {i+1}") if not api_res: print("Error sending market buy") return False # print("Verifying market buy order") # order_verified = self.verify_order(symbol, api_res['orderId']) # if not order_verified: # print(f"Order {api_res['orderId']} verification failed") # else: # print(f"Buy order for {quantity} {symbol} filled. Order ID: {api_res['orderId']}") print( f"Buy order for {quantity} {symbol} filled. Order ID: {api_res['orderId']}" ) after_buy_balance = self.get_asset_balance( asset=symbol.replace('USDT', ''))['free'] print(f"Balance after buy order: {after_buy_balance} {symbol}") if isclose(float(current_balance) + quantity, float(after_buy_balance), abs_tol=1): print("Market buy order success") return api_res
quantity=0.01, price=25000 )""" """price = float(order['fills'][0]['price']) pprint.pprint(price)""" print("************************************************") allcoins_info = client.get_all_tickers() """for coin in allcoins_info: if coin['symbol'] == 'XRPBUSD': our_price = coin['price']""" #print(our_price) print("**********************************************") orders = client.get_all_orders(symbol='BTCBUSD') order = client.get_order(symbol='XRPBUSD', orderId='42755') #delete order """result = client.cancel_order( symbol='BTCBUSD', orderId='1787')""" #open_orders = client.get_open_orders(symbol='BTCBUSD') #account_snapshot = client.get_account_snapshot(type='SPOT') """for filter in symbol_info['filters']: if filter['filterType'] == 'PERCENT_PRICE': multiplierDown = float(filter['multiplierDown']) pprint.pprint(multiplierDown) multiplierUp = float(filter['multiplierUp']) pprint.pprint(multiplierUp)"""
class TradingAccount(): def __init__(self, app={}): """Trading account object model Parameters ---------- app : object PyCryptoBot object """ # config needs to be a dictionary, empty or otherwise if not isinstance(app, object): raise TypeError('App is not a PyCryptoBot object.') if app.getExchange() == 'binance': self.client = Client(app.getAPIKey(), app.getAPISecret(), { 'verify': False, 'timeout': 20 }) # if trading account is for testing it will be instantiated with a balance of 1000 self.balance = pd.DataFrame( [['QUOTE', 1000, 0, 1000], ['BASE', 0, 0, 0]], columns=['currency', 'balance', 'hold', 'available']) self.app = app if app.isLive() == 1: self.mode = 'live' else: self.mode = 'test' self.orders = pd.DataFrame() def __convertStatus(self, val): if val == 'filled': return 'done' else: return val def _checkMarketSyntax(self, market): """Check that the market is syntactically correct Parameters ---------- market : str market to check """ if self.app.getExchange() == 'coinbasepro' and market != '': p = re.compile(r"^[1-9A-Z]{2,5}\-[1-9A-Z]{2,5}$") if not p.match(market): raise TypeError('Coinbase Pro market is invalid.') elif self.app.getExchange() == 'binance': p = re.compile(r"^[A-Z]{6,12}$") if not p.match(market): raise TypeError('Binance market is invalid.') def getOrders(self, market='', action='', status='all'): """Retrieves orders either live or simulation Parameters ---------- market : str, optional Filters orders by market action : str, optional Filters orders by action status : str Filters orders by status, defaults to 'all' """ # validate market is syntactically correct self._checkMarketSyntax(market) if action != '': # validate action is either a buy or sell if not action in ['buy', 'sell']: raise ValueError('Invalid order action.') # validate status is open, pending, done, active or all if not status in [ 'open', 'pending', 'done', 'active', 'all', 'filled' ]: raise ValueError('Invalid order status.') if self.app.getExchange() == 'binance': if self.mode == 'live': resp = self.client.get_all_orders(symbol=market) if len(resp) > 0: df = pd.DataFrame(resp) else: df = pd.DataFrame() if len(df) == 0: return pd.DataFrame() df = df[[ 'time', 'symbol', 'side', 'type', 'executedQty', 'cummulativeQuoteQty', 'status' ]] df.columns = [ 'created_at', 'market', 'action', 'type', 'size', 'value', 'status' ] df['created_at'] = df['created_at'].apply( lambda x: int(str(x)[:10])) df['created_at'] = df['created_at'].astype("datetime64[s]") df['size'] = df['size'].astype(float) df['value'] = df['value'].astype(float) df['action'] = df['action'].str.lower() df['type'] = df['type'].str.lower() df['status'] = df['status'].str.lower() df['price'] = df['value'] / df['size'] # pylint: disable=unused-variable for k, v in df.items(): if k == 'status': df[k] = df[k].map(self.__convertStatus) if action != '': df = df[df['action'] == action] df = df.reset_index(drop=True) if status != 'all' and status != '': df = df[df['status'] == status] df = df.reset_index(drop=True) return df else: # return dummy orders if market == '': return self.orders else: if (len(self.orders) > 0): return self.orders[self.orders['market'] == market] else: return pd.DataFrame() if self.app.getExchange() == 'coinbasepro': if self.mode == 'live': # if config is provided and live connect to Coinbase Pro account portfolio model = CBAuthAPI(self.app.getAPIKey(), self.app.getAPISecret(), self.app.getAPIPassphrase(), self.app.getAPIURL()) # retrieve orders from live Coinbase Pro account portfolio self.orders = model.getOrders(market, action, status) return self.orders else: # return dummy orders if market == '': return self.orders else: return self.orders[self.orders['market'] == market] def getBalance(self, currency=''): """Retrieves balance either live or simulation Parameters ---------- currency: str, optional Filters orders by currency """ if self.app.getExchange() == 'binance': if self.mode == 'live': resp = self.client.get_account() if 'balances' in resp: df = pd.DataFrame(resp['balances']) df = df[(df['free'] != '0.00000000') & (df['free'] != '0.00')] df['free'] = df['free'].astype(float) df['locked'] = df['locked'].astype(float) df['balance'] = df['free'] - df['locked'] df.columns = ['currency', 'available', 'hold', 'balance'] df = df[['currency', 'balance', 'hold', 'available']] df = df.reset_index(drop=True) if currency == '': # retrieve all balances return df else: # retrieve balance of specified currency df_filtered = df[df['currency'] == currency]['available'] if len(df_filtered) == 0: # return nil balance if no positive balance was found return 0.0 else: # return balance of specified currency (if positive) if currency in ['EUR', 'GBP', 'USD']: return float( self.app.truncate( float(df[df['currency'] == currency] ['available'].values[0]), 2)) else: return float( self.app.truncate( float(df[df['currency'] == currency] ['available'].values[0]), 4)) else: return 0.0 else: # return dummy balances if currency == '': # retrieve all balances return self.balance else: if self.app.getExchange() == 'binance': self.balance = self.balance.replace('QUOTE', currency) else: # replace QUOTE and BASE placeholders if currency in ['EUR', 'GBP', 'USD']: self.balance = self.balance.replace( 'QUOTE', currency) else: self.balance = self.balance.replace( 'BASE', currency) if self.balance.currency[self.balance.currency.isin( [currency])].empty == True: self.balance.loc[len( self.balance)] = [currency, 0, 0, 0] # retrieve balance of specified currency df = self.balance df_filtered = df[df['currency'] == currency]['available'] if len(df_filtered) == 0: # return nil balance if no positive balance was found return 0.0 else: # return balance of specified currency (if positive) if currency in ['EUR', 'GBP', 'USD']: return float( self.app.truncate( float(df[df['currency'] == currency] ['available'].values[0]), 2)) else: return float( self.app.truncate( float(df[df['currency'] == currency] ['available'].values[0]), 4)) else: if self.mode == 'live': # if config is provided and live connect to Coinbase Pro account portfolio model = CBAuthAPI(self.app.getAPIKey(), self.app.getAPISecret(), self.app.getAPIPassphrase(), self.app.getAPIURL()) if currency == '': # retrieve all balances return model.getAccounts()[[ 'currency', 'balance', 'hold', 'available' ]] else: df = model.getAccounts() # retrieve balance of specified currency df_filtered = df[df['currency'] == currency]['available'] if len(df_filtered) == 0: # return nil balance if no positive balance was found return 0.0 else: # return balance of specified currency (if positive) if currency in ['EUR', 'GBP', 'USD']: return float( self.app.truncate( float(df[df['currency'] == currency] ['available'].values[0]), 2)) else: return float( self.app.truncate( float(df[df['currency'] == currency] ['available'].values[0]), 4)) else: # return dummy balances if currency == '': # retrieve all balances return self.balance else: # replace QUOTE and BASE placeholders if currency in ['EUR', 'GBP', 'USD']: self.balance = self.balance.replace('QUOTE', currency) elif currency in ['BCH', 'BTC', 'ETH', 'LTC', 'XLM']: self.balance = self.balance.replace('BASE', currency) if self.balance.currency[self.balance.currency.isin( [currency])].empty == True: self.balance.loc[len( self.balance)] = [currency, 0, 0, 0] # retrieve balance of specified currency df = self.balance df_filtered = df[df['currency'] == currency]['available'] if len(df_filtered) == 0: # return nil balance if no positive balance was found return 0.0 else: # return balance of specified currency (if positive) if currency in ['EUR', 'GBP', 'USD']: return float( self.app.truncate( float(df[df['currency'] == currency] ['available'].values[0]), 2)) else: return float( self.app.truncate( float(df[df['currency'] == currency] ['available'].values[0]), 4)) def saveTrackerCSV(self, market='', save_file='tracker.csv'): """Saves order tracker to CSV Parameters ---------- market : str, optional Filters orders by market save_file : str Output CSV file """ # validate market is syntactically correct self._checkMarketSyntax(market) if self.mode == 'live': if self.app.getExchange() == 'coinbasepro': # retrieve orders from live Coinbase Pro account portfolio df = self.getOrders(market, '', 'done') elif self.app.getExchange() == 'binance': # retrieve orders from live Binance account portfolio df = self.getOrders(market, '', 'done') else: df = pd.DataFrame() else: # return dummy orders if market == '': df = self.orders else: if 'market' in self.orders: df = self.orders[self.orders['market'] == market] else: df = pd.DataFrame() if list(df.keys()) != [ 'created_at', 'market', 'action', 'type', 'size', 'value', 'fees', 'price', 'status' ]: # no data, return early return False df_tracker = pd.DataFrame() last_action = '' for market in df['market'].sort_values().unique(): df_market = df[df['market'] == market] df_buy = pd.DataFrame() df_sell = pd.DataFrame() pair = 0 # pylint: disable=unused-variable for index, row in df_market.iterrows(): if row['action'] == 'buy': pair = 1 if pair == 1 and (row['action'] != last_action): if row['action'] == 'buy': df_buy = row elif row['action'] == 'sell': df_sell = row if row['action'] == 'sell' and len(df_buy) != 0: df_pair = pd.DataFrame( [[ df_sell['status'], df_buy['market'], df_buy['created_at'], df_buy['type'], df_buy['size'], df_buy['value'], df_buy['fees'], df_buy['price'], df_sell['created_at'], df_sell['type'], df_sell['size'], df_sell['value'], df_sell['fees'], df_sell['price'] ]], columns=[ 'status', 'market', 'buy_at', 'buy_type', 'buy_size', 'buy_value', 'buy_fees', 'buy_price', 'sell_at', 'sell_type', 'sell_size', 'sell_value', 'sell_fees', 'sell_price' ]) df_tracker = df_tracker.append(df_pair, ignore_index=True) pair = 0 last_action = row['action'] if list(df_tracker.keys()) != [ 'status', 'market', 'buy_at', 'buy_type', 'buy_size', 'buy_value', 'buy_fees', 'buy_price', 'sell_at', 'sell_type', 'sell_size', 'sell_value', 'sell_fees', 'sell_price' ]: # no data, return early return False df_tracker['profit'] = np.subtract( np.subtract(df_tracker['sell_value'], df_tracker['buy_value']), np.add(df_tracker['buy_fees'], df_tracker['sell_fees'])) df_tracker['margin'] = np.multiply( np.true_divide(df_tracker['profit'], df_tracker['buy_value']), 100) df_sincebot = df_tracker[df_tracker['buy_at'] > '2021-02-1'] try: df_sincebot.to_csv(save_file, index=False) except OSError: raise SystemExit('Unable to save: ', save_file) def buy(self, cryptoMarket, fiatMarket, fiatAmount=0, manualPrice=0.00000000): """Places a buy order either live or simulation Parameters ---------- cryptoMarket: str Crypto market you wish to purchase fiatMarket, str QUOTE market funding the purchase fiatAmount, float QUOTE amount of crypto currency to purchase manualPrice, float Used for simulations specifying the live price to purchase """ # fiat funding amount must be an integer or float if not isinstance(fiatAmount, float) and not isinstance( fiatAmount, int): raise TypeError('QUOTE amount not numeric.') # fiat funding amount must be positive if fiatAmount <= 0: raise Exception('Invalid QUOTE amount.') if self.app.getExchange() == 'binance': # validate crypto market is syntactically correct p = re.compile(r"^[A-Z]{3,8}$") if not p.match(cryptoMarket): raise TypeError('Binance crypto market is invalid.') # validate fiat market is syntactically correct p = re.compile(r"^[A-Z]{3,8}$") if not p.match(fiatMarket): raise TypeError('Binance fiat market is invalid.') else: # crypto market should be either BCH, BTC, ETH, LTC or XLM if cryptoMarket not in ['BCH', 'BTC', 'ETH', 'LTC', 'XLM']: raise Exception( 'Invalid crypto market: BCH, BTC, ETH, LTC, ETH, or XLM') # fiat market should be either EUR, GBP, or USD if fiatMarket not in ['EUR', 'GBP', 'USD']: raise Exception('Invalid QUOTE market: EUR, GBP, USD') # reconstruct the exchange market using crypto and fiat inputs if self.app.getExchange() == 'binance': market = cryptoMarket + fiatMarket else: market = cryptoMarket + '-' + fiatMarket if self.app.getExchange() == 'binance': if self.mode == 'live': # execute a live market buy resp = self.client.order_market_buy(symbol=market, quantity=fiatAmount) # TODO: not finished print(resp) else: # fiat amount should exceed balance if fiatAmount > self.getBalance(fiatMarket): raise Exception('Insufficient funds.') # manual price must be an integer or float if not isinstance(manualPrice, float) and not isinstance( manualPrice, int): raise TypeError('Optional manual price not numeric.') price = manualPrice # if manualPrice is non-positive retrieve the current live price if manualPrice <= 0: if self.app.getExchange() == 'binance': api = BPublicAPI() price = api.getTicker(market) else: resp = requests.get( 'https://api-public.sandbox.pro.coinbase.com/products/' + market + '/ticker') if resp.status_code != 200: raise Exception( 'GET /products/' + market + '/ticker {}'.format(resp.status_code)) resp.raise_for_status() json = resp.json() price = float(json['price']) # calculate purchase fees fee = fiatAmount * 0.005 fiatAmountMinusFee = fiatAmount - fee total = float(fiatAmountMinusFee / float(price)) # append dummy order into orders dataframe ts = pd.Timestamp.now() price = (fiatAmountMinusFee * 100) / (total * 100) order = pd.DataFrame([[ '', market, 'buy', 'market', float('{:.8f}'.format(total)), fiatAmountMinusFee, 'done', '{:.8f}'.format(float(price)) ]], columns=[ 'created_at', 'market', 'action', 'type', 'size', 'value', 'status', 'price' ], index=[ts]) order['created_at'] = order.index self.orders = pd.concat( [self.orders, pd.DataFrame(order)], ignore_index=False) # update the dummy fiat balance self.balance.loc[ self.balance['currency'] == fiatMarket, 'balance'] = self.getBalance(fiatMarket) - fiatAmount self.balance.loc[ self.balance['currency'] == fiatMarket, 'available'] = self.getBalance(fiatMarket) - fiatAmount # update the dummy crypto balance self.balance.loc[self.balance['currency'] == cryptoMarket, 'balance'] = self.getBalance(cryptoMarket) + ( fiatAmountMinusFee / price) self.balance.loc[ self.balance['currency'] == cryptoMarket, 'available'] = self.getBalance(cryptoMarket) + ( fiatAmountMinusFee / price) else: if self.mode == 'live': # connect to coinbase pro api (authenticated) model = CBAuthAPI(self.app.getAPIKey(), self.app.getAPISecret(), self.app.getAPIPassphrase(), self.app.getAPIURL()) # execute a live market buy if fiatAmount > 0: resp = model.marketBuy(market, fiatAmount) else: resp = model.marketBuy(market, float(self.getBalance(fiatMarket))) # TODO: not finished print(resp) else: # fiat amount should exceed balance if fiatAmount > self.getBalance(fiatMarket): raise Exception('Insufficient funds.') # manual price must be an integer or float if not isinstance(manualPrice, float) and not isinstance( manualPrice, int): raise TypeError('Optional manual price not numeric.') price = manualPrice # if manualPrice is non-positive retrieve the current live price if manualPrice <= 0: resp = requests.get( 'https://api-public.sandbox.pro.coinbase.com/products/' + market + '/ticker') if resp.status_code != 200: raise Exception('GET /products/' + market + '/ticker {}'.format(resp.status_code)) resp.raise_for_status() json = resp.json() price = float(json['price']) # calculate purchase fees fee = fiatAmount * 0.005 fiatAmountMinusFee = fiatAmount - fee total = float(fiatAmountMinusFee / price) # append dummy order into orders dataframe ts = pd.Timestamp.now() price = (fiatAmountMinusFee * 100) / (total * 100) order = pd.DataFrame([[ '', market, 'buy', 'market', float('{:.8f}'.format(total)), fiatAmountMinusFee, 'done', price ]], columns=[ 'created_at', 'market', 'action', 'type', 'size', 'value', 'status', 'price' ], index=[ts]) order['created_at'] = order.index self.orders = pd.concat( [self.orders, pd.DataFrame(order)], ignore_index=False) # update the dummy fiat balance self.balance.loc[ self.balance['currency'] == fiatMarket, 'balance'] = self.getBalance(fiatMarket) - fiatAmount self.balance.loc[ self.balance['currency'] == fiatMarket, 'available'] = self.getBalance(fiatMarket) - fiatAmount # update the dummy crypto balance self.balance.loc[self.balance['currency'] == cryptoMarket, 'balance'] = self.getBalance(cryptoMarket) + ( fiatAmountMinusFee / price) self.balance.loc[ self.balance['currency'] == cryptoMarket, 'available'] = self.getBalance(cryptoMarket) + ( fiatAmountMinusFee / price) def sell(self, cryptoMarket, fiatMarket, cryptoAmount, manualPrice=0.00000000): """Places a sell order either live or simulation Parameters ---------- cryptoMarket: str Crypto market you wish to purchase fiatMarket, str QUOTE market funding the purchase fiatAmount, float QUOTE amount of crypto currency to purchase manualPrice, float Used for simulations specifying the live price to purchase """ if self.app.getExchange() == 'binance': # validate crypto market is syntactically correct p = re.compile(r"^[A-Z]{3,8}$") if not p.match(cryptoMarket): raise TypeError('Binance crypto market is invalid.') # validate fiat market is syntactically correct p = re.compile(r"^[A-Z]{3,8}$") if not p.match(fiatMarket): raise TypeError('Binance fiat market is invalid.') else: # crypto market should be either BCH, BTC, ETH, LTC or XLM if cryptoMarket not in ['BCH', 'BTC', 'ETH', 'LTC', 'XLM']: raise Exception( 'Invalid crypto market: BCH, BTC, ETH, LTC, ETH, or XLM') # fiat market should be either EUR, GBP, or USD if fiatMarket not in ['EUR', 'GBP', 'USD']: raise Exception('Invalid QUOTE market: EUR, GBP, USD') # reconstruct the exchange market using crypto and fiat inputs if self.app.getExchange() == 'binance': market = cryptoMarket + fiatMarket else: market = cryptoMarket + '-' + fiatMarket # crypto amount must be an integer or float if not isinstance(cryptoAmount, float) and not isinstance( cryptoAmount, int): raise TypeError('Crypto amount not numeric.') # crypto amount must be positive if cryptoAmount <= 0: raise Exception('Invalid crypto amount.') if self.app.getExchange() == 'binance': if self.mode == 'live': # execute a live market buy resp = self.client.order_market_sell(symbol=market, quantity=cryptoAmount) # TODO: not finished print(resp) else: # crypto amount should exceed balance if cryptoAmount > self.getBalance(cryptoMarket): raise Exception('Insufficient funds.') # manual price must be an integer or float if not isinstance(manualPrice, float) and not isinstance( manualPrice, int): raise TypeError('Optional manual price not numeric.') # calculate purchase fees fee = cryptoAmount * 0.005 cryptoAmountMinusFee = cryptoAmount - fee price = manualPrice # if manualPrice is non-positive retrieve the current live price if manualPrice <= 0: resp = requests.get( 'https://api-public.sandbox.pro.coinbase.com/products/' + market + '/ticker') if resp.status_code != 200: raise Exception('GET /products/' + market + '/ticker {}'.format(resp.status_code)) resp.raise_for_status() json = resp.json() price = float(json['price']) total = price * cryptoAmountMinusFee # append dummy order into orders dataframe ts = pd.Timestamp.now() price = ((price * cryptoAmount) * 100) / (cryptoAmount * 100) order = pd.DataFrame([[ '', market, 'sell', 'market', cryptoAmountMinusFee, float('{:.8f}'.format(total)), 'done', '{:.8f}'.format( float(price)) ]], columns=[ 'created_at', 'market', 'action', 'type', 'size', 'value', 'status', 'price' ], index=[ts]) order['created_at'] = order.index self.orders = pd.concat( [self.orders, pd.DataFrame(order)], ignore_index=False) # update the dummy fiat balance self.balance.loc[ self.balance['currency'] == fiatMarket, 'balance'] = self.getBalance(fiatMarket) + total self.balance.loc[ self.balance['currency'] == fiatMarket, 'available'] = self.getBalance(fiatMarket) + total # update the dummy crypto balance self.balance.loc[ self.balance['currency'] == cryptoMarket, 'balance'] = self.getBalance(cryptoMarket) - cryptoAmount self.balance.loc[ self.balance['currency'] == cryptoMarket, 'available'] = self.getBalance(cryptoMarket) - cryptoAmount else: if self.mode == 'live': # connect to Coinbase Pro API live model = CBAuthAPI(self.app.getAPIKey(), self.app.getAPISecret(), self.app.getAPIPassphrase(), self.app.getAPIURL()) # execute a live market sell resp = model.marketSell(market, float(self.getBalance(cryptoMarket))) # TODO: not finished print(resp) else: # crypto amount should exceed balance if cryptoAmount > self.getBalance(cryptoMarket): raise Exception('Insufficient funds.') # manual price must be an integer or float if not isinstance(manualPrice, float) and not isinstance( manualPrice, int): raise TypeError('Optional manual price not numeric.') # calculate purchase fees fee = cryptoAmount * 0.005 cryptoAmountMinusFee = cryptoAmount - fee price = manualPrice if manualPrice <= 0: # if manualPrice is non-positive retrieve the current live price resp = requests.get( 'https://api-public.sandbox.pro.coinbase.com/products/' + market + '/ticker') if resp.status_code != 200: raise Exception('GET /products/' + market + '/ticker {}'.format(resp.status_code)) resp.raise_for_status() json = resp.json() price = float(json['price']) total = price * cryptoAmountMinusFee # append dummy order into orders dataframe ts = pd.Timestamp.now() price = ((price * cryptoAmount) * 100) / (cryptoAmount * 100) order = pd.DataFrame([[ market, 'sell', 'market', cryptoAmountMinusFee, float('{:.8f}'.format(total)), 'done', price ]], columns=[ 'market', 'action', 'type', 'size', 'value', 'status', 'price' ], index=[ts]) order['created_at'] = order.index self.orders = pd.concat( [self.orders, pd.DataFrame(order)], ignore_index=False) # update the dummy fiat balance self.balance.loc[ self.balance['currency'] == fiatMarket, 'balance'] = self.getBalance(fiatMarket) + total self.balance.loc[ self.balance['currency'] == fiatMarket, 'available'] = self.getBalance(fiatMarket) + total # update the dummy crypto balance self.balance.loc[ self.balance['currency'] == cryptoMarket, 'balance'] = self.getBalance(cryptoMarket) - cryptoAmount self.balance.loc[ self.balance['currency'] == cryptoMarket, 'available'] = self.getBalance(cryptoMarket) - cryptoAmount
class Bot(object): TIME = 0 OPEN = 1 HIGH = 2 LOW = 3 CLOSE = 4 TREND = 'UP' API_KEY = '' API_SECRET = '' current_stop = {} price_round = 0 min_points_list = [] data_dict = { 'timestamp': [], 'close':[], 'low':[] } def __init__(self, symbol): self.logger = Logger().init_logger() config = configparser.ConfigParser() config.read('conf.ini') self.API_KEY = config['API_KEYS']['api_key'] self.API_SECRET = config['API_KEYS']['api_secret'] self.client = Client(self.API_KEY, self.API_SECRET) self.symbol = symbol order = self.client.get_all_orders(symbol=self.symbol)[-1] self.current_stop = { 'orderId': order['orderId'], 'price': float(order['stopPrice']) } p = float(self.client.get_symbol_info(symbol=self.symbol)['filters'][2]['minQty']) while p != 1: p = p * 10 self.price_round = self.price_round + 1 self.logger.info("bot started guarding, current stop: {}, price round: {}".format(self.current_stop, self.price_round)) def get_historical_data(self, time_interval): startDay = (datetime.datetime.now()).strftime('%Y-%m-%d') return self.client.get_historical_klines(self.symbol, time_interval, startDay) def data_dict_init(self, historical_data): self.data_dict['timestamp'].append(datetime.datetime.fromtimestamp(historical_data[-2][self.TIME] / 1e3).strftime("%d/%m/%Y, %H:%M:%S")) self.data_dict['close'].append(float(historical_data[-2][self.CLOSE])) self.data_dict['low'].append(float(historical_data[-2][self.LOW])) self.data_dict['timestamp'].append(datetime.datetime.fromtimestamp(historical_data[-1][self.TIME] / 1e3).strftime("%d/%m/%Y, %H:%M:%S")) self.data_dict['close'].append(float(historical_data[-1][self.CLOSE])) self.data_dict['low'].append(float(historical_data[-1][self.LOW])) self.logger.info("data dictionary has initiated {}".format(self.data_dict)) self.min_points_list.append( [ self.data_dict['timestamp'][-1], self.data_dict['low'][-1] ] ) def populate_data_dict(self, historical_data): if self.data_dict['timestamp'][-1] == datetime.datetime.fromtimestamp(historical_data[-2][self.TIME] / 1e3).strftime("%d/%m/%Y, %H:%M:%S"): return self.data_dict['timestamp'].append(datetime.datetime.fromtimestamp(historical_data[-2][self.TIME] / 1e3).strftime("%d/%m/%Y, %H:%M:%S")) self.data_dict['close'].append(float(historical_data[-2][self.CLOSE])) self.data_dict['low'].append(float(historical_data[-2][self.LOW])) def validate_min_list(self): mp_list = [] if len(self.min_points_list) < 2: return for mp in self.min_points_list: mp_list.append(mp[1]) diff = np.diff(mp_list) if diff[-1] < 0: self.logger.warning("the minimum list has lost it's validity with a new lower low") def create_min_list(self): diff_array = np.diff(self.data_dict['close']) if float(diff_array[-1]) > 0 and self.TREND == 'DOWN': self.TREND = 'UP' if self.min_points_list[-1][1] > self.data_dict['low'][-2]: self.min_points_list.pop() if self.data_dict['low'][-2] < self.data_dict['low'][-1]: self.min_points_list.append( [ self.data_dict['timestamp'][-2], self.data_dict['low'][-2] ] ) else: self.min_points_list.append( [ self.data_dict['timestamp'][-1], self.data_dict['low'][-1] ] ) self.logger.info("list of minimums is: {}".format(self.min_points_list)) if float(diff_array[-1]) < 0 and self.TREND == 'UP': self.TREND = 'DOWN' print("diff: {} trend:{}".format(diff_array[-1], self.TREND)) sleep(3) def wait_for_data(self, time_interval): time = datetime.datetime.now().strftime("%M") if 'h' in time_interval: while time != '00': time = datetime.datetime.now().strftime("%M") sleep(5) return interval = int(time_interval.replace('m','')) while int(time) % interval != 0: time = datetime.datetime.now().strftime("%M") sleep(5) def change_stop_limit(self, stop): self.client.cancel_order(orderId=self.current_stop['orderId'], symbol=self.symbol) quantity = float(self.client.get_asset_balance(asset=self.symbol.replace('USDT',''))['free']) quantity = round(quantity, self.price_round) order = self.client.create_order(type="STOP_LOSS_LIMIT", side="SELL",price=stop, stopPrice=stop, quantity=quantity, symbol=self.symbol, timeInForce='GTC') self.current_stop = { 'orderId': order['orderId'], 'price': stop } self.logger.info("stop has changed: {}".format(self.current_stop)) def is_new_stop(self): mp_list = [] diff_abs_list = [] if len(self.min_points_list) < 3: return False for mp in self.min_points_list: mp_list.append(mp[1]) diff = np.diff(mp_list) for d in diff: diff_abs_list.append(d/abs(d)) if diff_abs_list[-1] + diff_abs_list[-2] == 2: if self.min_points_list[-3][1] > self.current_stop['price']: return True return False def trade_guard(self, time_interval): historical_data = self.get_historical_data(time_interval) self.data_dict_init(historical_data) while True: self.wait_for_data(time_interval) historical_data = self.get_historical_data(time_interval) self.populate_data_dict(historical_data) self.create_min_list() if self.is_new_stop(): self.change_stop_limit(self.min_points_list[-3][1])
class ApiCalls: """Collection of api related methods.""" def __init__(self, mw, tp): self.mw = mw self.client = Client(mw.cfg_manager.api_key, mw.cfg_manager.api_secret, { "verify": True, "timeout": 10 }) app.client = self.client self.threadpool = tp def initialize(self): # print("setting client: " + str(self.client)) try: val["coins"] = self.availablePairs() val["accHoldings"] = self.getHoldings() val["tickers"] = self.getTickers() val["apiCalls"] += 3 # userMsg = dict() # accHoldings = dict() self.set_pair_values() self.mw.is_connected = True except (BinanceAPIException, NameError) as e: print("API ERROR") print(str(e)) if "code=-1003" in str(e): print("ja ein api error :)") elif "code=-2014": print("API KEY INVALID") # def get_tether(client): # tether_info = client.get_ticker(symbol="BTCUSDT") # return tether_info def set_pair_values(self): """Set various values based on the chosen pair.""" pair = self.mw.cfg_manager.pair val["decimals"] = len(str(val["coins"][pair]["tickSize"])) - 2 if int(val["coins"][pair]["minTrade"]) == 1: val["assetDecimals"] = 0 else: val["assetDecimals"] = len(str(val["coins"][pair]["minTrade"])) - 2 def availablePairs(self): """ Create a dictonary containing all BTC tradepairs excluding USDT. Keys are: {'symbol': 'ETHBTC', 'tradedMoney': 3024.89552855, 'baseAssetUnit': 'Ξ', 'active': True, 'minTrade': '0.00100000', 'baseAsset': 'ETH', 'activeSell': 66254.102, 'withdrawFee': '0', 'tickSize': '0.000001', 'prevClose': 0.044214, 'activeBuy': 0, 'volume': '66254.102000', 'high': '0.047998', 'lastAggTradeId': 2809764, 'decimalPlaces': 8, 'low': '0.043997', 'quoteAssetUnit': '฿', 'matchingUnitType': 'STANDARD', 'close': '0.047656', 'quoteAsset': 'BTC', 'open': '0.044214', 'status': 'TRADING', 'minQty': '1E-8'} """ # create a local dictionary coins = dict() # API Call products = self.client.get_products() # For every entry in API answer: for i, pair in enumerate(products["data"]): # Check if pair contains BTC, does not contain USDT and if volume is >0 if "BTC" in pair["symbol"] and "USDT" not in pair[ "symbol"] and float(products["data"][i]["volume"]) > 0.0: # Create a temporary dictionary to store keys and values tempdict = dict() # Add every key-value pair to the temp dictionary for key, value in pair.items(): tempdict[key] = value # Add every temp dictionary to the coin dictionary coins[tempdict["symbol"]] = tempdict return coins def getHoldings(self): """Make an inital API call to get BTC and coin holdings.""" # API Call: order = self.client.get_account() accHoldings = dict() for i in range(len(order["balances"])): accHoldings[order["balances"][i]["asset"]] = { "free": order["balances"][i]["free"], "locked": order["balances"][i]["locked"] } return accHoldings def getTickers(self): """Make an initial API call to get ticker data.""" ticker = self.client.get_ticker() # print(str(ticker)) all_tickers = dict() for _, ticker_data in enumerate(ticker): if "BTC" in ticker_data["symbol"]: # print(str(ticker_data)) all_tickers[ticker_data["symbol"]] = ticker_data return all_tickers def getTradehistory(self, pair): """Make an initial API call to get the trade history of a given pair. This is used until updated by websocket data.""" # API call globalList = list() trades = self.client.get_aggregate_trades(symbol=pair, limit=50) for _, trade in enumerate(reversed(trades)): globalList.insert( 0, { "price": str(trade["p"]), "quantity": str(trade["q"]), "maker": bool(trade["m"]), "time": str(trade["T"]) }) return list(reversed(globalList)) def getDepth(self, symbol): """Make an initial API call to get market depth (bids and asks).""" # API Call depth = self.client.get_order_book(symbol=symbol, limit=20) asks = depth["asks"] bids = depth["bids"] return {"bids": bids, "asks": asks} def api_create_order(self, side, pair, price, amount, progress_callback): print("create order: " + str(price) + " " + str(amount)) try: if side == "Buy": order = self.client.order_limit_buy(symbol=pair, quantity=str(amount), price=str(price)) elif side == "Sell": order = self.client.order_limit_sell(symbol=pair, quantity=str(amount), price=str(price)) print("order status: " + str(order)) return order except BinanceAPIException as e: print("create order failed: " + str(e)) print(str(order)) def api_cancel_order(self, client, order_id, symbol, progress_callback): print("cancel order " + str(symbol) + " " + str(order_id)) try: self.client.cancel_order(symbol=symbol, orderId=order_id) except BinanceAPIException as e: print("cancel failed " + str(e)) print(str(self.client)) print(str(symbol)) print(str(order_id)) def api_order_history(self, pair, progress_callback): orders = self.client.get_all_orders(symbol=pair) progress_callback.emit(orders) val["apiCalls"] += 1 def api_history(self, progress_callback): trade_history = self.getTradehistory(self.mw.cfg_manager.pair) progress_callback.emit({"history": reversed(trade_history)}) val["apiCalls"] += 1 def api_depth(self, progress_callback): depth = self.getDepth(self.mw.cfg_manager.pair) val["asks"] = depth["asks"] progress_callback.emit({"asks": val["asks"]}) val["bids"] = depth["bids"] progress_callback.emit({"bids": val["bids"]}) val["apiCalls"] += 1 def api_all_orders(self, progress_callback): print("CLEINT;" + str(self.client)) orders = self.client.get_open_orders() progress_callback.emit(orders) numberPairs = sum(val["pairs"].values()) print("number pairs: " + str(numberPairs)) def api_calls(self): """Inital and coin specific api calls""" worker = Worker(self.api_history) worker.signals.progress.connect(self.mw.live_data.batch_history) self.mw.threadpool.start(worker) worker = Worker(self.api_depth) worker.signals.progress.connect(self.mw.live_data.batch_orderbook) worker.signals.finished.connect(self.mw.limit_pane.t_complete) self.mw.threadpool.start(worker) self.get_trade_history(self.mw.cfg_manager.pair) def get_trade_history(self, pair): worker = Worker(partial(self.api_order_history, pair)) worker.signals.progress.connect(self.mw.history_table.orders_received) self.mw.threadpool.start(worker) def get_kline(self, pair, progress_callback): """Make an API call to get historical data of a coin pair.""" interval = "1m" try: # try since this is used heavily klines = self.client.get_klines(symbol=pair, interval=interval) except (ConnectionError, BinanceAPIException) as e: print(str(e)) progress_callback.emit([klines, pair, interval]) val["apiCalls"] += 1 def cancel_order_byId(self, order_id, symbol): """Cancel an order by id from within a separate thread.""" worker = Worker( partial(self.mw.api_manager.api_cancel_order, app.client, order_id, symbol)) # worker.signals.progress.connect(self.cancel_callback) self.threadpool.start(worker)
class BinanceExchange(AbstractExchange): _exchange_name = EXCHANGE__BINANCE _exchange_token = 'BNB' _intervals = { Candle.INTERVAL__1MINUTE: Client.KLINE_INTERVAL_1MINUTE, Candle.INTERVAL__5MINUTE: Client.KLINE_INTERVAL_5MINUTE, Candle.INTERVAL__15MINUTE: Client.KLINE_INTERVAL_15MINUTE, Candle.INTERVAL__1HOUR: Client.KLINE_INTERVAL_1HOUR } def __init__(self, api_key, api_secret, watchlist): super().__init__(api_key, api_secret, watchlist) self.client = Client(api_key, api_secret) def build_market_name(self, crypto, base_currency): # Binance uses HYDROBTC format return f"{crypto}{base_currency}" def initialize_market(self, crypto, base_currency, recheck=False): """ Make sure we have MarketParams for the given market """ market = self.build_market_name(crypto, base_currency) params = MarketParams.get_market( market, exchange=MarketParams.EXCHANGE__BINANCE) if not params or recheck: # Have to query the API and populate """ { 'symbol': 'ASTBTC', 'status': 'TRADING', 'baseAsset': 'AST', 'baseAssetPrecision': 8, 'quoteAsset': 'BTC', 'quotePrecision': 8, 'orderTypes': ['LIMIT', 'LIMIT_MAKER', 'MARKET', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT_LIMIT'], 'icebergAllowed': False, 'filters': [ { 'filterType': 'PRICE_FILTER', 'minPrice': '0.00000001', 'maxPrice': '100000.00000000', 'tickSize': '0.00000001' }, { 'filterType': 'LOT_SIZE', 'minQty': '1.00000000', 'maxQty': '90000000.00000000', 'stepSize': '1.00000000' }, { 'filterType': 'MIN_NOTIONAL', 'minNotional': '0.00100000' } ] } """ response = self.client.get_symbol_info(symbol=market) if not response: raise Exception( f"Couldn't retrieve current ticker for '{self.build_market_name(crypto, base_currency)}' on {self.exchange_name}" ) tick_size = None step_size = None min_notional = None multiplier_up = None avg_price_minutes = None for filter in response["filters"]: if filter['filterType'] == 'PRICE_FILTER': tick_size = Decimal(filter["tickSize"]) elif filter['filterType'] == 'LOT_SIZE': step_size = Decimal(filter["stepSize"]) elif filter['filterType'] == 'MIN_NOTIONAL': min_notional = Decimal(filter["minNotional"]) elif filter['filterType'] == 'PERCENT_PRICE': multiplier_up = Decimal(filter["multiplierUp"]) avg_price_minutes = Decimal(filter["avgPriceMins"]) if params: params.tick_size = tick_size params.step_size = step_size params.min_notional = min_notional params.multiplier_up = multiplier_up params.avg_price_minutes = avg_price_minutes params.save() print(f"Re-loaded MarketParams for {market}") else: MarketParams.create(exchange=MarketParams.EXCHANGE__BINANCE, market=market, price_tick_size=tick_size, lot_step_size=step_size, min_notional=min_notional, multiplier_up=multiplier_up, avg_price_minutes=avg_price_minutes) print(f"Loaded MarketParams for {market}") def ingest_latest_candles(self, market, interval, since=None, limit=5): """ Get the most recent n (limit) candles. [ 1499040000000, // Open time "0.01634790", // Open "0.80000000", // High "0.01575800", // Low "0.01577100", // Close "148976.11427815", // Volume 1499644799999, // Close time "2434.19055334", // Quote asset volume 308, // Number of trades "1756.87402397", // Taker buy base asset volume "28.46694368", // Taker buy quote asset volume "17928899.62484339" // Ignore ] """ if limit == 1: # Never ingest the most recent (still-open) candle return print(f"{market} candles: {limit} | {since}") # Give the exchange a breather so we don't get API limited if limit > 10: time.sleep(3) if since: # Convert Unix timestamp to binance's millisecond timestamp since = since * 1000 + 1 raw_data = self.client.get_klines(symbol=market, interval=self._intervals[interval], startTime=since, limit=limit) # print(json.dumps(raw_data, indent=4)) # Never ingest the most recent (still-open) candle (most recent is last) Candle.batch_create_candles(market, interval, self._format_candles(raw_data[:-1])) def load_historical_candles(self, market, interval, since): """ This is a historical batch update of all candles from 'since' to now. [ 1499040000000, // Open time "0.01634790", // Open "0.80000000", // High "0.01575800", // Low "0.01577100", // Close "148976.11427815", // Volume 1499644799999, // Close time "2434.19055334", // Quote asset volume 308, // Number of trades "1756.87402397", // Taker buy base asset volume "28.46694368", // Taker buy quote asset volume "17928899.62484339" // Ignore ] """ candles = self.client.get_historical_klines(market, self._intervals[interval], since) return self._format_candles(candles) def _format_candles(self, candles): results = [] for candle in candles: results.append({ "timestamp": candle[0] / 1000.0, "open": Decimal(candle[1]), "high": Decimal(candle[2]), "low": Decimal(candle[3]), "close": Decimal(candle[4]) }) return results def get_current_balance(self, asset='BTC'): return Decimal(self.client.get_asset_balance(asset=asset)["free"]) def get_current_balances(self): if config.is_test: pass else: """ { 'asset': 'BTC', 'free': '0.00000000', 'locked': '0.00000000' } """ return { 'BTC': self.get_current_balance(asset='BTC'), 'BNB': self.get_current_balance(asset='BNB') } def buy(self, market, quantity): """ Place a market buy order. As soon as it completes submit a stop-loss limit order. """ # Adjust quantity as needed to conform to MarketParams; quantity can only # be divided down to so many digits of precision, depending on the # particular market. market_params = MarketParams.get_market(market) quantized_qty = quantity.quantize(market_params.lot_step_size) """ { 'symbol': 'BNBBTC', 'orderId': 58158667, 'clientOrderId': 'jFTEtmTTUTqspMi6Oq08R9', 'transactTime': 1529546953007, 'price': '0.00000000', 'origQty': '1.55000000', 'executedQty': '1.55000000', 'status': 'FILLED', 'timeInForce': 'GTC', 'type': 'MARKET', 'side': 'BUY' } """ try: buy_order_response = self.client.order_market_buy( symbol=market, quantity=quantized_qty, newOrderRespType=Client. ORDER_RESP_TYPE_FULL # Need the full details to get 'commission' (aka fees). ) except Exception as e: print(f"-------------- MARKET BUY EXCEPTION!! --------------" + f" | {market}" + f" | quantized_qty: {quantized_qty}") # Throw it back up to bomb us out raise e if config.verbose: print( f"BUY ORDER: {json.dumps(buy_order_response, sort_keys=True, indent=4)}" ) order_id = buy_order_response["orderId"] timestamp = buy_order_response["transactTime"] / 1000 if buy_order_response["status"] != 'FILLED': # TODO: handle unfilled market buys raise Exception( f"Buy order not FILLED (yet?)\n{buy_order_response}") elif buy_order_response["status"] == 'FILLED': # Calculate an aggregate purchase price total_spent = Decimal(0.0) total_qty = Decimal(0.0) total_commission = Decimal(0.0) for fill in buy_order_response["fills"]: """ { "price": "4000.00000000", "qty": "1.00000000", "commission": "4.00000000", "commissionAsset": "USDT" } """ total_spent += Decimal(fill["price"]) * Decimal(fill["qty"]) total_qty += Decimal(fill["qty"]) total_commission += Decimal(fill["commission"]) purchase_price = total_spent / total_qty return { "order_id": order_id, "price": purchase_price, "quantity": total_qty, "fees": total_commission, "timestamp": timestamp } def reload_exchange_token(self, quantity): print( f"Reloading {quantity:.4f} {self.exchange_token} exchange tokens") market = f'{self.exchange_token}BTC' return self.buy(market, quantity) def market_sell(self, market, quantity): # Round prices to conform to price_tick_size for this market market_params = MarketParams.get_market(market) quantized_qty = quantity.quantize(market_params.lot_step_size) try: response = self.client.order_market_sell( symbol=market, quantity=quantized_qty, newOrderRespType=Client. ORDER_RESP_TYPE_FULL # Need the full details to get 'commission' (aka fees). ) except Exception as e: error_msg = (f"MARKET SELL ORDER: {market}" + f" | quantized_qty: {quantized_qty}\n" + f"{e}") cprint(error_msg, "red") # TODO: Email error notifications? raise e print(f"MARKET SELL ORDER: {response}") order_id = response["orderId"] timestamp = response["transactTime"] / 1000 if response["status"] != 'FILLED': # TODO: handle unfilled market sells raise Exception(f"Market sell order not FILLED (yet?)\n{response}") elif response["status"] == 'FILLED': # Calculate an aggregate sale price total_made = Decimal('0.0') total_qty = Decimal(response["executedQty"]) total_commission = Decimal('0.0') for fill in response["fills"]: """ { "price": "4000.00000000", "qty": "1.00000000", "commission": "4.00000000", "commissionAsset": "BNB" } """ total_made += Decimal(fill["price"]) * Decimal(fill["qty"]) total_commission += Decimal(fill["commission"]) sell_price = total_made / total_qty return { "order_id": order_id, "price": sell_price, "quantity": total_qty, "fees": total_commission, "timestamp": timestamp } else: raise Exception("Unhandled case!") def limit_sell(self, market, quantity, bid_price): # Round prices to conform to price_tick_size for this market market_params = MarketParams.get_market(market) quantized_qty = quantity.quantize(market_params.lot_step_size) bid_price = bid_price.quantize(market_params.price_tick_size) try: response = self.client.order_limit_sell( symbol=market, quantity=quantized_qty, price= f"{bid_price:0.8f}", # Pass as string to ensure input accuracy and format newOrderRespType=Client. ORDER_RESP_TYPE_FULL # Need the full details to get 'commission' (aka fees). ) except Exception as e: error_msg = (f"LIMIT SELL ORDER: {market}" + f" | quantized_qty: {quantized_qty}" + f" | bid_price: {bid_price}" + f"{e}") if 'PERCENT_PRICE' in error_msg: cprint( f"Attempted to set a price ({bid_price}) outside the exchange's {market} PERCENT_PRICE range", "red") return None if 'MIN_NOTIONAL' in error_msg: cprint( f"Attempted to set a notional value ({bid_price} * {quantized_qty}) outside the exchange's {market} MIN_NOTIONAL", "red") return None if 'Account has insufficient balance for requested action.' in error_msg: cprint( f"Insufficent balance for {market} LIMIT SELL {quantized_qty}" ) return None cprint(error_msg, "red") # TODO: Email error notifications? raise e if config.verbose: print(f"LIMIT SELL ORDER: {response}") order_id = response["orderId"] timestamp = response["transactTime"] / 1000 return { "order_id": order_id, "price": bid_price, "quantity": quantized_qty } def set_stop_loss(self, market, quantity, stop_loss_price): limit_price = stop_loss_price * config.params[ "stop_loss_limit_percentage"] # Round prices to conform to price_tick_size for this market market_params = MarketParams.get_market(market) # Round the quantity to one less decimal place than specified # e.g. lot_step_size = '0.001' but round 0.123 to 0.12 # see: https://redd.it/7ej5cn quantized_qty = quantity.quantize(market_params.lot_step_size) # Same for the price stop_loss_price = stop_loss_price.quantize( market_params.price_tick_size) limit_price = limit_price.quantize(market_params.price_tick_size) try: response = self.client.create_order( symbol=market, quantity=quantized_qty, price=limit_price, stopPrice=stop_loss_price, type=Client.ORDER_TYPE_STOP_LOSS_LIMIT, side=Client.SIDE_SELL, timeInForce=Client.TIME_IN_FORCE_GTC, newOrderRespType=Client.ORDER_RESP_TYPE_FULL) except binance.exceptions.BinanceAPIException as e: error_msg = (f"STOP LOSS LIMIT ORDER: {market}" + f" | quantity: {quantity}" + f" | quantized_qty: {quantized_qty}" + f" | price: {limit_price}" + f" | stopPrice: {stop_loss_price}\n" + f"{e}") cprint(error_msg, "red") return {"error_code": e.code} except Exception as e: error_msg = (f"STOP LOSS LIMIT ORDER: {market}" + f" | quantity: {quantity}" + f" | quantized_qty: {quantized_qty}" + f" | price: {limit_price}" + f" | stopPrice: {stop_loss_price}\n" + f"{e}") cprint(error_msg, "red") # TODO: Handle binance.exceptions.BinanceAPIException: APIError(code=-2010): Order would trigger immediately. # Immediately submit it as a market sell order instead? # TODO: Email error notifications? raise e order_id = response["orderId"] timestamp = response["transactTime"] / 1000 return { "order_id": order_id, "quantity": quantized_qty, "stop_loss_price": stop_loss_price, "limit_price": limit_price, "timestamp": timestamp } def cancel_order(self, market, order_id): """ { "symbol": "LTCBTC", "orderId": 28, "origClientOrderId": "myOrder1", "clientOrderId": "cancelMyOrder1", "transactTime": 1507725176595, "price": "1.00000000", "origQty": "10.00000000", "executedQty": "8.00000000", "cummulativeQuoteQty": "8.00000000", "status": "CANCELED", "timeInForce": "GTC", "type": "LIMIT", "side": "SELL" } """ result = self.client.cancel_order(symbol=market, orderId=order_id) return (result['status'] == 'CANCELED', result) def update_stop_loss(self, position, stop_loss_price): if config.is_test: return { "success": True, "timestamp": config.historical_timestamp, "order_id": 1 } else: self.cancel_order(market=position.market, order_id=position.sell_order_id) return self.set_stop_loss(market=position.market, quantity=position.sell_quantity, stop_loss_price=stop_loss_price) def get_sell_order(self, position): try: if not position.sell_order_id: # Can't look for nothing raise Exception(f"Position {position} has no sell_order_id") response = self.client.get_order(symbol=position.market, orderId=position.sell_order_id) """ { 'symbol': 'BNBBTC', 'orderId': 58158667, 'clientOrderId': 'jFTEtmTTUTqspMi6Oq08R9', 'price': '0.00000000', 'origQty': '1.55000000', 'executedQty': '1.55000000', 'status': 'FILLED', 'timeInForce': 'GTC', 'type': 'MARKET', 'side': 'BUY', 'stopPrice': '0.00000000', 'icebergQty': '0.00000000', 'time': 1529546953007, 'isWorking': True } """ except Exception as e: print(f"GET SELL ORDER STATUS:" + f" | symbol: {position.market}" + f" | orderId: {position.sell_order_id}" + f"\n{e}") raise e return response def get_sell_order_status(self, position): response = self.get_sell_order(position) if response["status"] == 'FILLED': # print(f"ORDER STATUS: FILLED: {response}") price = Decimal(response["stopPrice"]) if Decimal( response["stopPrice"]) != Decimal('0.0') else Decimal( response["price"]) quantity = Decimal(response["executedQty"]) # TODO: How to get the 'commission' fees? # Calculating unofficial fees for now # fees = self._calculate_fees(price, quantity) # Sell order is done! return { "status": response["status"], "sell_price": price, "quantity": quantity, # "fees": fees, "timestamp": response["time"] / 1000, } else: return {"status": response["status"]} def update_order_statuses(self, market, positions): """ Batch update open positions by market. """ if positions.count() == 0: return [] market_params = MarketParams.get_market( market, exchange=MarketParams.EXCHANGE__BINANCE) first_open_position = next(p for p in positions if p.sell_order_id is not None) print( f"Retrieving order statuses for {market}, starting at orderId {first_open_position.sell_order_id}" ) orders = self.client.get_all_orders( symbol=first_open_position.market, orderId=first_open_position.sell_order_id, limit=1000) """ [{ "symbol": "ONTBTC", "orderId": 95353124, "clientOrderId": "O3Ji1FvNiYt6rr9NvGEtC4", "price": "0.00020880", "origQty": "4.91000000", "executedQty": "0.00000000", "cummulativeQuoteQty": "0.00000000", "status": "NEW", "timeInForce": "GTC", "type": "LIMIT", "side": "SELL", "stopPrice": "0.00000000", "icebergQty": "0.00000000", "time": 1556814586405, "updateTime": 1556814586405, "isWorking": true }, {...}, {...}] """ print( f"{market} orders retrieved: {len(orders)} | positions: {positions.count()}" ) positions_sold = [] orders_processed = [] for position in positions: if position.sell_order_id is None: continue result = next( (r for r in orders if r['orderId'] == position.sell_order_id), None) if not result: cprint( f"orderId {position.sell_order_id} not found for position {position.id}: {market}", "red") # Assume the order can be found individually and proceed result = self.get_sell_order(position) print(result['status']) orders_processed.append(position.sell_order_id) if result['status'] == 'NEW': # Nothing to do. Still waiting for LIMIT SELL. continue elif result['status'] == 'FILLED': position.sell_price = Decimal(result['price']).quantize( market_params.price_tick_size) position.sell_quantity = Decimal( result['executedQty']).quantize( market_params.lot_step_size) position.sell_timestamp = result['updateTime'] / 1000 position.scalped_quantity = (position.buy_quantity - position.sell_quantity).quantize( market_params.lot_step_size) position.save() positions_sold.append(position) elif result['status'] == 'CANCELED': # Somehow the management of this order's cancellation didn't make it into the DB. print( f"CANCELED order not properly updated in DB: {market} {position.id}" ) position.sell_order_id = None position.sell_price = None position.sell_quantity = None position.save() else: raise Exception( f"Unimplemented order status: '{result['status']}'\n\n{json.dumps(result, sort_keys=True, indent=4)}" ) # Cancel any 'NEW' orders that aren't connected to a position # for order in orders: # if order['status'] == 'NEW' and order['orderId'] not in orders_processed: # # Cancel this order! # cprint(f"CANCELING {market} order {order['orderId']}", "red") # result = self.cancel_order(market, order['orderId']) # print(result) return positions_sold def get_buy_order_status(self, position): if config.is_test: # Have to simulate if the buy order would have filled at the current historical_timestamp candle = Candle.get_historical_candle(position.market, config.interval, config.historical_timestamp) if candle and position.purchase_price >= candle.low: return { "status": "FILLED", "fees": (position.buy_quantity * position.purchase_price * Decimal('0.0005')).quantize(Decimal('0.00000001'), rounding=decimal.ROUND_DOWN), "timestamp": candle.timestamp } else: return {"status": "OPEN", "timestamp": candle.timestamp} else: """ { 'symbol': 'BNBBTC', 'orderId': 58158667, 'clientOrderId': 'jFTEtmTTUTqspMi6Oq08R9', 'price': '0.00000000', 'origQty': '1.55000000', 'executedQty': '1.55000000', 'status': 'FILLED', 'timeInForce': 'GTC', 'type': 'MARKET', 'side': 'BUY', 'stopPrice': '0.00000000', 'icebergQty': '0.00000000', 'time': 1529546953007, 'isWorking': True } """ try: if not position.buy_order_id: # Can't look for nothing raise Exception(f"Position {position} has no buy_order_id") response = self.client.get_order(symbol=position.market, orderId=position.buy_order_id) except Exception as e: print(f"GET BUY ORDER STATUS:" + f" | symbol: {position.market}" + f" | orderId: {position.buy_order_id}" + f"\n{e}") raise e if response["status"] == 'FILLED': print(f"ORDER STATUS: FILLED: {response}") # TODO: How to get the 'commission' fees? # Calculate unofficial fees for now fees = self._calculate_fees(position.purchase_price, position.buy_quantity) return { "status": response["status"], "fees": fees, "timestamp": response["time"] / 1000, } else: return {"status": response["status"]} def _calculate_fees(self, price, quantity): fees = price * quantity / self.get_current_price( f"{self.exchange_token}BTC") * Decimal('0.0005') return fees.quantize(Decimal('0.00000001'), rounding=decimal.ROUND_DOWN) def get_current_price(self, market): return Decimal(self.client.get_ticker(symbol=market)["lastPrice"]) def get_current_ask(self, market): return Decimal( self.client.get_order_book(symbol=market).get('asks')[0][0]) def get_market_depth(self, market): return self.client.get_order_book(symbol=market) def get_moving_average(self, market, interval, since): """ [ 1499040000000, // Open time "0.01634790", // Open "0.80000000", // High "0.01575800", // Low "0.01577100", // Close "148976.11427815", // Volume 1499644799999, // Close time "2434.19055334", // Quote asset volume 308, // Number of trades "1756.87402397", // Taker buy base asset volume "28.46694368", // Taker buy quote asset volume "17928899.62484339" // Ignore ] """ self.candles = self.client.get_historical_klines( market, self.intervals[interval], since) ma = Decimal('0.0') for candle in self.candles: ma += (Decimal(candle[4]) + Decimal(candle[1])) / Decimal('2.0') return ma / Decimal(len(self.candles))
class Binance: client: Client = None __symbols: List[str] = None user: User = None def __init__(self, user: User): self.user = user self.client = Client(user.api_key, user.api_secret) def records(self) -> Generator[Tuple[Record, bool], None, None]: for symbol in self.symbols(): for r in self.get_orders(symbol): yield r def symbols(self) -> Generator[str, None, None]: if self.__symbols is not None: for symbol in self.__symbols: yield symbol return symbols = [] for v in all_symbols(self.assets()): if self.client.get_symbol_info(v.replace('/', '')) is not None: symbols.append(v) yield v self.__symbols = symbols def assets(self) -> List[str]: return [v['asset'] for v in self.client.get_account()['balances'] \ if float(v['free']) + float(v['locked']) != 0] def get_orders(self, symbol: str) -> List[Tuple[Record, bool]]: data = self.client.get_all_orders(symbol=symbol.replace('/', '')) return [ self.create_record(v, symbol) for v in data if v['status'] == 'FILLED' ] def create_record(self, table: Dict[str, str], symbol: str) -> Record: updates = { 'is_sell': table['side'] == 'SELL', 'symbol': symbol, 'executed_qty': float(table['executedQty']), 'cummulative_quote_qty': float(table['cummulativeQuoteQty']), 'user': self.user, 'time': datetime.fromtimestamp(int(table['time']) / 1000, tz=timezone.utc), } return Record.objects.get_or_create( id=int(table['orderId']), defaults=updates, ) def price(self, symbol: str) -> float: print(symbol) try: data = self.client.get_avg_price(symbol=symbol + 'USDT') return float(data['price']) except: return 1
bm = BinanceSocketManager(client) # start any sockets here, i.e a trade socket #conn_key = bm.start_depth_socket('BNBBTC', process_message, depth=BinanceSocketManager.WEBSOCKET_DEPTH_5) conn_key = bm.start_multiplex_socket(['bnbbtc@depth5','xrpbnb@depth5'], process_message) # then start the socket manager idx = conn_key.find('=') keys = conn_key[idx+1:].split('/') print keys bm.start() #bm.stop_socket('sds')#close with conn_key #bm.close()#close all ''' bnb_balance = client.get_asset_balance(asset='BNB') print bnb_balance if bnb_balance: bal = float(bnb_balance['free']) if bal > 1: #if first of pair is BNB then invert SIDE (ie from buy to sell) order = client.create_order(symbol='BNBBTC', side=SIDE_SELL, type=ORDER_TYPE_LIMIT, timeInForce=TIME_IN_FORCE_GTC, quantity=1, price='0.00377') print order orders = client.get_all_orders(symbol='BNBBTC') pprint.pprint(orders)