def __init__(self): self.mywss = WssClient(key=KEY, secret=SECRET) self.mywss2 = None # Tracks Websocket Connection self.connection_timer = None self.connection_timeout = 15 self.keep_running = True self.wssactions = {'hb': self._heartbeat_handler} self.mywss.authenticate(self.cb_auth) self.mywss.start() sleep(5) self._start_timers()
def startSockets(self): self.client = Client(specifications['api_key_p'], specifications['api_secret_p'], { "verify": False, "timeout": 20 }) self.bm = BinanceSocketManager(self.client) self.bm.start_trade_socket('BTCUSDT', self.market_event) self.bm.start_user_socket(self.user_event) self.bm.start() self.bitfinex_client = WssClient() self.bitfinex_client.subscribe_to_ticker(symbol="BTCUSD", callback=self.bitfinex_event) self.bitfinex_client.start()
def __init__(self, thread_manager, keys, pairs): """ keys as parsed from keys/bitfinex.json """ assert not Bitfinex.__instance_exists Bitfinex.__instance_exists = True super().__init__(thread_manager) self.__api_key = keys["key"] self.__api_secret = keys["secret"] self.__bfxv1 = ClientV1(self.__api_key, self.__api_secret, 2.0) self.__bfxv2 = ClientV2(self.__api_key, self.__api_secret) self.__ws_client = WssClient(self.__api_key, self.__api_secret) self.__ws_client.authenticate(lambda x: None) self.__ws_client.daemon = True self.__pairs = pairs self.__order_types = { Order.Type.MARKET: "market", Order.Type.LIMIT: "limit", Order.Type.IOC: "immediate-or-cancel", Order.Type.FOK: "fill-or-kill", } self.__book_feeds = {} self.__trade_feeds = {} self.__positions_queue = Queue() self.__positions_feed = self.positions_feed() thread_manager.attach("bitfinex-positions-queue", self.__track_positions) # TODO: Can this be dynamically loaded? (For other exchanges too.) self.__fees = {"maker": 0.001, "taker": 0.002} # TODO: Maybe start this in a lazy way? self.__ws_client.start() self.__frame = pd.Series( 0.0, # setting the initial value to 0 is important for volume tracking to work properly index=pd.MultiIndex.from_product( [[ExchangePair(self.id, x) for x in pairs], ["price", "volume"]]), ) for pair in pairs: pair_feed_book, runner_book = Feed.of( self.__generate_book_feed(pair)) self._thread_manager.attach(f"bitfinex-{pair}-book", runner_book) self.__book_feeds[pair] = pair_feed_book pair_feed_trade, runner_trade = Feed.of( self.__generate_trade_feed(pair)) self._thread_manager.attach(f"bitfinex-{pair}-trade", runner_trade) self.__trade_feeds[pair] = pair_feed_trade
def __init__(self, account, api_key, api_secret): self.account = account self.api_key = api_key self.api_secret = api_secret self.manager_candlestick = WssClient(self.api_key, self.api_secret) self.manager_depth = WssClient(self.api_key, self.api_secret) self.manager_ticker = WssClient(self.api_key, self.api_secret) self.manager_trades = WssClient(self.api_key, self.api_secret) self.started_candlestick = False self.started_depth = False self.started_ticker = False self.started_trades = False self.markets = None
def start(self, verbose=True): """ verbose - if set to True then channel information is printed out """ self.verbose = verbose client = WssClient() client.subscribe_to_ticker(symbol=self.pair, callback=self._handler) client.start()
class Robot: def __init__(self): self.bitcoin = None self.orderStatus = '' self.orderId = '' self.noOrderInProgress = True # prevent multiple orders due to exchange latency self.nextPosition = '' self.startSockets() BTCbalance = float(self.client.get_asset_balance(asset='BTC')['free']) USDTbalance = float( self.client.get_asset_balance(asset='USDT')['free']) rate = float( self.client.get_order_book(symbol='BTCUSDT')['bids'][0][0]) self.position = 'FLAT' if (rate * BTCbalance / USDTbalance) > 5: self.position = 'BITCOIN' if (rate * BTCbalance / USDTbalance) < 0.2: self.position = 'TETHER' print(self.position) def startSockets(self): self.client = Client(specifications['api_key_p'], specifications['api_secret_p'], { "verify": False, "timeout": 20 }) self.bm = BinanceSocketManager(self.client) self.bm.start_trade_socket('BTCUSDT', self.market_event) self.bm.start_user_socket(self.user_event) self.bm.start() self.bitfinex_client = WssClient() self.bitfinex_client.subscribe_to_ticker(symbol="BTCUSD", callback=self.bitfinex_event) self.bitfinex_client.start() def order(self, orderDetails): type = orderDetails[0] quantity = orderDetails[1] limitPrice = orderDetails[2] precision = 8 price_string = '{:0.0{}f}'.format(limitPrice, precision) if type == 'buy': order = self.client.order_limit_buy(symbol='BTCUSDT', quantity=quantity, price=price_string) self.orderId = order['orderId'] self.orderStatus = order['status'] self.logOrders('buy initiated, order:' + self.orderId) if type == 'sell': order = self.client.order_limit_sell(symbol='BTCUSDT', quantity=quantity, price=price_string) self.orderId = order['orderId'] self.orderStatus = order['status'] self.logOrders('sell initiated, order:' + self.orderId) def bitfinex_event(self, msg): if isinstance(msg, list): if isinstance(msg[1][0], int) or isinstance(msg[1][0], float): self.bitcoin = float(msg[1][0]) def user_event(self, msg): if msg['e'] == 'executionReport': if msg['x'] == 'TRADE': self.logOrders('order filled: ' + msg['i']) self.noOrderInProgress = True mailer.mailer(specifications['mail_a'], specifications['mail_p'], 'order filled: ' + msg['i']) self.position = self.nextPosition def testOrder(self, rate, action, tetP, btcP, limitPrice, quantity, nextPosition): self.position = nextPosition logString = action + ' ' + str(rate) + ', tether: ' + str( tetP) + ', bitcoin: ' + str(btcP) + ', limit: ' + str( limitPrice) + ', quantity: ' + str( quantity) + ', position: ' + nextPosition self.logOrders(logString) self.noOrderInProgress = True def market_event(self, msg): usdt = float(msg.get('p')) if self.noOrderInProgress and (self.bitcoin is not None): rate = self.bitcoin / usdt if self.position == 'FLAT': if rate > triggerHi: limitPrice = self.bitcoin * (1 + orderSpace) quantity = float( self.client.get_asset_balance(asset='BTC')['free']) self.NoOrderInProgress = False self.nextPosition = 'BITCOIN' buySell = 'buy' if production: self.order([buySell, quantity, limitPrice]) if test: self.testOrder(rate, buySell, usdt, self.bitcoin, limitPrice, quantity, self.nextPosition) if rate < triggerLo: limitPrice = self.bitcoin * (1 - orderSpace) quantity = float( self.client.get_asset_balance( asset='USDT')['free']) / limitPrice self.NoOrderInProgress = False self.nextPosition = 'TETHER' buySell = 'sell' if production: self.order([buySell, quantity, limitPrice]) if test: self.testOrder(rate, buySell, usdt, self.bitcoin, limitPrice, quantity, self.nextPosition) if self.position == 'BITCOIN': if rate < 1: limitPrice = self.bitcoin * (1 - orderSpace) quantity = 0.5 * float( self.client.get_asset_balance(asset='BTC')['free']) self.NoOrderInProgress = False self.nextPosition = 'FLAT' buySell = 'sell' if production: self.order([buySell, quantity, limitPrice]) if test: self.testOrder(rate, buySell, usdt, self.bitcoin, limitPrice, quantity, self.nextPosition) if self.position == 'TETHER': if rate > 1: limitPrice = self.bitcoin * (1 + orderSpace) quantity = 0.5 * float( self.client.get_asset_balance( asset='USDT')['free']) / limitPrice self.NoOrderInProgress = False self.nextPosition = 'FLAT' buySell = 'buy' if production: self.order([buySell, quantity, limitPrice]) if test: self.testOrder(rate, buySell, usdt, self.bitcoin, limitPrice, quantity, self.nextPosition) if test: print('tether:', usdt, 'bitcoin:', self.bitcoin, '1 USDT is', '{:10.8f}'.format(rate), 'USD. market position:', self.position) if test: self.logQuotes( str(rate) + ',' + str(usdt) + ',' + str(self.bitcoin)) def logOrders(self, payload): with open("ordersLog.txt", "a+") as file: file.write( str(datetime.datetime.utcnow()) + " " + str(payload) + "\n") def logQuotes(self, payload): with open(quoteLog, 'a') as file: file.write(str(datetime.datetime.utcnow()) + "," + payload + "\n")
class Bitfinex(Exchange): """The Bitfinex exchange. Store your API key and secret in the `BITFINEX_API_KEY` and `BITFINEX_SECRET` environment variables. """ # Allow only 1 instance. In the near future we should change the exchange classes to actually # be singletons, but first we should extract common logic into the `Exchange` base class before # making that change. __instance_exists = False def __init__(self, thread_manager, keys, pairs): """ keys as parsed from keys/bitfinex.json """ assert not Bitfinex.__instance_exists Bitfinex.__instance_exists = True super().__init__(thread_manager) self.__api_key = keys["key"] self.__api_secret = keys["secret"] self.__bfxv1 = ClientV1(self.__api_key, self.__api_secret, 2.0) self.__bfxv2 = ClientV2(self.__api_key, self.__api_secret) self.__ws_client = WssClient(self.__api_key, self.__api_secret) self.__ws_client.authenticate(lambda x: None) self.__ws_client.daemon = True self.__pairs = pairs self.__order_types = { Order.Type.MARKET: "market", Order.Type.LIMIT: "limit", Order.Type.IOC: "immediate-or-cancel", Order.Type.FOK: "fill-or-kill", } self.__book_feeds = {} self.__trade_feeds = {} self.__positions_queue = Queue() self.__positions_feed = self.positions_feed() thread_manager.attach("bitfinex-positions-queue", self.__track_positions) # TODO: Can this be dynamically loaded? (For other exchanges too.) self.__fees = {"maker": 0.001, "taker": 0.002} # TODO: Maybe start this in a lazy way? self.__ws_client.start() self.__frame = pd.Series( 0.0, # setting the initial value to 0 is important for volume tracking to work properly index=pd.MultiIndex.from_product( [[ExchangePair(self.id, x) for x in pairs], ["price", "volume"]]), ) for pair in pairs: pair_feed_book, runner_book = Feed.of( self.__generate_book_feed(pair)) self._thread_manager.attach(f"bitfinex-{pair}-book", runner_book) self.__book_feeds[pair] = pair_feed_book pair_feed_trade, runner_trade = Feed.of( self.__generate_trade_feed(pair)) self._thread_manager.attach(f"bitfinex-{pair}-trade", runner_trade) self.__trade_feeds[pair] = pair_feed_trade @property def id(self): return BITFINEX @staticmethod def encode_trading_pair(pair): base = pair.base if pair.base != Currency("BCH") else Currency("BAB") return f"t{base}{pair.quote}" @staticmethod def decode_trading_pair(pair_string): base_string = pair_string[1:4] if pair_string[1:4] != "BAB" else "BCH" return TradingPair(Currency(base_string), Currency(pair_string[4:7])) def book_feed(self, pair): if pair not in self.__pairs: raise ExchangeError("pair not supported by this Bitfinex client") return self.__book_feeds[pair] def __generate_book_feed(self, pair): msg_queue = Queue() self.__ws_client.subscribe_to_orderbook( Bitfinex.encode_trading_pair(pair), precision="P0", callback=msg_queue.put) order_book = OrderBook(ExchangePair(self.id, pair)) def handle_update(order_book, update): side = Side.BUY if update[2] > 0 else Side.SELL size = abs(update[2]) if update[1] != 0 else 0 order_book.update(side, BookLevel(update[0], size)) while True: msg = msg_queue.get() # Ignore status/subscription dicts, heartbeats if not isinstance(msg, list) or msg[1] == "hb": continue # see https://docs.bitfinex.com/v2/reference#ws-public-order-books for spec. if len(msg) == 2 and isinstance(msg[1][0], list): order_book.clear() for update in msg[1]: handle_update(order_book, update) else: handle_update(order_book, msg[1]) yield order_book def __generate_trade_feed(self, pair): msg_queue = Queue() self.__ws_client.subscribe_to_trades( Bitfinex.encode_trading_pair(pair), callback=msg_queue.put) while True: msg = msg_queue.get() # We only track 'te' trade executions (ignoring "tu" trade updates) # see https://docs.bitfinex.com/v2/reference for spec. if not isinstance(msg, list) or msg[1] != "te": continue self.__frame.loc[ExchangePair(self.id, pair), "price"] = float(msg[2][3]) self.__frame.loc[ExchangePair(self.id, pair), "volume"] += abs(float(msg[2][2])) yield self.__frame def frame(self, pairs): """ Returns frame for requested pairs. """ frame = self.__frame.copy() self.__frame.loc[pd.IndexSlice[ [ExchangePair(self.id, p) for p in pairs], "volume"]] = 0 return frame def positions_feed(self): if not hasattr(self, "__positions_feed") or self.__positions_feed is None: positions_feed, runner = Feed.of( iter(self.__positions_queue.get, None)) self._thread_manager.attach("bitfinex-positions-feed", runner) self.__positions_feed = positions_feed return self.__positions_feed def __track_positions(self): if not hasattr(self, "__positions_feed") or self.__positions_feed is None: positions = defaultdict(float) for pair in self.__pairs: currency = pair.base positions[currency] = 0.0 self.__positions_queue.put(deepcopy(positions)) def on_open(ws): nonce = int(time.time() * 1000000) auth_payload = "AUTH{}".format(nonce) signature = hmac.new(self.__api_secret.encode(), msg=auth_payload.encode(), digestmod=hashlib.sha384).hexdigest() payload = { "apiKey": self.__api_key, "event": "auth", "authPayload": auth_payload, "authNonce": nonce, "authSig": signature, "filter": ["trading"], } ws.send(json.dumps(payload)) def on_message(ws, msg): msg = json.loads(msg) # Ignore heartbeats or other irrelevant msgs if not isinstance(msg, list) or len(msg) <= 1 or msg[1] == "hb": return def update_positions(update, value=None): currency = Bitfinex.decode_trading_pair(update[0]).base # Only track exchange (trading) wallet. if len(update) >= 6: # If position is inactive, zero out balance if update[1] != "ACTIVE": value = 0.0 if value is None: positions[currency] = float(update[2]) else: positions[currency] = value self.__positions_queue.put(deepcopy(positions)) # Disambiguate positions snapshot/update/new/closed: if msg[1] == "ps": for update in msg[2]: update_positions(update) elif msg[1] == "pn" or msg[1] == "pu": update_positions(msg[2]) elif msg[1] == "pc": update_positions(msg[2], 0.0) # TODO: should probably abort def on_error(ws, error): Log.warn( "WS error within __track_positions for exchange {}: {}".format( self.id, error)) # TODO: refactor to not be recursive (bc of stack growth) def on_close(ws): Log.warn("WS closed unexpectedly for exchange {}".format(self.id)) Log.info("restarting WS for exchange {}".format(self.id)) time.sleep(3) self.__track_positions() ws = WebSocketApp( "wss://api.bitfinex.com/ws/", on_message=on_message, on_error=on_error, on_close=on_close, ) ws.on_open = on_open ws.run_forever() @property def positions(self): if self.__positions_feed is None: return self.positions_feed().latest return self.__positions_feed.latest @property def fees(self): return self.__fees def get_warmup_data(self, pairs, duration, resolution): rows = 0 data = {} last_time = None # Collect data from bitfinex API while rows < duration: for pair in pairs: bfx_pair = Bitfinex.encode_trading_pair(pair) limit = min(duration - rows, 5000) if last_time is None: data[pair] = self.__bfxv2.candles(resolution, bfx_pair, "hist", limit=limit) else: limit = min(limit + 1, 5000) data[pair] += self.__bfxv2.candles(resolution, bfx_pair, "hist", limit=limit, end=last_time)[1:] last_time = data[pairs[0]][-1][0] rows = len(data[pairs[0]]) # Prep data for strategy consumption prepped = [] times = [] for i in range(0, len(data[pairs[0]])): frame = {} for pair in pairs: elem = data[pair][i] frame[ExchangePair(self.id, pair), "price"] = elem[2] frame[ExchangePair(self.id, pair), "volume"] = elem[5] prepped.append(pd.Series(frame)) times.append(datetime.fromtimestamp(data[pairs[0]][i][0] / 1000)) prepped = pd.DataFrame(prepped, index=times).iloc[::-1] # TODO: also set last trade prices? return prepped def add_order(self, order): assert order.exchange_id == self.id payload = { "request": "/v1/order/new", "nonce": self.__bfxv1._nonce(), # Bitfinex v1 API expects "BTCUSD", v2 API expects "tBTCUSD": "symbol": Bitfinex.encode_trading_pair(order.pair)[1:], "amount": str(order.size), "price": str(order.price), "exchange": "bitfinex", "side": order.side.name.lower(), "type": self.__order_types[order.order_type], "is_postonly": order.maker_only, } try: response = self.__bfxv1._post("/order/new", payload=payload, verify=True) except TypeError as err: Log.warn("Bitfinex _post type error: {}".format(err)) except Exception as err: Log.warn("Swallowing unexpected error: {}".format(err)) return None Log.debug("Bitfinex-order-response", response) if "id" in response: order = OpenOrder(order, response["id"]) if not response["is_live"]: order.update_status(Order.Status.REJECTED) elif not response["is_cancelled"]: order.update_status(Order.Status.CANCELLED) return order return None def cancel_order(self, order_id): return self.__bfxv1.delete_order(order_id) def order_status(self, order_id): return self.__bfxv1.status_order(order_id) def get_open_positions(self): return self.__bfxv1.active_orders()
for k in cache: ticker = k[1:-3] columns_temp.append('BidPrice-' + ticker) columns_temp.append('BidVolume-' + ticker) columns_temp.append('AskPrice-' + ticker) columns_temp.append('AskVolume-' + ticker) list_columns.append(columns_temp) print(list_columns[0]) to_save = pd.DataFrame(data_combined, columns=list_columns[0]) to_save.to_csv(path_or_buf=results_filename, index=False) my_client.stop() # Just add and remove tickers here. my_client = WssClient(key, secret) my_client.subscribe_to_ticker(symbol="tBABUSD", callback=my_handler) my_client.subscribe_to_ticker(symbol="tBSVUSD", callback=my_handler) my_client.subscribe_to_ticker(symbol="tBTCUSD", callback=my_handler) my_client.subscribe_to_ticker(symbol="tEOSUSD", callback=my_handler) my_client.subscribe_to_ticker(symbol="tETHUSD", callback=my_handler) my_client.subscribe_to_ticker(symbol="tLTCUSD", callback=my_handler) my_client.subscribe_to_ticker(symbol="tNEOUSD", callback=my_handler) my_client.subscribe_to_ticker(symbol="tXRPUSD", callback=my_handler) try: print('It began in Africa...') my_client.start() except KeyboardInterrupt: my_client.stop()
len(positions_strategy_1[instrument_2[1:-3]]) - 1] < 0: print(instrument_1, ': BUY', BidPrice_Stock1, '/ ', instrument_2, ': SELL', BidPrice_Stock2) if pos == 0: pos = 1 ask1_cached = AskPrice_Stock1 bid2_cached = BidPrice_Stock2 if pos < 0: bid1_delta = bid1_cached - AskPrice_Stock1 bid2_delta = BidPrice_Stock2 - ask2_cached bid_total += (bid1_delta + bid2_delta) print('PnL cum:', bid_total) ask1_cached = AskPrice_Stock1 bid2_cached = BidPrice_Stock2 pos = 1 # Just add and remove tickers here. my_client = WssClient(key, secret) my_client.subscribe_to_ticker(symbol=instrument_1, callback=my_handler) my_client.subscribe_to_ticker(symbol=instrument_2, callback=my_handler) try: print('It began in Africa...') my_client.start() except KeyboardInterrupt: my_client.stop() print(' ---This is the end !---')
class MyWssTest(): def __init__(self): self.mywss = WssClient(key=KEY, secret=SECRET) self.mywss2 = None # Tracks Websocket Connection self.connection_timer = None self.connection_timeout = 15 self.keep_running = True self.wssactions = {'hb': self._heartbeat_handler} self.mywss.authenticate(self.cb_auth) self.mywss.start() sleep(5) self._start_timers() def cb_auth(self, message): LOGGER.info(f"cb_auth received {message}") if isinstance(message, list): msg_type = message[1] if msg_type in self.wssactions: self.wssactions[msg_type]() def _stop_timers(self): """Stops connection timers.""" if self.connection_timer: self.connection_timer.cancel() LOGGER.info("_stop_timers(): Timers stopped.") def _start_timers(self): """Resets and starts timers for API data and connection.""" LOGGER.info("_start_timers(): Resetting timers..") self._stop_timers() # Automatically reconnect if we didnt receive data self.connection_timer = threading.Timer(self.connection_timeout, self._connection_timed_out) self.connection_timer.start() def _connection_timed_out(self): """Issues a reconnection if the connection timed out. :return: """ LOGGER.info("_connection_timed_out(): Fired! Issuing reconnect..") self.mywss.authenticate(self.cb_auth) def _heartbeat_handler(self): LOGGER.info("new hb received") self._start_timers() def run(self): counter = 0 while self.keep_running: sleep(1) counter += 1 infomsg = (f'#run nr : {counter} || ' f'thread alive : {self.mywss.is_alive()} || ' f'nr of active threads : {threading.active_count()}') print(infomsg) ### example new order # if counter == 5: # exch_order = self.mywss.new_order("EXCHANGE LIMIT", "tIOTUSD", "-1000", "30") # print(f"new order is {exch_order}") ### example update order if counter == 7: update_info = {"id": "16666882406", "delta": "-400"} self.mywss.update_order(**update_info) ### example use calculations if counter == 8: self.mywss.calc(["position_tIOTUSD"]) ### first reconnect test if counter == 20: self.stopit() ### second reconnect test if counter == 50: self.stopit() def stopit(self): LOGGER.info("trying to stop") self.mywss.close()
class Bitfinex: def __init__(self, account, api_key, api_secret): self.account = account self.api_key = api_key self.api_secret = api_secret self.manager_candlestick = WssClient(self.api_key, self.api_secret) self.manager_depth = WssClient(self.api_key, self.api_secret) self.manager_ticker = WssClient(self.api_key, self.api_secret) self.manager_trades = WssClient(self.api_key, self.api_secret) self.started_candlestick = False self.started_depth = False self.started_ticker = False self.started_trades = False self.markets = None def get_exchange_symbol(self, symbol): if self.markets is None: self.markets = self.account.fetch_markets(self.account.EXCHANGE_BITFINEX) for market in self.markets: if market["symbol"] == symbol: symbol = market["id"] return symbol def start_candlestick_websocket(self, symbol, interval, callback): self.symbol = self.get_exchange_symbol(symbol) self.manager_candlestick.subscribe_to_candles(symbol=self.symbol,timeframe=interval,callback=callback) if self.started_candlestick == False: self.manager_candlestick.start() self.started_candlestick = True def stop_candlestick_websocket(self): self.manager_candlestick.close() def start_ticker_websocket(self, symbol, callback): self.symbol = self.get_exchange_symbol(symbol) self.manager_ticker.subscribe_to_ticker(symbol=self.symbol,callback=callback) if self.started_ticker == False: self.manager_ticker.start() self.started_ticker = True def stop_ticker_websocket(self): self.manager_ticker.close() def start_depth_websocket(self, symbol, callback): self.symbol = self.get_exchange_symbol(symbol) self.manager_depth.subscribe_to_orderbook(symbol=self.symbol,precision="P1",callback=callback) if self.started_depth == False: self.manager_depth.start() self.started_depth = True def stop_depth_websocket(self): self.manager_depth.close() def start_trades_websocket(self, symbol, callback): self.symbol = self.get_exchange_symbol(symbol) self.manager_trades.subscribe_to_trades(symbol=self.symbol,callback=callback) if self.started_trades == False: self.manager_trades.start() self.started_trades = True def stop_trades_websocket(self): self.manager_trades.close()