region Using declarations
using System;
using System.Collections.Generic;
using System.Windows.Media;
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;
#endregion
namespace NinjaTrader.NinjaScript.Indicators
{
public class MachineLearningSignalFilter : Indicator
{
private const int ACTIVATE = 1;
private const int DEACTIVATE = -1;
private const int STASIS = 0;
private int signalBeacon = STASIS;
private int timeTracker = 0;
region Properties
[NinjaScriptProperty]
[Display(Name = "Activate Signal Calculations", GroupName = "Signal Settings")]
public bool SignalEaster { get; set; }
[NinjaScriptProperty]
[Display(Name = "Use Data for Signal Generation", GroupName = "Signal Settings")]
public bool UseDataForSignal { get; set; }
[NinjaScriptProperty]
[Range(1, int.MaxValue)]
[Display(Name = "Temporal Stasis Period", Description = "Minimum 1", GroupName = "Trade Settings")]
public int StasisPeriod { get; set; }
[NinjaScriptProperty]
[Range(2, int.MaxValue)]
[Display(Name = "Chrono Window", Description = "Minimum 2", GroupName = "Temporal Settings")]
public int RetroScan { get; set; }
[NinjaScriptProperty]
[Range(2, 240)]
[Display(Name = "Temporal Normalization", GroupName = "Temporal Settings")]
public int ChronoNorm { get; set; }
[NinjaScriptProperty]
[Range(0.0001, 0.01)]
[Display(Name = "Neural Pulse Rate", GroupName = "Neural Network")]
public double NanoPulse { get; set; }
[NinjaScriptProperty]
[Range(50, 20000)]
[Display(Name = "Cycle Iterations", GroupName = "Neural Network")]
public int ChronoCycles { get; set; }
#endregion
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Machine Learning Signal Filter";
Name = "MLSignalFilter";
SignalEaster = true;
UseDataForSignal = true;
StasisPeriod = 5;
RetroScan = 5;
ChronoNorm = 100;
NanoPulse = 0.001;
ChronoCycles = 1500;
Calculate = Calculate.OnBarClose;
IsOverlay = true;
}
else if (State == State.Configure)
{
AddPlot(new Stroke(Brushes.Red, 2), PlotStyle.Cross, "StopBuy");
AddPlot(new Stroke(Brushes.Blue, 2), PlotStyle.Cross, "StopSell");
}
}
private double BioCurve(double zeta)
{
return 1.0 / (1.0 + Math.Exp(-zeta));
}
private double VectorDot(List<double> vectorA, double vectorB, int dimension)
{
double sum = 0;
for (int i = 0; i < dimension; i++)
{
sum += vectorA[i] * vectorB;
}
return sum;
}
private Tuple<double, double> CyberneticRegression(List<double> inputX, List<double> inputY, int dimension, double learningRate, int iterations)
{
double weight = 0.0;
double entropy = 0.0;
for (int cycle = 1; cycle <= iterations; cycle++)
{
double hypothesis = BioCurve(VectorDot(inputX, 0.0, dimension));
entropy = -1.0 / dimension * VectorDot(inputX, hypothesis - inputY[0], dimension);
double gradient = 1.0 / dimension * VectorDot(inputX, hypothesis - inputY[0], dimension);
weight -= learningRate * gradient;
}
return new Tuple<double, double>(entropy, BioCurve(VectorDot(inputX, weight, dimension)));
}
private double StellarNormalize(double dataStream, int dimension, double minimum, double maximum)
{
double highestValue = MAX(dimension)[0];
double lowestValue = MIN(dimension)[0];
return (maximum - minimum) * (dataStream - lowestValue) / (highestValue - lowestValue) + minimum;
}
protected override void OnBarUpdate()
{
if (CurrentBar < RetroScan) return;
List<double> baseInput = new List<double>();
List<double> syntheticInput = new List<double>();
// Populate input data
for (int i = 0; i < RetroScan; i++)
{
if (SignalEaster)
{
baseInput.Add(Time[i].Ticks);
syntheticInput.Add(Close[i]);
}
else
{
baseInput.Add(Close[i]);
syntheticInput.Add(Math.Log(Math.Abs(Math.Pow(Clos e[i], 2) - 1) + 0.5));
}
}
// Calculate regression
var result = CyberneticRegression(baseInput, syntheticInput, RetroScan, NanoPulse, ChronoCycles);
double entropy = result.Item1;
double forecast = result.Item2;
double normalizedEntropy = StellarNormalize(entropy, ChronoNorm, MIN(ChronoNorm)[0], MAX(ChronoNorm)[0]);
double normalizedForecast = StellarNormalize(forecast, ChronoNorm, MIN(ChronoNorm)[0], MAX(ChronoNorm)[0]);
// Signal Processing
bool filter = true;
int previousSignalBeacon = signalBeacon;
if (UseDataForSignal)
{
signalBeacon = Close[0] < normalizedEntropy && filter ? DEACTIVATE :
Close[0] > normalizedEntropy && filter ? ACTIVATE : signalBeacon;
}
else
{
signalBeacon = CrossBelow(normalizedEntropy, normalizedForecast) && filter ? DEACTIVATE :
CrossAbove(normalizedEntropy, normalizedForecast) && filter ? ACTIVATE : signalBeacon;
}
bool changed = previousSignalBeacon != signalBeacon;
timeTracker = changed ? 0 : timeTracker + 1;
// Trade Conditions
bool startLongTrade = changed && signalBeacon == ACTIVATE;
bool startShortTrade = changed && signalBeacon == DEACTIVATE;
bool endLongTrade = (signalBeacon == ACTIVATE && timeTracker == StasisPeriod && !changed) ||
(changed && signalBeacon == DEACTIVATE);
bool endShortTrade = (signalBeacon == DEACTIVATE && timeTracker == StasisPeriod && !changed) ||
(changed && signalBeacon == ACTIVATE);
// Plot signals
if (startLongTrade)
Draw.TriangleUp(this, "Buy" + CurrentBar, true, 0, Low[0], Brushes.Blue);
if (startShortTrade)
Draw.TriangleDown(this, "Sell" + CurrentBar, true, 0, High[0], Brushes.Red);
if (endLongTrade)
Values[0][0] = High[0]; // StopBuy
if (endShortTrade)
Values[1][0] = Low[0]; // StopSell
}
}
}

Comment