Back to Community
Intraday Pattern Strategy: Late morning weakness

Hello Quantopian Community,

goal of my research is to build a strategy/algorithm of another market imperfection, namely the "late morning weakness".
According to Admati, Anat and Paul Pfleiderer (1988) intraday pattern occur as a result of strategic behaviour. So what is the late morning weakness?

Research objective is to identify a situation in the morning around 10 am, when the market, after trending bullish at opening, diminishes it gains (coffee break hypothesis).

First i start to analyze the SPY from 2013/01 - 2015/11 using minute data. Briefly what i did is following:

  1. creating a loop from 9:30 - 1 o clock and normalize the price path to 1.
  2. Building average chart with top and bottom percentile lines
  3. Volume analysis (profile by daytime)
  4. Test for days

The main insight so far is that the effect occurs around 11:30 am on average. On Tuesdays there is a major decline around 11:30 am as well. The volume analysis implies a high volume at the beginning of the day.

Im glad to hear your response and many thanks to those who have good ideas :)

Further steps would be applying more filters and finding out under conditions under which the effect occurs. Its very interesting to do a backtest and see how it performs. Thank you for any Help!

Greetings, Emil

Loading notebook preview...
2 responses

This is my backtest so far. It open up a short position at 11:30 am every day and close position 30 minutes later. You can play around with days :)

Clone Algorithm
19
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):
    context.spy = sid(8554)
    
    # Rule out trading costs for now
    set_commission(commission.PerShare(cost=0, min_trade_cost=0))
    
    # Ensure the trades get filled
    set_slippage(slippage.FixedSlippage(spread=0))

    # open position 2 hours after market open (11:30) once per day
    schedule_function(open_positions, 
                      date_rules.every_day(), 
                      time_rules.market_open(hours=2, minutes=0)
                      )
        
    # close position at 12:00      
    schedule_function(close_positions, 
                      date_rules.every_day(),
                      time_rules.market_close(hours=4, minutes=0)
                      )
    
def open_positions(context, data):
    # Position 50% of our portfolio to be short in SPY
    order_target_percent(context.spy, -0.50)

def close_positions(context, data):
    order_target_percent(context.spy, 0)
    
There was a runtime error.

According to my new research results there is a market anomaly at 10.44am until 11.05am. In this time period the algo goes short in SPY every day. On Mondays and Tuesdays the effect is the biggest. For further research it might be more profitable to exclude other days.

Clone Algorithm
10
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 numpy as np
from datetime import date, datetime, timedelta, time

def initialize(context):
    context.spy = sid(8554) #better results with DJ 2174, SPY 8554
    context.entered = False
    context.enter_cash = 0
    
    # Rule out trading costs for now
    set_commission(commission.PerShare(cost=0, min_trade_cost=0))
    
    #no spread
    set_slippage(slippage.FixedSlippage(spread=0))

    # open position ..... after market open
    # 10.44am is 1 hours, 14 minutes
    schedule_function(func=enter, 
                      date_rule=date_rules.every_day(), 
                      time_rule=time_rules.market_open(hours=1,
                      minutes=14)
                      )
        
    # close position .... before market close 
    # 11.05 am 4 hours, 55 minutes
    schedule_function(func=exit, 
                      date_rule=date_rules.every_day(),
                      time_rule=time_rules.market_close(hours=4,
                      minutes=55)
                      )


def enter(context, data):
    
   if not context.entered :    
    # Position 50% of our portfolio to be short in SPY
     order_target_percent(context.spy, -1.0) # -3 leverage
     context.entered = True
     log.info("Entered")
     context.enter_cash = context.portfolio.cash

def exit(context, data):

   if context.entered :    
     order_target_percent(context.spy, 0.0)
     context.entered = False
     log.info("Exit")
     record(pnl=context.portfolio.portfolio_value /
            context.enter_cash - 1)
    
There was a runtime error.