NexusFi: Find Your Edge


Home Menu

 





CAGR custom performance metric for NT8


Discussion in Traders Hideout

Updated
    1. trending_up 1,349 views
    2. thumb_up 0 thanks given
    3. group 2 followers
    1. forum 2 posts
    2. attach_file 0 attachments




 
Search this Thread
  #1 (permalink)
 Arthurtrades 
Paris France
 
Experience: Intermediate
Platform: sd
Trading: sd
Posts: 20 since Aug 2023
Thanks Given: 45
Thanks Received: 13

Hello Guys,

I've been trying hard with chat gpt and my own modest dev skills to code a CAGR custom performance metric for NT8, but I'm stuck : I keep getting a NaN value. If anybody could look at the code and give an advice that would be amazing (i'm sure a lot of people here using ninja could use a CAGR...).

Code :

//
// Copyright (C) 2024, NinjaTrader LLC <www.ninjatrader.com>.
// NinjaTrader reserves the right to modify or overwrite this NinjaScript component with each release.
//
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.ComponentModel.DataAnnotations; // Added for DisplayAttribute
using NinjaTrader.Cbi; // For Account, Currency, AccountItem references

#endregion


namespace NinjaTrader.NinjaScript.PerformanceMetrics
{
public class CAGRPerformanceMetric : PerformanceMetric
{
private double startValue; // Start value of the portfolio
private double endValue; // End value of the portfolio
private double totalDurationYears; // Duration of the performance in years
private DateTime startTime; // Start time of the first trade
private DateTime endTime; // End time of the last trade

protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "CAGRPerformanceMetric"; // Name shown in UI
startValue = 0.0;
endValue = 0.0;
totalDurationYears = 0.0;
}
else if (State == State.Configure)
{
Values = new double[1]; // Only one value: CAGR
}
else if (State == State.Active)
{
// Reset all values at the start of a new backtest run
startValue = 0.0;
endValue = 0.0;
totalDurationYears = 0.0;
startTime = DateTime.MinValue;
endTime = DateTime.MinValue;
Array.Clear(Values, 0, Values.Length);
}
}

// This is called as each trade is added
protected override void OnAddTrade(Cbi.Trade trade)
{
// Initialize startValue and startTime at the first trade
if (startValue == 0.0 && trade.Entry != null)
{
startValue = trade.Entry.Account.Get(Cbi.AccountItem.RealizedProfitLoss, Cbi.Currency.UsDollar);
startTime = trade.Entry.Time;
}

// Update the endValue and endTime at each trade exit
if (trade.Exit != null)
{
endValue = trade.Exit.Account.Get(Cbi.AccountItem.RealizedProfitLoss, Cbi.Currency.UsDollar);
endTime = trade.Exit.Time;
}

// If we have both a start and end time, calculate the duration
if (startTime != DateTime.MinValue && endTime != DateTime.MinValue)
{
totalDurationYears = (endTime - startTime).TotalDays / 252; // Approximate years calculation
}

// Calculate CAGR if duration is valid
if (startValue > 0 && endValue > 0 && totalDurationYears > 0)
{
Values[0] = CalculateCAGR(startValue, endValue, totalDurationYears);
}
else
{
Values[0] = 0; // Handle cases where values are not yet valid
}
}

// CAGR calculation
private double CalculateCAGR(double start, double end, double years)
{
return Math.Pow(end / start, 1 / years) - 1;
}

// Called when merging metrics
protected override void OnMergePerformanceMetric(PerformanceMetricBase target)
{
CAGRPerformanceMetric targetMetrics = target as CAGRPerformanceMetric;

if (targetMetrics != null && TradesPerformance.TradesCount + targetMetrics.TradesPerformance.TradesCount > 0)
{
// Aggregate by recalculating CAGR over the combined performance data
double combinedStartValue = Math.Min(startValue, targetMetrics.startValue);
double combinedEndValue = Math.Max(endValue, targetMetrics.endValue);
double combinedDurationYears = totalDurationYears + targetMetrics.totalDurationYears;

if (combinedStartValue > 0 && combinedDurationYears > 0)
{
targetMetrics.Values[0] = CalculateCAGR(combinedStartValue, combinedEndValue, combinedDurationYears);
}
}
}

// Copy values to target metric
protected override void OnCopyTo(PerformanceMetricBase target)
{
CAGRPerformanceMetric targetMetrics = target as CAGRPerformanceMetric;

if (targetMetrics != null)
{
targetMetrics.startValue = startValue;
targetMetrics.endValue = endValue;
targetMetrics.totalDurationYears = totalDurationYears;
Array.Copy(Values, targetMetrics.Values, Values.Length);
}
}

// The format method allows you to customize the rendering of the performance value on the summary grid
public override string Format(object value, Cbi.PerformanceUnit unit, string propertyName)
{
double[] tmp = value as double[];
if (tmp != null && tmp.Length == 1 && unit == Cbi.PerformanceUnit.Percent)
{
if (tmp[0] != 0)
return tmp[0].ToString("P"); // Format as a percentage
else
return "0%"; // Handle zero values gracefully
}
return "N/A"; // Handle null or unexpected formats
}

[Display(ResourceType = typeof(Custom.Resource), Description = "", Name = "CAGR", Order = 0)]
public double[] Values { get; private set; }
}
}


Started this thread Reply With Quote

Can you help answer these questions
from other members on NexusFi?
Iran Ceasefire Surges to 19.5% on US 15-Point Plan -- 82 …
Prediction Markets & Event Contracts
El Clasico Draws $9.2M in Prediction Market Action -- Bi …
Prediction Markets & Event Contracts
TradingView Opens Volume Footprint Data to Pine Script - …
TradingView
Cboe Files for Near 24x5 Equities Trading -- December 20 …
Traders Hideout
Kraken Becomes First US Digital Asset Bank With Direct F …
Cryptocurrency
 
Best Threads (Most Thanked)
in the last 7 days on NexusFi
Sober Journey With S&P
22 thanks
2026 Jlab journal
10 thanks
Trying to learn Volume and price action correlation
8 thanks
Algo automated / semi-automated trading anyone?
6 thanks
Lady Vols Primer: Trading Volatility Journal
5 thanks
  #2 (permalink)
 
tradevelopers's Avatar
 tradevelopers   is a Vendor
 
Posts: 62 since Jan 2013
Thanks Given: 3
Thanks Received: 33


Arthurtrades View Post
Hello Guys,

I've been trying hard with chat gpt and my own modest dev skills to code a CAGR custom performance metric for NT8, but I'm stuck : I keep getting a NaN value. If anybody could look at the code and give an advice that would be amazing (i'm sure a lot of people here using ninja could use a CAGR...).

Code :

//
// Copyright (C) 2024, NinjaTrader LLC <www.ninjatrader.com>.
// NinjaTrader reserves the right to modify or overwrite this NinjaScript component with each release.
//
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.ComponentModel.DataAnnotations; // Added for DisplayAttribute
using NinjaTrader.Cbi; // For Account, Currency, AccountItem references

#endregion


namespace NinjaTrader.NinjaScript.PerformanceMetrics
{
public class CAGRPerformanceMetric : PerformanceMetric
{
private double startValue; // Start value of the portfolio
private double endValue; // End value of the portfolio
private double totalDurationYears; // Duration of the performance in years
private DateTime startTime; // Start time of the first trade
private DateTime endTime; // End time of the last trade

protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "CAGRPerformanceMetric"; // Name shown in UI
startValue = 0.0;
endValue = 0.0;
totalDurationYears = 0.0;
}
else if (State == State.Configure)
{
Values = new double[1]; // Only one value: CAGR
}
else if (State == State.Active)
{
// Reset all values at the start of a new backtest run
startValue = 0.0;
endValue = 0.0;
totalDurationYears = 0.0;
startTime = DateTime.MinValue;
endTime = DateTime.MinValue;
Array.Clear(Values, 0, Values.Length);
}
}

// This is called as each trade is added
protected override void OnAddTrade(Cbi.Trade trade)
{
// Initialize startValue and startTime at the first trade
if (startValue == 0.0 && trade.Entry != null)
{
startValue = trade.Entry.Account.Get(Cbi.AccountItem.RealizedProfitLoss, Cbi.Currency.UsDollar);
startTime = trade.Entry.Time;
}

// Update the endValue and endTime at each trade exit
if (trade.Exit != null)
{
endValue = trade.Exit.Account.Get(Cbi.AccountItem.RealizedProfitLoss, Cbi.Currency.UsDollar);
endTime = trade.Exit.Time;
}

// If we have both a start and end time, calculate the duration
if (startTime != DateTime.MinValue && endTime != DateTime.MinValue)
{
totalDurationYears = (endTime - startTime).TotalDays / 252; // Approximate years calculation
}

// Calculate CAGR if duration is valid
if (startValue > 0 && endValue > 0 && totalDurationYears > 0)
{
Values[0] = CalculateCAGR(startValue, endValue, totalDurationYears);
}
else
{
Values[0] = 0; // Handle cases where values are not yet valid
}
}

// CAGR calculation
private double CalculateCAGR(double start, double end, double years)
{
return Math.Pow(end / start, 1 / years) - 1;
}

// Called when merging metrics
protected override void OnMergePerformanceMetric(PerformanceMetricBase target)
{
CAGRPerformanceMetric targetMetrics = target as CAGRPerformanceMetric;

if (targetMetrics != null && TradesPerformance.TradesCount + targetMetrics.TradesPerformance.TradesCount > 0)
{
// Aggregate by recalculating CAGR over the combined performance data
double combinedStartValue = Math.Min(startValue, targetMetrics.startValue);
double combinedEndValue = Math.Max(endValue, targetMetrics.endValue);
double combinedDurationYears = totalDurationYears + targetMetrics.totalDurationYears;

if (combinedStartValue > 0 && combinedDurationYears > 0)
{
targetMetrics.Values[0] = CalculateCAGR(combinedStartValue, combinedEndValue, combinedDurationYears);
}
}
}

// Copy values to target metric
protected override void OnCopyTo(PerformanceMetricBase target)
{
CAGRPerformanceMetric targetMetrics = target as CAGRPerformanceMetric;

if (targetMetrics != null)
{
targetMetrics.startValue = startValue;
targetMetrics.endValue = endValue;
targetMetrics.totalDurationYears = totalDurationYears;
Array.Copy(Values, targetMetrics.Values, Values.Length);
}
}

// The format method allows you to customize the rendering of the performance value on the summary grid
public override string Format(object value, Cbi.PerformanceUnit unit, string propertyName)
{
double[] tmp = value as double[];
if (tmp != null && tmp.Length == 1 && unit == Cbi.PerformanceUnit.Percent)
{
if (tmp[0] != 0)
return tmp[0].ToString("P"); // Format as a percentage
else
return "0%"; // Handle zero values gracefully
}
return "N/A"; // Handle null or unexpected formats
}

[Display(ResourceType = typeof(Custom.Resource), Description = "", Name = "CAGR", Order = 0)]
public double[] Values { get; private set; }
}
}

Explain wht u need_?


Follow me on X Reply With Quote
  #3 (permalink)
 
tradevelopers's Avatar
 tradevelopers   is a Vendor
 
Posts: 62 since Jan 2013
Thanks Given: 3
Thanks Received: 33


 
Code
#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;
using NinjaTrader.NinjaScript.DrawingTools;
using NinjaTrader.NinjaScript.Indicators;
#endregion

namespace NinjaTrader.NinjaScript.PerformanceMetrics
{
    public class CAGRPerformanceMetric : PerformanceMetric
    {
        private double startValue; // Start value of the portfolio
        private double endValue; // End value of the portfolio
        private double totalDurationYears; // Duration of the performance in years
        private DateTime startTime; // Start time of the first trade
        private DateTime endTime; // End time of the last trade

        protected override void OnStateChange()
        {
            if (State == State.SetDefaults)
            {
                Name = "CAGRPerformanceMetric"; // Name shown in UI
                startValue = 0.0;
                endValue = 0.0;
                totalDurationYears = 0.0;
            }
            else if (State == State.Configure)
            {
                Values = new double[1]; // Only one value: CAGR
            }
            else if (State == State.Active)
            {
                // Reset all values at the start of a new backtest run
                startValue = 0.0;
                endValue = 0.0;
                totalDurationYears = 0.0;
                startTime = DateTime.MinValue;
                endTime = DateTime.MinValue;
                Array.Clear(Values, 0, Values.Length);
            }
        }

        // This is called as each trade is added
        protected override void OnAddTrade(Cbi.Trade trade)
        {
            // Initialize startValue and startTime at the first trade
            if (startValue == 0.0 && trade.Entry != null)
            {
                startValue = trade.Entry.Account.Get(Cbi.AccountItem.RealizedProfitLoss, Cbi.Currency.UsDollar);
                startTime = trade.Entry.Time;
            }

            // Update the endValue and endTime at each trade exit
            if (trade.Exit != null)
            {
                endValue = trade.Exit.Account.Get(Cbi.AccountItem.RealizedProfitLoss, Cbi.Currency.UsDollar);
                endTime = trade.Exit.Time;
            }

            // If we have both a start and end time, calculate the duration
            if (startTime != DateTime.MinValue && endTime != DateTime.MinValue)
            {
                totalDurationYears = (endTime - startTime).TotalDays / 252.0; // Approximate years calculation
            }

            // Calculate CAGR if duration is valid
            if (startValue > 0 && endValue > 0 && totalDurationYears > 0)
            {
                Values[0] = CalculateCAGR(startValue, endValue, totalDurationYears);
            }
            else
            {
                Values[0] = 0; // Handle cases where values are not yet valid
            }
        }

        // CAGR calculation
        private double CalculateCAGR(double start, double end, double years)
        {
            return Math.Pow(end / start, 1 / years) - 1;
        }

        // Called when merging metrics
        protected override void OnMergePerformanceMetric(PerformanceMetricBase target)
        {
            CAGRPerformanceMetric targetMetrics = target as CAGRPerformanceMetric;

            if (targetMetrics != null && TradesPerformance.TradesCount + targetMetrics.TradesPerformance.TradesCount > 0)
            {
                // Aggregate by recalculating CAGR over the combined performance data
                double combinedStartValue = Math.Min(startValue, targetMetrics.startValue);
                double combinedEndValue = Math.Max(endValue, targetMetrics.endValue);
                double combinedDurationYears = totalDurationYears + targetMetrics.totalDurationYears;

                if (combinedStartValue > 0 && combinedDurationYears > 0)
                {
                    targetMetrics.Values[0] = CalculateCAGR(combinedStartValue, combinedEndValue, combinedDurationYears);
                }
            }
        }

        // Copy values to target metric
        protected override void OnCopyTo(PerformanceMetricBase target)
        {
            CAGRPerformanceMetric targetMetrics = target as CAGRPerformanceMetric;

            if (targetMetrics != null)
            {
                targetMetrics.startValue = startValue;
                targetMetrics.endValue = endValue;
                targetMetrics.totalDurationYears = totalDurationYears;
                Array.Copy(Values, targetMetrics.Values, Values.Length);
            }
        }

        // The format method allows you to customize the rendering of the performance value on the summary grid
        public override string Format(object value, Cbi.PerformanceUnit unit, string propertyName)
        {
            double[] tmp = value as double[];
            if (tmp != null && tmp.Length == 1 && unit == Cbi.PerformanceUnit.Percent)
            {
                if (tmp[0] != 0)
                    return tmp[0].ToString("P"); // Format as a percentage
                else
                    return "0%"; // Handle zero values gracefully
            }
            return "N/A"; // Handle null or unexpected formats
        }

        [Display(ResourceType = typeof(Custom.Resource), Description = "", Name = "CAGR", Order = 0)]
        public double[] Values { get; private set; }
    }
}
FIXED ENjoy!


Follow me on X Reply With Quote




Last Updated on October 9, 2024


© 2026 NexusFi®, s.a., All Rights Reserved.
Av Ricardo J. Alfaro, Century Tower, Panama City, Panama, Ph: +507 833-9432 (Panama and Intl), +1 888-312-3001 (USA and Canada)
All information is for educational use only and is not investment advice. There is a substantial risk of loss in trading commodity futures, stocks, options and foreign exchange products. Past performance is not indicative of future results.
About Us - Contact Us - Site Rules, Acceptable Use, and Terms and Conditions - Downloads - Top
no new posts