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 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 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()
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)
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
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)
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.")
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()
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"))
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
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)
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)
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)
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)
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
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)