Category Archives: EasyLanguage Function

Passing a Two Dimensional Array to a Function- EasyLanguage

In this post, I want to share some code that I was surprised wasn’t really all that accessible.  I was in need of passing a 2-D array to a function and couldn’t remember the exact syntax.  The semantics of the problem is pretty straightforward.

  • Build Array
  • Pass Array as Reference
  • Unpack Array
  • Do Calculation

A 2D Array Is Just Like a Table

Any readers please correct me if I am wrong here, but you can’t use dynamic arrays on any dimension greater than 1.  A dynamic array is one where you don’t initially know the size of the array so you leave it blank and TradeStation allocates memory dynamically.  Also, there are a plethora of built-in functions that only work with dynamic arrays.  Once we step up in dimension then that all goes out the window.  I know what you are thinking, just use multiple dynamic arrays.  Sometimes you want to keep the accounting down to a minimum and a matrix or table fits this bill.  So if you do use multi-dimension arrays just remember you will need to know the total number of rows and columns in your table.  Table?  What do you mean table?  I thought we were talking about arrays.  Well, we are and a two-dimensional array can look like a table with rows as your first index and columns as your second.  Just like an Excel spreadsheet.  First, let’s create a very small and simple table in EasyLangauge:

array: testArray[2,2](0);

Once
begin
	testArray[0,0] = 100;
	testArray[0,1] = 5;
	
	testArray[1,0] = 200;
	testArray[1,1] = 10;
	
	testArray[2,0] = 300;
	testArray[2,1] = 7.5;
	
	Value2 = getArrayHH(testArray,2,2);
	Print (value2);

end;
Create a Very Small Table in Our Sandbox

 

My EasyLanguage Sandbox

You will notice I used the keyword Once.  I use this whenever I want to play around with some code in my EasyLanguage Sandbox.  Huh?  In programmer-ese a Sandbox is a quick and dirty environment that runs very quickly and requires nearly zero overhead.  So here I apply the code to print out just one line of output when applied to any chart.   Notice how I declare the 2-D array – use the keyword Array: and the name of the array or table and allocate the total number of rows as the first argument and the total number of columns as the second argument.  Also notice the arguments are separated by a comma and enclosed in square brackets.  The following value enclosed in parentheses is the default value of every element in the array.    Remember arrays are zero-based in EasyLanguage.  So if you dimension an array with the number 2 you access the rows with 0 and 1 and 2.  Same goes for columns as well.  Did you catch that.  If you dimension an array with the number 2 shouldn’t there be just 2 rows?  Well in EasyLanguage when you dimension an array you get a free element at row 0 and column 0.  In EasyLanguage you can just ignore row 0 and column 0 if you like.  Here is the code if you ignore row 0 and column 0.

Should I Use Row 0 and Column Zero – It’s A Preference

array: testArray[2,2](0);

Once
begin
	testArray[1,1] = 100;
	testArray[1,2] = 5;
	
	testArray[2,1] = 200;
	testArray[2,2] = 10;
		
	Value2 = getArrayHH(testArray,2,2);
	Print (value2);

end;
Ignore the Elements at Row 0 and Column 0

Out Of Bounds

Even though you get one free row you still cannot go beyond the boundaries of the array size.  If I were to say something like:

testArray[3,1] = 300;

I would get an error message.  If you want to work with a zero element then all of your code must coincide with that.  If not, then your code shouldn’t try to access row 0 or column 0.    Okay here is the function that I programmed for this little test:

inputs: tempArray[x,y](numericArray),numOfRows(numericSimple),whichColumn(numericSimple);

vars: j(0),tempHi(0),cnt(0);

For j= 1 to numOfRows 
Begin
	print(j," ",tempArray[j,whichColumn]);
	If tempArray[j,whichColumn] > tempHi then tempHi = tempArray[j,whichColumn];
end;

GetArrayHH = tempHi;
Function That Utilizes a 2D Array

Notice in the inputs how I declare the tempArray with the values x and y.  You could have used a and b if you like or any other letters.  This informs the compiler to expect a 2D array.  It doesn’t know the size and that’s not important as long as you control the boundaries from the calling routine.  The second parameter is the number of rows in the table and the third parameter is the column I am interested in.  In this example, I am interested in column 2.

The Caller Function is the QuarterBack – Make Sure You Don’t Throw it Out of Bounds

Again this function assumes the caller will prevent stepping out of bounds.  I loop the number of rows in the table and examine the second column and keep track of the highest value.  I then return the highest column value.

This was a simple post, but remembering the syntax can be tough and know that EasyLangauge is zero-based when it comes to arrays is nice to know.   You can also use this format for your own Sandbox.

 

 

Please follow and like us:
error

Using a Dictionary to Create a Trading System

Dictionary Recap

Last month’s post on using the elcollections dictionary was a little thin so I wanted to elaborate on it and also develop a trading system around the best patterns that are stored in the dictionary.  The concept of the dictionary exists in most programming languages and almost all the time uses the (key)–>value model.  Just like a regular dictionary a word or a key has a unique definition or value.  In the last post, we stored the cumulative 3-day rate of return in keys that looked like “+ + – –” or “+ – – +“.  We will build off this and create a trading system that finds the best pattern historically based on average return.  Since its rather difficult to store serial data in a Dictionary I chose to use Wilder’s smoothing average function.

Ideally, I Would Have Liked to Use a Nested Dictionary

Initially, I played around with the idea of the pattern key pointing to another dictionary that contained not only the cumulative return but also the frequency that each pattern hit up.  A dictionary is designed to have  unique key–> to one value paradigm.  Remember the keys are strings.  I wanted to have unique key–> to multiple values. And you can do this but it’s rather complicated.  If someone wants to do this and share, that would be great.  AndroidMarvin has written an excellent manual on OOEL and it can be found on the TradeStation forums.  

Ended Up Using A Dictionary With 2*Keys Plus an Array

So I didn’t want to take the time to figure out the nested dictionary approach or a vector of dictionaries – it gets deep quick.  So following the dictionary paradigm I came up with the idea that words have synonyms and those definitions are related to the original word.  So in addition to having keys like “+ + – -” or “- – + -” I added keys like “0”, “1” or “15”.  For every  + or – key string there exists a parallel key like “0” or “15”.  Here is what it looks like:

–  –  –  –  = “0”
– – – + = “1”
– – + – = “2”

You can probably see the pattern here.  Every “+” represents a 1 and every “0” represent 0 in a binary-based numbering system.  In the + or – key I store the last value of Wilders average and in the numeric string equivalent, I store the frequency of the pattern.

Converting String Keys to Numbers [Back and Forth]

To use this pattern mapping I had to be able to convert the “++–” to a number and then to a string.  I used the numeric string representation as a dictionary key and the number as an index into an array that store the pattern frequency.  Here is the method I used for this conversion.  Remember a method is just a function local to the analysis technique it is written.

//Lets convert the string to unique number
method int convertPatternString2Num(string pattString) 
Vars: int pattLen, int idx, int pattNumber;
begin
	pattLen = strLen(pattString);
	pattNumber = 0;
	For idx = pattLen-1 downto 0 
	Begin
		If MidStr(pattString,pattLen-idx,1) = "+" then pattNumber = pattNumber + power(2,idx);
	end;
	Return (pattNumber);
end;
String Pattern to Number

This is a simple method that parses the string from left to right and if there is a “+” it is raised to the power(2,idx) where idx is the location of “+” in the string.  So “+  +  –  –  ” turns out to be 8 + 4 + 0 + 0 or 12.

Once I retrieve the number I used it to index into my array and increment the frequency count by one.  And then store the frequency count in the correct slot in the dictionary.

patternNumber = convertPatternString2Num(patternString); 
//Keep track of pattern hits
patternCountArray[patternNumber] = patternCountArray[patternNumber] + 1;
//Convert pattern number to a string do use as a Dictionary Key
patternStringNum = numToStr(patternNumber,2);
//Populate the pattern number string key with the number of hits
patternDict[patternStringNum] = patternCountArray[patternNumber] astype double;
Store Value In Array and Dictionary

Calculating Wilder’s Average Return and Storing in Dictionary

Once I have stored an instance of each pattern [16] and the frequency of each pattern[16] I calculate the average return of each pattern and store that in the dictionary as well.

//Calculate the percentage change after the displaced pattern hits
Value1 =  (c - c[2])/c[2]*100;
//Populate the dictionary with 4 ("++--") day pattern and the percent change
if patternDict.Contains(patternString) then
Begin
	patternDict[patternString] = (patternDict[patternString] astype double * 
	(patternDict[patternStringNum] astype double - 1.00) + Value1) / patternDict[patternStringNum] astype double;
end
Else
begin
	patternDict[patternString] = value1;
//	print("Initiating: ",patternDict[patternString] astype double);
end;
(pAvg * (N-1) + return) / N

When you extract a value from a collection you must us an identifier to expresses its data type or you will get an error message : patternDict[patternString] holds a double value {a real number}  as well as patternDict[patternStringNum] – so I have to use the keyword asType.  Once I do my calculation I ram the new value right back into the dictionary in the exact same slot.  If the pattern string is not in the dictionary (first time), then the Else statement inserts the initial three-day rate of return.

Sort Through All of The Patterns and Find the Best!

The values in a dictionary are stored in alphabetic order and the string patterns are arranged in the first 16 keys.  So I loop through those first sixteen keys and extract the highest return value as the “best pattern.”

//  get the best pattern that produces the best average 3 bar return
vars: hiPattRet(0),bestPattString("");
If patternDict.Count > 29 then
Begin
	index = patternDict.Keys;
	values = patternDict.Values; 
	hiPattRet = 0;
	For iCnt = 0 to 15
	Begin
		If values[iCnt] astype double > hiPattRet then
		Begin
			hiPattRet = values[iCnt] astype double ;
			bestPattString = index[iCnt] astype string;
		end;
	end;
//	print(Date," BestPattString ",bestPattString," ",hiPattRet:8:4," CurrPattString ",currPattString);
end;
Extract Best Pattern From All History

If Today’s Pattern Matches the Best Then Take the Trade

// if the current pattern matches the best pattern then bar next bar at open
If currPattString = BestPattString then buy next bar at open;
// cover in three days
If barsSinceEntry > 2 then sell next bar at open;
Does Today Match the Best Pattern?

If today matches the best pattern then buy and cover after the second day.

Conclusion

I didn’t know if this code was worth proffering up but I decided to posit it because it contained a plethora of programming concepts: dictionary, method, string manipulation, and array.  I am sure there is a much better way to write this code but at least this gets the point across.

Contents of Dictionary at End of Run

++++    0.06
+++-   -0.08
++-+    0.12
++--   -0.18
+-++    0.08
+-+-    0.40
+--+   -0.46
+---    0.34
-+++    0.20
-++-    0.10
-+-+    0.23
-+--    0.31
--++    0.02
--+-    0.07
---+    0.22
----    0.46
0.00  103.00
1.00  128.00
10.00  167.00
11.00  182.00
12.00  146.00
13.00  168.00
14.00  163.00
15.00  212.00
2.00  157.00
3.00  133.00
4.00  143.00
5.00  181.00
6.00  151.00
7.00  163.00
8.00  128.00
9.00  161.00
Contents of Dictionary

Example of Trades

Pattern Dictionary System

 

Code in Universum

//Dictionary based trading sytem
//Store pattern return
//Store pattern frequency
// by George Pruitt
Using elsystem.collections; 

vars: string keystring("");
vars: dictionary patternDict(NULL),vector index(null), vector values(null);
array: patternCountArray[100](0);

input: patternTests(8);
  
var: patternTest(""),tempString(""),patternString(""),patternStringNum("");
var: patternNumber(0);
var: iCnt(0),jCnt(0);
//Lets convert the string to unique number
method int convertPatternString2Num(string pattString) 
Vars: int pattLen, int idx, int pattNumber;
begin
	pattLen = strLen(pattString);
	pattNumber = 0;
	For idx = pattLen-1 downto 0 
	Begin
		If MidStr(pattString,pattLen-idx,1) = "+" then pattNumber = pattNumber + power(2,idx);
	end;
	Return (pattNumber);
end;


once begin 
   clearprintlog; 
   patternDict = new dictionary; 
   index = new vector;
   values = new vector;
end;   

//Convert 4 day pattern displaced by 2 days
patternString = ""; 
for iCnt = 5 downto 2
begin
    if(close[iCnt]> close[iCnt+1]) then
    begin
        patternString = patternString + "+";
    end
    else
    begin
        patternString = patternString + "-";
    end;
end;

//What is the current 4 day pattern
vars: currPattString("");
currPattString = "";

for iCnt = 3 downto 0
begin
    if(close[iCnt]> close[iCnt+1]) then
    begin
        currPattString = currPattString + "+";
    end
    else
    begin
        currPattString = currPattString + "-";
    end;
end;

//Get displaced pattern number
patternNumber = convertPatternString2Num(patternString); 
//Keep track of pattern hits
patternCountArray[patternNumber] = patternCountArray[patternNumber] + 1;
//Convert pattern number to a string do use as a Dictionary Key
patternStringNum = numToStr(patternNumber,2);
//Populate the pattern number string key with the number of hits
patternDict[patternStringNum] = patternCountArray[patternNumber] astype double;
//Calculate the percentage change after the displaced pattern hits
Value1 =  (c - c[2])/c[2]*100;
//Populate the dictionary with 4 ("++--") day pattern and the percent change
if patternDict.Contains(patternString) then
Begin
	patternDict[patternString] = (patternDict[patternString] astype double * 
	(patternDict[patternStringNum] astype double - 1.00) + Value1) / patternDict[patternStringNum] astype double;
end
Else
begin
	patternDict[patternString] = value1;
//	print("Initiating: ",patternDict[patternString] astype double);
end;
//  get the best pattern that produces the best average 3 bar return
vars: hiPattRet(0),bestPattString("");
If patternDict.Count > 29 then
Begin
	index = patternDict.Keys;
	values = patternDict.Values; 
	hiPattRet = 0;
	For iCnt = 0 to 15
	Begin
		If values[iCnt] astype double > hiPattRet then
		Begin
			hiPattRet = values[iCnt] astype double ;
			bestPattString = index[iCnt] astype string;
		end;
	end;
//	print(Date," BestPattString ",bestPattString," ",hiPattRet:8:4," CurrPattString ",currPattString);
end;
// if the current pattern matches the best pattern then bar next bar at open
If currPattString = BestPattString then buy next bar at open;
// cover in three days
If barsSinceEntry > 2 then sell next bar at open;
Pattern Dictionary Part II

 

 

 

Please follow and like us:
error

Working Around 0:00 Time in EasyLanguage

Let’s say you want to carve out a special session of data from the 24-hour data session – maybe keep track of the highest high and lowest low from 9:00 p.m. to 4:00 p.m. the next day.  How would you do it?

To start with you would need to reset the highest high and lowest low values each day.  So you could say if the current bars time > StartTime and the prior bars time <= StartTime then you know the first bar of your specialized session has started.  So far so good.  If the time falls outside the boundaries of your special session then you want to ignore that data -right?  What about this:

If t >StartTime and t <= EndTime then…

{Remember EasyLanguage uses the end time stamp for its intraday bars}

Sounds good.  But what happens when time equals 2300 or 11:00 p.m.?  You want to include this time in your session but the if-then construct doesn’t work.    2300 is greater than 2100 but it’s not less than 1600 so it doesn’t pass the test.  The problems arise when the EndTime < StartTime.  It really isn’t since the EndTime is for the next day, but the computer doesn’t know that.  What to do?  Here is a quick little trick to help you solve this problem:  use a special offset if the time falls in a certain range.

EndTimeOffset = 0 ;

If t >=StartTime and t <= 2359 then EndTimeOffset= 2400 – EndTime;

Going back to our example of the current time of 2300 and applying this little bit of code our EndTimeOffset would be equal to 2400 – 1600 or 800.  So if t = 2300, you subtract 800 and get 1500 and that works.

2300 – 800 = 1500 which is less than 1600 –> works

What if t = 300 or 3:00 a.m.  Then EndTimeOffset = 0; 300 – 0 is definitely less than 1600.

That solves the problem with the EndTime.  Or does it?  What if EndTime is like 1503?  So you have 2400 – 1503 which is something like 897.  What if time is 2354 and you subtract 897 you get 1457 and that still works since its less than 1503.  Ok, what about if EndTime = 1859 then you get 2400 – 1859 which equals 541.  If time  = 2354 and you subtract 541 you get 1843 and that still works.

Is there a similar problem with the StartTime?  If t = 3:00 a.m. then it is not greater than our StartTime of 2100, but we want it in our window.  We need another offset.  This time we want to make a StartTime offset equal to 2400 when we cross the 0:00 timeline.  And then reset it to zero when we cross the StartTime timeline.  Let’s see if it works:

t = 2200 : is t > StartTime?  Yes

t=0002 : is t > StartTime?  No, but should be.  We crossed the 0000 timeline so we need to add 2400 to t and then compare to StartTime:

t + 2400 = 2402 and it is greater than StartTime.  Make sense?

Probably not but look at the code:

inputs: StartTime(numericSimple),EndTime(numericSimple),StartTimeOffSet(numericRef),EndTimeOffSet(numericRef);

If t >= StartTime and t[1] < StartTime then StartTimeOffSet = 0;
EndTimeOffSet = 0;
If t >= StartTime and t <= 2359 then EndTimeOffSet = 2400 - EndTime;
If t < t[1] then StartTimeOffSet = 2400;

TimeOffsets = 1; 
Function To Calculate Start and End Time Offsets

Here is an the indicator code that calls the function:

vars: startTimeWindow(2100),endTimeWindow(1600);
vars: startOffSet(0),endOffSet(0);
Value1 = timeOffSets(startTimeWindow,endTimeWindow,startOffSet,endOffSet);

If t+startOffset > startTimeWindow and t-endOffSet <=endTimeWindow then
Begin
	
end
Else
Begin
	print(d," ",t," outside time window ");
end;
Calling TimeOffsets Function

Hope this helps you out.  I am posting this for two reasons: 1) to help out and 2) prevent me from reinventing the wheel every time I have to use time constraints on a larger time frame of data.

StartTimeWindow = 2300

EndTimeWindow = 1400

Time = 2200, FALSE

Time = 2315, TRUE [2315 > 2300 and 2315 – (2400 -1400) <1400)]

This code should work with all times.  Shoot me an email if you find it doesn’t.

 

Please follow and like us:
error

Calculating Position Size with Optimal F

I had a reader of the blog ask how to use Optimal F.  That was really a great question.  A few posts back I provided the OptimalFGeo function but didn’t demonstrate on how to use it for allocation purposes.  In this post, I will do just that.

I Have Optimal F – Now What?

From Ralph Vince’s book, “Portfolio Management Formulas”, he states: “Once the highest f is found, it can readily be turned into a dollar amount by dividing the biggest loss by the negative optimal f.  For example, if our biggest loss is $100 and our optimal f is 0.25, then -$100/ 0.25 = $400.  In other words, we should bet 1 unit for every $400 we have in our stake.”

Convert Optimal F to dollars and then to number of shares

In my example strategy, I start out with an initial capital of $50,000 and allow reinvestment of profit or loss.  The protective stop is set as 3 X ATR(10).  A fixed $2000 profit objective is also utilized.  The conversion form Optimal F to position size is illustrated by the following lines of code:

//keep track of biggest loss
biggestLoss = minList(positionProfit(1),biggestLoss);
//calculate the Optimal F with last 10 trades.
OptF = OptimalFGeo(10);
//reinvest profit or loss
risk$ = initCapital$ + netProfit;
//convert Optimal F to $$$
if OptF <> 0 then numShares = risk$ / (biggestLoss / (-1*OptF));
Code snippet - Optimal F to Position Size
  1. Keep track of biggest loss
  2. Calculate optimal F with OptimalFGeo function – minimum 10 trades
  3. Calculate Risk$ by adding InitCapital to current NetProfit (Easylanguage keyword)
  4. Calculate position size by dividing Risk$  by the quotient of biggest loss and (-1) Optimal F

I applied the Optimal F position sizing to a simple mean reversion algorithm where you buy on a break out in the direction of the 50-day moving average after a lower low occurs.

Code listing:

vars: numShares(0),initCapital$(50000),biggestLoss(0),OptF(0),risk$(0);


//keep track of biggest loss
biggestLoss = minList(positionProfit(1),biggestLoss);
//calculate the Optimal F with last 10 trades.
OptF = OptimalFGeo(10);
//reinvest profit or loss
risk$ = initCapital$ + netProfit;
//convert Optimal F to $$$
if OptF <> 0 then numShares = risk$ / (biggestLoss / (-1*OptF));
numShares =  maxList(1,numShares);
//if Optf <> 0 then print(d," ",t," ",risk$ / (biggestLoss / (-1*OptF))," ",biggestLoss," ",optF);

if c > average(c,50) and low < low[1] then Buy numShares shares next bar at open + .25* range stop;

setStopPosition;
setProfitTarget(2000);

setStopLoss(3*avgTrueRange(10)*bigPointValue);
Strategy Using Optimal F

I have included the results below.  At one time during the testing the number of contracts jumped up to 23.  That is 23 mini Nasdaq futures ($20 * 7,300) * 23.  That’s a lot of leverage and risk.  Optimal doesn’t  always mean the best risk mitigation.  Please let me know if you find any errors in the code or in the logic.

 

Here is the ELD that incorporates the Strategy and the Function.USINGOPTIMALF

 

Please follow and like us:
error

George’s EasyLanguage BarsSince Function – How Many Bars Since?

BarsSince Function in EasyLanguage

Have you ever wondered how many bars have transpired since a certain condition was met?  Some platforms provide this capability:

If ExitFlag and (c crosses above average within 3 bars) then

TradeStation provides the MRO (Most Recent Occurrence) function that provides a very similar capability.  The only problem with this function is that it returns a -1 if the criteria are not met within the user provided lookback window.  If you say:

myBarsSinceCond = MRO(c crosses average(c,200),20,1) < 3

And c hasn’t crossed the 200-day moving average within the past twenty days the condition is still set to true because the function returns a -1.

I have created a function named BarsSince and you can set the false value to any value you wish.  In the aforementioned example, you would want the function to return a large number so the function would provide the correct solution.  Here’s how I did it:

inputs: 
	Test( truefalseseries ), 
	Length( numericsimple ), 
	Instance( numericsimple ) , { 0 < Instance <= Length}
	FalseReturnValue(numericsimple); {Return value if not found in length window}
	 
value1 = RecentOcc( Test, Length, Instance, 1 ) ;
If value1 = -1 then 
	BarsSince = FalseReturnValue
Else
	BarsSince = value1;
BarsSince Function Source Code

And here’s a strategy that uses the function:

inputs: profTarg$(2000),protStop$(1000),
rsiOBVal(60),rsiOSVal(40),slowAvgLen(100),
fastAvgLen(9),rsiLen(14),barsSinceMax(3);

Value1 = BarsSince(rsi(c,rsiLen) crosses above rsiOSVal,rsiLen,1,999);
Value2 = BarsSince(rsi(c,rsiLen) crosses below rsiOBVal,rsiLen,1,999);

If c > average(c, slowAvgLen) and c < average(c,fastAvgLen) and Value1 <barsSinceMax then buy next bar at open;

If c < average(c, slowAvgLen) and c > average(c,fastAvgLen) and Value2 <barsSinceMax then sellshort next bar at open;

setStopLoss(protStop$);
setProfitTarget(profTarg$)
Strategy Utilizing BarsSince Function

The function requires four arguments:

  1. The condition that is being tested [e.g.  rsi > crosses above 30]
  2. The lookback window [rsiLen – 14 bars in this case]
  3. Which occurrence [1 – most recent; 2- next most recent; etc…]
  4. False return value [999 in this case; if condition is not met in time]

A Simple Mean Reversion Using the Function:

Here are the results of this simple system utilizing the function.

Optimization Results:

I came up with this curve through a Genetic Optimization:

The BarsSince function adds flexibility or fuzziness when you want to test a condition but want to allow it to have a day (bar) or two tolerance.  In a more in-depth analysis, the best results very rarely occurred on the day the RSI crossed a boundary.   Email me with questions of course.

 

 

Please follow and like us:
error

EasyLanguage Code for Optimal F (Multi-Charts and VBA too!)

Optimal F in EasyLanguage for TradeStation and MultiCharts

Here is the code for the Optimal F calculation.  For a really good explanation of Optimal f, I refer you to Ralph Vince’s Book Portfolio Management FORMULAS.  We had programmed this years ago for our Excalibur software and I was surprised the EasyLanguage code was not really all that accessible on the internet.  Finding the Optimal f is found through an iterative process or in programmers terms a loop.  The code is really quite simple and I put it into a Function.  I decided to create this function because I wanted to demonstrate the ideas from my last post on how a function can store variable and array data.  Plus this code should be readily available somewhere out there.

//OptimalFGeo by George Pruitt
//My interpretation Sept. 2018
//www.georgepruitt.com
//georgeppruitt@gmail.com

input: minNumTrades(numericSimple);
vars: totalTradesCount(0),tradeCnt(0);
array: tradesArray[500](0);

vars: iCnt(00),jCnt(00),grandTot(0),highI(0);
vars: optF(0.0),gMean(0.0),fVal(0.0),HPR(0.0),TWR(0.0),hiTWR(0.0);
vars: biggestLoser(0.0),gat(0.0);

totalTradesCount = totalTrades;
If totalTradesCount > totalTradesCount[1] then
begin
	tradeCnt = tradeCnt + 1; 
	tradesArray[tradeCnt] = positionProfit(1);
end;

// Taken from my Fortran library - GPP and Vince Book PMF

optF = 0.0;
gMean = 1.00;
gat   = 0.00;
//Only calculate if new trade
IF(tradeCnt>minNumTrades and totalTradesCount > totalTradesCount[1]) then 
Begin
	biggestLoser = 0;
	grandTot = 0;
	For iCnt = 1 to tradeCnt //get the biggest loser
	begin
   		grandTot = grandTot + tradesArray[iCnt];
   		IF(tradesArray[iCnt]<biggestLoser) then biggestLoser = tradesArray[iCnt];
	end;
//	print(grandTot," ",biggestLoser);
	IF({grandTot > 0 and} biggestLoser <0) then 
	begin
//		print("Inside TWR Calculations");
		highI = 0;
		hiTWR = 0.0;
		for iCnt = 1 to 100
		begin
			fVal = .01 * iCnt;
			TWR = 1.0;
			for jCnt = 1 to tradeCnt // calculate the Terminal Wealth Relative
			begin
    			HPR = 1. + (fVal * (-1*tradesArray[jCnt]) / biggestLoser);
    			TWR = TWR * HPR;
 //   			print(fVal," ",iCnt," " ,jCnt," Trades ",tradesArray[jCnt]," HPR ",HPR:6:4," TWR : ",TWR:6:4," hiTWR",hiTWR:6:4," bl ",biggestLoser);
			end;
//			print(iCnt," ",TWR," ",hiTWR);
			IF(TWR>hiTWR) THEN
			begin
    			hiTWR = TWR;
    			optF = fVal;    	// assign optF to fVal in case its the correct one		
			end
			else
    			break;                     //highest f found - stop looping
		end;		
		If (TWR <= hiTWR or optF >= 0.999999) then
		begin
			TWR  = hiTWR;
			OptimalFGeo = optF;  //assign optF to the name of the function
		end;	
		gmean = power(TWR,(1.0 / tradeCnt));
		
		if(optF<>0) then GAT   = (gMean - 1.0) * (biggestLoser / -(optF));		
		print(d," gmean ",gmean:6:4," ",GAT:6:4);  // I calculate the GMEAN and GeoAvgTrade
	end;
end;
Optimal F Calculation by Ralph Vince code by George Pruitt

VBA version of Optimal F

For those of you who have a list of trades and want to see how this works in Excel here is the VBA code:

Sub OptimalF()

    Dim tradesArray(1000) As Double
    i = 0
    biggestLoser = 0#
    Do While (Cells(3 + i, 1) <> "")
        tradesArray(i) = Cells(3 + i, 1)
        If tradesArray(i) < bigLoser Then biggestLoser = tradesArray(i)
        i = i + 1
    Loop
    tradeCnt = i - 1
    highI = 0
    hiTWR = 0#
    rc = 3
    For fVal = 0.01 To 1 Step 0.01
        TWR = 1#
        For jCnt = 0 To tradeCnt
            HPR = 1# + (fVal * (-1 * tradesArray(jCnt)) / biggestLoser)
            TWR = TWR * HPR
            Cells(rc, 5) = jCnt
            Cells(rc, 6) = tradesArray(jCnt)
            Cells(rc, 7) = HPR
            Cells(rc, 8) = TWR
            rc = rc + 1
        Next jCnt
        Cells(rc, 9) = fVal
        Cells(rc, 10) = TWR
        rc = rc + 1

        If (TWR > hiTWR) Then
            hiTWR = TWR
            optF = fVal
        Else
            Exit For
        End If

    Next fVal
    If (TWR <= hiTWR Or optF >= 0.999999) Then
        TWR = hiTWR
        OptimalFGeo = optF
    End If
    Cells(rc, 8) = "Opt f"
    Cells(rc, 9) = optF
    rc = rc + 1
    gMean = TWR ^ (1# / (tradeCnt + 1))
    If (optF <> 0) Then GAT = (gMean - 1#) * (biggestLoser / -(optF))
    Cells(rc, 8) = "Geo Mean"
    Cells(rc, 9) = gMean
    rc = rc + 1
    Cells(rc, 8) = "Geo Avg Trade"
    Cells(rc, 9) = GAT

End Sub
VBA code for Optimal F

I will attach the eld and .xlsm file a little later.

 

 

 

Please follow and like us:
error

Multiple Ouput function in EasyLanguage

In the Pascal programming language you have Procedures and Functions.  Procedures are used when you want to modify multiple variables within a sub-program.  A function is a sub-program that returns a single value after it has been modified by say a formula.  EasyLanguage combines procedures and functions into one sub-program called a function.  Functions and procedures both have a formal parameter definition –  a list that describes the type of parameters that are being received by the calling program.  In Pascal procedures, you pass the address of the value that you want changed.  By modifying the contents of the address you can pass the value back and forth or in and out of the procedure.  In functions you pass by value.   Remember the parameter in a normal function call is used to instruct something within the body of the function and is not altered (e.g. the number 19 in value1 = average(c,19)).  This value doesn’t need to be modified it’s just used.  Look at the following code:

Here I am modifying mav1, mav2 and mav3 within the function and then passing the values back to the calling strategy/indicator/paintbar.  All functions must return a value so I simply assign the value 1 to the function name.  The key here is the keyword numericRef, once I change the values located in the addresses of mav1, mav2 and mav3 (address are provided by the keyword numericRef), they will be made available to the calling program.  This code allows the function to return more than just one value.

Please follow and like us:
error

Re-Entry After Taking A Profit

Here is some code I have been working on.  I will go into detail on the code a little later.  But this is how you monitor re-entering at a better price after taking a profit.  The problem with taking profits on longer term trend following systems is that the logic that got you into the position is probably still true and you will notice your algorithm will re-enter in the same direction.  So you need to inform your algorithm not to re-enter until a certain condition is met.  In this example, I only re-enter at a better price if the condition that got me into the trade is still valid.

Inputs: swingHiStrength(2),swingLowStrength(2),numDaysToLookBack(30),stopAmt$(500),profitAmt$(1000),getBackInAfterProfAmt$(250);
vars: mp(0),longProfTaken(false),shortProfTaken(false);

mp = marketPosition;

if not(longProfTaken) and mp <> 1 then Buy("BreakOut-B") next bar at highest(h[1],20) on a stop;
if not(shortProfTaken) and mp <>-1 then SellShort("BreakOut-S") next bar at lowest(l[1],20)  on a stop;

//If mp[0] = 0 and mp[1] = 1 then print(date," ",exitPrice(1)," ",entryPrice(1));

If longProfTaken then 
Begin
	If c < exitPrice(1) - getBackInAfterProfAmt$/bigPointValue then
	begin 
		longProfTaken = false;
		If mp <> -1 then buy("longRe-Entry") next bar at open;
	end;
end;

If shortProfTaken then 
Begin
	If c > exitPrice(1) + getBackInAfterProfAmt$/bigPointValue then
	begin 
		shortProfTaken = false;
		If mp <>-1 then sellShort("shortRe-Entry") next bar at open;
	end;
end;


If mp = 1 and c > entryPrice + profitAmt$/bigPointValue then 
begin
	sell this bar on close;
	longProfTaken = true;
end;

If mp =-1 and c < entryPrice - profitAmt$/bigPointValue then 
begin
	buyToCover this bar on close;
	shortProfTaken = true;
end;

If mp = -1 then longProfTaken = false;
If mp = 1 then shortProfTaken = false;

//if mp = 1 then setStopLoss(stopAmt$);
//if mp = 1 then setProfitTarget(profitAmt$);
Re-Entering At Better Price After Profit
Please follow and like us:
error

Learn to Program Pyramiding Algorithm

Would you like to learn how to do this?  Check back over the next few days and I will show you to do it.  Warning:  its not straightforward as it seems – some tricks are involved.  Remember to sign up for email notifications of new posts.

UPDATE[1]:  I have recorded an introductory webcast on how to program this pyramiding scheme.  This webcast is Part 1 and illustrates how to brainstorm and start thinking/programming about a problem.  Part 1 introduces some concepts that show how you can use and adapt some of EasyLanguage built-in reserved words and functions.  I start from the perspective of a somewhat beginning EasyLanguage programmer  – one that knows enough to maybe not get the problem solved, but at least get the ball rolling.  The final code may not look anything like the code I present in Part 1.  However it is sometimes important to go down the wrong trail so that you can learn the limitations of a programming language.  Once you know the limitations, you can go about programming workarounds and fixes.  I hope you enjoy Part 1  I should have Part 2 up soon.  Don’t be too critical, this is really the first webcast I have recorded.  You’ll notice I repeat myself and I refer  to one function input as a subscript.  Check it out:  https://youtu.be/ip-DyyKpOTo

Adding positions at fixed intervals.
Please follow and like us:
error

Hash Table In EasyLanguage [Part 2]

Using The Hash Table

Now that we have created an empty Hash Table and the Hash Index it is now time to start filling the table up with the appropriate information.  As I pointed out in my last post, every day of any given year can be represented by a nine character string. If January 1st lands on a Tuesday, you can express this day with the following string, “1stTueJan.” That is if you want to ignore the year and in this case, we do.

Mapping Into the Hash Table

The table has already been prepared as well as the index.  All we have to do is map the current day into the index.  The location of the index value in the Hash Index array will then be used to locate the day’s location in the Hash Table.  We will use a function to convert the current day of the year into a value our Hash Index can interpret.

Here is the code to the function.  Don’t fret too much at the number of lines of code!

inputs: testDate(numericSeries);

vars: testMonth(0),tempStr("");
Array : prefixStrArr[6](""),dayofweekStr[5](""),monthName[12]("");
vars: monCnt(0),tueCnt(0),wedCnt(0),thuCnt(0),friCnt(0),tempDate1(0),tempDate2(0);
vars: freshStart(false),occurString(""),dayString(""),monthString("");
vars: whatOccurOfMonthStr(""),cnt(0),td(0),myCnt(0),daysBack(0);

preFixStrArr[1] = "1st";
preFixStrArr[2] = "2nd";
preFixStrArr[3] = "3rd";
preFixStrArr[4] = "4th";
preFixStrArr[5] = "5th";
preFixStrArr[6] = "6th";

dayOfWeekStr[1] = "Mon";
dayOfWeekStr[2] = "Tue";
dayOfWeekStr[3] = "Wed";
dayofWeekStr[4] = "Thu";
dayOfWeekStr[5] = "Fri";

monthName[1] = "Jan";
monthName[2] = "Feb";
monthName[3] = "Mar";
monthName[4] = "Apr";
monthName[5] = "May";
monthName[6] = "Jun";
monthName[7] = "Jul";
monthName[8] = "Aug";
monthName[9] = "Sep";
monthName[10] = "Oct";
monthName[11] = "Nov";
monthName[12] = "Dec";

tempDate1 = month(testDate[0]);
tempDate2 = month(testDate[1]);
cnt = 0;monCnt = 0;tueCnt=0;wedCnt=0;thuCnt=0;friCnt=0;
While (month(date) = month(date[cnt])) and cnt < 30
Begin
//	print(date," ",date[cnt]," ",cnt);
	cnt = cnt + 1;
end;
daysBack = cnt -1;

If daysBack < 0 then daysBack = 0;

For cnt = daysBack downto 0
begin
	If dayOfWeek(date[cnt]) = 1 then monCnt = monCnt + 1;
	If dayOfWeek(date[cnt]) = 2 then tueCnt = tueCnt + 1;	
	If dayOfWeek(date[cnt]) = 3 then wedCnt = wedCnt + 1;
	If dayOfWeek(date[cnt]) = 4 then thuCnt = thuCnt + 1;
	If dayOfWeek(date[cnt]) = 5 then friCnt = friCnt + 1;
end;
//print("counts: ",monCnt," ",tueCnt," ",wedCnt," ",thuCnt," ",friCnt);

If dayOfWeek(date) = Monday then tempStr = preFixStrArr[monCnt];
If dayOfWeek(date) = Tuesday then tempStr = preFixStrArr[tueCnt];
If dayOfWeek(date) = Wednesday then tempStr = preFixStrArr[wedCnt];
If dayOfWeek(date) = Thursday then tempStr = preFixStrArr[thuCnt];
If dayOfWeek(date) = Friday then tempStr = preFixStrArr[friCnt];

tempStr = tempStr + dayOfWeekStr[dayOfWeek(date)];
tempStr = tempStr + monthName[month(date)];
GetWhichWeekMonth = tempStr;
GetWhichWeekMonth Function

Here is where using an integer representation of the date would reduce the number of lines of code tremendously.  Well, I made my bed I might as well sleep in it.  You will see some duplication between this code and the Hash Table creator function.  I have to store names for the week rank, day of the week, and month in arrays.  There isn’t a simple function that will pull the week rank from any given date.  So I simply take the date and work my way back to the beginning of the month counting each weekday as I go along.

For cnt = daysBack downto 0
begin
	If dayOfWeek(date[cnt]) = 1 then monCnt = monCnt + 1;
	If dayOfWeek(date[cnt]) = 2 then tueCnt = tueCnt + 1;	
	If dayOfWeek(date[cnt]) = 3 then wedCnt = wedCnt + 1;
	If dayOfWeek(date[cnt]) = 4 then thuCnt = thuCnt + 1;
	If dayOfWeek(date[cnt]) = 5 then friCnt = friCnt + 1;
end;

Getting The Hash Index

The number that is stored in the individual counters (monCnt, tueCnt, etc.) determines which week of the month the current day is located.  I build the string through concatenation.  First I get the week rank (“1st”, “2nd”, “3rd”, “4th”, “5th”), add the name of the day and then add the month.  The end result looks like “1stMonJan”.  From here I cross-reference the Hash Index and pull out the location of the of the string (aka index.)  Here is the function GetHashIndex.

input: hashIndex[n](stringArrayRef),hashTableRows(numericSimple),searchString(string);
vars: iCnt(0),done(false);

GetHashIndex = 0;
done = false;

For iCnt = 1 to hashTableRows
Begin
//	print("Looking for: ",searchString," ",hashIndex[iCnt]," ",iCnt);
	If searchString = hashIndex[iCnt] then 
	begin
		done = true;
		GetHashIndex = iCnt;
	end;
	If done then break;
end;
GetHashIndex

As you can see it is a linear search that returns the Hash Index’s Index.  Check out how I prematurely exit the loop by using the keyword Break.  This keyword knocks you out of any loop where it is located.  If you have a nested loop, the break only gets you out of that current loop where it is located.

Hast Table Indicator

Now how can we pull all this together to create a useful trading tool.  I used these tools to create an indicator that plots the average daily change from one day to the next.  So, if today is the “3rdMonJune” and the indicator reads 0.52, this represents that over the last X years the average percentage change is a plus .5%.  Would it make sense to buy the “2ndFriJun” and exit on the close of the “3rdMonJune?”  Maybe.

Here is the code for the Hash Table indicator.

vars: returnValString(""),iCnt(0),jCnt(0); 
array: weekDayMonthIndex[300]("");
array: HashTable[300,100](0);
array: timeLine[300](0);
vars: searchString(""),numYearsCollected(0),hashIndex(0);
vars: yCnt(0),numYears(0);
vars: hashRows(300);
vars: myBarCount(0),maxNumYearsInHash(0),avgDailyChange(0),dailyChangeSum(0);

If barNumber = 1 then  //build the hash index - index form "1stMonJul" "2ndFriDec"
begin
	Value1 = HashIndexCreator(weekDayMonthIndex);
end;

numYearsCollected = HashTableCreator(HashTable,weekDayMonthIndex);  {Build hash table as we go along}

If year(date) <> year(date[1]) then numYears = numYears + 1; 

If numYearsCollected > 3 then  // only pull information if there is at least three years of data
Begin
	searchString = GetWhichWeekMonth(date);	// convert today's date into a compatible Hash Index value
	hashIndex = GetHashIndex(weekDayMonthIndex,hashRows,searchString);  // find the location of today's value in the Hash Index
	dailyChangeSum = 0;;
//	print(d," ",searchString," ",hashIndex);
	For yCnt = 2 to numYearsCollected
	Begin
		dailyChangeSum = dailyChangeSum + HashTable[hashIndex,yCnt];
	end;
	avgDailyChange = dailyChangeSum/numYearsCollected;
	if year(date) = 116 then print(d," ",searchString," ",numYearsCollected," ",avgDailyChange);
	if numYearsCollected > numYears-1  then plot1(avgDailyChange,"AvgChgFromYesterday");
End;
HashTableIndicator

Results of Using the Hash Table

Here is a simple output of the results from the indicator for the year of 2016.  I sorted the data based on highest average daily change and number of years collected.

1160729 5thFriJul 7 0.95
1161031 5thMonOct 5 0.62
1160115 2ndFriJan 16 0.56
1160830 5thTueAug 7 0.55
1160713 2ndWedJul 17 0.52
1160812 2ndFriAug 17 0.52
1160519 3rdThuMay 16 0.43
1161003 1stMonOct 17 0.38
1160112 2ndTueJan 16 0.38
1160223 4thTueFeb 16 0.38
1161122 4thTueNov 16 0.37
1160804 1stThuAug 17 0.35
1160316 3rdWedMar 16 0.35
1160711 1stMonJul 17 0.34
1161121 3rdMonNov 17 0.34
1160225 4thThuFeb 16 0.34
1160517 3rdTueMay 16 0.34
1160610 2ndFriJun 16 0.34
1161215 3rdThuDec 17 0.33

It looks like the buying the close “4thThuJul” is the way to go!  But since there are only seven observations I think would think twice.  But, buying the close on the day prior to “2ndFriJan” might offer that technical advantage you’re looking for.

Please follow and like us:
error