Back to Community
My attempt at Natural Gas trading algo

Been working in this for a little bit cant quite get it to run take a look at what I have so far.

def rename_col(df):  
    log.info(df.head())  
    return df

def initialize(context):  
    context.investment_size = (context.portfolio.cash / 10.0)  
    #Invest 10 percent of total cash  
    context.stop_loss_pct = 0.995  
    #Use Stop Loss  
    set_symbol_lookup_date('2013-10-01')  
#Call Zonal Usage CSV  
fetch_csv('https://td2ec2in4mv1euwest.teamdrive.net/primespace/59358/public/UH34BU000Z7FX005394VPP9272UGRRD6/Combined%20Weather%20Data.csv?P1RID=1',  
        date_column='Date',  
        pre_func = rename_col,  
        date_format = '%m/%d/%y')  
context.stock = symbol('UNG')  
def handle_data(context, data):  
    cash = context.portfolio.cash  
    try:  
        for s in data  
            if 'Casio Usage' in data[s]:  
                Casio = data[s]['Casio Usage']  
                current_postion = context.portfolio.positions[s].amount  
                current_price = data[s].price  
#If Power Usage is high from temperature data then UNG will trade at a premium  
if (Casio > 650) and (current_postion == 0):  
                    if cash > context.investment_size:  
                        order_value(s,context.investment_size, style=StopOrder(context.stop_loss*current_price))

#If Power Usage is low then UNG should trade at a discount and we should exit position  
                elif (Casio <= 650) and (current_position > 0):  
                    order_target(s,0)  
     except Exception as e:  
        print(str(e))  
24 responses

Hi Spencer,

I cleaned up your code a little bit (indentations, changed symbol to sid, and fixed some incorrect syntax). It's shared below.

Currently the algo won't run because the "Symbols" column in the CSV needs to be lowercase ("symbols"). Once you make that change, give it another whirl.

def rename_col(df):  
    log.info(df.head())  
    return df

def initialize(context):  
    context.investment_size = (context.portfolio.cash / 10.0)  
    #Invest 10 percent of total cash  
    context.stop_loss_pct = 0.995  
    #Use Stop Loss  
    set_symbol_lookup_date('2013-10-01')  
    #Call Zonal Usage CSV  
    fetch_csv('https://td2ec2in4mv1euwest.teamdrive.net/primespace/59358/public/UH34BU000Z7FX005394VPP9272UGRRD6/Combined%20Weather%20Data.csv?P1RID=1',  
        date_column='Date',  
        pre_func = rename_col,  
        date_format = '%m/%d/%y')  
    context.stock = sid(33697)  
def handle_data(context, data):  
    cash = context.portfolio.cash  
    try:  
        for s in data: #loop through the stocks  
            if 'Casio Usage' in data[s]:  
                Casio = data[s]['Casio Usage']  
                current_postion = context.portfolio.positions[s].amount  
                current_price = data[s].price  
            #If Power Usage is high from temperature data then UNG will trade at a premium  
            if (Casio > 650) and (current_postion == 0):  
                if cash > context.investment_size:  
                        order_value(s,context.investment_size, style=StopOrder(context.stop_loss*current_price))

           #If Power Usage is low then UNG should trade at a discount and we should exit position  
            elif (Casio <= 650) and (current_position > 0):  
                    order_target(s,0)  
    except Exception as e:  
        print(str(e))  
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.

Wasn't sure if you meant the actual symbol or just the symbol title. Regardless I changed both and am still getting the "symbol error message," even after trying both changes. Is there anything else I can try to fix this problem? On a side note I fixed the csv to be more dynamic so I can simplify the algo logic.

KeyError: 'symbol'
There was a runtime error on line 15.

def rename_col(df):  
    log.info(df.head())  
    return df

def initialize(context):  
    context.investment_size = (context.portfolio.cash / 10.0)  
    #Invest 10 percent of total cash  
    context.stop_loss_pct = 0.995  
    #Use Stop Loss  
    set_symbol_lookup_date('2013-10-01')  
    #Call Zonal Usage CSV  
    fetch_csv('https://td2ec2in4mv1euwest.teamdrive.net/primespace/59358/public/2CN6QU800Z7FX00539QK0UNKT0NQAUQ2/Combined%20Weather%20Data%20Updated%20(1)%20(2).csv?P1RID=1',  
        date_column='Date',  
        pre_func = rename_col,  
        date_format = '%m/%d/%y')  
    context.stock = sid(33697)  
def handle_data(context, data):  
    cash = context.portfolio.cash  
    try:  
        for s in data: #loop through the stocks  
            if 'Total Usage' in data[s]:  
                Total_Usage = data[s]['Total Usage']  
                current_postion = context.portfolio.positions[s].amount  
                current_price = data[s].price  
            #If Power Usage is high from temperature data then UNG will trade at a premium  
            if (Total_Usage > 4500) and (current_postion == 0):  
                if cash > context.investment_size:  
                        order_value(s,context.investment_size, style=StopOrder(context.stop_loss*current_price))

           #If Power Usage is low then UNG should trade at a discount and we should exit position  
            elif (Total_Usage <= 3000) and (current_position > 0):  
                    order_target(s,0)  
    except Exception as e:  
        print(str(e))  

Ok I fixed the issue I went back into teamdrive and uploaded and republished it and I got the thing running. Results are mehhhh so far continuing to add perimeters (injection #'s, sentiment data, and seasonal buying selling broken up into peak times summer & winter and off peak spring & fall will continue to post updates if anyone cares (and or has any additional ideas or want to help in some fashion just let me know). Thank you for the help Alisa. I love how no matter how slow I go in trying to troubleshoot it is always the simple fixes that I seem to pass over unintentionally.

Here are some results I gotten so far.

Clone Algorithm
25
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 559fd0d9f8afcf1244480713
There was a runtime error.

@spencer, rather than hardcoding the numbers of what is low and high, I would recommend using a zscore to normalise, then the zscore will determine what high and low is in the window you are looking. I ran a quick covariance and there is a relationship to be had but I think a model with WTI or Brent added would work (I didnt dive into it deeply but glancing it it seemed to be significant)

Haven't really used zscore at all, but thanks for the input will defiantly look into it.

I am getting a werid buy order at the beginning of the backtest. Also it doesn't seem to be reacting correctly to my buy and sell orders. Can a fresh set of eyes look at it and let me know if they have any idea what is going wrong?

Clone Algorithm
25
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 559fed52d16a58124995000b
There was a runtime error.

Ive been trying to add a Zscore to normalize and do the highs and lows as suggested, but I am having issues with getting it to run. Could someone with more experience with Zscore give it a whirl?

So for some reason my algo isn't buying and selling like it should. Based on the criteria I put in it should go long and short 20 or 30 times and it only goes short once. Not really sure why as I seem to be calling the data correctly. It just for some reason isn't working correctly. Anyone know what is going wrong?

Clone Algorithm
7
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 55a52206f27d890c738f8d76
There was a runtime error.

Maybe your current_position > 0 should instead be current_position < 0 on line 37? That adds a few more trades. In general, it seems like your trading logic is set up kind of strangely. Having one if - elif ... - else clause usually makes more sense logically, so fixing that may help you at least have a better idea of what's happening. Also, cool idea, I like it when algos use statistics like power consumption!

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.

I am working on using zscore to define the buy and sell signals. It is my first time using a zscore so I'm just trial and erroring it. This should give more dynamic buy and sell signals.

Here is my newest iteration of the code fixed it to make to code less less commented out more things still is only shorting once and not following the logic after it shorts it once.

Clone Algorithm
50
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 55a68b00bcc1120c64976b69
There was a runtime error.

What is inside the for loop? Look carefully.

All the data in the CSV file im calling in is all I can see that in the for loop. But regardless I'm only using the Total_Usage during my order logic and that is where I believe the algo is going wrong correct?

So if the long if ... elif ... else statement in lines 43 to 57 is outside the for loop, why are you surprised that your algo "shorts only once"?

Shouldn't it run through the logic everyday and take positions based on the data output? And I can could see if I wasn't getting any orders, but I get a single short sell order then it never seem to recognize the criteria I set to exit and reenter position. Maybe I am not understanding the for loop logic correctly should I be nesting the logic in the for loop I tried to do that, but it didn't seem to run when I tried to do that. I also read up on for loop and didn't see anything that stood out to me.

How many assets are you trying to trade? Just one - the United States Natural Gas ETF (UNG), correct? Then your if need not be inside the for. (Most algorithms trade more assets, and then a typical pattern is: for security in data:, if that security meets some condition, order a buy or sell - the ifs are nested inside the for.)

How often are you trying to trade? In minute-mode back-testing, and in live trading, handle_data is called at the end of every trading minute, normally 9:31am to 4:00pm EST/EDT. Quantopian's code fills a data dictionary based on what happened that minute, your code looks at it, checks the various if conditions, and executes the order* functions or not, according to what you wrote (and not necessarily what you meant).

The order_target_percent function is designed to make sure the specified asset becomes the specified fraction of your portfolio - by buying if you have less, selling if you have more, or doing nothing if you're already within one share's worth of your target. This remains in force until another order* function call, for a nonzero number of shares, changes it. Holdings are not automatically sold or covered at the end of the day, or for any other reason. Once the conditions for buying (or shorting) were met and an order was sent, is it possible that they have never changed to the point of generating an exit signal?

Another thing: when are the conditions in lines 43 and 52 ever true?

Another thing: after you short in line 50, your "current position" becomes negative. Where is there an order_target_percent or any other order* function that will change such a position?

Good idea! I wonder how the language evaluates 1<x<1 is that always false? In other words, is it interpreted as AND or OR, if it's AND then there are troubles with those elifs.

In Python, 1<x<1 is equivalent to (1<x) and (x<1), except the value of x is only computed once. Now, if x contains a real number (of type float or int), there are three possible cases:
- x1; then x<1 is False, and(1<x) and (x<1) is False.
Thus, 1<x<1 is False for any numerical value of x. No good.

Using such chained inequalities is a good idea, though, as long as all comparison operators point in the same direction, eg. 1<=x<2; they are easier to write, easier to read, easier to understand, and run faster.

I thought so. But in the iteration of the code, lines 43 and 52 are (1750 > x > 4800) and (3300 >= x >= 3900) and as you @André stated that would never be true. Possibly the OP meant to say

    elif((x <= 3300 or x >= 3900)and ...  

Better still, from a readability perspective
elif(isModerateTemp(x) and positionIsOpen(context)) closePosition(context); Etc etc...with those functions defined in below in the same file.

Exactly. But I wanted the author to look and find it, and correct it himself. I don't mind helping people learn; but I don't support people writing sloppy code and throwing it at us to tie their loose ends for free. I can write all the code you need if you want me to, but not on these terms.

Good suggestion, putting conditions into separate functions.

I ended up fixing it 2 weeks ago by changing it to the correct logic

elif (3300 >= Total_Usage or Total_Usage >= 3900) and (current_position < 0):  

and I understood that when I am shorting the current_position will actually be x < 0 so for it to ever cover the short I needed to make it check to see if current_position < 0 as opposed as to what I was trying previously which was
current_position > 0 which works when to algo is long, but when short doesn't work.