Category Archives: TradingSimula18

Is your Big Point Value right?

Thinking of using futures data from a vendor such as CSI, Pinnacle, Norgate…?

You would be surprised how many quants still use Excel or they own homegrown back-testing software.  Determining trade execution is simple.  On a stop order, all you need to do is see if today’s bar is higher than the prior bars calculated trade signal – right.  And if it is then a buy stop order is executed and the calculated signal price.  Store the entry price and when the exit occurs do the math to calculate the profit or loss.  Before you can do this, you must know two very important contract specs on the futures data you are testing:

  1. big point value – I will explain how to calculate this a little later
  2. minimum tick move

That’s it.  You don’t need to know anything beyond this to properly calculate trade signals and trade P and L.  As long as you store this information for each futures/commodity market you are testing, then you will be good to go.  Many testing platforms store more information such as contract size, trading hours, expiration date and several other contract specs.  You don’t need to know this information if you all you are interested is proper signal placement and accurate trade accounting.  Once you set the big point value and minimum tick move you are good to go, right?  Yes, if you stick with the same data vendor.  I kid you not.  Data vendors often quote the same exact market with different decimal places.  If you look up a sugar quote at the CME website, you will see it quoted like 0.1908.  If you look it up on TradeStation it is quoted as 19.08.  It’s the same size contract 112,000 pounds, but if you use the big point value at the CME, it will not produce accurate calculations on the TradeStation quote.  You can’t use the contract size of 112,000 pounds to help with the math either.  You have to delve into how each data vendor quotes their data and this will impact the big point move and minimum tick move.  Most vendors are gracious enough to provide a futures dictionary with these specs.  But it is wise to know how to do this by hand as well.

If you had to take a test, could you calculate the profit or loss from a trade in soybeans, in sugar or in euro currency?

Are you kidding me this is super simple.  Just program the strategy in TradeStation or Amibroker or Multicharts or TradingSimula-18.  I took the NFA Series 3 test more than two decades ago, and this type of question was on the test.  The following information was given:

  • contract size – 5000 bushels
  • minimum tick or move – 1/4th of a cent or penny
  • long entry price 8.32’2 and sell exit price 8.48’6 (per bushel)

The entry and exit price may look a little funny.  The entry price is eight dollars and 32 1/4 cents.   Many still quote soybeans in 1/8ths.  So, 2 X 1/8th = 1/4th.  For fun let’s calculate the notional value of the entry and exit prices.  The notional value of a soybean contract at the entry price is $8.3225 * 5000 = $41,612.50.  As you know futures are highly leveraged and you can control this quantity of soybeans for a small percentage of the notional value (margin.)  Now the exit price is $8.4875 (6*1/8th = 3/4th of a cent) and the notional value of the contract at exit is $8.4875 * 5000 = $42,437.5.  The result of the trade was $42,437.50 – $41,612.50 = $825.  Or you could simply multiple the difference between entry and exit by 5000.  This would $0.165 * 5000.  This makes perfect sense, but as a broker it was difficult to keep the contract size of a market and its minimum tick move in your memory.  Well, if you did it long enough it wasn’t that difficult.  You can reduce the math down to one easy to remember value and quickly do the math in your head.  The concept of the big point move allows for this.  If you download your data from Pinnacle or CSI the price of soybeans is usually quoted like this:  848.75.  This would be eight hundred and 48.75 cents.  The big point move is the amount of dollars required to lift (or drop) the first digit to the left of the decimal by one.  The first digit to the left of the decimal in beans is a penny or one cent.  A one cent move in beans is 5000 * $0.01 or $50.  Going back to our trade example 848.75 – 832.25 = 16.5 – multiply this by $50 you get $825.  You can also derive the minimum move dollar value too.  If the minimum move is 1/4th of a cent, then you can multiply 5000 * $.0025 and this yields $12.5.

Why is this important to know?

You should know this if you are trading the market, period.  Also, if you are a quant and are using some back testing software that requires you to set up the database outside the purview of the back-tester, then these values must be known, and must also be accurate.  Since TradeStation integrates its own data, you don’t have to worry about this.  But if you want to use a database from Pinnacle, BarChart, CSI or Norgate, then you have to take the time to set this up, right off the bat.  I feel like the leading data vendors, provide accurate data.  But there are differences from one vendor to another.  Not all data vendors use the same number of decimal places in their market quotes, so you must be able to determine the big point value and minimum move from the data.   You can do all the math to determine profit and loss from the big point value.  Here is a snapshot of a few markets in the TS-18 dataMasterPinnacle and the TS-18 dataMasterCSI text files. Like I said, all vendors will provide this information for you, so you can set your database properly.  Some back-testing platforms require more contract specs, but TS-18 just needs this information to carry out accurate calculations.  Here TS-18 wants to know the symbol, big point value, minimum tick move, and market name.

Pinnacle Data
AN,1000,0.01,Aus.Dol
ZL,600,0.01,BeanOil
BN,625,0.01,B.Pound
ZU,1000,0.01,Crude
ZC,50,0.25,Corn
CC,10,1,Cocoa
CL,1000,0.01,Crude
CN,1000,0.01,Can.Dol
CT,500,0.01,Cotton
FN,1250,0.005,EuroCur


CSI Data
AD,100000,0.0001,Aus.Dol
BO,600,0.01,BeanOil
BP,62500,0.0001,B.Pound
CL,1000,0.01,Crude
C2,50,0.25,Corn
C_,50,0.25,Corn
CC,10,1,Cocoa
CD,100000,0.0001,Can.Dol
CL,1000,0.01,Crude
CT,500,0.01,Cotton
CU,125000,0.00005,EuroCur
EC,125000,0.00005,EuroCur

Other than the symbol names the values for each symbol are very similar.  Most data vendors, including CSI quote the Euro currency like this:

1.08735 and a move to 1.08740 = 0.00005 *$125,000 = $6.25

But Pinnacle data quotes it like this:

108.735 and a move to 108.740 = 0.005 * $1,250 = $6.25

The size of the contract isn’t different here.  It is the quote that is different.  The big point value is tied to the contract size and the format of the quote.  This is why knowing the big point value is so important.  If you don’t set up the contract specifications correctly, then you will receive inaccurate results.

One more test in sugar – from the CME website

  • contract size – 112,000 pounds
  • minimum tick or move – $0.0001
  • long entry price $0.1910 and sell exit price $0.1980 (per pound)
  • big point value = $112,000
  • $0.0070 * 112,000 = $784

From TradeStation

  • contract size – 112,000 pounds
  • minimum tick or move – $0.01
  • long entry price $19.10 and sell exit price $19.80 (per pound??)
  • big point value = $1,120
  • $0.70 * 1,120 = $784

Most fractional quotes are delivered in decimal format and the also very important minimum tick move.

The interest rate futures have minimum tick moves ranging from 1/256ths to 1/32nds.  Most vendors will give you a quote like 120.03125 for the bonds.  If you test in TradersStudio or Excel or any other platform where the data is not integrated and are using CSI (or any other vendor), then you must force your calculated prices to fall on an even tick.  Assume you do a calculation to buy a bond future at 120.022365895.  If you don’t take the minimum tick move into considerations, you might be filled at this theoretical price.  In reality you should be filled on an even tick at 120.03125.  This is worse but realistic fill. You could create what you think is a really awesome strategy where you are shaving off the price on every trade – getting a better fill on every trade.

Thinking of purchasing and using back-testing software or Excel and data from a data vendor?

Become your own quant and do this.  You will learn a lot about algorithm testing and development.  But first things first.  Get your database set up according to your data vendor.  Once this chore is complete, testing becomes smooth sailing.  I provide the databases for Pinnacle and CSI with TS-18. Other software provides these as well, and a way to create your own databases.

Trading Before and After Holidays

Morgan Stanley Research Idea from 2018

In May of 2018 Morgan Stanley presented a report from their research department.  The report was written by Tony Small and Matthew Hornbach.  The information presented validated the idea of seasonality in the markets.  The writers/researchers gathered all the data going back to 1984 on the ten-year futures and all the federal holidays going back over the same span of time.  They then analyzed either going long or short M days prior to the holiday and exiting N days before the holidays.  They also did the same thing after the holiday – entering M days after and exiting N days after.  The boundary was set to five days before and after.  This report was sent to me by one of my clients.  The conclusion of the article was that there seemed to be a seasonality component to the days prior and after a major holiday.  And that it seemed this component did indeed provide a technical advantage (on the surface.)  Here is their conclusion.

“Given the risk of data mining, investors should accept this analysis with some degree of caution,” Small and Hornbach said. “There is no guarantee that historical seasonality and past price performance will continue in the future.”

How would one test this idea on their own?

Tools needed:

  1. Python with NumPy installed.
  2. An AI chatbot such as ChatGPT.
  3. Access to TY futures data going back to 1984 (actual non-Panama adjusted data would be preferable.)
  4. A testing platform that allows future leak such as George’s own TradingSimula-18.  Excel would work too but would require a bunch of work.

Some programming knowledge.

  1. Create output for all of the federal holidays going back to January 1984.  This list must also include Good Fridays.  This list should have two fields – the date and the name of the holiday.
  2. TS-18 came in handy because I read the file that was created from step 1 and whenever I came across or spanned a holiday date, I created a loop that calculated the ROC for the prior five days and also for the five days that followed.   I created a nomenclature to quickly recognize the ROC span.
    • B54 – ROC between 5 days before and 4 days before – 1 day
    • B53 – ROC between 5 days before and 3 days before – 2 days
    • B52 – ROC between 5 days before and 2 days before – 3 days
    • B51 – ROC between 5 days before and 1 days before – 4 days
    • B43 – ROC between 4 days before and 3 days before – 1 day
    • B42 – ROC between 4 days before and 2 days before – 2 days
    • B41 – ROC between 4 days before and 1 days before – 3 days
    • B32 – ROC between 3 days before and 2 days before – 1 day
    • B31 – ROC between 3 days before and 1 days before – 2 days
    • B21 – ROC between 2 days before and 1 days before – 1 day
    • A12 – ROC between 1 days after and 2 days after – 1 day
    • A13 – ROC between 1 days after and 3 days after – 2 days
    • A14 – ROC between 1 days after and 4 days after – 3 days
    • A15 – ROC between 1 days after and 5 days after – 4 days
    • A23 – ROC between 2 days after and 3 days after – 1 day
    • A24 – ROC between 2 days after and 4 days after – 2 days
    • A25 – ROC between 2 days after and 5 days after – 3 days
    • A34 – ROC between 3 days after and 4 days after – 1 day
    • A35 – ROC between 3 days after and 5 days after – 2 days
    • A45 – ROC between 4 days after and 5 days after – 1 day
  3. This process created a file that looked like this:
    • Output of ROC with nested for loops
  4. With this data I summed up each ROC calculation (B54 thru A45).  From this you could pick an entry and exit day combination.  The best results were wrapped around Memorial Day.  Here was where I ran into a small but highly aggravating scenario.  Most of the holidays had similar numbers, but there were a few that had some really strange values.  I knew I couldn’t use regular adjusted futures data, because many times these values can go below zero and when doing division with these price adjustments, the values are simply not accurate.  So, I used continuous non adjusted data, but ran into a problem with the rollover gap.  Some of the holidays occurred really close to rollover dates.  The calculations that bridged a rollover was not accurate.  I then remembered Pinnacle data also includes Ratio Adjusted Data or RAD.  I applied the looping algorithm to this data, and it SEEMED to fix the problem.
  5. I was able to pick a B(n,m) or A(m,n) for each market.  If the magnitude of ROC was sufficiently negative, you could use the dates to short the market.

ChatGPT and Python and Numpy to the rescue

I asked ChatGPT to provide python code to generate a list of all federal holidays from 1984.

from pandas.tseries.holiday import USFederalHolidayCalendar
from datetime import datetime,timedelta
import numpy as np
# Create a calendar for U.S. federal holidays
cal = USFederalHolidayCalendar()

# Get the holidays for the year 2014
holidays_2014 = cal.holidays(start='1984-01-01', end='2024-12-31').to_pydatetime()

federalHolidays = []
date_list = holidays_2014.astype(datetime).tolist()
for n in range(len(date_list)):
dateString = f'{date_list[n]:%Y%m%d}'
federalHolidays.append(int(dateString))
Create a list of federal holidays

This worked great, but I had to convert the NumPy datetime construct to a string.  I like to keep things simple.  You will see this in the code.  I started to get to work on the second part of the task when I discovered there were no dates for GOOD FRIDAYS – argh!  ChatGPT to rescue once again.

def good_friday_dates(start_year, end_year):
good_friday_dates = []
for year in range(start_year, end_year + 1):
# Calculate the date of Easter Sunday for the given year
easter_sunday = calculate_easter_date(year)

# Good Friday is two days before Easter Sunday
good_friday = easter_sunday - timedelta(days=2)

# Add the Good Friday date to the list
good_friday_dates.append(good_friday)

return good_friday_dates

def calculate_easter_date(year):
# Algorithm to calculate the date of Easter Sunday
a = year % 19
b = year // 100
c = year % 100
d = b // 4
e = b % 4
f = (b + 8) // 25
g = (b - f + 1) // 3
h = (19 * a + b - d - g + 15) % 30
i = c // 4
k = c % 4
l = (32 + 2 * e + 2 * i - h - k) % 7
m = (a + 11 * h + 22 * l) // 451
month = (h + l - 7 * m + 114) // 31
day = ((h + l - 7 * m + 114) % 31) + 1
easter_date = datetime(year, month, day)
return easter_date

# Generate Good Friday dates from 1984 to 2024
good_friday_dates_1984_to_2024 = good_friday_dates(1984, 2024)
Hope this code is right for Good Fridays

Good Friday, as Easter, is all over the map.  Tomorrow is Good Friday.  The code looks pretty gross, so I just accepted it.  It may not be 100% accurate, but if ChatGPT says so….  Once I had a list of all holidays, I was able to start cooking (MLK was added in 1986 and Juneteenth in 2021.)  Since Juneteenth is relatively new it did not offer a statistically significant number of observations.

Future Leak comes in Handy.

Every quant needs some software that will look through data and do some calculations.  I did this project with EasyLanguage, but it was a little difficult.  I had to do a two-pass process (well I did the same with Python too.)  I first created an indicator that imported the holiday dates and then outputted the close (or open of next bar) for close[5], close[4], close[3], close[2], close[1] and then waited 1, 2, 3, 4 and 5 bars after the date and printed out those respective closing prices.  I did not do this step in EasyLanguage.  I actually create EasyLanguage as my output.  Here is what it looked like:

holidayName[1]="MemorialDay";
fiveBeforeArr[1]=1010518;
fourBeforeArr[1]=1010521;
threeBeforeArr[1]=1010522;
twoBeforeArr[1]=1010523;
oneBeforeArr[1]=1010524;
oneAfterArr[1]=1010529;
twoAfterArr[1]=1010530;
threeAfterArr[1]=1010531;
fourAfterArr[1]=1010601;
fiveAfterArr[1]=1010604;
holidayName[2]="July4th";
fiveBeforeArr[2]=1010626;
fourBeforeArr[2]=1010627;
threeBeforeArr[2]=1010628;
twoBeforeArr[2]=1010629;
oneBeforeArr[2]=1010702;
oneAfterArr[2]=1010705;
twoAfterArr[2]=1010706;
threeAfterArr[2]=1010709;
fourAfterArr[2]=1010710;
fiveAfterArr[2]=1010711;
holidayName[3]="LaborDay";
fiveBeforeArr[3]=1010824;
fourBeforeArr[3]=1010827;
threeBeforeArr[3]=1010828;
twoBeforeArr[3]=1010829;
oneBeforeArr[3]=1010830;
oneAfterArr[3]=1010904;
twoAfterArr[3]=1010905;
threeAfterArr[3]=1010906;
fourAfterArr[3]=1010907;
fiveAfterArr[3]=1010910;
All key dates are stored in arrays.

Future leak allows you to peak into the future and this comes in super handy when doing research like this.  Here is some python code in TS-18 that does all these calculations in one sweep.


# The date of the holiday and the holiday name are stored in lists
# Each Holiday has its own date and 20 ROC calculations
# These calculations are stored in lists as well
tempDate = int(extraData[extraCnt][0]) #first field in the csv file
whichHoliday = extraData[extraCnt][1] #second field in the csv file
if date[D] == tempDate or (date[D] > tempDate and date[D1] < tempDate):
foundJ = 99
for j in range(12):
if whichHoliday == listOfHolidayClass[j].holidayName:
foundJ = j
listOfHolidayClass[foundJ].holidayDates.append(tempDate);
for j in range(-5,-1): #Before Holiday
for k in range(j+1,0):
denom = close[D+j]
listOfHolidayClass[foundJ].rocList.append((close[D+k] - close[D+j])/denom)
for j in range(0,4): #After Holiday - here is the future leak
for k in range(j+1,5):
denom = close[D+j]
listOfHolidayClass[foundJ].rocList.append((close[D+k] - close[D+j])/denom)
extraCnt = extraCnt + 1
Python code using future leak to calculate the 20 ROC from before and after holiday

I then printed this out to a csv file and imported into Excel (see above) and found out the best combination of days for each holiday.  Now that I had the B(n,m) or the A(m,n) values, I need a process to tell TS-18 which day to buy/short and exit.  Here is some code that, again uses future leak, to print out the five dates before the holiday and the five dates after the holiday.

tempDate = int(extraData[extraCnt][0])
whichHoliday = extraData[extraCnt][1]
if date[D] == tempDate or (date[D] > tempDate and date[D1] < tempDate):
for j in range(12):
if whichHoliday == listOfHolidayClass[j].holidayName:
foundJ = j
print(date[D],end=',')
print(whichHoliday,end=',')
for j in range(-5,6):
print(date[D+j],end=',')
print("",end = '\n')
extraCnt = extraCnt + 1

# Here is what the file looked like
19840103,NewYear,19831223,19831227,19831228,19831229,19831230,19840103,19840104,19840105,19840106,19840109,19840110,
19840221,PresidentsDay,19840213,19840214,19840215,19840216,19840217,19840221,19840222,19840223,19840224,19840227,19840228,
19840423,GoodFriday,19840413,19840416,19840417,19840418,19840419,19840423,19840424,19840425,19840426,19840427,19840430,
19840529,MemorialDay,19840521,19840522,19840523,19840524,19840525,19840529,19840530,19840531,19840601,19840604,19840605,
19840705,July4th,19840627,19840628,19840629,19840702,19840703,19840705,19840706,19840709,19840710,19840711,19840712,
19840904,LaborDay,19840827,19840828,19840829,19840830,19840831,19840904,19840905,19840906,19840907,19840910,19840911,
19841008,ColumbusDay,19841001,19841002,19841003,19841004,19841005,19841008,19841009,19841010,19841011,19841012,19841015,
19841112,VeteransDay,19841105,19841106,19841107,19841108,19841109,19841112,19841113,19841114,19841115,19841116,19841119,
19841123,Thanksgiving,19841115,19841116,19841119,19841120,19841121,19841123,19841126,19841127,19841128,19841129,19841130,
Create the dates that occur in the date B4 and after holiday

Now read the data in and pick how many days before to b/s and before to exit or after to b/s and to exit

holidayBuyDates = []
holidayXitDates = []
gfCnt = 0

#structure of holidayDatesData list - all strings
# 0.) Holiday Date
# 1.) Holiday Name
# 2.) 5-Before
# 3.) 4-Before
# 4.) 3-Before
# 5.) 2-Before
# 6.) 1-Before
# 7.) 0-Before/After
# 8.) 1-After
# 9.) 2-After
# 10.) 3-After
# 11.) 4-After
# 12.) 5-After
#Below I pick 1 day after to buy and 4 days after to exit [8,11]
for j in range(numHolidayDates):
if holidayDatesData[j][1] == "MemorialDay":
holidayBuyDates.append(int(holidayDatesData[j][8]))
holidayXitDates.append(int(holidayDatesData[j][11]))



# Okay Let's put in some logic to create a long position
if not(long) and canTrade and date[D] == holidayBuyDates[gfCnt]:
price = close[D]
tradeName = "Hol-A1Buy"
posSize = 1
tradeTicket.append([buy,tradeName,posSize,price,mkt])
# Long Exits
if long and canTrade and date[D] == holidayXitDates[gfCnt]:
price = longExit
tradeName = "Hol-A4Xit"
tradeTicket.append([exitLong,tradeName,curShares,price,mkt])
gfCnt = gfCnt + 1
TradingSimual_-18 Code to execute buys and exits
No execution fees – executing on close of the day
Memorial Day Example -----------------------------------------
Sys Name : TS-18HolidayTest
Mkt Symb : TY Mkt Name : 10YNote
19840530 Hol-A1Buy 1 -35.90625 0.00 0.00
19840604 Hol-A4Xit 1 -34.12500 1731.25 1731.25
19850529 Hol-A1Buy 1 -18.43750 0.00 0.00
19850603 Hol-A4Xit 1 -16.96875 1418.75 3150.00
19860528 Hol-A1Buy 1 -0.40625 0.00 0.00
19860602 Hol-A4Xit 1 -2.87500 -2518.75 631.25
19870527 Hol-A1Buy 1 -1.09375 0.00 0.00
19870601 Hol-A4Xit 1 -0.28125 762.50 1393.75
19880601 Hol-A1Buy 1 -1.87500 0.00 0.00
19880606 Hol-A4Xit 1 -0.56250 1262.50 2656.25
19890531 Hol-A1Buy 1 2.68750 0.00 0.00
19890605 Hol-A4Xit 1 4.46875 1731.25 4387.50
19900530 Hol-A1Buy 1 2.50000 0.00 0.00
19900604 Hol-A4Xit 1 3.68750 1137.50 5525.00
19910529 Hol-A1Buy 1 6.59375 0.00 0.00
19910603 Hol-A4Xit 1 6.65625 12.50 5537.50
19920527 Hol-A1Buy 1 13.71875 0.00 0.00
19920601 Hol-A4Xit 1 14.50000 731.25 6268.75
19930602 Hol-A1Buy 1 28.21875 0.00 0.00
19930607 Hol-A4Xit 1 27.96875 -300.00 5968.75
19940601 Hol-A1Buy 1 24.62500 0.00 0.00
19940606 Hol-A4Xit 1 25.93750 1262.50 7231.25
19950531 Hol-A1Buy 1 33.34375 0.00 0.00
19950605 Hol-A4Xit 1 34.59375 1200.00 8431.25
19960529 Hol-A1Buy 1 32.09375 0.00 0.00
19960603 Hol-A4Xit 1 30.93750 -1206.25 7225.00
19970528 Hol-A1Buy 1 32.93750 0.00 0.00
19970602 Hol-A4Xit 1 33.75000 762.50 7987.50
19980527 Hol-A1Buy 1 40.00000 0.00 0.00
19980601 Hol-A4Xit 1 40.31250 262.50 8250.00
19990602 Hol-A1Buy 1 37.62500 0.00 0.00
19990607 Hol-A4Xit 1 37.81250 137.50 8387.50
20000531 Hol-A1Buy 1 36.10938 0.00 0.00
20000605 Hol-A4Xit 1 37.43750 1278.12 9665.62
20010530 Hol-A1Buy 1 42.92188 0.00 0.00
20010604 Hol-A4Xit 1 44.06250 1090.62 10756.25
20020529 Hol-A1Buy 1 50.14062 0.00 0.00
20020603 Hol-A4Xit 1 50.68750 496.88 11253.12
20030528 Hol-A1Buy 1 68.06250 0.00 0.00
20030602 Hol-A4Xit 1 68.34375 231.25 11484.38
20040602 Hol-A1Buy 1 64.43750 0.00 0.00
20040607 Hol-A4Xit 1 63.78125 -706.25 10778.12
20050601 Hol-A1Buy 1 72.32812 0.00 0.00
20050606 Hol-A4Xit 1 72.29688 -81.25 10696.88
20060531 Hol-A1Buy 1 65.46875 0.00 0.00
20060605 Hol-A4Xit 1 66.07812 559.38 11256.25
20070530 Hol-A1Buy 1 66.57812 0.00 0.00
20070604 Hol-A4Xit 1 66.06250 -565.62 10690.62
20080528 Hol-A1Buy 1 77.21875 0.00 0.00
20080602 Hol-A4Xit 1 76.10938 -1159.38 9531.25
20090527 Hol-A1Buy 1 87.12500 0.00 0.00
20090601 Hol-A4Xit 1 87.14062 -34.38 9496.88
20100602 Hol-A1Buy 1 95.48438 0.00 0.00
20100607 Hol-A4Xit 1 96.18750 653.12 10150.00
20110601 Hol-A1Buy 1 102.23438 0.00 0.00
20110606 Hol-A4Xit 1 103.12500 840.62 10990.62
20120530 Hol-A1Buy 1 115.32812 0.00 0.00
20120604 Hol-A4Xit 1 117.37500 1996.88 12987.50
20130529 Hol-A1Buy 1 115.51562 0.00 0.00
20130603 Hol-A4Xit 1 115.59375 28.12 13015.62
20140528 Hol-A1Buy 1 116.31250 0.00 0.00
20140602 Hol-A4Xit 1 116.56250 200.00 13215.62
20150527 Hol-A1Buy 1 121.20312 0.00 0.00
20150601 Hol-A4Xit 1 121.42188 168.75 13384.38
20160601 Hol-A1Buy 1 125.07812 0.00 0.00
20160606 Hol-A4Xit 1 126.50000 1371.88 14756.25
20170531 Hol-A1Buy 1 124.17188 0.00 0.00
20170605 Hol-A4Xit 1 124.65625 434.38 15190.62
20180530 Hol-A1Buy 1 120.35938 0.00 0.00
20180604 Hol-A4Xit 1 119.21875 -1190.62 14000.00
20190529 Hol-A1Buy 1 124.76562 0.00 0.00
20190603 Hol-A4Xit 1 125.76562 950.00 14950.00
20200527 Hol-A1Buy 1 137.43750 0.00 0.00
20200601 Hol-A4Xit 1 137.84375 356.25 15306.25
20210602 Hol-A1Buy 1 133.03125 0.00 0.00
20210607 Hol-A4Xit 1 133.42188 340.62 15646.88
20220601 Hol-A1Buy 1 122.48438 0.00 0.00
20220606 Hol-A4Xit 1 121.70312 -831.25 14815.62
20230531 Hol-A1Buy 1 115.40625 0.00 0.00
20230605 Hol-A4Xit 1 115.10938 -346.88 14468.75

Here are the results for the Morgan Stanley analysis in 2018

Best combos for entry and exit for each holiday.

My analysis covered the same period plus all of 2018, 2019, 2020, 2021, 2022, and 2023.  My combos were very similar in most cases, but the last few years moved the needle a little bit.  However, the results still showed a technical edge.

Trading the Equity Curve – Part 1 of N?

Only Trade the Best Segments of the Equity Curve – Cut Out Drawdown and Take Advantage of Run Ups! Really?

Equity curve feedback has been around for many years and seems highly logical, but one can’t get an industry-wide agreement on its benefit.  The main problem is to know when to turn trading off and then back on as you track the equity curve.  The most popular approach is to use a moving average of the equity curve to signal system participation.   When the equity curve moves below 30, 60, or 90 period-moving average of equity, then just turn it off and wait until the curve crosses back above the average.  This approach will be investigated in Part 2 of this series.  Another approach is to stop trading once the curve enters a drawdown that exceeds a certain level and then start back up once the equity curve recovers.  In this post, this method will be investigated.

Programmers Perspective

How do you go about programming this tool to start with.  There are probably multiple ways of accomplishing this task, but the two I have most often observed were the two pass process and the inline simultaneous tracking of the synthetic and actual equity curves.  The two pass process generates an unadulterated equity curve and stores the equity and trades either in memory or in a file.  The second part of the process monitors the external equity curve along with the external trades synchronously and while trading is turned on, the trades are executed as they occur chronologically.  When trading is turned off, the synthetic equity curve and trades are processed along the way.   The second method is to create, which I have coined (maybe others too!), a synthetic equity curve and synthetic trades.  I have done this in my TradingSimula_18 software by creating a SynthTrade Class.  This class contains all the properties of every trade and in turn can use this information to create a synthetic equity curve.  The synthetic equity curve and trades are untouched by the real time trading.

Start Simple

The creation of an equity curve monitor and processor is best started using a very simple system.  One market algorithm that enters and exits on different dates, where pyramiding and scaling in or out are not allowed.  The first algorithm that I tested was a mean reversion system where you buy after two consecutive down closes followed by an up close and then waiting one day.  Since I tested the ES over the past 10 years you can assume the trend is up. I must admit that the day delay was a mistake on my behalf.  I was experimenting with a four bar pattern and somehow forgot to look at the prior day’s action.  Since this is an experiment it is OK!

if marketPosition <> 1 and 
(c[2] < c[3] and c[3] < c[4] and c[1]  > =  c[2]) then
buy next bar at open;

//The exit is just as simple -
//get out after four days (includeing entry bar) on the next bars open - no stops or profit objectives.  

If barsSinceEntry > 2 then sell next bar at open;
Simple Strategy to test Synthetic Trading Engine

Here is the unadulterated equity curve using $0 for execution costs.

Non adjusted equity curve of our simple mean reversion system. Wait for a pull back and then a rally before entering.

The Retrace and Recover Method

In this initial experiment, trading is suspended once you reach a draw down of 10% from the peak of the equity curve and then resumes trading once a rally of 15% of the subsequent valley.  Here is an intriguing graphic.

Green means ON. Red means OFF. The lower curve is the resultant curve.

I did this analysis by hand with Excel and it is best case scenario.  Meaning that when trading is turned back on any current synthetic position is immediately executed in the real world.  This experiment resulted in nearly the same drawdown but a large drop in overall equity curve growth – $75K.

Put the Synthetic Equity Curve Engine to the Test

Now that I had the confirmed results of the experiment, I used them as the benchmark against my TS-18 Synthetic Trade Engine.  But before I installed the Equity Curve algorithm, I needed to make sure my synthetic trades lined up exactly with the real equity curve.  The synthetic curve should align 100% with the real equity curve.  If it doesn’t, then there is a problem.  This is another reason to start with a simple trading strategy.

Take a look here where I print out the Synthetic Equity curve on  a daily basis and compare it with the end result of the analysis.

Synth. matches Reality

Now let’s see if it worked.

Testing with Synth. Equity Curve Trading Turned ON!

The equity curves are very similar.  However, there is a difference and this is caused by how one re-enters after trading is turned back on.  In this version I tested waiting for a new trade signal which might take a few days.  You could re-enter in three different ways:

  1. Automatically enter synthetic position on the next bar’s open
  2. Wait for a new trade signal
  3. Enter immediately if you can get in at a better price

Using the 10% Ret. and 15% Rec. algorithm didn’t help at all.  What if we test 10% and 10%.

10% Ret. and 10% Rec.

Now that performed better – more profit and less draw down.  Now that I have the synthetic engine working on simple algorithms we can do all sorts of equity curve analysis.  In the next installment in this series I will make sure the TS-18 Synthetic Engine can handle more complicated entry and exit algorithms.  I have already tested a simple longer term trend following strategy on a medium sized portfolio and the synthetic engine worked fine.  The retracement/recovery algorithm at 10%/15% did not work and I will go into the “whys” in my next post.

Another Good Year For Trend Following

Take a Look at the Last Two Years

Simple Donchian on a one contract basis.  $100 Commission/slippage.  Tested from 2000 thru December 31, 2021.  Do you see why most trend followers failed after the 2008 monstrous year.   Many funds caught the 2008 move and more funds were added soon thereafter.  Promises of similar performance came to fruition in 2011.  This kept much of the “new money” on the board.  However, reality set in and weak handed funds left for greener pastures.  Those that stuck it out were rewarded in 2014.  The trend drought of 2014 -2019 eroded most of the confidence in managed futures.  The rationalization that limited resources would eventually rise in price sounded good initially, but then fell on deaf ears after months of draw down.  Well known CTAs and hedge funds shut their doors forever.   The long awaited promise of 2008 came in the form of a pandemic – but it was too late.   Maybe now the deluge that ended the drought will persevere (hopefully not in the form of a pandemic) into the future.  Prices do not need to rise endlessly, but they need to move one direction or another without many hiccups.   

Simple Donchian Caught Most of the Commodities Up Moves

Which Sectors Pushed this Curve through the Roof

These reports were generated by my Python based Trading Simula-18 using Pinnacle continuous data – rollover triggered by date.  This is my new sector analysis report where I graph the last four years performance.  The tabular data is for the entire 21 year history.  The best sectors were energy, grains, financials and metals.  Lumber was extraordinary

Sector Analysis Report
################################################
Currency -------------------------------------
BN -28012 44681
SN -26925 55337
AN 6560 34350
DX 16284 24387
FN 67463 31737
JN -22212 50362
CN -25355 44110
------------------------------------------------
Totals: -12198 141445
------------------------------------------------
Currency Last 4 Years ---------------------
|
||| |
||||||
| ||||||||
| |||||||||
| ||||||||||
|||||||||||||
||||||||||||||
|||||||||||||| |
|||||||||||||||||||
||||||||||||||||||||| |
|||||||||||||||||||||| | |
|||||||||||||||||||||| | |
|||||||||||||||||||||| | || | | |
------------------------------------------------ 0
||||||||||||||||||||||||| ||| |||| ||| |
||||||||||||||||||||||||| |||| |||||||||||
||||||||||||||||||||||||| | |||| ||||||||||||
||||||||||||||||||||||||||| |||||||||||||||||
|||||||||||||||||||||||||||| ||||||||||||||||||
||||||||||||||||||||||||||||||||||||||||||||||||
2018--------2019--------2020--------2021--------
################################################
Energies -------------------------------------
ZU 180750 38330
ZH 155696 85541
ZN 70630 74400
ZB 131874 66651
------------------------------------------------
Totals: 538951 154434
------------------------------------------------
Energies Last 4 Years ---------------------
| |
|| || ||
||| || |||
| ||||||||||
| |||||||||||
||| |||||||||||
|||| | ||||||||||||
||||||| ||||||||||||
|||||||||||||||||||||
|||||||||||||||||||||
|||||||||||||||||||||
|||||||||||||||||||||
| || |||||||||||||||||||||
|| || |||||||||||||||||||||
|| |||| |||||||||||||||||||||
||||||||| ||||||||||||||||||||||
| |||||||||| |||||||||||||||||||||||
|| || |||||||||||||||||||||||||||||||||||||
|||||| |||||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||||||||||||||||
2018--------2019--------2020--------2021--------
################################################
Metals -------------------------------------
ZG -17070 43540
ZI 68395 146885
ZK 101888 29475
ZP 82885 27600
ZA 174955 83910
------------------------------------------------
Totals: 411052 166703
------------------------------------------------
Metals Last 4 Years ---------------------
|
| | |
|| || |
|| |||| |||
| ||| | |||||||||
|| |||||||||||||||||
|| |||||||||||||||||||
|||||||||||||||||||||||
|||||||||||||||||||||||
|||||||||||||||||||||||
| | ||||||||||||||||||||||||
||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||
|| |||||||||||||||||||||||||||||||
|||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||||||||
|| |||||||||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||||||||||||||||
2018--------2019--------2020--------2021--------
################################################
Grains -------------------------------------
ZS 79175 20312
ZW -43438 51975
ZC 5238 26688
ZL 13248 24588
ZM 29860 28810
------------------------------------------------
Totals: 84083 88850
------------------------------------------------
Grains Last 4 Years ---------------------
|
|
||
||
|||||||||
|||||||||||
|||||||||||
|||||||||||
||||||||||||
| ||||||||||||
| | ||||||||||||
| || ||| ||||||||||||
||||| ||||| |||||||||||||
|||||||||||| |||||||||||||
|||||||||||| ||| | |||||||||||||
||||||||||||| ||||||| || ||||||||||||||
|||||||||||||||||||||||||||| ||||||||||||||
|||||||||||||||||||||||||||||| |||||||||||||||
||||||||||||||||||||||||||||||| |||||||||||||||
||||||||||||||||||||||||||||||||||||||||||||||||
2018--------2019--------2020--------2021--------
################################################
Financials -------------------------------------
US 35991 24959
TY -350 29175
TU 1473 23969
EC 4700 9650
------------------------------------------------
Totals: 41813 56453
------------------------------------------------
Financials Last 4 Years ---------------------
| | ||||
||| | |||||
||| | || ||||||
||||| ||||||||||
|||||| ||||||||||
|||||||| ||||||||||
|||||||| ||||||||||
||||||||| |||||||||||
||||||||||||||||||||||
| ||||||||||||||||||||||
| ||||||||||||||||||||||
| | ||||||||||||||||||||||
| | || ||||||||||||||||||||||
|| |||||| |||||||||||||||||||||||
||| |||||||||||||||||||||||||||||||
|||| || | |||||||||||||||||||||||||||||||
||||| |||| |||||||||||||||||||||||||||||||
||||||| ||||||||||||||||||||||||||||||||||||
|||||||| ||||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||||||||||||||||
2018--------2019--------2020--------2021--------
################################################
Softs -------------------------------------
SB 25927 15035
KC -49775 94069
CC -72140 76660
CT 16785 45470
Lumber 218513 51745
JO 2588 15760
------------------------------------------------
Totals: 141898 128540
------------------------------------------------
Softs Last 4 Years ---------------------
| ||
||||
|||||
|||||
||||||
| ||||||
||||||||
||||||||
||||||||
||||||||
------------------------------------------------ 0
| | |||||||||
| | ||||||||||
||||||||||||||||
||||||||||||||||
|||||||||||||||||
|||||||||||||||||
|| | ||||||||||||||||||
| ||||||||||| | |||||||||||||||||||||
||||||||||||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||||||||||||||||
2018--------2019--------2020--------2021--------
################################################
Meats -------------------------------------
ZT -29940 57680
ZZ 38480 15080
ZF 18413 57550
------------------------------------------------
Totals: 26952 66515
------------------------------------------------
Meats Last 4 Years ---------------------
| ||
| || ||
|| | || ||
||| | || ||
||||||||| ||||
| |||||||||| ||||
| | ||||||||||| |||||
| | ||||||||||| |||||| |
| || |||||||||||||||||| |
| ||| | |||||||||||||||||||||
|| ||| | |||||||||||||||||||||
|||||| || | ||||||||||||||||||||| |
|||||| || |||||||||||||||||||||||| |
|||||| || ||||||||||||||||||||||||||
|||||||| | || ||||||||||||||||||||||||||
|||||||| | ||| ||||||||||||||||||||||||||
|||||||||| | |||||||||||||||||||||||||||||||
||||||||||| | |||||||||||||||||||||||||||||||
|||||||||||| || ||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||||||||||||||||
2018--------2019--------2020--------2021--------

How Do You Program this in Python

Here is the module for TS-18.  There is a little extra code to keep track of sectors in case you want to limit sector exposure.  However, this code takes every trade on a one contract basis.  This code reflects my latest version of TS-18, which will be released shortly.

#  Define Long, Short, ExitLong and ExitShort Levels - mind your indentations
buyLevel = highest(myHigh,40,curBar,1)
shortLevel = lowest(myLow,40,curBar,1)
longExit = lowest(myLow,20,curBar,1)
shortExit = highest(myHigh,20,curBar,1)
ATR = sAverage(myTrueRange,30,curBar,1)
stopAmt = 2000/myBPV

ATR = sAverage(myTrueRange,30,curBar,1)

posSize = 1
mmLxit = 99999999
mmSxit = -99999999
if mp == 1 : mmLxit = entryPrice[-1] - stopAmt
if mp ==-1 : mmSxit = entryPrice[-1] + stopAmt



# Long Exit
if mp == 1 and myLow[curBar] <= mmLxit and mmLxit > longExit and barsSinceEntry > 1:
price = min(myOpen[curBar],mmLxit)
tradeName = "LxitMM"
numShares = curShares
exitPosition(price, curShares, tradeName, sysMarkDict)
unPackDict(sysMarkDict)
# Long Exit
if mp == 1 and myLow[curBar] <= longExit and barsSinceEntry > 1:
price = min(myOpen[curBar],longExit)
tradeName = "Lxit"
numShares = curShares
exitPosition(price, curShares, tradeName, sysMarkDict)
unPackDict(sysMarkDict)
# Short Exit
if mp == -1 and myHigh[curBar] >= shortExit and barsSinceEntry > 1:
price = max(myOpen[curBar],shortExit)
tradeName = "Sxit"
numShares = curShares
exitPosition(price, curShares, tradeName, sysMarkDict)
unPackDict(sysMarkDict)
# Short Exit
if mp == -1 and myHigh[curBar] >= entryPrice[-1] + stopAmt and barsSinceEntry > 1:
price = max(myOpen[curBar],entryPrice[-1] + stopAmt)
tradeName = "SxitMM"
numShares = curShares
exitPosition(price, curShares, tradeName,sysMarkDict)
unPackDict(sysMarkDict)
# Long Entry
if myHigh[curBar] >= buyLevel and mp !=1:
price = max(myOpen[curBar],buyLevel)
tradeName = "Simple Buy"
numShares = posSize
enterLongPosition(price,numShares,tradeName,sysMarkDict)
unPackDict(sysMarkDict)
# Short Entry
if myLow[curBar] <= shortLevel and mp !=-1 :
price = min(myOpen[curBar],shortLevel)
if mp == 0 : sectorTradesTodayList[curSector] +=1
tradeName = "Simple Sell"
numShares = posSize
enterShortPosition(price, numShares, tradeName, sysMarkDict)
unPackDict(sysMarkDict)
Python within Trading Simula-18

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.

 

The Cure for the Common Trend Follower – SOTF Part 2

Clenow’s algorithm is definitely an indicator for the current State of Trend Following (SOTF).  However, the 3 X ATR trailing stop mechanism actually dampens the profit/draw down ratio.  Take a look at this chart.

Battle of Titans

All Trend Following mechanisms have a very common thread in their entry mechanisms.   The thing that separates them is the preemptive exit.  Do you allow the the algorithm to exit on a purely market defined method or do you overlay trade management?  Here the best approach was to let the Bollinger Band system run unfettered; even though it seems somewhat illogical.  Many times  trade management actually increases draw down.   Is there a solution?  What about this – keep risk down by trading a small, yet diverse portfolio of high volume markets and overlay it with a stock index mean reversion algo.  Take a look.

Bollinger Marries ES Reversion

 

Should’ve, Would’ve , Could’ve.

This could be scaled up.  The mean reversion helped lift  the chart out of the flat and draw down periods of late.  However, the smaller portfolio did OK during this time period too!  Can four or five high volume markets replicate a much larger portfolio?  All tests were carried out with TradingSimula18 – the software that comes with my latest book.

State of Trend Following – Part 1

Clenow’s Trend Following System

Its a new decade! Time to see what’s up with Trend Following.

I am a huge fan of Andreas Clenow’s books, and how he demonstrated that a typical trader could replicate the performance of most large Trend Following CTAs and not pay the 2% / 20% management/incentive combo fees.  So. I felt the system that he described in his book would be a great representation of The State of Trend Following.  At the same time I am going to demonstrate TradingSimula18 (the software included in my latest book).

System Description

Take a look at my last post.  I provide the EasyLanguage and a pretty good description of Clenow’s strategy.

TradingSimula18 Code [Python]

#---------------------------------------------------------------------------------------------------
# Start programming your great trading ideas below here - don't touch stuff above
#---------------------------------------------------------------------------------------------------
# Define Long, Short, ExitLong and ExitShort Levels - mind your indentations
ATR = sAverage(myTrueRange,30,curBar,1)
posSize = 2000/(ATR*myBPV)
posSize = max(int(posSize),1)
posSize = min(posSize,20)
avg1 = xAverage(myClose,marketVal5[curMarket],50,curBar,1)
avg2 = xAverage(myClose,marketVal6[curMarket],100,curBar,1)
marketVal5[curMarket] = avg1
marketVal6[curMarket] = avg2
donchHi = highest(myHigh,50,curBar,1)
donchLo = lowest(myLow,50,curBar,1)

if mp == 1 : marketVal1[curMarket] = max(marketVal1[curMarket],myHigh[curBar-1]- 3 * ATR)
if mp ==-1 : marketVal2[curMarket] = min(marketVal2[curMarket],myLow[curBar-1]+ 3 * ATR)
# Long Entry
if avg1 > avg2 and myHigh[curBar-1] == donchHi and mp !=1:
price = myOpen[curBar]
tradeName = "TFClenowB";numShares = posSize
marketVal1[curMarket] = price - 3 * ATR
if mp <= -1:
profit,curShares,trades = bookTrade(entry,buy,price,myDate[curBar],tradeName,numShares)
barsSinceEntry = 1
marketMonitorList[curMarket].setSysMarkTrackingInfo(tradeName,cumuProfit,mp,barsSinceEntry,curShares,trades)
# Long Exit
if mp == 1 and myClose[curBar-1] <= marketVal1[curMarket] and barsSinceEntry > 1:
price = myOpen[curBar]
tradeName = "Lxit";numShares = curShares
profit,curShares,trades = bookTrade(exit,ignore,price,myDate[curBar],tradeName,numShares)
todaysCTE = profit;barsSinceEntry = 0
marketMonitorList[curMarket].setSysMarkTrackingInfo(tradeName,cumuProfit,mp,barsSinceEntry,curShares,trades)
# Short Entry
if avg1 < avg2 and myLow[curBar-1] == donchLo and mp !=-1:
price = myOpen[curBar];numShares = posSize
marketVal2[curMarket] = price + 3 * ATR
if mp >= 1:
tradeName = "TFClenowS"
profit,curShares,trades = bookTrade(entry,sell,price,myDate[curBar],tradeName,numShares)
barsSinceEntry = 1
marketMonitorList[curMarket].setSysMarkTrackingInfo(tradeName,cumuProfit,mp,barsSinceEntry,curShares,trades)
# Short Exit
if mp == -1 and myClose[curBar-1] >= marketVal2[curMarket] and barsSinceEntry > 1:
price = myOpen[curBar]
tradeName = "Sxit"; numShares = curShares
profit,curShares,trades = bookTrade(exit,ignore,price,myDate[curBar],tradeName,numShares)
todaysCTE = profit;barsSinceEntry = 0
marketMonitorList[curMarket].setSysMarkTrackingInfo(tradeName,cumuProfit,mp,barsSinceEntry,curShares,trades)
#----------------------------------------------------------------------------------------------------------------------------
# - Do not change code below - trade, portfolio accounting - our great idea should stop here
#----------------------------------------------------------------------------------------------------------------------------
TradingSimula18 Python System Testing Environment

I am going to go over this very briefly.   I know that many of the readers of my blog have attempted to use Python and the various packages out there and have given up on it.  Quantopia and QuantConnect are great websites, but I feel they approach back-testing with a programmer in mind.  This was the main reason I created TS-18 – don’t get me wrong its not a walk in the park either, but it doesn’t rely on external libraries to get the job done.  All the reports I show here are generated from the data created solely by TS-18.  Plus it is very modular – Step 1 leads to Step2 and on and on.   Referring to the code I calculate the ATR (average true range) by calling the simple average function sAverage.  I pass it myTrueRanges, 30, curBar and 1.   I am looking for the average true range over the last 30 days.  I then move onto my position sizing – posSize = $2,000 / ATR in $s.  PosSize must fit between 1 and 20 contracts.  The ATR calculation can get rather small for some markets and the posSize can get rather large.  Avg1 and Avg2 are exponential moving averages of length 50 and 100DonchHi and donchLo are the highest high and lowest low of the past 50 days.   If mp == 1 (long position) then a trailing stop (marketVal1) is set to whichever is higher – the current marketVal1 or the yesterday’s High – 3 X ATR;  the trailing stop tracks new intra-trade highs.  The trailing stop for the short side, marketVal2 is calculated in a similar manner, but low prices are used as well as a positive offset of 3 X ATR.  

Now the next section of code is quite a bit different than say EasyLanguage, but parallels some of the online Python paradigms. Here you must test the current bar’s extremes against the donchHi if you are flat and marketVal1 (the trailing stop variable) if you are long.  If flat you also test the low of the bar against donchLo.  The relationship between avg1 and avg2 are also examined.  If the testing criteria is true, then its up to you to assign the correct price, posSize and tradeName.  So you have four independent if-then constructs:

  • Long Entry – if flat test to see if a long position should be initiated
  • Long Exit – if Long then test to see if a liquidation should be initiated
  • Short Entry – if flat test to see if a short position should be initiated
  • Short Exit – if Short then test to see if a liquidation should be initiated

That’s it – all of the other things are handled by TS-18.  Now that I have completely bored you out of your mind, let’s move onto some results.

Results from 2000 – risking $2,000 per trade:

Roller Coaster Ride for most CTAs, Last one out turn off the lights!

Sector Performance from 2000

Sector Performance from 2000

From this chart it doesn’t make much sense to trade MEATS, SOFTS or GRAINS with a Trend Following approach or does it?

In the next post, I will go over the results with more in depth and possibly propose some ideas that might or might not help.  Stay Tuned!