async def fill_orders(orders, trader): if orders: assert trading_api.get_open_orders(trader.exchange_manager) for order in orders: order.filled_price = order.origin_price order.filled_quantity = order.origin_quantity await order.on_fill(force_fill=True) check_portfolio( trader.exchange_manager.exchange_personal_data. portfolio_manager.portfolio.portfolio, None, orders, True) assert len(trading_api.get_open_orders(trader.exchange_manager)) == 0
async def test_order_fill_callback_not_in_db(): try: producer, consumer, trader = await _get_tools() await producer._create_bottom_order(2, 1, 1) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 1)) open_orders = trading_api.get_open_orders(trader.exchange_manager) to_fill_order = open_orders[0] await _fill_order(to_fill_order, trader, trigger_update_callback=False, consumer=consumer) # remove order from db consumer.sell_targets_by_order_id = {} await consumer.trading_mode._order_notification_callback( None, trader.exchange_manager.id, None, symbol=to_fill_order.symbol, order=to_fill_order.to_dict(), is_from_bot=True, is_new=False) # create as task to allow creator's queue to get processed for _ in range(3): await asyncio_tools.wait_asyncio_next_cycle() await asyncio.create_task(_check_open_orders_count(trader, 3)) open_orders = trading_api.get_open_orders(trader.exchange_manager) assert all(o.status == trading_enums.OrderStatus.OPEN for o in open_orders) assert all(o.side == trading_enums.TradeOrderSide.SELL for o in open_orders) total_sell_quantity = sum(o.origin_quantity for o in open_orders) assert to_fill_order.origin_quantity * 0.95 <= total_sell_quantity <= to_fill_order.origin_quantity price = to_fill_order.filled_price max_price = price * consumer.PRICE_WEIGH_TO_PRICE_PERCENT[ consumer.DEFAULT_SELL_TARGET] increment = (max_price - price) / consumer.trading_mode.sell_orders_per_buy assert round(open_orders[0].origin_price, 7) == round(price + increment, 7) assert round(open_orders[1].origin_price, 7) == round(price + 2 * increment, 7) assert round(open_orders[2].origin_price, 7) == round(price + 3 * increment, 7) finally: await _stop(trader.exchange_manager)
def get_all_open_orders(): simulated_open_orders = [] real_open_orders = [] for exchange_manager in interfaces.get_exchange_managers(): if trading_api.is_trader_existing_and_enabled(exchange_manager): if trading_api.is_trader_simulated(exchange_manager): simulated_open_orders += trading_api.get_open_orders( exchange_manager) else: real_open_orders += trading_api.get_open_orders( exchange_manager) return real_open_orders, simulated_open_orders
async def test_create_bottom_order(): try: producer, consumer, trader = await _get_tools() price = 1000 market_quantity = 2 volume_weight = 1 risk_multiplier = 1.1 await producer._create_bottom_order(1, volume_weight, 1) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 1)) await asyncio_tools.wait_asyncio_next_cycle() order = trading_api.get_open_orders(trader.exchange_manager)[0] expected_quantity = market_quantity * risk_multiplier * \ consumer.VOLUME_WEIGH_TO_VOLUME_PERCENT[volume_weight] * \ consumer.SOFT_MAX_CURRENCY_RATIO assert round(order.origin_quantity, 7) == round(expected_quantity, 7) expected_price = price * consumer.LIMIT_PRICE_MULTIPLIER assert round(order.origin_price, 7) == round(expected_price, 7) portfolio = trader.exchange_manager.exchange_personal_data.portfolio_manager.portfolio.portfolio assert portfolio["USDT"][commons_constants.PORTFOLIO_AVAILABLE] > 0 assert order.order_id in consumer.sell_targets_by_order_id finally: await _stop(trader.exchange_manager)
async def test_create_orders_with_fixed_volume_per_order(): try: symbol = "BTC/USDT" producer, _, exchange_manager = await _get_tools(symbol) producer.buy_volume_per_order = decimal.Decimal("0.1") producer.sell_volume_per_order = decimal.Decimal("0.3") # set BTC/USD price at 4000 USD trading_api.force_set_mark_price(exchange_manager, symbol, 4000) await producer._ensure_staggered_orders() await asyncio.create_task(_check_open_orders_count(exchange_manager, 27)) created_orders = trading_api.get_open_orders(exchange_manager) created_buy_orders = [o for o in created_orders if o.side is trading_enums.TradeOrderSide.BUY] created_sell_orders = [o for o in created_orders if o.side is trading_enums.TradeOrderSide.SELL] assert len(created_buy_orders) == 2 # not enough funds to create more orders assert len(created_sell_orders) == producer.sell_orders_count # 25 # ensure only closest orders got created with the right value and in the right order assert created_buy_orders[0].origin_price == 3995 assert created_buy_orders[1].origin_price == 3990 assert created_sell_orders[0].origin_price == 4005 assert created_sell_orders[1].origin_price == 4010 assert created_sell_orders[0] is created_orders[0] assert all(o.origin_quantity == float(producer.buy_volume_per_order) for o in created_buy_orders) assert all(o.origin_quantity == float(producer.sell_volume_per_order) for o in created_sell_orders) finally: await _stop(exchange_manager)
async def _fill_order(order, trader, trigger_update_callback=True, ignore_open_orders=False, consumer=None): initial_len = len(trading_api.get_open_orders(trader.exchange_manager)) await order.on_fill(force_fill=True) if order.status == trading_enums.OrderStatus.FILLED: if not ignore_open_orders: assert len(trading_api.get_open_orders( trader.exchange_manager)) == initial_len - 1 if trigger_update_callback: await asyncio_tools.wait_asyncio_next_cycle() else: with mock.patch.object(consumer, "create_new_orders", new=mock.AsyncMock()): await asyncio_tools.wait_asyncio_next_cycle()
async def test_create_orders_without_enough_funds_for_all_orders_3_total_orders(): try: symbol = "BTC/USDT" producer, _, exchange_manager = await _get_tools(symbol) producer.buy_funds = decimal.Decimal("0.07") # 1 order producer.sell_funds = decimal.Decimal("0.000025") # 2 orders # set BTC/USD price at 4000 USD trading_api.force_set_mark_price(exchange_manager, symbol, 4000) await producer._ensure_staggered_orders() btc_available_funds = producer._get_available_funds("BTC") usd_available_funds = producer._get_available_funds("USDT") used_btc = 10 - btc_available_funds used_usd = 1000 - usd_available_funds assert used_usd >= producer.buy_funds * decimal.Decimal(0.99) assert used_btc >= producer.sell_funds * decimal.Decimal(0.99) # btc_available_funds for reduced because orders are not created assert 10 - 0.001 <= btc_available_funds < 10 assert 1000 - 100 <= usd_available_funds < 1000 await asyncio.create_task(_check_open_orders_count(exchange_manager, 1 + 2)) created_orders = trading_api.get_open_orders(exchange_manager) created_buy_orders = [o for o in created_orders if o.side is trading_enums.TradeOrderSide.BUY] created_sell_orders = [o for o in created_orders if o.side is trading_enums.TradeOrderSide.SELL] assert len(created_buy_orders) < producer.buy_orders_count assert len(created_buy_orders) == 1 assert len(created_sell_orders) < producer.sell_orders_count assert len(created_sell_orders) == 2 # ensure only orders closest to the current price have been created min_buy_price = 4000 - (producer.flat_spread / 2) - (producer.flat_increment * (len(created_buy_orders) - 1)) assert all( o.origin_price >= min_buy_price for o in created_buy_orders ) max_sell_price = 4000 + (producer.flat_spread / 2) + (producer.flat_increment * (len(created_sell_orders) - 1)) assert all( o.origin_price <= max_sell_price for o in created_sell_orders ) pf_btc_available_funds = trading_api.get_portfolio_currency(exchange_manager, "BTC") pf_usd_available_funds = trading_api.get_portfolio_currency(exchange_manager, "USDT") assert pf_btc_available_funds >= 10 - 0.000025 assert pf_usd_available_funds >= 1000 - 0.07 assert pf_btc_available_funds >= btc_available_funds assert pf_usd_available_funds >= usd_available_funds finally: await _stop(exchange_manager)
async def test_create_too_large_sell_orders(): try: producer, consumer, trader = await _get_tools() # case 1: too many orders to create: problem sell_quantity = 500000000 sell_target = 2 buy_price = 10000000 portfolio = trader.exchange_manager.exchange_personal_data.portfolio_manager.portfolio portfolio.portfolio["BTC"] = { commons_constants.PORTFOLIO_TOTAL: sell_quantity, commons_constants.PORTFOLIO_AVAILABLE: sell_quantity } order_id = "a" consumer.sell_targets_by_order_id[order_id] = sell_target await producer._create_sell_order_if_enabled(order_id, sell_quantity, buy_price) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 0)) # case 2: create split sell orders sell_quantity = 5000000 buy_price = 3000000 await producer._create_sell_order_if_enabled(order_id, sell_quantity, buy_price) # create as task to allow creator's queue to get processed for _ in range(17): await asyncio_tools.wait_asyncio_next_cycle() await asyncio.create_task(_check_open_orders_count(trader, 17)) open_orders = trading_api.get_open_orders(trader.exchange_manager) assert all(o.status == trading_enums.OrderStatus.OPEN for o in open_orders) assert all(o.side == trading_enums.TradeOrderSide.SELL for o in open_orders) total_sell_quantity = sum(o.origin_quantity for o in open_orders) assert sell_quantity * 0.9999 <= total_sell_quantity <= sell_quantity max_price = buy_price * consumer.PRICE_WEIGH_TO_PRICE_PERCENT[ sell_target] increment = (max_price - buy_price) / 17 assert round(open_orders[0].origin_price, 7) == round(buy_price + increment, 7) assert round(open_orders[-1].origin_price, 7) == round(max_price, 7) finally: await _stop(trader.exchange_manager)
async def _check_open_orders_count(trader, count): assert len(trading_api.get_open_orders(trader.exchange_manager)) == count
async def test_order_fill_callback(): try: producer, consumer, trader = await _get_tools() volume_weight = 1 price_weight = 1 await producer._create_bottom_order(1, volume_weight, price_weight) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 1)) # change weights to ensure no interference volume_weight = 3 price_weight = 3 open_orders = trading_api.get_open_orders(trader.exchange_manager) to_fill_order = open_orders[0] await _fill_order(to_fill_order, trader, consumer=consumer) # create as task to allow creator's queue to get processed for _ in range(consumer.trading_mode.sell_orders_per_buy): await asyncio_tools.wait_asyncio_next_cycle() await asyncio.create_task( _check_open_orders_count(trader, consumer.trading_mode.sell_orders_per_buy) ) assert to_fill_order.status == trading_enums.OrderStatus.FILLED open_orders = trading_api.get_open_orders(trader.exchange_manager) assert all(o.status == trading_enums.OrderStatus.OPEN for o in open_orders) assert all(o.side == trading_enums.TradeOrderSide.SELL for o in open_orders) total_sell_quantity = sum(o.origin_quantity for o in open_orders) assert to_fill_order.origin_quantity * 0.95 <= total_sell_quantity <= to_fill_order.origin_quantity price = to_fill_order.filled_price max_price = price * consumer.PRICE_WEIGH_TO_PRICE_PERCENT[1] increment = (max_price - price) / consumer.trading_mode.sell_orders_per_buy assert round(open_orders[0].origin_price, 7) == round(price + increment, 7) assert round(open_orders[1].origin_price, 7) == round(price + 2 * increment, 7) assert round(open_orders[2].origin_price, 7) == round(price + 3 * increment, 7) # now fill a sell order await _fill_order(open_orders[0], trader, consumer=consumer) # create as task to allow creator's queue to get processed await asyncio.create_task( _check_open_orders_count( trader, consumer.trading_mode.sell_orders_per_buy - 1)) # new buy order await producer._create_bottom_order(2, volume_weight, price_weight) # create as task to allow creator's queue to get processed await asyncio.create_task( _check_open_orders_count(trader, consumer.trading_mode.sell_orders_per_buy) ) finally: await _stop(trader.exchange_manager)
async def test_create_too_small_sell_orders(): try: producer, consumer, trader = await _get_tools() # case 1: not enough to create any order: problem sell_quantity = 0.001 sell_target = 2 buy_price = 0.001 order_id = "a" consumer.sell_targets_by_order_id[order_id] = sell_target await producer._create_sell_order_if_enabled(order_id, sell_quantity, buy_price) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 0)) # case 2: create less than 3 orders: 1 order sell_quantity = 0.1 buy_price = 0.01 await producer._create_sell_order_if_enabled(order_id, sell_quantity, buy_price) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 1)) open_orders = trading_api.get_open_orders(trader.exchange_manager) assert len(open_orders) == 1 assert all(o.status == trading_enums.OrderStatus.OPEN for o in open_orders) assert all(o.side == trading_enums.TradeOrderSide.SELL for o in open_orders) total_sell_quantity = sum(o.origin_quantity for o in open_orders) assert sell_quantity * 0.9999 <= total_sell_quantity <= sell_quantity max_price = buy_price * consumer.PRICE_WEIGH_TO_PRICE_PERCENT[ sell_target] assert round(open_orders[0].origin_price, 7) == round(max_price, 7) # case 3: create less than 3 orders: 2 orders sell_quantity = 0.2 sell_target = 2 buy_price = 0.01 # keep same order id to test no issue with it await producer._create_sell_order_if_enabled(order_id, sell_quantity, buy_price) # create as task to allow creator's queue to get processed for _ in range(3): await asyncio_tools.wait_asyncio_next_cycle() await asyncio.create_task(_check_open_orders_count(trader, 3)) open_orders = trading_api.get_open_orders(trader.exchange_manager) assert all(o.status == trading_enums.OrderStatus.OPEN for o in open_orders) assert all(o.side == trading_enums.TradeOrderSide.SELL for o in open_orders) second_total_sell_quantity = sum(o.origin_quantity for o in open_orders if o.origin_price >= 0.0107) assert sell_quantity * 0.9999 <= second_total_sell_quantity <= sell_quantity max_price = buy_price * consumer.PRICE_WEIGH_TO_PRICE_PERCENT[ sell_target] increment = (max_price - buy_price) / 2 assert round(open_orders[1].origin_price, 7) == round(buy_price + increment, 7) assert round(open_orders[2].origin_price, 7) == round(max_price, 7) finally: await _stop(trader.exchange_manager)
async def test_create_sell_orders(): try: producer, consumer, trader = await _get_tools() sell_quantity = 5 sell_target = 2 buy_price = 100 order_id = "a" consumer.sell_targets_by_order_id[order_id] = sell_target await producer._create_sell_order_if_enabled(order_id, sell_quantity, buy_price) # create as task to allow creator's queue to get processed for _ in range(consumer.trading_mode.sell_orders_per_buy): await asyncio_tools.wait_asyncio_next_cycle() await asyncio.create_task( _check_open_orders_count(trader, consumer.trading_mode.sell_orders_per_buy) ) open_orders = trading_api.get_open_orders(trader.exchange_manager) assert all(o.status == trading_enums.OrderStatus.OPEN for o in open_orders) assert all(o.side == trading_enums.TradeOrderSide.SELL for o in open_orders) total_sell_quantity = sum(o.origin_quantity for o in open_orders) assert sell_quantity * 0.9999 <= total_sell_quantity <= sell_quantity max_price = buy_price * consumer.PRICE_WEIGH_TO_PRICE_PERCENT[ sell_target] increment = (max_price - buy_price) / consumer.trading_mode.sell_orders_per_buy assert round(open_orders[0].origin_price, 7) == round(buy_price + increment, 7) assert round(open_orders[1].origin_price, 7) == round(buy_price + 2 * increment, 7) assert round(open_orders[2].origin_price, 7) == round(buy_price + 3 * increment, 7) # now fill a sell order await _fill_order(open_orders[0], trader, trigger_update_callback=False, consumer=consumer) # create as task to allow creator's queue to get processed await asyncio.create_task( _check_open_orders_count( trader, consumer.trading_mode.sell_orders_per_buy - 1)) sell_quantity = 3 sell_target = 3 buy_price = 2525 order_id_2 = "b" consumer.sell_targets_by_order_id[order_id_2] = sell_target await producer._create_sell_order_if_enabled(order_id_2, sell_quantity, buy_price) # create as task to allow creator's queue to get processed for _ in range(consumer.trading_mode.sell_orders_per_buy): await asyncio_tools.wait_asyncio_next_cycle() await asyncio.create_task( _check_open_orders_count( trader, consumer.trading_mode.sell_orders_per_buy * 2 - 1)) open_orders = trading_api.get_open_orders(trader.exchange_manager) assert all(o.status == trading_enums.OrderStatus.OPEN for o in open_orders) assert all(o.side == trading_enums.TradeOrderSide.SELL for o in open_orders) total_sell_quantity = sum(o.origin_quantity for o in open_orders if o.origin_price > 150) assert sell_quantity * 0.9999 <= total_sell_quantity <= sell_quantity max_price = buy_price * consumer.PRICE_WEIGH_TO_PRICE_PERCENT[ sell_target] increment = (max_price - buy_price) / consumer.trading_mode.sell_orders_per_buy assert round(open_orders[2 + 0].origin_price, 7) == round(buy_price + increment, 7) assert round(open_orders[2 + 1].origin_price, 7) == round(buy_price + 2 * increment, 7) assert round(open_orders[2 + 2].origin_price, 7) == round(buy_price + 3 * increment, 7) # now fill a sell order await _fill_order(open_orders[-1], trader, trigger_update_callback=False, consumer=consumer) # create as task to allow creator's queue to get processed await asyncio.create_task( _check_open_orders_count( trader, consumer.trading_mode.sell_orders_per_buy * 2 - 2)) finally: await _stop(trader.exchange_manager)
async def test_create_bottom_order_replace_current(): try: producer, consumer, trader = await _get_tools() price = 1000 market_quantity = 2 volume_weight = 1 risk_multiplier = 1.1 portfolio = trader.exchange_manager.exchange_personal_data.portfolio_manager.portfolio # first order await producer._create_bottom_order(1, volume_weight, 1) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 1)) await asyncio_tools.wait_asyncio_next_cycle() first_order = trading_api.get_open_orders(trader.exchange_manager)[0] assert first_order.status == trading_enums.OrderStatus.OPEN expected_quantity = market_quantity * risk_multiplier * \ consumer.VOLUME_WEIGH_TO_VOLUME_PERCENT[volume_weight] * consumer.SOFT_MAX_CURRENCY_RATIO assert round(first_order.origin_quantity, 7) == round(expected_quantity, 7) expected_price = price * consumer.LIMIT_PRICE_MULTIPLIER assert round(first_order.origin_price, 7) == round(expected_price, 7) available_after_order = portfolio.portfolio["USDT"][ commons_constants.PORTFOLIO_AVAILABLE] assert available_after_order > 0 assert first_order.order_id in consumer.sell_targets_by_order_id # second order, same weight await producer._create_bottom_order(1, volume_weight, 1) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 1)) await asyncio_tools.wait_asyncio_next_cycle() second_order = trading_api.get_open_orders(trader.exchange_manager)[0] assert first_order.status == trading_enums.OrderStatus.CANCELED assert second_order.status == trading_enums.OrderStatus.OPEN assert second_order is not first_order assert round(second_order.origin_quantity, 7) == round(first_order.origin_quantity, 7) assert round(second_order.origin_price, 7) == round(first_order.origin_price, 7) assert portfolio.portfolio["USDT"][ commons_constants.PORTFOLIO_AVAILABLE] == available_after_order assert first_order.order_id not in consumer.sell_targets_by_order_id assert second_order.order_id in consumer.sell_targets_by_order_id # third order, different weight volume_weight = 3 await producer._create_bottom_order(1, volume_weight, 1) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 1)) await asyncio_tools.wait_asyncio_next_cycle() third_order = trading_api.get_open_orders(trader.exchange_manager)[0] assert second_order.status == trading_enums.OrderStatus.CANCELED assert third_order.status == trading_enums.OrderStatus.OPEN assert third_order is not second_order and third_order is not first_order expected_quantity = market_quantity * \ consumer.VOLUME_WEIGH_TO_VOLUME_PERCENT[volume_weight] * consumer.SOFT_MAX_CURRENCY_RATIO assert round(third_order.origin_quantity, 7) != round( first_order.origin_quantity, 7) assert round(third_order.origin_quantity, 7) == round(expected_quantity, 7) assert round(third_order.origin_price, 7) == round(first_order.origin_price, 7) available_after_third_order = portfolio.portfolio["USDT"][ commons_constants.PORTFOLIO_AVAILABLE] assert available_after_third_order < available_after_order assert second_order.order_id not in consumer.sell_targets_by_order_id assert third_order.order_id in consumer.sell_targets_by_order_id # fill third order await _fill_order(third_order, trader, trigger_update_callback=False, consumer=consumer) # fourth order: can't be placed: an order on this candle got filled volume_weight = 3 await producer._create_bottom_order(1, volume_weight, 1) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 0)) # fifth order: in the next candle volume_weight = 2 new_market_quantity = portfolio.portfolio["USDT"][ commons_constants.PORTFOLIO_AVAILABLE] / price await producer._create_bottom_order(2, volume_weight, 1) # create as task to allow creator's queue to get processed await asyncio.create_task(_check_open_orders_count(trader, 1)) await asyncio_tools.wait_asyncio_next_cycle() fifth_order = trading_api.get_open_orders(trader.exchange_manager)[0] assert third_order.status == trading_enums.OrderStatus.FILLED assert fifth_order.status == trading_enums.OrderStatus.OPEN assert fifth_order is not third_order and fifth_order is not second_order and fifth_order is not first_order expected_quantity = new_market_quantity * risk_multiplier * \ consumer.VOLUME_WEIGH_TO_VOLUME_PERCENT[volume_weight] * consumer.SOFT_MAX_CURRENCY_RATIO assert round(fifth_order.origin_quantity, 7) != round( first_order.origin_quantity, 7) assert round(fifth_order.origin_quantity, 7) != round( third_order.origin_quantity, 7) assert round(fifth_order.origin_quantity, 7) == round(expected_quantity, 7) assert round(fifth_order.origin_price, 7) == round(first_order.origin_price, 7) assert portfolio.portfolio["USDT"][ commons_constants. PORTFOLIO_AVAILABLE] < available_after_third_order assert first_order.order_id not in consumer.sell_targets_by_order_id assert second_order.order_id not in consumer.sell_targets_by_order_id # third_order still in _get_order_identifier to keep history assert third_order.order_id in consumer.sell_targets_by_order_id assert fifth_order.order_id in consumer.sell_targets_by_order_id finally: await _stop(trader.exchange_manager)
async def _check_open_orders_count(exchange_manager, count): await _wait_for_orders_creation(count) assert len(trading_api.get_open_orders(exchange_manager)) == count
def orders(exchange_name, symbol): exchange_manager = cli.exchanges[exchange_name][ "exchange_factory"].exchange_manager click.echo(api.get_open_orders(exchange_manager))