NinjaTrader CycleExplorer Indicator

Our cloud-based cycles engine allows us to use all cycle tools from different platforms. The article describes how to setup the CycleExplorer as indicator in NinjaTrader.

We use the Newtonsoft.Json package to serialize and deserialize all calls to the cycles API. So you need to ensure that you have installed and referenced the package.


using Newtonsoft.Json; // Additional reference to package needed

1) Script & data initialization:

First, we define two new plots and our needed parameters. One plot for the detected cycle and another plot for the derived real price cycle. The displacements settings are used to plot the cycle into the future. So the displacement will be set dependent from the parameter settings for plotForward. This allows us to draw the cycle into the future.

public class WTT_CycleExplorer : Indicator
{		

protected override void OnStateChange()
{
	if (State == State.SetDefaults)
	{
		DrawOnPricePanel = false; // Draw objects now paint on the indicator panel itself  

		// Adds a plot to our NinjaScript Indicator
        AddPlot(new Stroke(Brushes.Red, 1), PlotStyle.Line, "Dominant Cycle");
        AddPlot(new Stroke(Brushes.Orange, 1), PlotStyle.Line, "Price Cycle");

		plotForward				= 0;
        minCycleLength			= 30;
		maxCycleLength			= 290;
		apiKey					= "";
		Name					= "WTT_CycleExplorer";
        Calculate				= Calculate.OnBarClose;
        IsOverlay				= false;
	}

	else if (State == State.DataLoaded)
	{
		if (plotForward>0) Displacement = plotForward;
	} 

}

2) Calling the CycleExplorer API :

We call the CycleExplorer API only at the last bar of the chart. Before reaching out to our cloud engine, we get all close values from the price chart and prepare a data list which will be posted to our cycles engine. We will use a basic HTTPClient call to connect with the cycles API endpoint. Before doing that, we just prepare the POST payload. Now we are ready to do a call via client.PostAsync. Finally, we check if the network call has returned some errors.

protected override void OnBarUpdate()
{

if (Count - 2 == CurrentBar) //Calculate cycle on last finished, closed bar
{
string APIresultContent="";

//Get and prepare the close data array
int seriesCount = Close.Count - 1;
List<double> closeList = new List<double>();
for (int i = 0; i <= seriesCount; i++)
closeList.Add(Close.GetValueAt(i));

//Call the WhenToTrade Cycles API endpoint POST CycleExplorer
using (var client = new HttpClient())
{
//WhenToTrade API URL
client.BaseAddress = new Uri("https://api.marketcycles.online/");

//Load the close dataset for HTTP API POST call
var contents = JsonConvert.SerializeObject(closeList.ToArray());
StringContent POSTcontent = new StringContent(contents, Encoding.UTF8,
"application/json");

//POST API CALL
var response = client.PostAsync("/api/CycleExplorer?minCycleLength="
+minCycleLength
+"&maxCycleLength="+maxCycleLength
+"&plotForward="+plotForward
+"&includeTimeseries=true&api_Key="+apiKey, POSTcontent).Result;

//Check for network errors
if( response.IsSuccessStatusCode)
{
APIresultContent = response.Content.ReadAsStringAsync().Result;
}
else
{
string msg = response.Content.ReadAsStringAsync().Result;
Draw.TextFixed(this, "apierror", "API ERROR:"+msg, TextPosition.TopLeft);
return;
}
}
&#91;/csharp&#93;

&#91;/av_textblock&#93;

&#91;av_hr class='invisible' height='20' shadow='no-shadow' position='center' custom_border='av-border-thin' custom_width='50px' custom_border_color='' custom_margin_top='30px' custom_margin_bottom='30px' icon_select='yes' custom_icon_color='' icon='ue808' font='entypo-fontello' admin_preview_bg='' av_uid='av-kmci6y'&#93;

&#91;av_textblock size='' font_color='' color='' admin_preview_bg='' av_uid='av-ix557e'&#93;
<h4>3) Getting the dominant cycle information from the cloud engine</h4>
If we havent had any network errors, we can now read and parse the returned API data <strong><code class="csharp plain">APIresultContent</code></strong> into internal cycle data <strong><code class="csharp plain">dominantCycle</code> </strong>via the JsonConvert method (see line 2). If this can be done without additional errors, we are ready to access all dominant cycle information via our new dataset <strong><code class="csharp plain">dominantCycle.xyz</code></strong>. To access the individual information we just add the value we want to use. E.g. to get the current dominant cycle length, we just use <strong><code class="csharp plain">dominantCycle.length</code></strong>. We can now access all data returned from the API call (<a href="https://api.marketcycles.online/swagger/ui/index#!/MarketCycles/CycleExplorer_0" target="_blank" rel="noopener noreferrer">see all parameters here</a>). We are now able to do any further calculation within NinjaTrader with these values. For our CycleExplorer we will just plot the current length and phase as text into the indicator window.


// Decode the cycle data from API call
var dominantCycle = JsonConvert.DeserializeObject<dynamic>(APIresultContent);

// Test for return errors from API?
string statusCode=dominantCycle.StatusCode !=null ? dominantCycle.StatusCode : "";
bool error = statusCode.IndexOf("error", StringComparison.OrdinalIgnoreCase) >= 0;
if (error)
{
Draw.TextFixed(this, "apierror", "API ERROR:"+statusCode, TextPosition.TopLeft);
return;
}

//get key cycle information from return object
var cyclelength = dominantCycle.length;
var currentPrice = dominantCycle.currentPrice;
var timeSeries = dominantCycle.TimeSeries;

4. Plotting the indicator:

As we called the API with the parameter includeTimeseries=true, the returned dataset also includes a  dominantCycle.TimeSeries array which gives us all raw DominantCycle values to plot as an indicator. So we dont need to do any cycle calculation on our own – all data needed was already prepared by our cloud engine. We just need to access the TimeSeries array and assign the values for each bar to our plots on the chart. We do this by assigning  Value[...]=timeSeries[...].dominantCycle in a loop for each bar on the chart.

// Fill cycle indicator values from timeSeries API return array (info: called just once for the full chart)
for (int i = 0; i <= CurrentBar; i++) { try { Value[CurrentBar-i]=timeSeries[i+plotForward].dominantCycle;if (timeSeries[i+plotForward].cycleHighlighter!=null) Values[1][CurrentBar-i]=timeSeries[i+plotForward].cycleHighlighter; } catch (Exception ex) { //should never happen, but in case lets ignore them and only print to log Print("Error: "+i+" - "+ex.Message); } } [/csharp][/av_textblock][av_hr class='invisible' height='20' shadow='no-shadow' position='center' custom_border='av-border-thin' custom_width='50px' custom_border_color='' custom_margin_top='30px' custom_margin_bottom='30px' icon_select='yes' custom_icon_color='' icon='ue808' font='entypo-fontello' admin_preview_bg='' av_uid='av-aj034q'][av_textblock size='' font_color='' color='' admin_preview_bg='' av_uid='av-8kbi6y']

Wrapping it up:

The example shows how simple it is to integrate the cycles cloud-based engine into charting platforms and indicators. The cloud API is based on our core code base which we use for years in a trading environment and which powers our standalone platform. All examples described in our books can be realized. Now, we are able to do the cycle analysis with any preferred front-end application. Said that, there will be no need for the WTT standalone application or additional data-feeds. We here at WTT will now concentrate on a stable service delivery for a cycle-analysis backbone without forcing anyone into closed standalone apps.

Download the full source code here: