Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
'Long Biggest Loser' Algorithm and Problem with 'get_open_orders(...)'

Hi All,

I wanted to share a fun little algorithm that I wrote up as a Pipeline exercise, and point out an interesting problem/bug that I found. This is my first algo on Quantopian, and would really welcome any feedback on the structure and syntax of the code (maybe to make it more "pythonic")!

I've made the structure of the code somewhat generic, and contained options to switch to longing the biggest winner by selecting a different Pipeline stream in the 'rebalance(...)' or to increase the basket of longs by adjusting the number of stocks taken from Pipeline. It can also be switched to shorting by replacing the signs in the 'order_target_percent(...)'.

The idea is simple, where the algo would long the biggest 'loser', i.e. the stock with the lowest daily return, from the previous day and hold it for 5 trading days, creating a revolving 5-stock flat-weighted portfolio that swap out one stock every day of the week (E.g. Buy Monday's biggest loser on Tuesday and sell it on the following Tuesday). In theory, this is similar to a contrarian strategy that essentially expects a "rebound" after a big loss for a particular stock. In practice, the strategy's performance is pretty lackluster, as seen in the attached backtest. The fact that there are such limited number of stocks in the portfolio means that the strategy's beta is surprisingly high.

Also, I wanted to point out a problem that I was having with 'get_open_orders()', where sometimes the result is not a collection of tuple(stock, order), but rather a list(stock). This seems to only result when 'order_target_percent(...)' cannot create a order (NOT cannot fill an order) even though the 'can_trade(security)' check is passed, thus creating an open order with no actual order_id associated to it. My guess is that this is due to the target weight being impossible due to portfolio and unit size (as it does occur with Berkshire shares), but it does occur with other shares as well.

I was using a snippet taken from John Fawcett (https://www.quantopian.com/posts/how-can-i-convert-unfilled-limit-orders-to-market-orders) to cancel and re-place my orders at the end of day, and the above erro causes the IDE to throw a 'not iterable' error where it is actually because the output does not match the tuple form (stock, order).

I hope this is useful to somebody and, again, would really like some feedback on how to improve!

3 responses

hi michael,
im probably less experienced in python and stocks than you. i hope i help not hurt, but it seems like people give the stock a chance in 5 days, i disagree. you ride good trades and you get out of bad trades. you trade trends, not time. maybe some sort of indicator that you like would help you get in and out of the good and bad trades at the right time. it might help. maybe an ma cross of the losers would mean that the rebound has begun. you dont want to long a stock that still is tanking. i think its better to open a position after confirmation of a reversal and miss out on a little profit than jump in to early and there is no bus.
with orders issue, it seems like an order disapears once its a position. i didnt get where your 'sells' order ids were coming from. im not that good at this. i just cancel all my open orders like so:

when i place an order:
if data.can_trade(stock):
order_id = order_percent(stock, -.5, style=LimitOrder(price - .05))
order_id = get_order(order_id)
context.puts.append(order_id)

in my case, every 2 minutes i cancel:
def the_cancelor(context, data):
for o in context.puts:
cancel_order(o)
for o in context.calls:
cancel_order(o)

i hope my comments help you in your success some how. good luck!
tyler

Hi Tyler,

I think you are right on the rebound indicators, and a little investigation into the strategy trades agree with you. The 5 day holding period is just a simple placeholder that would correspond to a strategy that rotates the holdings of the portfolio by week so I won't have to think about weights. I think in the next iteration of the strategy I would expand the Pipeline output to maybe the bottom/top decile in returns and then look for recent MA crosses.

On the order issue, I think order IDs are persistent within a backtest. Even when an order is filled, the Order object will still be avaiable, and the Order.status would simply return as 1 (for filled, according to the API https://www.quantopian.com/help#api-orderobj). You can also check the whether Order.filled is equal to Order.amount to see the number of shares or percentage of shares filled for your order. This would make sense particularly in the context of live trading, so that the record of individual trades can be tracked.

I think with my problem, it is the way that open orders are retrieved and how order_target_percent(...) works in the stack. Once I attempt an order_target_percent(...) that is impossible to fill, the Order object is not created (presumably because of fractional unit sizes?) but it is still added to the list of open orders that is retrieved when I call get_open_orders(...), so it returns a list with member Stock rather than a tuple of Stock and order.

On the way you have coded the_cancelor(...) should be OK, as the comments made here (https://www.quantopian.com/posts/closing-orders) would indicate that it should not affect portions of shares already filled as well as orders that are already fully completed. Just as a side note, if that is all you are using the context.puts/context.calls for, you are actually appending Order objects and the get_order(...) call in your code is not actually necessary as cancel_order(...) actually takes order IDs. Furthermore, if you don't reinitialize your puts/calls list you are probably leaving a lot of already canceled orders in your lists and are doing redundant cancel_order(...) calls.

Best!

Awesome, thanks Michael!