Measuring Drawdown

Hi,

In our previous post on Measuring Leverage we showed how subscribers can identify systems employing very high levels of leverage or Martingale techniques. This post extends that code to compare Drawdowns of various systems.

While C2 provides exhaustive statistics on Drawdown, it does not provide time taken to recover from Max.Drawdown. We believe this code will help subscribers in three ways:
(A) Compare Drawdown curves of systems (%),
(B) Identify systems with short Drawdown period (i.e:peak-valley), and
© Identify systems with fast recovery period (i.e:peak-valley-peak)

Disclaimer before we go any further : We are the owners of TickPrime SP500

Here are the results of this code:

Scatter plot to identify strategies with short Drawdown periods and fast Recovery periods (bottom left corner is better)

TimeSeries chart of Drawdown curves (less magnitude and less frequent is better)

Bar chart showing number of days taken by a strategy for its Max Drawdown period (less/right hand corner is better)

Bar chart showing number of days taken by a strategy to recover and make a new high when it experienced a Max.Drawdown (less/right hand corner is better)

Note: Recovery period includes Max.Drawdown period.

Here is the C2Explorer code for your perusal (we are putting it in public domain without any license):

// Select Systems we want in charts
Int64[] systemsIds = { 90325773, 46106678, 97642708, 95943912, 92728998
                       , 89240089, 94739988, 96506348, 93171889, 93192881
                     , 94661371, 97468745, 83833839, 75421760, 94514120
                     };

// Set starting date (YYYY,MM,DD)
var startingDate = new DateTime(2015,01,01);

// Create chart objects
ITimeSeriesChart notionalValueChart = new TimeSeriesChart();
notionalValueChart.Name = "Notional Value";
ITimeSeriesChart leverageMultipleChart = new TimeSeriesChart();
leverageMultipleChart.Name = "Leverage Multiple";
ITimeSeriesChart drawDownChart = new TimeSeriesChart();
drawDownChart.Name = "DrawDown (%)";
IColumnChart maxDDDaysChart = new ColumnChart("Max. Drawdown Period", "System", "Max. Drawdown Period (Days)");
IColumnChart maxDDRecoveryDaysChart = new ColumnChart("Recovery Period from Max. Drawdown", "System", "Recovery Period from Max. Drawdown (Days)");

// Variables
Random random = new Random();
decimal netOpenPos, maxEqValue = 0;
double maxDDRecoveryDays, currRecoveryDays = 0;
var maxEqDate = new DateTime(1970,01,01);
List<ITimeSeriesPoint> nvData = new List<ITimeSeriesPoint>();
List<ITimeSeriesPoint> lmData = new List<ITimeSeriesPoint>();
List<ITimeSeriesPoint> ddData = new List<ITimeSeriesPoint>();
List<Tuple<string, double, double>> ddDays = new List<Tuple<string, double, double>>();
List<XYData> ddXYdata = new List<XYData>();

// Add Systems to charts
foreach (var id in systemsIds)
{
    // Initialize Charts data for System
    IChartTimeSeries nvChSeries = new ChartTimeSeries();
    nvChSeries.Type = ChartTypes.Line;
    nvChSeries.Name = (from sys in C2SYSTEMS where sys.SystemId == id select sys.SystemName).First();
    nvChSeries.Color = Color.FromArgb(random.Next(255), random.Next(255), random.Next(255));

    IChartTimeSeries lmChSeries = new ChartTimeSeries();
    lmChSeries.Type = ChartTypes.Line;
    lmChSeries.Name = nvChSeries.Name;
    lmChSeries.Color = nvChSeries.Color;

    IChartTimeSeries ddChSeries = new ChartTimeSeries();
    ddChSeries.Type = ChartTypes.Line;
    ddChSeries.Name = nvChSeries.Name;
    ddChSeries.Color = nvChSeries.Color;

    // Reset Variables for System
    netOpenPos = 0;
    maxEqValue = 0;
    maxDDRecoveryDays = 0;
    currRecoveryDays = 0;
    nvData = new List<ITimeSeriesPoint>();
    lmData = new List<ITimeSeriesPoint>();
    ddData = new List<ITimeSeriesPoint>();
  
    // Identify Positions and NotionalValue
    foreach (var sigQ in (from sig in C2SIGNALS 
                        where (sig.SystemId == id && sig.TradedWhen > startingDate)
                        orderby sig.Id 
                        select new 
                        {d=sig.TradedWhen,q=sig.Quant,a=sig.Action,p=sig.TradePrice,ptV=sig.PtValue}
                       ))
    {
      if(sigQ.a == "BTO" || sigQ.a == "BTC"){ netOpenPos += sigQ.q; }else if(sigQ.a == "STO" || sigQ.a == "STC"){ netOpenPos -= sigQ.q; }
      nvData.Add( new TimeSeriesPoint() { DateTime = sigQ.d, Value = (double)(sigQ.ptV*sigQ.p*netOpenPos) });
    }
  
    // Identify Leverage
    foreach (var nv in nvData)
    {
      var eqPtVal = (from eqdata in C2EQUITY where (eqdata.SystemId == id && eqdata.DateTime > nv.DateTime) orderby eqdata.DateTime ascending select eqdata.Value).First();
      lmData.Add(new TimeSeriesPoint() { DateTime = nv.DateTime, Value = Math.Abs(nv.Value/(double)eqPtVal) });
      if(eqPtVal > maxEqValue)
      {
        maxEqValue = eqPtVal;
        maxEqDate = nv.DateTime;
      }
      currRecoveryDays = Math.Round((nv.DateTime-maxEqDate).TotalDays,0);
      maxDDRecoveryDays = (currRecoveryDays>maxDDRecoveryDays?currRecoveryDays:maxDDRecoveryDays);
      ddData.Add(new TimeSeriesPoint() { DateTime = nv.DateTime, Value = 100*(double)((eqPtVal-maxEqValue)/maxEqValue) });
    }

    // Configure Charts
    nvChSeries.Data = nvData;
    notionalValueChart.Add(nvChSeries);
    lmChSeries.Data = lmData;
    leverageMultipleChart.Add(lmChSeries);
    ddChSeries.Data = ddData;
    drawDownChart.Add(ddChSeries);
    ddDays.Add(new Tuple<string, double, double>(ddChSeries.Name
                                                 , (double)(from stat in C2STATS where (stat.SystemId == id && stat.StatName == "maxdrawdownDays") select stat.StatValueVal).First()
                                                   , maxDDRecoveryDays));
}

ddDays.Sort((a, b) => b.Item2.CompareTo(a.Item2));
foreach (var maxDDTuple in ddDays)
{
  maxDDDaysChart.Add(maxDDTuple.Item1, maxDDTuple.Item2);
}

ddDays.Sort((a, b) => b.Item3.CompareTo(a.Item3));
foreach (var maxDDRecoveryTuple in ddDays)
{
  maxDDRecoveryDaysChart.Add(maxDDRecoveryTuple.Item1, maxDDRecoveryTuple.Item3);
  ddXYdata.Add(new XYData(){X=(decimal)maxDDRecoveryTuple.Item2,Y=(decimal)maxDDRecoveryTuple.Item3});
}

// Plot the Charts
CHART = leverageMultipleChart;
HR();
CHART = notionalValueChart;
HR();
CHART = drawDownChart;
HR();
CHART = maxDDDaysChart;
HR();
CHART = maxDDRecoveryDaysChart;
HR();
CHART = (ScatterChart.Create(
    "Drawdown efficiency",
    "System",
    ddXYdata, "DrawDown Period (Days)","Recovery Period (Days)"));

Finally, it will be nice if you can share any modifications to this code, which you believe will benefit others.

Regards,
ACA

5 Likes

I just want to say I personally really appreciate the C2 Explorer code you have been sharing with the community. Kudos.

2 Likes

ACA, this and your other post on Measuring Leverage, are brilliant. They add an enormous amount of information as to how a strategy is making its money and the risk it is taking to do it. Please Matthew can you work on adding this to every system page by default? Thank you both.

2 Likes

ACA, I tried to use your Drawdown Recovery code but I get the error:
3:18:43 PM: *** Error: Sequence contains no elements
Thanks in advance for any help you can provide.

Dear ChasD,

We are no longer active on C2. Hence the delay in replying.

The error you received occurs if the C2 strategies you chose do not have all the data fields for the time period. You can try changing the starting period or the strategies to narrow down on what is causing the error.

Hope this helps. Good luck!

Regards,
ACA