Exemplo n.º 1
0
    0.025,
    logging_options=ArbitrageStrategy.OPTION_LOG_CREATE_ORDER)

clock = Clock(ClockMode.BACKTEST,
              start_time=start.timestamp(),
              end_time=end.timestamp())
clock.add_iterator(binance_market)
clock.add_iterator(ddex_market)
clock.add_iterator(strategy)

binance_market.set_balance("ETH", 100.0)
binance_market.set_balance("USDT", 10000.0)
ddex_market.set_balance("WETH", 100.0)
ddex_market.set_balance("DAI", 10000.0)

clock.backtest_til(start.timestamp() + 1)

ddex_weth_price = ddex_market.get_price("WETH-DAI", False)
binance_eth_price = binance_market.get_price("ETHUSDT", False)
start_ddex_portfolio_value = ddex_market.get_balance(
    "DAI") + ddex_market.get_balance("WETH") * ddex_weth_price
start_binance_portfolio_value = binance_market.get_balance(
    "USDT") + binance_market.get_balance("ETH") * binance_eth_price
print(f"start DDEX portfolio value: {start_ddex_portfolio_value}\n"
      f"start Binance portfolio value: {start_binance_portfolio_value}")

clock.backtest_til(end.timestamp())

ddex_weth_price = ddex_market.get_price("WETH-DAI", False)
binance_eth_price = binance_market.get_price("ETHUSDT", False)
ddex_portfolio_value = ddex_market.get_balance(
Exemplo n.º 2
0
class OrderExpirationTest(unittest.TestCase):
    start: pd.Timestamp = pd.Timestamp("2019-01-24", tz="UTC")
    end: pd.Timestamp = pd.Timestamp("2019-01-26", tz="UTC")
    market_name = "ETHUSDT"
    quote = "ETH"
    base = "USDT"

    def setUp(self):
        #self.weth_dai_data = DDEXOrderBookLoader("WETH-DAI", "WETH", "DAI")
        self.pair_data = BinanceOrderBookLoaderV2(self.market_name, "ETH",
                                                  "USDT")
        #self.pair_data = HuobiOrderBookLoader(self.market_name, "", "")
        self.clock = Clock(ClockMode.BACKTEST, 1.0, self.start.timestamp(),
                           self.end.timestamp())
        self.market = BacktestMarket()
        #self.market.add_data(self.weth_dai_data)
        self.market.add_data(self.pair_data)
        self.market.set_balance(self.quote, 200.0)
        self.market.set_balance(self.base, 20000.0)
        self.clock.add_iterator(self.market)

    def tearDown(self):
        #self.weth_dai_data.close()
        #self.eth_usd_data.close()
        self.pair_data.close()

    def verify_expired_order_cleanup(self, order_expired_events, limit_orders):
        """
        Recorded order expired event should indicate that these orders are no longer in the limit orders
        """
        limit_order_dict = {o.client_order_id: o for o in limit_orders}

        for index, order_expired_event in order_expired_events.iterrows():
            self.assertTrue(
                order_expired_event.order_id not in limit_order_dict)

    def test_ask_order_expiration_clean_up(self):
        ts_1 = pd.Timestamp("2019-01-24 00:02:15+00:00").timestamp()
        ts_2 = pd.Timestamp("2019-01-24 00:02:20+00:00").timestamp()
        trades = {
            ts_1: [(self.market_name, "sell", 1302, 255, {
                "expiration_ts": ts_1 + 9
            })],
            ts_2: [(self.market_name, "sell", 1302, 250, {
                "expiration_ts": ts_2 + 9
            })]
        }
        strategy: OrderExpirationTestStrategy = OrderExpirationTestStrategy(
            self.market, trades)
        self.clock.add_iterator(strategy)

        # first limit order made
        self.clock.backtest_til(self.start.timestamp() + 60 * 2 + 15)
        first_order_id = self.market.limit_orders[0].client_order_id
        self.assertTrue(len(self.market.limit_orders) == 1)
        self.assertTrue(first_order_id in
                        {o.order_id: o
                         for o in self.market.order_expirations})

        # second limit order made
        self.clock.backtest_til(self.start.timestamp() + 60 * 2 + 20)
        self.assertTrue(len(self.market.limit_orders) == 2)

        # first limit order expired
        self.clock.backtest_til(self.start.timestamp() + 60 * 2 + 25)
        # check if order expired event is fired
        self.assertTrue(first_order_id in [
            evt.order_id
            for i, evt in strategy.order_expired_events.iterrows()
        ])
        # check if the expired limit order is cleaned up
        self.verify_expired_order_cleanup(strategy.order_expired_events,
                                          self.market.limit_orders)

        self.assertTrue(len(self.market.limit_orders) == 1)
        second_order_id = self.market.limit_orders[0].client_order_id
        self.assertTrue(second_order_id in
                        {o.order_id: o
                         for o in self.market.order_expirations})

        # second limit order expired
        self.clock.backtest_til(self.start.timestamp() + 60 * 2 + 30)
        # check if order expired event is fired
        self.assertTrue(second_order_id in [
            evt.order_id
            for i, evt in strategy.order_expired_events.iterrows()
        ])
        # check if the expired limit order is cleaned up
        self.verify_expired_order_cleanup(strategy.order_expired_events,
                                          self.market.limit_orders)

    def test_bid_order_expiration_clean_up(self):
        ts_1 = pd.Timestamp("2019-01-24 00:12:15+00:00").timestamp()
        ts_2 = pd.Timestamp("2019-01-24 00:12:20+00:00").timestamp()

        trades = {
            ts_1: [(self.market_name, "buy", 100, 55, {
                "expiration_ts": ts_1 + 9
            })],
            ts_2: [(self.market_name, "buy", 100, 50, {
                "expiration_ts": ts_2 + 9
            }), (self.market_name, "buy", 100, 55, {
                "expiration_ts": ts_2 + 9
            })]
        }
        strategy: OrderExpirationTestStrategy = OrderExpirationTestStrategy(
            self.market, trades)
        self.clock.add_iterator(strategy)

        # first limit order made
        self.clock.backtest_til(self.start.timestamp() + 60 * 12 + 15)
        first_order_id = self.market.limit_orders[0].client_order_id
        self.assertTrue(len(self.market.limit_orders) == 1)
        self.assertTrue(first_order_id in
                        {o.order_id: o
                         for o in self.market.order_expirations})

        # second limit order made
        self.clock.backtest_til(self.start.timestamp() + 60 * 12 + 20)
        self.assertTrue(len(self.market.limit_orders) == 3)

        # first limit order expired
        self.clock.backtest_til(self.start.timestamp() + 60 * 12 + 25)
        # check if order expired event is fired
        self.assertTrue(first_order_id in [
            evt.order_id
            for i, evt in strategy.order_expired_events.iterrows()
        ])
        # check if the expired limit order is cleaned up
        self.verify_expired_order_cleanup(strategy.order_expired_events,
                                          self.market.limit_orders)

        self.assertTrue(len(self.market.limit_orders) == 2)
        second_order_id_1 = self.market.limit_orders[0].client_order_id
        second_order_id_2 = self.market.limit_orders[1].client_order_id

        self.assertTrue(second_order_id_1 in
                        {o.order_id: o
                         for o in self.market.order_expirations})
        self.assertTrue(second_order_id_2 in
                        {o.order_id: o
                         for o in self.market.order_expirations})

        # second limit order expired
        self.clock.backtest_til(self.start.timestamp() + 60 * 12 + 30)
        # check if order expired event is fired
        self.assertTrue(second_order_id_1 in [
            evt.order_id
            for i, evt in strategy.order_expired_events.iterrows()
        ])
        self.assertTrue(second_order_id_2 in [
            evt.order_id
            for i, evt in strategy.order_expired_events.iterrows()
        ])
        # check if the expired limit order is cleaned up
        self.verify_expired_order_cleanup(strategy.order_expired_events,
                                          self.market.limit_orders)
class CompositeOrderBookTest(unittest.TestCase):
    start: pd.Timestamp = pd.Timestamp("2019-01-25", tz="UTC")
    end: pd.Timestamp = pd.Timestamp("2019-01-26", tz="UTC")

    def setUp(self):
        self.weth_dai_data = DDEXOrderBookLoader("WETH-DAI", "WETH", "DAI")
        self.clock = Clock(ClockMode.BACKTEST, 1.0, self.start.timestamp(), self.end.timestamp())
        self.market = BacktestMarket()
        self.market.add_data(self.weth_dai_data)
        self.market.set_balance("WETH", 200.0)
        self.market.set_balance("DAI", 20000.0)
        self.clock.add_iterator(self.market)

    def tearDown(self):
        self.weth_dai_data.close()

    def verify_filled_order_recorded(self, recorded_filled_events, composite_order_book):
        bid_dict = {entry.price: (entry.amount, entry.update_id)
                    for entry in composite_order_book.traded_order_book.bid_entries()}
        ask_dict = {entry.price: (entry.amount, entry.update_id)
                    for entry in composite_order_book.traded_order_book.ask_entries()}
        for index, fill_event in recorded_filled_events.iterrows():
            if fill_event.trade_type is TradeType.SELL:
                self.assertTrue(fill_event.price in bid_dict)
                self.assertTrue(bid_dict[fill_event.price][0] == fill_event.amount)
                self.assertTrue(bid_dict[fill_event.price][1] == fill_event.timestamp)
            elif fill_event.trade_type is TradeType.BUY:
                self.assertTrue(fill_event.price in ask_dict)
                self.assertTrue(ask_dict[fill_event.price][0] == fill_event.amount)
                self.assertTrue(ask_dict[fill_event.price][1] == fill_event.timestamp)

    def verify_composite_order_book_correctness(self, composite_order_book):
        filled_bid_dict = {o.price: (o.amount, o.update_id)
                           for o in composite_order_book.traded_order_book.bid_entries()}
        filled_ask_dict = {o.price: (o.amount, o.update_id)
                           for o in composite_order_book.traded_order_book.ask_entries()}

        composite_bid_dict = {o.price: (o.amount, o.update_id) for o in composite_order_book.bid_entries()}
        composite_ask_dict = {o.price: (o.amount, o.update_id) for o in composite_order_book.ask_entries()}

        original_bid_dict = {o.price: (o.amount, o.update_id)
                             for o in composite_order_book.original_bid_entries()}
        original_ask_dict = {o.price: (o.amount, o.update_id)
                             for o in composite_order_book.original_ask_entries()}

        for filled_bid_price, filled_bid_amount in filled_bid_dict.items():
            if filled_bid_price in original_bid_dict:
                if (original_bid_dict[filled_bid_price] - filled_bid_amount) <= 0:
                    self.assertTrue(filled_bid_price not in composite_bid_dict)
                else:
                    self.assertTrue(composite_bid_dict[filled_bid_price] ==
                                    original_bid_dict[filled_bid_price] - filled_bid_amount)

        for filled_ask_price, filled_ask_amount in filled_ask_dict.items():
            if filled_ask_price in original_ask_dict:
                if (original_bid_dict[filled_ask_price] - filled_ask_amount) <= 0:
                    self.assertTrue(filled_ask_price not in composite_ask_dict)
                else:
                    self.assertTrue(composite_bid_dict[filled_ask_price] ==
                                    original_bid_dict[filled_ask_price] - filled_ask_amount)

    def verify_composite_order_book_cleanup(self, recorded_filled_events, composite_order_book):
        """
        Recorded fill order should be cleaned up when the original order book no longer contain that price entry
        """
        filled_bid_dict = {o.price: (o.amount, o.update_id)
                           for o in composite_order_book.traded_order_book.bid_entries()}
        filled_ask_dict = {o.price: (o.amount, o.update_id)
                           for o in composite_order_book.traded_order_book.ask_entries()}

        original_bid_dict = {o.price: (o.amount, o.update_id)
                             for o in composite_order_book.original_bid_entries()}
        original_ask_dict = {o.price: (o.amount, o.update_id)
                             for o in composite_order_book.original_ask_entries()}

        for index, fill_event in recorded_filled_events.iterrows():
            if fill_event.trade_type is TradeType.SELL:
                if fill_event.price not in original_bid_dict:
                    self.assertTrue(fill_event.price not in filled_bid_dict)

            elif fill_event.trade_type is TradeType.BUY:
                if fill_event.price not in original_ask_dict:
                    self.assertTrue(fill_event.price not in filled_ask_dict)

    def verify_composite_order_book_adjustment(self, composite_order_book):
        """
        Recorded fill order sohuld adjust it's amount to no larger than the original price entries' amount
        """
        filled_bid_dict = {o.price: (o.amount, o.update_id)
                           for o in composite_order_book.traded_order_book.bid_entries()}
        filled_ask_dict = {o.price: (o.amount, o.update_id)
                           for o in composite_order_book.traded_order_book.ask_entries()}

        original_bid_dict = {o.price: (o.amount, o.update_id)
                             for o in composite_order_book.original_bid_entries()}
        original_ask_dict = {o.price: (o.amount, o.update_id)
                             for o in composite_order_book.original_ask_entries()}

        for filled_bid_price, filled_bid_entry in filled_bid_dict.items():
            if filled_bid_price in original_bid_dict:
                self.assertTrue(original_bid_dict[filled_bid_price][0] >= filled_bid_entry[0])

        for filled_ask_price, filled_ask_entry in filled_ask_dict.items():
            if filled_ask_price in original_ask_dict:
                self.assertTrue(original_ask_dict[filled_ask_price][0] >= filled_ask_entry[0])

    def test_market_order(self):
        trades = {
            pd.Timestamp("2019-01-25 00:00:10+00:00").timestamp(): [
                ("WETH-DAI", "buy", 5.0),
                ("WETH-DAI", "sell", 5.0)
            ]
        }
        strategy: CompositeOrderBookTestStrategy = CompositeOrderBookTestStrategy(self.market, trades)
        self.clock.add_iterator(strategy)
        self.clock.backtest_til(self.start.timestamp()+10)

        self.verify_filled_order_recorded(strategy.order_filled_events, self.market.get_order_book("WETH-DAI"))
        self.verify_composite_order_book_correctness(self.market.get_order_book("WETH-DAI"))

        self.clock.backtest_til(self.start.timestamp() + 70)

        self.verify_composite_order_book_cleanup(strategy.order_filled_events, self.market.get_order_book("WETH-DAI"))

    def test_composite_order_book_adjustment(self):
        trades = {
            pd.Timestamp("2019-01-25 00:02:15+00:00").timestamp(): [
                ("WETH-DAI", "sell", 93.53 + 23.65)
            ]
        }
        strategy: CompositeOrderBookTestStrategy = CompositeOrderBookTestStrategy(self.market, trades)
        self.clock.add_iterator(strategy)
        self.clock.backtest_til(self.start.timestamp() + 60*2 + 15)
        self.clock.backtest_til(self.start.timestamp() + 60*2 + 25)
        self.verify_composite_order_book_adjustment(self.market.get_order_book("WETH-DAI"))