Back to Community
Tying variables to stocks

Hey guys,

New here, so forgive anything obvious I'm missing, but I'm working on a day trading system, and I can't seem to think of an appropriate way to store the 'entry' value I calculate from the ATR in my first function, so that I can then lookup that entry value for each specific stock in my handling function.

If anyone has any idea of how I could do this I would greatly appreciate it.

Clone Algorithm
4
Loading...
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
import pandas
import talib

def initialize(context):
    
    set_symbol_lookup_date('2015-06-01')
    
    # define the portfolio of stocks to watch: 
    context.stocks = {symbol('AAPL'), 
                      symbol('GOOG'),
                      symbol('AMZN')}
    
    schedule_function(calculate_atr,
                      date_rules.every_day(),
                      time_rules.market_open())
    
    schedule_function(func=liquidate, date_rule=date_rules.every_day(),
                      time_rule=
                      time_rules.market_close(hours=0, minutes=30),
                      half_days=True)

def calculate_atr(context, data):  
    
    # calculate the average range for the past 25 days  
    for stock in context.stocks :  
        
        # load historical data for the stock  
        highs = history(30, '1d', 'high')[stock]  
        lows = history(30, '1d', 'low')[stock]  
        closes = history(30, '1d', 'close_price')[stock]  
        
        # calculate the ATR for the stock  
        if highs.any() and lows.any() and closes.any():
            
            atr = talib.ATR(highs, lows, closes, timeperiod=18)[-1]
        
            # multiply that value by 10% to get your entry value
            # entry = atr * .10
        
# called on every trade event for the securities you specify. 
def handle_data(context, data):
    
    for stock in context.stocks :
        
        if data[stock].price > data[stock].open_price : # + entry :
            
            order_target_percent(stock, 0.25)
    
# run at 3:30pm every day
def liquidate(context, data):
    
    for stock in context.stocks :
        
        # sell positions
        order_target_percent(stock, 0)
                         
There was a runtime error.
3 responses

I think the easiest way to do this is to create a dictionary that stores each stock's entry. For example, you can access a certain stock's entry by doing:

context.entries[stock]  

I made an example below. I also have to check that we have previously calculated an entry for a certain stock, which is why I added the first part to the if statement.

Hopefully that helps!

Clone Algorithm
2
Loading...
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
import pandas
import talib

def initialize(context):
    
    set_symbol_lookup_date('2015-06-01')
    
    # define the portfolio of stocks to watch: 
    context.stocks = {symbol('AAPL'), 
                      symbol('GOOG'),
                      symbol('AMZN')}
    
    schedule_function(calculate_atr,
                      date_rules.every_day(),
                      time_rules.market_open())
    
    schedule_function(func=liquidate, date_rule=date_rules.every_day(),
                      time_rule=
                      time_rules.market_close(hours=0, minutes=30),
                      half_days=True)

    context.entries = {} # Create dictionary of entries
    
def calculate_atr(context, data):  
    
    # calculate the average range for the past 25 days  
    for stock in context.stocks :  
        
        # load historical data for the stock  
        highs = history(30, '1d', 'high')[stock]  
        lows = history(30, '1d', 'low')[stock]  
        closes = history(30, '1d', 'close_price')[stock]  
        
        # calculate the ATR for the stock  
        if highs.any() and lows.any() and closes.any():
            
            atr = talib.ATR(highs, lows, closes, timeperiod=18)[-1]
        
            # multiply that value by 10% to get your entry value
            context.entries[stock] = atr * .10
        
# called on every trade event for the securities you specify. 
def handle_data(context, data):
    
    for stock in context.stocks:
        
        if stock in context.entries and data[stock].price > data[stock].open_price + context.entries[stock]:
            
            order_target_percent(stock, 0.25)
    
# run at 3:30pm every day
def liquidate(context, data):
    
    for stock in context.stocks :
        
        # sell positions
        order_target_percent(stock, 0)
                         
There was a runtime error.
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.

Or you could use the zipline SIDData object as a dynamic property base. You can effectively stick any named property to a SIDData object and it will hang around as long as you need it. This backtest does a gap open test instead of your other given test.

The strategy keeps the SIDData objects in a context.S collection. And then you can just stick stuff to them:

context.S[stock].MyFavoriteFood = "Twinkies"  
context.S[stock].ConsumedPerYear = 18234  
Clone Algorithm
5
Loading...
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
import pandas
import talib
import zipline

ATRPeriods = 18

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def initialize(context):
    set_symbol_lookup_date('2015-06-01')
    
    # define the portfolio of stocks to watch: 
    symbols('AAPL', 'GOOG', 'AMZN')
    
    schedule_function(UpdateState,  date_rules.every_day(), time_rules.market_open())
    schedule_function(CalculateATR, date_rules.every_day(), time_rules.market_open())
    schedule_function(HandleEntry,  date_rules.every_day(), time_rules.market_open())
    # Run at 3:30pm every day
    schedule_function(HandleExit,   date_rules.every_day(), time_rules.market_close(minutes=30))
    
    context.S = {} # Dictionary of securities
    
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def handle_data(context, data):
    # Never do anything inside handle_data
    pass

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def CalculateATR(context, data):  
    # 
    # NOTE: on minutely data the latest price is for the first minute of the day
    #
    highDeck  = history(30, '1d', 'high').dropna(axis=1)
    lowDeck   = history(30, '1d', 'low').dropna(axis=1)
    closeDeck = history(30, '1d', 'close_price').dropna(axis=1)
    valid     = [sid for sid in highDeck if sid in data]
    highDeck  = highDeck[valid]
    lowDeck   = lowDeck[valid]
    closeDeck = closeDeck[valid]
    
    # calculate the average range for the past n days  
    for stock in context.S:
        try:
            atr = talib.ATR(highDeck[stock], lowDeck[stock], closeDeck[stock], timeperiod=ATRPeriods)[-1]
            context.S[stock].ATR = atr * .25
            context.S[stock].PriorClose = closeDeck[stock].iloc[-2]
        except: continue
        
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def HandleEntry(context, data):
    eligible = []
    for stock in context.S:
        # Price gap up -- close yesterday + ATR is less than open today
        if (context.S[stock].PriorClose + context.S[stock].ATR < context.S[stock].Open):
            eligible.append(stock)
            
    for stock in eligible:
        order_target_percent(stock, 1.0 / len(eligible))
        stockData = context.S[stock]
        print(">>> Entry {0:<6} {1:>7.2f} + {2:>7.2f} < {3:>7.2f}".format(
                stock.symbol, stockData.Close, stockData.ATR, stockData.Open))
        
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def HandleExit(context, data):
    for stock in context.S:
        # sell positions
        order_target_percent(stock, 0)
        
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def UpdateState(context, data):
    # Reconcile available stocks into context.S
    for stock in data:
        if (stock not in context.S):
            context.S[stock] = DataStock(stock, data[stock])
        else:
            context.S[stock].Update(data[stock])
            
    # Reconcile backwards for securities we can no longer trade
    removeThese = []
    for stock in context.S:
        if (stock not in data):
            removeThese.append(stock)
    for stock in removeThese:
        del context.S[stock]
        
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class DataStock(zipline.protocol.SIDData):
    def __init__(this, sid, dataSid):
        this.Update(dataSid)

    def Update(this, dataSid):
        this.Open   = dataSid.open_price
        this.High   = dataSid.high
        this.Low    = dataSid.low
        this.Close  = dataSid.close_price
        this.Volume = dataSid.volume        
There was a runtime error.

Also notice the history() call is outside of the 'for' loop since history already has the history for every stock in data (so no need to re-run history() inside the loop each time).
Another point generally (although it might not apply so directly to the code above): At one time I recall running into a cryptic message doing for stock in context.stocks that took a skype session with Q to figure it out, it was merely because a stock had no trades during that bar, and I was bitten by it way down the line in another function, so ever since then I stick with for stock in data, or for some other purposes for stock in context.portfolio.positions, just never for stock in context.stocks, recommend shying away from that. You can see MT has some safeguards.
Notice the vertical alignment of equal signs in Market Tech code, makes for easier reading.
You can even run wild with white space like I do:

        c.highs  = history(period, '1d', 'high') .bfill().fillna(1)  
        c.lows   = history(period, '1d', 'low')  .bfill().fillna(1)  
        c.prices = history(period, '1d', 'price').bfill().fillna(1)  

...or:

        if show_beta:       do_show_beta(      c, sec)  
        if show_volatility: do_show_volatility(c, sec)  
        if show_drawdown:   do_show_drawdown(  c, sec, price)  

(c = context) There's virtually no performance hit, Python runs a built version that removes all white space, and comments.
Part of the reason I posted that is that I'm not sure of what I'm doing with the fills in case anyone has a definitive tip.