Intraday Momentum -- Researched based strategy deep dive, part 1
Available to all readers. I create a rapid EOD model to test whether or not it is worth our time to look into this strategy. The daily strategy I created has a profit factor of 1.26 and a 1.13 Sharpe.
Disclaimer: the following post is an organized representation of my research and project notes. It doesn’t represent any type of advice, financial or otherwise. Its purpose is to be informative and educational. Backtest results are based on historical data, not real-time data. There is no guarantee that these hypothetical results will continue in the future. Day trading is extremely risky, and I do not suggest running any of these strategies live.
This strategy is based on the research found in references Maróy's1 & Zarattini's2. Both of them discuss the same strategy, but reference 1 is the newer paper that attempts to expand on and improve the original strategy. The purpose of this test is to see if there is an efficacy to the strategy idea. Maróy's paper uses several different methods to improve on the original paper, but it is highly optimized and only has results on 1 instrument in a short time period for back testing results. With that in mind, my goal is to recreate the logic from the papers and then see if we can make it more robust and applicable to more instruments.
Before testing this logic on the 1-minute or 1-second chart (used in Maróy's paper), I want to do a fast and dirty check of the logic using a daily historical chart. The purpose is to create a simple daily strategy that uses the same concepts from the paper to see if there is any reason to continue down to intraday time periods for further investigation.
So, let's summarize the main points of the paper and create a strategy in RealTest (RT) that has similar logic. We will use QQQ for this test, since that is the test the most recent paper decided to focus on. I will use historical returns to create the volatility bands and then set the strategy to take positions only if a bar crosses the upper or lower band. I will also set the strategy use the actual fill price of the trade to place our limit exit orders. This will ensure that orders look more like what you would see with a true intraday strategy.
Setting QtyPrice: FillPrice
technically introduces a "lookahead bias" in RT. Since RT operates on EOD data, and doesn't handle true streaming data throughout the day, it has to “look forward” to test this type of system. That means that this version has some inherent problems, if you were to try and use this strategy to actually auto trade a strategy. It can't know what price the instrument is going to open at so it can't generate accurate child/bracket orders if you try and use this strategy as is. That is perfectly fine for what I am doing with this strategy (and the reason it's freely available to all readers), but it should not be traded exactly how it is. At least, it shouldn't be traded with RT as it is.
Here's a quick breakdown of the logic I am applying for this test:
Instrument being tested is QQQ
Dates match the paper: 10/01/2014 to 10/01/2025
Allow optimizable parameters for volatility multipliers and lookback periods separately for long and short strategies.
Calculate historical volatility bands.
This is where I do something a little different. Since the paper uses an intraday chart to calculated historical volatility by time of day, I can't match that logic. Instead, I just measure volatility with historical daily returns.
I also use logarithmic returns here, instead of simple returns. When I recreate the intraday version, I will use both methods to see if we can make this strategy more robust. In our case here, since this is just a rapid idea test, using logarithmic returns should be fine. Feel free to experiment with simple returns if that is more your vibe.
I use a regular standard deviation calculation along with the volatility multipliers to create the upper and lower bands.
Use target volatility and historical volatility to calculate dynamic entry sizes.
Set a simple drawdown-based stop for the strategy.
Set exit targets based on percent gain and take a partial position exit at target. Else, hold the position until close.
RealTest, EOD Test Code:
Notes:
Based on a research paper.
Import: // requires Norgate Platinum subscription
DataSource: Norgate
IncludeList: QQQ
IncludeList: SPY
StartDate: 1/1/2014
EndDate: Latest
SaveAs: 27_QQQ.rtd
Settings:
DataFile: 27_QQQ.rtd
StartDate: 10/01/2014
EndDate: 10/01/2024
BarSize: Daily
AccountSize: 1e5
HolidayList: us_auto
Parameters:
lookback_long: from 2 to 14 step 1 def 9 // 2 - 14 days are most effective according to paper
lookback_short: from 2 to 14 step 1 def 6
volatility_mult_long: from 0.5 to 1.5 step 0.1 def 1
volatility_mult_short: from 0.5 to 1.5 step 0.1 def 1.3
target_volatility: 0.01 // Target daily volatility (1%)
risk: 0.2
Data:
log_returns: Log(Close / Close[1])
// Long indicators
historical_volatility_long: StdDev(log_returns, lookback_long)
upper_noise_band_long: C * Exp(volatility_mult_long * historical_volatility_long)
lower_noise_band_long: C * Exp(-volatility_mult_long * historical_volatility_long)
// Short indicators
historical_volatility_short: StdDev(log_returns, lookback_short)
upper_noise_band_short: C * Exp(volatility_mult_short * historical_volatility_short)
lower_noise_band_short: C * Exp(-volatility_mult_short * historical_volatility_short)
Library:
dynamic_position_size_long: (S.Equity * Min(1, target_volatility / historical_volatility_long)) / Close
dynamic_position_size_short: (S.Equity * Min(1, target_volatility / historical_volatility_short)) / Close
drawdown_pct: (S.MaxEquity - S.Equity) / S.MaxEquity
Charts:
upperNB: {} upper_noise_band_long[1]
lowerNB: {} lower_noise_band_long[1]
Benchmark: buy_and_hold
Side: Long
EntrySetup: Symbol = $SPY
ExitRule: Dividend // reinvest
Strategy: strategy_x_long
Allocation: S.Equity
QtyType: Shares
// This changes the exit limits and stops to "look ahead" and simulates the orders based on entry price instead of the predetermined entry order price.
// This is for when price opens above the entry price. RT assumes entry on open if entry stop/limit is above/below the order price.
// This behavior will cause some orders to exit immediately, if price gapped up/down far enough to have gone past the exit order price.
QtyPrice: FillPrice
Commission: Min(0.01 * FillValue, Max(0.005 * Shares, 1))
Side: Long
Quantity: dynamic_position_size_long
EntrySetup: InList(2) and drawdown_pct <= risk
EntryStop: upper_noise_band_long
ExitLimit: FillPrice * 1.01
ExitLimitQty: Shares * 0.75
ExitTime: ThisClose
ExitRule: Select(BarsHeld == 0, "EOD Exit")
Strategy: strategy_x_short
Allocation: S.Equity
QtyType: Shares
// QtyPrice changes the exit limits and stops to "look ahead" and simulates the orders based on entry price instead of the predetermined entry order price.
// This is for when price opens above the entry price. RT assumes entry on open if entry stop/limit is above/below the order price.
// This behavior will cause some orders to exit immediately, if price gapped up/down far enough to have gone past the exit order price.
QtyPrice: FillPrice
Commission: Min(0.01 * FillValue, Max(0.005 * Shares, 1))
Side: Short
Quantity: dynamic_position_size_short
EntrySetup: InList(2) and drawdown_pct <= risk
EntryStop: lower_noise_band_short
ExitLimit: FillPrice * 0.98
ExitLimitQty: Shares * 0.75
ExitTime: ThisClose
ExitRule: Select(BarsHeld == 0, "EOD Exit")
Results:
The results aren't terrible, right? Test 2 shows the results for the strategy if we remove the exit logic and limits. There isn't much of a difference, but it does help the drawdown to simulate taking partial profits intraday.
What happens next?
I am going to move over to Sierra Chart and turn this into a true intraday strategy. First, I will create the indicator (gotta do a little C++ judo to set it up right). Then, I will create the actual strategy from the research papers we are looking at and see if I can get close to the same results.
And before I talk about what comes after that, I want to point out a few things about these papers. As far as I can tell, neither of them has been published in a traditional peer-reviewed journal. Maróy's paper doesn't have any academic affiliation (just the author's personal e-mail) and Zarattini's is published in a journal, but not a traditional peer-reviewed journal. The earlier paper also has affiliations with private trading and research firms. These facts already give me pause, but it doesn't mean there isn't some sort of edge here.
I think it's also important to point the very obvious problems that we know are present in the logic being presented to us in these papers. First, the strategies only focus on 1 instrument. This means they can easily be overfit. Second, the most recent paper uses a large amount of optimization to get the results it is presenting. It even points out that the results for the strategy are better on QQQ, so they are only focusing on QQQ and not SPY, like the original paper. That is basically saying (in the paper, none the less) that this strategy is overfit to a specific market/instrument.
We don't need to reproduce the strategy to know that it has some shortcomings. The papers practically admit their own faults while presenting their findings. Oddly enough, I don't believe that any of this means that you couldn't use this strategy to make money. I think it needs some real work, to go into the portfolio, but I am hopeful that it's possible.
Conclusion
Thanks for reading and hopefully you are excited about seeing what this strategy turns into in the end. The next post will cover creating the intraday volatility bands indicator. After that, we will dive into creating trying to recreate the strategy results from the paper. Finally, we will end with my attempt at updating the strategy so that it is more reliable. Ideally, I would like to be able to use something like this with ES. The idea is sound, I think.
Anyway, until the next one...
Happy hunting!
Feel free to comment below or e-mail me if you need help with anything, wish to criticize, or have thoughts on improvements. You can also DM me or start a chat thread. Red Team members can access this code and more at the private HGT GitHub repo. As always, this newsletter represents refined versions of my research notes. That means these notes are plastic. There could be mistakes or better ways to accomplish what I am trying to do. Nothing is perfect, and I always look for ways to improve my techniques.
References:
Maróy, Á. (2025). Improvements to intraday momentum strategies using parameter optimization and different exit strategies. Swiss Finance Institute Research Paper Series, No. 24-97. https://ssrn.com/abstract=5095349
Zarattini, C., Aziz, A., & Barbon, A. (2024). Beat the market: An effective intraday momentum strategy for S&P500 ETF (SPY). SSRN Electronic Journal. https://doi.org/10.2139/ssrn.4824172