예제 #1
0
    def get_orders(self, action: int, portfolio: 'Portfolio') -> 'List[Order]':

        if action == 0:
            return []

        (ep, (criteria, proportion, duration, side)) = self.actions[action]

        instrument = side.instrument(ep.pair)
        wallet = portfolio.get_wallet(ep.exchange.id, instrument=instrument)

        balance = wallet.balance.as_float()
        size = (balance * proportion)
        size = min(balance, size)

        quantity = (size * instrument).quantize()

        if size < 10 ** -instrument.precision \
                or size < self.min_order_pct * portfolio.net_worth \
                or size < self.min_order_abs:
            return []

        order = Order(step=self.clock.step,
                      side=side,
                      trade_type=self._trade_type,
                      exchange_pair=ep,
                      price=ep.price,
                      quantity=quantity,
                      criteria=criteria,
                      end=self.clock.step + duration if duration else None,
                      portfolio=portfolio)

        if self._order_listener is not None:
            order.attach(self._order_listener)

        return [order]
예제 #2
0
def test_iadd(mock_exchange_class, mock_order_spec_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)

    # Market order
    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))

    order_spec = mock_order_spec_class.return_value

    assert len(order._specs) == 0
    order.add_order_spec(order_spec)
    assert len(order._specs) == 1

    assert order_spec in order._specs
예제 #3
0
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
예제 #4
0
def test_attach(mock_exchange_class, mock_order_listener_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=5000.00 * USD,
                  portfolio=portfolio,
                  price=Decimal(7000.00))

    listener = mock_order_listener_class.return_value

    assert len(order.listeners) == 0
    order.attach(listener)
    assert len(order.listeners) == 1
    assert listener in order.listeners
예제 #5
0
def test_is_complete(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)

    # Market order
    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))

    assert not order.is_complete

    order.remaining = 0 * USD
    assert order.is_complete
예제 #6
0
def test_properties(mock_exchange_class):

    exchange = mock_exchange_class.return_value
    exchange.options = ExchangeOptions()
    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_pair=ExchangePair(exchange, USD / BTC),
                  side=TradeSide.BUY,
                  trade_type=TradeType.LIMIT,
                  quantity=5000.00 * USD,
                  portfolio=portfolio,
                  price=7000.00)

    assert order
    assert order.step == 0
    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
예제 #7
0
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))

    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))

    pattern = re.compile("<[A-Z][a-zA-Z]*:\\s(\\w+=.*,\\s)*(\\w+=.*)>")

    string = str(order)
    assert string

    assert string == pattern.fullmatch(string).string
예제 #8
0
    def create_order(self, order: 'Order') -> 'Order':
        """Creates an order following from another order.

        Parameters
        ----------
        order : `Order`
            The previous order in the order path.

        Returns
        -------
        `Order`
            The order created from the specification parameters and the
            parameters of `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)
예제 #9
0
def test_init(mock_exchange_class):

    exchange = mock_exchange_class.return_value
    exchange.options = ExchangeOptions()
    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_pair=ExchangePair(exchange, USD / BTC),
                  side=TradeSide.BUY,
                  trade_type=TradeType.MARKET,
                  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.remaining == order.quantity
    assert isinstance(order.pair, TradingPair)
    assert order.pair.base == USD
    assert order.pair.quote == BTC
예제 #10
0
def market_order(side: "TradeSide", exchange_pair: "ExchangePair",
                 price: float, size: float, portfolio: "Portfolio") -> "Order":
    """Creates a market order.

    Parameters
    ----------
    side : `TradeSide`
        The side of the order.
    exchange_pair : `ExchangePair`
        The exchange pair to perform the order for.
    price : float
        The current price.
    size : float
        The size of the order.
    portfolio : `Portfolio`
        The portfolio being used in the order.

    Returns
    -------
    `Order`
        A market order.
    """

    instrument = side.instrument(exchange_pair.pair)
    order = Order(step=portfolio.clock.step,
                  side=side,
                  trade_type=TradeType.MARKET,
                  exchange_pair=exchange_pair,
                  price=price,
                  quantity=(size * instrument),
                  portfolio=portfolio)

    return order
예제 #11
0
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()
예제 #12
0
def test_to_json(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))

    d = {
        "id": str(order.id),
        "step": int(order.step),
        "exchange_pair": str(order.exchange_pair),
        "status": str(order.status),
        "type": str(order.type),
        "side": str(order.side),
        "base_symbol": str(order.pair.base.symbol),
        "quote_symbol": str(order.pair.quote.symbol),
        "quantity": str(order.quantity),
        "size": float(order.size),
        "remaining": str(order.remaining),
        "price": float(order.price),
        "criteria": str(order.criteria),
        "path_id": str(order.path_id),
        "created_at": str(order.created_at)
    }

    assert order.to_json() == d
예제 #13
0
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
예제 #14
0
def test_complete_basic_order(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

    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_complete = mock.Mock(return_value=None)
    order.attach(listener)

    order.execute()

    trade = mock_trade_class.return_value
    trade.size = Decimal(5197.00)
    trade.quantity = 5197.00 * USD
    trade.commission = 3.00 * USD

    order.fill(trade)

    assert order.status == OrderStatus.PARTIALLY_FILLED
    next_order = order.complete()
    assert order.status == OrderStatus.FILLED

    listener.on_complete.assert_called_once_with(order)
    assert not next_order
예제 #15
0
def hidden_limit_order(side: "TradeSide",
                       exchange_pair: "ExchangePair",
                       limit_price: float,
                       size: float,
                       portfolio: "Portfolio",
                       start: int = None,
                       end: int = None):
    """Creates a hidden limit order.

    Parameters
    ----------
    side : `TradeSide`
        The side of the order.
    exchange_pair : `ExchangePair`
        The exchange pair to perform the order for.
    limit_price : float
        The limit price of the order.
    size : float
        The size of the order.
    portfolio : `Portfolio`
        The portfolio being used in the order.
    start : int, optional
        The start time of the order.
    end : int, optional
        The end time of the order.

    Returns
    -------
    `Order`
        A hidden limit order.
    """
    side = TradeSide[side]
    instrument = side.instrument(exchange_pair.pair)

    order = Order(step=portfolio.clock.step,
                  side=side,
                  trade_type=TradeType.MARKET,
                  exchange_pair=exchange_pair,
                  price=limit_price,
                  quantity=(size * instrument),
                  start=start,
                  end=end,
                  portfolio=portfolio,
                  criteria=Limit(limit_price=limit_price))

    return order
예제 #16
0
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]
예제 #17
0
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)
예제 #18
0
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
예제 #19
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 != []
예제 #20
0
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
예제 #21
0
def risk_managed_order(side: "TradeSide",
                       trade_type: "TradeType",
                       exchange_pair: "ExchangePair",
                       price: float,
                       quantity: "Quantity",
                       down_percent: float,
                       up_percent: float,
                       portfolio: "Portfolio",
                       start: int = None,
                       end: int = None):
    """Create a stop order that manages for percentages above and below the
    entry price of the order.

    Parameters
    ----------
    side : `TradeSide`
        The side of the order.
    trade_type : `TradeType`
        The type of trade to make when going in.
    exchange_pair : `ExchangePair`
        The exchange pair to perform the order for.
    price : float
        The current price.
    down_percent: float
        The percentage the price is allowed to drop before exiting.
    up_percent : float
        The percentage the price is allowed to rise before exiting.
    quantity : `Quantity`
        The quantity of the order.
    portfolio : `Portfolio`
        The portfolio being used in the order.
    start : int, optional
        The start time of the order.
    end : int, optional
        The end time of the order.

    Returns
    -------
    `Order`
        A stop order controlling for the percentages above and below the entry
        price.
    """

    side = TradeSide(side)
    instrument = side.instrument(exchange_pair.pair)

    order = Order(step=portfolio.clock.step,
                  side=side,
                  trade_type=TradeType(trade_type),
                  exchange_pair=exchange_pair,
                  price=price,
                  start=start,
                  end=end,
                  quantity=quantity,
                  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,
        exchange_pair=exchange_pair,
        criteria=risk_criteria)

    order.add_order_spec(risk_management)

    return order
예제 #22
0
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
예제 #23
0
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.add_order_spec(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)
예제 #24
0
    def get_orders(self, action: int, portfolio: 'Portfolio') -> 'List[Order]':

        # HOLD Action
        if action == 0:
            return []

        #(ep, (criteria, proportion, duration, side)) = self.actions[action]
        #ep = self.actions[action][0]
        #(criteria, proportion, duration, side) = self.actions[action][1]
        ep, side = self.actions[action]

        # Empty Orders List
        orders = []

        # check different action
        if (action != self.action):
            # proportion of used balance
            proportion = 1
            duration = None
            criteria = None
            # get base/quote wallets
            quote_wallet = portfolio.get_wallet(ep.exchange.id,
                                                instrument=ep.pair.quote)
            base_wallet = portfolio.get_wallet(ep.exchange.id,
                                               instrument=ep.pair.base)

            # get current price
            price = ep.price

            # create orders to close all already open-position
            positions = quote_wallet.get_positions().values(
            )  # avoid RuntimeError: dictionary changed size during iteration, when remove position from dict.
            for position in positions:
                if position.is_open:
                    #close positions based on last opened orders
                    #quantity = Quantity(instrument=ep.pair.quote, size=position.quantity.size)
                    # adjut worth quantity - if sell-position already open
                    quantity = Quantity(instrument=ep.pair.quote,
                                        size=position.get_worth_quantity())
                    position.is_locked = True
                    order = Order(path_id=position.id,
                                  step=self.clock.step,
                                  side=TradeSide.CLOSE,
                                  trade_type=self._trade_type,
                                  exchange_pair=ep,
                                  price=ep.price,
                                  quantity=quantity,
                                  criteria=criteria,
                                  end=self.clock.step +
                                  duration if duration else None,
                                  portfolio=portfolio)

                    # Transfer Funds from Quote Wallet to Base Wallet
                    transfer = Wallet.transfer(
                        source=quote_wallet,
                        target=base_wallet,
                        quantity=order.quantity,
                        commission=Quantity(instrument=ep.pair.quote, size=0),
                        exchange_pair=order.exchange_pair,
                        reason="CLOSE")
                    self.broker.submit(order)
                    self.broker.update()

            if side != TradeSide.CLOSE:
                # return money to base-wallets
                instrument = side.instrument(ep.pair)

                balance = base_wallet.balance.as_float()
                size = (balance * proportion)
                size = min(balance, size)

                quantity = (size * instrument).quantize()

                value = size * float(price)
                if size < 10 ** -instrument.precision \
                        or value < self.min_order_pct*portfolio.net_worth:
                    return []

                order = Order(step=self.clock.step,
                              side=side,
                              trade_type=self._trade_type,
                              exchange_pair=ep,
                              price=ep.price,
                              quantity=quantity,
                              criteria=criteria,
                              end=self.clock.step +
                              duration if duration else None,
                              portfolio=portfolio)
                orders.append(order)

                if self._order_listener is not None:
                    order.attach(self._order_listener)

            # TODO: Check a way to check: check only when order is completed
            self.action = action

        return orders
예제 #25
0
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