I tried to code this by following the SampleCumProfit example. I made some changes, because I only want to perform the calculations at the end of each backtest, not after each trade. Also, since this metric is only applicable for currency-based backtests, I only populated the targetMetrics.Values[(int)Cbi.PerformanceUnit.Currency] array. The script seems to perform the calculations correctly, but when I run a backtest, instead of displaying the result, it displays System.Double[]. I've attached a screen shot that shows this and I've posted the code below. Please help me get this working.
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Gui.Tools;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
#endregion
//This namespace holds Performance metrics in this folder and is required. Do not change it.
namespace NinjaTrader.NinjaScript.PerformanceMetrics
{
public class RiskAdjustedReturn : PerformanceMetric
{
private double maeAllTrades;
private Cbi.Currency denomination = (Cbi.Currency) (-1);
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = @"";
Name = "Risk Adjusted Return";
}
else if (State == State.Active)
{
maeAllTrades = 0;
}
}
protected override void OnAddTrade(Cbi.Trade trade)
{
if (denomination == (Cbi.Currency) (-1))
denomination = trade.Exit.Account.Denomination;
if (trade.MaeCurrency > maeAllTrades)
maeAllTrades = trade.MaeCurrency;
}
// This is called as the values of a trade metric are saved, which occurs e.g. in the strategy analyzer on optimizer runs
protected override void OnCopyTo(PerformanceMetricBase target)
{
// You need to cast, in order to access the right type
RiskAdjustedReturn targetMetrics = (target as RiskAdjustedReturn);
double annualizedNetProfit = TradesPerformance.NetProfit / ((TradesPerformance.MaxDate - TradesPerformance.MinDate).TotalDays / 365); // Net Profit divided by years
targetMetrics.Values[(int)Cbi.PerformanceUnit.Currency] = (annualizedNetProfit / maeAllTrades) / 100;
}
// This is called as the trade metric is merged, which occurs e.g. in the strategy analyzer for the total row and for aggregate
protected override void OnMergePerformanceMetric(PerformanceMetricBase target)
{
// You need to cast, in order to access the right type
RiskAdjustedReturn targetMetrics = (target as RiskAdjustedReturn);
// This is just a simple weighted average sample
if (targetMetrics != null && TradesPerformance.TradesCount + targetMetrics.TradesPerformance.TradesCount > 0)
for (int i = 0; i < Values.Length; i++)
targetMetrics.Values[i] = (targetMetrics.Values[i] * targetMetrics.TradesPerformance.TradesCount + Values[i] * TradesPerformance.TradesCount) / (TradesPerformance.TradesCount + targetMetrics.TradesPerformance.TradesCount);
}
#region Properties
// The display attribute determines the name of the performance value on the grid (the actual property name below is irrelevant for that matter)
[Display(ResourceType = typeof(Custom.Resource), Description = "", Name = "Risk adjusted return", Order = 0)]
public double[] Values
{ get; private set; }
#endregion
}
}

Comment