#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net.Sockets;
using System.Net;
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.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.DrawingTools;
using System.Globalization;
using System.Reflection;
#endregion
//This namespace holds Indicators in this folder and is required. Do not change it.
namespace NinjaTrader.NinjaScript.Indicators
{
public class EncogIndicator : Indicator
{
public const int TIMEOUT = 20000;
private enum IndicatorState
{
///
/// The indicator has not yet connected.
///
Uninitialized,
///
/// The indicator has connected and is ready for use.
/// Also indicates that the indicator is not blocking
/// and waiting for a bar.
///
Ready,
///
/// The indicator has sent a bar, and is now waiting on
/// a response from Encog.
///
SentBar,
///
/// An error has occured. The indicator is useless at
/// this point and will perform no further action.
///
Error
}
private Socket _sock;
///
/// The source data requested, for example HIGH, LOW, etc, as well as
/// 3rd party indicators.
///
private IList _sourceData;
///
/// Are we in blocking mode? If we are just downloading data, then probably not
/// in blocking mode. If we are going to actually display indicator data then we
/// are in blocking mode. Blocking mode requres a "bar" response from Encog for
/// each bar. Non-blocking mode can simply "stream".
///
private bool _blockingMode = false;
///
/// The state of the indicator.
///
private IndicatorState _indicatorState = IndicatorState.Uninitialized;
///
/// The output data from Encog to diplay as the indicator.
///
private double[] _indicatorData = new double[8];
///
/// Error text that should be displayed if we are in an error state.
///
private string _errorText;
///
/// Do not change this. The "wire protocol" uses USA format numbers.
/// This does not affect display.
///
private readonly CultureInfo _cultureUSA = new CultureInfo("en-US");
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = @"Enter the description for your new custom Indicator here.";
Name = "EncogIndicator";
Calculate = Calculate.OnBarClose;
IsOverlay = false;
DisplayInDataBox = true;
DrawOnPricePanel = true;
DrawHorizontalGridLines = true;
DrawVerticalGridLines = true;
PaintPriceMarkers = true;
ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
//Disable this property if your indicator requires custom values that cumulate with each new market data event.
//See Help Guide for additional information.
IsSuspendedWhileInactive = true;
AddPlot(new Stroke(Brushes.Orange), PlotStyle.Line, "Plot1");
AddPlot(new Stroke(Brushes.Red), PlotStyle.Line, "Plot2");
AddPlot(new Stroke(Brushes.Green), PlotStyle.Line, "Plot3");
AddPlot(new Stroke(Brushes.Orange), PlotStyle.Bar, "Bar1");
AddPlot(new Stroke(Brushes.Red), PlotStyle.Bar, "Bar2");
AddPlot(new Stroke(Brushes.Green), PlotStyle.Bar, "Bar3");
AddPlot(new Stroke(Brushes.Firebrick), PlotStyle.TriangleDown, "IndSell");
AddPlot(new Stroke(Brushes.Green), PlotStyle.TriangleUp, "IndBuy");
AddLine(new Stroke(Brushes.DarkOliveGreen),0, "Osc1");
AddLine(new Stroke(Brushes.Khaki), 0, "Osc2");
AddLine(new Stroke(Brushes.CadetBlue), 0, "Osc3");
Console.WriteLine("Init");
Host = "localhost";
Port = 5128;
}
else if (State == State.Configure)
{
}
else if(State == State.Terminated)
{
if (_sock != null)
{
Log("Encog: OnTermination called, shutting down connection.", LogLevel.Information);
PerformGoodBye();
}
}
}
protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
{
}
protected override void OnMarketDepth(MarketDepthEventArgs marketDepthUpdate)
{
}
protected override void OnBarUpdate()
{
if (CurrentBar < 1) return;
// Try to connect, if we are not yet connected.
// We do this here so that we do not connect everytime the indicator is instanciated.
// Indicators are often instanciated several times before they are actually used.
if (_indicatorState == IndicatorState.Uninitialized)
{
OpenConnection();
}
// Are we in an error state? If so, display and exit.
if (_indicatorState == IndicatorState.Error)
{
DrawError();
return;
}
// If we are actually connected to a socket, then communicate with it.
if (_sock != null)
{
StringBuilder line = new StringBuilder();
long when = Time[0].Second +
Time[0].Minute * 100l +
Time[0].Hour * 10000l +
Time[0].Day * 1000000l +
Time[0].Month * 100000000l +
Time[0].Year * 10000000000l;
line.Append("\"" + PACKET_BAR + "\",");
line.Append(when);
line.Append(",\"");
line.Append(this.Instrument.FullName);
line.Append("\"");
foreach (string name in _sourceData)
{
ISeries source;
int totalBars = 1;
string name2 = name;
ParseArraySize(name, ref totalBars, ref name2);
if (string.Compare(name2, "HIGH", true) == 0)
{
source = High;
}
else if (string.Compare(name2, "LOW", true) == 0)
{
source = Low;
}
else if (string.Compare(name2, "OPEN", true) == 0)
{
source = Open;
}
else if (string.Compare(name2, "CLOSE", true) == 0)
{
source = Close;
}
else if (string.Compare(name2, "VOL", true) == 0)
{
source = Volume;
}
else if (string.Compare(name2, "THIS", true) == 0)
{
source = Values[0];
}
else
{
source = Eval(name2);
if (source == null)
{
return;
}
}
// now copy needed data
var cnt = CurrentBar + 1;
for (int i = 0; i < totalBars; i++)
{
line.Append(",");
if (i >= cnt)
{
line.Append("?");
}
else
{
//line.Append(Convert.ToString(source[i]));
line.Append(Convert.ToString(source[i], _cultureUSA));
}
}
}
Send(line.ToString());
// if we are expecting data back from the socket, then wait for it.
if (_blockingMode)
{
// we are now waiting for a bar
_indicatorState = IndicatorState.SentBar;
while (_indicatorState != IndicatorState.Error && _indicatorState != IndicatorState.Ready)
{
WaitForPacket();
}
// we got a bar message, then display it
if (_indicatorState == IndicatorState.Ready)
{
if (!double.IsNaN(_indicatorData[0]))
{
Plot1[0] = _indicatorData[0];
}
if (!double.IsNaN(_indicatorData[1]))
{
Plot2[0] = _indicatorData[1];
}
if (!double.IsNaN(_indicatorData[2]))
{
Plot3[0] = _indicatorData[2];
}
if (!double.IsNaN(_indicatorData[3]))
{
Bar1[0] = _indicatorData[3];
}
if (!double.IsNaN(_indicatorData[4]))
{
Bar2[0] = _indicatorData[4];
}
if (!double.IsNaN(_indicatorData[5]))
{
Bar3[0] = _indicatorData[5];
}
if (!double.IsNaN(_indicatorData[6]))
{
IndSell[0] = _indicatorData[6];
}
if (!double.IsNaN(_indicatorData[7]))
{
IndBuy[0] = _indicatorData[7];
}
}
}
else
{
var hold = this.DrawOnPricePanel;
DrawOnPricePanel = false;
Draw.TextFixed(this,"general msg", "This indicator only sends data, so there is no display.", TextPosition.Center);
DrawOnPricePanel = hold;
}
}
}
///
/// Send data to the remote socket, if we are connected.
/// If we are not connected, ignore. Data is sent in ASCII.
///
/// The data to send to the remote.
protected void Send(String str)
{
if( _sock!=null )
{
byte[] msg = Encoding.ASCII.GetBytes(str+"\n");
_sock.Send(msg);
}
}
///
/// Open a connection to Encog. Also send the HELLO packet.
///
protected void OpenConnection()
{
try
{
IPAddress[] ips = Dns.GetHostAddresses(Host);
IPAddress targetIP = null;
// first we need to resolve the host name to an IP address
foreach( IPAddress ip in ips )
{
if( ip.AddressFamily == AddressFamily.InterNetwork )
{
targetIP = ip;
break;
}
}
// if successful, then connect to the remote and send the HELLO packet.
if( targetIP!=null )
{
IPEndPoint endPoint = new IPEndPoint(targetIP,Port);
_sock = new Socket(targetIP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_sock.Connect(endPoint);
Send("\""+PACKET_HELLO+"\",\"NinjaTrader 8\",\""+ "EMA" + "\"");
}
else
{
// report failure and exit
PerformError("Failed to resolve: " + Host);
return;
}
// make sure the socket will timeout
_sock.ReceiveTimeout = TIMEOUT;
_indicatorState = IndicatorState.Uninitialized;
while( _indicatorState == IndicatorState.Uninitialized )
{
WaitForPacket();
}
}
catch (Exception ex)
{
_sock = null;
PerformError(ex.Message);
}
}
///
/// Parse a line of CSV. Lines can have quote escaped text.
/// A quote inside a string should be HTML escaped (i.e. ")
/// Only " is supported, and it must be lower case.
///
/// Ideally, I would like to use HttpUtility, however, in .Net
/// 3.5, used by NT, this requires an additonal reference,
/// which is an extra step in the setup process.
///
/// The line to parse.
/// A list of values.
protected IList ParseCSV(string line)
{
var quote = false;
var temp = new StringBuilder();
var result = new List();
foreach(char ch in line)
{
if( ch=='\"' )
{
quote = !quote;
}
else if( ch==',' && !quote )
{
result.Add(temp.ToString().Replace(""","\""));
temp.Length = 0;
quote = false;
}
else
{
temp.Append(ch);
}
}
result.Add(temp.ToString().Replace(""","\""));
return result;
}
///
/// Parse an array size, for example "avg[5]". Return both the
/// number, and the name before the brakets.
///
/// The string to parse.
/// The index parsed.
/// The name parsed.
private void ParseArraySize(string str, ref int index, ref string name)
{
var idx = str.IndexOf('[');
if( idx==-1 )
{
return;
}
var idx2 = str.IndexOf(']',idx);
if( idx2==-1 )
{
return;
}
var s = str.Substring(idx+1,idx2-idx-1);
index = int.Parse(s);
if( index<1 )
{
index = 1;
}
name = str.Substring(0,idx).Trim();
}
///
/// The HELLO packet, sent from the client to the server to provide version information.
///
public const string PACKET_HELLO = "HELLO";
///
/// The GOODBYE packet, sent from the client to the server to end communication.
///
public const string PACKET_GOODBYE = "GOODBYE";
///
/// The SIGNALS packet, sent from the client to the server to specify requested data.
///
public const string PACKET_SIGNALS = "SIGNALS";
///
/// The INIT packet, sent from the server to the client to provide config information.
///
public const string PACKET_INIT = "INIT";
///
/// The BAR packet, sent from the client to the server at the end of each BAR.
///
public const string PACKET_BAR = "BAR";
///
/// The IND packet, sent from the server to the clinet, in response to a BAR.
///
public const string PACKET_IND = "IND";
///
/// The ERROR packet, used to move to an error state.
///
public const String PACKET_ERROR = "ERROR";
///
/// The WARNING packet, used to log a warning.
///
public const String PACKET_WARNING = "WARNING";
///
/// Wait for a packet. Timeout if necessary.
///
public void WaitForPacket() {
var line = new StringBuilder();
var buffer = new byte[1024];
var charBuffer = new char[1024];
var actualSize = 0;
// if there was an error, nothing to wait for.
if( this._indicatorState == IndicatorState.Error )
{
return;
}
// attempt to get a packet
try
{
actualSize = _sock.Receive(buffer);
}
catch(SocketException ex)
{
PerformError("Socket Error: " + ex.Message);
return;
}
// If we got a packet, then process it.
if( actualSize>0 ) {
// packets are in ASCII
ASCIIEncoding ascii = new ASCIIEncoding();
ascii.GetChars(buffer,0,actualSize,charBuffer,0);
// Break up the packets, they are ASCII lines.
for(int i=0;i0 )
{
GotPacket(line.ToString());
line.Length = 0;
}
}
}
}
}
///
/// Handle an error. Display the error to the user, log it,
/// and set the state to error. No further processing on this
/// indicator will occur.
///
/// The error text.
protected void PerformError(string whatError)
{
try
{
_indicatorState = IndicatorState.Error;
_errorText = whatError;
Log("Encog Error: " + whatError ,LogLevel.Error);
Log("Encog: Shutting down socket", LogLevel.Information);
Send("\""+PACKET_GOODBYE+"\"");
_sock.Close();
_sock = null;
}
finally
{
DrawError();
}
}
///
/// Tell the remote that we are about to disconnect.
///
protected void PerformGoodBye()
{
try
{
Log("Encog: Shutting down socket", LogLevel.Information);
Send("\""+PACKET_GOODBYE+"\"");
_sock.Close();
_sock = null;
}
finally
{
_indicatorState = IndicatorState.Uninitialized;
}
}
///
/// Process a packet.
///
/// The packet line.
protected void GotPacket(String line) {
var list = ParseCSV(line);
var temp = new List();
list[0] = list[0].ToUpper();
// a SIGNALS packet tells us what indicators and fund data the remote wants.
if( string.Compare(list[0],PACKET_SIGNALS,true)==0 )
{
_sourceData = new List();
for(int i=1;i
/// Fill the parameters of a 3rd party indicator.
///
/// The name of the indicator( i.e. "MACD(12,26,9)")
/// The parameter list.
/// The objects
protected object[] FillParams(MethodInfo targetMethod, IList paramList)
{
ParameterInfo[] pi = targetMethod.GetParameters();
object[] result = new Object[paramList.Count];
// loop over the parameters and create objects of the correct type.
for(int i=0;i
/// Evaluate a custom indicator. These indicators must be in the form:
///
/// INDICATOR.VALUE[BARS_NEEDED]
///
/// For example,
///
/// MACD(12,26,9).Avg[1]
///
/// This would request the current BAR of the Avg value of the MACD indicator.
/// If the indicator has only one value, then the following format is used.
///
/// EMA(14)[1]
///
/// This would request the current bar of EMA, with a period of 14.
///
/// The indicator string.
///
protected ISeries Eval(string str)
{
IList indicatorParams = new List();
int index2;
try
{
// first extract the indicator name
var index = str.IndexOf('(');
if( index == -1 )
{
index = str.Length;
}
var indicatorName = str.Substring(0,index).Trim();
// now extract params
index = str.IndexOf('(',index);
if( index!=-1 )
{
index2 = str.IndexOf(')');
if( index2!=-1)
{
var s = str.Substring(index+1,index2-index-1).Trim();
indicatorParams = ParseCSV(s);
}
else
{
PerformError("Invalid custom indicator: " + str);
return null;
}
index = index2+1;
}
// find the indicator method
MethodInfo[] methods = GetType().GetMethods();
MethodInfo targetMethod = null;
foreach( MethodInfo m in methods )
{
if( m.Name.CompareTo(indicatorName)==0 && m.GetParameters().Length==indicatorParams.Count)
{
targetMethod = m;
}
}
// determine if there is a property name
string propertyName;
var p = str.IndexOf('.',index);
if( p!=-1 )
{
index = p+1;
index2 = str.IndexOf('[',index);
if( index2==-1 )
{
propertyName = str.Substring(index);
}
else
{
propertyName = str.Substring(index,index2-index);
}
index = index2;
}
else
{
propertyName = "Value";
}
propertyName = propertyName.Trim();
// execute indicator
var rawParams = FillParams(targetMethod,indicatorParams);
object rtn = targetMethod.Invoke(this,rawParams);
if( rtn==null )
{
PerformError("Custom indicator returned null: " + str);
return null;
}
var pi = rtn.GetType().GetProperty(propertyName);
if( pi==null )
{
PerformError("Custom indicator property not found: " + str);
return null;
}
ISeries ds = (ISeries)pi.GetValue(rtn,null);
return ds;
}
catch(Exception ex)
{
if( ex.InnerException==null )
{
PerformError("Eval Error: " + ex.Message);
}
else
{
PerformError("Eval Error: " + ex.InnerException.Message);
}
}
return null;
}
void DrawError()
{
if (this.ChartControl != null && _errorText != null)
{
bool hold = this.DrawOnPricePanel;
this.DrawOnPricePanel = false;
Draw.TextFixed(this, "error msg", "ERROR:" + _errorText, TextPosition.Center);
this.DrawOnPricePanel = hold;
}
}
public string Host{get;set;}
public int Port{get;set;}
[Browsable(false)]
[XmlIgnore]
public Series Plot1 { get { return Values[0]; } }
[Browsable(false)]
[XmlIgnore]
public Series Plot2 { get { return Values[1]; } }
[Browsable(false)]
[XmlIgnore]
public Series Plot3 { get { return Values[2]; } }
[Browsable(false)]
[XmlIgnore]
public Series Bar1 { get { return Values[3]; } }
[Browsable(false)]
[XmlIgnore]
public Series Bar2 { get { return Values[4]; } }
[Browsable(false)]
[XmlIgnore]
public Series Bar3 { get { return Values[5]; } }
[Browsable(false)]
[XmlIgnore]
public Series IndSell { get { return Values[6]; } }
[Browsable(false)]
[XmlIgnore]
public Series IndBuy { get { return Values[7]; } }
}
}
#region NinjaScript generated code. Neither change nor remove.
namespace NinjaTrader.NinjaScript.Indicators
{
public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
{
private EncogIndicator[] cacheEncogIndicator;
public EncogIndicator EncogIndicator()
{
return EncogIndicator(Input);
}
public EncogIndicator EncogIndicator(ISeries input)
{
if (cacheEncogIndicator != null)
for (int idx = 0; idx < cacheEncogIndicator.Length; idx++)
if (cacheEncogIndicator[idx] != null && cacheEncogIndicator[idx].EqualsInput(input))
return cacheEncogIndicator[idx];
return CacheIndicator(new EncogIndicator(), input, ref cacheEncogIndicator);
}
}
}
namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
{
public Indicators.EncogIndicator EncogIndicator()
{
return indicator.EncogIndicator(Input);
}
public Indicators.EncogIndicator EncogIndicator(ISeries input )
{
return indicator.EncogIndicator(input);
}
}
}
namespace NinjaTrader.NinjaScript.Strategies
{
public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
{
public Indicators.EncogIndicator EncogIndicator()
{
return indicator.EncogIndicator(Input);
}
public Indicators.EncogIndicator EncogIndicator(ISeries input )
{
return indicator.EncogIndicator(input);
}
}
}
#endregion