def test_sell_on_exchange(): wallets = [Wallet(exchange, 0 * USD), Wallet(exchange, 10 * BTC)] portfolio = Portfolio(base_instrument=USD, wallets=wallets) base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) quantity = (1 / 10) * quote_wallet.balance order = Order(side=TradeSide.SELL, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=quantity, portfolio=portfolio) current_price = 12390.90 quote_wallet -= quantity.size * order.pair.quote quote_wallet += order.quantity trade = exchange._execute_sell_order(order, base_wallet, quote_wallet, current_price) assert trade assert trade.size == 0.997 * BTC assert trade.commission == 0.003 * BTC
def test_cancel(mock_order_listener_class, mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.id = "fake_exchange_id" wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_name="coinbase", side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) listener = mock_order_listener_class.return_value listener.on_cancel = mock.Mock(return_value=None) order.attach(listener) order.execute(exchange) # Execute fake trade price = 7010.00 scale = order.price / price commission = 3.00 * USD trade = mock_trade_class.return_value trade.size = scale * order.size - commission.size trade.price = price trade.commission = commission base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) base_size = trade.size + commission.size quote_size = (order.price / trade.price) * (trade.size / trade.price) base_wallet -= Quantity(USD, size=base_size, path_id=order.path_id) quote_wallet += Quantity(BTC, size=quote_size, path_id=order.path_id) order.fill(exchange, trade) assert order.status == OrderStatus.PARTIALLY_FILLED assert base_wallet.balance == 4800.00 * USD assert round(base_wallet.locked[order.path_id].size, 2) == 7.42 assert quote_wallet.balance == 0 * BTC assert round(quote_wallet.locked[order.path_id].size, 8) == 0.73925519 order.cancel() listener.on_cancel.assert_called_once_with(order) assert round(base_wallet.balance.size, 2) == 4807.42 assert order.path_id not in base_wallet.locked assert round(quote_wallet.balance.size, 8) == 0.73925519 assert order.path_id not in quote_wallet.locked
def test_path_order_runs_though_broker(): wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(base_instrument=USD, wallets=wallets) exchange.reset() portfolio.reset() broker.reset() base_wallet = portfolio.get_wallet(exchange.id, USD) quantity = (1 / 10) * base_wallet.balance order = Order(side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=quantity, portfolio=portfolio) order = order.add_recipe( Recipe(side=TradeSide.SELL, trade_type=TradeType.MARKET, pair=USD / BTC, criteria=StopLoss(direction=StopDirection.EITHER, up_percent=0.02, down_percent=0.10))) broker.submit(order) while len(broker.unexecuted) > 0: broker.update() portfolio.update() obs = exchange.next_observation(1) pytest.fail("Failed.")
def test_execute(mock_order_listener_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.id = "fake_id" wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) listener = mock_order_listener_class.return_value listener.on_execute = mock.Mock(return_value=None) order.attach(listener) assert order.status == OrderStatus.PENDING order.execute(exchange) assert order.status == OrderStatus.OPEN wallet_usd = portfolio.get_wallet(exchange.id, USD) wallet_btc = portfolio.get_wallet(exchange.id, BTC) assert wallet_usd.balance == 4800 * USD assert wallet_usd.locked_balance == 5200 * USD assert order.path_id in wallet_usd.locked.keys() assert wallet_btc.balance == 0 * BTC listener.on_execute.assert_called_once_with(order, exchange)
def test_release(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.id = "fake_exchange_id" wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) order.execute(exchange) wallet_usd = portfolio.get_wallet(exchange.id, USD) assert wallet_usd.balance == 4800 * USD assert wallet_usd.locked_balance == 5200 * USD assert order.path_id in wallet_usd.locked.keys() order.release() assert wallet_usd.balance == 10000 * USD assert wallet_usd.locked_balance == 0 * USD assert order.path_id not in wallet_usd.locked.keys()
def test_release(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) order.execute() wallet_usd = portfolio.get_wallet(exchange.id, USD) assert wallet_usd.balance == 4800 * USD assert wallet_usd.locked_balance == 5200 * USD assert order.path_id in wallet_usd.locked.keys() order.release() assert wallet_usd.balance == 10000 * USD assert wallet_usd.locked_balance == 0 * USD assert order.path_id not in wallet_usd.locked.keys()
def test_init(): wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(base_instrument=USD, wallets=wallets) base_wallet = portfolio.get_wallet(exchange.id, USD) quantity = (1 / 10) * base_wallet.balance order = Order(side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=quantity, portfolio=portfolio) assert order assert order.id assert order.path_id assert order.quantity.instrument == USD assert order.filled_size == 0 assert order.remaining_size == order.quantity assert isinstance(order.pair, TradingPair) assert order.pair.base == USD assert order.pair.quote == BTC
def test_adding_recipe(): wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(base_instrument=USD, wallets=wallets) base_wallet = portfolio.get_wallet(exchange.id, USD) quantity = (1 / 10) * base_wallet.balance order = Order(side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=quantity, portfolio=portfolio) order += Recipe( side=TradeSide.SELL, trade_type=TradeType.MARKET, pair=BTC / USD, criteria=StopLoss(direction=StopDirection.EITHER, up_percent=0.02, down_percent=0.10), ) assert order.pair
def test_order_runs_through_broker(): wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(base_instrument=USD, wallets=wallets) exchange.reset() portfolio.reset() broker.reset() base_wallet = portfolio.get_wallet(exchange.id, USD) quantity = (1 / 10) * base_wallet.balance order = Order(side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=quantity, portfolio=portfolio) base_wallet -= quantity.size * order.pair.base base_wallet += order.quantity broker.submit(order) broker.update() portfolio.update()
def test_create_from_sell_order(mock_order_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 2 * BTC)] portfolio = Portfolio(USD, wallets) order = mock_order_class.return_value order.portfolio = portfolio order.exchange_pair = ExchangePair(exchange, USD / BTC) order.path_id = "fake_path_id" order.price = 7000.00 wallet_usd = portfolio.get_wallet(exchange.id, USD) wallet_usd.lock(quantity=1000 * USD, order=order, reason="test") assert float(wallet_usd.balance.size) == 9000 assert float(wallet_usd.locked[order.path_id].size) == 1000 order_spec = OrderSpec(side=TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC)) next_order = order_spec.create_order(order) assert next_order assert next_order.side == TradeSide.BUY assert next_order.type == TradeType.MARKET assert next_order.exchange_pair == ExchangePair(exchange, USD / BTC) assert next_order.path_id == order.path_id assert next_order.quantity.path_id == order.path_id assert next_order.quantity.instrument == USD
def test_create_from_buy_order(mock_order_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.id = "fake_id" wallets = [Wallet(exchange, 10000.00*USD), Wallet(exchange, 2*BTC)] portfolio = Portfolio(USD, wallets) order = mock_order_class.return_value order.portfolio = portfolio order.path_id = "fake_path_id" order.price = 7000.00 wallet_btc = portfolio.get_wallet(exchange.id, BTC) wallet_btc -= 0.4*BTC wallet_btc += Quantity(BTC, 0.4, path_id=order.path_id) assert wallet_btc.balance == 1.6 * BTC assert wallet_btc.locked[order.path_id].size == 0.4 * BTC order_spec = OrderSpec( side=TradeSide.SELL, trade_type=TradeType.MARKET, pair=USD/BTC ) next_order = order_spec.create_order(order, exchange) assert next_order assert next_order.side == TradeSide.SELL assert next_order.type == TradeType.MARKET assert next_order.pair == USD/BTC assert next_order.path_id == order.path_id assert next_order.quantity.path_id == order.path_id assert next_order.quantity.instrument == BTC
def test_create_from_sell_order(mock_order_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.id = "fake_id" wallets = [Wallet(exchange, 10000.00*USD), Wallet(exchange, 2*BTC)] portfolio = Portfolio(USD, wallets) order = mock_order_class.return_value order.portfolio = portfolio order.path_id = "fake_path_id" order.price = 7000.00 wallet_usd = portfolio.get_wallet(exchange.id, USD) wallet_usd -= 1000*USD wallet_usd += Quantity(USD, 1000, path_id=order.path_id) assert wallet_usd.balance == 9000 * USD assert wallet_usd.locked[order.path_id].size == 1000 * USD order_spec = OrderSpec( side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD/BTC ) next_order = order_spec.create_order(order, exchange) assert next_order assert next_order.side == TradeSide.BUY assert next_order.type == TradeType.MARKET assert next_order.pair == USD/BTC assert next_order.path_id == order.path_id assert next_order.quantity.path_id == order.path_id assert next_order.quantity.instrument == USD
def test_cancel(mock_order_listener_class, mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) listener = mock_order_listener_class.return_value listener.on_cancel = mock.Mock(return_value=None) order.attach(listener) order.execute() # Execute fake trade price = Decimal(7010.00) scale = order.price / price commission = 3.00 * USD trade = mock_trade_class.return_value trade.size = Decimal(scale * order.size - commission.size) trade.quantity = trade.size * USD trade.price = price trade.commission = commission base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) base_size = trade.size + commission.size quote_size = (order.price / trade.price) * (trade.size / trade.price) base_wallet.withdraw(quantity=Quantity(USD, size=base_size, path_id=order.path_id), reason="test") quote_wallet.deposit(quantity=Quantity(BTC, size=quote_size, path_id=order.path_id), reason="test") order.fill(trade) assert order.status == OrderStatus.PARTIALLY_FILLED assert base_wallet.balance == 4800.00 * USD assert float(round(base_wallet.locked[order.path_id].size, 2)) == 7.42 assert quote_wallet.balance == 0 * BTC assert float(round(quote_wallet.locked[order.path_id].size, 8)) == 0.73925519 order.cancel() listener.on_cancel.assert_called_once_with(order) assert float(round(base_wallet.balance.size, 2)) == 4807.42 assert order.path_id not in base_wallet.locked assert float(round(quote_wallet.balance.size, 8)) == 0.73925519 assert order.path_id not in quote_wallet.locked
def test_complete_complex_order(mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) side = TradeSide.BUY order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) risk_criteria = Stop("down", 0.03) ^ Stop("up", 0.02) risk_management = OrderSpec( side=TradeSide.SELL if side == TradeSide.BUY else TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC), criteria=risk_criteria) order += risk_management order.execute() # Execute fake trade price = Decimal(7010.00) scale = order.price / price commission = 3.00 * USD base_size = scale * order.size - commission.size trade = mock_trade_class.return_value trade.size = Decimal(base_size) trade.quantity = base_size * USD trade.price = price trade.commission = commission base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) base_size = trade.size + trade.commission.size quote_size = (order.price / trade.price) * (trade.size / trade.price) base_wallet.withdraw(quantity=Quantity(USD, size=base_size, path_id=order.path_id), reason="test") quote_wallet.deposit(quantity=Quantity(BTC, size=quote_size, path_id=order.path_id), reason="test") # Fill fake trade order.fill(trade) assert order.path_id in portfolio.get_wallet(exchange.id, USD).locked assert order.status == OrderStatus.PARTIALLY_FILLED next_order = order.complete() assert order.status == OrderStatus.FILLED assert next_order assert next_order.path_id == order.path_id assert next_order.size assert next_order.status == OrderStatus.PENDING assert next_order.side == TradeSide.SELL assert next_order.exchange_pair == ExchangePair(exchange, USD / BTC)
def test_on_fill_with_complex_order(mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options.max_trade_size = 1e6 exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.quote_price = lambda pair: Decimal(7000.00) broker = Broker() broker.exchanges = [exchange] wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) side = TradeSide.BUY order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) risk_criteria = Stop("down", 0.03) ^ Stop("up", 0.02) risk_management = OrderSpec( side=TradeSide.SELL if side == TradeSide.BUY else TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC), criteria=risk_criteria) order += risk_management order.attach(broker) order.execute() broker._executed[order.id] = order # Execute fake trade price = Decimal(7000.00) scale = order.price / price commission = 3.00 * USD base_size = scale * order.size - commission.size trade = mock_trade_class.return_value trade.order_id = order.id trade.size = base_size trade.quantity = base_size * USD trade.price = price trade.commission = commission base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) base_size = trade.size + trade.commission.size quote_size = (order.price / trade.price) * (trade.size / trade.price) base_wallet.withdraw(quantity=Quantity(USD, size=base_size, path_id=order.path_id), reason="test") quote_wallet.deposit(quantity=Quantity(BTC, size=quote_size, path_id=order.path_id), reason="test") assert trade.order_id in broker.executed.keys() assert trade not in broker.trades assert broker.unexecuted == [] order.fill(trade) assert order.remaining == 0 assert trade in broker.trades[order.id] assert broker.unexecuted != []
def test_complete_complex_order(mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.id = "fake_exchange_id" wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) side = TradeSide.BUY order = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) risk_criteria = Stop("down", 0.03) ^ Stop("up", 0.02) risk_management = OrderSpec(side=TradeSide.SELL if side == TradeSide.BUY else TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, criteria=risk_criteria) order += risk_management order.execute(exchange) # Execute fake trade price = 7010.00 scale = order.price / price commission = 3.00 * USD base_size = scale * order.size - commission.size trade = mock_trade_class.return_value trade.size = base_size trade.price = price trade.commission = commission base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) base_size = trade.size + trade.commission.size quote_size = (order.price / trade.price) * (trade.size / trade.price) base_wallet -= Quantity(USD, size=base_size, path_id=order.path_id) quote_wallet += Quantity(BTC, size=quote_size, path_id=order.path_id) # Fill fake trade order.fill(exchange, trade) assert order.path_id in portfolio.get_wallet(exchange.id, USD).locked assert order.status == OrderStatus.PARTIALLY_FILLED next_order = order.complete(exchange) assert order.status == OrderStatus.FILLED assert next_order assert next_order.path_id == order.path_id assert next_order.size assert next_order.status == OrderStatus.PENDING assert next_order.side == TradeSide.SELL assert next_order.pair == USD/BTC
def test_on_fill_with_complex_order(mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.id = "fake_exchange_id" broker = Broker(exchange) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) side = TradeSide.BUY order = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) risk_criteria = Stop("down", 0.03) ^ Stop("up", 0.02) risk_management = OrderSpec( side=TradeSide.SELL if side == TradeSide.BUY else TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, criteria=risk_criteria) order += risk_management order.attach(broker) order.execute(exchange) broker._executed[order.id] = order # Execute fake trade price = 7000.00 scale = order.price / price commission = 3.00 * USD base_size = scale * order.size - commission.size trade = mock_trade_class.return_value trade.order_id = order.id trade.size = base_size trade.price = price trade.commission = commission base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) base_size = trade.size + trade.commission.size quote_size = (order.price / trade.price) * (trade.size / trade.price) base_wallet -= Quantity(USD, size=base_size, path_id=order.path_id) quote_wallet += Quantity(BTC, size=quote_size, path_id=order.path_id) assert trade.order_id in broker.executed.keys() assert trade not in broker.trades assert broker.unexecuted == [] order.fill(exchange, trade) assert order.remaining_size == 0 assert trade in broker.trades[order.id] assert broker.unexecuted != []