Ejemplo n.º 1
0
#Accessing data stored in BtfxWss:
ticker_q = wss.tickers('BTCUSD')
while not ticker_q.empty():
    print(ticker_q.get()[0][0][0])
    break

##################################################################################

curOrder = wss.orders.get()
nbOrders = len(curOrder[0][1])  #number of orders

pricy = "8399"  #Must be string

if nbOrders != 0:  #Cancel all orders
    for item in curOrder[0][1]:
        wss.cancel_order(id=item[0])
#######################################################################
Morder = {
    "type": "EXCHANGE LIMIT",
    "symbol": "tBTCUSD",
    "amount": "-0.007",
    "price": "",
    "hidden": 0
}

Zorder = replace_value_with_definition(Morder, 'price', pricy)

time.sleep(5)  #First make sure to cancel

wss.new_order(**Zorder)
print(wss.orders_new.get())
Ejemplo n.º 2
0
class Trader(_Account):
    """
    Class interface for trading on the Bitfinex Exchange
    :param key: str: Bitfinex api-key
    :param secret:  str: Bitfinex api-secret
    """
    _sleep_time = 0.01
    _min_order_value = 35.
    _max_order_history = 100
    _trade_types = {'market', 'limit'}
    _sources = ('Orders', 'Order New', 'Order Update', 'Order Cancel',
                'Wallets')

    def __init__(self, key, secret):
        super(Trader, self).__init__()
        self.symbols = []
        self.symbols_gen = get_symbols_as_updated()
        self.wss = BtfxWss(key=key, secret=secret, log_level='CRITICAL')
        self._disconnect_event = Event()
        self._receiver = None

    def cancel(self, _id):
        """
        Cancel an order
        :param _id: int: order id
        :return: None
        """
        self.wss.cancel_order(multi=False, id=_id)

    def cancel_all(self, older_than=0):
        """
        Cancel all orders older than a certain time
        :param older_than: int/float:
            age of order that should be cancelled (in seconds)
        :return: None
        """
        now = time.time()
        for _id, order in self._orders.copy().items():
            if now - order['timestamp'] > older_than:
                self.cancel(_id)

    def connect(self):
        """Open a connection to a Bitfinex websocket"""
        self.wss.start()
        while not self.wss.conn.connected.is_set():
            time.sleep(1e-4)
        self.wss.authenticate()
        # This thread will wait for 99.999% of its life
        subscriber_thread = Thread(target=self._subscribe)
        subscriber_thread.setDaemon(True)
        subscriber_thread.start()
        # This thread should be working and is therefore joined
        self._receiver = Thread(target=self._receive)
        self._receiver.start()

    def close(self):
        """Close the connection to the Bitfinex websocket"""
        self._disconnect_event.set()
        for symbol in self.symbols:
            self.wss.unsubscribe_from_ticker(symbol)
        self.wss.stop()
        self._receiver.join()

    def order(self,
              symbol,
              price,
              *,
              dollar_amount=None,
              ratio=None,
              value_ratio=None,
              trade_type='limit',
              pad_price=None,
              return_id=True):
        """
        Make an exchange order
        :param symbol: str:
            ticker symbol to order (e.g. 'BTCUSD', 'ETHUSD'...)

        :param price: float:
            Price at which to submit order
            You can also pass "market" as an argument for price
            to order at current market value
            e.g. Trader.order('BTCUSD', 'market', dollar_amount=5)

        :param dollar_amount: float:
            Dollar equivalent value of trade amount (negative for selling)
            e.g. ordering 10 BTCUSD at $5 per coin
                -> Trader.order('BTCUSD', 5, dollar_amount=50)

        :param ratio: float:
            Ratio of available balance for requested symbol (negative for sell)
            e.g. With $1000 USD in wallet, ordering 100 BTCUSD at $5 per coin
                -> Trader.order('BTCUSD', 5, ratio=0.5)

        :param value_ratio: float:
            Ratio of Trader.value (negative for selling)
            e.g. With $500 in USD and $500 in BTCUSD (at $5) in wallet,
                 ordering 100 BTCUSD at $5 per coin
                -> Trader.order('BTCUSD', 5, value_ratio=0.5)

        :param trade_type: str: (optional)
            Bitfinex api trade type - one of {'market', 'limit'}
            see https://www.bitfinex.com/features for details

        :param pad_price: float: (optional)
            Ratio based price padding - used to undercut or overshoot price
            If buying then pad_price * price is added to submitted price
            If selling then pad_price * price is subtracted from price

        :param return_id: bool: (optional)
            If true, Trader.order blocks until order_id is returned
            from Bitfinex, otherwise returns immediately

        :return: int:
            If return_id is True, then order id is returned, otherwise None

        :raises: AssertionError:
            If values passed are non-consistent
        """

        assert dollar_amount or ratio or value_ratio, \
            'Must provide either `dollar_amount`, `ratio` or `value_ratio`'
        assert sum(bool(i) for i in [dollar_amount, ratio, value_ratio]) == 1,\
            'Must provide only 1 of `dollar_amount`, `ratio` or `value_ratio`'
        assert trade_type.lower() in self._trade_types, \
            'Unknown trade type, try one of these: %s' % str(self._trade_types)

        symbol_lower = symbol.lower().replace('usd', '')
        symbol = symbol.upper().split('USD')[0] + 'USD'
        buying = (dollar_amount or ratio or value_ratio) > 0

        if price == 'market' or trade_type == 'market':
            trade_type = 'market'
            price = self._prices[symbol]

        if pad_price:
            delta = price * pad_price
            price += max(delta, 0.01) if buying else min(-delta, -0.01)
        assert price >= 0.01, 'Price cannot be less than $0.01'

        if buying:
            max_amount = self.available_balances['usd'] / price
        else:
            max_amount = self.available_balances[symbol_lower]

        if dollar_amount:
            amount = dollar_amount / price
        elif ratio:
            amount = max_amount * ratio
        else:
            amount = min(max(-max_amount, self.value * value_ratio / price),
                         max_amount)
        amount, max_amount = round(amount, 8), round(max_amount, 8)

        assertion_msg = 'Trade value (${:.2f}) is %s available trade value ' \
                        '($%.2f)'.format(abs(amount * price))
        assert abs(amount) >= self._min_order_value / price, \
            assertion_msg % ('below minimum', self._min_order_value)
        assert abs(amount) <= max_amount, \
            assertion_msg % ('above maximum', max_amount * price)

        current_order_ids = set(self._orders.keys())
        if len(self._executed_orders) == 0:
            last_executed_id = None
        else:
            last_executed_id = self._executed_orders[-1][0]

        self.wss.new_order(
            cid=int(time.time()),
            type="EXCHANGE %s" % trade_type.upper(),
            symbol="t%s" % symbol,
            amount="%.8f" % amount,
            price="%.2f" % price,
        )

        while return_id:
            for _id in self._orders.copy().keys():
                # new order arrives in _orders
                if _id not in current_order_ids:
                    return _id
            else:
                # new order is executed immediately
                if len(self._executed_orders) > 0 \
                 and self._executed_orders[-1][0] != last_executed_id:
                    return self._executed_orders[-1][0]
                time.sleep(self._sleep_time)

    def subscribe(self, symbol):
        """
        Subscribe to a symbol for price watching
        :param symbol: str: symbol to subscribe to (e.g. 'BTCUSD', 'ETHUSD'...)
        """
        self.symbols.append(symbol)
        self.wss.subscribe_to_ticker(symbol)

    def wait_execution(self, _id, seconds=1e9):
        """
        Waits for an order to execute
        :param _id: int: id of order to wait for
        :param seconds: int/float: time to wait before raising error
        :return: dict: order in json format
        :raises: TimeoutError (if order is not executed in given time)
        """
        start_time = time.time()
        while time.time() - start_time < seconds:
            try:
                ids, trades = tuple(zip(*self._executed_orders))
                return trades[ids.index(_id)]
            except ValueError:
                time.sleep(self._sleep_time)
        raise TimeoutError('Waiting for execution of order '
                           '%d timed out after %d seconds' % (_id, seconds))

    def _receive(self):
        while not self._disconnect_event.is_set():
            for source in self._sources:
                with suppress(Empty):
                    q = self.wss.queue_processor.account[source]
                    cmd, data = q.get_nowait()[0]
                    self._update(cmd, data)

            for symbol in self.symbols:
                with suppress(Empty):
                    q = self.wss.queue_processor.tickers[('ticker', symbol)]
                    data = q.get_nowait()[0]
                    self._update('t' + symbol, data)

    def _subscribe(self):
        for symbol in self.symbols_gen:
            self.subscribe(symbol)
Ejemplo n.º 3
0
class WebSocketApi(object):
    """ Wrapper to use BtfxWss. """
    def __init__(self, symbols=None, callbacks=None):
        """
        Args:
            symbols: A list used to subscribe tickers.
            callbacks: A list of functions to handle events:
                'reset',
                'process_wallet',
                'process_order',
                'process_tick',
                'process_notification'
        """
        required_callbacks = [
            'reset',
            'process_wallet',
            'process_order',
            'process_tick',
            'process_notification'
        ]
        if not symbols or not callbacks:
            log(LOG_ERROR, 'Require parameters symbols and callbacks')
            return
        for callback in required_callbacks:
            if callback not in callbacks:
                log(LOG_ERROR, 'Require %s callback function' % callback)
                return

        self._tick_symbols = symbols
        self._callbacks = callbacks

        self._received_order_snapshot = False
        self._received_wallet_snapshot = False
        self._wss = BtfxWss(
            key=config.BFX_API_KEY, secret=config.BFX_API_SECRET)
        self._wss.start()

    def __connect(self):
        """
        Reset data and subscribe tick data after connect to server.
        """
        log(LOG_INFO, "Server connected")
        self._received_order_snapshot = False
        self._received_wallet_snapshot = False

        self._wss.authenticate()
        for pair in self._tick_symbols:
            symbol = 't' + pair
            self._wss.subscribe_to_ticker(symbol)

    def __check_system(self):
        """ Check the connection is established or not. """
        try:
            server_q = self._wss.opened
            while not server_q.empty():
                server_q.get()
                self._callbacks['reset']()
                self.__connect()
        except KeyError:
            # KeyError means Btfxwss doesn't get related information yet.
            # It's fine to pass and check in the next time.
            pass

    def __check_account_info(self):
        """ Check account information. """
        try:
            wallets_q = self._wss.wallets
            while not wallets_q.empty():
                self.__received_wallets(wallets_q.get()[0][1])

            wallet_update_q = self._wss.wallet_update
            while not wallet_update_q.empty():
                self.__received_wallet_update(
                    wallet_update_q.get()[0][1])

            orders_q = self._wss.orders
            while not orders_q.empty():
                self.__received_orders(orders_q.get()[0][1])

            order_new_q = self._wss.order_new
            while not order_new_q.empty():
                self.__received_order(order_new_q.get()[0][1])

            order_cancel_q = self._wss.order_cancel
            while not order_cancel_q.empty():
                self.__received_order(order_cancel_q.get()[0][1])

            notification_q = self._wss.notifications
            while not notification_q.empty():
                self.__received_notification(notification_q.get()[0][1])
        except KeyError:
            # KeyError means Btfxwss doesn't get related information yet.
            # It's fine to pass and check in the next time.
            pass

    def __check_tickers(self):
        """ Check tick data. """
        for symbol in self._tick_symbols:
            try:
                trades_q = self._wss.tickers(symbol)
                while not trades_q.empty():
                    self.__received_tickers(symbol, trades_q.get()[0])
            except KeyError:
                # KeyError means Btfxwss doesn't get related information
                # yet. It's fine to pass and check in the next time.
                pass

    def __check_unused_info(self):
        """
        Websocket may have many events which are not used.
        Just pop it and ignore it. Otherwise, the queue may use too many
        memories.
        """
        def pop_queue(queue):
            """ pop unused queue """
            while not queue.empty():
                queue.get()

        try:
            queues = [
                self._wss.credits, self._wss.offer_new,
                self._wss.offer_cancel, self._wss.credit_new,
                self._wss.credit_close, self._wss.credit_update,
                self._wss.positions, self._wss.offer_update,
                self._wss.order_update, self._wss.position_update,
                self._wss.position_close, self._wss.loan_new,
                self._wss.loan_close, self._wss.loan_update,
                self._wss.unknown]

            for queue in queues:
                pop_queue(queue)
        except KeyError:
            # KeyError means Btfxwss doesn't get related information yet.
            # It's fine to pass and check in the next time.
            pass

    def __received_wallets(self, wallets):
        """
        Handle wallet snapshot.
        Args:
            wallets: balance of all currencies
        """
        for wallet in wallets:
            self._callbacks['process_wallet'](bitfinex_v2_rest.Wallet(wallet))
        self._received_wallet_snapshot = True

    def __received_wallet_update(self, wallet):
        """
        Handle wallet update.
        Args:
            wallets: balance of one currency
        """
        self._callbacks['process_wallet'](bitfinex_v2_rest.Wallet(wallet))

    def __received_orders(self, orders):
        """
        Handle order snapshot.
        Args:
            orders: current orders snapshot
        """
        for order in orders:
            self._callbacks['process_order'](bitfinex_v2_rest.Order(order))
        self._received_order_snapshot = True

    def __received_order(self, order):
        """
        Handle one order
        Args:
            order: order status
        """
        self._callbacks['process_order'](bitfinex_v2_rest.Order(order))

    def __received_tickers(self, pair, tickers):
        """
        Handle ticks
        Args:
            pair: ex BTCUSD
            tickers: tickers of the pair
        """
        if isinstance(tickers, list):
            for tick in tickers:
                self._callbacks['process_tick'](
                    bitfinex_v2_rest.TradingTicker([pair] + tick))

    def __received_notification(self, message):
        """
        Handle notification
        Args:
            message: notification from web socket
        """
        self._callbacks['process_notification'](
            bitfinex_v2_rest.Notifications(message))

    def new_order(self, pair, price, amount):
        """
        Create an new order.
        Args:
            pair: ex BTCUSD.
            price: 0 means the market order.
            amount: Positive number means buy order. Negative number means
                    sell order.
        """
        symbol = 't' + pair
        cid = int(round(time.time() * 1000))
        if price > 0:
            order = {
                'cid': cid,
                'type': "EXCHANGE LIMIT",
                'symbol': symbol,
                'amount': str(amount),
                'price': str(price),
                'hidden': 0
                }
        else:
            order = {
                'cid': cid,
                'type': "EXCHANGE MARKET",
                'symbol': symbol,
                'amount': str(amount),
                'hidden': 0
                }
        self._wss.new_order(**order)

    def cancel_order(self, order_id):
        """
        Cancel an order.
        Args:
            order_id: The id of the order.
        """
        value = {'id': order_id}
        self._wss.cancel_order(False, **value)

    def check_events(self):
        """ Check all events from web socket """
        self.__check_system()
        self.__check_account_info()
        self.__check_tickers()
        self.__check_unused_info()

    def is_received_order_snapshot(self):
        """ Return True if recevied order snapshot """
        return self._received_order_snapshot

    def is_received_wallet_snapshot(self):
        """ Return True if recevied wallet snapshot """
        return self._received_wallet_snapshot