How to model Mark Minervini's SEPA with Amibroker and Norgate Platinum

Hello all,

Thought I'd share my latest exploit. I've been beta testing @NorgateData new platform for a while now, and recently subscribed to the platinum package. Upon reading about everything it offers, I realized a lot of the fundamental data can help me partially model Mark Minervini's Specific Entry Point Analysis (SEPA) explained in his first book, "Trade Like a Stock Market Wizard". Mark's strategy seems to veer towards swing trading, but also seems to have its lineage in CANSLIM as well. I won't get too into the book's details, as you probably all should read it (and his second book!), but SEPA has five main points:

  1. Trend -> The trend template he explains is modeled
  2. Fundamentals -> Modeled
  3. Catalyst -> More news based so cannot mdel
  4. Entry Points -> Modeling his volatility contraction pattern is next on my list
  5. Exit Points -> next on my list

This script makes heavy use of Norgate Fundamental data using their custom function "NorgateFundamentals()", native function GetFnData() and some of my own functions, one of which I explained in my other post on IBD relative strength score. Norgate has been really great, I really wish they offered EOD and fundamental data for more countries, especially Canada!

Function modeling the trend template:

function Trending(Carray, Harray, Larray)
	// 52 week high/low check, within 25%
	W52wkHigh = Carray > 0.75 * HHV(Harray, 52 * 5) AND Carray > LLV(Larray, 52*5)*1.30;

	// Check trend using SMAs
	SMA050 = MA(Carray, 050);
	SMA150 = MA(Carray, 150);
	SMA200 = MA(Carray, 200);

	UnwrappedSMA = Carray > SMA050 AND SMA050 > SMA150 AND SMA150 > SMA200;

	UpTrend = SMA200 > Ref(SMA200, -22);

	Stage2 = W52wkHigh AND UnwrappedSMA AND UpTrend;
	return Stage2;

Only stocks that satisfy the above Trending criteria and has a relative strength of > 75 are considered in this exploration. To cover the fundamentals aspect of the SEPA strategy and also some other technical analysis aspects common to IBD style trading, I use a scoring system to rank stocks. Earnings and Sales get double value, while most get a single point. Some more esoteric points get only half score.

Below is the main exploration script:

// SEPA Screener Based on Minervini Books
#include "C:\Users\mamus\Dropbox\Michael\Documents\AmiBroker Files\In Test\Cassiopeia\"
#include_once "Formulas\Norgate Data\Norgate Data"

/// Points System
TotalFactors = 0;
Score        = 0;

// 1. IPO Date Filter
RecentIPO = BarCount < 252*10;
if (RecentIPO) 

// 2. Market Cap Filter
MarketCap = NorgateFundamentals("mktcap"); // in millions
GoodCaps  = MarketCap  < 1e4 AND MarketCap  > 1e2;
if (GoodCaps) Score++;

// 3. ROE Filter
ROE = GetFnData("ReturnOnEquity");
GoodROE   = ROE > 17;
if (GoodROE) Score++;

// 4. Sales QoQ
QRG = GetFnData("QtrlyRevenueGrowth");
GoodSales = QRG  > 20;
if (GoodSales) Score += 2;
TotalFactors += 2;

// 5. Good Earnings
QEG = GetFnData("QtrlyEarningsGrowth");
GoodEarns = QEG > 25;
if (GoodEarns) Score += 2;
TotalFactors += 2;

// 6. RS Line New High
RSL = RSLine(C);  
RSLNewHigh = LastValue(RSL) >= LastValue(HHV(RSL, 63));
if (RSLNewHigh) Score++;

// 7. RS Above 85
RS      = GetRS(Name());
GoodRS  = LastValue(RS) >= 85;
if (GoodRS) Score++;

// 8. Earnings Surprise
ES = NorgateFundamentals("epssurpriseqprc");
GoodES = ES > 20;
if (GoodES) Score++;

// 9. Annual Earnings
AE = NorgateFundamentals("ttmepschg");
GoodAE = AE > 20;
if (GoodAE) Score++;

// 10. Net Profit Margin
NPM = NorgateFundamentals("ttmnpmgn");
GoodNPM = NPM > 10;
if (GoodNPM) Score++;

// 11. Consensus growth rate
CGR = NorgateFundamentals("projltgrowthrate");
GoodCGR = CGR > 20;
if (GoodCGR) Score += 0.5;
TotalFactors += 0.5;

// 12. TTM Inventory Turnover
TIT = NorgateFundamentals("ttminvturn");
GoodTIT = TIT > 5;
if (GoodTIT) Score += 0.5;
TotalFactors += 0.5;

ScorePct  = Score / TotalFactors * 100;
Filter    = Trending(C, H, L) AND RS > 75 AND ScorePct > 50;

Ranking = ScorePct;

AddColumn(BarCount/252, "Years Public");
AddColumn(MarketCap,    "Market Cap");
AddColumn(ROE, "ROE");
AddColumn(QRG, "Qrtly Rev Gwth");
AddColumn(QEG, "Qtly Earn Gwth");
AddColumn(RSLNewHigh, "RSL New High");
AddColumn(RS, "RS");
AddColumn(ES, "Earn Surpr %");
AddColumn(AE, "TTM/TTM Earn %"); 
AddColumn(NPM, "Net Prof Mgn");
AddColumn(CGR, "Cons Gwth Rate");
AddColumn(TIT, "TTM Inv TO");

You'll notice I filter out anything that scores less than 50%. Running this screen on NASDAQ, NYSE and ARCA give me about 400 stocks or so, more than enough to make a crème de la crème stock universe. For I run some screens on this universe looking for simple breakouts, RS line new highs, stocks near highs, but my next conquest is to really find bases with substantial volatility contractions.

As usual, any comments, improvements and criticisms are welcome!



@rocketPower thanks for sharing your ideas. I was wondering if you realize that Norgate does not provide Historical Fundamental data, i.e. their fundamental data is for the most current reporting quarter. For backtesting your system you would need a historical source for your fundamental inputs.

I am not sure which data vendor has a good historical fundamental data feed but maybe other users can provide suggestions.

Good luck on your work!

1 Like

Yes indeed I cannot backtest using fundamental data on Norgate. I use this exploration for watchlist building to build a further edge from purely price/volume backtested strategies. I have to make the assumption on the empirical evidence of the usage of this data by successful traders that it is enough of a rationale to follow suit.

I'm definitely interested in seeing what other data services people use with Amibroker. Norgate is great, but I can't believe it's the only viable and affordable option for historical price data.


1 Like

How would you know they are successful?
A long time ago I bought this book from John Carter:

NONE of the ideas could be validated using backtesting.
He sells his TTQ squeeze indicator that goes for almost 500US$, it's 4 lines in Amibroker AFL.

That's only one example. This trading arena is full of terrible people. I could go on with examples.
I believe it only when I see it on my screen.


Very true. I believe historical fundamentals are available however they seem pretty expensive.

In any case I backtest my strategy on all stocks, including delisted. But yes I cannot backtest narrowing down that universe to those that are fundamentally strong. In this case I rely on to validate the assumption that this reduced universe will outperform the market:

  1. His track record is well known, via stock investing championship wins but more importantly he's been audited by KPMG.
  2. His trading is really an augmented version of CAN SLIM which is a system that has been developed quantitatively by William O'Neil. Then IBD 50 has outperformed the S&P500 for arguably a statistically significant period of time.

If anyone knows where to get relatively affordable historical fundamental data, including delisted stocks, please let me know. I'd be glad to bridge that gap.



@rocketPower, one source of US fundamentals (that seems not too costly for personal use) is "Sharadar Core US Fundamentals Database" that you can find on the Quandl.

Updated daily, this database provides up to 20 years of history, for 139 essential fundamental indicators and financial ratios, for more than 12,500 US public companies.
(More than 5,000 active and 7,500 delisted companies).

How to use this data in AmiBroker is another story!


Thanks Beppe,

I will look into this. There seems to be a lot of US-based data which is perfect! Have you tried using their data in any way?


@rocketPower no.

I found them while looking for an alternative source of world's stock quotations, and after seeing their sample data, it seemed appropriate for what you were looking for.
(I did not suggest you EodHistoricaData since their history is short - 4 years - and above all, at present, they don't keep track of delisted stocks.)

I also noticed that on the Sharadar site blog there is a short article about Tiingo: it would be interesting to know if the fundamentals data published by them are coming from that source.
Maybe @Rishi_at_tiingo could provide us with some additional info.


I have nothing but positive things to say about Sharadar @rocketpower and @beppe. Vince, the founder, and I go way back and he is a close friend. I've used their data from the early days and its only gotten better (more expansive). It's also one of the most accurate and cleanest data sets I've seen. My users have noticed this too and have pointed it out multiple times.

I recommend Shardar to so many people that at this point Vince gave me a code I can give people for 20% off. I don't get any commission for giving you this - just pure joy of sharing his data. The discount code is: RSOPS-20P-SF

You get 20% off indefinitely.




Thanks for all the great info and most certainly the discount code! Do you know if their data packages work well with Amibroker? Is it as seamless as Norgate or do you need to do some customized processing?


@rocketPower was trying to use the above code and RSline does not seem to be a Amibroker function.. are you using some prop indicators in this code.

I also had a look at sharadar for funnymentals. Anyone tried playing with it, i.e. processing it in such a way that it can be used in AB backtests?


Hey sorry for answering so late. Yeah RSLine is just the stock closing price divided by the S&P500 closing price.

Same exact thing you see in IBD charts.

Hope that helps!


In the Function modeling the trend template:, is there a way to make this a part of a backtestable strategy? I have attempted to do something similar but only as conditions of a Buy setup. I have struggled though figuring out how to do that in regard to the High/Low check percentage portion. Coding the moving averages I have found to be simple. Essentially what I am trying to do is integrate a screen into my strategy for portfolio backtesting.

Sure why not! It's only mostly price analysis

What I have done thus far is simply:

BlockquoteBuy = Cross( C, trailArray ) AND C > EMA150 AND C > EMA200 AND EMA150 > EMA200 AND EMA50 > EMA150 AND EMA50 > EMA200 AND C > EMA50 AND Index >= IndexEMA;

As I said this takes care of the moving average portion. Any suggestions how I would add Minervini's percentage of high and lows criteria and relative strength ranking to the strategy? I have tried to study Functions and Filters but cant quite seem to make sense of how to do this.

Wow....I sat here all evening going over what you wrote Rocketpower and then shortly after I replied, It like it just smacked me in the face. I dont know why I didnt see it, you have all of the math there. This really is like learning a new language. I did try to run what you have as both a scan and an exploration before putting it into my strategy to see if I could get it to do anything but it didnt return any results. It seems it should so I'll just keep pondering over it and see what I can come up with. Thanks for you work on this and thanks for your earlier response.

Just to be clear, Minervini's strategy uses simple moving averages, not exponential, but feel free to customize to your comfort level!

Check my newer post as I think I model it better:

Using eTables with Amibroker

1 Like

Hehe, your post made me chuckle. It's a fun ride this investing game, so please remember to enjoy it!

One advice about filtering, make sure you are clear on what is negotiable, and what is not negotiable.

Then filter accordingly.

1 Like

You mentioned that you would be working next on coding his Volatility Contraction Pattern....I'd love to hear how that works out. That is exactly what I am trying to do now but I don't have near enough experience with AFL. You will definitely figure it out before I do Im sure. After reading all three of his books, he has hit on so many points I have struggled with in my own trading and I am intrigued by a lot of what he does. Seeing the things you are working on definitely catches my interest!