Beispiel #1
0
    def test_order_fill_record(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        symbol: str = "HOT-WETH"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()

        try:
            # Try to buy 0.05 ETH worth of HOT from the exchange, and watch for completion event.
            current_price: Decimal = self.market.get_price(symbol, True)
            amount: Decimal = Decimal("0.05") / current_price
            order_id = self.market.buy(symbol, amount)
            [buy_order_completed_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCompletedEvent))

            # Reset the logs
            self.market_logger.clear()

            # Try to sell back the same amount of HOT to the exchange, and watch for completion event.
            amount = buy_order_completed_event.base_asset_amount
            order_id = self.market.sell(symbol, amount)
            [sell_order_completed_event] = self.run_parallel(
                self.market_logger.wait_for(SellOrderCompletedEvent))

            # Query the persisted trade logs
            trade_fills: List[TradeFill] = recorder.get_trades_for_config(
                config_path)
            self.assertEqual(2, len(trade_fills))
            buy_fills: List[TradeFill] = [
                t for t in trade_fills if t.trade_type == "BUY"
            ]
            sell_fills: List[TradeFill] = [
                t for t in trade_fills if t.trade_type == "SELL"
            ]
            self.assertEqual(1, len(buy_fills))
            self.assertEqual(1, len(sell_fills))

            order_id = None

        finally:
            if order_id is not None:
                self.market.cancel(symbol, order_id)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
    def test_order_fill_record(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        sql: SQLConnectionManager = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
        recorder.start()

        try:
            # Try to buy 1 LINK from the exchange, and watch for completion event.
            bid_price: Decimal = self.market.get_price("LINKETH", True)
            amount: Decimal = 1
            order_id = self.place_order(True, "LINKETH", amount, OrderType.LIMIT, bid_price, 10001,
                                        FixtureBinance.ORDER_BUY_LIMIT, FixtureBinance.WS_AFTER_BUY_1,
                                        FixtureBinance.WS_AFTER_BUY_2)
            [buy_order_completed_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCompletedEvent))

            # Reset the logs
            self.market_logger.clear()

            # Try to sell back the same amount of LINK to the exchange, and watch for completion event.
            ask_price: Decimal = self.market.get_price("LINKETH", False)
            amount = buy_order_completed_event.base_asset_amount
            order_id = self.place_order(False, "LINKETH", amount, OrderType.LIMIT, ask_price, 10002,
                                        FixtureBinance.ORDER_SELL_LIMIT, FixtureBinance.WS_AFTER_SELL_1,
                                        FixtureBinance.WS_AFTER_SELL_2)
            [sell_order_completed_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCompletedEvent))

            # Query the persisted trade logs
            trade_fills: List[TradeFill] = recorder.get_trades_for_config(config_path)
            self.assertGreaterEqual(len(trade_fills), 2)
            buy_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "BUY"]
            sell_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "SELL"]
            self.assertGreaterEqual(len(buy_fills), 1)
            self.assertGreaterEqual(len(sell_fills), 1)

            order_id = None

        finally:
            if order_id is not None:
                self.market.cancel("LINKETH", order_id)
                self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
Beispiel #3
0
    def test_order_fill_record(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        trading_pair: str = "ETH-USDT"
        sql: SQLConnectionManager = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
        recorder.start()

        try:
            # Try to buy 0.01 ETH from the exchange, and watch for completion event.
            amount: Decimal = Decimal(0.01)
            order_id, _ = self.place_order(True, trading_pair, amount, OrderType.MARKET, 0, 10001,
                                           FixtureKucoin.ORDER_PLACE, FixtureKucoin.ORDER_GET_MARKET_BUY)
            [buy_order_completed_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCompletedEvent))

            # Reset the logs
            self.market_logger.clear()

            # Try to sell back the same amount of ETH to the exchange, and watch for completion event.
            amount: Decimal = Decimal(buy_order_completed_event.base_asset_amount)
            order_id, _ = self.place_order(False, trading_pair, amount, OrderType.MARKET, 0, 10002,
                                           FixtureKucoin.ORDER_PLACE, FixtureKucoin.ORDER_GET_MARKET_SELL)
            [sell_order_completed_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCompletedEvent))

            # Query the persisted trade logs
            trade_fills: List[TradeFill] = recorder.get_trades_for_config(config_path)
            self.assertEqual(2, len(trade_fills))
            buy_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "BUY"]
            sell_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "SELL"]
            self.assertEqual(1, len(buy_fills))
            self.assertEqual(1, len(sell_fills))

            order_id = None

        finally:
            if order_id is not None:
                self.market.cancel(trading_pair, order_id)
                self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
            self.market_logger.clear()
    def test_order_fill_record(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        trading_pair: str = "XRP-BTC"
        sql: SQLConnectionManager = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
        recorder.start()

        try:
            current_bid_price: Decimal = self.market.get_price(trading_pair, True)
            bid_price: Decimal = current_bid_price * Decimal('1.01')
            quantize_bid_price: Decimal = self.market.quantize_order_price(trading_pair, bid_price)

            amount: Decimal = Decimal("0.0005") / quantize_bid_price * Decimal('1.1')
            quantized_amount: Decimal = self.market.quantize_order_amount(trading_pair, amount)
            order_id = self.market.buy(trading_pair, quantized_amount)
            [buy_order_completed_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCompletedEvent))

            # Reset the logs
            self.market_logger.clear()

            amount = Decimal(buy_order_completed_event.base_asset_amount)
            order_id = self.market.sell(trading_pair, amount)
            [sell_order_completed_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCompletedEvent))

            # Query the persisted trade logs
            trade_fills: List[TradeFill] = recorder.get_trades_for_config(config_path)
            self.assertEqual(2, len(trade_fills))
            buy_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "BUY"]
            sell_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "SELL"]
            self.assertEqual(1, len(buy_fills))
            self.assertEqual(1, len(sell_fills))

            order_id = None

        finally:
            if order_id is not None:
                self.market.cancel(trading_pair, order_id)
                self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
Beispiel #5
0
    def test_order_fill_record(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        trading_pair: str = "ETH-USDT"
        sql: SQLConnectionManager = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
        recorder.start()

        try:

            amount: Decimal = Decimal("0.06")
            quantized_amount: Decimal = self.market.quantize_order_amount(trading_pair, amount)
            order_id, _ = self.place_order(True, trading_pair, quantized_amount, OrderType.MARKET, 0, 10001,
                                           FixtureBittrex.ORDER_PLACE_FILLED, FixtureBittrex.WS_ORDER_FILLED)
            [buy_order_completed_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCompletedEvent))

            # Reset the logs
            self.market_logger.clear()

            amount = Decimal(buy_order_completed_event.base_asset_amount)
            order_id, _ = self.place_order(False, trading_pair, amount, OrderType.MARKET, 0, 10001,
                                           FixtureBittrex.ORDER_PLACE_FILLED, FixtureBittrex.WS_ORDER_FILLED)
            [sell_order_completed_event] = self.run_parallel(self.market_logger.wait_for(SellOrderCompletedEvent))

            # Query the persisted trade logs
            trade_fills: List[TradeFill] = recorder.get_trades_for_config(config_path)
            self.assertEqual(2, len(trade_fills))
            buy_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "BUY"]
            sell_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "SELL"]
            self.assertEqual(1, len(buy_fills))
            self.assertEqual(1, len(sell_fills))

            order_id = None

        finally:
            if order_id is not None:
                self.market.cancel(trading_pair, order_id)
                self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
    def _initialize_markets(self, market_names: List[Tuple[str, List[str]]]):
        ethereum_rpc_url = global_config_map.get("ethereum_rpc_url").value

        # aggregate trading_pairs if there are duplicate markets
        market_trading_pairs_map = {}
        for market_name, trading_pairs in market_names:
            if market_name not in market_trading_pairs_map:
                market_trading_pairs_map[market_name] = []
            market_class: MarketBase = MARKET_CLASSES.get(
                market_name, MarketBase)
            for trading_pair in trading_pairs:
                exchange_trading_pair: str = market_class.convert_to_exchange_trading_pair(
                    trading_pair)
                market_trading_pairs_map[market_name].append(
                    exchange_trading_pair)

        for market_name, trading_pairs in market_trading_pairs_map.items():
            if global_config_map.get("paper_trade_enabled").value:
                try:
                    market = create_paper_trade_market(market_name,
                                                       trading_pairs)
                except Exception:
                    raise
                paper_trade_account_balance = global_config_map.get(
                    "paper_trade_account_balance").value
                for asset, balance in paper_trade_account_balance.items():
                    market.set_balance(asset, balance)

            elif market_name == "binance":
                binance_api_key = global_config_map.get(
                    "binance_api_key").value
                binance_api_secret = global_config_map.get(
                    "binance_api_secret").value
                market = BinanceMarket(
                    binance_api_key,
                    binance_api_secret,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required,
                )

            elif market_name == "radar_relay":
                assert self.wallet is not None
                market = RadarRelayMarket(
                    wallet=self.wallet,
                    ethereum_rpc_url=ethereum_rpc_url,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required,
                )

            elif market_name == "bamboo_relay":
                assert self.wallet is not None
                use_coordinator = global_config_map.get(
                    "bamboo_relay_use_coordinator").value
                pre_emptive_soft_cancels = global_config_map.get(
                    "bamboo_relay_pre_emptive_soft_cancels").value
                market = BambooRelayMarket(
                    wallet=self.wallet,
                    ethereum_rpc_url=ethereum_rpc_url,
                    trading_pairs=trading_pairs,
                    use_coordinator=use_coordinator,
                    pre_emptive_soft_cancels=pre_emptive_soft_cancels,
                    trading_required=self._trading_required,
                )

            elif market_name == "coinbase_pro":
                coinbase_pro_api_key = global_config_map.get(
                    "coinbase_pro_api_key").value
                coinbase_pro_secret_key = global_config_map.get(
                    "coinbase_pro_secret_key").value
                coinbase_pro_passphrase = global_config_map.get(
                    "coinbase_pro_passphrase").value

                market = CoinbaseProMarket(
                    coinbase_pro_api_key,
                    coinbase_pro_secret_key,
                    coinbase_pro_passphrase,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "huobi":
                huobi_api_key = global_config_map.get("huobi_api_key").value
                huobi_secret_key = global_config_map.get(
                    "huobi_secret_key").value
                market = HuobiMarket(
                    huobi_api_key,
                    huobi_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "liquid":
                liquid_api_key = global_config_map.get("liquid_api_key").value
                liquid_secret_key = global_config_map.get(
                    "liquid_secret_key").value

                market = LiquidMarket(
                    liquid_api_key,
                    liquid_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    user_stream_tracker_data_source_type=
                    UserStreamTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "dolomite":
                assert self.wallet is not None
                is_test_net: bool = global_config_map.get(
                    "ethereum_chain_name").value == "DOLOMITE_TEST"
                market = DolomiteMarket(
                    wallet=self.wallet,
                    ethereum_rpc_url=ethereum_rpc_url,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    isTestNet=is_test_net,
                    trading_required=self._trading_required,
                )
            elif market_name == "bittrex":
                bittrex_api_key = global_config_map.get(
                    "bittrex_api_key").value
                bittrex_secret_key = global_config_map.get(
                    "bittrex_secret_key").value
                market = BittrexMarket(
                    bittrex_api_key,
                    bittrex_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "kucoin":
                kucoin_api_key = global_config_map.get("kucoin_api_key").value
                kucoin_secret_key = global_config_map.get(
                    "kucoin_secret_key").value
                kucoin_passphrase = global_config_map.get(
                    "kucoin_passphrase").value
                market = KucoinMarket(
                    kucoin_api_key,
                    kucoin_passphrase,
                    kucoin_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "eterbase":
                eterbase_api_key = global_config_map.get(
                    "eterbase_api_key").value
                eterbase_secret_key = global_config_map.get(
                    "eterbase_secret_key").value
                eterbase_account = global_config_map.get(
                    "eterbase_account").value
                market = EterbaseMarket(
                    eterbase_api_key,
                    eterbase_secret_key,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required,
                    eterbase_account=eterbase_account)
            elif market_name == "kraken":
                kraken_api_key = global_config_map.get("kraken_api_key").value
                kraken_secret_key = global_config_map.get(
                    "kraken_secret_key").value
                market = KrakenMarket(
                    kraken_api_key,
                    kraken_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            else:
                raise ValueError(f"Market name {market_name} is invalid.")

            self.markets[market_name]: MarketBase = market

        self.markets_recorder = MarketsRecorder(
            self.trade_fill_db,
            list(self.markets.values()),
            self.strategy_file_name,
            self.strategy_name,
        )
        self.markets_recorder.start()
    def test_order_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))

            # Try to put limit buy order for 0.02 ETH at fraction of USDC market price, and watch for order creation event.
            order_id = self.underpriced_limit_buy()
            [order_created_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id,
                             list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[
                Order] = recorder.get_orders_for_config_and_market(
                    config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(
                config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: KrakenMarket = KrakenMarket(conf.kraken_api_key,
                                                     conf.kraken_secret_key,
                                                     trading_pairs=[PAIR])
            for event_tag in self.events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path,
                                       strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(
                saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            self.market.cancel(PAIR, order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel(PAIR, order_id)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            unlink(self.db_path)
Beispiel #8
0
    def test_orders_saving_and_restoration(self):
        self.customSetUp()
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        symbol: str = "ethusdt"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))

            # Try to put limit buy order for 0.04 ETH, and watch for order creation event.
            current_bid_price: Decimal = self.market.get_price(symbol, True)
            bid_price: Decimal = current_bid_price * Decimal(0.8)
            quantize_bid_price: Decimal = self.market.quantize_order_price(
                symbol, bid_price)

            amount: Decimal = Decimal(0.04)
            quantized_amount: Decimal = self.market.quantize_order_amount(
                symbol, amount)

            self.mock_api.order_id = self.mock_api.MOCK_HUOBI_LIMIT_OPEN_ORDER_ID
            order_id = self.market.buy(symbol, quantized_amount,
                                       OrderType.LIMIT, quantize_bid_price)
            [order_created_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id,
                             list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[
                Order] = recorder.get_orders_for_config_and_market(
                    config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(
                config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: HuobiMarket = HuobiMarket(
                huobi_api_key=MOCK_HUOBI_API_KEY,
                huobi_secret_key=MOCK_HUOBI_SECRET_KEY,
                symbols=["ethusdt", "btcusdt"])
            self.market.shared_client: TestClient = self.client
            mock_data_source: MockAPIOrderBookDataSource = MockAPIOrderBookDataSource(
                self.client, HuobiOrderBook, ["ethusdt"])
            self.market.order_book_tracker.data_source = mock_data_source

            for event_tag in self.events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path,
                                       strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(
                saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            self.mock_api.order_id = self.mock_api.MOCK_HUOBI_LIMIT_OPEN_ORDER_ID
            self.mock_api.order_response_dict[
                self.mock_api.
                MOCK_HUOBI_LIMIT_OPEN_ORDER_ID]["data"]["state"] = "canceled"
            self.market.cancel(symbol, order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel(symbol, order_id)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
Beispiel #9
0
    def test_z_orders_saving_and_restoration(self):
        self.market.reset_state()

        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        symbol: str = self.base_token_symbol + "-" + self.quote_token_symbol
        sql: SQLConnectionManager = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states["limit_orders"]))

            # Try to put limit buy order for 0.05 Quote Token worth of Base Token, and watch for order creation event.
            current_bid_price: float = self.market.get_price(symbol, True)
            bid_price: float = current_bid_price * 0.8
            quantize_bid_price: Decimal = self.market.quantize_order_price(symbol, bid_price)

            amount: float = 0.05 / bid_price
            quantized_amount: Decimal = self.market.quantize_order_amount(symbol, amount)

            expires = int(time.time() + 60 * 5)
            order_id = self.market.buy(symbol, float(quantized_amount), OrderType.LIMIT, float(quantize_bid_price),
                                       expiration_ts=expires)
            [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states["limit_orders"]))
            self.assertEqual(order_id, list(self.market.tracking_states["limit_orders"].keys())[0])

            # Verify orders from recorder
            recorded_orders: List[Order] = recorder.get_orders_for_config_and_market(config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertIsInstance(saved_market_states.saved_state["limit_orders"], dict)
            self.assertGreater(len(saved_market_states.saved_state["limit_orders"]), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.market_events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: BambooRelayMarket = BambooRelayMarket(
                wallet=self.wallet,
                ethereum_rpc_url=conf.test_web3_provider_list[0],
                order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                symbols=[conf.test_bamboo_relay_base_token_symbol + "-" + conf.test_bamboo_relay_quote_token_symbol],
                use_coordinator=True,
                pre_emptive_soft_cancels=True
            )
            for event_tag in self.market_events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states["limit_orders"]))
            self.market.restore_tracking_states(saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states["limit_orders"]))

            # Cancel the order and verify that the change is saved.
            self.market.cancel(symbol, order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states["limit_orders"]))
            saved_market_states = recorder.get_market_states(config_path, self.market)
            self.assertEqual(1, len(saved_market_states.saved_state["limit_orders"]))
        finally:
            if order_id is not None:
                self.market.cancel(symbol, order_id)
                self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
    def test_orders_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        symbol: str = "ethusdt"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))

            # Try to put limit buy order for 0.04 ETH, and watch for order creation event.
            current_bid_price: float = self.market.get_price(symbol, True)
            bid_price: float = current_bid_price * 0.8
            quantize_bid_price: Decimal = self.market.quantize_order_price(
                symbol, bid_price)

            amount: float = 0.04
            quantized_amount: Decimal = self.market.quantize_order_amount(
                symbol, amount)

            order_id = self.market.buy(symbol, quantized_amount,
                                       OrderType.LIMIT, quantize_bid_price)
            [order_created_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id,
                             list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[
                Order] = recorder.get_orders_for_config_and_market(
                    config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(
                config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: HuobiMarket = HuobiMarket(
                huobi_api_key=conf.huobi_api_key,
                huobi_secret_key=conf.huobi_secret_key,
                symbols=["ethusdt", "btcusdt"])
            for event_tag in self.events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path,
                                       strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(
                saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            self.market.cancel(symbol, order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel(symbol, order_id)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
    def _initialize_markets(self, market_names: List[Tuple[str, List[str]]]):
        ethereum_rpc_url = global_config_map.get("ethereum_rpc_url").value

        # aggregate symbols if there are duplicate markets
        market_symbols_map = {}
        for market_name, symbols in market_names:
            if market_name not in market_symbols_map:
                market_symbols_map[market_name] = []
            market_symbols_map[market_name] += symbols

        for market_name, symbols in market_symbols_map.items():
            if global_config_map.get("paper_trade_enabled").value:
                self._notify(f"\nPaper trade is enabled for market {market_name}")
                try:
                    market = create_paper_trade_market(market_name, symbols)
                except Exception:
                    raise
                paper_trade_account_balance = global_config_map.get("paper_trade_account_balance").value
                for asset, balance in paper_trade_account_balance:
                    market.set_balance(asset, balance)

            elif market_name == "ddex" and self.wallet:
                market = DDEXMarket(wallet=self.wallet,
                                    ethereum_rpc_url=ethereum_rpc_url,
                                    order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                                    symbols=symbols,
                                    trading_required=self._trading_required)

            elif market_name == "idex" and self.wallet:
                idex_api_key: str = global_config_map.get("idex_api_key").value
                try:
                    market = IDEXMarket(idex_api_key=idex_api_key,
                                        wallet=self.wallet,
                                        ethereum_rpc_url=ethereum_rpc_url,
                                        order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                                        symbols=symbols,
                                        trading_required=self._trading_required)
                except Exception as e:
                    self.logger().error(str(e))

            elif market_name == "binance":
                binance_api_key = global_config_map.get("binance_api_key").value
                binance_api_secret = global_config_map.get("binance_api_secret").value
                market = BinanceMarket(binance_api_key,
                                       binance_api_secret,
                                       order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                                       symbols=symbols,
                                       trading_required=self._trading_required)

            elif market_name == "radar_relay" and self.wallet:
                market = RadarRelayMarket(wallet=self.wallet,
                                          ethereum_rpc_url=ethereum_rpc_url,
                                          symbols=symbols,
                                          trading_required=self._trading_required)

            elif market_name == "bamboo_relay" and self.wallet:
                use_coordinator = global_config_map.get("bamboo_relay_use_coordinator").value
                pre_emptive_soft_cancels = global_config_map.get("bamboo_relay_pre_emptive_soft_cancels").value
                market = BambooRelayMarket(wallet=self.wallet,
                                           ethereum_rpc_url=ethereum_rpc_url,
                                           symbols=symbols,
                                           use_coordinator=use_coordinator,
                                           pre_emptive_soft_cancels=pre_emptive_soft_cancels,
                                           trading_required=self._trading_required)

            elif market_name == "coinbase_pro":
                coinbase_pro_api_key = global_config_map.get("coinbase_pro_api_key").value
                coinbase_pro_secret_key = global_config_map.get("coinbase_pro_secret_key").value
                coinbase_pro_passphrase = global_config_map.get("coinbase_pro_passphrase").value

                market = CoinbaseProMarket(coinbase_pro_api_key,
                                           coinbase_pro_secret_key,
                                           coinbase_pro_passphrase,
                                           symbols=symbols,
                                           trading_required=self._trading_required)
            elif market_name == "huobi":
                huobi_api_key = global_config_map.get("huobi_api_key").value
                huobi_secret_key = global_config_map.get("huobi_secret_key").value
                market = HuobiMarket(huobi_api_key,
                                     huobi_secret_key,
                                     order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                                     symbols=symbols,
                                     trading_required=self._trading_required)
            else:
                raise ValueError(f"Market name {market_name} is invalid.")

            self.markets[market_name]: MarketBase = market

        self.markets_recorder = MarketsRecorder(
            self.trade_fill_db,
            list(self.markets.values()),
            in_memory_config_map.get("strategy_file_path").value,
            in_memory_config_map.get("strategy").value
        )
        self.markets_recorder.start()
Beispiel #12
0
    def test_orders_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        trading_pair: str = "ETH-USDT"
        sql: SQLConnectionManager = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))
            current_bid_price: Decimal = self.market.get_price(trading_pair, True) * Decimal('0.80')
            quantize_bid_price: Decimal = self.market.quantize_order_price(trading_pair, current_bid_price)
            bid_amount: Decimal = Decimal('0.06')
            quantized_bid_amount: Decimal = self.market.quantize_order_amount(trading_pair, bid_amount)

            order_id, exch_order_id = self.place_order(True, trading_pair, quantized_bid_amount, OrderType.LIMIT,
                                                       quantize_bid_price, 10001,
                                                       FixtureBittrex.ORDER_PLACE_OPEN, FixtureBittrex.WS_ORDER_OPEN)
            [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id, list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[Order] = recorder.get_orders_for_config_and_market(config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: BittrexMarket = BittrexMarket(
                bittrex_api_key=API_KEY,
                bittrex_secret_key=API_SECRET,
                trading_pairs=["XRP-BTC"]
            )
            for event_tag in self.events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            self.cancel_order(trading_pair, order_id, exch_order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel(trading_pair, order_id)
                self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
Beispiel #13
0
    def test_orders_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        trading_pair: str = "ETH-USDT"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))

            # Try to put limit buy order for 0.04 ETH, and watch for order creation event.
            current_bid_price: float = self.market.get_price(
                trading_pair, True)
            bid_price: Decimal = Decimal(current_bid_price * Decimal(0.8))
            quantize_bid_price: Decimal = self.market.quantize_order_price(
                trading_pair, bid_price)

            amount: Decimal = Decimal(0.04)
            quantized_amount: Decimal = self.market.quantize_order_amount(
                trading_pair, amount)

            order_id, exch_order_id = self.place_order(
                True, trading_pair, quantized_amount, OrderType.LIMIT_MAKER,
                quantize_bid_price, 10001, FixtureKucoin.ORDER_PLACE,
                FixtureKucoin.ORDER_GET_BUY_UNMATCHED)
            [order_created_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id,
                             list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[
                Order] = recorder.get_orders_for_config_and_market(
                    config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(
                config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: KucoinMarket = KucoinMarket(
                kucoin_api_key=API_KEY,
                kucoin_passphrase=API_PASSPHRASE,
                kucoin_secret_key=API_SECRET,
                trading_pairs=["ETH-USDT"])
            for event_tag in self.events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path,
                                       strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(
                saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            if API_MOCK_ENABLED:
                resp = FixtureKucoin.ORDER_CANCEL.copy()
                resp["data"]["cancelledOrderIds"] = exch_order_id
                self.web_app.update_response(
                    "delete", API_BASE_URL, f"/api/v1/orders/{exch_order_id}",
                    resp)
            # Cancel the order and verify that the change is saved.
            self.market.cancel(trading_pair, order_id)
            if API_MOCK_ENABLED:
                resp = FixtureKucoin.ORDER_GET_CANCELLED.copy()
                resp["data"]["id"] = exch_order_id
                resp["data"]["clientOid"] = order_id
                self.web_app.update_response(
                    "get", API_BASE_URL, f"/api/v1/orders/{exch_order_id}",
                    resp)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel(trading_pair, order_id)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
            self.market_logger.clear()
Beispiel #14
0
    def test_orders_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))

            # Try to put limit buy order for 0.005 ETH worth of CEL, and watch for order creation event.
            current_bid_price: Decimal = self.market.get_price("CEL-ETH", True)
            bid_price: Decimal = current_bid_price * Decimal("0.8")
            quantize_bid_price: Decimal = self.market.quantize_order_price(
                "CEL-ETH", bid_price)

            amount: Decimal = 1
            quantized_amount: Decimal = self.market.quantize_order_amount(
                "CEL-ETH", amount)

            order_id, buy_exchange_id = self.place_order(
                True, "CEL-ETH", quantized_amount, OrderType.LIMIT,
                quantize_bid_price, 10001, FixtureLiquid.ORDER_SAVE_RESTORE,
                FixtureLiquid.ORDERS_GET_AFTER_BUY)
            [order_created_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id,
                             list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[
                Order] = recorder.get_orders_for_config_and_market(
                    config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(
                config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.events:
                self.market.remove_listener(event_tag, self.market_logger)

            self.market: LiquidMarket = LiquidMarket(
                API_KEY,
                API_SECRET,
                order_book_tracker_data_source_type=
                OrderBookTrackerDataSourceType.EXCHANGE_API,
                user_stream_tracker_data_source_type=
                UserStreamTrackerDataSourceType.EXCHANGE_API,
                trading_pairs=['ETH-USD', 'CEL-ETH'])

            for event_tag in self.events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path,
                                       strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(
                saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            if API_MOCK_ENABLED:
                order_cancel_resp = FixtureLiquid.ORDER_CANCEL_SAVE_RESTORE.copy(
                )
                self.web_app.update_response(
                    "put", API_HOST, f"/orders/{str(buy_exchange_id)}/cancel",
                    order_cancel_resp)
            self.market.cancel("CEL-ETH", order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel("CEL-ETH", order_id)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
Beispiel #15
0
    def test_orders_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))

            # Try to put limit buy order for 0.02 ETH worth of ZRX, and watch for order creation event.
            current_bid_price: Decimal = self.market.get_price("LINKETH", True)
            bid_price: Decimal = current_bid_price * Decimal("0.8")
            quantize_bid_price: Decimal = self.market.quantize_order_price(
                "LINKETH", bid_price)

            amount: Decimal = 1
            quantized_amount: Decimal = self.market.quantize_order_amount(
                "LINKETH", amount)

            if API_MOCK_ENABLED:
                resp = self.order_response(FixtureBinance.ORDER_BUY_NOT_FILLED,
                                           1000001, "buy", "LINKETH")
                self.web_app.update_response("post", self.base_api_url,
                                             "/api/v3/order", resp)
            order_id = self.market.buy("LINKETH", quantized_amount,
                                       OrderType.LIMIT, quantize_bid_price)
            [order_created_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id,
                             list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[
                Order] = recorder.get_orders_for_config_and_market(
                    config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(
                config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: BinanceMarket = BinanceMarket(
                binance_api_key=API_KEY,
                binance_api_secret=API_SECRET,
                order_book_tracker_data_source_type=
                OrderBookTrackerDataSourceType.EXCHANGE_API,
                user_stream_tracker_data_source_type=
                UserStreamTrackerDataSourceType.EXCHANGE_API,
                trading_pairs=["LINKETH", "ZRXETH"])
            for event_tag in self.events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path,
                                       strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(
                saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            if API_MOCK_ENABLED:
                resp = self.fixture(FixtureBinance.CANCEL_ORDER,
                                    origClientOrderId=order_id,
                                    side="BUY")
                self.web_app.update_response(
                    "delete",
                    self.base_api_url,
                    "/api/v3/order",
                    resp,
                    params={'origClientOrderId': order_id})
            self.market.cancel("LINKETH", order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel("LINKETH", order_id)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
class HummingbotApplication(*commands):
    KILL_TIMEOUT = 10.0
    APP_WARNING_EXPIRY_DURATION = 3600.0
    APP_WARNING_STATUS_LIMIT = 6

    _main_app: Optional["HummingbotApplication"] = None

    @classmethod
    def logger(cls) -> HummingbotLogger:
        global s_logger
        if s_logger is None:
            s_logger = logging.getLogger(__name__)
        return s_logger

    @classmethod
    def main_application(cls) -> "HummingbotApplication":
        if cls._main_app is None:
            cls._main_app = HummingbotApplication()
        return cls._main_app

    def __init__(self):
        self.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
        self.parser: ThrowingArgumentParser = load_parser(self)
        self.app = HummingbotCLI(input_handler=self._handle_command,
                                 bindings=load_key_bindings(self),
                                 completer=load_completer(self))

        self.markets: Dict[str, MarketBase] = {}
        self.wallet: Optional[Web3Wallet] = None
        # strategy file name and name get assigned value after import or create command
        self.strategy_file_name: str = None
        self.strategy_name: str = None
        self.strategy_task: Optional[asyncio.Task] = None
        self.strategy: Optional[StrategyBase] = None
        self.market_pair: Optional[CrossExchangeMarketPair] = None
        self.market_trading_pair_tuples: List[MarketTradingPairTuple] = []
        self.clock: Optional[Clock] = None

        self.init_time: int = int(time.time() * 1e3)
        self.start_time: Optional[int] = None
        self.assets: Optional[Set[str]] = set()
        self.starting_balances = {}
        self.placeholder_mode = False
        self.log_queue_listener: Optional[
            logging.handlers.QueueListener] = None
        self.data_feed: Optional[DataFeedBase] = None
        self.notifiers: List[NotifierBase] = []
        self.kill_switch: Optional[KillSwitch] = None
        self._app_warnings: Deque[ApplicationWarning] = deque()
        self._trading_required: bool = True

        self.trade_fill_db: SQLConnectionManager = SQLConnectionManager.get_trade_fills_instance(
        )
        self.markets_recorder: Optional[MarketsRecorder] = None
        self._script_iterator = None

    @property
    def strategy_config_map(self):
        if self.strategy_name is not None:
            return get_strategy_config_map(self.strategy_name)
        return None

    def _notify(self, msg: str):
        self.app.log(msg)
        for notifier in self.notifiers:
            notifier.add_msg_to_queue(msg)

    def _handle_command(self, raw_command: str):
        # unset to_stop_config flag it triggered before loading any command
        if self.app.to_stop_config:
            self.app.to_stop_config = False

        raw_command = raw_command.lower().strip()
        try:
            if self.placeholder_mode:
                pass
            else:
                args = self.parser.parse_args(args=raw_command.split())
                kwargs = vars(args)
                if not hasattr(args, "func"):
                    return
                f = args.func
                del kwargs["func"]
                f(**kwargs)
        except InvalidCommandError as e:
            self._notify("Invalid command: %s" % (str(e), ))
        except ArgumentParserError as e:
            if not self.be_silly(raw_command):
                self._notify(str(e))
        except NotImplementedError:
            self._notify(
                "Command not yet implemented. This feature is currently under development."
            )
        except Exception as e:
            self.logger().error(e, exc_info=True)

    async def _cancel_outstanding_orders(self) -> bool:
        success = True
        try:
            on_chain_cancel_on_exit = global_config_map.get(
                "on_chain_cancel_on_exit").value
            bamboo_relay_use_coordinator = global_config_map.get(
                "bamboo_relay_use_coordinator").value
            kill_timeout: float = self.KILL_TIMEOUT
            self._notify("Cancelling outstanding orders...")

            for market_name, market in self.markets.items():
                # By default, the bot does not cancel orders on exit on Radar Relay or Bamboo Relay,
                # since all open orders will expire in a short window
                if not on_chain_cancel_on_exit and (
                        market_name == "radar_relay" or
                    (market_name == "bamboo_relay"
                     and not bamboo_relay_use_coordinator)):
                    continue
                cancellation_results = await market.cancel_all(kill_timeout)
                uncancelled = list(
                    filter(lambda cr: cr.success is False,
                           cancellation_results))
                if len(uncancelled) > 0:
                    success = False
                    uncancelled_order_ids = list(
                        map(lambda cr: cr.order_id, uncancelled))
                    self._notify(
                        "\nFailed to cancel the following orders on %s:\n%s" %
                        (market_name, '\n'.join(uncancelled_order_ids)))
        except Exception:
            self.logger().error("Error canceling outstanding orders.",
                                exc_info=True)
            success = False

        if success:
            self._notify("All outstanding orders cancelled.")
        return success

    async def run(self):
        await self.app.run()

    def add_application_warning(self, app_warning: ApplicationWarning):
        self._expire_old_application_warnings()
        self._app_warnings.append(app_warning)

    def clear_application_warning(self):
        self._app_warnings.clear()

    @staticmethod
    def _initialize_market_assets(
            market_name: str,
            trading_pairs: List[str]) -> List[Tuple[str, str]]:
        market_class: MarketBase = MARKET_CLASSES.get(market_name, MarketBase)
        market_trading_pairs: List[Tuple[str, str]] = [
            market_class.split_trading_pair(trading_pair)
            for trading_pair in trading_pairs
        ]
        return market_trading_pairs

    @staticmethod
    def _convert_to_exchange_trading_pair(
            market_name: str, hb_trading_pair: List[str]) -> List[str]:
        market_class: MarketBase = MARKET_CLASSES.get(market_name, MarketBase)
        return [
            market_class.convert_to_exchange_trading_pair(trading_pair)
            for trading_pair in hb_trading_pair
        ]

    def _initialize_wallet(self, token_trading_pairs: List[str]):
        if not using_wallet():
            return

        ethereum_wallet = global_config_map.get("ethereum_wallet").value
        private_key = Security._private_keys[ethereum_wallet]
        ethereum_rpc_url = global_config_map.get("ethereum_rpc_url").value
        erc20_token_addresses = get_erc20_token_addresses(token_trading_pairs)

        chain_name: str = global_config_map.get("ethereum_chain_name").value
        self.wallet: Web3Wallet = Web3Wallet(
            private_key=private_key,
            backend_urls=[ethereum_rpc_url],
            erc20_token_addresses=erc20_token_addresses,
            chain=getattr(EthereumChain, chain_name),
        )

    def _initialize_markets(self, market_names: List[Tuple[str, List[str]]]):
        ethereum_rpc_url = global_config_map.get("ethereum_rpc_url").value

        # aggregate trading_pairs if there are duplicate markets
        market_trading_pairs_map = {}
        for market_name, trading_pairs in market_names:
            if market_name not in market_trading_pairs_map:
                market_trading_pairs_map[market_name] = []
            market_class: MarketBase = MARKET_CLASSES.get(
                market_name, MarketBase)
            for trading_pair in trading_pairs:
                exchange_trading_pair: str = market_class.convert_to_exchange_trading_pair(
                    trading_pair)
                market_trading_pairs_map[market_name].append(
                    exchange_trading_pair)

        for market_name, trading_pairs in market_trading_pairs_map.items():
            if global_config_map.get("paper_trade_enabled").value:
                try:
                    market = create_paper_trade_market(market_name,
                                                       trading_pairs)
                except Exception:
                    raise
                paper_trade_account_balance = global_config_map.get(
                    "paper_trade_account_balance").value
                for asset, balance in paper_trade_account_balance.items():
                    market.set_balance(asset, balance)

            elif market_name == "binance":
                binance_api_key = global_config_map.get(
                    "binance_api_key").value
                binance_api_secret = global_config_map.get(
                    "binance_api_secret").value
                market = BinanceMarket(
                    binance_api_key,
                    binance_api_secret,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required,
                )

            elif market_name == "radar_relay":
                assert self.wallet is not None
                market = RadarRelayMarket(
                    wallet=self.wallet,
                    ethereum_rpc_url=ethereum_rpc_url,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required,
                )

            elif market_name == "bamboo_relay":
                assert self.wallet is not None
                use_coordinator = global_config_map.get(
                    "bamboo_relay_use_coordinator").value
                pre_emptive_soft_cancels = global_config_map.get(
                    "bamboo_relay_pre_emptive_soft_cancels").value
                market = BambooRelayMarket(
                    wallet=self.wallet,
                    ethereum_rpc_url=ethereum_rpc_url,
                    trading_pairs=trading_pairs,
                    use_coordinator=use_coordinator,
                    pre_emptive_soft_cancels=pre_emptive_soft_cancels,
                    trading_required=self._trading_required,
                )

            elif market_name == "coinbase_pro":
                coinbase_pro_api_key = global_config_map.get(
                    "coinbase_pro_api_key").value
                coinbase_pro_secret_key = global_config_map.get(
                    "coinbase_pro_secret_key").value
                coinbase_pro_passphrase = global_config_map.get(
                    "coinbase_pro_passphrase").value

                market = CoinbaseProMarket(
                    coinbase_pro_api_key,
                    coinbase_pro_secret_key,
                    coinbase_pro_passphrase,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "huobi":
                huobi_api_key = global_config_map.get("huobi_api_key").value
                huobi_secret_key = global_config_map.get(
                    "huobi_secret_key").value
                market = HuobiMarket(
                    huobi_api_key,
                    huobi_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "liquid":
                liquid_api_key = global_config_map.get("liquid_api_key").value
                liquid_secret_key = global_config_map.get(
                    "liquid_secret_key").value

                market = LiquidMarket(
                    liquid_api_key,
                    liquid_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    user_stream_tracker_data_source_type=
                    UserStreamTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "dolomite":
                assert self.wallet is not None
                is_test_net: bool = global_config_map.get(
                    "ethereum_chain_name").value == "DOLOMITE_TEST"
                market = DolomiteMarket(
                    wallet=self.wallet,
                    ethereum_rpc_url=ethereum_rpc_url,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    isTestNet=is_test_net,
                    trading_required=self._trading_required,
                )
            elif market_name == "bittrex":
                bittrex_api_key = global_config_map.get(
                    "bittrex_api_key").value
                bittrex_secret_key = global_config_map.get(
                    "bittrex_secret_key").value
                market = BittrexMarket(
                    bittrex_api_key,
                    bittrex_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "kucoin":
                kucoin_api_key = global_config_map.get("kucoin_api_key").value
                kucoin_secret_key = global_config_map.get(
                    "kucoin_secret_key").value
                kucoin_passphrase = global_config_map.get(
                    "kucoin_passphrase").value
                market = KucoinMarket(
                    kucoin_api_key,
                    kucoin_passphrase,
                    kucoin_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            elif market_name == "eterbase":
                eterbase_api_key = global_config_map.get(
                    "eterbase_api_key").value
                eterbase_secret_key = global_config_map.get(
                    "eterbase_secret_key").value
                eterbase_account = global_config_map.get(
                    "eterbase_account").value
                market = EterbaseMarket(
                    eterbase_api_key,
                    eterbase_secret_key,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required,
                    eterbase_account=eterbase_account)
            elif market_name == "kraken":
                kraken_api_key = global_config_map.get("kraken_api_key").value
                kraken_secret_key = global_config_map.get(
                    "kraken_secret_key").value
                market = KrakenMarket(
                    kraken_api_key,
                    kraken_secret_key,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    trading_pairs=trading_pairs,
                    trading_required=self._trading_required)
            else:
                raise ValueError(f"Market name {market_name} is invalid.")

            self.markets[market_name]: MarketBase = market

        self.markets_recorder = MarketsRecorder(
            self.trade_fill_db,
            list(self.markets.values()),
            self.strategy_file_name,
            self.strategy_name,
        )
        self.markets_recorder.start()

    def _initialize_notifiers(self):
        if global_config_map.get("telegram_enabled").value:
            # TODO: refactor to use single instance
            if not any(
                [isinstance(n, TelegramNotifier) for n in self.notifiers]):
                self.notifiers.append(
                    TelegramNotifier(
                        token=global_config_map["telegram_token"].value,
                        chat_id=global_config_map["telegram_chat_id"].value,
                        hb=self,
                    ))
        for notifier in self.notifiers:
            notifier.start()
Beispiel #17
0
    def test_orders_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        symbol: str = ETH_QNT
        sql: SQLConnectionManager = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))

            # Try to put limit buy order for 0.05 ETH worth of QNT, and watch for order creation event.
            bid_price = Decimal("0.00000002")
            quantize_bid_price: Decimal = self.market.quantize_order_price(symbol, bid_price)

            amount: Decimal = Decimal("18000000")
            quantized_amount: Decimal = self.market.quantize_order_amount(symbol, amount)

            expires = int(time.time() + 60 * 5)
            order_id = self.market.buy(symbol, quantized_amount, OrderType.LIMIT, quantize_bid_price,
                                       expiration_ts=expires)
            [order_created_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id, list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[Order] = recorder.get_orders_for_config_and_market(config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.market_events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: IDEXMarket = IDEXMarket(
                idex_api_key=conf.idex_api_key,
                wallet=self.wallet,
                ethereum_rpc_url=conf.test_web3_provider_list[0],
                order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                symbols=[ETH_QNT]
            )
            for event_tag in self.market_events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            self.market.cancel(symbol, order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(config_path, self.market)
            self.assertEqual(1, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel(symbol, order_id)
                self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
    def test_orders_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()

        try:
            self.assertEqual(0, len(self.market.tracking_states))

            # Try to put limit buy order for 0.02 ETH worth of ZRX, and watch for order creation event.
            current_bid_price: Decimal = self.market.get_price("ZRXETH", True)
            bid_price: Decimal = current_bid_price * Decimal("0.8")
            quantize_bid_price: Decimal = self.market.quantize_order_price(
                "ZRXETH", bid_price)

            amount: Decimal = Decimal("0.02") / bid_price
            quantized_amount: Decimal = self.market.quantize_order_amount(
                "ZRXETH", amount)

            order_id = self.market.buy("ZRXETH", quantized_amount,
                                       OrderType.LIMIT, quantize_bid_price)
            [order_created_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event
            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id,
                             list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[
                Order] = recorder.get_orders_for_config_and_market(
                    config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(
                config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)
            for event_tag in self.events:
                self.market.remove_listener(event_tag, self.market_logger)
            self.market: BinanceMarket = BinanceMarket(
                binance_api_key=conf.binance_api_key,
                binance_api_secret=conf.binance_api_secret,
                order_book_tracker_data_source_type=
                OrderBookTrackerDataSourceType.EXCHANGE_API,
                user_stream_tracker_data_source_type=
                UserStreamTrackerDataSourceType.EXCHANGE_API,
                trading_pairs=["ZRXETH", "IOSTETH"])
            for event_tag in self.events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path,
                                       strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(
                saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            self.market.cancel("ZRXETH", order_id)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel("ZRXETH", order_id)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
class HummingbotApplication(*commands):
    KILL_TIMEOUT = 10.0
    IDEX_KILL_TIMEOUT = 30.0
    APP_WARNING_EXPIRY_DURATION = 3600.0
    APP_WARNING_STATUS_LIMIT = 6

    _main_app: Optional["HummingbotApplication"] = None

    @classmethod
    def logger(cls) -> HummingbotLogger:
        global s_logger
        if s_logger is None:
            s_logger = logging.getLogger(__name__)
        return s_logger

    @classmethod
    def main_application(cls) -> "HummingbotApplication":
        if cls._main_app is None:
            cls._main_app = HummingbotApplication()
        return cls._main_app

    def __init__(self):
        self.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
        self.parser: ThrowingArgumentParser = load_parser(self)
        self.app = HummingbotCLI(
            input_handler=self._handle_command,
            bindings=load_key_bindings(self),
            completer=load_completer(self))

        self.acct: Optional[LocalAccount] = None
        self.markets: Dict[str, MarketBase] = {}
        self.wallet: Optional[Web3Wallet] = None
        self.strategy_task: Optional[asyncio.Task] = None
        self.strategy: Optional[StrategyBase] = None
        self.market_pair: Optional[CrossExchangeMarketPair] = None
        self.market_trading_pair_tuples: List[MarketTradingPairTuple] = []
        self.clock: Optional[Clock] = None

        self.init_time: int = int(time.time() * 1e3)
        self.start_time: Optional[int] = None
        self.assets: Optional[Set[str]] = set()
        self.starting_balances = {}
        self.placeholder_mode = False
        self.log_queue_listener: Optional[logging.handlers.QueueListener] = None
        self.reporting_module: Optional[ReportAggregator] = None
        self.data_feed: Optional[DataFeedBase] = None
        self.notifiers: List[NotifierBase] = []
        self.kill_switch: Optional[KillSwitch] = None
        self.liquidity_bounty: Optional[LiquidityBounty] = None
        self._initialize_liquidity_bounty()
        self._app_warnings: Deque[ApplicationWarning] = deque()
        self._trading_required: bool = True

        self.trade_fill_db: SQLConnectionManager = SQLConnectionManager.get_trade_fills_instance()
        self.markets_recorder: Optional[MarketsRecorder] = None

    def init_reporting_module(self):
        if not self.reporting_module:
            self.reporting_module = ReportAggregator(
                self,
                report_aggregation_interval=global_config_map["reporting_aggregation_interval"].value,
                log_report_interval=global_config_map["reporting_log_interval"].value)
        self.reporting_module.start()

    def _notify(self, msg: str):
        self.app.log(msg)
        for notifier in self.notifiers:
            notifier.add_msg_to_queue(msg)

    def _handle_command(self, raw_command: str):
        raw_command = raw_command.lower().strip()
        try:
            if self.placeholder_mode:
                pass
            else:
                logging.getLogger("hummingbot.command_history").info(raw_command)
                args = self.parser.parse_args(args=raw_command.split())
                kwargs = vars(args)
                if not hasattr(args, "func"):
                    return
                f = args.func
                del kwargs['func']
                f(**kwargs)
        except InvalidCommandError as e:
            self._notify("Invalid command: %s" % (str(e),))
        except ArgumentParserError as e:
            self._notify(str(e))
        except NotImplementedError:
            self._notify("Command not yet implemented. This feature is currently under development.")
        except Exception as e:
            self.logger().error(e, exc_info=True)

    async def _cancel_outstanding_orders(self) -> bool:
        success = True
        try:
            on_chain_cancel_on_exit = global_config_map.get("on_chain_cancel_on_exit").value
            bamboo_relay_use_coordinator = global_config_map.get("bamboo_relay_use_coordinator").value
            kill_timeout: float = self.KILL_TIMEOUT
            self._notify("Cancelling outstanding orders...")

            for market_name, market in self.markets.items():
                if market_name == "idex":
                    self._notify(f"IDEX cancellations may take up to {int(self.IDEX_KILL_TIMEOUT)} seconds...")
                    kill_timeout = self.IDEX_KILL_TIMEOUT
                # By default, the bot does not cancel orders on exit on Radar Relay or Bamboo Relay,
                # since all open orders will expire in a short window
                if not on_chain_cancel_on_exit and (market_name == "radar_relay" or (market_name == "bamboo_relay" and not bamboo_relay_use_coordinator)):
                    continue
                cancellation_results = await market.cancel_all(kill_timeout)
                uncancelled = list(filter(lambda cr: cr.success is False, cancellation_results))
                if len(uncancelled) > 0:
                    success = False
                    uncancelled_order_ids = list(map(lambda cr: cr.order_id, uncancelled))
                    self._notify("\nFailed to cancel the following orders on %s:\n%s" % (
                        market_name,
                        '\n'.join(uncancelled_order_ids)
                    ))
        except Exception:
            self.logger().error(f"Error canceling outstanding orders.", exc_info=True)
            success = False

        if success:
            self._notify("All outstanding orders cancelled.")
        return success

    async def run(self):
        await self.app.run()

    def add_application_warning(self, app_warning: ApplicationWarning):
        self._expire_old_application_warnings()
        self._app_warnings.append(app_warning)

    def clear_application_warning(self):
        self._app_warnings.clear()

    @staticmethod
    def _initialize_market_assets(market_name: str, symbols: List[str]) -> List[Tuple[str, str]]:
        market: MarketBase = MARKET_CLASSES.get(market_name, MarketBase)
        market_symbols: List[Tuple[str, str]] = [market.split_symbol(symbol) for symbol in symbols]
        return market_symbols

    def _initialize_wallet(self, token_symbols: List[str]):
        ethereum_rpc_url = global_config_map.get("ethereum_rpc_url").value
        erc20_token_addresses = get_erc20_token_addresses(token_symbols)

        if self.acct is not None:
            self.wallet: Web3Wallet = Web3Wallet(private_key=self.acct.privateKey,
                                                 backend_urls=[ethereum_rpc_url],
                                                 erc20_token_addresses=erc20_token_addresses,
                                                 chain=EthereumChain.MAIN_NET)

    def _initialize_markets(self, market_names: List[Tuple[str, List[str]]]):
        ethereum_rpc_url = global_config_map.get("ethereum_rpc_url").value

        # aggregate symbols if there are duplicate markets
        market_symbols_map = {}
        for market_name, symbols in market_names:
            if market_name not in market_symbols_map:
                market_symbols_map[market_name] = []
            market_symbols_map[market_name] += symbols

        for market_name, symbols in market_symbols_map.items():
            if global_config_map.get("paper_trade_enabled").value:
                self._notify(f"\nPaper trade is enabled for market {market_name}")
                try:
                    market = create_paper_trade_market(market_name, symbols)
                except Exception:
                    raise
                paper_trade_account_balance = global_config_map.get("paper_trade_account_balance").value
                for asset, balance in paper_trade_account_balance:
                    market.set_balance(asset, balance)

            elif market_name == "ddex" and self.wallet:
                market = DDEXMarket(wallet=self.wallet,
                                    ethereum_rpc_url=ethereum_rpc_url,
                                    order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                                    symbols=symbols,
                                    trading_required=self._trading_required)

            elif market_name == "idex" and self.wallet:
                idex_api_key: str = global_config_map.get("idex_api_key").value
                try:
                    market = IDEXMarket(idex_api_key=idex_api_key,
                                        wallet=self.wallet,
                                        ethereum_rpc_url=ethereum_rpc_url,
                                        order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                                        symbols=symbols,
                                        trading_required=self._trading_required)
                except Exception as e:
                    self.logger().error(str(e))

            elif market_name == "binance":
                binance_api_key = global_config_map.get("binance_api_key").value
                binance_api_secret = global_config_map.get("binance_api_secret").value
                market = BinanceMarket(binance_api_key,
                                       binance_api_secret,
                                       order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                                       symbols=symbols,
                                       trading_required=self._trading_required)

            elif market_name == "radar_relay" and self.wallet:
                market = RadarRelayMarket(wallet=self.wallet,
                                          ethereum_rpc_url=ethereum_rpc_url,
                                          symbols=symbols,
                                          trading_required=self._trading_required)

            elif market_name == "bamboo_relay" and self.wallet:
                use_coordinator = global_config_map.get("bamboo_relay_use_coordinator").value
                pre_emptive_soft_cancels = global_config_map.get("bamboo_relay_pre_emptive_soft_cancels").value
                market = BambooRelayMarket(wallet=self.wallet,
                                           ethereum_rpc_url=ethereum_rpc_url,
                                           symbols=symbols,
                                           use_coordinator=use_coordinator,
                                           pre_emptive_soft_cancels=pre_emptive_soft_cancels,
                                           trading_required=self._trading_required)

            elif market_name == "coinbase_pro":
                coinbase_pro_api_key = global_config_map.get("coinbase_pro_api_key").value
                coinbase_pro_secret_key = global_config_map.get("coinbase_pro_secret_key").value
                coinbase_pro_passphrase = global_config_map.get("coinbase_pro_passphrase").value

                market = CoinbaseProMarket(coinbase_pro_api_key,
                                           coinbase_pro_secret_key,
                                           coinbase_pro_passphrase,
                                           symbols=symbols,
                                           trading_required=self._trading_required)
            elif market_name == "huobi":
                huobi_api_key = global_config_map.get("huobi_api_key").value
                huobi_secret_key = global_config_map.get("huobi_secret_key").value
                market = HuobiMarket(huobi_api_key,
                                     huobi_secret_key,
                                     order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.EXCHANGE_API,
                                     symbols=symbols,
                                     trading_required=self._trading_required)
            else:
                raise ValueError(f"Market name {market_name} is invalid.")

            self.markets[market_name]: MarketBase = market

        self.markets_recorder = MarketsRecorder(
            self.trade_fill_db,
            list(self.markets.values()),
            in_memory_config_map.get("strategy_file_path").value,
            in_memory_config_map.get("strategy").value
        )
        self.markets_recorder.start()

    def _initialize_notifiers(self):
        if global_config_map.get("telegram_enabled").value:
            # TODO: refactor to use single instance
            if not any([isinstance(n, TelegramNotifier) for n in self.notifiers]):
                self.notifiers.append(TelegramNotifier(token=global_config_map["telegram_token"].value,
                                                       chat_id=global_config_map["telegram_chat_id"].value,
                                                       hb=self))
        for notifier in self.notifiers:
            notifier.start()

    def _initialize_liquidity_bounty(self):
        if liquidity_bounty_config_map.get("liquidity_bounty_enabled").value is not None and \
           liquidity_bounty_config_map.get("liquidity_bounty_client_id").value is not None:
            self.liquidity_bounty = LiquidityBounty.get_instance()
            self.liquidity_bounty.start()
Beispiel #20
0
    def _initialize_markets(self, market_names: List[Tuple[str, List[str]]]):
        ethereum_rpc_url = global_config_map.get("ethereum_rpc_url").value

        # aggregate symbols if there are duplicate markets
        market_symbols_map = {}
        for market_name, symbols in market_names:
            if market_name not in market_symbols_map:
                market_symbols_map[market_name] = []
            market_symbols_map[market_name] += symbols

        for market_name, symbols in market_symbols_map.items():
            if market_name == "ddex" and self.wallet:
                market = DDEXMarket(
                    wallet=self.wallet,
                    ethereum_rpc_url=ethereum_rpc_url,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    symbols=symbols,
                    trading_required=self._trading_required)

            elif market_name == "idex" and self.wallet:
                try:
                    market = IDEXMarket(
                        wallet=self.wallet,
                        ethereum_rpc_url=ethereum_rpc_url,
                        order_book_tracker_data_source_type=
                        OrderBookTrackerDataSourceType.EXCHANGE_API,
                        symbols=symbols,
                        trading_required=self._trading_required)
                except Exception as e:
                    self.logger().error(str(e))

            elif market_name == "binance":
                binance_api_key = global_config_map.get(
                    "binance_api_key").value
                binance_api_secret = global_config_map.get(
                    "binance_api_secret").value
                market = BinanceMarket(
                    binance_api_key,
                    binance_api_secret,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    symbols=symbols,
                    trading_required=self._trading_required)

            elif market_name == "radar_relay" and self.wallet:
                market = RadarRelayMarket(
                    wallet=self.wallet,
                    ethereum_rpc_url=ethereum_rpc_url,
                    symbols=symbols,
                    trading_required=self._trading_required)

            elif market_name == "bamboo_relay" and self.wallet:
                market = BambooRelayMarket(wallet=self.wallet,
                                           ethereum_rpc_url=ethereum_rpc_url,
                                           symbols=symbols)

            elif market_name == "coinbase_pro":
                coinbase_pro_api_key = global_config_map.get(
                    "coinbase_pro_api_key").value
                coinbase_pro_secret_key = global_config_map.get(
                    "coinbase_pro_secret_key").value
                coinbase_pro_passphrase = global_config_map.get(
                    "coinbase_pro_passphrase").value

                market = CoinbaseProMarket(
                    coinbase_pro_api_key,
                    coinbase_pro_secret_key,
                    coinbase_pro_passphrase,
                    symbols=symbols,
                    trading_required=self._trading_required)

            else:
                raise ValueError(f"Market name {market_name} is invalid.")

            self.markets[market_name]: MarketBase = market

        self.markets_recorder = MarketsRecorder(
            self.trade_fill_db, list(self.markets.values()),
            in_memory_config_map.get("strategy_file_path").value,
            in_memory_config_map.get("strategy").value)
        self.markets_recorder.start()