Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Leverage and long/short exposure: one class to rule them all

Unless your algorithm has a single point of portfolio rebalancing (daily, weekly, monthly, whatever rebalancing) it is difficult to keep track of leverage, long and short exposure and long/short balancing.

Think of an algorithm that constantly scans a big universe of stocks looking for some events, at any minute of the day the algorithm might decide to enter some positions (long or short) or exit few others (again long or short). Once the algorithm finds an event, it needs to know if there is some cash available (given a target leverage) and it actually needs to know how much cash is available to enter long positions and how much for short positions (given a target percentage of long vs short exposure).

To properly handle the above scenario I wrote a class that, given a target leverage and a target short/long percentage exposure, it calculates at any time the following:
- the cash available for entering new positions, if any
- how much of the above cash should be used for long and how much for short positions

To use it:

def initialize(context):  

    # Define your targets at initialization: I want leverage 1.3  and 60%/40% Long/Short balance  
    context.exposure = ExposureMngr(target_leverage = 1.3,  
                                    target_long_exposure_perc = 0.60,  
                                    target_short_exposure_perc = 0.40)  
def handle_data(context, data):  

   #  
   # update internal state (open orders and positions)  
   # Don't need to call this every minute, only when you call the methods below  
   #  
   context.exposure.update(context, data)   

   #  
   # After update is called, you can access the following information  
   #

   # how much cash available for trading  
   context.exposure.get_available_cash(consider_open_orders = True)  
   # get long and short available cash as two distinct values  
   context.exposure.get_available_cash_long_short(consider_open_orders = True)  

   # same as account.leverage but this keeps track of open orders  
   context.exposure.get_current_leverage(consider_open_orders = True)                    

   # sum of long and short positions current value  
   context.exposure.get_exposure(consider_open_orders = True)  
   # get long and short position values as two distinct values  
   context.exposure.get_long_short_exposure(consider_open_orders = True)  
   # get long and short exposure as percentage  
   context.exposure.get_long_short_exposure_pct(consider_open_orders = True,  consider_unused_cash = True)  

I really appreciate bug fixes and improvements. Also if you have a better idea on how to keep track of leverage and long/short exposure I'd like to know.

Attached an example algorithm: intraday mean reversion (trades every 20 minutes the best/worst performing securities in the last 90 minutes). It's just an example on how to use the class, it is not intended to be profitable

13 responses

Luca - this is fantastic! Thanks for sharing. If you ever want to integrate this into Zipline, we always welcome pull requests :)

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Greatly needed. Well done.

and if we use context.account.settled_cash instead of cash you can use this for cash accounts with:

context.exposure = ExposureMngr(target_leverage = 0.99,  
                                    target_long_exposure_perc = 0.99,  
                                    target_short_exposure_perc = 0.00)  

Super thankful to have spotted this. I have been dreading needing to figure something like this out. You're a god among men. You should definitely get this into the official Zipline module, I've desperately been wanting a solution for this for a while.

@Peter. I thought about that, but never tried as I don't need it. Also you should set target_long_exposure_perc = 1.00 (not 0.99).
target_long_exposure_perc + target_short_exposure_perc should equal 1.0 otherwise you are using the exposure to modelling the leverage. You can do it but it's not clear and target_leverage is there for a reason. Let target_long_exposure_perc and target_short_exposure_perc model the long/short percentage and target_leverage models the leverage. Keep it simple.

@Harrison. I don't feel like integrating code in zipline but if someone likes the idea they can feel free to use my code.

A nice python shorthand for:

        for stock in context.portfolio.positions:  
            amount = context.portfolio.positions[stock].amount  
            last_sale_price = context.portfolio.positions[stock].last_sale_price  

Can be rewritten:

for stock, position in context.portfolio.positions.iteritems():  
            amount = position.amount  
            last_sale_price = position.last_sale_price  

@Dan, definitely nicer and more pythonic.

Hi Luca,
I was working on your algorithm to understand whole process and I was trying to run each code part by creating a hypothetical dataset. In the process_order_queue(context, data) function, there is a variable which is called actual_amount. As I understand actual_value is a security specific variable to decide how many shares we are willing to order for that security.
However, when I try to mimic this code it gets the maximum value in the whole dictionary (key and value pairs) if the max_share_allowed is bigger than any value in the dictionary. Or it gives the max_shares_allowed as output in case max_shares_allowed is lower than any maximum value. For example when I say max_share_allowed = 40 the out is 36. Or when I say max_share_allowed = 20 the out is 20 for actual_amount. But this variable gets one value from whole dictionary and it is not security specific.
Am I missing something?

I attached hypothetical data and code as notebook.

Gerard, please note that in my code max_share_allowed is calculated for each security. Here are the details from my code:

volume_history = data.history(context.order_queue.keys(), fields='volume', bar_count=30, frequency='1m')  
volume = volume_history.mean() # average volume of the last 30 minutes  
[...]
max_share_allowed = round(context.sec_volume_limit_perc * volume[sec]) # limit the order volume to a fixed percentage of volume history

Was looking for something like this. Haven't used it yet, but thanks so much Luca!

Is is good enough for Live trading? How can I maximize it to suit for IB Demo?
What are the parameters?

No. Quoting myself: "Here is an example algorithm [...] whose only purpose is to show the class usage."

The class itself has been extensively tested though.

I really agree something like this should be baked into zipline.

Luca, if you are thinking about improvements, I have a few suggestions. There really needs to be a better way to handle risk management. And, risk management is all about position size (based on price volatility) and using stops. So, for example, I want to lose a max of 1% of my total capital per trade and I need to figure out how volatile a stock is to set my position size and stop. With higher price swings, I might want a smaller position and a larger stop, and the opposite with lower swings.

This would be nice if you can think of total trades in a portfolio first and then work down to max risk per trade. Then, with each trade think of handling initial purchase and management of stops. Basically, a portfolio risk management scheme.

Another idea, which I've been tossing around a bit, is to pyramid into or out of a trade. Divide total trade into four units. Buy one unit. Price goes up, buy another. Price goes up again, buy another. Price goes down, sell one unit, etc. People either pyramid into or out of a trade or you can do it both ways.

I'm not sure if you are doing this, but the best way I've found is basically a bookkeeping scheme with a data structure in context.trades. So, with the pyramid idea above, I would have an entry:

{'ticker': ticker, price: [x1, x2, x3], amount: [y1, y2, y3], etc...}

which makes it easy, since you are just accessing and updating numbers. When you pyramid in, you append to price and amount. When you pyramid out, you pop off price and amount. Basically, once you design the data structure, you are good to go, and this approach would be good for the risk management as well.

Anyway, just some thoughts :-)