def test_to_dict(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)) order_spec = OrderSpec(side=TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC)) d = order_spec.to_dict() assert d == { "id": order_spec.id, "type": order_spec.type, "exchange_pair": order_spec.exchange_pair, "criteria": order_spec.criteria } order_spec = OrderSpec(side=TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC), criteria=lambda order, exchange: True) d = order_spec.to_dict() assert d == { "id": order_spec.id, "type": order_spec.type, "exchange_pair": order_spec.exchange_pair, "criteria": order_spec.criteria }
def test_init(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 side = TradeSide.BUY trade_type = TradeType.MARKET # Create order specification without criteria order_spec = OrderSpec(side=side, trade_type=trade_type, exchange_pair=ExchangePair(exchange, USD / BTC)) assert order_spec.id assert order_spec.side == side assert order_spec.type == trade_type assert order_spec.exchange_pair == ExchangePair(exchange, USD / BTC) assert not order_spec.criteria # Create order specification with criteria order_spec = OrderSpec(side=side, trade_type=trade_type, exchange_pair=ExchangePair(exchange, USD / BTC), criteria=lambda order, exchange: True) assert order_spec.id assert order_spec.side == side assert order_spec.type == trade_type assert order_spec.exchange_pair == ExchangePair(exchange, USD / BTC) assert order_spec.criteria
def test_on_cancel(mock_exchange_class, cancel_listener): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "bitfinex" 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.attach(cancel_listener) assert not cancel_listener.listened order.cancel() assert cancel_listener.listened
def test_on_complete(mock_trade_class, mock_exchange_class, complete_listener): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "bitfinex" 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.attach(complete_listener) order.execute() trade = mock_trade_class.return_value trade.size = Decimal(5197.00) trade.quantity = trade.size * USD trade.commission = 3.00 * USD order.fill(trade) assert not complete_listener.listened order.complete() assert complete_listener.listened
def test_transfer(): exchange = mock.Mock() price = Decimal(9750.19).quantize(Decimal(10)**-2) exchange.quote_price = lambda pair: price exchange.name = "bitfinex" order = mock.Mock() order.path_id = "fake_id" exchange_pair = ExchangePair(exchange, USD / BTC) source = Wallet(exchange, 18903.89 * USD) source.lock(917.07 * USD, order, "test") target = Wallet(exchange, 3.79283997 * BTC) quantity = (256.19 * USD).lock_for("fake_id") commission = (2.99 * USD).lock_for("fake_id") Wallet.transfer(source, target, quantity, commission, exchange_pair, "transfer") source = Wallet(exchange, 3.79283997 * BTC) source.lock(3.00000029 * BTC, order, "test") target = Wallet(exchange, 18903.89 * USD) quantity = (2.19935873 * BTC).lock_for("fake_id") commission = (0.00659732 * BTC).lock_for("fake_id") Wallet.transfer(source, target, quantity, commission, exchange_pair, "transfer")
def test_str(mock_exchange): exchange = mock_exchange.return_value exchange.name = "coinbase" exchange_pair = ExchangePair(exchange, USD/BTC) assert str(exchange_pair) == "coinbase:USD/BTC"
def test_str(mock_exchange): exchange = mock_exchange.return_value exchange.name = "bitfinex" exchange_pair = ExchangePair(exchange, USD/BTC) assert str(exchange_pair) == "bitfinex:USD/BTC"
def test_valid_init(mock_exchange): exchange = mock_exchange.return_value exchange.name = "coinbase" exchange_pair = ExchangePair(exchange, USD/BTC) assert exchange_pair assert exchange_pair.pair.base == USD assert exchange_pair.pair.quote == BTC
def test_create_from_buy_order(mock_order_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "bitfinex" 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 = Decimal(7000.00) wallet_btc = portfolio.get_wallet(exchange.id, BTC) wallet_btc.lock( quantity=0.4 * BTC, order=order, reason="test" ) assert float(wallet_btc.balance.size) == 1.6 assert float(wallet_btc.locked[order.path_id].size) == 0.4 order_spec = OrderSpec( side=TradeSide.SELL, 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.SELL 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 == BTC
def exchange_pairs(self) -> 'List[ExchangePair]': """All the exchange pairs in the portfolio. (`List[ExchangePair]`, read-only)""" exchange_pairs = [] for w in self.wallets: if w.instrument != self.base_instrument: exchange_pairs += [ ExchangePair(w.exchange, self.base_instrument / w.instrument) ] return exchange_pairs
def test_str(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)) order_spec = OrderSpec(side=TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC)) pattern = re.compile("<[A-Z][a-zA-Z]*:\\s(\\w+=.*,\\s)*(\\w+=.*)>") string = str(order_spec) assert string assert string == pattern.fullmatch(string).string
def test_on_fill(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 = "bitfinex" 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) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=7000.00) order.attach(broker) order.execute() broker.executed[order.id] = order trade = mock_trade_class.return_value trade.quantity = 5197.00 * USD trade.commission = 3.00 * USD trade.order_id = order.id assert order.status == OrderStatus.OPEN order.fill(trade) assert order.status == OrderStatus.FILLED assert order.remaining == 0 assert trade in broker.trades[order.id]
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.options.max_trade_size = 1e6 exchange.id = "fake_exchange_id" exchange.name = "bitfinex" 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.add_order_spec(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 proportion_order(portfolio: 'Portfolio', source: 'Wallet', target: 'Wallet', proportion: float) -> 'Order': """Creates an order that sends a proportion of funds from one wallet to another. Parameters ---------- portfolio : `Portfolio` The portfolio that contains both wallets. source : `Wallet` The source wallet for the funds. target : `Wallet` The target wallet for the funds. proportion : float The proportion of funds to send. """ assert 0.0 < proportion <= 1.0 exchange = source.exchange base_params = { 'step': portfolio.clock.step, 'portfolio': portfolio, 'trade_type': TradeType.MARKET, 'start': portfolio.clock.step, 'end': portfolio.clock.step + 1 } pair = None is_source_base = (source.instrument == portfolio.base_instrument) is_target_base = (target.instrument == portfolio.base_instrument) if is_source_base or is_target_base: if is_source_base: pair = source.instrument / target.instrument else: pair = target.instrument / source.instrument exchange_pair = ExchangePair(exchange, pair) balance = source.balance.as_float() size = min(balance * proportion, balance) quantity = (size * source.instrument).quantize() params = { **base_params, 'side': TradeSide.BUY if is_source_base else TradeSide.SELL, 'exchange_pair': exchange_pair, 'price': exchange_pair.price, 'quantity': quantity } return Order(**params) pair = portfolio.base_instrument / source.instrument exchange_pair = ExchangePair(exchange, pair) balance = source.balance.as_float() size = min(balance * proportion, balance) quantity = (size * source.instrument).quantize() params = { **base_params, 'side': TradeSide.SELL, 'exchange_pair': exchange_pair, 'price': exchange_pair.price, 'quantity': quantity } order = Order(**params) pair = portfolio.base_instrument / target.instrument order.add_order_spec( OrderSpec(side=TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, pair), criteria=None)) return order