def setUp(self):
     self.weth_dai_data = DDEXOrderBookLoader("WETH-DAI", "WETH", "DAI")
     self.clock = Clock(ClockMode.BACKTEST, 1.0, self.start.timestamp(), self.end.timestamp())
     self.market = BacktestMarket()
     self.market.add_data(self.weth_dai_data)
     self.market.set_balance("WETH", 200.0)
     self.market.set_balance("DAI", 20000.0)
     self.clock.add_iterator(self.market)
Пример #2
0
 def setUp(self):
     #self.weth_dai_data = DDEXOrderBookLoader("WETH-DAI", "WETH", "DAI")
     self.pair_data = BinanceOrderBookLoaderV2(self.market_name, "ETH",
                                               "USDT")
     #self.pair_data = HuobiOrderBookLoader(self.market_name, "", "")
     self.clock = Clock(ClockMode.BACKTEST, 1.0, self.start.timestamp(),
                        self.end.timestamp())
     self.market = BacktestMarket()
     #self.market.add_data(self.weth_dai_data)
     self.market.add_data(self.pair_data)
     self.market.set_balance(self.quote, 200.0)
     self.market.set_balance(self.base, 20000.0)
     self.clock.add_iterator(self.market)
Пример #3
0
def main():
    # Define the data cache path.
    hummingsim.set_data_path(os.path.join(os.environ["PWD"], "data"))

    # Define the parameters for the backtest.
    start = pd.Timestamp("2018-12-12", tz="UTC")
    end = pd.Timestamp("2019-01-12", tz="UTC")
    binance_symbol = ("ETHUSDT", "ETH", "USDT")
    ddex_symbol = ("WETH-DAI", "WETH", "DAI")

    binance_market = BacktestMarket()
    ddex_market = BacktestMarket()
    binance_market.config = MarketConfig(AssetType.BASE_CURRENCY, 0.001,
                                         AssetType.QUOTE_CURRENCY, 0.001, {})
    ddex_market.config = MarketConfig(AssetType.BASE_CURRENCY, 0.001,
                                      AssetType.QUOTE_CURRENCY, 0.001, {})
    binance_loader = BinanceOrderBookLoaderV2(*binance_symbol)
    ddex_loader = DDEXOrderBookLoader(*ddex_symbol)

    binance_market.add_data(binance_loader)
    ddex_market.add_data(ddex_loader)

    binance_market.set_quantization_param(
        QuantizationParams("ETHUSDT", 5, 3, 5, 3))
    ddex_market.set_quantization_param(
        QuantizationParams("WETH-DAI", 5, 3, 5, 3))

    market_pair = CrossExchangeMarketPair(*([ddex_market] + list(ddex_symbol) +
                                            [binance_market] +
                                            list(binance_symbol)))
    strategy = CrossExchangeMarketMakingStrategy(
        [market_pair],
        0.003,
        logging_options=CrossExchangeMarketMakingStrategy.
        OPTION_LOG_MAKER_ORDER_FILLED)

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

    binance_market.set_balance("ETH", 10.0)
    binance_market.set_balance("USDT", 1000.0)
    ddex_market.set_balance("WETH", 10.0)
    ddex_market.set_balance("DAI", 1000.0)

    clock.backtest()
    binance_loader.close()
    ddex_loader.close()
Пример #4
0
 def setUp(self):
     self.clock: Clock = Clock(ClockMode.BACKTEST,
                               start_time=self.start_time,
                               end_time=self.end_time)
     self.order_book_loader: BinanceOrderBookLoaderV2 = BinanceOrderBookLoaderV2(
         "BTCUSDT", "BTC", "USDT")
     self.order_book: OrderBook = self.order_book_loader.order_book
     self.clock.add_iterator(self.order_book_loader)
 def setUp(self):
     self.clock: Clock = Clock(ClockMode.BACKTEST,
                               start_time=self.start_time,
                               end_time=self.end_time)
     self.order_book_loader: HuobiOrderBookLoader = HuobiOrderBookLoader(
         "btcusdt", "btc", "usdt")
     self.order_book: OrderBook = self.order_book_loader.order_book
     self.clock.add_iterator(self.order_book_loader)
Пример #6
0
async def main():
    cmd_args = CmdlineParser().parse_args()
    with open(cmd_args.key_file, "r") as fd:
        encrypted_json: Dict[str, any] = json.load(fd)

    wallet: Web3Wallet = Web3Wallet(
        Account.decrypt(encrypted_json, getpass.getpass("Wallet password: "******"createdAt"], o["id"], o["marketId"]) for o in orders],
            columns=["Created", "OrderID", "Symbol"])
        order_data.Created = order_data.Created.astype(
            "datetime64[ms]").astype("datetime64[ns, UTC]")
        order_data = order_data.set_index("Created")

        while True:
            try:
                sample_ids: List[str] = list(order_data.sample(10).OrderID)
                tasks: List[asyncio.Future] = [
                    market.get_order(order_id) for order_id in sample_ids
                ]
                response_data: List[Dict[str,
                                         any]] = await asyncio.gather(*tasks)

                mismatches: int = 0
                for expected_id, response in zip(sample_ids, response_data):
                    returned_order_id = response["id"]
                    if returned_order_id != expected_id:
                        print(
                            f"    - Error: requested for {expected_id} but got {returned_order_id} back."
                        )
                        mismatches += 1

                if mismatches < 1:
                    print(
                        f"[{str(pd.Timestamp.utcnow())}] All fetches passed.")
                else:
                    print(
                        f"[{str(pd.Timestamp.utcnow())}] {mismatches} out of 10 requests failed."
                    )

                now: float = time.time()
                next_tick: float = now // 1 + 1
                await asyncio.sleep(next_tick - now)
            except asyncio.CancelledError:
                raise
Пример #7
0
    def setUp(self):
        self.clock: Clock = Clock(ClockMode.BACKTEST, 1.0, self.start_timestamp, self.end_timestamp)
        self.maker_market: BacktestMarket = BacktestMarket()
        self.taker_market: BacktestMarket = BacktestMarket()
        self.maker_data: MockOrderBookLoader = MockOrderBookLoader(*self.maker_symbols)
        self.taker_data: MockOrderBookLoader = MockOrderBookLoader(*self.taker_symbols)
        self.maker_data.set_balanced_order_book(1.0, 0.5, 1.5, 0.01, 10)
        self.taker_data.set_balanced_order_book(1.0, 0.5, 1.5, 0.001, 3)
        self.maker_market.add_data(self.maker_data)
        self.taker_market.add_data(self.taker_data)
        self.maker_market.set_balance("COINALPHA", 5)
        self.maker_market.set_balance("WETH", 5)
        self.taker_market.set_balance("COINALPHA", 5)
        self.taker_market.set_balance("ETH", 5)
        self.maker_market.set_quantization_param(
            QuantizationParams(
                self.maker_symbols[0], 5, 5, 5, 5
            )
        )
        self.taker_market.set_quantization_param(
            QuantizationParams(
                self.taker_symbols[0], 5, 5, 5, 5
            )
        )

        self.market_pair: CrossExchangeMarketPair = CrossExchangeMarketPair(
            *(
                [self.maker_market] + self.maker_symbols +
                [self.taker_market] + self.taker_symbols +
                [2]
            )
        )

        logging_options: int = (CrossExchangeMarketMakingStrategy.OPTION_LOG_ALL &
                                (~CrossExchangeMarketMakingStrategy.OPTION_LOG_NULL_ORDER_SIZE))
        self.strategy: CrossExchangeMarketMakingStrategy = CrossExchangeMarketMakingStrategy(
            [self.market_pair],
            0.003,
            order_size_portfolio_ratio_limit=0.3,
            logging_options=logging_options
        )
        self.clock.add_iterator(self.maker_market)
        self.clock.add_iterator(self.taker_market)
        self.clock.add_iterator(self.strategy)

        self.maker_order_fill_logger: EventLogger = EventLogger()
        self.taker_order_fill_logger: EventLogger = EventLogger()
        self.cancel_order_logger: EventLogger = EventLogger()
        self.maker_market.add_listener(MarketEvent.OrderFilled, self.maker_order_fill_logger)
        self.taker_market.add_listener(MarketEvent.OrderFilled, self.taker_order_fill_logger)
        self.maker_market.add_listener(MarketEvent.OrderCancelled, self.cancel_order_logger)
Пример #8
0
    def setUp(self):
        self.clock: Clock = Clock(ClockMode.BACKTEST, 1.0, self.start_timestamp, self.end_timestamp)
        self.market_1: BacktestMarket = BacktestMarket()
        self.market_2: BacktestMarket = BacktestMarket()
        self.market_1_data: MockOrderBookLoader = MockOrderBookLoader(*self.market_1_symbols)
        self.market_2_data: MockOrderBookLoader = MockOrderBookLoader(*self.market_2_symbols)
        self.market_1_data.set_balanced_order_book(1.0, 0.5, 1.5, 0.01, 10)
        self.market_2_data.set_balanced_order_book(1.3, 0.5, 1.5, 0.001, 4)

        self.market_1.add_data(self.market_1_data)
        self.market_2.add_data(self.market_2_data)

        self.market_1.set_balance("COINALPHA", 5)
        self.market_1.set_balance("ETH", 5)

        self.market_2.set_balance("COINALPHA", 5)
        self.market_2.set_balance("WETH", 5)

        self.market_1.set_quantization_param(
            QuantizationParams(
                self.market_1_symbols[0], 5, 5, 5, 5
            )
        )
        self.market_2.set_quantization_param(
            QuantizationParams(
                self.market_2_symbols[0], 5, 5, 5, 5
            )
        )
        self.market_pair: ArbitrageMarketPair = ArbitrageMarketPair(
            *(
                [self.market_1] + self.market_1_symbols + [self.market_2] + self.market_2_symbols
            )
        )

        logging_options: int = ArbitrageStrategy.OPTION_LOG_ALL
        self.strategy: ArbitrageStrategy = ArbitrageStrategy(
            [self.market_pair],
            min_profitability=0.01
        )
        self.logging_options = logging_options
        self.clock.add_iterator(self.market_1)
        self.clock.add_iterator(self.market_2)
        self.clock.add_iterator(self.strategy)

        self.market_1_order_fill_logger: EventLogger = EventLogger()
        self.market_2_order_fill_logger: EventLogger = EventLogger()

        self.market_1.add_listener(MarketEvent.OrderFilled, self.market_1_order_fill_logger)
        self.market_2.add_listener(MarketEvent.OrderFilled, self.market_2_order_fill_logger)
 def setUpClass(cls):
     cls.clock: Clock = Clock(ClockMode.REALTIME)
     cls.wallet = Web3Wallet(private_key=conf.web3_private_key_radar,
                             backend_urls=conf.test_web3_provider_list,
                             erc20_token_addresses=[conf.mn_zerox_token_address, conf.mn_weth_token_address],
                             chain=EthereumChain.MAIN_NET)
     cls.market: RadarRelayMarket = RadarRelayMarket(wallet=cls.wallet,
                                                     web3_url=conf.test_web3_provider_list[0],
                                                     order_book_tracker_data_source_type=
                                                         OrderBookTrackerDataSourceType.EXCHANGE_API,
                                                     symbols=["ZRX-WETH"])
     print("Initializing Radar Relay market... ")
     cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
     cls.clock.add_iterator(cls.wallet)
     cls.clock.add_iterator(cls.market)
     cls.ev_loop.run_until_complete(cls.clock.run_til(time.time() + 1))
     cls.ev_loop.run_until_complete(cls.wait_til_ready())
     print("Ready.")
    def setUpClass(cls):
        global MAINNET_RPC_URL

        cls.clock: Clock = Clock(ClockMode.REALTIME)
        cls.market: BinanceMarket = BinanceMarket(
            MAINNET_RPC_URL,
            conf.binance_api_key,
            conf.binance_api_secret,
            order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.
            EXCHANGE_API,
            user_stream_tracker_data_source_type=UserStreamTrackerDataSourceType
            .EXCHANGE_API,
            symbols=["ZRXETH", "LOOMETH"])
        print("Initializing Binance market... this will take about a minute.")
        cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
        cls.clock.add_iterator(cls.market)
        cls.ev_loop.run_until_complete(cls.clock.run_til(time.time() + 1))
        cls.ev_loop.run_until_complete(cls.wait_til_ready())
        print("Ready.")
Пример #11
0
    def setUpClass(cls):
        cls.clock: Clock = Clock(ClockMode.REALTIME)
        cls.erc20_token_address = conf.test_erc20_token_address
        cls.w3 = Web3(Web3.HTTPProvider(conf.test_web3_provider_list[0]))

        cls.wallet_a = Web3Wallet(conf.web3_test_private_key_a,
                                  conf.test_web3_provider_list,
                                  [cls.erc20_token_address])
        cls.wallet_b = Web3Wallet(conf.web3_test_private_key_b,
                                  conf.test_web3_provider_list,
                                  [cls.erc20_token_address])

        cls.erc20_token: ERC20Token = list(
            cls.wallet_a.current_backend.erc20_tokens.values())[0]

        cls.clock.add_iterator(cls.wallet_a)
        cls.clock.add_iterator(cls.wallet_b)
        cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()

        next_iteration = (time.time() // 5.0 + 1) * 5
        cls.ev_loop.run_until_complete(cls.clock.run_til(next_iteration))
def ddex_printout():
    wethdai_data: DDEXOrderBookLoader = DDEXOrderBookLoader("WETH-DAI", "WETH", "DAI")
    market: Market = BacktestMarket()
    market.add_data(wethdai_data)

    market.set_balance("ETH", 20.0)

    print("Beginning Balance:", market.get_all_balances())

    start: pd.Timestamp = pd.Timestamp("2018-12-01", tz="UTC")
    end: pd.Timestamp = pd.Timestamp("2018-12-02", tz="UTC")
    clock: Clock = Clock(ClockMode.BACKTEST, 600.0, start.timestamp(), end.timestamp())

    print_out_strategy: PrintOutStrategy2 = PrintOutStrategy2(market)
    clock.add_iterator(market)
    clock.add_iterator(print_out_strategy)
    clock.backtest()

    print("End Balance:", market.get_all_balances())

    wethdai_data.close()
def bittrex_printout():
    ethusdt_data: BittrexOrderBookLoader = BittrexOrderBookLoader("USDT-ETH", "ETH", "USDT")
    trxeth_data: BittrexOrderBookLoader = BittrexOrderBookLoader("ETH-TRX", "TRX", "ETH")
    market: Market = BacktestMarket()
    market.add_data(trxeth_data)
    market.add_data(ethusdt_data)
    market.set_balance("ETH", 20.0)
    market.set_balance("TRX", 0.0)
    print("Beginning Balance:", market.get_all_balances())

    start: pd.Timestamp = pd.Timestamp("2018-12-11", tz="UTC")
    end: pd.Timestamp = pd.Timestamp("2018-12-12", tz="UTC")
    clock: Clock = Clock(ClockMode.BACKTEST, 600.0, start.timestamp(), end.timestamp())

    print_out_strategy: PrintOutStrategy2 = PrintOutStrategy2(market)
    clock.add_iterator(market)
    clock.add_iterator(print_out_strategy)
    clock.backtest()

    print("End Balance:", market.get_all_balances())
    ethusdt_data.close()
    trxeth_data.close()
Пример #14
0
 def setUpClass(cls):
     cls.clock: Clock = Clock(ClockMode.REALTIME)
     cls.market: CoinbaseProMarket = CoinbaseProMarket(
         web3_url=conf.test_web3_provider_list[0],
         coinbase_pro_api_key=conf.coinbase_pro_api_key,
         coinbase_pro_secret_key=conf.coinbase_pro_secret_key,
         coinbase_pro_passphrase=conf.coinbase_pro_passphrase,
         symbols=["ETH-USDC", "ETH-USD"])
     cls.wallet: Web3Wallet = Web3Wallet(
         private_key=conf.web3_private_key_coinbase_pro,
         backend_urls=conf.test_web3_provider_list,
         erc20_token_addresses=[
             conf.mn_weth_token_address, conf.mn_zerox_token_address
         ],
         chain=EthereumChain.MAIN_NET)
     print(
         "Initializing Coinbase Pro market... this will take about a minute."
     )
     cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
     cls.clock.add_iterator(cls.market)
     cls.clock.add_iterator(cls.wallet)
     cls.ev_loop.run_until_complete(cls.clock.run_til(time.time() + 1))
     cls.ev_loop.run_until_complete(cls.wait_til_ready())
     print("Ready.")
class CompositeOrderBookTest(unittest.TestCase):
    start: pd.Timestamp = pd.Timestamp("2019-01-25", tz="UTC")
    end: pd.Timestamp = pd.Timestamp("2019-01-26", tz="UTC")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def test_composite_order_book_adjustment(self):
        trades = {
            pd.Timestamp("2019-01-25 00:02:15+00:00").timestamp(): [
                ("WETH-DAI", "sell", 93.53 + 23.65)
            ]
        }
        strategy: CompositeOrderBookTestStrategy = CompositeOrderBookTestStrategy(self.market, trades)
        self.clock.add_iterator(strategy)
        self.clock.backtest_til(self.start.timestamp() + 60*2 + 15)
        self.clock.backtest_til(self.start.timestamp() + 60*2 + 25)
        self.verify_composite_order_book_adjustment(self.market.get_order_book("WETH-DAI"))
Пример #16
0
class HummingbotApplication:
    KILL_TIMEOUT = 5.0

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

    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[CrossExchangeMarketMakingStrategy] = None
        self.market_pair: Optional[CrossExchangeMarketPair] = None
        self.clock: Optional[Clock] = None

        self.placeholder_mode = False
        self.log_queue_listener: Optional[
            logging.handlers.QueueListener] = None
        self.reporting_module: Optional[ReportAggregator] = 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 _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.app.log("Invalid command: %s" % (str(e), ))
        except ArgumentParserError as e:
            self.app.log(str(e))
        except EnvironmentError as e:
            # Handle order book too thin error more gracefully
            if "no price quote is possible" in str(e):
                self.logger().error(
                    f"Order book error: Not enough volume on order book. Please consider choosing a "
                    f"trading pair with more volume or trade on a different exchange. {e}"
                )
        except NotImplementedError:
            self.app.log(
                "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:
        on_chain_cancel_on_exit = global_config_map.get(
            "on_chain_cancel_on_exit").value
        success = True
        self.app.log("Cancelling outstanding orders...")
        for market_name, market in self.markets.items():
            # By default, the bot does not cancel orders on exit on Radar Relay, since all open orders will
            # expire in a short window
            if not on_chain_cancel_on_exit and market_name == "radar_relay":
                continue
            cancellation_results = await market.cancel_all(self.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.app.log(
                    "\nFailed to cancel the following orders on %s:\n%s" %
                    (market_name, '\n'.join(uncancelled_order_ids)))
        if success:
            self.app.log("All outstanding orders cancelled.")
        return success

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

    @property
    def config_complete(self):
        return len(self._get_empty_configs()) == 0

    @staticmethod
    def _get_empty_configs() -> List[str]:
        config_map = load_required_configs()
        return [
            key for key, config in config_map.items() if config.value is None
        ]

    def get_wallet_balance(self) -> pd.DataFrame:
        return pd.DataFrame(data=list(self.wallet.get_all_balances().items()),
                            columns=["currency",
                                     "balance"]).set_index("currency")

    def get_exchange_balance(self, exchange_name: str) -> pd.DataFrame:
        market: MarketBase = self.markets[exchange_name]
        raw_balance: pd.DataFrame = pd.DataFrame(
            data=list(market.get_all_balances().items()),
            columns=["currency", "balance"]).set_index("currency")
        return raw_balance[raw_balance.balance > 0]

    def config(self, key: str = None):
        self.app.clear_input()
        if key is not None and key not in load_required_configs().keys():
            self.app.log("Invalid config variable %s" % (key, ))
            return
        if key is not None:
            keys = [key]
        else:
            keys = self._get_empty_configs()
        asyncio.ensure_future(self._config_loop(keys))

    async def _create_or_import_wallet(self):
        choice = await self.app.prompt(
            prompt=global_config_map.get("wallet").prompt)
        if choice == "import":
            private_key = await self.app.prompt(
                prompt="Your wallet private key >>> ", is_password=True)
            password = await self.app.prompt(
                prompt="A password to protect your wallet key >>> ",
                is_password=True)

            self.acct = import_and_save_wallet(password, private_key)
            self.app.log("Wallet %s imported into hummingbot" %
                         (self.acct.address, ))
        elif choice == "create":
            password = await self.app.prompt(
                prompt="A password to protect your wallet key >>> ",
                is_password=True)
            self.acct = create_and_save_wallet(password)
            self.app.log("New wallet %s created" % (self.acct.address, ))
        else:
            self.app.log('Invalid choice. Please enter "create" or "import".')
            result = await self._create_or_import_wallet()
            return result
        return self.acct.address

    async def _unlock_wallet(self):
        choice = await self.app.prompt(
            prompt=
            "Would you like to unlock your previously saved wallet? (y/n) >>> "
        )
        if choice.lower() == "y":
            wallets = list_wallets()
            self.app.log("Existing wallets:")
            self.list(obj="wallets")
            if len(wallets) == 1:
                public_key = wallets[0]
            else:
                public_key = await self.app.prompt(
                    prompt="Which wallet would you like to import ? >>> ")
            password = await self.app.prompt(prompt="Enter your password >>> ",
                                             is_password=True)
            try:
                acct = unlock_wallet(public_key=public_key, password=password)
                self.app.log("Wallet %s unlocked" % (acct.address, ))
                self.acct = acct
                return self.acct.address
            except Exception as e:
                self.app.log("Cannot unlock wallet. Please try again.")
                result = await self._unlock_wallet()
                return result
        else:
            value = await self._create_or_import_wallet()
            return value

    async def _import_or_create_strategy_config(self):
        current_strategy: str = in_memory_config_map.get("strategy").value
        strategy_file_path_cv: ConfigVar = in_memory_config_map.get(
            "strategy_file_path")
        choice = await self.app.prompt(
            prompt="Import previous configs or create a new config file? "
            "(import/create) >>> ")
        if choice == "import":
            strategy_path = await self.app.prompt(strategy_file_path_cv.prompt)
            strategy_path = strategy_path
            self.app.log(
                f"Loading previously saved config file from {strategy_path}..."
            )
        elif choice == "create":
            strategy_path = await copy_strategy_template(current_strategy)
            self.app.log(f"new config file at {strategy_path} created.")
        else:
            self.app.log('Invalid choice. Please enter "create" or "import".')
            strategy_path = await self._import_or_create_strategy_config()

        # Validate response
        if not strategy_file_path_cv.validate(strategy_path):
            self.app.log(
                f"Invalid path {strategy_path}. Please enter \"create\" or \"import\"."
            )
            strategy_path = await self._import_or_create_strategy_config()
        return strategy_path

    async def _config_loop(self, keys: List[str] = []):
        self.app.log("Please follow the prompt to complete configurations: ")
        self.placeholder_mode = True
        self.app.toggle_hide_input()

        single_key = len(keys) == 1

        async def single_prompt(cvar: ConfigVar):
            if cvar.required or single_key:
                if cvar.key == "strategy_file_path":
                    val = await self._import_or_create_strategy_config()
                elif cvar.key == "wallet":
                    wallets = list_wallets()
                    if len(wallets) > 0:
                        val = await self._unlock_wallet()
                    else:
                        val = await self._create_or_import_wallet()
                    logging.getLogger("hummingbot.public_eth_address").info(
                        val)
                else:
                    val = await self.app.prompt(prompt=cvar.prompt,
                                                is_password=cvar.is_secure)
                if not cvar.validate(val):
                    self.app.log("%s is not a valid %s value" %
                                 (val, cvar.key))
                    val = await single_prompt(cvar)
            else:
                val = cvar.value
            if val is None or (isinstance(val, string_types)
                               and len(val) == 0):
                val = cvar.default
            return val

        async def inner_loop(_keys: List[str]):
            for key in _keys:
                current_strategy: str = in_memory_config_map.get(
                    "strategy").value
                strategy_cm: Dict[str, ConfigVar] = get_strategy_config_map(
                    current_strategy)
                if key in in_memory_config_map:
                    cv: ConfigVar = in_memory_config_map.get(key)
                elif key in global_config_map:
                    cv: ConfigVar = global_config_map.get(key)
                else:
                    cv: ConfigVar = strategy_cm.get(key)

                value = await single_prompt(cv)
                cv.value = parse_cvar_value(cv, value)
                if single_key:
                    self.app.log(f"\nNew config saved:\n{key}: {str(value)}")
            if not self.config_complete:
                await inner_loop(self._get_empty_configs())

        try:
            await inner_loop(keys)
            await write_config_to_yml()
            if not single_key:
                self.app.log(
                    "\nConfig process complete. Enter \"start\" to start market making."
                )
        except asyncio.TimeoutError:
            self.logger().error("Prompt timeout")
        except Exception as err:
            self.logger().error("Unknown error while writing config. %s" %
                                (err, ),
                                exc_info=True)
        finally:
            self.app.toggle_hide_input()
            self.placeholder_mode = False
            self.app.change_prompt(prompt=">>> ")

    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, str]]):
        ethereum_rpc_url = global_config_map.get("ethereum_rpc_url").value

        for market_name, symbol in market_names:
            if market_name == "ddex" and self.wallet:
                market = DDEXMarket(
                    wallet=self.wallet,
                    web3_url=ethereum_rpc_url,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    symbols=[symbol])

            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(
                    web3_url=ethereum_rpc_url,
                    binance_api_key=binance_api_key,
                    binance_api_secret=binance_api_secret,
                    order_book_tracker_data_source_type=
                    OrderBookTrackerDataSourceType.EXCHANGE_API,
                    symbols=[symbol])

            elif market_name == "radar_relay" and self.wallet:
                market = RadarRelayMarket(wallet=self.wallet,
                                          web3_url=ethereum_rpc_url,
                                          symbols=[symbol])

            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(
                    web3_url=ethereum_rpc_url,
                    coinbase_pro_api_key=coinbase_pro_api_key,
                    coinbase_pro_secret_key=coinbase_pro_secret_key,
                    coinbase_pro_passphrase=coinbase_pro_passphrase,
                    symbols=[symbol])

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

            self.markets[market_name]: MarketBase = market

    def status(self) -> bool:
        if self.config_complete:
            self.app.log(" - Config complete")
        else:
            self.app.log(
                ' x Pending config. Please enter "config" before starting the bot.'
            )
            return False

        eth_node_valid = check_web3(
            global_config_map.get("ethereum_rpc_url").value)
        if eth_node_valid:
            self.app.log(" - Ethereum node running and current")
        else:
            self.app.log(
                ' x Bad ethereum rpc url. Your node may be syncing. '
                'Please re-configure by entering "config ethereum_rpc_url"')
            return False

        if self.wallet is not None:
            has_minimum_eth = self.wallet.get_balance("ETH") > 0.01
            if has_minimum_eth:
                self.app.log(" - Minimum ETH requirement satisfied")
            else:
                self.app.log(
                    " x Not enough ETH in wallet. "
                    "A small amount of Ether is required for sending transactions on Decentralized Exchanges"
                )

        loading_markets: List[str] = []
        for market_name, market in self.markets.items():
            if not market.ready:
                loading_markets.append(market_name)

        if self.strategy is None:
            return True
        elif len(loading_markets) > 0:
            for loading_market in loading_markets:
                self.app.log(
                    f" x Waiting for {loading_market} market to get ready for trading. "
                    f"Please keep the bot running and try to start again in a few minutes"
                )
            return False

        self.app.log(" - All markets ready")
        self.app.log("\n" + self.strategy.format_status())
        return True

    def help(self, command):
        if command == 'all':
            self.app.log(self.parser.format_help())
        else:
            subparsers_actions = [
                action for action in self.parser._actions
                if isinstance(action, argparse._SubParsersAction)
            ]

            for subparsers_action in subparsers_actions:
                subparser = subparsers_action.choices.get(command)
                self.app.log(subparser.format_help())

    def get_balance(self,
                    currency: str = "WETH",
                    wallet: bool = False,
                    exchange: str = None):
        if wallet:
            if self.wallet is None:
                self.app.log(
                    'Wallet not available. Please configure your wallet (Enter "config wallet")'
                )
            elif currency is None:
                self.app.log(f"{self.get_wallet_balance()}")
            else:
                self.app.log(self.wallet.get_balance(currency.upper()))
        elif exchange:
            if exchange in self.markets:
                if currency is None:
                    self.app.log(f"{self.get_exchange_balance(exchange)}")
                else:
                    self.app.log(self.markets[exchange].get_balance(
                        currency.upper()))
            else:
                self.app.log(
                    'The exchange you entered has not been initialized. '
                    'You may check your exchange balance after entering the "start" command.'
                )
        else:
            self.help("get_balance")

    def list(self, obj: str):
        if obj == "wallets":
            wallets = list_wallets()
            if len(wallets) == 0:
                self.app.log(
                    'Wallet not available. Please configure your wallet (Enter "config wallet")'
                )
            else:
                self.app.log('\n'.join(wallets))

        elif obj == "exchanges":
            if len(EXCHANGES) == 0:
                self.app.log("No exchanges available")
            else:
                self.app.log('\n'.join(EXCHANGES))

        elif obj == "configs":
            columns: List[str] = ["Key", "Current Value"]

            global_cvs: List[ConfigVar] = list(
                in_memory_config_map.values()) + list(
                    global_config_map.values())
            global_data: List[List[str, Any]] = [[
                cv.key,
                len(str(cv.value)) * "*" if cv.is_secure else str(cv.value)
            ] for cv in global_cvs]
            global_df: pd.DataFrame = pd.DataFrame(data=global_data,
                                                   columns=columns)
            self.app.log("\nglobal configs:")
            self.app.log(str(global_df))

            strategy = in_memory_config_map.get("strategy").value
            if strategy:
                strategy_cvs: List[ConfigVar] = get_strategy_config_map(
                    strategy).values()
                strategy_data: List[List[str, Any]] = [[
                    cv.key,
                    len(str(cv.value)) * "*" if cv.is_secure else str(cv.value)
                ] for cv in strategy_cvs]
                strategy_df: pd.DataFrame = pd.DataFrame(data=strategy_data,
                                                         columns=columns)

                self.app.log(f"\n{strategy} strategy configs:")
                self.app.log(str(strategy_df))

            self.app.log("\n")

        elif obj == "trades":
            lines = []
            if self.strategy is None:
                self.app.log("No strategy available, cannot show past trades.")
            else:
                if len(self.strategy.trades) > 0:
                    df = Trade.to_pandas(self.strategy.trades)
                    df_lines = str(df).split("\n")
                    lines.extend(["", "  Past trades:"] +
                                 ["    " + line for line in df_lines])
                else:
                    lines.extend(["  No past trades."])
            self.app.log("\n".join(lines))
        else:
            self.help("list")

    def describe(self, wallet: bool = False, exchange: str = None):
        if wallet:
            if self.wallet is None:
                self.app.log(
                    'None available. Your wallet may not have been initialized. Enter "start" to initialize '
                    'your wallet.')
            else:
                self.app.log(self.wallet.address)
                self.app.log(f"{self.get_wallet_balance()}")
        elif exchange is not None:
            if exchange in self.markets:
                self.app.log(f"{self.get_exchange_balance(exchange)}")
            else:
                raise InvalidCommandError(
                    "The exchange you specified has not been initialized")
        else:
            self.help("describe")

    def start(self, log_level: Optional[str] = None):
        is_valid = self.status()
        if not is_valid:
            return

        if log_level is not None:
            init_logging("hummingbot_logs.yml",
                         override_log_level=log_level.upper())

        ExchangeRateConversion.get_instance().start()
        strategy_name = in_memory_config_map.get("strategy").value
        self.init_reporting_module()
        self.app.log(
            f"Status check complete. Starting '{strategy_name}' strategy...")
        asyncio.ensure_future(self.start_market_making(strategy_name))

    async def start_market_making(self, strategy_name: str):
        strategy_cm = get_strategy_config_map(strategy_name)
        if strategy_name == "cross_exchange_market_making":
            maker_market = strategy_cm.get("maker_market").value.lower()
            taker_market = strategy_cm.get("taker_market").value.lower()
            raw_maker_symbol = strategy_cm.get(
                "maker_market_symbol").value.upper()
            raw_taker_symbol = strategy_cm.get(
                "taker_market_symbol").value.upper()
            min_profitability = strategy_cm.get("min_profitability").value
            trade_size_override = strategy_cm.get("trade_size_override").value
            strategy_report_interval = global_config_map.get(
                "strategy_report_interval").value
            limit_order_min_expiration = strategy_cm.get(
                "limit_order_min_expiration").value
            cancel_order_threshold = strategy_cm.get(
                "cancel_order_threshold").value
            active_order_canceling = strategy_cm.get(
                "active_order_canceling").value
            top_depth_tolerance_rules = [(re.compile(re_str), value)
                                         for re_str, value in strategy_cm.get(
                                             "top_depth_tolerance").value]
            top_depth_tolerance = 0.0

            for regex, tolerance_value in top_depth_tolerance_rules:
                if regex.match(raw_maker_symbol) is not None:
                    top_depth_tolerance = tolerance_value

            try:
                maker_assets: Tuple[str, str] = SymbolSplitter.split(
                    maker_market, raw_maker_symbol)
                taker_assets: Tuple[str, str] = SymbolSplitter.split(
                    taker_market, raw_taker_symbol)
            except ValueError as e:
                self.app.log(str(e))
                return

            market_names: List[Tuple[str,
                                     str]] = [(maker_market, raw_maker_symbol),
                                              (taker_market, raw_taker_symbol)]

            self._initialize_wallet(
                token_symbols=list(set(maker_assets + taker_assets)))
            self._initialize_markets(market_names)

            self.market_pair = CrossExchangeMarketPair(
                *([self.markets[maker_market], raw_maker_symbol] +
                  list(maker_assets) +
                  [self.markets[taker_market], raw_taker_symbol] +
                  list(taker_assets) + [top_depth_tolerance]))

            strategy_logging_options = (
                CrossExchangeMarketMakingStrategy.OPTION_LOG_CREATE_ORDER
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_ADJUST_ORDER |
                CrossExchangeMarketMakingStrategy.OPTION_LOG_MAKER_ORDER_FILLED
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_REMOVING_ORDER
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_STATUS_REPORT |
                CrossExchangeMarketMakingStrategy.OPTION_LOG_MAKER_ORDER_HEDGED
            )

            self.strategy = CrossExchangeMarketMakingStrategy(
                market_pairs=[self.market_pair],
                min_profitability=min_profitability,
                status_report_interval=strategy_report_interval,
                logging_options=strategy_logging_options,
                trade_size_override=trade_size_override,
                limit_order_min_expiration=limit_order_min_expiration,
                cancel_order_threshold=cancel_order_threshold,
                active_order_canceling=active_order_canceling)

        elif strategy_name == "arbitrage":
            primary_market = strategy_cm.get("primary_market").value.lower()
            secondary_market = strategy_cm.get(
                "secondary_market").value.lower()
            raw_primary_symbol = strategy_cm.get(
                "primary_market_symbol").value.upper()
            raw_secondary_symbol = strategy_cm.get(
                "secondary_market_symbol").value.upper()
            min_profitability = strategy_cm.get("min_profitability").value
            try:
                primary_assets: Tuple[str, str] = SymbolSplitter.split(
                    primary_market, raw_primary_symbol)
                secondary_assets: Tuple[str, str] = SymbolSplitter.split(
                    secondary_market, raw_secondary_symbol)

            except ValueError as e:
                self.app.log(str(e))
                return

            market_names: List[Tuple[str, str]] = [
                (primary_market, raw_primary_symbol),
                (secondary_market, raw_secondary_symbol)
            ]
            self._initialize_wallet(
                token_symbols=list(set(primary_assets + secondary_assets)))

            self._initialize_markets(market_names)
            self.market_pair = ArbitrageMarketPair(
                *([self.markets[primary_market], raw_primary_symbol] +
                  list(primary_assets) +
                  [self.markets[secondary_market], raw_secondary_symbol] +
                  list(secondary_assets)))

            strategy_logging_options = ArbitrageStrategy.OPTION_LOG_ALL

            self.strategy = ArbitrageStrategy(
                market_pairs=[self.market_pair],
                min_profitability=min_profitability,
                logging_options=strategy_logging_options)

        else:
            raise NotImplementedError

        try:
            self.clock = Clock(ClockMode.REALTIME)
            if self.wallet is not None:
                self.clock.add_iterator(self.wallet)
            for market in self.markets.values():
                if market is not None:
                    self.clock.add_iterator(market)
            self.clock.add_iterator(self.strategy)
            self.strategy_task: asyncio.Task = asyncio.ensure_future(
                self.clock.run())
            self.app.log(
                f"\n'{strategy_name}' strategy started.\n"
                f"You can use the `status` command to query the progress.")
        except Exception as e:
            self.logger().error(str(e), exc_info=True)

    async def stop(self, skip_order_cancellation: bool = False):
        self.app.log("\nWinding down...")
        if not skip_order_cancellation:
            # Remove the strategy from clock before cancelling orders, to
            # prevent race condition where the strategy tries to create more
            # ordres during cancellation.
            self.clock.remove_iterator(self.strategy)
            success = await self._cancel_outstanding_orders()
            if success:
                # Only erase markets when cancellation has been successful
                self.markets = {}
        if self.reporting_module:
            self.reporting_module.stop()
        if self.strategy_task is not None and not self.strategy_task.cancelled(
        ):
            self.strategy_task.cancel()
        self.wallet = None
        self.strategy_task = None
        self.strategy = None
        self.market_pair = None
        self.clock = None

    async def exit(self, force: bool = False):
        if self.strategy_task is not None and not self.strategy_task.cancelled(
        ):
            self.strategy_task.cancel()
        if force is False:
            success = await self._cancel_outstanding_orders()
            if not success:
                self.app.log(
                    'Wind down process terminated: Failed to cancel all outstanding orders. '
                    '\nYou may need to manually cancel remaining orders by logging into your chosen exchanges'
                    '\n\nTo force exit the app, enter "exit -f"')
                return
            # Freeze screen 1 second for better UI
            await asyncio.sleep(1)
        self.app.exit()

    async def export_private_key(self):
        if self.acct is None:
            self.app.log(
                "Your wallet is currently locked. Please enter \"config\""
                " to unlock your wallet first")
        else:
            self.placeholder_mode = True
            self.app.toggle_hide_input()

            ans = await self.app.prompt(
                "Are you sure you want to print your private key in plain text? (y/n) >>> "
            )

            if ans.lower() in {"y" or "yes"}:
                self.app.log(
                    "\nWarning: Never disclose this key. Anyone with your private keys can steal any assets "
                    "held in your account.\n")
                self.app.log("Your private key:")
                self.app.log(self.acct.privateKey.hex())

            self.app.change_prompt(prompt=">>> ")
            self.app.toggle_hide_input()
            self.placeholder_mode = False
Пример #17
0
    async def start_market_making(self, strategy_name: str):
        strategy_cm = get_strategy_config_map(strategy_name)
        if strategy_name == "cross_exchange_market_making":
            maker_market = strategy_cm.get("maker_market").value.lower()
            taker_market = strategy_cm.get("taker_market").value.lower()
            raw_maker_symbol = strategy_cm.get(
                "maker_market_symbol").value.upper()
            raw_taker_symbol = strategy_cm.get(
                "taker_market_symbol").value.upper()
            min_profitability = strategy_cm.get("min_profitability").value
            trade_size_override = strategy_cm.get("trade_size_override").value
            strategy_report_interval = global_config_map.get(
                "strategy_report_interval").value
            limit_order_min_expiration = strategy_cm.get(
                "limit_order_min_expiration").value
            cancel_order_threshold = strategy_cm.get(
                "cancel_order_threshold").value
            active_order_canceling = strategy_cm.get(
                "active_order_canceling").value
            top_depth_tolerance_rules = [(re.compile(re_str), value)
                                         for re_str, value in strategy_cm.get(
                                             "top_depth_tolerance").value]
            top_depth_tolerance = 0.0

            for regex, tolerance_value in top_depth_tolerance_rules:
                if regex.match(raw_maker_symbol) is not None:
                    top_depth_tolerance = tolerance_value

            try:
                maker_assets: Tuple[str, str] = SymbolSplitter.split(
                    maker_market, raw_maker_symbol)
                taker_assets: Tuple[str, str] = SymbolSplitter.split(
                    taker_market, raw_taker_symbol)
            except ValueError as e:
                self.app.log(str(e))
                return

            market_names: List[Tuple[str,
                                     str]] = [(maker_market, raw_maker_symbol),
                                              (taker_market, raw_taker_symbol)]

            self._initialize_wallet(
                token_symbols=list(set(maker_assets + taker_assets)))
            self._initialize_markets(market_names)

            self.market_pair = CrossExchangeMarketPair(
                *([self.markets[maker_market], raw_maker_symbol] +
                  list(maker_assets) +
                  [self.markets[taker_market], raw_taker_symbol] +
                  list(taker_assets) + [top_depth_tolerance]))

            strategy_logging_options = (
                CrossExchangeMarketMakingStrategy.OPTION_LOG_CREATE_ORDER
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_ADJUST_ORDER |
                CrossExchangeMarketMakingStrategy.OPTION_LOG_MAKER_ORDER_FILLED
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_REMOVING_ORDER
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_STATUS_REPORT |
                CrossExchangeMarketMakingStrategy.OPTION_LOG_MAKER_ORDER_HEDGED
            )

            self.strategy = CrossExchangeMarketMakingStrategy(
                market_pairs=[self.market_pair],
                min_profitability=min_profitability,
                status_report_interval=strategy_report_interval,
                logging_options=strategy_logging_options,
                trade_size_override=trade_size_override,
                limit_order_min_expiration=limit_order_min_expiration,
                cancel_order_threshold=cancel_order_threshold,
                active_order_canceling=active_order_canceling)

        elif strategy_name == "arbitrage":
            primary_market = strategy_cm.get("primary_market").value.lower()
            secondary_market = strategy_cm.get(
                "secondary_market").value.lower()
            raw_primary_symbol = strategy_cm.get(
                "primary_market_symbol").value.upper()
            raw_secondary_symbol = strategy_cm.get(
                "secondary_market_symbol").value.upper()
            min_profitability = strategy_cm.get("min_profitability").value
            try:
                primary_assets: Tuple[str, str] = SymbolSplitter.split(
                    primary_market, raw_primary_symbol)
                secondary_assets: Tuple[str, str] = SymbolSplitter.split(
                    secondary_market, raw_secondary_symbol)

            except ValueError as e:
                self.app.log(str(e))
                return

            market_names: List[Tuple[str, str]] = [
                (primary_market, raw_primary_symbol),
                (secondary_market, raw_secondary_symbol)
            ]
            self._initialize_wallet(
                token_symbols=list(set(primary_assets + secondary_assets)))

            self._initialize_markets(market_names)
            self.market_pair = ArbitrageMarketPair(
                *([self.markets[primary_market], raw_primary_symbol] +
                  list(primary_assets) +
                  [self.markets[secondary_market], raw_secondary_symbol] +
                  list(secondary_assets)))

            strategy_logging_options = ArbitrageStrategy.OPTION_LOG_ALL

            self.strategy = ArbitrageStrategy(
                market_pairs=[self.market_pair],
                min_profitability=min_profitability,
                logging_options=strategy_logging_options)

        else:
            raise NotImplementedError

        try:
            self.clock = Clock(ClockMode.REALTIME)
            if self.wallet is not None:
                self.clock.add_iterator(self.wallet)
            for market in self.markets.values():
                if market is not None:
                    self.clock.add_iterator(market)
            self.clock.add_iterator(self.strategy)
            self.strategy_task: asyncio.Task = asyncio.ensure_future(
                self.clock.run())
            self.app.log(
                f"\n'{strategy_name}' strategy started.\n"
                f"You can use the `status` command to query the progress.")
        except Exception as e:
            self.logger().error(str(e), exc_info=True)
Пример #18
0
ddex_market.add_data(ddex_loader)

binance_market.set_quantization_param(QuantizationParams(
    "ETHUSDT", 5, 3, 5, 3))
ddex_market.set_quantization_param(QuantizationParams("WETH-DAI", 5, 3, 5, 3))

market_pair1 = ArbitrageMarketPair(*([ddex_market] + list(ddex_symbol) +
                                     [binance_market] + list(binance_symbol)))

strategy = ArbitrageStrategy(
    [market_pair1],
    0.025,
    logging_options=ArbitrageStrategy.OPTION_LOG_CREATE_ORDER)

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

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

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

ddex_weth_price = ddex_market.get_price("WETH-DAI", False)
binance_eth_price = binance_market.get_price("ETHUSDT", False)
start_ddex_portfolio_value = ddex_market.get_balance(
 def setUp(self):
     self.clock: Clock = Clock(ClockMode.BACKTEST, start_time=self.start_time, end_time=self.end_time)
     self.order_book_loader: DDEXOrderBookLoader = DDEXOrderBookLoader("WETH-DAI", "WETH", "DAI")
     self.order_book: OrderBook = self.order_book_loader.order_book
     self.clock.add_iterator(self.order_book_loader)
Пример #20
0
class OrderExpirationTest(unittest.TestCase):
    start: pd.Timestamp = pd.Timestamp("2019-01-24", tz="UTC")
    end: pd.Timestamp = pd.Timestamp("2019-01-26", tz="UTC")
    market_name = "ETHUSDT"
    quote = "ETH"
    base = "USDT"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        # second limit order expired
        self.clock.backtest_til(self.start.timestamp() + 60 * 12 + 30)
        # check if order expired event is fired
        self.assertTrue(second_order_id_1 in [
            evt.order_id
            for i, evt in strategy.order_expired_events.iterrows()
        ])
        self.assertTrue(second_order_id_2 in [
            evt.order_id
            for i, evt in strategy.order_expired_events.iterrows()
        ])
        # check if the expired limit order is cleaned up
        self.verify_expired_order_cleanup(strategy.order_expired_events,
                                          self.market.limit_orders)
Пример #21
0
 def setUp(self):
     self.clock: Clock = Clock(ClockMode.BACKTEST, start_time=self.start_time, end_time=self.end_time)
     self.order_book_loader: RadarRelayOrderBookLoader = RadarRelayOrderBookLoader("WETH-DAI", "WETH", "DAI")
     self.order_book: RadarRelayOrderBook = self.order_book_loader.order_book
     self.active_order_tracker: RadarRelayActiveOrderTracker = self.order_book_loader.active_order_tracker
     self.clock.add_iterator(self.order_book_loader)
Пример #22
0
import asyncio
import time

from hummingbot.cli.settings import get_erc20_token_addresses
from wings.web3_wallet import Web3Wallet
from wings.clock import Clock, ClockMode
from wings.ddex_market import DDEXMarket
from wings.ethereum_chain import EthereumChain
from wings.order_book_tracker import OrderBookTrackerDataSourceType

token_addresses = get_erc20_token_addresses(["WETH", "DAI"])
zrx_addr = "0x74622073a4821dbfd046E9AA2ccF691341A076e1"
pkey = "7BB21B1C4C9C0A474BCD08C1BA3C31ACEA8B6840AC72A67EDD38CB32899CBF87"
server = "http://aws-mainnet-1.mainnet-rpc-headless.mainnet:8545"
clock = Clock(ClockMode.REALTIME)
wallet = Web3Wallet(pkey, [server],
                    token_addresses,
                    chain=EthereumChain.MAIN_NET)
market = DDEXMarket(
    wallet,
    server,
    order_book_tracker_data_source_type=OrderBookTrackerDataSourceType.
    EXCHANGE_API,
    symbols=["WETH-DAI"])
clock.add_iterator(wallet)
clock.add_iterator(market)


async def main():
    begin = time.time() // 1
Пример #23
0
    async def start_market_making(self, strategy_name: str):
        strategy_cm = get_strategy_config_map(strategy_name)
        if strategy_name == "cross_exchange_market_making":
            maker_market = strategy_cm.get("maker_market").value.lower()
            taker_market = strategy_cm.get("taker_market").value.lower()
            raw_maker_symbol = strategy_cm.get(
                "maker_market_symbol").value.upper()
            raw_taker_symbol = strategy_cm.get(
                "taker_market_symbol").value.upper()
            min_profitability = strategy_cm.get("min_profitability").value
            trade_size_override = strategy_cm.get("trade_size_override").value
            strategy_report_interval = global_config_map.get(
                "strategy_report_interval").value
            limit_order_min_expiration = strategy_cm.get(
                "limit_order_min_expiration").value
            cancel_order_threshold = strategy_cm.get(
                "cancel_order_threshold").value
            active_order_canceling = strategy_cm.get(
                "active_order_canceling").value
            top_depth_tolerance_rules = [(re.compile(re_str), value)
                                         for re_str, value in strategy_cm.get(
                                             "top_depth_tolerance").value]
            top_depth_tolerance = 0.0

            for regex, tolerance_value in top_depth_tolerance_rules:
                if regex.match(raw_maker_symbol) is not None:
                    top_depth_tolerance = tolerance_value

            try:
                maker_assets: Tuple[str, str] = SymbolSplitter.split(
                    maker_market, raw_maker_symbol)
                taker_assets: Tuple[str, str] = SymbolSplitter.split(
                    taker_market, raw_taker_symbol)
            except ValueError as e:
                self.app.log(str(e))
                return

            market_names: List[Tuple[str, List[str]]] = [
                (maker_market, [raw_maker_symbol]),
                (taker_market, [raw_taker_symbol])
            ]
            self._initialize_wallet(
                token_symbols=list(set(maker_assets + taker_assets)))
            self._initialize_markets(market_names)
            self.assets = set(maker_assets + taker_assets)

            self.market_pair = CrossExchangeMarketPair(
                *([self.markets[maker_market], raw_maker_symbol] +
                  list(maker_assets) +
                  [self.markets[taker_market], raw_taker_symbol] +
                  list(taker_assets) + [top_depth_tolerance]))

            strategy_logging_options = (
                CrossExchangeMarketMakingStrategy.OPTION_LOG_CREATE_ORDER
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_ADJUST_ORDER |
                CrossExchangeMarketMakingStrategy.OPTION_LOG_MAKER_ORDER_FILLED
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_REMOVING_ORDER
                | CrossExchangeMarketMakingStrategy.OPTION_LOG_STATUS_REPORT |
                CrossExchangeMarketMakingStrategy.OPTION_LOG_MAKER_ORDER_HEDGED
            )

            self.strategy = CrossExchangeMarketMakingStrategy(
                market_pairs=[self.market_pair],
                min_profitability=min_profitability,
                status_report_interval=strategy_report_interval,
                logging_options=strategy_logging_options,
                trade_size_override=trade_size_override,
                limit_order_min_expiration=limit_order_min_expiration,
                cancel_order_threshold=cancel_order_threshold,
                active_order_canceling=active_order_canceling)

        elif strategy_name == "arbitrage":
            primary_market = strategy_cm.get("primary_market").value.lower()
            secondary_market = strategy_cm.get(
                "secondary_market").value.lower()
            raw_primary_symbol = strategy_cm.get(
                "primary_market_symbol").value.upper()
            raw_secondary_symbol = strategy_cm.get(
                "secondary_market_symbol").value.upper()
            min_profitability = strategy_cm.get("min_profitability").value
            try:
                primary_assets: Tuple[str, str] = SymbolSplitter.split(
                    primary_market, raw_primary_symbol)
                secondary_assets: Tuple[str, str] = SymbolSplitter.split(
                    secondary_market, raw_secondary_symbol)

            except ValueError as e:
                self.app.log(str(e))
                return

            market_names: List[Tuple[str, List[str]]] = [
                (primary_market, [raw_primary_symbol]),
                (secondary_market, [raw_secondary_symbol])
            ]
            self._initialize_wallet(
                token_symbols=list(set(primary_assets + secondary_assets)))
            self._initialize_markets(market_names)
            self.assets = set(primary_assets + secondary_assets)

            self.market_pair = ArbitrageMarketPair(
                *([self.markets[primary_market], raw_primary_symbol] +
                  list(primary_assets) +
                  [self.markets[secondary_market], raw_secondary_symbol] +
                  list(secondary_assets)))

            strategy_logging_options = ArbitrageStrategy.OPTION_LOG_ALL

            self.strategy = ArbitrageStrategy(
                market_pairs=[self.market_pair],
                min_profitability=min_profitability,
                logging_options=strategy_logging_options)
        elif strategy_name == "discovery":
            try:
                market_1 = strategy_cm.get("primary_market").value.lower()
                market_2 = strategy_cm.get("secondary_market").value.lower()
                target_symbol_1 = list(
                    strategy_cm.get("target_symbol_1").value)
                target_symbol_2 = list(
                    strategy_cm.get("target_symbol_2").value)
                target_profitability = float(
                    strategy_cm.get("target_profitability").value)
                target_amount = float(strategy_cm.get("target_amount").value)
                equivalent_token: List[List[str]] = list(
                    strategy_cm.get("equivalent_tokens").value)

                market_names: List[Tuple[str, List[str]]] = [
                    (market_1, target_symbol_1), (market_2, target_symbol_2)
                ]

                target_base_quote_1: List[Tuple[str, str]] = [
                    SymbolSplitter.split(market_1, symbol)
                    for symbol in target_symbol_1
                ]
                target_base_quote_2: List[Tuple[str, str]] = [
                    SymbolSplitter.split(market_2, symbol)
                    for symbol in target_symbol_2
                ]

                for asset_tuple in (target_base_quote_1 + target_base_quote_2):
                    self.assets.add(asset_tuple[0])
                    self.assets.add(asset_tuple[1])

                self._initialize_wallet(token_symbols=list(self.assets))
                self._initialize_markets(market_names)
                self.market_pair = DiscoveryMarketPair(*([
                    self.markets[market_1],
                    self.markets[market_1].get_active_exchange_markets
                ] + [
                    self.markets[market_2],
                    self.markets[market_2].get_active_exchange_markets
                ]))

                self.strategy = DiscoveryStrategy(
                    market_pairs=[self.market_pair],
                    target_symbols=target_base_quote_1 + target_base_quote_2,
                    equivalent_token=equivalent_token,
                    target_profitability=target_profitability,
                    target_amount=target_amount)
            except Exception as e:
                self.app.log(str(e))
                self.logger().error("Error initializing strategy.",
                                    exc_info=True)
        else:
            raise NotImplementedError

        try:
            self.clock = Clock(ClockMode.REALTIME)
            if self.wallet is not None:
                self.clock.add_iterator(self.wallet)
            for market in self.markets.values():
                if market is not None:
                    self.clock.add_iterator(market)
            self.clock.add_iterator(self.strategy)
            self.strategy_task: asyncio.Task = asyncio.ensure_future(
                self._run_clock())
            self.app.log(
                f"\n  '{strategy_name}' strategy started.\n"
                f"  You can use the `status` command to query the progress.")

            self.starting_balances = await self.wait_till_ready(
                self.balance_snapshot)
            self.stop_loss_tracker = StopLossTracker(
                self.data_feed, list(self.assets), list(self.markets.values()),
                lambda *args, **kwargs: asyncio.ensure_future(
                    self.stop(*args, **kwargs)))
            await self.wait_till_ready(self.stop_loss_tracker.start)
        except Exception as e:
            self.logger().error(str(e), exc_info=True)