[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Combined Equity Curves



PureBytes Links

Trading Reference Links

Further thoughts on the code I posted:


* I realized that a bar-by-bar equity indicator is overkill for most 
traders, and needlessly adds complexity and wastes global memory.  
Most people who trade a basket of systems are not going to watch the 
aggregate equity on a minute-by-minute basis.  I suspect a daily 
equity indicator would be sufficient for nearly everyone.  So, change 
the TimeIndex calculation (on both the "storing" code AND the 
"reading" code in the indicator) to offset by days, not bars: 
TimeIndex = NowDate - StartDate.  That allocates memory for one 
equity value per day, per system.  Then only run the 
storage/retrieval code on a daily basis.  (See revised code, below.)

The value of SysEquity in the indicator will remain unchanged during 
the trading day, so even if you're charting intraday data the 
indicator will continue to plot the total start-of-day equity 
throughout the day.

BTW, this change solves the problem of running systems with different 
timeframes.  As long as all your systems are daily or less, they can 
all play together with this approach.  Whether they run on 1min or 
60min bars, they all store one equity value per day.  If you run some 
of your systems on weekly or monthly bars, you should add code to 
fill in the daily values between your weekly/monthly bars so your 
equity indicator can retrieve valid equity values for your weekly and 
monthly systems, and plot valid equity on a daily basis.  (Or you 
could just plot the equity indicator on a weekly/monthly chart, and 
ignore the daily equity changes from your other systems.)


* I should have made the code a bit more bombproof.  In particular, 
the code I posted won't behave very well if you run system 1 through 
N, or the indicator, before running System 0 to initialize memory.  
If System 0 hasn't written the StartDate value into memory, the other 
code will try to compute a NowDate-StartDate offset using whatever 
value is in the StartDate location.  This is likely to result in a 
bogus TimeIndex value that might access illegal memory locations.

So let's add a bit of protection.  I've inserted a "marker" value in 
global memory when I initialize StartDate, and I only allow the other 
code to access StartDate if the "marker" is present.  I also generate 
the marker value *using* the value of StartDate, so that marker is 
valid only for that StartDate.  It's sort of like a "parity check" 
for StartDate.

This prevents you from accessing a bogus value of StartDate before 
System 0 has run the *first* time, AND if you change the value of 
StartDate for System 0.


* I said that you might want to run the equity indicator on the same 
chart as your master system.  On further thought that's probably not 
a good idea.  To make sure your equity plot is valid, you shouldn't 
run the equity indicator until ALL other systems have run and stored 
their equity values.  So you should probably run the equity indicator 
on a separate chart, or at least on the same chart as the LAST system 
that you run.

You might want to put System 0 in one workspace, and all other 
systems and the indicator in another workspace.  That allows you to 
control the order that they get run.  Open the System 0 workspace 
first, THEN open the everything-else workspace.


* I added a plot line to show Maximum equity to date (closed or open, 
depending on what you decided to store).  This makes it easier to see 
drawdowns.



Again, let me warn you that I haven't tested this code.  I've spent 
way too much time on it already.  :-)  But it should be very close to 
what you need, and hopefully it was an instructive exercise for 
everyone who stuck with me this far.

Enjoy,
Gary

=========================

Run this code in each system.  BTW, I believe you can put this into 
an "Includesystem" and pass the MySysNum value as a parameter, but I 
don't usually use "Includesystem"s so I'm no expert there.  I've 
written this code assuming you do that.  If it turns out that doesn't 
work, you can move the MySysNum value back into the Constants section.


Inputs: MySysNum(0);

{ This code stores the closed equity for N systems.
  All equity values for the same bar are stored contiguously, e.g.:
  (Base)  (Base+1)   (Base+Offset)
  Marker  StartDate  Sys0Bar1  Sys1Bar1  ... Sys0Bar2  Sys1Bar2 ...

  All told, the data array uses up NSys*maxdays+Offset memory
  locations.  NSys and Offset are defined in the Constants
  section below, and "maxdays" is the maximum number of days
  (NOT bars) spanned by all of your systems.  I.e. it is the
  last day found in any system, minus the start date of System 0.

  System #0 is the "master" system, and it must be run *before*
  all other systems.  Its starting date is stored as the starting
  date for the equity data, and all other systems key off that date.
  No system should have a starting date earlier than System #0
  (because it will result in rather skewed equity figures) but
  the code shouldn't bomb if one does.

  Set the system ID for each system in the MySysNum input value.
  Make sure that each system has a unique ID value, using 0 for the
  "master" system and 1 through NSys for the others.
}

{ Constants:  
    NSys:      number of systems to be combined
    Base:      location in global memory to store the system data
    Offset:    # slots of "overhead" memory before data array
    MinPerDay: # minutes per day (duh :-)
}
Vars: NSys(10), Base(1000), Offset(2), MinPerDay(1440);

Vars: Marker(0), NowDate(0), StartDate(0), SysEquity(0);

{ Get current date.  Store base date in global memory,
  just before the array of system data.
}

NowDate = DateToJulian(Date);

if (CurrentBar = 1) then begin
  { "Master" system stores the date; all others read that date }
  if (MySysNum = 0) then begin
    StartDate = NowDate;
    Marker = 112233 - StartDate;
    Save_TS_Data(Base, Marker);
    Save_TS_Data(Base+1, StartDate);
    end
  else begin
    Load_TS_Data(Base, &Marker);
    Load_TS_Data(Base+1, &StartDate);
    { Clear StartDate if no valid Marker found }
    if (Marker <> 112233 - StartDate) then StartDate = 0;
    end;
  end;

{ Store the system equity values once per day.
  Must check for StartDate <> 0 because TS sometimes doesn't 
  set CurrentBar = 1 until the 2nd or 3rd bar !! 
  StartDate <> 0 also blocks access to global memory
  if Marker hasn't been set by System 0.
  Check for NowDate >= StartDate in case system #N has 
  an earlier start date than System 0. 
}

if (StartDate <> 0) and (NowDate >= StartDate) 
     and (Date <> Date[1]) then begin
  TimeIndex = NowDate - StartDate;
  Sys Equity = I_ClosedEquity;    
                        { Or use I_OpenEquity if you prefer }
  Save_TS_Data(Base + Offset + Nsys*TimeIndex + MySysNum, SysEquity);
  end;

======================

In the EquityCurve indicator:  

Vars: NSys(10), Base(1000), Offset(2), MinPerDay(1440);

Vars: NowDate(0), StartDate(0), SysNum(0), 
      SysEquity(0), Eq(0), MaxEq(0);

{ Get current & base date. }

NowDate = DateToJulian(Date);

if (CurrentBar = 1) then begin
    Load_TS_Data(Base, &Marker);
    Load_TS_Data(Base+1, &StartDate);
    { Clear StartDate if no valid Marker found }
    if (Marker <> 112233 - StartDate) then StartDate = 0;
    end;

{ Retrieve equity for each system for this day, sum & plot }

if (StartDate <> 0) and (NowDate >= StartDate) 
    and (Date <> Date[1]) then begin
  TimeIndex = NowDate-StartDate;
  SysEquity = 0;
  for SysNum = 0 to NSys-1 begin
    Load_TS_Data(Base + Offset + Nsys*TimeIndex + SysNum, &Eq);
    SysEquity = SysEquity + Eq;
    end;
  end;

if (SysEquity > MaxEq) then MaxEq = SysEquity;

Plot1(SysEquity, "Equity");
Plot2(MaxEq, "Max Equity");