Category Archives: Complete EasyLanguage Systems

The Complete Turtle EasyLanguage [Well About as Close as You Can Get]

The Complete Turtle EasyLanguage – Almost!

I have seen a plethora of posts on the Turtle trading strategies where the rules and code are provided.  The codes range from a mere Donchian breakout to a fairly close representation.  Without dynamic portfolio feedback its rather impossible to program the portfolio constraints as explained by Curtis Faith in his well received “Way Of The Turtle.”  But the other components can be programmed rather closely to Curtis’ descriptions.   I wanted to provide this code in a concise manner to illustrate some of EasyLanguage’s esoteric constructs and some neat shortcuts.  First of all let’s take a look at how the system has performed on Crude for the past 15 years.

Turtle Performance on Crude past 15 years

If a market trends, the Turtle will catch it.  Look how the market rallied in 2007 and immediately snapped back in 2008, and look at how the Turtle caught the moves – impressive.  But see how the system stops working in congestion.  It did take a small portion of the 2014 move down and has done a great job of catching the pandemic collapse and bounce.  In my last post, I programmed the LTL (Last Trader Loser) function to determine the success/failure of the Turtle System 1 entry.  I modified it slightly for this post and used it in concert with Turtle System 2 Entry and the 1/2N AddOn pyramid trade to get as close as possible to the core of the Turtle Entry/Exit logic.

Can Your Program This – sure you CAN!

Can You Program This?

I will provide the ELD so you can review at your leisure, but here are the important pieces of the code that you might not be able to derive without a lot of programming experience.

If mp[1] <> mp and mp <> 0 then 
begin
	if mp = 1 then
	begin	
		origEntry = entryPrice;
		origEntryName = "Sys1Long";
		If ltl = False and h >= lep1[1] then origEntryName = "Sys2Long";
	end;
	if mp =-1 then
	begin	
		origEntry = entryPrice;
		origEntryName = "Sys1Short";
		If ltl = False and l <= sep1[1] then origEntryName = "Sys2Short";
	end;
end;
Keeping Track Of Last Entry Signal Price and Name

This code determines if the current market position is not flat and is different than the prior bar’s market position.  If this is the case then a new trade has been executed.  This information is needed so that you know which exit to apply without having to forcibly tie them together using EasyLanguage’s from Entry keywords.  Here I just need to know the name of the entry.  The entryPrice is the entryPrice.  Here I know if the LTL is false, and the entryPrice is equal to or greater/less  than (based on current market position) than System 2 entry levels, then I know that System 2 got us into the trade.

If mp = 1 and origEntryName = "Sys1Long" then Sell currentShares shares next bar at lxp stop;
If mp =-1 and origEntryName = "Sys1Short" then buyToCover currentShares shares next bar at sxp stop;

//55 bar component - no contingency here
If mp = 0 and ltl = False then buy("55BBO") next bar at lep1 stop;
If mp = 1 and origEntryName = "Sys2Long" then sell("55BBO-Lx") currentShares shares next bar at lxp1 stop;

If mp = 0 and ltl = False then sellShort("55SBO") next bar at sep1 stop;
If mp =-1 and origEntryName = "Sys2Short" then buyToCover("55SBO-Sx") currentShares shares next bar at sxp1 stop;
Entries and Exits

The key to this logic is the keywords currentShares shares.  This code tells TradeStation to liquidate all the current shares or contracts at the stop levels.  You could use currentContracts contracts if you are more comfortable with futures vernacular.

AddOn Pyramiding Signal Logic

Before you can pyramid you must turn it on in the Strategy Properties.

Turn Pyramiding ON
If mp = 1 and currentShares < 4 then buy("AddonBuy") next bar at entryPrice + (currentShares * .5*NValue) stop;
If mp =-1 and currentShares < 4 then sellShort("AddonShort") next bar at entryPrice - (currentShares * .5*NValue) stop;

This logic adds positions on from the original entryPrice in increments of 1/2N.  The description for this logic is a little fuzzy.  Is the N value the ATR reading when the first contract was put on or is it dynamically recalculated?  I erred on the side of caution and used the N when the first contract was put on.  So to calculate the AddOn long entries you simply take the original entryPrice and add the currentShares * .5N.  So if currentShares is 1, then the next pyramid level would be entryPrice + 1* .5N.  If currentShares is 2 ,then entryPrice + 2* .5N and so on an so forth.  The 2N stop trails from the latest entryPrice.  So if you put on 4 contracts (specified in Curtis’ book), then the trailing exit would be 2N from where you added the 4th contract.  Here is the code for that.

Liquidate All Contracts at Last Entry –  2N

vars: lastEntryPrice(0);
If cs <= 1 then lastEntryPrice = entryPrice;
If cs > 1 and cs > cs[1] and mp = 1 then lastEntryPrice = entryPrice + ((currentShares-1) * .5*NValue);
If cs > 1 and cs > cs[1] and mp =-1 then lastEntryPrice = entryPrice - ((currentShares-1) * .5*NValue);

//If mp = -1 then print(d," ",lastEntryPrice," ",NValue);

If mp = 1 then sell("2NLongLoss") currentShares shares next bar at lastEntryPrice-2*NValue stop;
If mp =-1 then buyToCover("2NShrtLoss") currentShares shares next bar at lastEntryPrice+2*NValue Stop;
Calculate Last EntryPrice and Go From There

I introduce a new variable here: cs.  CS stands for currentShares and I keep track of it from bar to bar.  If currentShares or cs is less than or equal to1 I know that the last entryPrice was the original entryPrice.  Things get a little more complicated when you start adding positions – initially I couldn’t remember if EasyLanguage’s entryPrice contained the last entryPrice or the original – turns out it is the original – good to know.  So, if currentShares is greater than one and the current bar’s currentShares is greater than the prior bar’s currentShares, then I know I added on another contract and therefore must update lastEntryPrice.  LastEntryPrice is calculated by taking the original entryPrice and adding (currentShares-1) * .5N.  Now this is the theoretical entryPrice, because I don’t take into consideration slippage on entry.  You could make this adjustment.  So, once I know the lastEntryPrice I can determine 2N from that price.

Getting Out At 2N Trailing Stop

If mp = 1 then sell("2NLongLoss") currentShares shares next bar at lastEntryPrice-2*NValue stop;
If mp =-1 then buyToCover("2NShrtLoss") currentShares shares next bar at lastEntryPrice+2*NValue Stop;
Get Out At LastEntryPrice +/-2N

That’s all of the nifty code.  Below is the function and ELD for my implementation of the Turtle dual entry system.   You will see some rather sophisticated code when it comes to System 1 Entry and this is because of these scenarios:

  • What if you are theoretically short and are theoretically stopped out for a true loser and you can enter on the same bar into a long trade.
  • What if you are theoretically short and the reversal point would result in a losing trade.  You wouldn’t  record the loser in time to enter the long position at the reversal point.
  • What if you are really short and the reversal point would results in a true loser, then you would want to allow the reversal at that point

There are probably some other scenarios, but I think I covered all bases.  Just let me know if that’s not the case.  What I did to validate the entries was I programmed a 20/10 day breakout/failure with a 2N stop and then went through the list and deleted the trades that followed a non 2N loss (10 bar exit for a loss or a win.)  Then I made sure those trades were not in the output of the complete system.  There was quite a bit of trial and error.  If you see a mistake, like I said, just let me know.

Remember I published the results of different permutations of this strategy incorporating dynamic portfolio feedback at my other site www.trendfollowingsystems.com.  These results reflect the a fairly close portfolio that Curtis suggests in his book.

TURTLELTLFUNCTEST

 

Please follow and like us:

Turn of the Month Trading Strategy [Stock Indices Only]

The System

This system has been around for several years.  Its based on the belief that fund managers start pouring money into the market near the end of the month and this creates momentum that lasts for just a few days.  The original system states to enter the market on the close of the last bar of the day if the its above a certain moving average value.  In the Jaekle and Tomasini book, the authors describe such a trading system.  Its quite simple, enter on the close of the month if its greater than X-Day moving average and exit either 4 days later or if during the trade the closing price drops below the X-Day moving average.

EasyLanguage or Multi-Charts Version

Determining the end of the month should be quite easy -right?  Well if you want to use EasyLanguage on TradeStation and I think on Multi-Charts you can’t sneak a peek at the next bar’s open to determine if the current bar is the last bar of the month.  You can try, but you will receive an error message that you can’t mix this bar on close with next bar.  In other words you can’t take action on today’s close if tomorrow’s bar is the first day of the month.  This is designed, I think, to prevent from future leak or cheating.  In TradeStation the shift from backtesting to trading is designed to be a no brainer, but this does provide some obstacles when you only want to do a backtest.

LDOM function – last day of month for past 15 years or so

So I had to create a LastDayOfMonth function.  At first I thought if the day of the month is the 31st then it is definitely the last bar of the month.  And this is the case no matter what.  And if its the 30th then its the last day of the month too if the month is April, June, Sept, and November.  But what happens if the last day of the month falls on a weekend.  Then if its the 28th and its a Friday and the month is blah, blah, blah.  What about February?  To save time here is the code:

Inputs: movAvgPeriods(50);
vars: endOfMonth(false),theDayOfWeek(0),theMonth(0),theDayOfMonth(0),isLeapYear(False);

endOfMonth = false;
theDayOfWeek = dayOfWeek(date);
theMonth = month(date);
theDayOfMonth = dayOfMonth(date);
isLeapYear = mod(year(d),4) = 0;

// 29th of the month and a Friday
if theDayOfMonth = 29 and theDayOfWeek = 5 then 
	endOfMonth = True;
// 30th of the month and a Friday
if theDayOfMonth = 30 and theDayOfWeek = 5 then 
	endOfMonth = True;
// 31st of the month 	
if theDayOfMonth = 31 then 
	endOfMonth = True;
// 30th of the month and April, June, Sept, or Nov
if theDayOfMonth = 30 and (theMonth=4 or theMonth=6 or theMonth=9 or theMonth=11) then 
	endOfMonth = True;
// 28th of the month and February and not leap year
if theDayOfMonth = 28 and theMonth = 2 and not(isLeapYear)  then 
	endOfMonth = True;
// 29th of the month and February and a leap year or 28th, 27th and a Friday	
if theMonth = 2 and isLeapYear then
Begin
	If theDayOfMonth = 29 or ((theDayOfMonth = 28 or theDayOfMonth = 27) and theDayOfWeek = 5) then 
	endOfMonth = True;	
end;
// 28th of the month and Friday and April, June, Sept, or Nov
if theDayOfMonth = 28 and (theMonth = 4 or theMonth = 6 or 
	theMonth = 9 or theMonth =11) and theDayOfWeek = 5 then
	endOfMonth = True;
// 27th, 28th of Feb and Friday	
if theMonth = 2 and theDayOfWeek = 5 and theDayOfMonth = 27 then
	endOfMonth = True;
// 26th of Feb and Friday and not LeapYear
if theMonth = 2 and theDayOfWeek = 5 and theDayOfMonth = 26 and not(isLeapYear) then
	endOfMonth = True;	
// Memorial day adjustment
If theMonth = 5 and theDayOfWeek = 5 and theDayOfMonth = 28 then
	endOfMonth = True;
//Easter 2013 adjustment
If theMonth = 3 and year(d) = 113 and theDayOfMonth = 28 then
	endOfMonth = True;
//Easter 2018 adjustment
If theMonth = 3 and year(d) = 118 and theDayOfMonth = 29 then
	endOfMonth = True;	

if endOfMonth and c > average(c,movAvgPeriods) then	
	Buy("BuyDay") this bar on close;

If C <average(c,movAvgPeriods) then 
	Sell("MovAvgExit") this bar on close;
If BarsSinceEntry=4 then 
	Sell("4days") this bar on close;
Last Day Of Month Function and Strategy

All the code is generic except for the hard code for days that are a consequence of Good Friday.

All this code because I couldn’t sneak a peek at the date of tomorrow.  Here are the results of trading the ES futures sans execution costs for the past 15 years.

Last Day Of Month Buy If C > 50 Day Mavg

What if it did the easy way and executed the open of the first bar of the month.

If c > average(c,50) and month(d) <> month(d of tomorrow) then 
	buy next bar at open;

If  barsSinceEntry >=3 then 
	sell next bar at open;

If marketPosition = 1 and c < average(c,50) then 
	sell next bar at open;
Buy First Day Of Month
First Day of Month If C > 50 Day Mavg

The results aren’t as good but it sure was easier to program.

TradingSimula-18 Version

Since you can use daily bars we can test this with my TradingSimula-18 Python platform.  And we will execute on the close of the month.  Here is the snippet of code that you have to concern yourself with.  Here I am using Sublime Text and utilizing their text collapsing tool to hide non-user code:

Small Snippet of TS-18 Code

This was easy to program in TS-18 because I do allow Future Leak – in other words I will let you sneak a peek at tomorrow’s values and make a decision today.  Now many people might say this is a huge boo-boo, but with great power comes great responsibility.  If you go in with eyes wide open, then you will only use the data to make things easier or even doable, but without cheating.  Because you are only going to cheat yourself.  Its in your best interest do follow the rules.  Here is the line that let’s you leak into the future.

If isNewMonth(myDate[curBar+1])

The curBar is today and curBar+1 is tomorrow.  So I am saying if tomorrow is the first day of the month then buy today’s close.  Here you are leaking into the future but not taking advantage of it.  We all know if today is the last day of the month, but try explaining that to a computer.  You saw the EasyLanguage code.  So things are made easier with future leak, but not taking advantage of .

Here is a quick video of running the TS-18 Module of 4 different markets.

 

Please follow and like us:

A Quant’s ToolBox: Beautiful Soup, Python, Excel and EasyLanguage

Many Times It Takes Multiple Tools to Get the Job Done

Just like a mechanic, a Quant needs tools to accomplish many programming tasks.  In this post, I use a toolbox to construct an EasyLanguage function that will test a date and determine if it is considered a Holiday in the eyes of the NYSE.

Why a Holiday Function?

TradeStation will pump holiday data into a chart and then later go back and take it out of the database.  Many times the data will only be removed from the daily database, but still persist in the intraday database.  Many mechanical day traders don’t want to trade on a shortened holiday session or use the data for indicator/signal calculations.  Here is an example of a gold chart reflecting President’s Day data in the intra-day data and not in the daily.

Holiday Data Throws A Monkey Wrench Into the Works

This affects many stock index day traders.  Especially if automation is turned on.  At the end of this post I provide a link to my youTube channel for a complete tutorial on the use of these tools to accomplish this task.  It goes along with this post.

First Get The Data

I searched the web for a list of historical holiday dates and came across this:

Historic List of Holidays and Their Dates

You might be able to find this in a easier to use format, but this was perfect for this post.

Extract Data with Beautiful Soup

Here is where Python and the plethora of its libraries come in handy.  I used pip to install the requests and the bs4 libraries.  If this sounds like Latin to you drop me an email and I will shoot you some instructions on how to install these libraries.  If you have Python, then you have the download/install tool known as pip.

Here is the Python code.  Don’t worry it is quite short.

# Created:     24/02/2020
# Copyright:   (c) George 2020
# Licence:     <your licence>
#-------------------------------------------------------------------------------

import requests
from bs4 import BeautifulSoup

url = 'http://www.market-holidays.com/'
page = requests.get(url)
soup = BeautifulSoup(page.text,'html.parser')
print(soup.title.text)
all_tables = soup.findAll('table')
#print (all_tables)
print (len(all_tables))
#print (all_tables[0])
print("***")
a = list()
b = list()
c = list()
#print(all_tables[0].find_all('tr')[0].text)
for numTables in range(len(all_tables)-1):
    for rows in all_tables[numTables].find_all('tr'):
        a.append(rows.find_all('td')[0].text)
        b.append(rows.find_all('td')[1].text)

for j in range(len(a)-1):
    print(a[j],"-",b[j])
Using Beautiful Soup to Extract Table Data

As you can see this is very simple code.  First I set the variable url to the website where the holidays are located.  I Googled on how to do this – another cool thing about Python – tons of users.  I pulled the data from the website and stuffed it into the page object.  The page object has several attributes (properties) and one of them  is a text representation of the entire page.  I pass this text to the BeautifulSoup library and inform it to parse it with the html.parser.  In other words, prepare to extract certain values based on html tags.  All_tables contains all of the tables that were parsed from the text file using Soup.  Don’t worry how this works, as its not important, just use it as a tool.  In my younger days as a programmer I would have delved into how this works, but it wouldn’t be worth the time because I just need the data to carry out my objective; this is one of the reasons classically trained programmers never pick up the object concept.  Now that I have all the tables in a list I can loop through each row in each table.  It looked liker there were 9 rows and 2 columns in the different sections of the website, but I didn’t know for sure so I just let the library figure this out for me.  So I played around with the code and found out that the first two columns of the table contained the name of the holiday and the date of the holiday.  So, I simply stuffed the text values of these columns in two lists:  a and b.  Finally I print out the contents of the two lists, separated by a hyphen, into the Interpreter window.  At this point I could simply carry on with Python and create the EasyLanguage statements and fill in the data I need.  But I wanted to play around with Excel in case readers didn’t want to go the Python route.  I could have used a powerful editor such as NotePad++ to extract the data from the website in place of Python.  GREP could have done this.  GREP is an editor tool to find and replace expressions in a text file.

Use Excel to Create Actual EasyLanguage – Really!

I created a new spreadsheet.  I used Excel, but you could use any spreadsheet software.   I first created a prototype of the code I would need to encapsulate the data into array structures.  Here is what I want the code to look like:

Arrays: holidayName[300](""),holidayDate[300](0);

holidayName[1]="New Year's Day ";	holidayDate[1]=19900101;
Code Prototype

This is just the first few lines of the function prototype.  But you can notice a repetitive pattern.  The array names stay the same – the only values that change are the array elements and the array indices.  Computers love repetitiveness.  I can use this information a build a spreadsheet – take a look.

Type EasyLanguage Into the Columns and Fill Down!

I haven’t copied the data that I got out of Python just yet.  That will be step 2.  Column A has the first array name holidayName (notice I put the left square [ bracket in the column as well).  Column B will contain the array index and this is a formula.  Column C contains ]=”.  Column D will contain the actual holiday name and Column E contains theThese columns will build the holidayName array.  Columns G throuh K will build the holidayDates array.    Notice column  H  equals column B.  So whatever we do to column B (Index) will be reflected in Column H (Index).  So we have basically put all the parts of the EasyLanguage into  Columns A thru K. 

Excel provides tools for manipulating strings and text.  I will use the Concat function to build my EasyLanguageBut before I can use Concat all the stuff I want to string together must be in a string or text format.  The only column in the first five that is not a string is Column B.  So the first thing I have to do is convert it to text.  First copy the column and paste special as values.  Then go to your Data Tab and select Text To Columns. 

Text To Columns

It will ask if fixed width or delimited – I don’t think it matters which you pick.  On step 3 select text.

Text To Columns – A Powerful Tool

The Text To Columns button will solve 90% of your formatting issues in Excel.    Once you do this you will notice the numbers will be left justified – this signifies a text format.  Now lets select another sheet in the workbook and past the holiday data.

Copy Holiday Data Into Another Spreadsheet

New Year's Day - January 1, 2021
Martin Luther King, Jr. Day - January 18, 2021
Washington's Birthday (Presidents' Day) - February 15, 2021
Good Friday - April 2, 2021
Memorial Day - May 31, 2021
Independence Day - July 5, 2021
Labor Day - September 6, 2021
Thanksgiving - November 25, 2021
Christmas - December 24, 2021
New Year's Day - January 1, 2020
Martin Luther King, Jr. Day - January 20, 2020
Washington's Birthday (Presidents' Day) - February 17, 2020
Good Friday - April 10, 2020
Memorial Day - May 25, 2020
Holiday Output

 

Data Is In Column A

Text To Columns to the rescue.  Here I will separate the data with the “-” as delimiter and tell Excel to import the second column in Date format as MDY.  

Text To Columns with “-” as the delimiter and MDY as Column B Format

Now once the data is split accordingly into two columns with the correct format – we need to convert the date column into a string.

Convert Date to a String

Now the last couple of steps are really easy.  Once you have converted the date to a string, copy Column A and past into Column D from the first spreadsheet.  Since this is text, you can simply copy and then paste.  Now go back to Sheet 2 and copy Column C and paste special [values] in Column J on Sheet 1.  All we need to do now is concatenate the strings in Columns A thru E for the EasyLanguage for the holidayName array.  Columns G thru K will be concatenated for the holidayDate array.  Take a look.

Concatenate all the strings to create the EasyLanguage

Now create a function in the EasyLanguage editor and name it IsHoliday and have it return a boolean value.  Then all you need to do is copy/paste Columns F and L and the data from the website will now be available for you use.   Here is a portion of the function code.  Notice I declare the holidayNameStr as a stringRef?  I did this so I could change the variable in the function and pass it back to the calling routine.

Inputs : testDate(numericSeries),holidayNameStr(stringRef);

Arrays: holidayName[300](""),holidayDate[300](0);

holidayNameStr = "";

holidayName[1]="New Year's Day ";	holidayDate[1]=19900101;
holidayName[2]="Martin Luther King, Jr. Day ";	holidayDate[2]=19900115;
holidayName[3]="Washington's Birthday (Presidents' Day) ";	holidayDate[3]=19900219;
holidayName[4]="Good Friday ";	holidayDate[4]=19900413;
holidayName[5]="Memorial Day ";	holidayDate[5]=19900528;
holidayName[6]="Independence Day ";	holidayDate[6]=19900704;
holidayName[7]="Labor Day ";	holidayDate[7]=19900903;
holidayName[8]="Thanksgiving ";	holidayDate[8]=19901122;
holidayName[9]="New Year's Day ";	holidayDate[9]=19910101;
holidayName[10]="Martin Luther King, Jr. Day ";	holidayDate[10]=19910121;
holidayName[11]="Washington's Birthday (Presidents' Day) ";	holidayDate[11]=19910218;

// There are 287 holiays in the database.
// Here is the looping mechanism to compare the data that is passed
// to the database

vars: j(0);
IsHoliday = False;
For j=1 to 287
Begin
	If testDate = holidayDate[j] - 19000000 then
	Begin
		holidayNameStr = holidayName[j] + " " + numToStr(holidayDate[j],0);
		IsHoliday = True;
	end;
end;
A Snippet Of The Function - Including Header and Looping Mechanism

This was a pretty long tutorial and might be difficult to follow along.  If you want to watch my video, then go to this link.

I created this post to demonstrate the need to have several tools at your disposal if you really want to become a Quant programmer.  How you use those tools is up to you.  Also you will be able to take bits and pieces out of this post and use in other ways to get the data you really need.  I could have skipped the entire Excel portion of the post and just did everything in Python.  But I know a lot of Quants that just love spreadsheets.  You have to continually hone your craft in this business.   And you can’t let one software application limit your creativity.  If you have a problem always be on the lookout for alternative platforms and/or languages to help you solve it.

 

 

Please follow and like us:

Get My Latest Book-TrendFollowing Systems: A DIY Project – Batteries Included

Just wanted to let you know that my latest book has just been published.

Trend Following Systems: A DIY Project – Batteries Included: Can You Reboot and Fix Yesterday’s Algorithms to Work with Today’s Markets? 

Trend Following Systems: A DIY Project – Batteries Included

This book introduces my new Python back-tester, TradingSimula-18.  It is completely and I mean completely self contained.  All you need is the latest version of Python and you will be up and running trading systems in less than 5 minutes.  Fifteen years of data on 30 futures is included (data from Quandl).  I have included more than 20 scripts that you can test and build on.   This back-tester is different than the one I published in the Ultimate Algorithmic Trading System Toolbox.  It utilizes what I call the horizontal portfolio spanning paradigm.  Instead of sequentially testing different markets in a portfoio:

It process data in the following manner:

This form of testing allows for decisions to be made on a portfolio basis at the end of any historic bar.   Things like inputting portfolio performance into an allocation formula is super simple.  However, this paradigm opens up a lot of different “what-if” scenarios.

  1. What If I Limit 2 Markets Per Sector
  2. What If I Turn Off A Certain Sector
  3. What If I Liquidate The Largest OTE loser
  4. What If I Liquidate The Largest OTE winner
  5. What If I Only Trade The Ten Markets With The Highest ADX Values

All the data and market performance and portfolio performance is right at your fingertips.  Your testing is only limited by your creativity.

The best part is you get to learn raw Python without having to install complicated libraries like SciKit, Numpy or Pandas.  You don’t even need to install distributions of commercial products – like Anaconda.  Don’t get me wrong I think Anaconda is awesome but many times it is overkill.  If you want to do machine learning then that is the way to go.  If you want to test simple Trend Following algorithms and make portfolio level decisions you don’t need a data science application.

There isn’t a complicated interface to learn.  Its all command line driven from Python’s IDLE.  90% of the source code is revealed for the back-testing software.  Its like one of those see-thru calculators.  You see all the circuits and semiconductors, but in Python.  So you will need to flow through the code to get to the sections that pertain to your test.  Here is a small sample of how you set up the testing parameters for a Donchian Script.

 

#--------------------------------------------------------------------------------
#   If you want to ignore a bunch of non-eseential stuff then
#      S C R O L L   A L M O S T  H A L F   W A Y  D O W N
#--------------------------------------------------------------------------------
#TradingSimula18.py - programmed by George Pruitt
#Built on the code and ideas from "The Ultimate Algorithmic Tradins System T-Box"
#Code is broken into sections
#Most sections can and should be ignored
#Each trading algorithm must be programmed with this template
#This is the main entry into the platform
#--------------------------------------------------------------------------------
#Import Section - inlcude functions, classes, variables from external modules
#--------------------------------------------------------------------------------
# --- Do  not change below here
from getData import getData
from equityDataClass import equityClass
from tradeClass import tradeInfo
from systemMarket import systemMarketClass
from indicators import highest,lowest,rsiClass,stochClass,sAverage,bollingerBands
from indicators import highest,lowest,rsiClass,stochClass,sAverage,bollingerBands,\
    adxClass,sAverage2
from portfolio import portfolioClass
from systemAnalytics import calcSystemResults
from utilityFunctions import getDataAtribs,getDataLists,roundToNearestTick,calcTodaysOTE
from utilityFunctions import setDataLists,removeDuplicates
from portManager import portManagerClass,systemMarkTrackerClass
from positionMatrixClass import positionMatrixClass
from barCountCalc import barCountCalc

from sectorClass import sectorClass, parseSectors, numPosCurrentSector,getCurrentSector
#-------------------------------------------------------------------------------------------------
# Pay no attention to these two functions - unless you want to
#-------------------------------------------------------------------------------------------------
def exitPos(myExitPrice,myExitDate,tempName,myCurShares):
    global tradeName,entryPrice,entryQuant,exitPrice,numShares,myBPV,cumuProfit
    if mp < 0:
        trades = tradeInfo('liqShort',myExitDate,tempName,myExitPrice,myCurShares,0)
        profit = trades.calcTradeProfit('liqShort',mp,entryPrice,myExitPrice,entryQuant,myCurShares) * myBPV
        profit = profit - myCurShares *commission;trades.tradeProfit = profit;cumuProfit += profit
        trades.cumuProfit = cumuProfit
    if mp > 0:
        trades = tradeInfo('liqLong',myExitDate,tempName,myExitPrice,myCurShares,0)
        profit = trades.calcTradeProfit('liqLong',mp,entryPrice,myExitPrice,entryQuant,myCurShares) * myBPV
        profit = profit - myCurShares * commission;trades.tradeProfit = profit;cumuProfit += profit
        trades.cumuProfit = cumuProfit
    curShares = 0
    for remShares in range(0,len(entryQuant)):curShares += entryQuant[remShares]
    return (profit,trades,curShares)

def bookTrade(entryOrExit,lOrS,price,date,tradeName,shares):
    global mp,commission,totProfit,curShares,barsSinceEntry,listOfTrades
    global entryPrice,entryQuant,exitPrice,numShares,myBPV,cumuProfit
    if entryOrExit == -1:
        profit,trades,curShares = exitPos(price,date,tradeName,shares);mp = 0
    else:
        profit = 0;curShares = curShares + shares;barsSinceEntry = 1;entryPrice.append(price);entryQuant.append(shares)
        if lOrS == 1:mp += 1;trades = tradeInfo('buy',date,tradeName,entryPrice[-1],shares,1)
        if lOrS ==-1:mp -= 1;trades = tradeInfo('sell',date,tradeName,entryPrice[-1],shares,1)
    return(profit,curShares,trades)

dataClassList = list()

marketMonitorList,masterDateList,masterDateGlob,entryPrice = ([] for i in range(4))
buy = entry = 1; sell = exit = -1; ignore = 0;
entryQuant,exitQuant,trueRanges,myBPVList = ([] for i in range(4))
myComNameList,myMinMoveList,systemMarketList = ([] for i in range(3))
cond1,cond2,cond3,cond4 = ([] for i in range(4))
marketVal1,marketVal2,marketVal3,marketVal4 = ([] for i in range(4))
portManager = portManagerClass();marketList = getData();portfolio = portfolioClass()
numMarkets = len(marketList);positionMatrix = positionMatrixClass();positionMatrix.numMarkets = numMarkets
firstMarketLoop = True

#----------------------------------------------------------------------------------
# Set up algo parameters here
#----------------------------------------------------------------------------------
startTestDate = 20100101 #must be in yyyymmdd
stopTestDate = 20190228 #must be in yyyymmdd
rampUp = 100 # need this minimum of bars to calculate indicators
sysName = 'Donch-MAX2NSect' #System Name here
initCapital = 500000
commission = 100
Ignore Most Of This Code

Everything is batched processed: set up, pick market or portfolio, run.  Then examine all of the reports.  Here is an example of the sector analysis report.

          Total Profit  Max DrawDown
Currency    -------------------------------
BP           -14800      19062 
SF            -8600      53575 
AD             4670      11480 
DX            10180      10279 
EC            -9000      16775 
JY            10025      18913 
CD           -19720      21830 
-------------------------------------------
Totals:      -27245      69223
-------------------------------------------
Energies    -------------------------------
CL           -40400      55830 
HO            80197      23382 
NG           -14870      28920 
RB           -45429      61419 
-------------------------------------------
Totals:      -20502      75957
-------------------------------------------
Metals      -------------------------------
GC            27210      36610 
SI            -1848       2389 
HG            -2402       2438 
PL           -16750      25030 
PA            27230      38615 
-------------------------------------------
Totals:       33440      61472
-------------------------------------------
Grains      -------------------------------
S_            27312       9088 
W_           -25538      32600 
C_            -1838      12212 
BO            -8460       9544 
SM              390      11250 
RR            -1390      12060 
-------------------------------------------
Totals:       -9523      34135
-------------------------------------------
Financials  -------------------------------
US            29488      18375 
TY              969      12678 
TU            -2020       3397 
FV            -2616       4531 
ED            -4519       4869 
-------------------------------------------
Totals:       21302      30178
-------------------------------------------
Softs       -------------------------------
SB            -1716      19717 
KC            15475      44413 
CC              540       8090 
CT            -8705      35660 
LB            22269      16586 
OJ             4720       8262 
-------------------------------------------
Totals:       32583      57976
-------------------------------------------
Meats       -------------------------------
LC           -18910      24020 
LH           -31270      35640 
FC            14600      25737 
-------------------------------------------
Totals:      -35580      59550
-------------------------------------------
Sector Analysis

Plus I include EasyLanguage for the majority of the scripts.  Of course without the portfolio level management.  I am working on a new website that will support the new book at TrendFollowingSystems.com.

Please take a look at my latest book – it would make an awesome Christmas present.

 

Please follow and like us:

MULTI-TIME FRAME – KEEPING TRACK OF DISCRETE TIME FRAMES

Just a quick post here.  I was asked how to keep track of the opening price for each time frame from our original Multi-Time Frame indicator and I was answering the question when I thought about modifying the indicator.  This version keeps track of each discrete time frame.  The original simply looked back a multiple of the base chart to gather the highest highs and lowest lows and then would do a simple calculation to determine the trend.  So let’s say its 1430 on a five-minute bar and you are looking back at time frame 2.  All I did was get the highest high and lowest low two bars back and stored that information as the high and low of time frame 2.  Time frame 3 simply looked back three bars to gather that information.  However if you tried to compare these values to a 10-minute or 15-minute chart they would not match.

In this version, I use the modulus function to determine the demarcation of each time frame.  If I hit the border of the time frame I reset the open, high, low and carry that value over until I hit the next demarcation.  All the while collecting the highest highs and lowest lows.  In this model, I am working my way from left to right instead of right to left.  And in doing so each time frame is discrete.

Let me know which version you like best.

 

Inputs:tf1Mult(2),tf2Mult(3),tf3Mult(4),tf4Mult(5);



vars: mtf1h(0),mtf1l(0),mtf1o(0),mtf1c(0),mtf1pvt(0),diff1(0),
      mtf2h(0),mtf2l(0),mtf2o(0),mtf2c(0),mtf2pvt(0),diff2(0),
      mtf3h(0),mtf3l(0),mtf3o(0),mtf3c(0),mtf3pvt(0),diff3(0),
      mtf4h(0),mtf4l(0),mtf4o(0),mtf4c(0),mtf4pvt(0),diff4(0),
      mtf0pvt(0),diff0(0);

If barNumber = 1 then
Begin
	mtf1o = o;
	mtf2o = o;
	mtf3o = o;
	mtf4o = o;
end;


If barNumber > 1 then
Begin

	Condition1 =  mod((barNumber+1),tf1Mult) = 0;
	Condition2 =  mod((barNumber+1),tf2Mult) = 0;
	Condition3 =  mod((barNumber+1),tf3Mult) = 0;
	Condition4 =  mod((barNumber+1),tf4Mult) = 0;
	
	mtf1h = iff(not(condition1[1]),maxList(high,mtf1h[1]),high);
	mtf1l = iff(not(condition1[1]),minList(low,mtf1l[1]),low);
	mtf1o = iff(condition1[1],open,mtf1o[1]);
	mtf1c = close;

	
	mtf0pvt = (close + high + low) / 3;
	diff0 = close - mtf0pvt;
	
	mtf2h = iff(not(condition2[1]),maxList(high,mtf2h[1]),high);
	mtf2l = iff(not(condition2[1]),minList(low,mtf2l[1]),low);
	mtf2o = iff(condition2[1],open,mtf2o[1]);
	mtf2c = close;
	
	
	mtf1pvt = (mtf1h+mtf1l+mtf1c) / 3;
	diff1 = mtf1c - mtf1pvt;
		
	mtf2pvt = (mtf2h+mtf2l+mtf2c) / 3;
	diff2 = mtf2c - mtf2pvt;
		
	mtf3h = iff(not(condition3[1]),maxList(high,mtf3h[1]),high);
	mtf3l = iff(not(condition3[1]),minList(low,mtf3l[1]),low);
	mtf3o = iff(condition3[1],open,mtf3o[1]);
	mtf3c = close;
	
	mtf3pvt = (mtf3h+mtf3l+mtf3c) / 3;
	diff3 = mtf3c - mtf3pvt;
	
	mtf4h = iff(not(condition4[1]),maxList(high,mtf4h[1]),high);
	mtf4l = iff(not(condition4[1]),minList(low,mtf4l[1]),low);
	mtf4o = iff(condition4[1],open,mtf4o[1]);
	mtf4c = close;

	mtf4pvt = (mtf4h+mtf4l+mtf4c) / 3;
	diff4 = mtf4c - mtf4pvt;
	
	
	Condition10 = diff0 > 0;
	Condition11 = diff1 > 0;
	Condition12 = diff2 > 0;
	Condition13 = diff3 > 0;
	Condition14 = diff4 > 0;
	 
	If condition10 then setPlotColor(1,Green) else SetPlotColor(1,Red);
	If condition11 then setPlotColor(2,Green) else SetPlotColor(2,Red);
	If condition12 then setPlotColor(3,Green) else SetPlotColor(3,Red);
	If condition13 then setPlotColor(4,Green) else SetPlotColor(4,Red);
	If condition14 then setPlotColor(5,Green) else SetPlotColor(5,Red);
	
	condition6 = condition10 and condition11 and condition12 and condition13 and condition14;
	Condition7 = not(condition10) and not(condition11) and not(condition12) and not(condition13) and not(condition14);

	If condition6 then setPlotColor(7,Green);
	If condition7 then setPlotColor(7,Red);
	
	If condition6 or condition7 then plot7(7,"trend");

	Plot6(5,"line");	
	Plot1(4,"t1");
	Plot2(3,"t2");
	Plot3(2,"t3");
	Plot4(1,"t4");
	Plot5(0,"t5"); 

end;
Multi-Time Frame with Discrete Time Frames
Please follow and like us:

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:

Multi-Time Frame – Using Built-in Indicators and Multi Data Charts

A reader of this blog wanted to be able to use different time frames and some built-in indicators and output the information in a similar fashion as I did in the original MTF post.  There are numerous ways to program this but the two easiest are to use data structures such as arrays or vectors or use TradeStation’s own multi data inputs.  The more complicated of the two would be to use arrays and stay compliant with Multicharts.  Or in that same vein use vectors and not stay compliant with Multicharts.  I chose, for this post, the down and dirty yet compliant method.  [NOTE HERE! When I started this post I didn’t realize it was going to take the turn I ended up with.  Read thoroughly before playing around with the code to see that it is what you are really, really looking for.]  I created a multi data chart with five-time frames: 5,10,15,30 and 60 minutes.  I then hid data2 thru data5.  I created an MTF indicator that plots the relationship of the five time frames applied to the ADX indicator with length 14.  If the ADX > 20 then the plot will be green else it will be red.  If all plots align, then the composite plot will reflect the alignment color.

Using the MTF indicator with ADX
{EasyLanguage MultiTime Frame Indicator)
 written by George Pruitt - copyright 2019 by George Pruitt
 }


Inputs:adxLen(14),adxTrendVall(20);

vars: adxData1(0),adxData2(0),adxData3(0),adxData4(0),adxData5(0);


If barNumber > 1 then
Begin
	
	adxData1 = adx(adxLen) of data1;
	adxData2 = adx(adxLen) of data2;
	adxData3 = adx(adxLen) of data3;
	adxData4 = adx(adxLen) of data4;
	adxData5 = adx(adxLen) of data5;
	
	Condition10 = adxData1 > adxTrendVall;
	Condition11 = adxData2 > adxTrendVall;
	Condition12 = adxData3 > adxTrendVall;
	Condition13 = adxData4 > adxTrendVall;
	Condition14 = adxData5 > adxTrendVall;
	 
	If condition10 then setPlotColor(1,Green) else SetPlotColor(1,Red);
	If condition11 then setPlotColor(2,Green) else SetPlotColor(2,Red);
	If condition12 then setPlotColor(3,Green) else SetPlotColor(3,Red);
	If condition13 then setPlotColor(4,Green) else SetPlotColor(4,Red);
	If condition14 then setPlotColor(5,Green) else SetPlotColor(5,Red);
	
	condition6 = condition10 and condition11 and condition12 and condition13 and condition14;
	Condition7 = not(condition10) and not(condition11) and not(condition12) and not(condition13) and not(condition14);

	If condition6 then setPlotColor(7,Green);
	If condition7 then setPlotColor(7,Red);
	
	If condition6 or condition7 then plot7(7,"trend");

	Plot6(5,"line");	
	Plot1(4,"t1");
	Plot2(3,"t2");
	Plot3(2,"t3");
	Plot4(1,"t4");
	Plot5(0,"t5"); 

end;
MTF with 5 data streams and ADX

This code is very similar to the original MTF indicator, but here I simply pass a pointer to the different time frames to the ADX function.  Since the ADX function only requires a length input I had assumed I could use the following format to get the result for each individual time frame:

adxData1 = adx(14) of data1;

adxData2 = adx(14) of data2;

This assumption worked out.

But are we really getting what we really, really want?  I might be putting too much thought into this but of the five-time frame indicator dots, only the 5-minute will change on a 5-minute basis.  The 10-min dot will stay the same for two 5-min bars.  The dots will reflect the closing of the PRIOR time frame and the current 5-min bar is ignored in the calculation.  This may be what you want, I will leave that up to you.  Here is an illustration of the delay in the different time frames.

So when you look at each dot color remember to say to yourself – this is the result of the prior respective time frame’s closing price.  You can say to yourself, “Okay this is the ADX of the current 5-minute bar and this is the ADX of the prior 10-minute close and this is the ADX of the prior 15 minutes close and so on and so on.   We all know that the last 5 minutes will change all of the time frames closing tick, but it may or may not change the price extremes of those larger time frames.   I will show you how to do this in the next post.   If you want to see the impact of the last 5- minutes, then you must build your bars internally and dynamically.

 

Please follow and like us:

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:

A Bar Scoring System – inspired by Keith Fitschen

In Keith’s wonderful book, “Building Reliable Trading Sytems”, he reveals several algorithms that classify an instruments’ movement potential.  In the part of the book that is titled Scoring by a Bar Type Criterion, he describes eight different two-day patterns that involve 3 different criteriaEight different Bar-Types

He looks at the relationship between today’s open and today’s close, today’s close and yesterday’s close, and today’s close in terms of the day’s range.  Bar-Types 1 to 4 all have the close of today >= close of yesterday.  Bar-Types 5 to 8 have close of today < close of yesterday.

I wanted to program this into my TradeStation and do some research to see if the concept is valid.  In his book, Keith tested a lot of different stocks and commodities.  In this post, I just test the ES, US, and Beans.  This form of research can be used to enhance an existing entry technique.

Here is how I defined the eight different bar types:

array : barTypeArray[8](false); 

midRange = (h + l)/2;

barTypeArray[0] = c >= c[1] and c > o and c >= midRange;
barTypeArray[1] = c >= c[1] and c > o and c <  midRange;
barTypeArray[2] = c >= c[1] and c < o and c >= midRange;
barTypeArray[3] = c >= c[1] and c < o and c <  midRange;
barTypeArray[4] = c <  c[1] and c > o and c >= midRange;
barTypeArray[5] = c <  c[1] and c > o and c <  midRange;
barTypeArray[6] = c <  c[1] and c < o and c >= midRange;
barTypeArray[7] = c <  c[1] and c < o and c <= midRange;
Defining Eight Different Bar Types

I used a brute force approach by creating an 8-element array of boolean values.  Remember EasyLanguage uses a 0 index.  If the two -day pattern matches one of the eight criteria I assign the element a true value.  If it doesn’t match then a false value is assigned.  I use an input value to tell the computer which pattern I am looking for.  If I choose Bar-Type[0] and there is a true value in that array element then I take a trade.   By providing this input I can optimize over all the different Bar-Types.

Input : 
	BarTypeNumber(0), // which bar type
	buyOrSell(1),   //1 to buy 2 to sell
	numDaysToHold(2); //how many days to hold position




For cnt = 0 to 7 //remember to start at 0
Begin
	If barTypeArray[cnt] = true then whichBarType = cnt;
end;

If whichBarType = BarTypeNumber then 
begin
 	if buyOrSell = 1 then buy this bar on close;
	if buyOrSell = 2 then sellshort this bar on close;
end;
Loop Thru Array to find Bar Type

Here are some results of looping through all eight Bar-Types, Buy and Sell, and holding from 1 to 5 days.

ES – ten – year results – remember these are hypothetical results with no commission or slippage.

Here’s what the equity curve looks like.   Wild swings lately!!

Beans:

Bonds

Keith was right – look at the Bar Category that bubbled to the top every time – the most counter-trend pattern.  My Bar-Type Number 7  is the same as Keith’s 8.  Here is the code in its entirety.

{Bar Scoring by Keith Fitschen
 from his book "Building Reliable Trading Systems" 2013 Wiley}
 
Input : BarTypeNumber(0),
	 buyOrSell(1),
	 numDaysToHold(2);
	 
vars: midRange(0);
array : barTypeArray[8](false); 

midRange = (h + l)/2;

barTypeArray[0] = c >= c[1] and c > o and c >= midRange;
barTypeArray[1] = c >= c[1] and c > o and c <  midRange;
barTypeArray[2] = c >= c[1] and c < o and c >= midRange;
barTypeArray[3] = c >= c[1] and c < o and c <  midRange;
barTypeArray[4] = c <  c[1] and c > o and c >= midRange;
barTypeArray[5] = c <  c[1] and c > o and c <  midRange;
barTypeArray[6] = c <  c[1] and c < o and c >= midRange;
barTypeArray[7] = c <  c[1] and c < o and c <= midRange;

vars: whichBarType(0),cnt(0);

For cnt = 0 to 7
Begin
	If barTypeArray[cnt] = true then whichBarType = cnt;
end;

If whichBarType = BarTypeNumber then 
begin
 	if buyOrSell = 1 then buy this bar on close;
	if buyOrSell = 2 then sellshort this bar on close;
end;

If barsSinceEntry = numDaysToHold then
begin
	If marketPosition = 1 then sell this bar on close;
	If marketPosition =-1 then buytocover this bar on close;
end;
Bar Scoring Example

Keith’s book is very well researched and written.  Pick one up if you can find one under $500.  I am not kidding.  Check out Amazon.

 

 

Please follow and like us:

What’s Our Vector Victor – Tiptoeing in the EL Collections

Tired of Manipulating Arrays – Try a Vector and a Queue

Vectors:

An array like structure but are dynamic and have a plethora of tools at your disposal.  Arrays are cool and can be multi-dimensional and can be easily manipulated.  But they require a lot of forethought as to how much size to reserve for their implementation.  Now don’t think this is going to be an advanced EasyLanguage tutorial, because it’s really not.  Most of us TRS-80, Ti-99/4A, Vic-20 and Commodore 64 trained programmers of the early ’80s have not welcomed objects with open arms and that is really a mistake.  In this sense we are like cavemen – we have all of the rudimentary tools at our disposal and can create some really cool stuff and we can really understand what we are doing.  With time and effort, we can get to the same place as object-oriented programmers.  We just don’t like the concept of using other’s tools as much as we like using ours.  So if you aren’t classically trained in programming you may have an advantage when tieing into the objects of a programming language.  This little tutorial is a very brief glimpse into a whole different world of programming.  The beauty is you can combine “old school” programming with objects – even if you don’t understand how the objects are truly constructed.    I want to introduce the concept of the Vector and the Queue-  truly cool Swiss Army knives.  First the vector.  Let’s just jump into some of the code – it really is simple.

Object Instantiation – a long word for declaring variable:
Using elsystem.collections;

Vars: Vector opVector(NULL),
      Vector hiVector(Null),
      Vector loVector(Null),
      Vector clVector(Null),
      Vector barVector(Null),
      Queue timeStampQue(Null);
Once
Begin
	barVector = new Vector;
	opVector = new Vector;
	hiVector = new Vector;
	loVector = new Vector;
	clVector = new Vector;
	timeStampQue = new Queue;
end;
Instantiating and Declaring Vectors and Queue

You have to tell EasyLanguage you want to use some of the tools in the elsystem.collections.  You do this by simply tell it you are Using elsystem.collections.  The word collections is a catch-all for a bunch of different types of data structures.  Remember data structures are just programming constructs used to hold data – like an array.  All the variables that you declare in EasyLanguage are arrays – you just aren’t really aware of it.   When you index into them to get prior values then you become slightly aware of it.  In this portion of code, I create five vectors and one queue and assign them the Null or an empty value.  I just finished a programming gig where I had to build dynamically sized bars from the base data.  Kind of like creating 15, 30, 60-minute bars from a 5-minute bar chart or stream.   I did this using arrays because I wanted to be able to index into them to go back in time and I didn’t how far I wanted to go back.  So I declared some arrays with large dimensions to be safe.  This really takes a bite out of your resources which costs space and time.  I had played with Vector like objects in Python, so I thought I would post about them here and show how cool they are.  Remember this is a rudimentary program and could be streamlined and cleaned up.  Each vector will store their respective time, open, high, low and close values of the combined bar.  In a later post, I would like to do this with a Dictionary.  So the opVector will hold the open price, the hiVector will hold the high price and so on.

Build a Queue – why the extra ue?

I want to build 15-minute bars from 5-minute bars so I need to know when to sample the data to properly collect the corresponding data.  If I start at 9:30 then I want to sample the data at 9:45 and look back three bars to get the open and the highest high and the lowest low.  The close will simply be the close of the 9:45 bar.  I want to do this at 9:45, 10:00. 10:15 and so on.  I could manipulate the time and use the modulus function to see if the minutes are multiples of 15 and I tried this but it didn’t work too well.  So I thought since I was already in the collections why not build a list or a queue with all the timestamps I would need.  This is how I did it.

vars: hrs(0),mins(0),barMult(3),combBarTimeInterval(0),totBarsInHour(0),startTime(930),endTime(1615),cnt(0);

Once
Begin
	mins = fracPortion(t/100);
	combBarTimeInterval = barInterval*barMult;
	While value1 < endTime
	Begin
		cnt = cnt + 1;
		Value1 = calcTime(startTime,cnt*combBarTimeInterval);
//		print("Inside queue : ",Value1," ",cnt*combBarTimeInterval);
		timeStampQue.Enqueue(Value1);
	end;	
end;
Populating A Queue With Time Stamps

I simply use the CalcTime function to add 15-minute intervals to the start time and then I add them to the queue:  timeStampQue.Enqueue(Value1);  You access the methods or tools to a class by using the dot (” . “) notation.  Once I instantiated or created the timeStampQue I gained access to all the tools that belong to that object.  The Enqueue method simply appends the list the value that you pass it.  I would have preferred the method to be labeled simply add.  How did I figure out the right method name you ask?  I accessed the Dictionary from the View menu in the TDE.  Here is a picture to help:

Dictionary:

I use the keyword Once to just execute the code one time.  You could have said if BarNumber = 1, but why not use the tools at your disposal,   I figured out the combBarTimeInterval by using the 5-minute bar multiplier (3).  I then looped from startTime to endTime in 15-minute intervals and stored the timeStamps in the queue.  So every time stamp I need is in the timeStampQue.  All I need now is to compare the time of the 5-minute bar to the time stamps inside the queue.  This is where using object really come in handy.

Queue Methods:

Old school would have looped through all of the elements in the list and compared them to the value I was seeking and if found it would return true.  In the object world, I can simply ask the object itself to see if the value is in it:

condition1 = timeStampQue.Contains(t);

Cool!  If condition1 is true then I know I am sitting on the 5-minute bar that shares the same timestamp as a 15-minute bar.  If the time stamps are the same then I can start building the large timeframe from the lower timeframe.  You add elements to a vector by using the insert method.  I simply looked it up in the dictionary.   I had to specify where to insert the value in the vector.  I simply inserted each value into the [0] location.  Remember we are inserting so everything else in the vector is moved down.

Vector Methods:

 

If condition1 then
Begin
	barVector.insert(0,t);
	opVector.insert(0,open[2]);
	hiVector.insert(0,highest(h[0],3)); 
	loVector.insert(0,lowest(l[0],3));
	clVector.insert(0,close[0]);
end;
Inserting Values at Vector Location 0

I only need to keep track of the last 10 15-minute bars, so once the vector count exceeded 10, I simply popped off the value at the back end – pop_back().  I figured this out by looking at Martin Whittaker’s awesome website – www.markplex.com. 

 

If opVector.Count > 10 then 
begin
    barVector.pop_back();
	opVector.pop_back();
	hiVector.pop_back();
	loVector.pop_back(); 
	clVector.pop_back();
end;
Popping the Back-End

To check my work I printed the 15-minute bars on each 5-minute bar to make sure the bars were being built properly.  These data structures expect an object to be inserted, added, popped so when you print out one of their values you have to tell the print statement what the object should be translated as.  Here the keyword asType comes into play.  Take a look at my code, and you will see what I mean.  I hope this gets you excited about objects because the collections class can save you a ton of time and is really cool.  Use it and you can brag that you are an OOP programmer at your next cocktail party.

Code Listing:
Using elsystem.collections;

Vars: Vector opVector(NULL),
      Vector hiVector(Null),
      Vector loVector(Null),
      Vector clVector(Null),
      Vector barVector(Null),
      Queue timeStampQue(Null);
Once
Begin
	barVector = new Vector;
	opVector = new Vector;
	hiVector = new Vector;
	loVector = new Vector;
	clVector = new Vector;
	timeStampQue = new Queue;
end;

vars: hrs(0),mins(0),barMult(3),combBarTimeInterval(0),totBarsInHour(0),startTime(930),endTime(1615),cnt(0);

Once
Begin
	mins = fracPortion(t/100);
	combBarTimeInterval = barInterval*barMult;
	While value1 < endTime
	Begin
		cnt = cnt + 1;
		Value1 = calcTime(startTime,cnt*combBarTimeInterval);
//		print("Inside queue : ",Value1," ",cnt*combBarTimeInterval);
		timeStampQue.Enqueue(Value1);
	end;	
end;	
	
	
	
condition1 = timeStampQue.Contains(t);

Print(d," ",t," ",condition1);

If condition1 then
Begin
	barVector.insert(0,t);
	opVector.insert(0,open[2]);
	hiVector.insert(0,highest(h[0],3)); 
	loVector.insert(0,lowest(l[0],3));
	clVector.insert(0,close[0]);
end;
 
If opVector.Count > 10 then 
begin
    barVector.pop_back();
	opVector.pop_back();
	hiVector.pop_back();
	loVector.pop_back(); 
	clVector.pop_back();
end;

vars:vectCnt(0);
print(d," ",t);
If opVector.Count > 9 then
Begin
	For vectCnt = 0 to 9 
	begin
		print(vectCnt," ",barVector.at(vectCnt) astype int," ",opVector.at(vectCnt) astype double," ",hiVector.at(vectCnt) astype double," ",loVector.at(vectCnt) astype double," ",clVector.at(vectCnt) astype double);
	end;
end;
Program in its Entirety

 

Please follow and like us: