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