Back to Community
Using changing slope of a moving average to trade futures

Hi,

If I wanted to create a schedule function that did not use time as it's decision of when to execute the code, how would I do that?
I want the schedule function to execute based on parameters I set previously, not associated with time.

My sloppy code looks like this now... having syntax errors, no updated backtest...

def initialize(context):
context.security = sid(8554)

""" The problem is this schedule function situation gets called on hours, minutes, market open, market close etc... but does not execute when the conditions of the trader function are met... it executes when the hour happens, when the minute happens, when market open or market close happens etc... it needs to execute when the event occurs, in this case the crossover, the "x", formed by the mavg_1 and mavg_2 signifying a change in the market going up or down or sideways. """

"""Also I want to be able to only execute the trader function when I do not have a position, in other words I do not want to take a position if I already have a position in the underlying security, in this case SPY. The following code is an attempt to describe that situation. """

def position(context,data):

# position = context.portfolio.positions
# if position > abs(2000000):
# return True

        #do go to the trader function below

def trader(context,data):

price_hist_2 = data.history(context.security, 'price', 9, '1m')  
mavg_1 = price_hist_2.mean()  
price_hist = data.history(context.security, 'price', 18, '1m')  
mavg_2 = price_hist.mean()  

"""Here I need to calculate the changing slope of mavg_1 and mavg_2. When the slope of mavg_1 begins to become negative after the slope has been positive, a downward moving market should be coming, and the "x". When the slope of mavg_1 becomes positive after being negative, an upward moving market should be coming, and the "x". The shift in slope of the mavg_1 being positive then negative then positive then negative and so on is where you capture your profit, you're making profit in the change in location of the "x". The crossover of the moving averages indicates the long or short market... When the lines intersect, you trade. When the lines don't intersect but are still moving with coresponding slopes indicative of the direction you are positioned for, the code executes on a small percentage of profit, continuously, to capute profit. """

slope_1 = changing slope of mavg_1  
slope_2 = changing slope of mavg_2  


cost_basis = context.portfolio.positions[context.security].cost_basis  
current_price = context.portfolio.positions[context.security].last_sale_price  
account_value = context.portfolio.portfolio_value  


if mavg_2 < mavg_1 and :  
    order_percent(context.security, .10)  
    log.info("Long")  

if mavg_2 > mavg_1:  
    order_percent(context.security, -.10)  
    log.info("Short")  

"""Here is me trying to execute the small percentage change of profit by exiting all positions at a .0625 % gain"""

elif current_price >= cost_basis * 1.0625 or current_price <= cost_basis * .9375:  
    order_percent(context.security, 0.0)  
    log.info("exiting")

If you look at the transaction details in the backtest you will see it's only trading one time a day, 2 minutes after market open, rather than through out the day whenever the crossover of the moving averages occurs.

I guess my main question is, how do you program a "Schedule Function" without using time to determine when it executes, instead of using time it would use any other indicator.

Best,
Lovis

Clone Algorithm
2
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
"""
This is a template algorithm on Quantopian for you to adapt and fill in.
"""
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import AverageDollarVolume
 
def initialize(context):
    """
    Called once at the start of the algorithm.
    """   
    context.security = sid(8554)
        
    # Rebalance every day, 1 hour after market open.
def run_func(context):
    schedule_function(trader, date_rules.every_day(), time_rules.market_open())
    
    
def position(context,data):
    position = context.portfolio.positions
    if position > abs(2000000):
            return True
            
            #do go to the trader function below

def trader(context,data):
    """
    Execute orders according to our schedule_function() timing. 
    """
    price_hist = data.history(context.security, 'price', 18, '1m')
    mavg_2 = price_hist.mean()
    price_hist_2 = data.history(context.security, 'price', 9, '1m')
    mavg_1 = price_hist_2.mean()
    cost_basis = context.portfolio.positions[context.security].cost_basis
    current_price = context.portfolio.positions[context.security].last_sale_price
    account_value = context.portfolio.portfolio_value
    
    
    if mavg_2 <= mavg_1:
        order_percent(context.security, .10)
        log.info("Long")
        
    if mavg_2 >= mavg_1:
        order_percent(context.security, -.10)
        log.info("Short")
    
    elif current_price >= cost_basis * 1.0625 or current_price <= cost_basis * .9375:
        order_percent(context.security, 0.0) 
        log.info("exiting")

    record(account_value = account_value)
    pass
There was a runtime error.
3 responses

The maximum frequency of trading on Quantopian is a per-minute basis - you can't set event based triggers. Rather, you should check every minute whether your condition has changed, and if it has, execute your trading logic.

For your use case, you can schedule a function to run every minute of the day, and at each minute check the condition and execute the appropriate logic.

To schedule a function for every minute of the day, you can use a loop like the one below in your initialize function:

def initialize(context):  
  # For every minute available (max is 6 hours and 30 minutes)  
  total_minutes = 6*60 + 30

  for i in range(1, total_minutes):  
      # This will start at 9:31AM and will run every minute  
      schedule_function(  
      myfunc,  
        date_rules.every_day(),  
        time_rules.market_open(minutes=i),  
        True  
      )  
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 Matthew,

Thanks for your post.

Wait: You're saying you CAN NOT SET EVENT based triggers?!?! What is the point of using Quantopian if you are not allowed to set event based triggers? In order to create a profitable technical analysis based trading strategy you have to use events to figure out when to trigger (to execute, entering or exiting)... that's how it works! Even if you're a human, clicking the mouse. If I'm not trading on a culmination of events, I'm just trading randomly, and that does not make sense! The whole point of technical analysis is to use indicators to determine when events are happening, when those events happen you make a trade, if and only if the event is happening. We're trying to increase the probability of winning trades, not keep that probability at 50 50... If I can't execute based on events, in the Quantopian environment, Quantopian is useless : (

? Lovis

I built a quick algo with the above criteria, as best as I could understand from the question.

The way to do criteria based trading is to set up a function that tests the criteria as frequently as you want, daily, minutely, etc...
The key is to use if statements so that the algo only initiates a trade when the criteria is met.
You can't set up an algo in Quantopian to execute a function when the criteria are met.
You have to run the function frequently (as often as you want to test the criteria), but only allow it to act/trade when the criteria are met.
Running a scheduled function and initiating trades can easily be separated. A scheduled function can run much more frequently than it trades, in this algo it checks the criteria every minute, but trades quite infrequently (I even reduced your exiting band so that it would trade a bit more often).
Hope this helps!

In this example the first function tests every minute to check the criteria:
1. Is there a current open position, if no continue evaluation, if yes, then that is the end of the function.
2. if there is no open position, check if the sign of the slope has changed. If it has not changed, then it doesn't do anything. If it has changed then it initiates the trade.

There is a second function that tests every minute with different criteria.
1. Is there an open position, if yes then continue evaluation, if no then that is the end of the function.
2. If there is an open position, the criteria for selling the position are evaluated. If the criteria are met, it closes the position. If not it doesn't do anything.

Clone Algorithm
17
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
def initialize(context):
    #Security to trade
    context.security = sid(8554)
    
    #Global variable to keep your test criteria
    context.moving_average_test = 0
    
     # For every minute available (max is 6 hours and 30 minutes)  
    total_minutes = 6*60 + 30

    #Function to check the trading criteria
    for i in range(1, total_minutes):  
        # This will start at 9:31AM and will run every minute  
        schedule_function(  
        criteria_test,  
        date_rules.every_day(),  
        time_rules.market_open(minutes=i),  
        True  
      )      
    
    #Function to exit an open position given the stated criteria
    for i in range(1, total_minutes):  
        # This will start at 9:31AM and will run every minute  
        schedule_function(  
        sell_position_test,  
        date_rules.every_day(),  
        time_rules.market_open(minutes=i),  
        True  
      )
        
def criteria_test(context, data):
    #Create a global variable to store the test result for comparison
    #context.moving_average_test will store the previous value for the slope for comparison
    previous_slope = context.moving_average_test
    print(previous_slope)
    
    #Obtain current data
    price_hist = data.history(context.security, 'price', 18, '1m')
    mavg_2 = price_hist.mean()

    price_hist_2 = data.history(context.security, 'price', 9, '1m')
    mavg_1 = price_hist_2.mean()
     
    current_slope = ((mavg_1 - mavg_2) / mavg_2)*1000
    print(current_slope)
    #Once you have obtained your data you can decide if you want to make a trade based on that information
    #Test first criteria - Is there already an open position?
    
    #Determine current position
    position = context.portfolio.positions_value
    
    #If position is zero, then continue with the criteria test
    #If you aleady have a position, then it will not make a trade
    if position == 0:
        
        #If the slope of the moving average has changed sign then enter position
        if previous_slope > 0 and current_slope < 0:
            order_percent(context.security, -1)
            log.info("Short")
        
        if previous_slope < 0 and current_slope > 0:
            order_percent(context.security, 1)
            log.info("Long")
    
    #update global variable
    context.moving_average_test = current_slope
            
    record(current_slope = current_slope, previous_slope = previous_slope, position_value = position)

def sell_position_test(context, data):
    
    #Obtain data for test criteria
    position = context.portfolio.positions_value
    cost_basis = context.portfolio.positions[context.security].cost_basis
    current_price = context.portfolio.positions[context.security].last_sale_price

    #Test your criteria
    #Do you have a current position?
    if position != 0:
        cost_basis = context.portfolio.positions[context.security].cost_basis
        current_price = context.portfolio.positions[context.security].last_sale_price
        
        #Has your position appreciated/depreciated by the stated criteria?  If so, sell the entire position.
        if abs(current_price) >= (abs((cost_basis * 1.02))) or abs(current_price) <= abs((cost_basis * .98)):
            if position > 0:
                order_percent(context.security, -1)
            if position < 0:
                order_percent(context.security, 1)
            log.info("exiting")      
    
There was a runtime error.