Category Archives: Must Know

Anatomy Of Mean Reversion in EasyLanguage

Look at this equity curve:

As long as you are in a bull market buying dips can be very consistent and profitable.  But you want to use some type of entry signal and trade management other than just buying a dip and selling a rally.  Here is the anatomy of a mean reversion trading algorithm that might introduce some code that you aren’t familiar.  Scroll through the code and I will  summarize below.

inputs: mavlen(200),rsiLen(2),rsiBuyVal(20),rsiSellVal(80),holdPeriod(5),stopLoss$(4500);
vars: iCnt(0),dontCatchFallingKnife(false),meanRevBuy(false),meanRevSell(false),consecUpClose(2),consecDnClose(2);

Condition1 = c > average(c,mavLen);

Condition2 = rsi(c,rsiLen) < rsiBuyVal;
Condition3 = rsi(c,rsiLen) > rsiSellVal;


Value1 = 0;
Value2 = 0;

For iCnt = 0 to consecUpClose - 1 
Begin
	value1 = value1 + iff(c[iCnt] > c[iCnt+1],1,0);
end;

For iCnt = 0 to consecDnClose - 1 
Begin
	Value2 = value2 + iff(c[iCnt] < c[iCnt+1],1,0);
end;

dontCatchFallingKnife = absValue(C - c[1]) < avgTrueRange(10)*2.0;

meanRevBuy = condition1 and condition2 and dontCatchFallingKnife;
meanRevSell =  not(condition1) and condition3 and dontCatchFallingKnife;

If meanRevBuy then buy this bar on close;
If marketPosition = 1 and condition1 and value1 >= consecUpClose then sell("ConsecUpCls") this bar on close;

If meanRevSell then sellShort this bar on close;
If marketPosition = -1 and not(condition1) and value2 >= consecDnClose then buyToCover this bar close;

setStopLoss(stopLoss$);


If barsSinceEntry = holdPeriod then
Begin
	if marketPosition = 1 and not(meanRevBuy) then sell this bar on close;
	if marketPosition =-1 and not(meanRevSell) then buytocover this bar on close;
end;
Mean Reversion System

I am using a very short term RSI indicator, a la Connors, to initiate long trades.  Basically when the 2 period RSI dips below 30 and the close is above the 200-day moving average I will buy only if I am not buying “a falling knife.”  In February several Mean Reversion algos kept buying as the market fell and eventually got stopped out with large losses.  Had they held on they probably would have been OK.  Here I don’t buy if the absolute price difference between today’s close and yesterday’s is greater than 2 X the ten day average true range.  Stay away from too much “VOL.”

Once a trade is put on I use the following logic to keep track of consecutive closing relationships:

For iCnt = 0 to consecUpClose - 1 
Begin
	value1 = value1 + iff(c[iCnt] > c[iCnt+1],1,0);
end;
Using the IFF function in EasyLanguage

Here I am using the IFF function to compare today’s close with the prior day’s.  iCnt is a loop counter that goes from 0 to 1. IFF checks the comparison and if it’s true it returns the first value after the comparison and if false it returns the last value.  Here if I have two consecutive up closes value1 accumulates to 2.  If I am long and I have two up closes I get out.  With this template you can easily change this by modifying the input:  consecUpClose.  Trade management also includes a protective stop and a time based exit.  If six days transpire without two up closes then the system gets out – if the market can’t muster two positive closes, then its probably not going to go anywhere.  The thing with mean reversion, more so with other types of systems, is the use or non use of a protective stop.  Wide stops are really best, because you are betting on the market to revert.  Look at the discrepancy of results using different stop levels on this system:

Here an $1,800 stop only cut the max draw down by $1,575.  But it came at a cost of $17K in profit.  Stops, in the case of Mean Reversion, are really used for the comfort of the trader.

This code has the major components necessary to create a complete trading system.  Play around with the code and see if you can come up with a better entry mechanism.

Making Trading Decisions on Current Month’s Profit/Loss

Keeping track of intra-month profit or loss

In real time trading I have noticed that once you reach a certain loss for the month its best, sometimes, to circle the wagons and quit trading until the beginning of the next month.  This concept works best for very short term or day trade algorithms, as its very easy to get started back up.  You can do this with Trend Following, but you must build a logical and replicable process for re-entering existing positions.  Let’s assume a trading algorithm whose averaging losing month is $1500 and you are currently down $2000 – what are the chances that you will revert to the mean or draw down further?  Probably 50/50.  Who knows you might turn around and actually make money by month’s end.  If you review a track record of a hedge fund manager, trader, or algorithm and they show a bar chart of monthly returns and there sticking out like a sore thumb is a big down bar, that kind of makes you think that could happen again.  If you can control the monthly downside without sacrificing the existing Profit:DrawDown ratio, then why not do it.

Sample Code To Monitor IntraMonth $P/L

if month(date) <> month(date[1]) then
Begin
	begMonthProf = netProfit; 
	print(d," ",t," ",begMonthProf);
	canTrade = true;
end;
Capture Beginning Of Month Net Profit

Here I am comparing the month of the current bar against the month of the prior bar.  If they are not equal, then we have a new month.  Store the netProfit in the variable begMonthProf.  All you have to do is compare the current bar’s netProfit to begMonthProf and make a decision.  Here is some code:

Making a Trading Decision Based on Monthly $P/L

		If dayOfMonth(date) > 15 and begMonthProf - netProfit >= intraMonthMaxLoss then canTrade = false;
If Down MaxLoss for Month and Past Mid-Month - Quit Trading

If the day of the month is greater than 15 (month half over) and the difference between the current netProfit and begMonthProfit is greater than a negative intraMonthMaxLoss then quit trading for the month.  Only turn it back on the first bar of the next month.  See how this works for your algos.

How to Keep Track of BuysToday and SellsToday

The Useful MP

We all know how to use the reserved word/function MarketPosition – right?  Brief summary if not – use MarketPosition to see what your current position is: -1 for short, +1 for long and 0 for flat.  MarketPosition acts like a function because you can index it to see what you position was prior to the current position – all you need to do is pass a parameter for the number of positions ago.  If you pass it a one (MarketPosition(1)) then it will return the your prior position.  If you define a variable such as MP you can store each bars MarketPosition and this can come in handy.

mp = marketPosition;

If mp[1] <> 1 and mp = 1 then buysToday = buysToday + 1;
If mp[1] <> -1 and mp = -1 then sellsToday = sellsToday + 1;
Keeping Track of Buy and Sell Entries on Daily Basis

The code compares prior bar’s MP value with the current bar’s.   If there is a change in the value, then the current market position has changed.   Going from not 1 to 1 indicates a new long position.  Going from not -1 to -1 implies a new short.  If the criteria is met, then the buysToday or sellsToday counters are incremented.  If you want to keep the number of buys or sells to a certain level, let’s say once or twice,  you can incorporate this into your code.

If  time >= startTradeTime and t < endTradeTime and 
	buysToday < 1 and 
	rsi(c,rsiLen) crosses above rsiBuyVal then buy this bar on close;
If  time >= startTradeTime and t < endTradeTime and 
	sellsToday < 1 and 
	rsi(c,rsiLen) crosses below rsiShortVal then sellShort this bar on close;
Using MP to Keep Track of BuysToday and SellsToday

This logic will work most of the time, but it depends on the robustness of the builtin MarketPosition function Look how this logic fails in the following chart:

I didn't want entries in the same direction per day!
I only wanted 1 short entry per day!

MarketPosition Failure

Failure in the sense that the algorithm shorted twice in the same day.  Notice on the first trade how the profit objective was hit on the very next bar.  The problem with MarketPosition is that it only updates at the end of the bar one bar after the entry.  So MarketPosition stays 0 during the duration of this trade.  If MarketPosition doesn’t change then my counter won’t work.  TradeStation should update MarketPosition at the end of the entry bar.  Alas it doesn’t work this way.  I figured a way around it though.  I will push the code out and explain it later in more detail.

Input: rsiLen(14),rsiBuyVal(30),rsiShortVal(70),profitObj$(250),protStop$(300),startTradeTime(940),endTradeTime(1430);

Vars: mp(0),buysToday(0),sellsToday(0),startOfDayNetProfit(0);

If d <> d[1] then
Begin
	buysToday = 0;
	sellsToday = 0;
	startOfDayNetProfit = netProfit;
end;

{mp = marketPosition;

If mp[1] <> 1 and mp = 1 then buysToday = buysToday + 1;
If mp[1] <> -1 and mp = -1 then sellsToday = sellsToday + 1;}

If entriesToday(date) > buysToday + sellsToday then 
Begin
	If marketPosition = 1 then buysToday = buysToday + 1;
	If marketPosition =-1 then sellsToday = sellsToday + 1;
	If marketPosition = 0 then
	Begin
		if netProfit > startOfDayNetProfit then
		begin
			if exitPrice(1) > entryPrice(1) then buysToday = buysToday + 1;
			If exitPrice(1) < entryPrice(1) then sellsToday = sellsToday + 1;
		end;;
		if netProfit < startOfDayNetProfit then
		Begin
			if exitPrice(1) < entryPrice(1) then buysToday = buysToday + 1;
			If exitPrice(1) > entryPrice(1) then sellsToday = sellsToday + 1;
		end;
	end;
	print(d," ",t," ",buysToday," ",sellsToday);
end;

If  time >= startTradeTime and t < endTradeTime and 
	buysToday < 1 and 
	rsi(c,rsiLen) crosses above rsiBuyVal then buy this bar on close;
If  time >= startTradeTime and t < endTradeTime and 
	sellsToday < 1 and 
	rsi(c,rsiLen) crosses below rsiShortVal then sellShort this bar on close;

SetProfittarget(profitObj$);
SetStopLoss(protStop$);

SetExitOnClose;
A Better Buy and Short Entries Counter

TradeStation does update EntriesToday at the end of the bar so you can use this keyword/function to help keep count of the different type of entries.  If MP is 0 and EntriesToday increments then you know an entry and an exit has occurred (takes care of the MarketPosition snafu) – all you need to do is determine if the entry was a buy or a sell.  NetProfit is also updated when a trade is closed.   I establish the StartOfDayNetProfit on the first bar of the day (line 9 in the code) and then examine EntriesToday and if NetProfit increased or decreased.  EntryPrice and ExitPrice are also updated at the end of the bar so I can also use them to extract the information I need.   Since MarketPosition is 0  I have to pass 1 to the EntryPrice and ExitPrice functions – prior position’s prices.  From there I can determine if a Long/Short entry occurred.  This seems like a lot of work for what you get out of it, but if you are controlling risk by limiting the number of trades (exposure) then an accurate count is so very important.

An alternative is to test on a higher resolution of data – say 1 minute bars.  In doing this you give a buffer to the MarketPosition function – more bars to catch up.

 

Pyramiding and then Scaling Out at Different Price Levels – EasyLanguage

TOTAL, TOTAL, TOTAL – an important keyword

I just learned something new!  I guess I never programmed a strategy that pyramided at different price levels and scaled out at different price levels.

Initially I thought no problem.  But I couldn’t get it to work – I tried everything and then I came across the keyword Total and then I remembered.  If you don’t specify Total in you exit directives then the entire position is liquidated.  Unless you are putting all your positions on at one time – like I did in my last post.   So remember if you are scaling out of a pyramid position use Total in your logic.

vars: maxPosSize(2);

If currentContracts < maxPosSize - 1 and c > average(c,50) and c = lowest(c,3) then buy("L3Close") 1 contract this bar on close;
If currentContracts < maxPosSize and c > average(c,50) and c = lowest(c,4) then buy("L4Close") 1 contract this bar on close;


If currentContracts = 2 and c = highest(c,5) then sell 1 contract total this bar on close;
If currentContracts = 1 and c = highest(c,10) then sell 1 contract total this bar on close;
Scaling Out Of Pyramid

Why you have to use the Total I don’t know.  You specify the number of contracts in the directive and that is sufficient if you aren’t pyramiding.  The pyramiding throws a “monkey wrench” in to the works.