- Build a Seasonal Analysis Indicator for ThinkOrSwim
- Why Seasonal Patterns Matter
- Understanding the Seasonal Calculation Logic
- Step-by-Step Implementation
- Real-World Seasonal Patterns
- Examples from Our Video Analysis
- Using Your Seasonal Analysis Data
- Combining with Other Analysis
- Advanced Applications
- Common Implementation Mistakes
- Building Your Monthly Watchlist
Build Your Own Seasonal Analysis System in ThinkOrSwim
Learn how to build a custom ThinkOrSwim indicator that automatically calculates and displays historical monthly performance data for any stock, ETF, or index.
This indicator provides:
- Win rates for each month based on 20+ years of data
- Average monthly returns with percentage calculations
- Color-coded labels highlighting strongest and weakest months
- Real-time updates as new data becomes available
Perfect for identifying seasonal patterns like September’s historically weak performance or November’s strong tendencies in major indices.
Build a Seasonal Analysis Indicator for ThinkOrSwim
Seasonal patterns in the stock market are documented and measurable. September has been the worst month for the S&P 500 since 1928, averaging a -1.1% decline, while April shows positive returns 89% of the time over the past 18 years. Rather than manually researching these patterns for every stock you trade, our custom Seasonal Analysis indicator automatically calculates 20+ years of monthly performance data and displays it directly on your ThinkOrSwim charts.
Why Seasonal Patterns Matter
Seasonal effects persist because of predictable institutional behaviors. The Stock Trader’s Almanac documents that the “Best Six Months” (November-April) have outperformed May-October by significant margins since 1950. These aren’t random occurrences—they reflect systematic behavioral patterns that create trading opportunities.
Consider these documented patterns:
September Effect: September has been negative for the S&P 500 with just a 44.2% win rate since 1928, making it the only month with consistently negative returns. This occurs because institutional traders return from summer vacations and process accumulated negative news.
April Reliability: April shows remarkable consistency with positive returns in 16 of the past 18 years. Technology stocks often benefit from Q1 earnings optimism during this period.
Year-End Rally: The Santa Claus Rally occurs during the last five trading days of December plus the first two of January, averaging 1.3% gains since 1950.
Understanding the Seasonal Calculation Logic
Our indicator tracks monthly performance using open-to-close calculations across all available historical data. For each month, it calculates:
Win Rate: The percentage of years that month produced positive returns. A 70% win rate means 7 out of 10 years showed gains.
Average Return: The mean percentage return across all years, accounting for both winning and losing periods.
Sample Size: The total number of monthly data points, ensuring statistical relevance.
The code uses ThinkScript’s `getMonth()` function to identify historical periods and `TotalSum()` to aggregate performance data across multiple years.
Step-by-Step Implementation
Let’s build this indicator from scratch, starting with the basic framework and then adding each monthly calculation. This tutorial follows the same approach shown in our comprehensive video “Build a Seasonal Analysis Indicator for ThinkOrSwim in Under 25 Minutes”.
Step 1: Chart Requirements
First, we ensure the indicator only works on appropriate timeframes:
Assert(getAggregationPeriod() >= AggregationPeriod.Day,
"Seasonal Analysis Indicator only works on daily time frame and higher charts.");
This prevents users from applying the indicator to intraday charts where monthly analysis wouldn’t make sense.
Step 2: January Analysis Framework
We’ll build the January logic first, then replicate it for other months:
# January Analysis
def jan = getMonth() == 1;
def janOpen = if jan and !jan[1] then open else janOpen[1];
def janClose = if jan and !jan[-1] then close else janClose[1];
def janDiff = if jan and !jan[-1] and janOpen > 0 then (janClose-janOpen)/janOpen else 0;
This logic:
- Identifies all January bars with
getMonth() == 1 - Captures the first January opening price
- Captures the last January closing price
- Calculates the percentage change for that January
Step 3: Statistical Calculations
Next, we count winning and losing Januaries:
def janUp = TotalSum(if jan and !jan[-1] and janDiff > 0 then 1 else 0);
def janDown = TotalSum(if jan and !jan[-1] and janDiff <= 0 then 1 else 0);
def janTotalCount = TotalSum(jan and !jan[-1]);
def janAvgDiff = TotalSum(janDiff)/janTotalCount;
This creates running totals for positive months, negative months, total samples, and average returns.
Step 4: Visual Display
We display results with color-coded labels:
AddLabel(yes, "Jan: " + AsPercent(janUp/janTotalCount) + " | " + AsPercent(janAvgDiff),
if(janUp/janTotalCount) > 0.5 and janAvgDiff > 0 then color.green else color.yellow);
Green labels indicate months with both above-50% win rates AND positive average returns. Yellow indicates weaker performance.
Step 5: Complete All Months
The same pattern repeats for February through December, changing only the month number in `getMonth() == [number]`.
Real-World Seasonal Patterns
Based on our analysis of historical data, here are the patterns you'll typically see:
Strongest Months: April (historically positive 70%+ of the time), November (strong year-end positioning), and December (institutional rebalancing).
Weakest Months: September (only month with negative average returns), February (post-January momentum fade), and June (summer doldrums begin).
Sector Variations: Retail stocks show November-December strength, energy stocks often perform better in winter months, and agricultural stocks follow crop cycle patterns.
Examples from Our Video Analysis
In our video analysis of market indices, we've documented specific seasonal behaviors. Our "S&P 500 November Seasonality Patterns in Bear Markets" research shows that November's typical 80-90% bullish success rate drops to just 50% during bear markets.
Our analysis in the "ThinkOrSwim Earnings Scan With Built-in Seasonality Analysis" video demonstrates how to combine earnings calendars with seasonal patterns for high-probability setups.
We've also analyzed specific stocks showing pronounced seasonal effects, including agricultural stocks that follow planting and harvest cycles, and energy stocks with winter heating demand patterns.
Using Your Seasonal Analysis Data
This data works best as a probability guide, not a guarantee. Here's how to apply it:
Position Sizing: Consider reducing position sizes during historically weak months for specific holdings. If a stock shows consistent September weakness, you might trim positions heading into September.
Entry Timing: Look for opportunities during historically weak months of strong stocks. A quality stock showing weakness during its typical weak season may present buying opportunities.
Options Strategies: Time options strategies around seasonal patterns. Consider protective puts before weak seasons or calls before historically strong periods.
Combining with Other Analysis
Seasonal patterns work best combined with:
Technical Analysis: Wait for technical confirmation before acting on seasonal patterns. A stock in a strong uptrend during its weak season may continue higher.
Fundamental Analysis: Company-specific events can override seasonal patterns. Earnings, product launches, or management changes may be more significant than historical seasonality.
Market Context: Overall market conditions matter. Strong bull markets can override individual seasonal weakness, while bear markets may extend beyond typical seasonal boundaries.
Advanced Applications
Sector Rotation: Build sector-specific seasonal calendars. Technology might show Q4 strength while utilities show summer weakness patterns.
International Markets: Different markets have different seasonal patterns. European markets may show strength during different months than U.S. markets.
Combined Analysis: Layer seasonal analysis with our Market Pulse indicator and Squeeze Course tools for comprehensive trade setups.
Common Implementation Mistakes
Over-Reliance on Patterns: Seasonal data shows tendencies, not certainties. Market conditions, economic events, and company-specific news can override seasonal effects.
Ignoring Sample Size: Newer stocks or ETFs may not have enough historical data for reliable seasonal analysis. Look for at least 15-20 years of data for meaningful patterns.
Market Timing Attempts: Don't try to time entire portfolios around seasonal patterns. Use this data to inform position sizing and risk management, not wholesale market timing.
Building Your Monthly Watchlist
Use the seasonal data to build focused monthly watchlists. Our "ThinkOrSwim Earnings Scan With Built-in Seasonality Analysis" video shows how to combine earnings calendars with seasonal patterns for high-probability setups.
This systematic approach lets you identify stocks entering their historically strong periods while avoiding those entering weak phases.
#Seasonal Analysis Indicator
#Written by TOS Indicators
#Sample Month: December
#December
def dec = getMonth() == 12;
// ... 9 more lines ...Here are some resources that you may find useful: