Пример #1
0
 def maybe_build_expiring_orders(self, portfolio, daily_data):
     """
     Make sell orders for the oldest pairs held. Also removes these pairs
     from the `assets_in_pairs` set.
     """
     if len(self.pairs) < period:
         return []
     orders = []
     expiring_pairs = self.pairs[0]
     for pair in expiring_pairs:
         # Sell half of the first asset.
         first_ticker = pair.first_ticker
         first_held = portfolio.stocks[first_ticker]
         # TODO: Might want to use (first_held // 2) + 1?
         first_units_to_sell = first_held // 2
         orders += [
             supervisor_pb2.Order(algorithm_id=self.algorithm_id,
                                  ticker=first_ticker,
                                  volume=first_units_to_sell,
                                  stop=0.0,
                                  position=pair.position_spec)
         ]
         # Invest this in the second asset.
         target = \
             first_units_to_sell * daily_data.prices[first_ticker].close
         second_ticker = pair.second_ticker
         second_units_to_buy = math.floor(
             portfolio_helpers.divide_or_zero(
                 target, daily_data.prices[second_ticker].close))
         while second_units_to_buy > 0:
             next_chunk = max(math.floor(0.9 * second_units_to_buy), 1.0)
             orders += [
                 supervisor_pb2.Order(algorithm_id=self.algorithm_id,
                                      ticker=second_ticker,
                                      volume=-next_chunk,
                                      stop=0.0)
             ]
             second_units_to_buy -= next_chunk
         # Remove these assets from the `assets_in_pairs` set.
         self.assets_in_pairs.remove(first_ticker)
         self.assets_in_pairs.remove(second_ticker)
         # Close the position
         self.registrar.supervisor_stub.ClosePosition(pair.position_spec)
     return orders
Пример #2
0
def sell_stocks_with_stop(algorithm_id, tickers, portfolio, stop):
    """
    Creates orders to sell all tickers at the stop price determined by the
    given `stop` lambda. TODO: Move to a helper package.
    """
    orders = []
    for ticker in tickers:
        if ticker not in portfolio.stocks:
            continue
        units = portfolio.stocks[ticker]
        orders += [
            supervisor_pb2.Order(algorithm_id=algorithm_id,
                                 ticker=ticker,
                                 volume=-units,
                                 stop=stop(ticker))
        ]
    return orders
Пример #3
0
 def build_pair_orders(self, pairs, portfolio, daily_data):
     """
     Builds orders to buy/sell as appropriate in the given pair. Also updates
     the `assets_in_pairs` set.
     """
     orders = []
     for pair in pairs:
         # Open the position
         pair.position_spec = self.registrar.supervisor_stub.OpenPosition(
             supervisor_pb2.OpenPositionInput(
                 algorithm_id=self.algorithm_id,
                 ticker=[pair.first_ticker, pair.second_ticker]))
         # Sell all of the second asset.
         second_ticker = pair.second_ticker
         second_units_to_sell = portfolio.stocks[second_ticker]
         orders += portfolio_helpers.sell_stocks_market_order(
             self.algorithm_id, [second_ticker], portfolio)
         # Invest this in the first asset.
         second_price = daily_data.prices[second_ticker].close
         target = second_units_to_sell * second_price
         first_price = daily_data.prices[pair.first_ticker].close
         first_units_to_buy = math.floor(target / first_price)
         # Break orders up in case there are a large number of target stocks.
         while first_units_to_buy > 0:
             next_chunk = max(math.floor(0.9 * first_units_to_buy), 1.0)
             orders += [
                 supervisor_pb2.Order(algorithm_id=self.algorithm_id,
                                      ticker=pair.first_ticker,
                                      volume=next_chunk,
                                      limit=1.02 * first_price,
                                      position=pair.position_spec)
             ]
             first_units_to_buy -= next_chunk
         # Record in the `assets_in_pairs` set.
         self.assets_in_pairs.add(pair.first_ticker)
         self.assets_in_pairs.add(second_ticker)
     self.pairs.append(pairs)
     return orders
Пример #4
0
def orders_for_target_units(algorithm_id, ticker, target_units, limit):
    """
    Makes orders for the target number of units batched as 0.9*target,
    0.9*(target - 0.9*target), and so on, and includes any number of single
    unit orders necessary to reach target.
    """
    orders = []
    placed = 0
    next_batch = max(1, int(target_units * 0.9))
    while True:
        if placed + next_batch > target_units:
            if next_batch == 1:
                break
            next_batch = max(1, int((target_units - placed) * 0.9))
            continue
        orders += [
            supervisor_pb2.Order(algorithm_id=algorithm_id,
                                 ticker=ticker,
                                 volume=next_batch,
                                 limit=limit)
        ]
        placed += next_batch
    return orders
Пример #5
0
 def test_happy_path(self):
     orders = [
         supervisor_pb2.Order(volume=5.0, limit=5.0),
         supervisor_pb2.Order(volume=6.0, limit=8.0),
         supervisor_pb2.Order(volume=7.0, limit=9.0),
         supervisor_pb2.Order(volume=8.0, limit=2.0),
         supervisor_pb2.Order(volume=-8.0, limit=2.0),
         supervisor_pb2.Order(volume=-9.0, limit=3.0),
     ]
     sorted_orders = portfolio_helpers.orders_sorted_descending(orders)
     for i in range(1, len(sorted_orders)):
         prev_order = sorted_orders[i - 1]
         order = sorted_orders[i]
         # Sells before buys.
         self.assertFalse(prev_order.volume > 0.0 and order.volume <= 0.0)
         if prev_order.volume <= 0.0:
             continue
         # Buys in descending order.
         self.assertLess(order.limit * order.volume,
                         prev_order.limit * prev_order.volume)
Пример #6
0
def invest_approximately_uniformly_in_targets(algorithm_id,
                                              portfolio,
                                              daily_data,
                                              targets,
                                              investment_limit=None,
                                              UP=1.01,
                                              DOWN=0.99):
    """
    Attempt to approximately invest uniformly across all assets.
    NOTE: Unlike the Go version, this function will work on a portfolio that is
          not all cash.
    """
    if len(targets) == 0:
        return []
    total_limit_of_orders = 0.0
    if investment_limit == None:
        investment_limit = portfolio.usd
    num_assets = len(set([ticker for ticker in daily_data.prices]))
    target = DOWN * portfolio_value(portfolio, daily_data.prices) / num_assets
    target_investments = dict()
    # Note: this will represent a total limit of
    #
    #   1.01 * \sum_stock floor(target / price) * price <=
    #   1.01 * \sum_stock target =
    #   1.01 * 0.99 * \sum_stocks portfolioValue / # stocks =
    #   1.01 * 0.99 * portfolioValue =
    #   0.9999 * portfolioValue
    #
    # Thus, the investment is safe as long as UP * DOWN <= 1.
    orders = []
    for ticker in daily_data.prices:
        target_investments[ticker] = 0.0
        prices = daily_data.prices[ticker]
        if ticker not in targets or prices.close < 1e-4:
            continue
        volume = math.floor(target / prices.close) - portfolio.stocks[ticker]
        if volume <= 0.0:
            continue
        limit = UP * prices.close
        if total_limit_of_orders + volume * limit >= investment_limit:
            continue
        orders.append(
            supervisor_pb2.Order(algorithm_id=algorithm_id,
                                 ticker=ticker,
                                 volume=volume,
                                 limit=limit))
        target_investments[ticker] = volume * limit
        total_limit_of_orders += volume * limit
    # Continue investing until we can't get closer to a uniform investment.
    while portfolio.usd - total_limit_of_orders > 0.0:
        next_ticker = None
        next_improvement = 0.0
        next_price = 0.0
        for ticker in daily_data.prices:
            prices = daily_data.prices[ticker]
            if ticker not in targets:
                continue
            current_target = target_investments[ticker]
            close_price = prices.close
            if close_price + total_limit_of_orders >= investment_limit:
                continue
            hypothetical_delta = abs(close_price + current_target - target)
            current_delta = abs(current_target - target)
            improvement = current_delta - hypothetical_delta
            if improvement > next_improvement:
                next_improvement = improvement
                next_ticker = ticker
                next_price = close_price
        if next_improvement == 0.0:
            # No improvement can be made.
            break
        # Place an order for a next_ticker.
        limit = UP * next_price
        orders.append(
            supervisor_pb2.Order(algorithm_id=algorithm_id,
                                 ticker=next_ticker,
                                 volume=1.0,
                                 limit=limit))
        target_investments[next_ticker] += limit
        total_limit_of_orders += limit
    return orders