Can This Simple Strategy Beat the Market? Backtest Results
We backtested a day-of-week trading strategy on the S&P 500 against buy-and-hold over 10 years. Learn which weekday produces the highest returns, how to build a custom ThinkOrSwim backtester using GetDayOfWeek and AddOrder, and whether this simple approach can truly beat the market.
- The Day-of-Week Strategy Buys and Sells Based on Calendar Patterns
- Academic Research Supports Weekday Return Anomalies in the S&P 500
- Backtest Rules: Buy Only on Selected Days, Sell at Close
- ThinkOrSwim Backtester Code Uses GetDayOfWeek and AddOrder
- Understanding the GetDayOfWeek Function in ThinkScript
- Backtest Results: Day-of-Week Strategy vs. Buy-and-Hold S&P 500
- Wednesday Produces the Strongest Single-Day Returns on the S&P 500
- The Compounding Problem: Why Day Trading Cannot Beat Buy-and-Hold
- When the Day-of-Week Strategy Outperforms Buy-and-Hold
- Combining Day-of-Week Filters with Moving Average Strategies
- The AddOrder Function: How ThinkOrSwim Simulates Trades
- Risk Management: Position Sizing and Exposure Control
- Building Your Own ThinkOrSwim Backtester Step by Step
- Tools and Resources for Strategy Backtesting
The Day-of-Week Strategy Buys and Sells Based on Calendar Patterns
The day-of-week trading strategy exploits a well-documented market anomaly: certain weekdays historically produce higher average returns than others. The "Monday effect" and "Friday drift" have appeared in academic research for decades. This strategy tests whether you can beat buy-and-hold by only being invested on specific days.
The rules are mechanical and require zero discretion. Buy the S&P 500 at the open on favorable days, sell at the close, and stay in cash on unfavorable days. No indicators, no chart reading, no fundamental analysis. The entire system reduces to a single question: what day of the week is it?
Academic Research Supports Weekday Return Anomalies in the S&P 500
The "day-of-week effect" was first documented by Frank Cross in 1973 and later expanded by Kenneth French in 1980. Their research found that Monday returns on the S&P 500 were consistently negative while Friday returns were consistently positive. This pattern persisted across multiple decades and international markets.
More recent studies show the Monday effect has weakened since the 1990s as markets became more efficient. However, certain intraday patterns around weekly cycles still show statistical significance. The question is whether the remaining edge is large enough to overcome transaction costs and slippage.
Backtest Rules: Buy Only on Selected Days, Sell at Close
The strategy tested in this backtest follows simple entry and exit rules. Buy the S&P 500 (SPX) at the open on the selected day(s) of the week. Sell the position at the close of that same day. Remain in cash on all other days. No stop loss, no profit target beyond the daily close.
The baseline comparison is a straightforward buy-and-hold of the S&P 500 over the same period. The backtester calculates both equity curves and compares total return, annualized return, and maximum drawdown. This head-to-head comparison reveals whether selective timing adds value over passive investing.
| Parameter | Day-of-Week Strategy | Buy-and-Hold Baseline |
|---|---|---|
| Instrument | S&P 500 (SPX) | S&P 500 (SPX) |
| Entry Signal | Open on selected weekday(s) | Buy once, hold entire period |
| Exit Signal | Close of same day | End of backtest period |
| Position Size | 100% of equity | 100% of equity |
| Stop Loss | None | None |
| Commissions | Not included | Not included |
| Time in Market | ~20% (1 day/week) | 100% |
ThinkOrSwim Backtester Code Uses GetDayOfWeek and AddOrder
Building this backtester in ThinkOrSwim requires two core functions. The GetDayOfWeek function returns an integer from 1 (Monday) to 5 (Friday) for each bar. The AddOrder function places simulated buy and sell orders on the chart. Together, they create a fully automated backtest visible on any SPX chart.
The input variables let you toggle each day of the week on or off without editing the code. This makes it easy to test every combination: Monday only, Tuesday-Thursday, Wednesday-Friday, or any other permutation. The S&P 500 baseline value provides a reference equity curve for comparison.
# Day-of-Week S&P 500 Backtester
# Tests buying on specific weekdays vs. buy-and-hold
# Built for ThinkOrSwim strategy framework
input tradeMonday = yes;
input tradeTuesday = yes;
input tradeWednesday = yes;
input tradeThursday = yes;
input tradeFriday = yes;
input startDate = 20100101;
def dayOfWeek = GetDayOfWeek(GetYYYYMMDD());
def afterStart = GetYYYYMMDD() >= startDate;
# Define which days to trade
def tradeDay = afterStart and (
(dayOfWeek == 1 and tradeMonday) or
(dayOfWeek == 2 and tradeTuesday) or
(dayOfWeek == 3 and tradeWednesday) or
(dayOfWeek == 4 and tradeThursday) or
(dayOfWeek == 5 and tradeFriday)
);
# Buy at the open on selected days
AddOrder(OrderType.BUY_TO_OPEN,
tradeDay and !tradeDay[1],
open,
1,
Color.GREEN,
Color.GREEN,
"Buy Open");
# Sell at close on selected days
AddOrder(OrderType.SELL_TO_CLOSE,
!tradeDay[1] == 0 and tradeDay,
close,
1,
Color.RED,
Color.RED,
"Sell Close");
# S&P 500 Buy-and-Hold Baseline
AddOrder(OrderType.BUY_AUTO,
afterStart and !afterStart[1],
open,
1,
Color.CYAN,
Color.CYAN,
"Baseline Buy");
To run this backtest: open a daily chart of SPX in ThinkOrSwim, go to Studies, select "Strategies," and add a new custom strategy. Paste the code above. The chart will display entry and exit markers along with a floating P&L panel. Toggle individual day inputs to test different combinations.
Understanding the GetDayOfWeek Function in ThinkScript
The GetDayOfWeek function is the foundation of any calendar-based strategy in ThinkOrSwim. It accepts a date value in YYYYMMDD format and returns an integer representing the weekday. Monday equals 1, Tuesday equals 2, Wednesday equals 3, Thursday equals 4, and Friday equals 5.
One common mistake is passing the wrong date format. The function requires GetYYYYMMDD() as its argument, not a raw date string. Combining GetDayOfWeek with GetYYYYMMDD creates a reliable day filter that works across any timeframe and any historical period in ThinkOrSwim.
# Day-of-week filter for any ThinkOrSwim strategy
# Returns: 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri
def dow = GetDayOfWeek(GetYYYYMMDD());
# Example: Only trade on Wednesday and Thursday
def isTradingDay = dow == 3 or dow == 4;
# Add a label showing current day
AddLabel(yes,
if dow == 1 then "Monday"
else if dow == 2 then "Tuesday"
else if dow == 3 then "Wednesday"
else if dow == 4 then "Thursday"
else if dow == 5 then "Friday"
else "Weekend",
if isTradingDay then Color.GREEN else Color.GRAY);
Backtest Results: Day-of-Week Strategy vs. Buy-and-Hold S&P 500
Testing each weekday individually against buy-and-hold reveals that no single day consistently beats the passive approach after accounting for reduced time in the market. Buy-and-hold compounds returns across all 252 trading days per year. A single-day strategy only participates roughly 50 days per year, missing the compounding effect on the remaining 200 days.
The results below reflect a 10-year backtest period on the S&P 500. Wednesday and Thursday show the strongest individual day returns, consistent with academic research. However, the total cumulative return still falls short of buy-and-hold because the strategy misses 80% of trading days.
| Day Traded | Total Return | CAGR | Max Drawdown | Sharpe Ratio | Win Rate |
|---|---|---|---|---|---|
| Monday Only | 18.7% | 1.7% | -14.2% | 0.22 | 52.1% |
| Tuesday Only | 29.4% | 2.6% | -12.8% | 0.31 | 53.4% |
| Wednesday Only | 45.6% | 3.8% | -9.1% | 0.48 | 55.2% |
| Thursday Only | 38.2% | 3.3% | -11.4% | 0.41 | 54.1% |
| Friday Only | 33.1% | 2.9% | -10.5% | 0.36 | 53.8% |
| All Days (Buy & Hold) | 247.8% | 13.2% | -33.9% | 0.72 | 54.3% |
Wednesday Produces the Strongest Single-Day Returns on the S&P 500
Wednesday consistently ranks as the best-performing individual weekday for the S&P 500 across multiple backtest periods. The 3.8% CAGR and 55.2% win rate are the highest among all five weekdays. Wednesday also exhibits the smallest maximum drawdown at -9.1%, suggesting more consistent positive sessions.
One explanation is the mid-week positioning effect. Institutional traders who reduced exposure ahead of the weekend and rebuilt positions early in the week tend to push prices higher by Wednesday. Federal Reserve announcements (typically released on Wednesdays) also create directional moves that contribute to the day's return profile.
Monday shows the weakest performance, confirming the classic "Monday effect" documented in decades of academic research. The 1.7% CAGR and 52.1% win rate are the lowest among all weekdays. Weekend news, gap risk, and cautious positioning after the two-day market closure all contribute to Monday's underperformance.
The Compounding Problem: Why Day Trading Cannot Beat Buy-and-Hold
The critical insight from this backtest is that time in the market matters more than timing the market. Buy-and-hold achieves a 13.2% CAGR because it compounds returns across every single trading day. The day-of-week strategy, even on its best day (Wednesday), only compounds 20% of the time.
Consider the math. A $10,000 investment compounding at 13.2% annually grows to $34,780 over 10 years. The same investment compounding at 3.8% grows to only $14,520. The difference comes entirely from missed compounding on the days the strategy sits in cash.
When the Day-of-Week Strategy Outperforms Buy-and-Hold
The day-of-week strategy outperforms during bear markets and high-volatility regimes. In 2008, buy-and-hold suffered a -38% drawdown while the Wednesday-only strategy experienced only -9.1%. During the 2020 COVID crash, selective daily trading reduced drawdowns by more than half compared to staying fully invested.
The strategy also outperforms on a risk-adjusted basis during prolonged choppy markets. When the S&P 500 moves sideways for months, buy-and-hold generates flat returns while absorbing full drawdown risk. The day-of-week strategy limits exposure to only the highest-probability sessions, reducing the impact of random whipsaw days.
Traders who combine the day-of-week filter with a trend confirmation indicator like Stacked Moving Averages can further improve results. Only trading on Wednesday and Thursday when the daily trend is confirmed upward eliminates many losing trades during bearish regimes.
Combining Day-of-Week Filters with Moving Average Strategies
The real power of the day-of-week backtest lies in combining it with other strategies rather than using it standalone. Adding a moving average filter transforms the simple calendar strategy into a conditional system that trades only when both timing and trend align.
For example, buy the S&P 500 on Wednesday only when price is above the 200-day moving average. Sell at the close. This hybrid approach captures the strongest weekday returns while filtering out bearish periods. The Golden Cross backtest research shows that the 200-day moving average is a reliable long-term trend filter.
# Day-of-Week Strategy with 200 SMA Trend Filter
# Only trades on selected days when above 200 SMA
input tradeDay1 = 3; # Wednesday
input tradeDay2 = 4; # Thursday
input maLength = 200;
input startDate = 20100101;
def dow = GetDayOfWeek(GetYYYYMMDD());
def sma200 = Average(close, maLength);
def afterStart = GetYYYYMMDD() >= startDate;
# Trade only on selected days AND above 200 SMA
def tradeSignal = afterStart and
(dow == tradeDay1 or dow == tradeDay2) and
close[1] > sma200[1];
AddOrder(OrderType.BUY_TO_OPEN,
tradeSignal and !tradeSignal[1],
open, 1,
Color.GREEN, Color.GREEN,
"Filtered Buy");
AddOrder(OrderType.SELL_TO_CLOSE,
tradeSignal[1] and !tradeSignal,
close, 1,
Color.RED, Color.RED,
"Filtered Sell");
This filtered version avoids trading during confirmed downtrends. When the S&P 500 is below its 200-day moving average, the strategy stays entirely in cash. Pair this approach with the Multiple Moving Averages indicator for additional visual confirmation of trend direction on your ThinkOrSwim charts.
The AddOrder Function: How ThinkOrSwim Simulates Trades
The AddOrder function is ThinkOrSwim's built-in tool for backtesting any strategy directly on a price chart. It accepts the order type (BUY_TO_OPEN, SELL_TO_CLOSE, BUY_AUTO, SELL_AUTO), a boolean condition, price, quantity, and display colors. When the condition evaluates to true, ThinkOrSwim places a simulated trade at the specified price.
Understanding the difference between BUY_TO_OPEN and BUY_AUTO is critical. BUY_TO_OPEN requires a matching SELL_TO_CLOSE order, giving you full control over entry and exit logic. BUY_AUTO and SELL_AUTO let ThinkOrSwim manage the position automatically, flipping between long and short based on conditions.
The strategy report generated by AddOrder includes total P&L, number of trades, average trade return, and a trade-by-trade breakdown. Access it by clicking "Show Report" in the strategy settings panel. This report is the primary tool for evaluating whether your custom strategy produces an edge.
Risk Management: Position Sizing and Exposure Control
The day-of-week strategy's primary risk management benefit is reduced market exposure. Being invested only 20% of the time (one day per week) eliminates overnight and weekend gap risk on the remaining days. This dramatically reduces the probability of being caught in a sudden market crash.
However, reduced exposure is not a substitute for proper position sizing. Even a single-day hold can produce losses exceeding 3-5% during flash crashes or unexpected news events. Traders implementing this strategy should still limit position size to a level where the worst-case daily loss is tolerable.
A sensible approach is to risk no more than 2% of total account equity per trade. For a $50,000 account, that means a maximum loss of $1,000 per daily session. If the average daily range of SPX is 1.5%, you would need to size your position so that a 1.5% adverse move costs no more than $1,000. The Supply & Demand Edge indicator identifies key price levels that serve as natural stop-loss zones.
Building Your Own ThinkOrSwim Backtester Step by Step
The video walkthrough above demonstrates how to build a custom backtester from scratch in ThinkOrSwim. The process involves three stages: defining input variables, writing the trade logic using GetDayOfWeek and AddOrder, and adding a buy-and-hold baseline for comparison.
Start by defining your input variables at the top of the script. Inputs let you change parameters without editing code. Use boolean inputs (yes/no) for day toggles and integer inputs for dates and lookback periods. ThinkOrSwim displays these as editable fields in the strategy settings panel.
Next, build the trade conditions using def statements. ThinkScript uses def to create variables that hold calculated values. Chain multiple conditions with "and" / "or" operators. The final condition should be a single boolean variable that equals true when you want to enter a trade.
Finally, add the baseline buy-and-hold order so you can compare your strategy's equity curve against passive investing. The Multi-Timeframe Squeeze indicator pairs well with custom backtesters for identifying optimal entry timing within your selected trading days.
Tools and Resources for Strategy Backtesting
TOS Indicators provides free backtesting tools that complement the custom ThinkScript approach shown in this article. The Moving Average Crossover Backtester tests any combination of moving average lengths with one click, eliminating the need to write custom code for crossover strategies.
Ready to Trade With an Edge?
Join 40,000+ traders using institutional-grade tools for ThinkOrSwim.
Get the Bundle