Welcome to NexusFi: the best trading community on the planet, with over 150,000 members Sign Up Now for Free
Genuine reviews from real traders, not fake reviews from stealth vendors
Quality education from leading professional traders
We are a friendly, helpful, and positive community
We do not tolerate rude behavior, trolling, or vendors advertising in posts
We are here to help, just let us know what you need
You'll need to register in order to view the content of the threads and start contributing to our community. It's free for basic access, or support us by becoming an Elite Member -- see if you qualify for a discount below.
-- Big Mike, Site Administrator
(If you already have an account, login at the top of the page)
I have been looking for a way to share data between indicators and charts, i.e. replicating the Global Variable dll on TradeStation. The reason for this is that I would like to avoid having to recalculate stuff and spend CPU when I already have the values somewhere else.
I have come up with a very simple solution involving user methods, and it seems to work fine real time.
My lack of C# skills have prevented me from implementing historical data, but the way I see it is that it would be required to store a link to some sort of array per global variable. And then it would be necessary with some logic to retrieve the correct data when historical data is processed.
Would someone like to contribute to make this work on historical data, or does someone have a better idea?
The core are two methods
- SetGlobalVariable -> called by the calculating indicator with a uniqueue identifier (I use the following naming convention <Symbol><chart length><sending indicator>)
- GetGlobalVariable -> called by the receiving indicator, obviously using the same uniqueue identifier
In the mean time I have updated the code slightly to support historical global variables.
Methods required:
Here a sample indicator to push a value - it is the same indicator as above, but it pushes with Time[1] in order to get the data right on the receiving chart(s):
And finally the code for plotting global variables. Be aware that you need to ensure that the sending chart is calculated before the receiver. In case that does not happen when loading ninja you need to go and recalculate manually using F5.
When I add this user defined method, it compiles fine, but it causes all my custom indicators to stop appearing on the Indicator chart dialog. Can anyone see what might be causing that?
I have not heard of similar problems and I have no idea what this could be.
I guess that Ninja Trader does not provide support when it comes to custom code, but they do provide a framework in which it is possible to program the platform. And when this framework starts behaving strangely in some instances, then I assume that they would be interested in investigating the issue.
I have not heard of similar problems and I have no idea what this could be.
Thanks for the reply, this turned out to be a Reference issue. Somehow a reference to .NET 4.0 snuck in and I had to point it back to 2.0.
I also made some changes that I have not thoroughly tested, but seem to be working for me.
I moved the GV list into its own class to further insulate it conceptually and I changed up the indicator codes. The intended function is now that you would set the sending indicator's "Input series" to whatever you like. So in other words you can choose any indicator available within your platform as an Input. In order to make this work I had to change from Time[0] to Bars.GetTime(CurrentBar).
Anyhow hope it is helpful:
Global Variable User Defined Method
// by Geir Nilsen
// https://nexusfi.com/ninjatrader-programming/18967-global-variables-ninjatrader.html
// use SetGlobalVariable to push the global variable value. Note that this method has to be called at CurrentBar 0
// in order to do some house keeping.
#region Using declarations
using System;
using System.ComponentModel;
using System.Drawing;
using NinjaTrader.Cbi;
using NinjaTrader.Data;
using NinjaTrader.Gui.Chart;
using System.Data;
using System.Collections.Generic;
#endregion
namespace NinjaTrader.Indicator
{
partial class Indicator
{
// the following dictionaries hold real time values. When new time stamp comes in then data is sent to
// table for historic data
public class GV
{
public static Dictionary<string, DateTime> gvTime = new Dictionary<string, DateTime>();
public static Dictionary<string, double> gvPrice = new Dictionary<string, double>();
public static DataTable gvTable = new DataTable(); // stores all historical data
}
public void SetGlobalVariable(string identifier, DateTime timeStamp, double value)
{
// prepare table first time the method is used
if(GV.gvTable.Columns.Count == 0)
{
GV.gvTable.Columns.Add("ID", typeof(string));
GV.gvTable.Columns.Add("Time", typeof(DateTime));
GV.gvTable.Columns.Add("Price", typeof(double));
}
// clean up table in case data is already available (when refreshing chart)
if(CurrentBar == 0)
{
string criteria = "ID = '" + identifier + "'"; // sql string
DataRow[] toBeDeleted = GV.gvTable.Select(criteria); // perform sql
if(toBeDeleted.Length > 0)
foreach (DataRow row in toBeDeleted)
GV.gvTable.Rows.Remove(row);
}
// add new values. For historical data table a row can just be added
if(Historical)
GV.gvTable.Rows.Add(identifier, timeStamp, value);
else
{
// check if new timestamp is <> from what is held in dictionary -> new bar
// ==> add row to gvTable. store current value in dictionary.
DateTime oldTimeStamp;
GV.gvTime.TryGetValue(identifier, out oldTimeStamp);
// if we get a new timeStamp, then real time time stamp has changed. Send to table
if(timeStamp != oldTimeStamp)
{
GV.gvTable.Rows.Add(identifier, timeStamp, value);
}
// cache updated global variable
GV.gvTime[identifier] = timeStamp;
GV.gvPrice[identifier] = value;
}
}
public double GetGlobalVariable(string identifier, DateTime timeStamp)
{
double toBeReturned = -1;
if(!Historical)
// check if timeStamp is cached in dictionary. If yes, then return value
GV.gvPrice.TryGetValue(identifier, out toBeReturned);
else
{
// select specified global variable data from table
string criteria = "ID = '" + identifier + "'"; // sql string
DataRow[] valuesForID = GV.gvTable.Select(criteria); // perform sql
// walk through from oldest until time stamp in table is younger than what we are looking for
foreach(DataRow row in valuesForID)
{
if((DateTime)row[1] <= timeStamp)
toBeReturned = (double)row[2];
else
break;
}
}
return toBeReturned;
}
}
}
GlobalVariableSend Indicator
#region Using declarations
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Xml.Serialization;
using NinjaTrader.Data;
using NinjaTrader.Gui.Chart;
using System.Windows.Forms;
#endregion
namespace NinjaTrader.Indicator
{
/// <summary>
/// GlobalVariableSend
/// https://nexusfi.com/ninjatrader-programming/18967-global-variables-ninjatrader.html
/// </summary>
[Description("GlobalVariableSend")]
public class GlobalVariableSend : Indicator
{
//-> Variables
//<- Variables
//-> Properties
private string identifier;
[Description("Global variable identifier")]
[GridCategory("Parameters")]
public string Identifier
{
get { return identifier; }
set { identifier = value; }
}
[Browsable(false)] // this line prevents the data series from being displayed in the indicator properties dialog, do not remove
[XmlIgnore()] // this line ensures that the indicator can be saved/recovered as part of a chart template, do not remove
public DataSeries GVsend
{
get { return Values[0]; }
}
//<- Properties
protected override void Initialize()
{
Add(new Plot(new Pen(Color.OrangeRed, 2), PlotStyle.Line, "GVsend"));
Overlay = false;
identifier = Instrument.FullName + " " + BarsPeriod.Value + " " + BarsPeriod.BasePeriodType + " " + "[UniqueID?]";
BarsRequired = 0;
}
protected override void OnStartUp()
{
if (identifier == Instrument.FullName + " " + BarsPeriod.Value + " " + BarsPeriod.BasePeriodType + "[UniqueID?]")
{
MessageBox.Show("It appears you are using the default identifier.\n\nThe intention is to replace 'UniqueID?' with something of your choosing. . .", "Global Variable Send Unique Identifier", MessageBoxButtons.OK, MessageBoxIcon.Exclamation,MessageBoxDefaultButton.Button1);
}
}
protected override void OnBarUpdate()
{
GVsend.Set(Input[0]);
SetGlobalVariable(identifier, Bars.GetTime(CurrentBar), GVsend[0]);
}
}
}
GlobalVariableReceive Indicator
#region Using declarations
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Data;
using NinjaTrader.Gui.Chart;
using System.Windows.Forms;
#endregion
// This namespace holds all indicators and is required. Do not change it.
namespace NinjaTrader.Indicator
{
/// <summary>
/// GlobalVariableReceive
/// https://nexusfi.com/ninjatrader-programming/18967-global-variables-ninjatrader.html
/// </summary>
[Description("GlobalVariableReceive")]
public class GlobalVariableReceive : Indicator
{
//-> Variables
private bool isError = false;
//<- Variables
//-> Properties
private string identifier;
[Description("Global variable identifier")]
[GridCategory("Parameters")]
public string Identifier
{
get { return identifier; }
set { identifier = value; }
}
[Browsable(false)] // this line prevents the data series from being displayed in the indicator properties dialog, do not remove
[XmlIgnore()] // this line ensures that the indicator can be saved/recovered as part of a chart template, do not remove
public DataSeries GVreceive
{
get { return Values[0]; }
}
//<- Properties
protected override void Initialize()
{
Add(new Plot(new Pen(Color.OrangeRed, 2), PlotStyle.Line, "GVreceive"));
Overlay = false;
identifier = "???";
}
protected override void OnStartUp()
{
if (identifier == "???")
{
MessageBox.Show("It appears you are using the default identifier.\n\nEnsure this identifier matches that of the sending chart.", "Global Variable Receive Unique Identifier", MessageBoxButtons.OK, MessageBoxIcon.Exclamation,MessageBoxDefaultButton.Button1);
}
}
protected override void OnBarUpdate()
{
if (isError) return;
try
{
GVreceive.Set(GetGlobalVariable(identifier, Time[0]));
}
catch (Exception e)
{
isError = true;
MessageBox.Show(e.ToString(), "Global Variable Receive Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation,MessageBoxDefaultButton.Button1);
}
}
}
}
I would avoid accessing public static data sets directly in order to be more thread-safe. First, I would make the data private
private static Dictionary<string, double> gvHist = new Dictionary<string, double>();
Then, in any methods that read or write the global data I would place locking mechanisms to avoid multi-threading problems. To do this I would first create a lock
private static object lockObject = new object();
Then create a critical section around reading/reading of the Dictionary