Esempio n. 1
0
    def _match_order(self, order: OrderData, buy_cross_price, sell_cross_price,
                     buy_best_price, sell_best_price):
        direction = order.direction
        buy_cross = (direction == EnumOrderDirection.BUY
                     and order.price >= buy_cross_price)

        sell_cross = (direction == EnumOrderDirection.SELL
                      and order.price <= sell_cross_price)

        if buy_cross or sell_cross or order.order_type == EnumOrderType.MARKET:
            order.status = EnumOrderStatus.FILLED
            order.executed_volume = order.volume
            order.order_id = generate_id()

            trade = TradeData.from_order(order)
            trade.trade_id = generate_id()

            if direction == EnumOrderDirection.BUY:
                trade.price = min(order.price, buy_best_price)
            else:
                trade.price = max(order.price, sell_best_price)

            trade.commission = trade.price * trade.volume * self.fee_rate
            trade.commission_asset = trade.contract.asset_quote

            logger.debug('trade matched\n%s', trade.pretty_string())
            self.callback(trade)
            self.callback(order)
            return order.client_order_id
        return ''
Esempio n. 2
0
    def match_order(self, order: OrderData):
        logger.debug(f'{self} got order {order}')

        if EnumOrderStatus.NEW == order.status:
            order.status = EnumOrderStatus.FILLED
            order.executed_volume = order.volume
            order.order_id = generate_id()

            trade = TradeData.from_order(order)
            trade.trade_id = generate_id()

            if trade.direction == EnumOrderDirection.BUY:
                trade.price = trade.price * (1 + self.slippage)
            else:
                trade.price = trade.price * (1 - self.slippage)

            trade.commission_asset = order.contract.asset_quote
            trade.commission = trade.price * trade.volume * self.fee_rate

            self.callback(trade)
            self.callback(order)

        elif EnumOrderStatus.CANCELLING == order.status:
            order.status = EnumOrderStatus.CANCELLED
            self.callback(order)
Esempio n. 3
0
    def contract_check(self):
        contract = self.contract

        volume = self.volume = self.floor(self.volume, contract.lot_size)
        price = self.price = self.round(self.price, contract.tick_size)

        if contract.contract_type == EnumContractType.SPOT:
            notional = abs(volume) * price
        else:
            notional = abs(volume)

        if abs(volume) < contract.min_quantity:
            logger.debug(f'Order volume {volume} not reach the min requirement {contract.min_quantity}')
            return False
        if notional < contract.min_notional:
            logger.debug(f'Order notional {notional} not reach the min requirement {contract.min_notional}')
            return False
        if abs(volume) > contract.max_quantity:
            logger.debug(f'Order volume {volume} break the max requirement {contract.max_quantity}')
            return False
        if notional > contract.max_notional:
            logger.debug(f'Order notional {notional} break the max requirement {contract.max_notional}')
            return False
        if price < contract.min_price:
            logger.debug(f'Order price {price} not reach the min requirement {contract.min_price}')
            return False

        return True
Esempio n. 4
0
    def create_order(self, order: OrderData, params: dict = None):
        self.throttle()
        self._local_order_manager.on_order(order)

        order = copy.copy(order)
        contract = self._contracts[order.symbol]
        symbol_root = contract.symbol_root
        if params is None:
            params = {}
        try:
            price = order.price
            if order.order_type == EnumOrderType.MARKET:
                price = None

            logger.debug('%s sending order %s', self, order.client_order_id)
            data = self._exchange.create_order(symbol_root,
                                               self.ORDER_TYPE_MAP[order.order_type],
                                               self.DIRECTION_MAP[order.direction],
                                               order.volume,
                                               price,
                                               params)
            if 'status' in data:
                status = self.STATUS_MAP_REVERSE[data['status']]
            else:
                status = EnumOrderStatus.PENDING
            order.status = status
            order.order_id = data['id']
            logger.debug('%s sent order %s successfully with order id %s and status %s ', self, order.client_order_id,
                         order.order_id, order.status)

        except ccxt.InsufficientFunds as e:
            order.status = EnumOrderStatus.REJECTED
            balance_df = self.fetch_balance()
            base_amount = balance_df.loc[contract.symbol_base]
            quote_amount = balance_df.loc[contract.symbol_quote]
            logger.warn(
                "Insufficient balance for order: %s\n%s\n%s", e.args, order,
                f'There was only {contract.symbol_base} \n'
                f'{base_amount}  \n'
                f'and {quote_amount} \n'
                f'{contract.symbol_quote} \n'
                f'in the balance. '
            )
        except ccxt.InvalidOrder as e:
            order.status = EnumOrderStatus.REJECTED
            logger.warn("InvalidOrder error: %s\n%s", e.args, order)
        except ccxt.ExchangeError as e:
            logger.warn("Exchange error: %s\n%s", e.args, order)
            order.status = EnumOrderStatus.REJECTED
        except ccxt.NetworkError as e:
            logger.warn("Network error: %s\n%s", e.args, order)
            order.status = EnumOrderStatus.ERROR
        except requests.HTTPError as e:
            logger.warn("Http error: %s\n%s", e.args, order)
            order.status = EnumOrderStatus.ERROR

        self._local_order_manager.on_order(order)
        self.on_order(order)
        return order
Esempio n. 5
0
 def _process_order_request(self, order: OrderData):
     exchange = order.contract.exchange
     if exchange in self._gateway_dict:
         gateway = self._gateway_dict[exchange]
         logger.debug('%s send %s', gateway, order)
         gateway.send_order(order)
     else:
         logger.info("No such %s gateway exist", exchange)
Esempio n. 6
0
    def fetch_bars(self, symbol, freq, start_date, end_date=None, return_df=False):
        symbol_root = self.contracts[symbol].symbol_root
        if end_date is None:
            now = datetime.datetime.utcnow()
            end_date = start_date + TIME_INTERVAL_MAP[freq] * (self.N_LIMIT_BAR - 1)
            end_date = min(end_date, now - TIME_INTERVAL_MAP[freq])
        start_timestamp = int(calendar.timegm(start_date.timetuple()) * 1000)
        end_timestamp = int(calendar.timegm(end_date.timetuple()) * 1000)
        time_delta = int(TIME_INTERVAL_MAP[freq].total_seconds() * 1000)

        ohlcv_list = []
        while start_timestamp <= end_timestamp:
            self.throttle()
            ohlcv = self._exchange.fetch_ohlcv(symbol_root, freq, start_timestamp, self.N_LIMIT_BAR)
            if ohlcv is not None and len(ohlcv):
                ohlcv_list.extend(ohlcv)
                actual_last_timestamp = ohlcv[-1][0]
                logger.debug('download {symbol}:{freq} from [{start} ~~ {end}]'.format(
                    symbol=symbol,
                    freq=freq,
                    start=datetime.datetime.fromtimestamp(start_timestamp / 1000),
                    end=datetime.datetime.fromtimestamp(actual_last_timestamp / 1000),
                ))
                expect_last_timestamp = start_timestamp + (self.N_LIMIT_BAR - 1) * time_delta
                next_timestamp = max(actual_last_timestamp, expect_last_timestamp) + time_delta
                start_timestamp = next_timestamp
            else:
                break

        # transform into data frame:
        #   1. check data duplication
        #   2. constrain data in giver date range
        #   3. timestamp -> datetime
        bar_df = pd.DataFrame(ohlcv_list, columns=['datetime', 'open', 'high', 'low', 'close', 'volume'])

        bar_list = []
        if len(bar_df):
            non_duplicate_index = ~bar_df['datetime'].duplicated()
            date_range_index = bar_df['datetime'] <= end_timestamp
            filter_index = non_duplicate_index & date_range_index

            bar_df = bar_df[filter_index]
            bar_df['datetime'] = pd.to_datetime(bar_df['datetime'], unit='ms')
            bar_df['frequency'] = freq
            bar_df['symbol'] = symbol

            if return_df:
                return bar_df

            ohlcv_records = bar_df.to_dict('records')
            for record in ohlcv_records:
                bar = BarData.from_dict(record)
                bar_list.append(bar)
        return bar_list
Esempio n. 7
0
    def match_order(self, order: OrderData):
        if EnumOrderStatus.NEW == order.status:
            self._order_container_dict[order.symbol].add(order)
            order.status = EnumOrderStatus.PENDING
            order.order_id = generate_id()
            self.callback(order)

        elif EnumOrderStatus.CANCELLING == order.status:
            self._order_container_dict[order.symbol].remove_by_id(
                order.client_order_id)
            order.status = EnumOrderStatus.CANCELLED
            self.callback(order)
            logger.debug('order %s cancelled', order.client_order_id)
Esempio n. 8
0
    def fetch_order_status(self, order: OrderData):
        self.throttle()
        logger.debug('fetch status for order %s', order)
        symbol = order.symbol
        symbol_root = self.contracts[symbol].symbol_root
        order_id = order.order_id
        data = self._exchange.fetch_order(order_id, symbol_root)
        order_status = copy.copy(order)

        order_status.status = self.STATUS_MAP_REVERSE[data['status']]
        order_status.executed_volume = data['filled']

        return order_status
Esempio n. 9
0
 def on_packet(self, packet: dict):
     if 'type' in packet:
         name = packet['type']
         if name in self._callback_dict:
             callback = self._callback_dict[name]
             callback(packet)
         elif name == 'ping':
             pass
         else:
             logger.debug(
                 "%s received data without corresponding callback: %s",
                 self, packet)
     else:
         logger.debug("%s received other data: %s", self, packet)
Esempio n. 10
0
 def _save(self, base_data: BaseData):
     if isinstance(base_data, OrderData):
         self.save_order(base_data)
     elif isinstance(base_data, TradeData):
         self.save_trade(base_data)
     elif isinstance(base_data, PositionData):
         self.save_position(base_data)
     elif isinstance(base_data, BalanceData):
         self.save_balance(base_data)
     elif isinstance(base_data, PnLData):
         self.save_pnl(base_data)
     else:
         raise TypeError(
             'Not an expect data to store, got {}'.format(base_data))
     logger.debug('save {} to database'.format(base_data))
Esempio n. 11
0
 def on_order_status(self, order: OrderData):
     client_order_id = order.client_order_id
     if order.is_closed():
         if client_order_id in self._order_frozen_dict:
             asset, amount = self._order_frozen_dict[client_order_id]
             balance = self._get_balance(asset)
             balance.frozen_amount -= amount
             del self._order_frozen_dict[client_order_id]
             logger.debug('%s unfreeze %.6f amount of %s by order %s', self,
                          amount, asset, order.client_order_id)
     else:
         if client_order_id not in self._order_frozen_dict:
             symbol = order.symbol
             order_type = order.order_type
             if order_type in ORIGIN_ORDER_TYPES:
                 asset, amount = self._get_position(symbol).frozen_by_order(
                     order)
                 balance = self._get_balance(asset)
                 balance.frozen_amount += amount
                 self._order_frozen_dict[order.client_order_id] = (asset,
                                                                   amount)
                 logger.debug('%s freeze %.6f amount of %s by order %s',
                              self, amount, asset, order.client_order_id)
Esempio n. 12
0
    def cancel_order(self, order_cancel: OrderData):
        self.throttle()
        self._local_order_manager.on_order(order_cancel)

        order_id = order_cancel.order_id
        contract = self._contracts[order_cancel.symbol]
        symbol_root = contract.symbol_root
        logger.debug('%s cancel order %s', self, order_cancel.client_order_id)
        try:
            self._exchange.cancel_order(order_id, symbol_root)
            logger.debug('%s canceled order %s successfully', self, order_cancel.client_order_id)
            order_cancel.status = EnumOrderStatus.CANCELLED
        except ccxt.errors.OrderNotFound as e:
            logger.info("Order already been closed or cancelled before: %s\n%s", e.args, order_cancel)
            order_status = self.fetch_order_status(order_cancel)
            order_cancel.on_order(order_status)
        except ccxt.errors.NetworkError as e:
            logger.info("Network error: %s\n%s", e.args, order_cancel)
            order_cancel.status = EnumOrderStatus.CANCEL_ERROR

        self._local_order_manager.on_order(order_cancel)
        self.on_order(order_cancel)
        return order_cancel
Esempio n. 13
0
    def on_user_stream(self, data):
        if data['e'] == 'executionReport':
            logger.debug(f'{self} received raw data {data}')
            if data['C'] != 'null':
                cli_id = data['C']
            else:
                cli_id = data['c']

            order = self.api.oms.clone(cli_id)
            if order is None:
                return

            order.order_id = str(data['i'])
            order.executed_volume = float(data['z'])
            order.executed_notional = float(data['Z'])
            order.status = STATUS_MAP_REVERSE[data['X']]

            if float(data['l']):
                trade = TradeData.from_order(order)
                trade.trade_id = str(data['t'])
                trade.price = float(data['L'])
                trade.volume = float(data['l'])
                trade.commission = float(data['n'])
                trade.commission_asset = '.'.join((data['N'], self.TAG))
                trade.datetime = datetime.datetime.utcfromtimestamp(data['E'] /
                                                                    1000.0)
                trade.strategy_id = order.strategy_id
                trade.client_order_id = order.client_order_id

                contract = trade.contract
                if trade.commission != 0 and trade.commission_asset == 'BNB.BNC':
                    if (trade.commission_asset != contract.asset_base) and \
                            (trade.commission_asset != contract.asset_quote):

                        commission_trade = trade.copy()
                        commission_trade.commission = 0.0
                        commission_trade.symbol = 'BNB' + contract.symbol_quote + '.BNC'
                        commission_trade.direction = EnumOrderDirection.SELL

                        notional = trade.volume * trade.price * 0.00075
                        commission_trade.price = notional / trade.commission
                        commission_trade.volume = notional / commission_trade.price
                        commission_trade.trade_id = generate_id()
                        self.api.on_trade(commission_trade)

                        trade.commission_asset = contract.asset_quote
                        trade.commission = notional

                self.api.on_trade(trade)
                logger.debug("%s: receive %s", self, trade)

            self.api.on_order(order)
            if order.is_closed():
                self.api.oms.pop(order.client_order_id)

            logger.debug("%s: receive %s", self, order)
Esempio n. 14
0
    def cancel_order(self, client_order_id):
        if not self._trading_mode:
            logger.info(
                'Cancel request not send, Reason: %s is in trading state %s',
                self, self._trading_mode)
            return

        if client_order_id not in self._working_order_dict:
            logger.debug(
                'Cancel request not send, Reason: order %s is not in working order list',
                client_order_id)
            return

        working_order = self._working_order_dict[client_order_id]

        if working_order.is_closed():
            logger.info(
                'Cancel request not send, Reason: order %s is already finished with status %s',
                working_order.client_order_id, working_order.status)
            return

        if working_order.status == EnumOrderStatus.CANCELLING:
            logger.debug(
                'Cancel request not send, Reason: order %s has been sent before',
                client_order_id)
            return

        if not working_order.order_id:
            logger.info(
                'Cancel request not send, Reason: order %s is not alive in exchange yet (no order id)',
                client_order_id)
            return

        cancel_req = copy.copy(working_order)
        cancel_req.status = EnumOrderStatus.CANCELLING
        logger.debug('%s cancel order with client order id %s', self,
                     client_order_id)
        self.broker.send_order(cancel_req)
Esempio n. 15
0
 def send_order(self, order: OrderData):
     logger.debug(f'{self} plan to send {order.pretty_string()}')
     self._send_order_impl(copy.copy(order))
Esempio n. 16
0
 def on_trade(self, trade: TradeData):
     super(AlgorithmTemplate, self).on_trade(trade)
     logger.debug(f'{self} receive new execution volume {trade.volume}')
     trade_notional = trade.volume * trade.price
     self.target_order.executed_volume += trade.volume
     self.target_order.executed_notional += trade_notional
Esempio n. 17
0
 def add(self, order: OrderData):
     if order.client_order_id not in self._order_dict:
         self._count += 1
         self._order_dict[order.client_order_id] = order
         logger.debug('order %s created in matcher', order.client_order_id)