@Eric - as you point out my algo is not currently robust to trading illiquid stocks - it simply blindly calculates the order sizes need to reach the target allocation and places all those orders at once. Perhaps too simplistic for some use cases.
If you want to do something different then you can drop back to the basic order() type. Also keep in mind that our backtester is open-soure software (https://github.com/quantopian/zipline)
The order_target_percent method was contributed by a member of our open source community, Jeremian Lowin (see code below). If you come up with a way to extend this and make it more robust please share it back so we can pull in any improvements. I've also thought that a nice addition would be to add a parameter to define a threshold for rebalance, so for example if you don't want to place trades for less than 100 shares, or 10 shares or whatever you could specify that. Haven't gotten to it yet though.
def order_target_percent(self, sid, target, limit_price=None,
stop_price=None):
"""
Place an order to adjust a position to a target percent of the
current portfolio value. If the position doesn't already exist, this is
equivalent to placing a new order. If the position does exist, this is
equivalent to placing an order for the difference between the target
percent and the current percent.
Note that target must expressed as a decimal (0.50 means 50\%).
"""
if sid in self.portfolio.positions:
current_position = self.portfolio.positions[sid].amount
current_price = self.portfolio.positions[sid].last_sale_price
current_value = current_position * current_price
else:
current_value = 0
target_value = self.portfolio.portfolio_value * target
req_value = target_value - current_value
return self.order_value(sid, req_value, limit_price, stop_price)
which then uses:
def order_value(self, sid, value, limit_price=None, stop_price=None):
"""
Place an order by desired value rather than desired number of shares.
If the requested sid is found in the universe, the requested value is
divided by its price to imply the number of shares to transact.
value > 0 :: Buy/Cover
value < 0 :: Sell/Short
Market order: order(sid, value)
Limit order: order(sid, value, limit_price)
Stop order: order(sid, value, None, stop_price)
StopLimit order: order(sid, value, limit_price, stop_price)
"""
last_price = self.trading_client.current_data[sid].price
if np.allclose(last_price, 0):
zero_message = "Price of 0 for {psid}; can't infer value".format(
psid=sid
)
self.logger.debug(zero_message)
# Don't place any order
return
else:
amount = value / last_price
return self.order(sid, amount, limit_price, stop_price)