Back to Community
Multiple Time Frames Algos

Hello,

I have searched the community but couldn't find an example for a multiple time frame algos, I am also struggling with the resample method,
I want to have 15 minutes data and 1 hour data besides my 1 minute data. if any one got an example I'd be very thankful.

11 responses

Hi Adham,

If I'm understanding you correctly, I think this algorithm that calls a function my_func every 30 minutes using schedule_function in a for loop should help you!

Clone Algorithm
17
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
def initialize(context):
  # Puts a stock in our universe so that this code runs
  context.stock = sid(24)
    
  # For every minute available (max is 6 hours and 30 minutes)
  total_minutes = 6*60 + 30

  for i in range(1, total_minutes):
    # Every 30 minutes run schedule
    if i % 30 == 0:
      # This will start at 9:31AM and will run every 30 minutes
      schedule_function(
      myfunc,
        date_rules.every_day(),
        time_rules.market_open(minutes=i),
        True
      )

# Runs every 30 minutes following schedule_function calls in initialize()
def myfunc(context,data):
  pass

# Runs every minute bar
def handle_data(context,data):
  pass
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.

Hi Jamie,

What I mean is an algo that uses different time frames simultaneously, using the so called resample method, I have faced only inconsistency with this method so far, for example I cloned your example and added the resample method but the output is inconsistent (length of Series) which cause me IndexError: index out of bounds when I use it with my trading logic :-

Output:-
2015-09-04PRINT17
2015-09-04PRINT17
2015-09-04PRINT17
2015-09-04PRINT17
2015-09-04PRINT17
2015-09-04PRINT17
2015-09-04PRINT16
2015-09-08PRINT17
2015-09-08PRINT17
2015-09-08PRINT17
2015-09-08PRINT17
2015-09-08PRINT17
2015-09-08PRINT17
2015-09-08PRINT16
2015-09-09PRINT18
2015-09-09PRINT18
2015-09-09PRINT18
2015-09-09PRINT18
2015-09-09PRINT18
2015-09-09PRINT18
2015-09-09PRINT16
2015-09-10PRINT17
2015-09-10PRINT17
2015-09-10PRINT17
2015-09-10PRINT17
2015-09-10PRINT17
2015-09-10PRINT17
2015-09-10PRINT16
2015-09-11PRINT17
2015-09-11PRINT17
2015-09-11PRINT17
2015-09-11PRINT17
2015-09-11PRINT17
2015-09-11PRINT17
2015-09-11PRINT16
2015-09-14PRINT17
2015-09-14PRINT17
2015-09-14PRINT17
2015-09-14PRINT17
2015-09-14PRINT17
2015-09-14PRINT17
2015-09-14PRINT16
2015-09-15PRINT18
2015-09-15PRINT18
2015-09-15PRINT18
2015-09-15PRINT18
2015-09-15PRINT18
2015-09-15PRINT18
2015-09-15PRINT16
2015-09-16PRINT17
2015-09-16PRINT17
2015-09-16PRINT17
2015-09-16PRINT17
2015-09-16PRINT17
2015-09-16PRINT17
2015-09-16PRINT16
2015-09-17PRINT17
2015-09-17PRINT17
2015-09-17PRINT17
2015-09-17PRINT17
2015-09-17PRINT17
2015-09-17PRINT17
2015-09-17PRINT16
2015-09-18PRINT17
2015-09-18PRINT17
2015-09-18PRINT17
2015-09-18PRINT17
2015-09-18PRINT17
2015-09-18PRINT17
2015-09-18PRINT16
2015-09-21PRINT17
2015-09-21PRINT17
2015-09-21PRINT17
2015-09-21PRINT17
2015-09-21PRINT17
2015-09-21PRINT17
2015-09-21PRINT16
2015-09-22PRINT18
2015-09-22PRINT18
2015-09-22PRINT18
2015-09-22PRINT18
2015-09-22PRINT18
2015-09-22PRINT18
2015-09-22PRINT16
2015-09-23PRINT17
2015-09-23PRINT17
2015-09-23PRINT17
2015-09-23PRINT17
2015-09-23PRINT17
2015-09-23PRINT17
2015-09-23PRINT16
2015-09-24PRINT17
2015-09-24PRINT17
2015-09-24PRINT17
2015-09-24PRINT17
2015-09-24PRINT17
2015-09-24PRINT17
2015-09-24PRINT16
End of logs.

Clone Algorithm
1
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
def initialize(context):
  # Puts a stock in our universe so that this code runs
  context.stock = sid(24)
    
  # For every minute available (max is 6 hours and 30 minutes)
  total_minutes = 6*60 + 30

  for i in range(1, total_minutes):
    # Every 30 minutes run schedule
    if i % 50 == 0:
      # This will start at 9:31AM and will run every 30 minutes
      schedule_function(
      myfunc,
        date_rules.every_day(),
        time_rules.market_open(minutes=i),
        True
      )

# Runs every 30 minutes following schedule_function calls in initialize()
def myfunc(context,data):
  close_history = history(14*50,'1m','close_price')[context.stock].resample('50Min',how='last',closed='left', label='left').dropna()
  print (len(close_history))
# Runs every minute bar
def handle_data(context,data):
  pass
There was a runtime error.

I'm not sure I follow. Do you think you could elaborate on what you mean by using different time frames simultaneously? Also, could you explain to me what you are trying to do with the .resample() method? This might help me understand!

If you mean you would like to execute a function once every 15 minutes, another function once every 30 minutes, and another once every hour, you can make 3 schedule function calls, each in its own for loop like my first example!

I mean I want to have access to different frequency time frames besides 1 minute, like the 30 minutes timeframe period and the 1 hour timeframe..
as if you are passing 30min /1 hour into history function :-

history(15,'30m','close_price')
history(15,'1h','close_price')

I want to have access to different time frames

You might be able to find your answer here: https://www.quantopian.com/posts/testing-accuracy-of-resample-1w-for-weekly-history-dataframe

I knew this link and several other, but unfortunately I haven't find my answer there, I hope someone from Quantopian can elaborate on this matter

Hi Adham,

The reason you are getting inconsistent length Series lengths is simply a result of the time frame you chose and the way the .resample() method works. Since 50 minutes does not easily divide the 390 minute trading day, and since you are looking back in history longer than the current day, where each .resample() 'window' starts will change based on which minute bar calls the history function. For example, you might have a window start at 4:00pm (market close) and then another start at 9:30am (market open) because the .resample method does not recognize the gap when the markets are closed.

If you either restrict your history lookback to just the current day, or you choose a number that is divisible by the 390 minute trading day, you shouldn't run into the inconsistent size problem you are having.

Here is a functional 30-minute resolution example of the algorithm you posted.

Clone Algorithm
12
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
def initialize(context):
  # Puts a stock in our universe so that this code runs
  context.stock = sid(24)
    
  # For every minute available (max is 6 hours and 30 minutes)
  total_minutes = 6*60 + 30

  for i in range(1, total_minutes):
    # Every 30 minutes run schedule
    if i % 30 == 0:
      # This will start at 9:31AM and will run every 30 minutes
      schedule_function(
      myfunc,
        date_rules.every_day(),
        time_rules.market_open(minutes=i),
        True
      )

# Runs every 30 minutes following schedule_function calls in initialize()
def myfunc(context,data):
  close_history = history(14*30,'1m','close_price')[context.stock].resample('30Min',how='last',closed='left', label='left').dropna()
  print (len(close_history))
# Runs every minute bar
def handle_data(context,data):
  pass
There was a runtime error.

Thanks Jamie for pointing this out, I recognize this while playing yesterday with different numbers, although I am still having some weird behavior which I couldn't replicate

KeyError: Timestamp ......

which I went over it with a try except block, it may be due to the other point you mentioned that resample doesn't recognize the ** gap when the markets are closed** or maybe it is something else.

Thank you again for your dedication to solve the problem, I do appreciate it

Yeah, if you resample, you need to dropna() afterwards.

Same issue: I would like to get a custom timeframe of data. Just taking the last 180 bars from 1m charts and calling it 12 bars of 15m charts does not represent how we chart the data in contemporary charting systems. Typical systems align the bars to the start of the day or start of the month or week or... actually I don't even really know. But telling me to take the previous bars and simply transposing the last 15 and calling it a 15 minute bar does not sit well.

Maybe I am confused but it seems like I should be able to enter '15m', '30m', '1h', '2h', '1w', '1M', '1y', etc. into the history function to get the data I am trying to curate.

Because of this thread and my anticipation of the need for the 15m and 1h charts, I made this algo. Of course, if this is already made or integrated, please let me know so I can use the proper implementation.

Known buggies/issues:
- Assumes start of day is at 13:30 and therefore may not account for daylight savings time. This needs further development.
- Need to evaluate each timeframe to ensure that is defined as expected: for example, the minute and hourly is based on the start of day which is 31 minutes into the hour. A 15m chart would have its first period from 9:31 to 9:45, second period from 9:46 to 10:00, etc.
- Also, I don't think 7, 31, and 365 are the proper constants for week month and year, but I will leave these as is for now.
- Only tested using a single symbol. I would not suggest trying on a series of symbols. Probably the simple solution is to just iterate over the multiple symbols. That is how I would implement the functionality.
- Probably not optimized, especially if you are trying to take multiple timeframes on the same symbol. There is likely much optimization that could be accomplished in the future and more so out of our control here.
- I am kinda burned on this project, so I am probably missing stuff, take it as it is, let me/us know how to make it better.

Check the source of Historic Custom Timeframe and take all the code from def historic_custom_timeframe and below. You will also need the first line of code to import pandas as pd

You will be able to get your data as exemplified in the top portion of the code like this:

hourly_chart_100_bars = historic_custom_timeframe(data,sid(24),'1h',100)
fifteenMinute_chart_30_bars = historic_custom_timeframe(data,sid(24),'15m',30)

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 as pd

def initialize(context):
    #context.symbols = sid(22467)
    context.symbols = sid(24)
    schedule_function(print_data, date_rules.every_day(), time_rules.market_close(minutes=50))
        
def print_data(context, data):
    values = historic_custom_timeframe(data,context.symbols,'1y',5)
    print values
 
def handle_data(context,data):
    pass
        
def historic_custom_timeframe(data,sym,time_frame,bars):
    """
    data - typical quantopian data object
    sym - a single symbol at this time
    bar - number of bars back 
    time_frame - a string with a number and letter:
      '15m' = 15 minutes 
      '4h' = 4 hours (aggregating minute data)
      '1d' = 1 days (aggregating daily data)
      '1w' = 1 week
      '1M' = 1 month
      '1y' = 1 year (aggregating daily data)
    """
    
    ##
    # Helper functions - Math, NaN, Aggregation
    ##
    
    def parseint(string):
        return int(''.join([x for x in string if x.isdigit()]))

    def nan_min(a,b):
        if a!=a: return b
        if b!=b: return a
        return min(a,b)

    def nan_max(a,b):
        if a!=a: return b
        if b!=b: return a
        return max(a,b)

    def nan_first(a,b):
        if a!=a: return b
        return a

    def nan_last(a,b):
        if b!=b: return a
        return b

    def nan_add(a,b):
        if a!=a: return b
        if b!=b: return a
        return a+b
    
    ##
    # Helper functions - Data Store
    ##
    
    def bar_cols():
        return ['open','high','low','close','price','volume']

    def bar_new(row):
        return pd.Series(row)
    
    def bar_agg(data,row):
        data.set_value('open', nan_first(data['open'],row['open']))
        data.set_value('low', nan_min(data['low'],row['low']))
        data.set_value('high', nan_max(data['high'],row['high']))
        data.set_value('close', nan_last(data['close'],row['close']))
        data.set_value('price', nan_last(data['price'],row['price']))
        data.set_value('volume', nan_add(data['volume'],row['volume']))
        return data

    def row_print(text,index,row):
        print text + " y" + str(index.year) + " M" + str(index.month) + " w" + str(index.week) + " d" + str(index.day) + " h" + str(index.hour) + " m" + str(index.minute) + " o" + str(row['open']) + " h" + str(row['high']) + " l" + str(row['low']) + " c" + str(row['close']) + " p" + str(row['price']) + " v" + str(row['volume'])
    
    ##
    # Helper functions - Period Start: from this index to the previous, would you call this a new period for the timeframe?
    ##
    
    def period_start_minute(index,prev_index,n):
        # this does not account for daylight savings, which would use 14 not 13
        # day starts at 9:31 which is 13:31 UTC or 14:31 UTC in winter time.
        # assuming 1m indicies
        if not prev_index: return False
        start_m = 31+13*60
        cur_m = index.minute + index.hour*60 - start_m
        prev_m = prev_index.minute + prev_index.hour*60 - start_m
        return (int(cur_m/n) != int(prev_m/n))

    def period_start_hour(index,prev_index,n):
        # this does not account for daylight savings, which would use 14 not 13
        # day starts at 9:31 which is 13:31 UTC or 14:31 UTC in winter time.
        # assuming 1m indicies
        if not prev_index: return False
        start_m = 31+13*60
        cur_m = index.minute + index.hour*60 - start_m
        prev_m = prev_index.minute + prev_index.hour*60 - start_m
        return (int(cur_m/60/n) != int(prev_m/60/n))

    def period_start_day(index,prev_index,n):
        # really only expecting 1d, will rezero on the month, not intending to test for more than 1d
        # assuming 1d indicies
        if not prev_index: return False
        return (prev_index.month != index.month) or ((index.day-1) % n) == 0

    def period_start_week(index,prev_index,n):
        # really only expecting 1w, will rezero on the year, not intending to test for more than 1w
        # assuming 1d indicies
        if not prev_index: return False
        return (prev_index.week != index.week) and (index.week % n) == 0

    def period_start_month(index,prev_index,n):
        # assuming 1d indicies
        if not prev_index: return False
        return (prev_index.month != index.month) and (index.month % n) == 0

    def period_start_year(index,prev_index,n):
        if not prev_index: return False
        return (prev_index.year != index.year) and (index.year % n) == 0
    
    
    
    ##
    # Do the work!
    ##
    
    tf_s = str(time_frame);
    tf_n = parseint(time_frame)
    if 'm' in tf_s:
        period_start = period_start_minute
        history_bars = tf_n*(bars+1)-1
        history_tf = '1m'
    elif 'h' in tf_s:
        period_start = period_start_hour
        history_bars = 60*tf_n*(bars+1)-1
        history_tf = '1m'
    elif 'd' in tf_s:
        period_start = period_start_day
        history_bars = tf_n*(bars+1)-1
        history_tf = '1d'
    elif 'w' in tf_s:
        period_start = period_start_week
        history_bars = 7*tf_n*(bars+1)-1
        history_tf = '1d'
    elif 'M' in tf_s:
        period_start = period_start_month
        history_bars = 31*tf_n*(bars+1)-1
        history_tf = '1d'
    elif 'y' in tf_s:
        period_start = period_start_year
        history_bars = 365*tf_n*(bars+1)-1
        history_tf = '1d'
    else:
        raise ValueError('Timeframe should contain a number and one of [mhdwMy]')
      
    d = data.history(sym,bar_cols(),history_bars,history_tf)
    df = pd.DataFrame(columns=bar_cols())
    
    cur_d = None
    cur_index = None
    prev_index = None
    
    for index, row in d.iterrows():
        if period_start(index,prev_index,tf_n):
            if cur_index:
                df.loc[cur_index] = cur_d
            cur_index = index
            cur_d = bar_new(row)
            row_print("period_point",index,row)
        elif cur_index:
            bar_agg(cur_d,row)
        prev_index = index
            
    if cur_index:
        df.loc[cur_index] = cur_d

       
    return df
        
There was a runtime error.