Crashed in OnTick with IOException
29 Oct 2018, 02:15
I am using FIX API Sample program to implement FIX engine in cTrader.
I am getting this crash quite often with following message:
29/10/2018 00:07:56.238 | Crashed in OnTick with IOException: Unable to read data from the transport connection: An established connection was aborted by the software in your host machine.
I am using HeartBeat every 10 second. Still I am not able to get rid of this error. This makes my FIX engine program unsuable.
Could you please give me some idea why this is happening and any work-around you have to fix this issue.
Thanks,
Replies
netread2004
05 Nov 2018, 02:34
RE:
Panagiotis Charalampous said:
Hi netread2004,
Can you share the cBot code so that we can reproduce the issue?
Best Regards,
Panagiotis
Here is the error messages I am getting now when I run this code.
05/11/2018 00:27:49.254 | cBot "FIXengine_iCMarkets_test" was started successfully for GBPUSD, m1.
05/11/2018 00:28:09.613 | Crashed in OnTimer with IOException: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.
05/11/2018 00:28:09.613 | Crashed in OnStop with IOException: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.
05/11/2018 00:28:09.629 | cBot "FIXengine_iCMarkets_test" was stopped for GBPUSD, m1.
Please change FIX credential appropriatly to match with your server while testing it in your environment.
//using FIX_API_Library;
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
namespace cAlgo
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
public partial class frmFIXAPISample : Robot
{
[Parameter("Source")]
public DataSeries Source { get; set; }
[Parameter("Stop Loss (pips)", DefaultValue = 1.3, MinValue = 1)]
public double StopLossInPips { get; set; }
[Parameter("Take Profit (pips)", DefaultValue = 100.0, MinValue = 1)]
public double TakeProfitInPips { get; set; }
[Parameter("Quantity (Lots)", DefaultValue = 0.01, MinValue = 0.01, Step = 0.01)]
public double Quantity { get; set; }
[Parameter("Bollinger Bands Deviations", DefaultValue = 0.5)]
public double Deviations { get; set; }
[Parameter("Bollinger Bands Periods", DefaultValue = 2)]
public int Periods { get; set; }
[Parameter("Bollinger Bands MA Type")]
public MovingAverageType MAType { get; set; }
[Parameter("BarHeight Thresh", DefaultValue = 16)]
public int BarHeightThresh { get; set; }
[Parameter("pipUpDn Thresh", DefaultValue = 180)]
public double pipUpDnThresh { get; set; }
[Parameter("Tralling Stop (pips)", DefaultValue = 1.3)]
public double StopLoss { get; set; }
[Parameter("Inital Stop Loss (pips)", DefaultValue = 1.3)]
public double init_StopLoss { get; set; }
[Parameter("RiskPC", DefaultValue = 10.0)]
public double risk { get; set; }
[Parameter("AllowedSpread", DefaultValue = 2.0)]
public double allowedSpread { get; set; }
[Parameter("toDisp", DefaultValue = true)]
public bool toDisp { get; set; }
[Parameter("ordNumDisp", DefaultValue = false)]
public bool ordNumDisp { get; set; }
[Parameter("printWholeFixMsg", DefaultValue = false)]
public bool printWholeFixMsg { get; set; }
[Parameter("symbolID", DefaultValue = 1)]
public int symbolID { get; set; }
public const int EURUSD = 1;
public const int GBPUSD = 2;
string txtMessageSend_Text = "";
string txtMessageReceived_Text = "";
bool deCryptEn = true;
bool deCryptNoPrint = false;
bool stopLossPlaced = false;
bool lookForPrice = false;
bool filledDueToSLHit = false;
string initPriceForSL;
string randomNum = "";
string[] filledPosiitonID =
{
""
};
string filPosID;
string avePricePosition;
string clOrdNumGlob;
bool origStopOrdFilled = false;
int totalPosGlob = 0;
//Ctrader
private int _pricePort = 5211;
private int _tradePort = 5212;
private string _host = "h1.p.ctrader.cn";
private string _username = "3388140";
private string _password = "qwerty12";
private string _senderCompID = "icmarkets.3388140";
private string _targetCompID = "cServer";
private string _senderSubID = "TRADE";
private int _messageSequenceNumber = 1;
private int _testRequestID = 1;
private TcpClient _priceClient;
private SslStream _priceStreamSSL;
private TcpClient _tradeClient;
private SslStream _tradeStreamSSL;
private MessageConstructor _messageConstructor;
private double High;
private double Low;
double spread;
string globIDForCheckOrder = "";
string globIdForSLCheckStat = "";
protected override void OnStart()
{
btnLogonT_Click();
Timer.TimerTick += OnTimerTick;
Timer.Start(10);
Positions.Closed += PositionsOnClosed;
}
protected override void OnStop()
{
btnLogoutT_Click();
}
private void OnTimerTick()
{
btnHeartbeatT_Click();
}
private void PositionsOnClosed(PositionClosedEventArgs args)
{
var position = args.Position;
Print("Position closed with {0} profit", position.GrossProfit);
filledDueToSLHit = false;
stopLossPlaced = false;
clOrdNumGlob = "";
origStopOrdFilled = false;
initPriceForSL = "";
Print("Order Closed Successfully!!!! -------------");
filPosID = "";
globIDForCheckOrder = "";
globIdForSLCheckStat = "";
}
protected override void OnTick()
{
High = MarketSeries.High.Last(0);
Low = MarketSeries.Low.Last(0);
int totalPositions = Positions.Count;
var totalPendOrders = PendingOrders.Count;
var barHeight = High - Low;
totalPosGlob = totalPositions;
if (origStopOrdFilled && totalPositions == 0)
{
}
spread = (Symbol.Ask - Symbol.Bid) / Symbol.PipSize;
}
private string getRandomNumber(string coment)
{
Random random = new Random();
int randomNumber = random.Next();
randomNum = randomNumber.ToString();
if (ordNumDisp)
Print(coment + " ClOrdeID : ", randomNum);
return (randomNum);
}
private void printSendReceive()
{
Print("SendText : ", txtMessageSend_Text.Replace("\u0001", " "));
Print("ReceivedText : ", txtMessageReceived_Text.Replace("\u0001", " "));
}
private void deCryptMessage(string sentMssg, string recvdMessg)
{
}
//End of my routines
public frmFIXAPISample()
{
//InitializeComponent();
_priceClient = new TcpClient(_host, _pricePort);
_priceStreamSSL = new SslStream(_priceClient.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
_priceStreamSSL.AuthenticateAsClient(_host);
_tradeClient = new TcpClient(_host, _tradePort);
_tradeStreamSSL = new SslStream(_tradeClient.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
_tradeStreamSSL.AuthenticateAsClient(_host);
_messageConstructor = new MessageConstructor(_host, _username, _password, _senderCompID, _senderSubID, _targetCompID);
}
private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
return false;
}
private void ClearText()
{
txtMessageSend_Text = "";
txtMessageReceived_Text = "";
}
private string SendPriceMessage(string message, bool readResponse = true)
{
return SendMessage(message, _priceStreamSSL, readResponse);
}
private string SendTradeMessage(string message, bool readResponse = true)
{
return SendMessage(message, _tradeStreamSSL, readResponse);
}
private string SendMessage(string message, SslStream stream, bool readResponse = true)
{
var byteArray = Encoding.ASCII.GetBytes(message);
System.Net.ServicePointManager.Expect100Continue = false;
//I inserted Above line to see if it fixes crash issue
stream.Write(byteArray, 0, byteArray.Length);
var buffer = new byte[1024];
if (readResponse)
{
Thread.Sleep(100);
stream.Read(buffer, 0, 1024);
}
_messageSequenceNumber++;
var returnMessage = Encoding.ASCII.GetString(buffer);
return returnMessage;
}
private void btnLogon_Click()
{
ClearText();
var message = _messageConstructor.LogonMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber, 30, false);
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message);
if (printWholeFixMsg)
printSendReceive();
}
private void btnTestRequest_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.TestRequestMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber, _testRequestID);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message);
}
private void btnLogout_Click()
{
ClearText();
var message = _messageConstructor.LogoutMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber);
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message);
_messageSequenceNumber = 1;
if (printWholeFixMsg)
printSendReceive();
}
private void btnMarketDataRequest_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.MarketDataRequestMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber, "EURUSD:WDqsoT", 1, 0, 0, 1, 1);
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message);
}
private void btnHeartbeat_Click()
{
ClearText();
var message = _messageConstructor.HeartbeatMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber);
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message, false);
if (printWholeFixMsg)
printSendReceive();
}
private void btnResendRequest_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.ResendMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber, _messageSequenceNumber - 1);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message);
}
private void btnReject_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.RejectMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber, 0);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message);
}
private void btnSequenceReset_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.SequenceResetMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber, 0);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message);
}
private void btnNewOrderSingle_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.NewOrderSingleMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, "1408471", 1, 1, DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss"), 1000, 1, "1");
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
}
private void btnOrderStatusRequest_Click(string orderId, int symbl, int ordtyp)
{
ClearText();
var message = _messageConstructor.OrderStatusRequest(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, orderId, symbl, ordtyp);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
if (printWholeFixMsg)
printSendReceive();
if (deCryptEn)
deCryptMessage(txtMessageSend_Text, txtMessageReceived_Text);
}
private string Timestamp()
{
return (DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds.ToString();
}
private void btnRequestForPositions_Click(string posID)
{
ClearText();
var message = _messageConstructor.RequestForPositions(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, posID);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
if (printWholeFixMsg)
printSendReceive();
if (deCryptEn)
deCryptMessage(txtMessageSend_Text, txtMessageReceived_Text);
}
private void btnLogonT_Click()
{
ClearText();
var message = _messageConstructor.LogonMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, 30, false);
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
if (printWholeFixMsg)
printSendReceive();
if (deCryptEn)
deCryptMessage(txtMessageSend_Text, txtMessageReceived_Text);
}
private void btnHeartbeatT_Click()
{
ClearText();
var message = _messageConstructor.HeartbeatMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber);
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message, false);
if (printWholeFixMsg)
printSendReceive();
}
private void btnTestRequestT_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.TestRequestMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, _testRequestID);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
}
private void btnLogoutT_Click()
{
ClearText();
var message = _messageConstructor.LogoutMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber);
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
_messageSequenceNumber = 1;
if (printWholeFixMsg)
printSendReceive();
if (deCryptEn)
deCryptMessage(txtMessageSend_Text, txtMessageReceived_Text);
}
private void btnResendRequestT_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.ResendMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, _messageSequenceNumber - 1);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
}
private void btnSpotMarketData_Click(object sender, EventArgs e)
{
ClearText();
var message = _messageConstructor.MarketDataRequestMessage(MessageConstructor.SessionQualifier.QUOTE, _messageSequenceNumber, "EURUSD:WDqsoT", 1, 1, 0, 1, 1);
txtMessageSend_Text = message;
txtMessageReceived_Text = SendPriceMessage(message);
}
private void btnStopOrder_Click(int symb, int vol, double stpPrice, string ClOrdID, int side, string posiID)
{
ClearText();
var message = _messageConstructor.NewOrderSingleMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, ClOrdID, symb, side, DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss"), vol, 3, "3", 0,
(decimal)stpPrice, "", posiID);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
if (printWholeFixMsg)
printSendReceive();
if (deCryptEn)
deCryptMessage(txtMessageSend_Text, txtMessageReceived_Text);
}
private void btnCancelOrder_Click(string ClOrdID, string oldOrdID)
{
ClearText();
var message = _messageConstructor.CancelOrderSingleMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, ClOrdID, oldOrdID, 1, DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss"), 3, "3", 0);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
if (printWholeFixMsg)
printSendReceive();
if (deCryptEn)
deCryptMessage(txtMessageSend_Text, txtMessageReceived_Text);
}
private void btnLimitOrder_Click()
{
ClearText();
var message = _messageConstructor.NewOrderSingleMessage(MessageConstructor.SessionQualifier.TRADE, _messageSequenceNumber, "10", 1, 1, DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss"), 1000, 2, "3", (decimal)1.08);
_testRequestID++;
txtMessageSend_Text = message;
txtMessageReceived_Text = SendTradeMessage(message);
if (printWholeFixMsg)
printSendReceive();
}
}
public class MessageConstructor
{
public enum SessionMessageType
{
Logon,
Logout,
Heartbeat,
TestRequest,
Resend,
Reject,
SequenceReset
}
public enum ApplicationMessageType
{
MarketDataRequest,
MarketDataIncrementalRefresh,
NewOrderSingle,
CancelOrderSingle,
OrderStatusRequest,
ExecutionReport,
BusinessMessageReject,
RequestForPosition,
PositionReport
}
public enum SessionQualifier
{
QUOTE,
TRADE
}
private string _host;
private string _username;
private string _password;
private string _senderCompID;
private string _senderSubID;
private string _targetCompID;
public MessageConstructor(string host, string username, string password, string senderCompID, string senderSubID, string targetCompID)
{
_host = host;
_username = username;
_password = password;
_senderCompID = senderCompID;
_senderSubID = senderSubID;
_targetCompID = targetCompID;
}
/// <summary>
/// Logons the message.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="heartBeatSeconds">The heart beat seconds.</param>
/// <param name="resetSeqNum">All sides of FIX session should have sequence numbers reset. Valid value
///is "Y" = Yes(reset)..</param>
/// <returns></returns>
public string LogonMessage(SessionQualifier qualifier, int messageSequenceNumber, int heartBeatSeconds, bool resetSeqNum)
{
var body = new StringBuilder();
//Defines a message encryption scheme.Currently, only transportlevel security
//is supported.Valid value is "0"(zero) = NONE_OTHER (encryption is not used).
body.Append("98=0|");
//Heartbeat interval in seconds.
//Value is set in the 'config.properties' file (client side) as 'SERVER.POLLING.INTERVAL'.
//30 seconds is default interval value. If HeartBtInt is set to 0, no heart beat message
//is required.
body.Append("108=" + heartBeatSeconds + "|");
// All sides of FIX session should have
//sequence numbers reset. Valid value
//is "Y" = Yes(reset).
if (resetSeqNum)
body.Append("141=Y|");
//The numeric User ID. User is linked to SenderCompID (#49) value (the
//user’s organization).
body.Append("553=" + _username + "|");
//USer Password
body.Append("554=" + _password + "|");
var header = ConstructHeader(qualifier, SessionMessageCode(SessionMessageType.Logon), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
public string HeartbeatMessage(SessionQualifier qualifier, int messageSequenceNumber)
{
var header = ConstructHeader(qualifier, SessionMessageCode(SessionMessageType.Heartbeat), messageSequenceNumber, string.Empty);
var trailer = ConstructTrailer(header);
var headerAndMessageAndTrailer = header + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
public string TestRequestMessage(SessionQualifier qualifier, int messageSequenceNumber, int testRequestID)
{
var body = new StringBuilder();
//Heartbeat message ID. TestReqID should be incremental.
body.Append("112=" + testRequestID + "|");
var header = ConstructHeader(qualifier, SessionMessageCode(SessionMessageType.TestRequest), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
public string LogoutMessage(SessionQualifier qualifier, int messageSequenceNumber)
{
var header = ConstructHeader(qualifier, SessionMessageCode(SessionMessageType.Logout), messageSequenceNumber, string.Empty);
var trailer = ConstructTrailer(header);
var headerAndMessageAndTrailer = header + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Resends the message.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">Message sequence number of last record in range to be resent.</param>
/// <param name="endSequenceNo">The end sequence no.</param>
/// <returns></returns>
public string ResendMessage(SessionQualifier qualifier, int messageSequenceNumber, int endSequenceNo)
{
var body = new StringBuilder();
//Message sequence number of last record in range to be resent.
body.Append("16=" + endSequenceNo + "|");
var header = ConstructHeader(qualifier, SessionMessageCode(SessionMessageType.Resend), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
public string RejectMessage(SessionQualifier qualifier, int messageSequenceNumber, int rejectSequenceNumber)
{
var body = new StringBuilder();
//Referenced message sequence number.
body.Append("45=" + rejectSequenceNumber + "|");
var header = ConstructHeader(qualifier, SessionMessageCode(SessionMessageType.Reject), messageSequenceNumber, string.Empty);
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
public string SequenceResetMessage(SessionQualifier qualifier, int messageSequenceNumber, int rejectSequenceNumber)
{
var body = new StringBuilder();
//New Sequence Number
body.Append("36=" + rejectSequenceNumber + "|");
var header = ConstructHeader(qualifier, SessionMessageCode(SessionMessageType.SequenceReset), messageSequenceNumber, string.Empty);
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Constructs a message for requesting market data.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="marketDataRequestID">Unique quote request id. New ID for a new subscription, same one as previously used for subscription removal.</param>
/// <param name="subscriptionRequestType">1 = Snapshot plus updates (subscribe) 2 = Disable previous snapshot plus update request (unsubscribe).</param>
/// <param name="marketDepth">Full book will be provided, 0 = Depth subscription, 1 = Spot subscription.</param>
/// <param name="marketDataEntryType">Always set to 2 (both bid and ask will be sent).</param>
/// <param name="noRelatedSymbol">Number of symbols requested.</param>
/// <param name="symbol">Instrument identificators are provided by Spotware.</param>
/// <returns></returns>
public string MarketDataRequestMessage(SessionQualifier qualifier, int messageSequenceNumber, string marketDataRequestID, int subscriptionRequestType, int marketDepth, int marketDataEntryType, int noRelatedSymbol, long symbol = 0)
{
var body = new StringBuilder();
//Unique quote request id. New ID for a new subscription, same one as previously used for subscription removal.
body.Append("262=" + marketDataRequestID + "|");
//1 = Snapshot plus updates (subscribe) 2 = Disable previous snapshot plus update request (unsubscribe)
body.Append("263=" + subscriptionRequestType + "|");
//Full book will be provided, 0 = Depth subscription, 1 = Spot subscription
body.Append("264=" + marketDepth + "|");
//Only Incremental refresh is supported.
body.Append("265=1|");
//Always set to 2 (both bid and ask will be sent).
body.Append("267=2|");
//Always set to 2 (both bid and ask will be sent).
body.Append("269=0|269=1|");
//Number of symbols requested.
body.Append("146=" + noRelatedSymbol + "|");
//Instrument identificators are provided by Spotware.
body.Append("55=" + symbol + "|");
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.MarketDataRequest), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Constructs a message for requesting market data snapshot.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="symbol">Instrument identificators are provided by Spotware.</param>
/// <param name="noMarketDataEntries">Number of entries following.</param>
/// <param name="entryType">Valid values are: 0 = BID, 1 = OFFER.</param>
/// <param name="entryPrice">Price of Market Data Entry.</param>
/// <param name="marketDataRequestID">Unique quote request id. New ID for a new subscription, same one as previously used for subscription removal.</param>
/// <returns></returns>
public string MarketDataSnapshotMessage(SessionQualifier qualifier, int messageSequenceNumber, long symbol, string noMarketDataEntries, int entryType, decimal entryPrice, string marketDataRequestID = "")
{
var body = new StringBuilder();
//Unique quote request id. New ID for a new subscription, same one as previously used for subscription removal.
body.Append("262=" + marketDataRequestID + "|");
//Instrument identificators are provided by Spotware.
body.Append("55=" + symbol + "|");
//Number of entries following.
body.Append("268=" + noMarketDataEntries + "|");
//Valid values are: 0 = BID, 1 = OFFER.
body.Append("269=" + entryType + "|");
//Price of Market Data Entry.
body.Append("270=" + entryPrice + "|");
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.MarketDataRequest), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Markets the data incremental refresh message.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="marketDataRequestID">Unique quote request id. New ID for a new subscription, same one as previously used for subscription removal.</param>
/// <param name="noMarketDataEntries">Number of entries following. This repeating group contains a list of all types of Market Data Entries the requester wants to receive.</param>
/// <param name="marketDataUpdateAction">Type of Market Data update action. Valid values: 0 = NEW, 2 = DELETE.</param>
/// <param name="marketDataEntryType">Valid values are: 0 = BID, 1 = OFFER.</param>
/// <param name="marketDataEntryID">ID of Market Data Entry.</param>
/// <param name="noRelatedSymbol">The no related symbol.</param>
/// <param name="marketDataEntryPrice">Conditionally required when MDUpdateAction <279> = New(0).</param>
/// <param name="marketDataEntrySize">Conditionally required when MDUpdateAction <279> = New(0).</param>
/// <param name="symbol">/Instrument identificators are provided by Spotware.</param>
/// <returns></returns>
public string MarketDataIncrementalRefreshMessage(SessionQualifier qualifier, int messageSequenceNumber, string marketDataRequestID, int noMarketDataEntries, int marketDataUpdateAction, int marketDataEntryType, string marketDataEntryID, int noRelatedSymbol, decimal marketDataEntryPrice = 0, double marketDataEntrySize = 0,
long symbol = 0)
{
var body = new StringBuilder();
//Unique quote request id. New ID for a new subscription, same one as previously used for subscription removal.
body.Append("262=" + marketDataRequestID + "|");
//Number of entries following. This repeating group contains a list of all types of Market Data Entries the requester wants to receive
body.Append("268=" + noMarketDataEntries + "|");
//Type of Market Data update action. Valid values: 0 = NEW, 2 = DELETE
body.Append("279=" + marketDataUpdateAction + "|");
//Valid values are: 0 = BID, 1 = OFFER
body.Append("269=" + marketDataEntryType + "|");
//ID of Market Data Entry.
body.Append("278=" + marketDataEntryID + "|");
//Instrument identificators are provided by Spotware
body.Append("55=" + symbol + "|");
if (marketDataEntryPrice > 0)
{
//Conditionally required when MDUpdateAction <279> = New(0)
body.Append("270=" + marketDataEntryPrice + "|");
}
if (marketDataEntrySize > 0)
{
//Conditionally required when MDUpdateAction <279> = New(0)
body.Append("271=" + marketDataEntrySize + "|");
}
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.MarketDataIncrementalRefresh), messageSequenceNumber, string.Empty);
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
//return headerAndMessageAndTrailer;
}
/// <summary>
/// News the order single message.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="orderID">Unique identifier for the order, allocated by the client.</param>
/// <param name="symbol">The symbol.</param>
/// <param name="side">1= Buy, 2 = Sell.</param>
/// <param name="transactTime">Client generated request time.</param>
/// <param name="orderQuantity">The fixed currency amount.</param>
/// <param name="orderType">1 = Market, the Order will be processed by 'Immediate Or Cancel'scheme(see TimeInForce(59): IOC);
/// 2 = Limit, the Order will be processed by 'Good Till Cancel' scheme(see TimeInForce(59): GTC).</param>
/// <param name="timeInForce">1 = Good Till Cancel (GTC), it will be active only for Limit Orders (see OrdType(40)) ;
/// 3 = Immediate Or Cancel (IOC), it will be active only for Market Orders(see OrdType(40));
/// 6 = Good Till Date(GTD), it will be active only if ExpireTime is defined (see ExpireTime(126)).
/// GTD has a high priority, so if ExpireTime is defined, GTD will be used for the Order processing.</param>
/// <param name="price">The worst client price that the client will accept.
/// Required when OrdType = 2, in which case the order will notfill unless this price can be met.</param>
/// /// <param name="stopPrice">Reserved for future use</param>
/// <param name="expireTime"> Expire Time in YYYYMMDDHH:MM:SS format.If is assigned then the Order will be processed by 'Good Till Date' scheme
/// (see TimeInForce: GTD).</param>
/// <param name="positionID">Position ID, where this order should be placed. If not set, new position will be created, it’s id will be returned in ExecutionReport(8) message.</param>
/// <returns></returns>
public string NewOrderSingleMessage(SessionQualifier qualifier, int messageSequenceNumber, string orderID, long symbol, int side, string transactTime, int orderQuantity, int orderType, string timeInForce, decimal price = 0,
decimal stopPrice = 0, string expireTime = "", string positionID = "")
{
var body = new StringBuilder();
//Unique identifier for the order, allocated by the client.
body.Append("11=" + orderID + "|");
//Instrument identificators are provided by Spotware.
body.Append("55=" + symbol + "|");
//1= Buy, 2 = Sell
body.Append("54=" + side + "|");
// Client generated request time.
body.Append("60=" + transactTime + "|");
//The fixed currency amount.
body.Append("38=" + orderQuantity + "|");
//1 = Market, the Order will be processed by 'Immediate Or Cancel'scheme(see TimeInForce(59): IOC);
//2 = Limit, the Order will be processed by 'Good Till Cancel' scheme(see TimeInForce(59): GTC).
body.Append("40=" + orderType + "|");
if (price != 0)
{
//Reserved for future use.
body.Append("44=" + price + "|");
}
if (stopPrice != 0)
{
//The worst client price that the client will accept.
//Required when OrdType = 2, in which case the order will notfill unless this price can be met.
body.Append("99=" + stopPrice + "|");
}
// 1 = Good Till Cancel (GTC), it will be active only for Limit Orders (see OrdType(40)) ;
// 3 = Immediate Or Cancel (IOC), it will be active only for Market Orders(see OrdType(40));
// 6 = Good Till Date(GTD), it will be active only if ExpireTime is defined (see ExpireTime(126)).
// GTD has a high priority, so if ExpireTime is defined, GTD will be used for the Order processing.
body.Append("59=" + timeInForce + "|");
if (expireTime != string.Empty)
{
// Expire Time in YYYYMMDDHH:MM:SS format.If is assigned then the Order will be processed by 'Good Till Date' scheme
// (see TimeInForce: GTD).
body.Append("126=" + expireTime + "|");
}
if (positionID != string.Empty)
{
// Position ID, where this order should be placed. If not set, new position will be created, it’s id will be returned in ExecutionReport(8) message.
body.Append("721=" + positionID + "|");
}
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.NewOrderSingle), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
public string CancelOrderSingleMessage(SessionQualifier qualifier, int messageSequenceNumber, string orderID, string oldOrdID, int side, string transactTime, int orderType, string timeInForce, decimal stopPrice = 0, string expireTime = "",
string positionID = "")
{
var body = new StringBuilder();
//OldID identifier for the order, allocated by the client.
body.Append("41=" + oldOrdID + "|");
//Unique identifier for the order, allocated by the client.
body.Append("11=" + orderID + "|");
/* following is NOT needed for cancel orderModify Order.
body.Append("55=" + symbol + "|");
body.Append("54=" + side + "|");
body.Append("60=" + transactTime + "|");
body.Append("38=" + orderQuantity + "|");
body.Append("40=" + orderType + "|");
body.Append("99=" + price + "|");
//Instrument identificators are provided by Spotware.
body.Append("55=" + symbol + "|");
//1= Buy, 2 = Sell
body.Append("54=" + side + "|");
// Client generated request time.
body.Append("60=" + transactTime + "|");
//The fixed currency amount.
body.Append("38=" + orderQuantity + "|");
//1 = Market, the Order will be processed by 'Immediate Or Cancel'scheme(see TimeInForce(59): IOC);
//2 = Limit, the Order will be processed by 'Good Till Cancel' scheme(see TimeInForce(59): GTC).
body.Append("40=" + orderType + "|");
if (price != 0)
{
//Reserved for future use.
body.Append("44=" + price + "|");
}
if (stopPrice != 0)
{
//The worst client price that the client will accept.
//Required when OrdType = 2, in which case the order will notfill unless this price can be met.
// body.Append("99=" + stopPrice + "|");
}
// 1 = Good Till Cancel (GTC), it will be active only for Limit Orders (see OrdType(40)) ;
// 3 = Immediate Or Cancel (IOC), it will be active only for Market Orders(see OrdType(40));
// 6 = Good Till Date(GTD), it will be active only if ExpireTime is defined (see ExpireTime(126)).
// GTD has a high priority, so if ExpireTime is defined, GTD will be used for the Order processing.
// body.Append("59=" + timeInForce + "|"); //not needed for modify
if (expireTime != string.Empty)
{
// Expire Time in YYYYMMDDHH:MM:SS format.If is assigned then the Order will be processed by 'Good Till Date' scheme
// (see TimeInForce: GTD).
body.Append("126=" + expireTime + "|");
}
if (positionID != string.Empty)
{
// Position ID, where this order should be placed. If not set, new position will be created, it’s id will be returned in ExecutionReport(8) message.
body.Append("721=" + positionID + "|");
}
*/
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.CancelOrderSingle), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Orders the status request.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="orderID">Unique identifier for the order, allocated by the client.</param>
/// <returns></returns>
public string OrderStatusRequest(SessionQualifier qualifier, int messageSequenceNumber, string orderID, int symb, int orderTyp)
{
var body = new StringBuilder();
// Unique identifier for the order, allocated by the client.
body.Append("11=" + orderID + "|");
//Symbol - added by me
//body.Append("55=" + symb + "|");//it was giving me error
// 1 = Buy; 2 = Sell. There is for the FIX compatibility only, so it will be ignored.
body.Append("54=" + orderTyp + "|");
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.OrderStatusRequest), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Executions the report.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="cTraderOrderID">cTrader order id..</param>
/// <param name="orderStatus"> 0 = New; 1 = Partially filled; 2 = Filled; 8 = Rejected; 4 = Cancelled(When an order is partially filled, "Cancelled" is
/// returned signifying Tag 151: LeavesQty is cancelled and will not be subsequently filled); C = Expired.</param>
/// <param name="transactTime">Time the transaction represented by this ExecutionReport occurred message(in UTC).</param>
/// <param name="symbol">Instrument identificators are provided by Spotware..</param>
/// <param name="side">1 = Buy; 2 = Sell.</param>
/// <param name="averagePrice">The price at which the deal was filled.For an IOC or GTD order, this is the VWAP(Volume Weighted Average Price) of the filled order.</param>
/// <param name="orderQuantity"> The fixed currency amount.</param>
/// <param name="leavesQuantity">The amount of the order still to be filled.This is a value between 0 (fully filled) and OrderQty (partially filled).</param>
/// <param name="cumQuantity">The total amount of the order which has been filled.</param>
/// <param name="orderID"> Unique identifier for the order, allocated by the client.</param>
/// <param name="orderType"> 1 = Market; 2 = Limit.</param>
/// <param name="price">If supplied in the NewOrderSingle, it is echoed back in this ExecutionReport.</param>
/// <param name="stopPrice">If supplied in the NewOrderSingle, it is echoed back in this ExecutionReport.</param>
/// <param name="timeInForce">U1 = Good Till Cancel (GTC); 3 = Immediate Or Cancel (IOC); 6 = Good Till Date (GTD).</param>
/// <param name="expireTime"> If supplied in the NewOrderSingle, it is echoed back in this ExecutionReport.</param>
/// <param name="text">Where possible, message to explain execution report.</param>
/// <param name="orderRejectionReason">0 = OrdRejReason.BROKER_EXCHANGE_OPTION</param>
/// <param name="positionID">Position ID.</param>
/// <returns></returns>
public string ExecutionReport(SessionQualifier qualifier, int messageSequenceNumber, string cTraderOrderID, string orderStatus, string transactTime, long symbol = 0, int side = 1, int averagePrice = 0, int orderQuantity = 0, int leavesQuantity = 0,
int cumQuantity = 0, string orderID = "", string orderType = "", int price = 0, int stopPrice = 0, string timeInForce = "", string expireTime = "", string text = "", int orderRejectionReason = -1, string positionID = "")
{
var body = new StringBuilder();
// cTrader order id.
body.Append("37=" + cTraderOrderID + "|");
// Unique identifier for the order, allocated by the client.
body.Append("11=" + orderID + "|");
// Execution Type.
body.Append("150=F|");
// 0 = New; 1 = Partially filled; 2 = Filled; 8 = Rejected; 4 = Cancelled(When an order is partially filled, "Cancelled" is
// returned signifying Tag 151: LeavesQty is cancelled and will not be subsequently filled); C = Expired.
body.Append("39=" + orderStatus + "|");
// Instrument identificators are provided by Spotware.
body.Append("55=" + symbol + "|");
// 1 = Buy; 2 = Sell.
body.Append("54=" + side + "|");
// Time the transaction represented by this ExecutionReport occurred message(in UTC).
body.Append("60=" + transactTime + "|");
if (averagePrice > 0)
{
// The price at which the deal was filled.For an IOC or GTD order, this is the VWAP(Volume Weighted Average Price) of the filled order.
body.Append("6=" + averagePrice + "|");
}
if (orderQuantity > 0)
{
// The fixed currency amount.
body.Append("38=" + orderQuantity + "|");
}
if (leavesQuantity > 0)
{
// The amount of the order still to be filled.This is a value between 0 (fully filled) and OrderQty (partially filled).
body.Append("151=" + leavesQuantity + "|");
}
if (cumQuantity > 0)
{
// The total amount of the order which has been filled.
body.Append("14=" + cumQuantity + "|");
}
if (orderType != string.Empty)
{
// 1 = Market; 2 = Limit.
body.Append("40=" + orderType + "|");
}
if (price > 0)
{
// If supplied in the NewOrderSingle, it is echoed back in this ExecutionReport.
body.Append("44=" + price + "|");
}
if (stopPrice > 0)
{
// If supplied in the NewOrderSingle, it is echoed back in this ExecutionReport.
body.Append("99=" + stopPrice + "|");
}
if (timeInForce != string.Empty)
{
// U1 = Good Till Cancel (GTC); 3 = Immediate Or Cancel (IOC); 6 = Good Till Date (GTD).
body.Append("59=" + timeInForce + "|");
}
if (expireTime != string.Empty)
{
// If supplied in the NewOrderSingle, it is echoed back in this ExecutionReport.
body.Append("126=" + expireTime + "|");
}
if (text != string.Empty)
{
// Where possible, message to explain execution report.
body.Append("58=" + text + "|");
}
if (orderRejectionReason != -1)
{
// 0 = OrdRejReason.BROKER_EXCHANGE_OPTION
body.Append("103=" + orderRejectionReason + "|");
}
if (positionID != string.Empty)
{
// Position ID.
body.Append("721=" + positionID + "|");
}
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.OrderStatusRequest), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Businesses the message reject.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="referenceSequenceNum">MsgSeqNum<34> of rejected message.</param>
/// <param name="referenceMessageType">TThe MsgType<35> of the FIX message being referenced.</param>
/// <param name="businessRejectRefID">The value of the business-level 'ID' field on the message being referenced.Required unless the corresponding ID field was not specified.</param>
/// <param name="businessRejectReason">Where possible, message to explain reason for rejection.</param>
/// <param name="text">Where possible, message to explain reason for rejection.</param>
/// <returns></returns>
public string BusinessMessageReject(SessionQualifier qualifier, int messageSequenceNumber, int referenceSequenceNum = 0, string referenceMessageType = "", string businessRejectRefID = "", int businessRejectReason = -1, string text = "")
{
var body = new StringBuilder();
if (referenceSequenceNum != 0)
{
// MsgSeqNum<34> of rejected message.
body.Append("45=" + referenceSequenceNum + "|");
}
if (referenceMessageType != string.Empty)
{
// The MsgType<35> of the FIX message being referenced.
body.Append("372=" + referenceMessageType + "|");
}
if (businessRejectRefID != string.Empty)
{
// The value of the business-level 'ID' field on the message being referenced.Required unless the corresponding ID field was not specified.
body.Append("379=" + businessRejectRefID + "|");
}
if (businessRejectReason != -1)
{
// Code to identify reason for a Business Message Reject<j> message. 0 = OTHER.
body.Append("380=" + businessRejectReason + "|");
}
if (text != string.Empty)
{
// Where possible, message to explain reason for rejection.
body.Append("58=" + text + "|");
}
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.BusinessMessageReject), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Requests for positions.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="requestID">Unique request ID (set by client).</param>
/// <param name="positionRequestID">Position ID to request. If not set, all open positions will be returned.</param>
/// <returns></returns>
public string RequestForPositions(SessionQualifier qualifier, int messageSequenceNumber, string requestID, string positionRequestID = "")
{
var body = new StringBuilder();
// Unique request ID (set by client).
body.Append("710=" + requestID + "|");
if (positionRequestID != string.Empty)
{
// Position ID to request. If not set, all open positions will be returned.
body.Append("721=" + positionRequestID + "|");
}
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.RequestForPosition), messageSequenceNumber, body.ToString());
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Gets the position of reporting.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="requestID">Id of RequestForPositions.</param>
/// <param name="totalNumberOfPositionReports">Total count of PositionReport’s in sequence when PosReqResult(728) is VALID_REQUEST, otherwise = 0.</param>
/// <param name="positionRequestResult">0 = Valid Request; 2 = No open positions found that match criteria.</param>
/// <param name="positionID">Position ID (is not set if PosReqResult(728) is not VALID_REQUEST).</param>
/// <param name="symbol">The symbol for which the current Position Report is prepared. (is not set if PosReqResult(728) is not VALID_REQUEST).</param>
/// <param name="noOfPositions">1 when PosReqResult(728) is VALID_REQUEST, otherwise not set.</param>
/// <param name="longQuantity">Position’s open volume in case of BUY trade side, = 0 in case of SELL trade side, is not set if PosReqResult(728) is not VALID_REQUEST.</param>
/// <param name="shortQuantity">Position’s open volume in case of SELL trade side, = 0 in case of BUY trade side, is not set if PosReqResult(728) is not VALID_REQUEST.</param>
/// <param name="settlementPrice">Average price of the opened volume in the current PositionReport.</param>
/// <returns></returns>
public string PositionReport(SessionQualifier qualifier, int messageSequenceNumber, string requestID, string totalNumberOfPositionReports, string positionRequestResult, string positionID = "", string symbol = "", string noOfPositions = "", string longQuantity = "", string shortQuantity = "",
string settlementPrice = "")
{
var body = new StringBuilder();
// Id of RequestForPositions.
body.Append("710=" + requestID + "|");
if (positionID != string.Empty)
{
// Position ID (is not set if PosReqResult(728) is not VALID_REQUEST).
body.Append("721=" + positionID + "|");
}
// Total count of PositionReport’s in sequence when PosReqResult(728) is VALID_REQUEST, otherwise = 0.
body.Append("727=" + totalNumberOfPositionReports + "|");
// 0 = Valid Request; 2 = No open positions found that match criteria.
body.Append("728=" + positionRequestResult + "|");
if (symbol != string.Empty)
{
// The symbol for which the current Position Report is prepared. (is not set if PosReqResult(728) is not VALID_REQUEST).
body.Append("55=" + symbol + "|");
}
if (noOfPositions != string.Empty)
{
// 1 when PosReqResult(728) is VALID_REQUEST, otherwise not set.
body.Append("702=" + noOfPositions + "|");
}
if (longQuantity != string.Empty)
{
// Position’s open volume in case of BUY trade side, = 0 in case of SELL trade side, is not set if PosReqResult(728) is not VALID_REQUEST.
body.Append("704=" + requestID + "|");
}
if (shortQuantity != string.Empty)
{
//Position’s open volume in case of SELL trade side, = 0 in case of BUY trade side, is not set if PosReqResult(728) is not VALID_REQUEST.
body.Append("705=" + shortQuantity + "|");
}
if (settlementPrice != string.Empty)
{
// Average price of the opened volume in the current PositionReport.
body.Append("730=" + settlementPrice + "|");
}
var header = ConstructHeader(qualifier, ApplicationMessageCode(ApplicationMessageType.PositionReport), messageSequenceNumber, string.Empty);
var headerAndBody = header + body;
var trailer = ConstructTrailer(headerAndBody);
var headerAndMessageAndTrailer = header + body + trailer;
return headerAndMessageAndTrailer.Replace("|", "\u0001");
}
/// <summary>
/// Constructs the message header.
/// </summary>
/// <param name="qualifier">The session qualifier.</param>
/// <param name="type">The message type.</param>
/// <param name="messageSequenceNumber">The message sequence number.</param>
/// <param name="bodyMessage">The body message.</param>
/// <returns></returns>
private string ConstructHeader(SessionQualifier qualifier, string type, int messageSequenceNumber, string bodyMessage)
{
var header = new StringBuilder();
// Protocol version. FIX.4.4 (Always unencrypted, must be first field
// in message.
header.Append("8=FIX.4.4|");
var message = new StringBuilder();
// Message type. Always unencrypted, must be third field in message.
message.Append("35=" + type + "|");
// ID of the trading party in following format: <BrokerUID>.<Trader Login>
// where BrokerUID is provided by cTrader and Trader Login is numeric
// identifier of the trader account.
message.Append("49=" + _senderCompID + "|");
// Message target. Valid value is "CSERVER"
message.Append("56=" + _targetCompID + "|");
// Additional session qualifier. Possible values are: "QUOTE", "TRADE".
message.Append("57=" + qualifier.ToString() + "|");
// Assigned value used to identify specific message originator.
message.Append("50=" + _senderSubID + "|");
// Message Sequence Number
message.Append("34=" + messageSequenceNumber + "|");
// Time of message transmission (always expressed in UTC(Universal Time
// Coordinated, also known as 'GMT').
message.Append("52=" + DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss") + "|");
var length = message.Length + bodyMessage.Length;
// Message body length. Always unencrypted, must be second field in message.
header.Append("9=" + length + "|");
header.Append(message);
return header.ToString();
}
/// <summary>
/// Constructs the message trailer.
/// </summary>
/// <param name="message">The message trailer.</param>
/// <returns></returns>
private string ConstructTrailer(string message)
{
//Three byte, simple checksum. Always last field in message; i.e. serves,
//with the trailing<SOH>,
//as the end - of - message delimiter. Always defined as three characters
//(and always unencrypted).
var trailer = "10=" + CalculateChecksum(message.Replace("|", "\u0001").ToString()).ToString().PadLeft(3, '0') + "|";
return trailer;
}
/// <summary>
/// Calculates the checksum.
/// </summary>
/// <param name="dataToCalculate">The data to calculate.</param>
/// <returns></returns>
private int CalculateChecksum(string dataToCalculate)
{
byte[] byteToCalculate = Encoding.ASCII.GetBytes(dataToCalculate);
int checksum = 0;
foreach (byte chData in byteToCalculate)
{
checksum += chData;
}
return checksum % 256;
}
/// <summary>
/// Returns the session the message code.
/// </summary>
/// <param name="type">The session message type.</param>
/// <returns></returns>
private string SessionMessageCode(SessionMessageType type)
{
switch (type)
{
case SessionMessageType.Heartbeat:
return "0";
//break;
case SessionMessageType.Logon:
return "A";
//break;
case SessionMessageType.Logout:
return "5";
// break;
case SessionMessageType.Reject:
return "3";
//break;
case SessionMessageType.Resend:
return "2";
//break;
case SessionMessageType.SequenceReset:
return "4";
//break;
case SessionMessageType.TestRequest:
return "1";
default:
//break;
return "0";
}
}
/// <summary>
/// Returns the application message code.
/// </summary>
/// <param name="type">The application message type.</param>
/// <returns></returns>
private string ApplicationMessageCode(ApplicationMessageType type)
{
switch (type)
{
case ApplicationMessageType.MarketDataRequest:
return "V";
//break;
case ApplicationMessageType.MarketDataIncrementalRefresh:
return "X";
//break;
case ApplicationMessageType.NewOrderSingle:
return "D";
//break;
case ApplicationMessageType.CancelOrderSingle:
return "F";
// break;
case ApplicationMessageType.OrderStatusRequest:
return "H";
// break;
case ApplicationMessageType.ExecutionReport:
return "8";
// break;
case ApplicationMessageType.BusinessMessageReject:
return "j";
// break;
case ApplicationMessageType.RequestForPosition:
return "AN";
// break;
case ApplicationMessageType.PositionReport:
return "AP";
default:
// break;
return "0";
}
}
}
}
@netread2004
netread2004
05 Nov 2018, 09:31
Looks like this is logON issue. I checked response for LogOn FIX message and the response is empty message.
As far as my original issue is concerend, I think it was arising due to sending logOn message to the server already Logged on and no Logout sent.
I will monitor this further and update on this.
Thanks
@netread2004

PanagiotisCharalampous
29 Oct 2018, 11:37
Hi netread2004,
Can you share the cBot code so that we can reproduce the issue?
Best Regards,
Panagiotis
@PanagiotisCharalampous