Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
set_long_only() behavior?

If I uncomment set_long_only() in the attached code, I get this runtime error:


TradingControlViolation: Order for -833.0 shares of Security(3149 [GE]) violates trading constraint LongOnly({}).  
There was a runtime error on line 99.

However, if I run the code without set_long_only(), I see no log output from:

    for stock in context.stocks[:-1]:  
        shares = context.portfolio.positions  
        if shares < 0:  
            print stock  

Any idea what's going on? If short positions are actually being established, then shouldn't I see them with context.portfolio.positions?

Grant

6 responses

I think there's some weird issue with set_long_only() were it doesn't check if you have a negative position in a security, but just throws an exception whenever you try to sell shares (even if you already own those shares and you are not shorting). Maybe the Quantopian folks can look into that.

Hi Grant,

The implementation of all the new Trading Guards is in Zipline at
https://github.com/quantopian/zipline/blob/master/zipline/finance/controls.py.

In the case of LongOnly, the implementation is pretty straightforward, we do

def validate(self,  
             sid,  
             amount,  
             portfolio,  
             algo_datetime,  
             algo_current_data):  
    """  
    Fail if we would hold negative shares of sid after completing this  
    order.  
    """  
    if portfolio.positions[sid].amount + amount < 0:  
        self.fail(sid, amount)  

where portfolio is the same object that's available to Quantopian algorithms on the context object.

One thing I noticed in your post is that you're doing:

for stock in context.stocks[:-1]:  
    shares = context.portfolio.positions # < -- This returns a dictionary!  
    if shares < 0:  
        print stock  

This is causing you to compare the dictionary of all your positions with 0. As it turns out,

{'a': 'b'} < 0

evaluates to False in CPython. I couldn't find a clear reference for the semantics of the dictionary comparision operator,
but some quick testing suggests that any dictionary will compare greater than any integer.

I'm guessing what you actually want is:

for stock in context.stocks[:-1]:  
    shares = context.portfolio.positions[stock] # < -- Indexing with the current stock.  
    if shares < 0:  
        print stock  
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.

Nihar, you're correct that set_long_only doesn't check if you have a short position. Instead, what it does is, prior to any order being placed, check if the immediate execution of that order would result in you holding a short position. This can result in some counterintuitive behavior if, for example, you have open buys for a security that trades infrequently (or if you are buying with a stop/limit price specified, which can delay or prevent the execution of your order depending on the price movement of the security).

Since they're designed first and foremost to protect live traders, the built-in trading controls are designed to be conservative with respect to their error conditions. We generally can't make strong assumptions about the execution timing of your open orders, so by comparing your currently held position with the order being placed, we're effectively checking if the worst possible case would result in violation of the guard.

Thanks Scott,

I changed my code to:

for stock in context.stocks[:-1]:  
        shares = context.portfolio.positions[stock].amount  
        if shares < 0:  
            print stock.sid  
            print stock  

I still see the problem. I'll continue to look into it. I'm wondering if there might be a rounding problem in the evaluation of:

if portfolio.positions[sid].amount + amount < 0  

The current amount (portfolio.positions[sid].amount) will always be a whole number, right? But is the desired amount (generated by order_target_percent) a whole number? If not, as an example, then a number slightly less than zero could result from the sum (and then get rounded down to zero before the order is filled). Is this possible?

Grant

Here's a direct demonstration that there is likely a bug in the 'set_long_only' implementation:

def initialize(context):  
    context.spy = sid(8554)  
    set_long_only()  
def handle_data(context, data):  
    shares = context.portfolio.positions[context.spy].amount  
    if shares == 0:  
        order(context.spy,1)  
    else:  
        order(context.spy,-1)  
        # order(context.spy,-1.1)  
        # Runtime Error:  
        # TradingControlViolation: Order for -1.1 shares of  
        # Security(8554 [SPY]) violates trading constraint  
        # LongOnly({}).  

As I mention above, my hunch is that there is a rounding/truncation problem in:

if portfolio.positions[sid].amount + amount < 0  

In zipline's blotter.py , I see that a round/truncation is applied to the number of shares to be filled by the order method:

# This fixes a bug that if amount is e.g. -27.99999 due to  
        # floating point madness we actually want to treat it as -28.  
        def almost_equal_to(a, eps=1e-4):  
            if abs(a - round(a)) <= eps:  
                return round(a)  
            else:  
                return a

        # Fractional shares are not supported.  
        amount = int(almost_equal_to(amount))  

This same rounding/truncation needs to be applied prior to determining if the attempted order will violate the long-only criterion.

Grant

Hi Grant,

This issue should be fixed in the latest Zipline. See https://github.com/quantopian/zipline/commit/49eaeeb6ae5897e9ff7d96cc5da40e74f3128721

I've also opened up an internal ticket to explicitly document the semantics of non-integer ordering, as right now doing

order(some_sid, -1.1)  

is undocumented/undefined behavior but also doesn't throw an exception or warning.