Ejemplo n.º 1
0
    def on_order_book_update_message(self, instrument: int,
                                     sequence_number: int,
                                     ask_prices: List[int],
                                     ask_volumes: List[int],
                                     bid_prices: List[int],
                                     bid_volumes: List[int]) -> None:
        """Called periodically to report the status of an order book.

        The sequence number can be used to detect missed or out-of-order
        messages. The five best available ask (i.e. sell) and bid (i.e. buy)
        prices are reported along with the volume available at each of those
        price levels.
        """
        if instrument == Instrument.FUTURE:
            future_order_book = [
                list(zip(ask_prices, ask_volumes)),
                list(zip(bid_prices, bid_volumes))
            ]
            all_prices = np.array(ask_prices + bid_prices)
            all_volumes = np.array(ask_volumes + bid_volumes)
            # Weighted average of both bid and ask prices and volumes
            fair_value = int(
                round(
                    np.sum(
                        np.multiply(
                            all_prices,
                            np.divide(all_volumes, np.sum(all_volumes)))) /
                    100) * 100)
            if len(self.future_data) < NUM_POINTS:
                self.future_data.append(fair_value)
            else:
                self.future_data = self.future_data[1:] + [fair_value]
                if self.position == 0 and self.waiting_for_server is False:
                    self.can_amend_order = True
                    if len(self.etf_data) == NUM_POINTS:
                        slope, intercept, r_value, p_value, std_err = lin_reg(
                            range(1, 11), self.future_data)
                        r_squared = r_value**2
                        # Case - no trend in futures market --> market maker role
                        # p-value for a hypothesis test whose null hypothesis is that the slope is zero
                        if p_value >= CRIT_VAL:
                            total_volume = self.ideal_trade_volume(
                                self.etf_data)
                            side_volume = total_volume // 2
                            self.logger.warning(
                                f"Total trade volume: {total_volume}")
                            self.ask_id = next(self.order_ids)
                            self.bid_id = next(self.order_ids)
                            # Subcases - low volatility, confident market vs. high volatility, uncertain market
                            ask_price = (fair_value + SPREAD // 2
                                         if r_squared > R_SQUARED_THRESH else
                                         fair_value + SPREAD)
                            self.send_insert_order(self.ask_id, Side.SELL,
                                                   ask_price, side_volume,
                                                   Lifespan.GOOD_FOR_DAY)
                            bid_price = (fair_value - SPREAD // 2
                                         if r_squared > R_SQUARED_THRESH else
                                         fair_value - SPREAD)
                            self.send_insert_order(self.bid_id, Side.BUY,
                                                   bid_price, side_volume,
                                                   Lifespan.GOOD_FOR_DAY)
                            self.waiting_for_server = True
                        # Case - upward trending futures market/arbitrage opportunities --> market taker role
                elif np.abs(self.position) > BUY_SELL_DIFF_THRESH and self.waiting_for_server is False and \
                        self.can_amend_order is True:
                    self.logger.warning(
                        f"Warning - cornering market with ETF position: {self.position}"
                    )
                    # Too many buy orders, need to readjust ask price
                    if self.position > 0:
                        self.send_cancel_order(self.ask_id)
                        self.ask_id = next(self.order_ids)
                        ask_price = self.etf_order_book[0][1][0]
                        self.send_insert_order(self.ask_id, Side.SELL,
                                               ask_price,
                                               self.ask_remaining_volume,
                                               Lifespan.GOOD_FOR_DAY)
                    # Too many sell orders, need to readjust bid price
                    else:
                        self.send_cancel_order(self.bid_id)
                        self.bid_id = next(self.order_ids)
                        bid_price = self.etf_order_book[1][1][0]
                        self.send_insert_order(self.bid_id, Side.BUY,
                                               bid_price,
                                               self.bid_remaining_volume,
                                               Lifespan.GOOD_FOR_DAY)
                    self.waiting_for_server = True
                    self.can_amend_order = False
        else:
            self.etf_order_book = [
                list(zip(ask_prices, ask_volumes)),
                list(zip(bid_prices, bid_volumes))
            ]
            if len(self.etf_data) < NUM_POINTS:
                self.etf_data.append(np.var(ask_prices + bid_prices))
            else:
                self.etf_data = self.etf_data[1:] + [
                    np.var(ask_prices + bid_prices)
                ]
Ejemplo n.º 2
0
    def on_order_book_update_message(self, instrument: int,
                                     sequence_number: int,
                                     ask_prices: List[int],
                                     ask_volumes: List[int],
                                     bid_prices: List[int],
                                     bid_volumes: List[int]) -> None:
        """Called periodically to report the status of an order book.

        The sequence number can be used to detect missed or out-of-order
        messages. The five best available ask (i.e. sell) and bid (i.e. buy)
        prices are reported along with the volume available at each of those
        price levels.
        """
        if instrument == Instrument.FUTURE:
            best_ask = ask_prices[0]
            best_bid = bid_prices[0]
            # Average of best ask and bid price rounded to nearest multiple of tick size
            fair_value = int(round(((best_ask + best_bid) / 2) / 100) * 100)
            if len(self.future_data) < NUM_POINTS:
                self.future_data.append(fair_value)
            else:
                self.future_data = self.future_data[1:] + [fair_value]
        else:
            self.etf_order_book = [
                list(zip(ask_prices, ask_volumes)),
                list(zip(bid_prices, bid_volumes))
            ]
            if len(self.etf_data) < NUM_POINTS:
                self.etf_data.append(np.var(ask_prices + bid_prices))
            else:
                self.etf_data = self.etf_data[1:] + [
                    np.var(ask_prices + bid_prices)
                ]
        # Primary function for inserting new pairs of orders (bid and ask)
        if len(self.future_data) == NUM_POINTS and len(
                self.etf_data) == NUM_POINTS:
            # Most recent stored fair value of future
            fair_value = self.future_data[-1]
            # self.logger.warning(f"Fair value of future: {fair_value}")
            if self.waiting_for_server is False and len(
                    self.active_orders) == 0:
                slope, intercept, r_value, p_value, std_err = lin_reg(
                    range(NUM_POINTS), self.future_data)
                r_squared = r_value**2
                # Case 1) - no trend in futures market --> market maker role
                # p-value for a hypothesis test whose null hypothesis is that the slope is zero
                if p_value >= CRIT_VAL:
                    volume = self.ideal_trade_volume(self.etf_data)
                    # self.logger.warning(f"Trade volume: {volume*2}")
                    ask_id = next(self.order_ids)
                    bid_id = next(self.order_ids)
                    # Subcase 1) - low volatility, confident market
                    # self.logger.warning(f"R squared: {r_squared}")
                    if r_squared > R_SQUARED_THRESH:
                        ask_price = int(fair_value + UPPER_SPREAD)
                        bid_price = int(fair_value - LOWER_SPREAD)
                    # Subcase 2) - high volatility, uncertain market
                    else:
                        # Best ask and bid price in etf order book
                        ask_price = self.etf_order_book[0][0][0]
                        bid_price = self.etf_order_book[1][0][0]
                    self.send_insert_order(ask_id, Side.SELL, ask_price,
                                           volume, Lifespan.GOOD_FOR_DAY)
                    self.send_insert_order(bid_id, Side.BUY, bid_price, volume,
                                           Lifespan.GOOD_FOR_DAY)
                    # Store ask volume as negative, bid volume as positive
                    self.active_orders[ask_id] = [-volume]
                    self.active_orders[bid_id] = [volume]
                    self.waiting_for_server = True
                # Case 2) - upward trending futures market/arbitrage opportunities --> market taker role
        # IMPLEMENTATION CORRECT
        if len(self.active_orders) == 2:
            for order_id in self.active_orders:
                self.active_orders[order_id].append(
                    self.active_orders[order_id][-1])
                # Ensure list will always only store 2 volumes at any given time
                if len(self.active_orders[order_id]) == 4:
                    self.active_orders[order_id] = self.active_orders[
                        order_id][1:]
        # IMPLEMENTATION INCORRECT
        if self.net_position > 0:
            for order_id in list(self.active_orders):
                # Ensuring we obtain the relevant ask order
                if self.active_orders[order_id][-1] < 0 and len(self.active_orders[order_id]) == 3 and \
                        self.active_orders[order_id][-1] == self.active_orders[order_id][-3]:
                    self.send_cancel_order(order_id)
                    self.active_orders.pop(order_id)
                    ask_id = next(self.order_ids)
                    # Best bid  price in etf order book
                    ask_price = self.etf_order_book[0][0][
                        0]  # Might change this to cross the spread if doesn't w
                    volume = np.abs(self.net_position)
                    self.send_insert_order(ask_id, Side.SELL, ask_price,
                                           volume, Lifespan.GOOD_FOR_DAY)
                    self.active_orders[ask_id] = [-volume]
        elif self.net_position < 0:
            for order_id in list(self.active_orders):
                # Ensuring we obtain the relevant bid order
                if self.active_orders[order_id][-1] > 0 and len(self.active_orders[order_id]) == 3 and \
                        self.active_orders[order_id][-1] == self.active_orders[order_id][-3]:
                    self.send_cancel_order(order_id)
                    self.active_orders.pop(order_id)
                    bid_id = next(self.order_ids)
                    # Best ask price in etf order book
                    bid_price = self.etf_order_book[1][0][
                        0]  # Might change this to cross the spread if doesn't w
                    volume = np.abs(self.net_position)
                    self.send_insert_order(bid_id, Side.BUY, bid_price, volume,
                                           Lifespan.GOOD_FOR_DAY)
                    self.active_orders[bid_id] = [volume]
        volume = np.abs(self.net_position)
        # IMPLEMENTATION CORRECT
        # Can open new set of orders once net ETF position is 0
        if np.abs(self.net_position
                  ) < BUY_SELL_DIFF_THRESH and self.waiting_for_server is True:
            order_volume_zero = True
            # Secondary check to ensure volume on both sides has been filled
            for order_id in self.active_orders:
                if self.active_orders[order_id][-1] != 0:
                    order_volume_zero = False
                    break
            if order_volume_zero:
                self.active_orders = {}
                self.waiting_for_server = False
        # IMPLEMENTATION CORRECT
        # Cornering market case
        elif np.abs(
                self.net_position
        ) > BUY_SELL_DIFF_THRESH and self.waiting_for_server is True:
            can_cancel = True
            for order_id in self.active_orders:
                if len(self.active_orders[order_id]) != 3:
                    can_cancel = False
                elif len(self.active_orders[order_id]) == 3 and len(
                        set(self.active_orders[order_id])) > 1:
                    can_cancel = False
            if can_cancel:
                for order_id in self.active_orders:
                    self.send_cancel_order(order_id)
                if self.net_position > 0:
                    ask_id = next(self.order_ids)
                    # Best ask price in etf order book
                    ask_price = self.etf_order_book[0][0][0]
                    self.send_insert_order(ask_id, Side.SELL, ask_price,
                                           volume, Lifespan.FILL_AND_KILL)
                elif self.net_position < 0:
                    bid_id = next(self.order_ids)
                    # Best bid price in etf order book
                    bid_price = self.etf_order_book[1][0][0]
                    self.send_insert_order(bid_id, Side.BUY, bid_price, volume,
                                           Lifespan.FILL_AND_KILL)
                self.active_orders = {}

        if len(self.active_orders) == 0:
            self.waiting_for_server = False
        self.logger.warning(f"Active orders: {self.active_orders}")
Ejemplo n.º 3
0
    def on_order_book_update_message(self, instrument: int,
                                     sequence_number: int,
                                     ask_prices: List[int],
                                     ask_volumes: List[int],
                                     bid_prices: List[int],
                                     bid_volumes: List[int]) -> None:
        """Called periodically to report the status of an order book.

        The sequence number can be used to detect missed or out-of-order
        messages. The five best available ask (i.e. sell) and bid (i.e. buy)
        prices are reported along with the volume available at each of those
        price levels.
        """
        # print(f"Pre execution ask orders: {self.active_ask_orders}")
        # print(f"Pre execution bid orders: {self.active_bid_orders}")
        if instrument == Instrument.FUTURE:
            self.fair_value_future = (ask_prices[0] + bid_prices[0]) / 2
            self.best_future_ask_price = ask_prices[0]
            self.best_future_bid_price = bid_prices[0]
            if len(self.future_data) < NUM_POINTS:
                self.future_data.append(self.fair_value_future)
            else:
                self.future_data = self.future_data[1:] + [
                    self.fair_value_future
                ]
        else:
            self.fair_value_etf = (ask_prices[0] + bid_prices[0]) / 2
            self.best_etf_ask_price = ask_prices[0]
            self.best_etf_bid_price = bid_prices[0]

        volume = V_MAX
        # print(f"Volume: {volume}")
        if np.abs(volume + self.net_position) >= NET_POS_THRESHOLD:
            self.dump_position()

        if len(self.active_ask_orders) < ACTIVE_ORDER_COUNT_LIM//2 and len(self.active_bid_orders) < \
                ACTIVE_ORDER_COUNT_LIM//2 and len(self.future_data) == NUM_POINTS:
            slope, intercept, r_value, p_value, std_err = lin_reg(
                range(NUM_POINTS), self.future_data)
            # Case 1) - futures market not trending, p-value is for hypothesis test that slope is equal to 0
            if p_value >= CRIT_VAL:
                ask_id = next(self.order_ids)
                # print(f"Ask price: {self.best_etf_ask_price}")
                self.send_insert_order(ask_id, Side.SELL,
                                       self.best_etf_ask_price, volume,
                                       Lifespan.GOOD_FOR_DAY)
                self.active_ask_orders[ask_id] = [
                    self.best_etf_ask_price, volume, 0
                ]

                bid_id = next(self.order_ids)
                # print(f"Bid price: {self.best_etf_bid_price}")
                self.send_insert_order(bid_id, Side.BUY,
                                       self.best_etf_bid_price, volume,
                                       Lifespan.GOOD_FOR_DAY)
                self.active_bid_orders[bid_id] = [
                    self.best_etf_bid_price, volume, 0
                ]
            # Case 2) - trending futures market
            else:
                # Upward trending futures market
                if slope > 0:
                    # Fair value of future higher than fair value of etf
                    if self.fair_value_future > self.fair_value_etf:
                        ask_id = next(self.order_ids)
                        # print(f"Ask price: {self.best_future_ask_price}")
                        self.send_insert_order(ask_id, Side.SELL,
                                               self.best_future_ask_price,
                                               volume, Lifespan.GOOD_FOR_DAY)
                        self.active_ask_orders[ask_id] = [
                            self.best_future_ask_price, volume, 0
                        ]

                        bid_id = next(self.order_ids)
                        bid_price = max(self.best_future_bid_price,
                                        self.best_etf_ask_price)
                        # print(f"Bid price: {bid_price}")
                        self.send_insert_order(bid_id, Side.BUY, bid_price,
                                               volume, Lifespan.GOOD_FOR_DAY)
                        self.active_bid_orders[bid_id] = [bid_price, volume, 0]
                    # Fair value of future lower than fair value of etf
                    else:
                        ask_id = next(self.order_ids)
                        # print(f"Ask price: {self.best_etf_ask_price}")
                        self.send_insert_order(ask_id, Side.SELL,
                                               self.best_etf_ask_price, volume,
                                               Lifespan.GOOD_FOR_DAY)
                        self.active_ask_orders[ask_id] = [
                            self.best_etf_ask_price, volume, 0
                        ]

                        bid_id = next(self.order_ids)
                        bid_price = max(self.best_future_ask_price,
                                        self.best_etf_bid_price)
                        # print(f"Bid price: {bid_price}")
                        self.send_insert_order(bid_id, Side.BUY, bid_price,
                                               volume, Lifespan.GOOD_FOR_DAY)
                        self.active_bid_orders[bid_id] = [bid_price, volume, 0]
                # Downward trending futures market
                else:
                    # Fair value of future higher than fair value of etf
                    if self.fair_value_future > self.fair_value_etf:
                        ask_id = next(self.order_ids)
                        ask_price = min(self.best_future_bid_price,
                                        self.best_etf_ask_price)
                        # print(f"Ask price: {ask_price}")
                        self.send_insert_order(ask_id, Side.SELL, ask_price,
                                               volume, Lifespan.GOOD_FOR_DAY)
                        self.active_ask_orders[ask_id] = [ask_price, volume, 0]

                        bid_id = next(self.order_ids)
                        # print(f"Bid price: {self.best_etf_bid_price}")
                        self.send_insert_order(bid_id, Side.BUY,
                                               self.best_etf_bid_price, volume,
                                               Lifespan.GOOD_FOR_DAY)
                        self.active_bid_orders[bid_id] = [
                            self.best_etf_bid_price, volume, 0
                        ]
                    else:
                        ask_id = next(self.order_ids)
                        ask_price = min(self.best_future_ask_price,
                                        self.best_etf_bid_price)
                        # print(f"Ask price: {ask_price}")
                        self.send_insert_order(ask_id, Side.SELL, ask_price,
                                               volume, Lifespan.GOOD_FOR_DAY)
                        self.active_ask_orders[ask_id] = [ask_price, volume, 0]

                        bid_id = next(self.order_ids)
                        # print(f"Bid price: {self.best_future_bid_price}")
                        self.send_insert_order(bid_id, Side.BUY,
                                               self.best_future_bid_price,
                                               volume, Lifespan.GOOD_FOR_DAY)
                        self.active_bid_orders[bid_id] = [
                            self.best_future_bid_price, volume, 0
                        ]

        # Dealing with stale orders
        for ask_order_id in list(self.active_ask_orders):
            self.active_ask_orders[ask_order_id][2] += 1
            if self.active_ask_orders[ask_order_id][2] >= STALE_THRESHOLD:
                self.send_cancel_order(ask_order_id)
                self.active_ask_orders.pop(ask_order_id)

        for bid_order_id in list(self.active_bid_orders):
            self.active_bid_orders[bid_order_id][2] += 1
            if self.active_bid_orders[bid_order_id][2] >= STALE_THRESHOLD:
                self.send_cancel_order(bid_order_id)
                self.active_bid_orders.pop(bid_order_id)