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