def create_order(self, order: 'Order') -> 'Order': wallet_instrument = self.side.instrument(self.exchange_pair.pair) exchange = order.exchange_pair.exchange wallet = order.portfolio.get_wallet(exchange.id, instrument=wallet_instrument) quantity = wallet.locked.get(order.path_id, None) if not quantity or quantity.size == 0: return None return Order(step=exchange.clock.step, side=self.side, trade_type=self.type, exchange_pair=self.exchange_pair, quantity=quantity, portfolio=order.portfolio, price=self.exchange_pair.price, criteria=self.criteria, end=order.end, path_id=order.path_id)
def test_init(mock_portfolio_class): portfolio = mock_portfolio_class.return_value order = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5000 * USD, price=7000, portfolio=portfolio) assert order assert order.id assert order.path_id assert order.step == 0 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_properties(mock_portfolio_class): portfolio = mock_portfolio_class.return_value order = Order(side=TradeSide.BUY, trade_type=TradeType.LIMIT, pair=USD / BTC, quantity=5000.00 * USD, portfolio=portfolio, price=7000.00) assert order assert order.base_instrument == USD assert order.quote_instrument == BTC assert order.size == 5000.00 * USD assert order.price == 7000.00 assert order.trades == [] assert order.is_buy assert not order.is_sell assert not order.is_market_order assert order.is_limit_order assert order.created_at == 0
def hidden_limit_order(step: int, side: 'TradeSide', pair: 'TradingPair', price: float, size: float, portfolio: 'Portfolio', ttl_in_seconds: int = None, ttl_in_steps: int = None): instrument = side.instrument(pair) order = Order(step=step, side=side, trade_type=TradeType.MARKET, pair=pair, price=price, quantity=(size * instrument), ttl_in_seconds=ttl_in_seconds, ttl_in_steps=ttl_in_steps, portfolio=portfolio, criteria=Limit(limit_price=price) ) return order
def test_detach(mock_portfolio_class, mock_order_listener_class): portfolio = mock_portfolio_class.return_value order = Order(side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5000.00 * USD, portfolio=portfolio, price=7000.00) listener = mock_order_listener_class.return_value order.attach(listener) assert len(order._listeners) == 1 assert listener in order._listeners order.detach(listener) assert len(order._listeners) == 0 assert listener not in order._listeners
def test_fill(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_fill = mock.Mock(return_value=None) order.attach(listener) order.execute(exchange) trade = mock_trade_class.return_value trade.size = 3997.00 trade.commission = 3.00 * USD assert order.status == OrderStatus.OPEN order.fill(exchange, trade) assert order.status == OrderStatus.PARTIALLY_FILLED assert order.remaining_size == 1200.00 listener.on_fill.assert_called_once_with(order, exchange, trade)
def test_on_fill(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) order = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) order.attach(broker) order.execute(exchange) broker._executed[order.id] = order trade = mock_trade_class.return_value trade.size = 5197.00 trade.commission = 3.00 * USD trade.order_id = order.id assert order.status == OrderStatus.OPEN order.fill(exchange, trade) assert order.status == OrderStatus.FILLED assert order.remaining_size == 0 assert trade in broker.trades[order.id]
def test_update_on_single_exchange_with_multiple_orders(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) broker = Broker(exchange) # Submit order 1 o1 = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) o1.is_executable_on = mock.MagicMock(side_effect=[False, True]) broker.submit(o1) # Submit order 2 o2 = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=230.00 * USD, portfolio=portfolio, price=7300.00) o2.is_executable_on = mock.MagicMock(side_effect=[True, False]) broker.submit(o2) # No updates have been made yet assert o1 in broker.unexecuted and o1 not in broker.executed assert o2 in broker.unexecuted and o2 not in broker.executed # First update broker.update() assert o1 in broker.unexecuted and o1.id not in broker.executed assert o2 not in broker.unexecuted and o2.id in broker.executed # Second update broker.update() assert o1 not in broker.unexecuted and o1.id in broker.executed assert o2 not in broker.unexecuted and o2.id in broker.executed
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 risk_managed_order(step: int, side: 'TradeSide', trade_type: 'TradeType', pair: 'TradingPair', price: float, size: float, down_percent: float, up_percent: float, portfolio: 'Portfolio'): order = Order(step=step, side=side, trade_type=trade_type, pair=pair, price=price, quantity=(size * pair.base), portfolio=portfolio) risk_criteria = Stop("down", down_percent) ^ Stop("up", up_percent) risk_management = OrderSpec( side=TradeSide.SELL if side == TradeSide.BUY else TradeSide.BUY, trade_type=TradeType.MARKET, pair=pair, criteria=risk_criteria) order += risk_management return order
def get_order(self, action: int, exchange: 'Exchange', portfolio: 'Portfolio') -> Order: if action == 0: return None (pair, stop_loss, take_profit, size) = self._actions[action] base_instrument = pair.base if self._trade_side == TradeSide.BUY else pair.quote base_wallet = portfolio.get_wallet(exchange.id, instrument=base_instrument) price = exchange.quote_price(pair) size = min(base_wallet.balance.size, (base_wallet.balance.size * size)) if size < 10**-base_instrument.precision: return None buy_quantity = size * base_instrument order = Order(side=self._trade_side, trade_type=self._trade_type, pair=pair, price=price, quantity=buy_quantity, portfolio=portfolio) risk_criteria = StopLoss(direction='either', up_percent=take_profit, down_percent=stop_loss) risk_management = Recipe(side=TradeSide.SELL if self._trade_side == TradeSide.BUY else TradeSide.BUY, trade_type=TradeType.MARKET, pair=pair, criteria=risk_criteria) order.add_recipe(risk_management) if self._order_listener is not None: order.attach(self._order_listener) return order
def test_on_execute(mock_exchange_class, execute_listener): 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(side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD / BTC, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) order.attach(execute_listener) assert not execute_listener.listened order.execute(exchange) assert execute_listener.listened
def test_on_cancel(mock_exchange_class, cancel_listener): exchange = mock_exchange_class.return_value exchange.id = "fake_exchange_id" exchange.name = "coinbase" 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) order.attach(cancel_listener) assert not cancel_listener.listened order.cancel() assert cancel_listener.listened
def test_execute(mock_order_listener_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 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_execute = mock.Mock(return_value=None) order.attach(listener) assert order.status == OrderStatus.PENDING order.execute() 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)
def test_is_executable_on(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 # Market order 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=5000.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) exchange.quote_price = mock.Mock(return_value=Decimal(6800.00)) assert order.is_executable() exchange.quote_price = mock.Mock(return_value=Decimal(7200.00)) assert order.is_executable() # Limit order 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.LIMIT, quantity=5000.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) exchange.quote_price = mock.Mock(return_value=Decimal(6800.00)) assert order.is_executable() exchange.quote_price = mock.Mock(return_value=Decimal(7200.00)) assert order.is_executable() # Stop Order wallets = [Wallet(exchange, 0 * USD), Wallet(exchange, 2 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.SELL, trade_type=TradeType.LIMIT, quantity=1 * BTC, portfolio=portfolio, price=Decimal(7000.00), criteria=Stop("down", 0.03)) exchange.quote_price = mock.Mock(return_value=Decimal(1 - 0.031) * order.price) assert order.is_executable() exchange.quote_price = mock.Mock(return_value=Decimal(1 - 0.02) * order.price) assert not order.is_executable()
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_is_executable_on(mock_portfolio_class, mock_exchange_class): exchange = mock_exchange_class.return_value portfolio = mock_portfolio_class.return_value # Market order order = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.MARKET, pair=USD/BTC, quantity=5000.00 * USD, portfolio=portfolio, price=7000.00) exchange.quote_price = mock.Mock(return_value=6800.00) assert order.is_executable_on(exchange) exchange.quote_price = mock.Mock(return_value=7200.00) assert order.is_executable_on(exchange) # Limit order order = Order(step=0, side=TradeSide.BUY, trade_type=TradeType.LIMIT, pair=USD/BTC, quantity=5000.00 * USD, portfolio=portfolio, price=7000.00) exchange.quote_price = mock.Mock(return_value=6800.00) assert order.is_executable_on(exchange) exchange.quote_price = mock.Mock(return_value=7200.00) assert order.is_executable_on(exchange) # Stop Order order = Order(step=0, side=TradeSide.SELL, trade_type=TradeType.LIMIT, pair=USD/BTC, quantity=5000.00 * USD, portfolio=portfolio, price=7000.00, criteria=Stop("down", 0.03)) exchange.quote_price = mock.Mock(return_value=(1 - 0.031)*order.price) assert order.is_executable_on(exchange) exchange.quote_price = mock.Mock(return_value=(1 - 0.02) * order.price) assert not order.is_executable_on(exchange)
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 assert_execute_order( current_price, base_balance, quote_balance, order_side, order_quantity, order_price, ): mock_clock = mock.Mock() clock = mock_clock.return_value clock.step = 3 base = base_balance.instrument quote = quote_balance.instrument current_price = Decimal(current_price).quantize( Decimal(10)**-base.precision) order_price = Decimal(order_price).quantize(Decimal(10)**-base.precision) options = ExchangeOptions() mock_exchange = mock.Mock() exchange = mock_exchange.return_value exchange.name = "coinbase" exchange.options = options exchange.quote_price = lambda pair: current_price base_wallet = Wallet(exchange, base_balance) quote_wallet = Wallet(exchange, quote_balance) portfolio = Portfolio(USD, [base_wallet, quote_wallet]) order = Order(step=1, side=order_side, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, base / quote), quantity=order_quantity, portfolio=portfolio, price=order_price, path_id="fake_id") order.status = OrderStatus.OPEN if order_side == TradeSide.BUY: trade = execute_buy_order( order, base_wallet, quote_wallet, current_price=current_price, options=options, clock=clock, ) base_balance = base_wallet.locked['fake_id'].size quote_balance = quote_wallet.locked['fake_id'].size expected_base_balance = order_quantity.size - (trade.size + trade.commission.size) expected_quote_balance = trade.size / current_price expected_base_balance = expected_base_balance.quantize( Decimal(10)**-base.precision) expected_quote_balance = expected_quote_balance.quantize( Decimal(10)**-quote.precision) assert base_balance == expected_base_balance assert quote_balance == expected_quote_balance else: trade = execute_sell_order( order, base_wallet, quote_wallet, current_price=current_price, options=options, clock=clock, ) base_balance = base_wallet.locked['fake_id'].size quote_balance = quote_wallet.locked['fake_id'].size expected_base_balance = trade.size * current_price expected_base_balance = expected_base_balance.quantize( Decimal(10)**-base.precision) assert base_balance == expected_base_balance assert quote_balance == 0
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 != []