I have a sever running already (acceptor) and I am trying to build something to send orders to this server by using quickfix
and c#
.net 4.7
I installed the quick fix library and tried to copy the sample tradeClient from the official website, but I am not seeing any actual connection made.
PS:
- I am using WinForms, so I commented out the Console.Readline() and put a fixed value for testing purposes.
- when it trigger
run()
it will run the QueryNew function, it never hit theOnLogon()
function - from the console, i see the following log message:
<event> Created session>
<event> Connecting to xxx.xxx.xxx.xxx on port xxxx
<event> Connection successed
<outgoing> 8=Fix4.2|9=71|35=A|34=2|49=SENDER|52=20230207 15:06:56.858|56=TARGET|90=0|108=30|10=134|
<event> Initiated logon request
The thread 0X84a8 has exited with code 0(0x0).
i have a few questions:
- Is it correct that whenever
run()
started, it will automatically tried to logon? or do I have to call a logon function? - Is there any additional setup required from the server side if I am trying to connect it with a new application? (Currently, there is already a program connecting to it, so the network is not a concern)
- How can I logon to the session and get the heartbeat and send orders after
Here is my code
Initiator.cs
using QuickFix;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FIXLogAnalyser.FixSender
{
public partial class Initiator : Form
{
public Initiator()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("=============");
Console.WriteLine("This is only an example program, meant to run against the Executor or SimpleAcceptor example programs.");
Console.WriteLine();
Console.WriteLine(" ! ! !");
Console.WriteLine(" DO NOT USE THIS ON A COMMERCIAL FIX INTERFACE! It won't work and it's a bad idea!");
Console.WriteLine(" ! ! !");
Console.WriteLine();
Console.WriteLine("=============");
//if (args.Length != 1)
//{
// System.Console.WriteLine("usage: TradeClient.exe CONFIG_FILENAME");
// System.Environment.Exit(2);
//}
string file = "tradeclient.cfg";
try
{
QuickFix.SessionSettings settings = new QuickFix.SessionSettings(file);
TradeClientApp application = new TradeClientApp();
QuickFix.IMessageStoreFactory storeFactory = new QuickFix.FileStoreFactory(settings);
QuickFix.ILogFactory logFactory = new QuickFix.ScreenLogFactory(settings);
QuickFix.Transport.SocketInitiator initiator = new QuickFix.Transport.SocketInitiator(application, storeFactory, settings, logFactory);
// this is a developer-test kludge. do not emulate.
application.MyInitiator = initiator;
initiator.Start();
Console.WriteLine(initiator.IsLoggedOn());
application.Run();
initiator.Stop();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
Environment.Exit(1);
}
}
}
tradeClientApp.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using QuickFix;
using QuickFix.Fields;
namespace FIXLogAnalyser.FixSender
{
class TradeClientApp : QuickFix.MessageCracker, QuickFix.IApplication
{
Session _session = null;
// This variable is a kludge for developer test purposes. Don't do this on a production application.
public IInitiator MyInitiator = null;
#region IApplication interface overrides
public void OnCreate(SessionID sessionID)
{
_session = Session.LookupSession(sessionID);
}
public void OnLogon(SessionID sessionID)
{
try
{
Console.WriteLine("Logon - " + sessionID.ToString());
}
catch (Exception ex)
{
Console.WriteLine("fail: " + ex.Message);
}
}
public void OnLogout(SessionID sessionID) { Console.WriteLine("Logout - " + sessionID.ToString()); }
public void FromAdmin(Message message, SessionID sessionID)
{
Console.WriteLine("from admin: " + message);
}
public void ToAdmin(Message message, SessionID sessionID)
{
Console.WriteLine(sessionID.SenderCompID + " - " + sessionID.TargetCompID);
if (message.GetType() == typeof(QuickFix.FIX42.Logon))
{
// message.SetField(new Username("YOUR_USERNAME"));
// message.SetField(new Password("YOUR_PASSWORD"));
}
// message.SetField(new QuickFix.Fields.Account("YOUR_ACCOUNT_NUMBER"));
}
public void FromApp(Message message, SessionID sessionID)
{
Console.WriteLine("IN: " + message.ToString());
try
{
Crack(message, sessionID);
}
catch (Exception ex)
{
Console.WriteLine("==Cracker exception==");
Console.WriteLine(ex.ToString());
Console.WriteLine(ex.StackTrace);
}
}
public void ToApp(Message message, SessionID sessionID)
{
try
{
bool possDupFlag = false;
if (message.Header.IsSetField(QuickFix.Fields.Tags.PossDupFlag))
{
possDupFlag = QuickFix.Fields.Converters.BoolConverter.Convert(
message.Header.GetString(QuickFix.Fields.Tags.PossDupFlag)); /// FIXME
}
if (possDupFlag)
throw new DoNotSend();
}
catch (FieldNotFoundException)
{ }
Console.WriteLine();
Console.WriteLine("OUT: " + message.ToString());
}
#endregion
#region MessageCracker handlers
public void OnMessage(QuickFix.FIX42.ExecutionReport m, SessionID s)
{
Console.WriteLine("Received execution report");
}
public void OnMessage(QuickFix.FIX42.OrderCancelReject m, SessionID s)
{
Console.WriteLine("Received order cancel reject");
}
#endregion
public void Run()
{
try
{
Console.WriteLine(MyInitiator.IsLoggedOn());
char action = QueryAction();
if (action == '1')
QueryEnterOrder();
else if (action == '2')
QueryCancelOrder();
else if (action == '3')
QueryReplaceOrder();
else if (action == '4')
QueryMarketDataRequest();
else if (action == 'g')
{
if (this.MyInitiator.IsStopped)
{
Console.WriteLine("Restarting initiator...");
this.MyInitiator.Start();
}
else
Console.WriteLine("Already started.");
}
else if (action == 'x')
{
if (this.MyInitiator.IsStopped)
Console.WriteLine("Already stopped.");
else
{
Console.WriteLine("Stopping initiator...");
this.MyInitiator.Stop();
}
}
else if (action == 'q' || action == 'Q')
Console.WriteLine("break");// break;
Console.WriteLine(MyInitiator.IsLoggedOn());
}
catch (System.Exception e)
{
Console.WriteLine("Message Not Sent: " + e.Message);
Console.WriteLine("StackTrace: " + e.StackTrace);
}
Console.WriteLine("Program shutdown.");
}
private void SendMessage(Message m)
{
if (_session != null)
_session.Send(m);
else
{
// This probably won't ever happen.
Console.WriteLine("Can't send message: session not created.");
}
}
private char QueryAction()
{
// Commands 'g' and 'x' are intentionally hidden.
Console.Write("\n"
+ "1) Enter Order\n"
+ "2) Cancel Order\n"
+ "3) Replace Order\n"
+ "4) Market data test\n"
+ "Q) Quit\n"
+ "Action: "
);
HashSet<string> validActions = new HashSet<string>("1,2,3,4,q,Q,g,x".Split(','));
string cmd = "g";
// string cmd = Console.ReadLine().Trim();
if (cmd.Length != 1 || validActions.Contains(cmd) == false)
throw new System.Exception("Invalid action");
return cmd.ToCharArray()[0];
}
private void QueryEnterOrder()
{
Console.WriteLine("\nNewOrderSingle");
QuickFix.FIX42.NewOrderSingle m = QueryNewOrderSingle44();
if (m != null && QueryConfirm("Send order"))
{
m.Header.GetString(Tags.BeginString);
SendMessage(m);
}
}
private void QueryCancelOrder()
{
Console.WriteLine("\nOrderCancelRequest");
QuickFix.FIX42.OrderCancelRequest m = QueryOrderCancelRequest44();
if (m != null && QueryConfirm("Cancel order"))
SendMessage(m);
}
private void QueryReplaceOrder()
{
Console.WriteLine("\nCancelReplaceRequest");
QuickFix.FIX42.OrderCancelReplaceRequest m = QueryCancelReplaceRequest42();
if (m != null && QueryConfirm("Send replace"))
SendMessage(m);
}
private void QueryMarketDataRequest()
{
Console.WriteLine("\nMarketDataRequest");
QuickFix.FIX42.MarketDataRequest m = QueryMarketDataRequest44();
if (m != null && QueryConfirm("Send market data request"))
SendMessage(m);
}
private bool QueryConfirm(string query)
{
Console.WriteLine();
Console.WriteLine(query + "?: ");
// string line = Console.ReadLine().Trim();
string line = "y";
return (line[0].Equals('y') || line[0].Equals('Y'));
}
#region Message creation functions
private QuickFix.FIX42.NewOrderSingle QueryNewOrderSingle44()
{
QuickFix.Fields.OrdType ordType = null;
QuickFix.FIX42.NewOrderSingle newOrderSingle = new QuickFix.FIX42.NewOrderSingle(
QueryClOrdID(),
QueryHandlInst(),
QuerySymbol(),
QuerySide(),
new TransactTime(DateTime.Now),
ordType = QueryOrdType()); ;
newOrderSingle.Set(new HandlInst('1'));
newOrderSingle.Set(QueryOrderQty());
newOrderSingle.Set(QueryTimeInForce());
if (ordType.getValue() == OrdType.LIMIT || ordType.getValue() == OrdType.STOP_LIMIT)
newOrderSingle.Set(QueryPrice());
if (ordType.getValue() == OrdType.STOP || ordType.getValue() == OrdType.STOP_LIMIT)
newOrderSingle.Set(QueryStopPx());
return newOrderSingle;
}
private QuickFix.FIX42.OrderCancelRequest QueryOrderCancelRequest44()
{
QuickFix.FIX42.OrderCancelRequest orderCancelRequest = new QuickFix.FIX42.OrderCancelRequest(
QueryOrigClOrdID(),
QueryClOrdID(),
QuerySymbol(),
QuerySide(),
new TransactTime(DateTime.Now));
orderCancelRequest.Set(QueryOrderQty());
return orderCancelRequest;
}
private QuickFix.FIX42.OrderCancelReplaceRequest QueryCancelReplaceRequest42()
{
QuickFix.FIX42.OrderCancelReplaceRequest ocrr = new QuickFix.FIX42.OrderCancelReplaceRequest(
QueryOrigClOrdID(),
QueryClOrdID(),
QueryHandlInst(),
QuerySymbol(),
QuerySide(),
new TransactTime(DateTime.Now),
QueryOrdType());
ocrr.Set(new HandlInst('1'));
if (QueryConfirm("New price"))
ocrr.Set(QueryPrice());
if (QueryConfirm("New quantity"))
ocrr.Set(QueryOrderQty());
return ocrr;
}
private QuickFix.FIX42.MarketDataRequest QueryMarketDataRequest44()
{
MDReqID mdReqID = new MDReqID("MARKETDATAID");
SubscriptionRequestType subType = new SubscriptionRequestType(SubscriptionRequestType.SNAPSHOT);
MarketDepth marketDepth = new MarketDepth(0);
QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup marketDataEntryGroup = new QuickFix.FIX42.MarketDataRequest.NoMDEntryTypesGroup();
marketDataEntryGroup.Set(new MDEntryType(MDEntryType.BID));
QuickFix.FIX42.MarketDataRequest.NoRelatedSymGroup symbolGroup = new QuickFix.FIX42.MarketDataRequest.NoRelatedSymGroup();
symbolGroup.Set(new Symbol("LNUX"));
QuickFix.FIX42.MarketDataRequest message = new QuickFix.FIX42.MarketDataRequest(mdReqID, subType, marketDepth);
message.AddGroup(marketDataEntryGroup);
message.AddGroup(symbolGroup);
return message;
}
#endregion
#region field query private methods
private ClOrdID QueryClOrdID()
{
Console.WriteLine();
Console.Write("ClOrdID? ");
return new ClOrdID("orderID123");
// return new ClOrdID(Console.ReadLine().Trim());
}
private OrigClOrdID QueryOrigClOrdID()
{
Console.WriteLine();
Console.Write("OrigClOrdID? ");
//return new OrigClOrdID(Console.ReadLine().Trim())
return new OrigClOrdID("OrigClordID123");
}
private HandlInst QueryHandlInst()
{
Console.WriteLine();
Console.Write("QueryHandlInst? ");
// return new HandlInst(Console.ReadLine()[0]);
return new HandlInst('1');
}
private Symbol QuerySymbol()
{
Console.WriteLine();
Console.Write("Symbol? ");
// return new Symbol(Console.ReadLine().Trim());
return new Symbol("700 HK");
}
private Side QuerySide()
{
Console.WriteLine();
Console.WriteLine("1) Buy");
Console.WriteLine("2) Sell");
Console.WriteLine("3) Sell Short");
Console.WriteLine("4) Sell Short Exempt");
Console.WriteLine("5) Cross");
Console.WriteLine("6) Cross Short");
Console.WriteLine("7) Cross Short Exempt");
Console.Write("Side? ");
// string s = Console.ReadLine().Trim();
string s = "1";
char c = ' ';
switch (s)
{
case "1": c = Side.BUY; break;
case "2": c = Side.SELL; break;
case "3": c = Side.SELL_SHORT; break;
case "4": c = Side.SELL_SHORT_EXEMPT; break;
case "5": c = Side.CROSS; break;
case "6": c = Side.CROSS_SHORT; break;
case "7": c = 'A'; break;
default: throw new Exception("unsupported input");
}
return new Side(c);
}
private OrdType QueryOrdType()
{
Console.WriteLine();
Console.WriteLine("1) Market");
Console.WriteLine("2) Limit");
Console.WriteLine("3) Stop");
Console.WriteLine("4) Stop Limit");
Console.Write("OrdType? ");
//string s = Console.ReadLine().Trim();
string s = "1";
char c = ' ';
switch (s)
{
case "1": c = OrdType.MARKET; break;
case "2": c = OrdType.LIMIT; break;
case "3": c = OrdType.STOP; break;
case "4": c = OrdType.STOP_LIMIT; break;
default: throw new Exception("unsupported input");
}
return new OrdType(c);
}
private OrderQty QueryOrderQty()
{
Console.WriteLine();
Console.Write("OrderQty? ");
// return new OrderQty(Convert.ToDecimal(Console.ReadLine().Trim()));
return new OrderQty(Convert.ToDecimal("400.00"));
}
private TimeInForce QueryTimeInForce()
{
Console.WriteLine();
Console.WriteLine("1) Day");
Console.WriteLine("2) IOC");
Console.WriteLine("3) OPG");
Console.WriteLine("4) GTC");
Console.WriteLine("5) GTX");
Console.Write("TimeInForce? ");
// string s = Console.ReadLine().Trim();
string s = "1";
char c = ' ';
switch (s)
{
case "1": c = TimeInForce.DAY; break;
case "2": c = TimeInForce.IMMEDIATE_OR_CANCEL; break;
case "3": c = TimeInForce.AT_THE_OPENING; break;
case "4": c = TimeInForce.GOOD_TILL_CANCEL; break;
case "5": c = TimeInForce.GOOD_TILL_CROSSING; break;
default: throw new Exception("unsupported input");
}
return new TimeInForce(c);
}
private Price QueryPrice()
{
Console.WriteLine();
Console.Write("Price? ");
return new Price(Convert.ToDecimal(Console.ReadLine().Trim()));
}
private StopPx QueryStopPx()
{
Console.WriteLine();
Console.Write("StopPx? ");
return new StopPx(Convert.ToDecimal(Console.ReadLine().Trim()));
}
#endregion
}
}
tradeClient.cfg
[DEFAULT]
ConnectionType=initiator
ReconnectInterval=2
FileStorePath=logpath
FileLogPath=log
StartTime=01:00:00
EndTime=23:00:00
UseDataDictionary=Y
DataDictionary=FIX42.xml
SocketConnectHost=xxx.xxx.xxx.xxx
SocketConnectPort=xxxx
LogoutTimeout=5
ResetOnLogon=N
ResetOnDisconnect=Y
[SESSION]
# inherit ConnectionType, ReconnectInterval and SenderCompID from default
BeginString=FIX.4.2
SenderCompID=SENDER
TargetCompID=TARGET
HeartBtInt=30