Skip to content

Add S&P500 ranking for Jegadeesh and Titman’s seminal paper#1000

Open
bennycode wants to merge 19 commits intomainfrom
sp500-ranking
Open

Add S&P500 ranking for Jegadeesh and Titman’s seminal paper#1000
bennycode wants to merge 19 commits intomainfrom
sp500-ranking

Conversation

@bennycode
Copy link
Owner

No description provided.

@bennycode
Copy link
Owner Author

@bennycode
Copy link
Owner Author

The strategy actually works better when you exclude the last month of returns from your formation period, because there's a short-term reversal effect at the 1-month horizon. So ideally you'd be looking at winners from roughly 12 months ago to 1 month ago, not the very latest movers.

@bennycode
Copy link
Owner Author

Limiting loss:
The most straightforward approach is a stop-loss. For a momentum strategy, common methods include setting a stop at a fixed percentage below your entry (say 7-10% for individual stocks), or using a trailing stop that moves up with the price but never down. A trailing stop is particularly well-suited to momentum because the whole thesis is that winners keep winning — if the trend breaks, you want out.

You could also use a time-based exit: if after a set period (3 month) the stock hasn't performed, you cut it regardless. This avoids the slow bleed of a winner that stops winning but doesn't quite hit your stop.

For a momentum strategy with a 6-month holding period, something more appropriate might be the daily 20 or 50 EMA. You'd wait for a pullback to that moving average and enter on the bounce, which gives you a better risk-reward. Your stop is just below the EMA, and your upside is the continuation of the trend. This is sometimes called "buying the dip within an uptrend."

A practical entry approach could look like this: identify your momentum winners from the 6-month screen, then on the daily chart wait for price to pull back to the 20-day EMA, confirm it holds (a daily close above it), and enter there with a stop just below the 50-day EMA. That gives you a defined risk level and you're entering at a discount rather than chasing.

@bennycode
Copy link
Owner Author

The flow would be: screen for 6-month momentum winners (outside the library) → feed daily candle data into EMA(20), EMA(50), RSI(14), MACD, ATR(14), and BBANDS → enter when EMA(20) > EMA(50) AND RSI bounces from 40-50 range AND MACD is bullish → set stop at entry minus 2x ATR → exit when EMA(20) crosses below EMA(50) OR price breaks below Bollinger middle band OR your 6-month holding period expires.

Core trend confirmation — EMA crossover:
Use a 20-day EMA and 50-day EMA (or their equivalents in your candle timeframe). When the 20 crosses above the 50, you have trend confirmation that the momentum is intact. This is your primary entry signal. The library's EMA class handles this with streaming updates, so you can feed it price data in real time.
Entry timing — RSI for pullback detection:
Use the RSI (14-period on daily candles) to identify when a momentum stock pulls back without breaking trend. You're looking for RSI dipping to the 40-50 range and then turning back up — that's the "buying the dip within an uptrend" we discussed. You don't want to buy when RSI is above 70 (overbought and likely to stall).

Trend strength — MACD:
The MACD gives you signal line crossovers and histogram direction. Use it as a confirmation filter: only enter when MACD histogram is positive or turning positive. The library's MACD returns bullish/bearish signals via getSignal(), which makes this straightforward to automate.

Stop-loss management — ATR:
This is the key risk management piece. Use ATR (Average True Range, 14-period) to set dynamic stop-losses based on actual volatility rather than arbitrary percentages. A common approach is setting your stop at 2x ATR below your entry price. If ATR is $5, your stop is $10 below entry. This adapts automatically — volatile stocks get wider stops, calm ones get tighter stops.

Bollinger Bands for exit signals:
BBANDS can help you identify when a momentum stock is getting overextended. When price consistently rides the upper band, that's healthy momentum. When it starts falling back inside and breaks the middle band (the 20-period SMA), it's a warning sign to tighten your stop or exit.

@bennycode
Copy link
Owner Author

Introduce a /report command to fetch reports in a specified interval.

@bennycode
Copy link
Owner Author

bennycode commented Mar 13, 2026

So the simplified system is:
Screen for 6-month winners → wait for EMA(20, daily candles) > EMA(50, daily candles) → check RSI(14, daily candles) isn't above 70 → enter → set trailing stop at 2x ATR (last 14 trading days) → exit when EMA(20, daily candles) crosses below EMA(50, daily candles) or stop gets hit → rotate after 6 months.

Replace --cron flag with --every flag using ms-compatible intervals
(1m, 1h, 1d, 1w, etc.) for consistency with strategy candle intervals.
Drop node-cron dependency in favor of setInterval.
Reports without --every now execute immediately and return results
directly. No DB entry is created for one-shot reports. The reportRun
command is removed as it is no longer needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant