Compare commits
45
Commits
development
...
fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25805cf675 | ||
|
|
1621ad03bf | ||
|
|
193f248f06 | ||
|
|
5212fb272e | ||
|
|
6772c77e01 | ||
|
|
95c589f594 | ||
|
|
699c7c5bfe | ||
|
|
7de82b5685 | ||
|
|
a8e9d0d7d8 | ||
|
|
d078e73179 | ||
|
|
4351d6e66a | ||
|
|
9850d6bde0 | ||
|
|
d23a072ad6 | ||
|
|
6ef0808f49 | ||
|
|
e1c2d8aaaf | ||
|
|
719fd03063 | ||
|
|
d436dae7cd | ||
|
|
f66004b465 | ||
|
|
aa8a56787b | ||
|
|
35a69396f1 | ||
|
|
ddfb6d1105 | ||
|
|
005c0f9a5d | ||
|
|
57af89b628 | ||
|
|
feab3cbb89 | ||
|
|
1f7c1be194 | ||
|
|
f78f18b3b4 | ||
|
|
6e7fa91718 | ||
|
|
8cac406547 | ||
|
|
0d83f28cbc | ||
|
|
941f4bf837 | ||
|
|
8bbe9cfd98 | ||
|
|
ba633532eb | ||
|
|
7bf50415ca | ||
|
|
edbaa9c435 | ||
|
|
7789f39c9d | ||
|
|
6e188b4f73 | ||
|
|
33e8ff58c5 | ||
|
|
24ae03a9f8 | ||
|
|
5fe0335e1a | ||
|
|
6cbf707094 | ||
|
|
7e4ad55d9d | ||
|
|
35a0074014 | ||
|
|
b8dd5c119d | ||
|
|
8ada73aa64 | ||
|
|
a16ff30873 |
No files matched your search
@@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\sp
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_excel", "src\client\spark_excel\spark_excel.csproj", "{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_fix", "src\client\spark_fix\spark_fix.csproj", "{D8031C13-7BEA-430A-BB60-40927D15CFC3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -219,6 +221,26 @@ Global
|
||||
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x86.ActiveCfg = Release|x64
|
||||
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x86.Build.0 = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x64.Build.0 = Debug|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|Any CPU.Build.0 = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x64.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x64.Build.0 = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x86.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x86.Build.0 = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x64.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x64.Build.0 = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x86.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x86.ActiveCfg = Release|x64
|
||||
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x86.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>x64</Platforms>
|
||||
<RuntimeIdentifiers>win7-x64</RuntimeIdentifiers>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets>
|
||||
<ApplicationIcon>logo.ico</ApplicationIcon>
|
||||
|
||||
@@ -3,4 +3,5 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("spark_core")]
|
||||
[assembly: InternalsVisibleTo("sparkcmd")]
|
||||
[assembly: InternalsVisibleTo("spark_test")]
|
||||
[assembly: InternalsVisibleTo("spark_fix")]
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using static bts.api.Security;
|
||||
|
||||
namespace bts.api {
|
||||
struct FutureLeg {
|
||||
public struct FutureLeg {
|
||||
public readonly Venue exchange;
|
||||
public readonly string symbol, exchange_desc;
|
||||
public readonly FutureContractImmutable future;
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using static bts.api.Security;
|
||||
|
||||
namespace bts.api {
|
||||
internal static class SecurityHelper {
|
||||
public static class SecurityHelper {
|
||||
public static IEnumerable<FutureLeg> get_flat_future_legs(SecurityImmutable sec) {
|
||||
switch (sec.ContractCase) {
|
||||
case ContractOneofCase.Spread: return _get_flat_future_legs(sec.Spread.Legs, 1);
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace bts {
|
||||
Plugin,
|
||||
Sdk,
|
||||
Http,
|
||||
Fix
|
||||
// Please keep this list in sync with logging.h/logging.cpp
|
||||
}
|
||||
|
||||
@@ -102,9 +103,10 @@ namespace bts {
|
||||
internal static Logger Assert => new Logger(Subsystem.Assert);
|
||||
internal static Logger Ui => new Logger(Subsystem.Ui);
|
||||
internal static Logger Testing => new Logger(Subsystem.Testing);
|
||||
internal static Logger DataFlow => new Logger(Subsystem.DataFlow);
|
||||
internal static Logger DataFlow => new Logger(Subsystem.DataFlow);
|
||||
public static Logger Sdk => new Logger(Subsystem.Sdk);
|
||||
public static Logger Http => new Logger(Subsystem.Http);
|
||||
internal static Logger FIX => new Logger(Subsystem.Fix);
|
||||
|
||||
internal static void log_at(this Logger logger, Level level, string message) {
|
||||
switch (level) {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using bts;
|
||||
using bts.api.admin;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.api.core
|
||||
{
|
||||
public class AdminModelWrapper
|
||||
{
|
||||
readonly AdminDataImmutable? _admin_data;
|
||||
public AdminDataImmutable? admin_data => _admin_data;
|
||||
readonly string _user_name;
|
||||
readonly List<BTSAccountImmutable> _allowed_accounts;
|
||||
readonly Dictionary<string, string> _allowed_account_dict = new Dictionary<string, string>();
|
||||
public AdminModelWrapper(AdminDataImmutable? admin_data = null, string user_name = null)
|
||||
{
|
||||
_admin_data = admin_data;
|
||||
_user_name = user_name ?? string.Empty;
|
||||
_allowed_accounts = search_admin(u => u.Name == _user_name).Where(x =>
|
||||
{
|
||||
return (x.Item2 != null && x.Item2.HasPermissions) || (x.Item3 != null && x.Item3.HasPermissions);
|
||||
}).Select(xx => xx.Item1).ToList();
|
||||
foreach (var val in _allowed_accounts)
|
||||
_allowed_account_dict[val.Name] = val.Name;
|
||||
}
|
||||
public IEnumerable<BTSAccountImmutable> viewable_accounts()
|
||||
{
|
||||
return _allowed_accounts;
|
||||
}
|
||||
public bool can_view_account(string account)
|
||||
{
|
||||
return _allowed_account_dict.ContainsKey(account) || _user_name.ToLower().Equals("bts");
|
||||
}
|
||||
|
||||
IEnumerable<Tuple<BTSAccountImmutable, TradingPermissionUnit, AdminPermissionUnit>>
|
||||
search_admin(Func<BTSUserImmutable, bool> predicate)
|
||||
{
|
||||
var rv = new List<Tuple<BTSAccountImmutable, TradingPermissionUnit, AdminPermissionUnit>>();
|
||||
if (_admin_data.HasValue)
|
||||
{
|
||||
var users = _admin_data.Value.Users.Where(predicate);
|
||||
if (users != null && users.Count() == 1)
|
||||
{
|
||||
var user = users.ElementAt(0);
|
||||
if (user.HasUserData && user.UserData.HasAccountPermissions)
|
||||
{
|
||||
foreach (var kv in user.UserData.AccountPermissions.AccountPermissions)
|
||||
{
|
||||
var account_id = kv.Key;
|
||||
var accounts = _admin_data.Value.Accounts.Where(a => a.Id == account_id);
|
||||
if (accounts != null && accounts.Count() == 1)
|
||||
{
|
||||
var account = accounts.ElementAt(0);
|
||||
rv.Add(Tuple.Create(account, kv.Value.TradingPermission, kv.Value.AdminPermission));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.api.position;
|
||||
using bts.spark;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace bts.api.core
|
||||
{
|
||||
public static class PermissionFilters
|
||||
{
|
||||
public static IObservable<Update<PositionUnitImmutable, int>>
|
||||
PermissionFilter(this IObservable<Update<PositionUnitImmutable, int>> source, IObservable<AdminModelWrapper> am)
|
||||
{
|
||||
var pred = Observable.Create<Func<PositionUnitImmutable, bool>>(observer =>
|
||||
{
|
||||
return am.Subscribe(x =>
|
||||
{
|
||||
Func<PositionUnitImmutable, bool> filter = (z => x.can_view_account(z.Account));
|
||||
observer.OnNext(filter);
|
||||
});
|
||||
});
|
||||
|
||||
return source.Filter(pred);
|
||||
}
|
||||
|
||||
public static IObservable<Update<OrderRecordImmutable, uint>>
|
||||
PermissionFilter(this IObservable<Update<OrderRecordImmutable, uint>> source, IObservable<AdminModelWrapper> am)
|
||||
{
|
||||
var pred = Observable.Create<Func<OrderRecordImmutable, bool>>(observer =>
|
||||
{
|
||||
return am.Subscribe(x =>
|
||||
{
|
||||
Func<OrderRecordImmutable, bool> filter = (z => x.can_view_account(z.Def.Account ?? string.Empty));
|
||||
observer.OnNext(filter);
|
||||
});
|
||||
});
|
||||
|
||||
return source.Filter(pred);
|
||||
}
|
||||
|
||||
public static IObservable<Update<FillImmutable, string>>
|
||||
PermissionFilter(this IObservable<Update<FillImmutable, string>> source, IObservable<AdminModelWrapper> am)
|
||||
{
|
||||
var pred = Observable.Create<Func<FillImmutable, bool>>(observer =>
|
||||
{
|
||||
return am.Subscribe(x =>
|
||||
{
|
||||
Func<FillImmutable, bool> filter = (z => x.can_view_account(z.Account));
|
||||
observer.OnNext(filter);
|
||||
});
|
||||
});
|
||||
|
||||
return source.Filter(pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,6 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void sync_with_models<T, TModel>(this IList<T> target, IEnumerable<TModel> models, Func<TModel, T> add_factory, Func<T, TModel, T> update_factory) {
|
||||
var model_list = models as IList<TModel> ?? models.ToList();
|
||||
if (model_list.Count == 0) {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using QuickFix;
|
||||
using QuickFix.Fields;
|
||||
|
||||
namespace bts.fix
|
||||
{
|
||||
public class FixInit
|
||||
{
|
||||
static FixInit()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
}
|
||||
|
||||
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var exception = e.ExceptionObject as Exception;
|
||||
Log.FIX.error($"Unhandled Exception: {e.ExceptionObject} {exception?.StackTrace}");
|
||||
if (e.IsTerminating)
|
||||
{
|
||||
Log.system_is_crashing("Unhandled exception", e.ExceptionObject);
|
||||
}
|
||||
}
|
||||
|
||||
public static int start_acceptor(string settings_file, bool orders_allowed, string http_address = null)
|
||||
{
|
||||
SessionSettings settings = new SessionSettings(settings_file);
|
||||
IApplication app = new FixRouter( (th, sess) =>
|
||||
{
|
||||
if(orders_allowed)
|
||||
return new OrderSession(th, sess);
|
||||
else
|
||||
return new DropCopySession(th, sess);
|
||||
});
|
||||
IMessageStoreFactory storeFactory = new FileStoreFactory(settings);
|
||||
ILogFactory logFactory = new FileLogFactory(settings);
|
||||
IAcceptor acceptor = new ThreadedSocketAcceptor(app, storeFactory, settings, logFactory);
|
||||
|
||||
acceptor.Start();
|
||||
HttpServer srv = null;
|
||||
if (!string.IsNullOrEmpty(http_address))
|
||||
{
|
||||
if(!http_address.StartsWith("http"))
|
||||
http_address = "http://127.0.0.1:5080/";
|
||||
srv = new HttpServer(http_address, settings);
|
||||
srv.Start();
|
||||
}
|
||||
Console.WriteLine("press <q> to quit");
|
||||
while (Console.Read() != 'q') { }
|
||||
acceptor.Stop();
|
||||
if(srv != null)
|
||||
srv.Stop();
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Text;
|
||||
|
||||
using QuickFix;
|
||||
using QuickFix.Fields;
|
||||
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.api.user;
|
||||
using bts.md;
|
||||
using bts.api.admin;
|
||||
|
||||
using security_key = System.UInt32;
|
||||
|
||||
namespace bts.fix
|
||||
{
|
||||
interface IFixRouter
|
||||
{
|
||||
IEnumerable<SecurityImmutable> get_securities();
|
||||
SecurityImmutable? lookup(security_key key);
|
||||
SecurityImmutable? lookup(string desc);
|
||||
|
||||
void send(Message mess, SessionID sess);
|
||||
void send_session_reject(SessionID sess, string text, Message refmessage = null);
|
||||
void send_biz_reject(SessionID sess, string text, Message refmessage, int reason_code = 0);
|
||||
}
|
||||
|
||||
class FixRouter : QuickFix.MessageCracker, IFIXSession, IFixRouter
|
||||
{
|
||||
readonly ConcurrentDictionary<SessionID, QuickFix.IApplication> _sessions = new ConcurrentDictionary<SessionID, IApplication>();
|
||||
readonly Func<IFixRouter, SessionID, IApplication> _factory;
|
||||
readonly MarketDataClient _md = new MarketDataClient();
|
||||
readonly object _lock = new object();
|
||||
|
||||
public FixRouter(Func<IFixRouter, SessionID, IApplication> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
IApplication session(SessionID sessionID)
|
||||
{
|
||||
IApplication app;
|
||||
if (!_sessions.TryGetValue(sessionID, out app))
|
||||
{
|
||||
app = _factory(this, sessionID);
|
||||
_sessions[sessionID] = app;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
public void send(Message mess, SessionID sess)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
Session.SendToTarget(mess, sess);
|
||||
}
|
||||
catch (SessionNotFound)
|
||||
{
|
||||
Log.assert(false, "session not found " + sess);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.FIX.error(sess.ToString() + " " + ex.Message + " " + sess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void send_session_reject(SessionID sess, string text, Message refmessage = null)
|
||||
{
|
||||
QuickFix.FIX44.Reject rej = null;
|
||||
if (refmessage != null && refmessage.Header.IsSetField(Tags.MsgSeqNum))
|
||||
rej = new QuickFix.FIX44.Reject(new QuickFix.Fields.RefSeqNum(refmessage.Header.GetInt(Tags.MsgSeqNum)));
|
||||
else
|
||||
rej = new QuickFix.FIX44.Reject();
|
||||
rej.Text = new Text(text);
|
||||
send(rej, sess);
|
||||
}
|
||||
|
||||
public void send_biz_reject(SessionID sess, string text, Message refmessage, int reason_code = 0)
|
||||
{
|
||||
QuickFix.FIX44.BusinessMessageReject rej = new QuickFix.FIX44.BusinessMessageReject();
|
||||
|
||||
rej.RefMsgType = new QuickFix.Fields.RefMsgType(refmessage.Header.GetString(Tags.MsgType));
|
||||
rej.RefSeqNum = new QuickFix.Fields.RefSeqNum(refmessage.Header.GetInt(Tags.MsgSeqNum));
|
||||
rej.BusinessRejectReason = new QuickFix.Fields.BusinessRejectReason(reason_code);
|
||||
rej.Text = new Text(text);
|
||||
send(rej, sess);
|
||||
}
|
||||
|
||||
void handle_session_exception(Exception ex, SessionID sessionID, Message message = null)
|
||||
{
|
||||
string msg = "==ERROR in session " + sessionID.ToString() + "== ";
|
||||
msg += message?.GetType().Name ?? string.Empty;
|
||||
msg += ex?.ToString() ?? string.Empty;
|
||||
if(!string.IsNullOrEmpty(ex.StackTrace))
|
||||
msg += Environment.NewLine + ex.StackTrace;
|
||||
|
||||
bts.Log.FIX.error(msg);
|
||||
Console.WriteLine(msg);
|
||||
//now shut this fucker down
|
||||
try
|
||||
{
|
||||
var sess = Session.LookupSession(sessionID);
|
||||
if(sess != null)
|
||||
sess.Disconnect(msg);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
void handle_field_not_found(FieldNotFoundException ex, SessionID sess, Message message)
|
||||
{
|
||||
if(ex.Field > 0)
|
||||
send_biz_reject(sess, $"Field {ex.Field} was expected but not found", message, 5 );
|
||||
//QF does not include the tag number....
|
||||
//throw new FieldNotFoundException($"Field {ex.Field} was expected but not found", ex);
|
||||
else
|
||||
throw ex;
|
||||
}
|
||||
|
||||
public void FromAdmin(Message message, SessionID sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).FromAdmin(message, sessionID);
|
||||
}
|
||||
catch (FieldNotFoundException ex) { handle_field_not_found(ex, sessionID, message); }
|
||||
catch (UnsupportedMessageType) { throw; }
|
||||
catch (IncorrectTagValue) { throw; }
|
||||
catch (Exception ex) { handle_session_exception(ex, sessionID, message); }
|
||||
}
|
||||
|
||||
public void FromApp(Message message, SessionID sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).FromApp(message, sessionID);
|
||||
}
|
||||
catch (FieldNotFoundException ex) { handle_field_not_found(ex, sessionID, message); }
|
||||
catch (UnsupportedMessageType) { throw; }
|
||||
catch (IncorrectTagValue) { throw; }
|
||||
catch (Exception ex) { handle_session_exception(ex, sessionID, message); }
|
||||
}
|
||||
|
||||
public void OnCreate(SessionID sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).OnCreate(sessionID);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handle_session_exception(ex, sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLogon(SessionID sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).OnLogon(sessionID);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handle_session_exception(ex, sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLogout(SessionID sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO stackoverflow?
|
||||
session(sessionID).OnLogout(sessionID);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handle_session_exception(ex, sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
public void ToAdmin(Message message, SessionID sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).ToAdmin(message, sessionID);
|
||||
}
|
||||
catch (FieldNotFoundException ex) { handle_field_not_found(ex, sessionID, message); }
|
||||
catch (UnsupportedMessageType) { throw; }
|
||||
catch (IncorrectTagValue) { throw; }
|
||||
catch (Exception ex) { handle_session_exception(ex, sessionID, message); }
|
||||
}
|
||||
|
||||
public void ToApp(Message message, SessionID sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).ToApp(message, sessionID);
|
||||
}
|
||||
catch (FieldNotFoundException ex) { handle_field_not_found(ex, sessionID, message); }
|
||||
catch (UnsupportedMessageType) { throw; }
|
||||
catch (IncorrectTagValue) { throw; }
|
||||
catch (Exception ex) { handle_session_exception(ex, sessionID, message); }
|
||||
}
|
||||
|
||||
IEnumerable<SecurityImmutable> IFixRouter.get_securities()
|
||||
{
|
||||
return _md.securities;
|
||||
}
|
||||
|
||||
SecurityImmutable? IFixRouter.lookup(uint key)
|
||||
{
|
||||
return _md.lookup(key);
|
||||
}
|
||||
|
||||
SecurityImmutable? IFixRouter.lookup(string desc)
|
||||
{
|
||||
if (_md.lookup_security_key_from_desc(desc, out uint key))
|
||||
return _md.lookup(key);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,585 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using QuickFix;
|
||||
|
||||
namespace bts.fix
|
||||
{
|
||||
public class HttpServer : IDisposable
|
||||
{
|
||||
private readonly HttpListener _httpListener;
|
||||
private Thread _connectionThread;
|
||||
private Boolean _running, _disposed;
|
||||
private readonly SessionSettings _sessionSettings;
|
||||
private StringBuilder _sbHtmlHeader;
|
||||
|
||||
|
||||
public HttpServer(string prefix, SessionSettings settings)
|
||||
{
|
||||
if (!HttpListener.IsSupported)
|
||||
{
|
||||
// Requires at least a Windows XP with Service Pack 2
|
||||
throw new NotSupportedException(
|
||||
"The Http Server cannot run on this operating system.");
|
||||
}
|
||||
|
||||
_httpListener = new HttpListener();
|
||||
_httpListener.Prefixes.Add(prefix);
|
||||
_sessionSettings = settings;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (!_httpListener.IsListening)
|
||||
{
|
||||
_httpListener.Start();
|
||||
_running = true;
|
||||
// Use a thread to listen to the Http requests
|
||||
_connectionThread = new Thread(ConnectionThreadStart);
|
||||
_connectionThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_httpListener.IsListening)
|
||||
{
|
||||
_running = false;
|
||||
_httpListener.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectionThreadStart()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
// Grab the context and pass it to the processor methods to handle it
|
||||
HttpListenerContext context = _httpListener.GetContext();
|
||||
HttpListenerRequest request = context.Request;
|
||||
HttpListenerResponse response = context.Response;
|
||||
string responseString;
|
||||
|
||||
_sbHtmlHeader = new StringBuilder(@"<HEAD><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>");
|
||||
_sbHtmlHeader.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>] [<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEADER><BODY>", request.Url.OriginalString);
|
||||
|
||||
switch (request.Url.AbsolutePath)
|
||||
{
|
||||
case "/":
|
||||
responseString = ProcessRoot(request);
|
||||
break;
|
||||
case "/session":
|
||||
responseString = SessionDetails(request);
|
||||
break;
|
||||
case "/resetSession":
|
||||
responseString = ResetSession(request);
|
||||
break;
|
||||
case "/resetSessions":
|
||||
responseString = ResetSessions(request);
|
||||
break;
|
||||
case "/refreshSession":
|
||||
responseString = RefreshSession(request);
|
||||
break;
|
||||
case "/refreshSessions":
|
||||
responseString = RefreshSessions(request);
|
||||
break;
|
||||
case "/enableSessions":
|
||||
responseString = EnableSessions(request);
|
||||
break;
|
||||
case "/disableSessions":
|
||||
responseString = DisableSessions(request);
|
||||
break;
|
||||
default:
|
||||
responseString = ProcessRoot(request);
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
|
||||
response.ContentLength64 = buffer.Length;
|
||||
Stream output = response.OutputStream;
|
||||
output.Write(buffer, 0, buffer.Length);
|
||||
output.Close();
|
||||
}
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{
|
||||
Console.WriteLine("HTTP server was shut down.");
|
||||
}
|
||||
}
|
||||
|
||||
private string DisableSessions(HttpListenerRequest request)
|
||||
{
|
||||
bool confirm = false;
|
||||
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
|
||||
|
||||
if (request.QueryString["confirm"] != null)
|
||||
{
|
||||
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
|
||||
{
|
||||
confirm = true;
|
||||
foreach (SessionID session in _sessionSettings.GetSessions())
|
||||
{
|
||||
Session sessionDetails = Session.LookupSession(session);
|
||||
sessionDetails.Logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (confirm)
|
||||
{
|
||||
sbHtmlPageBody = new StringBuilder();
|
||||
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been disabled</h2></CENTER>", "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to disable all sessions ?</h2></CENTER>");
|
||||
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, disable sessions</A>] [<A HREF='/'>NO, do not disable sessions</A>]</CENTER>", request.Url.OriginalString);
|
||||
}
|
||||
return sbHtmlPageBody.ToString();
|
||||
}
|
||||
|
||||
private string EnableSessions(HttpListenerRequest request)
|
||||
{
|
||||
bool confirm = false;
|
||||
string urlOriginalString = request.Url.OriginalString;
|
||||
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
|
||||
|
||||
if (request.QueryString["confirm"] != null)
|
||||
{
|
||||
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
|
||||
{
|
||||
confirm = true;
|
||||
foreach (SessionID session in _sessionSettings.GetSessions())
|
||||
{
|
||||
Session sessionDetails = Session.LookupSession(session);
|
||||
sessionDetails.Logon();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (confirm)
|
||||
{
|
||||
sbHtmlPageBody = new StringBuilder();
|
||||
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been enabled</h2></CENTER>", "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to enable all sessions ?</h2></CENTER>");
|
||||
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, enable sessions</A>] [<A HREF='/'>NO, do not enable sessions</A>]</CENTER>", urlOriginalString);
|
||||
}
|
||||
return sbHtmlPageBody.ToString();
|
||||
}
|
||||
|
||||
private string RefreshSession(HttpListenerRequest request)
|
||||
{
|
||||
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
|
||||
Session sessionDetails = Session.LookupSession(sessionId);
|
||||
if (sessionDetails == null) throw new Exception("Session not found");
|
||||
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
|
||||
bool confirm = false;
|
||||
string urlOriginalString = request.Url.OriginalString;
|
||||
|
||||
string url = "/session?" + GetParameterList(urlOriginalString);
|
||||
|
||||
if (request.QueryString["confirm"] != null)
|
||||
{
|
||||
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
|
||||
{
|
||||
confirm = true;
|
||||
sessionDetails.Refresh();
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "confirm");
|
||||
}
|
||||
}
|
||||
|
||||
if (confirm)
|
||||
{
|
||||
sbHtmlPageBody = new StringBuilder();
|
||||
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL=/session?{0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", GetParameterList(urlOriginalString));
|
||||
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>] [<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEAD><BODY>", urlOriginalString);
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='/session?{0}'>{1}</A> has been refreshed</h2></CENTER>", GetParameterList(url), sessionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><h2>Are you sure you want to refresh session <A HREF='{0}'>{1}</A>?</h2></CENTER>", url, sessionId);
|
||||
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}&confirm=1'>YES, refresh session</A>] [<A HREF='{0}'>NO, do not refresh session</A>]</CENTER>", url);
|
||||
}
|
||||
return sbHtmlPageBody.ToString();
|
||||
}
|
||||
|
||||
private string RefreshSessions(HttpListenerRequest request)
|
||||
{
|
||||
bool confirm = false;
|
||||
string urlOriginalString = request.Url.OriginalString;
|
||||
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
|
||||
|
||||
if (request.QueryString["confirm"] != null)
|
||||
{
|
||||
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
|
||||
{
|
||||
confirm = true;
|
||||
foreach (SessionID session in _sessionSettings.GetSessions())
|
||||
{
|
||||
Session sessionDetails = Session.LookupSession(session);
|
||||
sessionDetails.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (confirm)
|
||||
{
|
||||
sbHtmlPageBody = new StringBuilder();
|
||||
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been refreshed</h2></CENTER>", "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to refresh all sessions ?</h2></CENTER>");
|
||||
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, refresh sessions</A>] [<A HREF='/'>NO, do not refresh sessions</A>]</CENTER>", urlOriginalString);
|
||||
}
|
||||
return sbHtmlPageBody.ToString();
|
||||
}
|
||||
|
||||
private string ResetSessions(HttpListenerRequest request)
|
||||
{
|
||||
bool confirm = false;
|
||||
string urlOriginalString = request.Url.OriginalString;
|
||||
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
|
||||
|
||||
if (request.QueryString["confirm"] != null)
|
||||
{
|
||||
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
|
||||
{
|
||||
confirm = true;
|
||||
foreach (SessionID session in _sessionSettings.GetSessions())
|
||||
{
|
||||
Session sessionDetails = Session.LookupSession(session);
|
||||
sessionDetails.Reset("Reset from WebInterface");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (confirm)
|
||||
{
|
||||
sbHtmlPageBody = new StringBuilder();
|
||||
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been reset</h2></CENTER>", "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to reset all sessions ?</h2></CENTER>");
|
||||
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, reset sessions</A>] [<A HREF='/'>NO, do not reset sessions</A>]</CENTER>", urlOriginalString);
|
||||
}
|
||||
return sbHtmlPageBody.ToString();
|
||||
}
|
||||
|
||||
private string ResetSession(HttpListenerRequest request)
|
||||
{
|
||||
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
|
||||
Session sessionDetails = Session.LookupSession(sessionId);
|
||||
if (sessionDetails == null) throw new Exception("Session not found");
|
||||
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
|
||||
|
||||
bool confirm = false;
|
||||
string urlOriginalString = request.Url.OriginalString;
|
||||
|
||||
string url = "/session?" + GetParameterList(urlOriginalString);
|
||||
|
||||
if (request.QueryString["confirm"] != null)
|
||||
{
|
||||
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
|
||||
{
|
||||
confirm = true;
|
||||
sessionDetails.Reset("Reset from WebInterface");
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "confirm");
|
||||
}
|
||||
}
|
||||
|
||||
if (confirm)
|
||||
{
|
||||
sbHtmlPageBody = new StringBuilder();
|
||||
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL=/session?{0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", GetParameterList(urlOriginalString));
|
||||
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>] [<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEAD><BODY>", urlOriginalString);
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='/session?{0}'>{1}</A> has been reset</h2></CENTER>", GetParameterList(url), sessionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><h2>Are you sure you want to reset session <A HREF='{0}'>{1}</A>?</h2></CENTER>", url, sessionId);
|
||||
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}&confirm=1'>YES, reset session</A>] [<A HREF='{0}'>NO, do not reset session</A>]</CENTER>", url);
|
||||
}
|
||||
return sbHtmlPageBody.ToString();
|
||||
}
|
||||
|
||||
|
||||
private string ProcessRoot(HttpListenerRequest request)
|
||||
{
|
||||
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
|
||||
|
||||
//Session count
|
||||
sbHtmlPageBody.AppendFormat("<CENTER><b><i>{0} Sessions managed by QuickFIX</b></i></CENTER><HR/>", _sessionSettings.GetSessions().Count);
|
||||
|
||||
//session management links
|
||||
sbHtmlPageBody.AppendFormat(@"<CENTER><A HREF='/resetSessions{0}'>RESET</A> <A HREF='/refreshSessions'>REFRESH</A> <A HREF='/enableSessions'>ENABLE</A> <A HREF='/disableSessions'>DISABLE</A></CENTER><HR/></HEADER><BODY>", GetParameterList(request.Url.OriginalString));
|
||||
|
||||
//Start the table generation
|
||||
sbHtmlPageBody.Append("<table id=\"sessionlist\" style=\"border-width:1; padding:2; width:100%\">");
|
||||
|
||||
sbHtmlPageBody.Append("<tr>");
|
||||
sbHtmlPageBody.Append(AddCell("Session", true));
|
||||
sbHtmlPageBody.Append(AddCell("Type", true));
|
||||
sbHtmlPageBody.Append(AddCell("Enabled", true));
|
||||
sbHtmlPageBody.Append(AddCell("Session Time", true));
|
||||
sbHtmlPageBody.Append(AddCell("Logged On", true));
|
||||
sbHtmlPageBody.Append(AddCell("Next Incoming", true));
|
||||
sbHtmlPageBody.Append(AddCell("Next Outgoing", true));
|
||||
sbHtmlPageBody.Append("</tr>");
|
||||
|
||||
foreach (SessionID session in _sessionSettings.GetSessions())
|
||||
{
|
||||
Session sessionDetails = Session.LookupSession(session);
|
||||
sbHtmlPageBody.Append("<tr>");
|
||||
sbHtmlPageBody.Append(AddCell(String.Format(
|
||||
"<a href=\"session?BeginString={0}&SenderCompID={1}&TargetCompID={2}\">{0}:{1}->{2}</a>",
|
||||
session.BeginString, session.SenderCompID, session.TargetCompID)));
|
||||
sbHtmlPageBody.Append(AddCell(sessionDetails.IsInitiator ? "initiator" : "acceptor"));
|
||||
sbHtmlPageBody.Append(AddCell(sessionDetails.IsEnabled ? "yes" : "no"));
|
||||
sbHtmlPageBody.Append(AddCell(sessionDetails.IsSessionTime ? "yes" : "no"));
|
||||
sbHtmlPageBody.Append(AddCell(sessionDetails.IsLoggedOn ? "yes" : "no"));
|
||||
sbHtmlPageBody.Append(AddCell(sessionDetails.NextTargetMsgSeqNum.ToString()));
|
||||
sbHtmlPageBody.Append(AddCell(sessionDetails.NextSenderMsgSeqNum.ToString()));
|
||||
sbHtmlPageBody.Append("</tr>");
|
||||
}
|
||||
|
||||
sbHtmlPageBody.Append("</table>");
|
||||
return sbHtmlPageBody.ToString();
|
||||
}
|
||||
|
||||
public string SessionDetails(HttpListenerRequest request)
|
||||
{
|
||||
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
|
||||
Session sessionDetails = Session.LookupSession(sessionId);
|
||||
if (sessionDetails == null) throw new Exception("Session not found");
|
||||
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
|
||||
|
||||
string url = request.Url.OriginalString;
|
||||
string urlOriginalString = request.Url.OriginalString;
|
||||
|
||||
if (request.QueryString["enabled"] != null)
|
||||
{
|
||||
if (!Convert.ToBoolean(request.QueryString["enabled"]))
|
||||
sessionDetails.Logout();
|
||||
else
|
||||
sessionDetails.Logon();
|
||||
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "Enabled");
|
||||
}
|
||||
|
||||
if (request.QueryString["next incoming"] != null)
|
||||
{
|
||||
int value = Convert.ToInt16(request.QueryString["next incoming"]);
|
||||
sessionDetails.NextTargetMsgSeqNum = value <= 0 ? 1 : value;
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "next incoming");
|
||||
}
|
||||
|
||||
if (request.QueryString["Next Outgoing"] != null)
|
||||
{
|
||||
int value = Convert.ToInt16(request.QueryString["Next Outgoing"]);
|
||||
sessionDetails.NextSenderMsgSeqNum = value <= 0 ? 1 : value;
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "Next Outgoing");
|
||||
}
|
||||
|
||||
if (request.QueryString["SendRedundantResendRequests"] != null)
|
||||
{
|
||||
sessionDetails.SendRedundantResendRequests = Convert.ToBoolean(request.QueryString["SendRedundantResendRequests"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "SendRedundantResendRequests");
|
||||
}
|
||||
|
||||
if (request.QueryString["CheckCompId"] != null)
|
||||
{
|
||||
sessionDetails.CheckCompID = Convert.ToBoolean(request.QueryString["CheckCompId"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "CheckCompId");
|
||||
}
|
||||
|
||||
if (request.QueryString["CheckLatency"] != null)
|
||||
{
|
||||
sessionDetails.CheckLatency = Convert.ToBoolean(request.QueryString["CheckLatency"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "CheckLatency");
|
||||
}
|
||||
|
||||
if (request.QueryString["MaxLatency"] != null)
|
||||
{
|
||||
int value = Convert.ToInt16(request.QueryString["MaxLatency"]);
|
||||
sessionDetails.MaxLatency = value <= 0 ? 1 : value;
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "MaxLatency");
|
||||
}
|
||||
|
||||
if (request.QueryString["LogonTimeout"] != null)
|
||||
{
|
||||
int value = Convert.ToInt16(request.QueryString["LogonTimeout"]);
|
||||
sessionDetails.LogonTimeout = value <= 0 ? 1 : value;
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "LogonTimeout");
|
||||
}
|
||||
|
||||
if (request.QueryString["LogoutTimeout"] != null)
|
||||
{
|
||||
int value = Convert.ToInt16(request.QueryString["LogoutTimeout"]);
|
||||
sessionDetails.LogoutTimeout = value <= 0 ? 1 : value;
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "LogoutTimeout");
|
||||
}
|
||||
|
||||
if (request.QueryString["ResetOnLogon"] != null)
|
||||
{
|
||||
sessionDetails.ResetOnLogon = Convert.ToBoolean(request.QueryString["ResetOnLogon"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnLogon");
|
||||
}
|
||||
|
||||
if (request.QueryString["ResetOnLogout"] != null)
|
||||
{
|
||||
sessionDetails.ResetOnLogout = Convert.ToBoolean(request.QueryString["ResetOnLogout"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnLogout");
|
||||
}
|
||||
|
||||
if (request.QueryString["ResetOnDisconnect"] != null)
|
||||
{
|
||||
sessionDetails.ResetOnDisconnect = Convert.ToBoolean(request.QueryString["ResetOnDisconnect"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnDisconnect");
|
||||
}
|
||||
|
||||
if (request.QueryString["RefreshOnLogon"] != null)
|
||||
{
|
||||
sessionDetails.RefreshOnLogon = Convert.ToBoolean(request.QueryString["RefreshOnLogon"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "RefreshOnLogon");
|
||||
}
|
||||
|
||||
if (request.QueryString["MillisecondsInTimestamp"] != null)
|
||||
{
|
||||
sessionDetails.MillisecondsInTimeStamp = Convert.ToBoolean(request.QueryString["MillisecondsInTimestamp"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "MillisecondsInTimestamp");
|
||||
}
|
||||
|
||||
if (request.QueryString["PersistMessages"] != null)
|
||||
{
|
||||
sessionDetails.PersistMessages = Convert.ToBoolean(request.QueryString["PersistMessages"]);
|
||||
url = RemoveQueryStringByKey(urlOriginalString, "PersistMessages");
|
||||
}
|
||||
|
||||
|
||||
sbHtmlPageBody.AppendFormat(@"<CENTER>{0}</CENTER><HR/>", sessionId);
|
||||
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/resetSession?{0}'>RESET</A>] [<A HREF='/refreshSession?{0}'>REFRESH</A>]</CENTER><HR/></HEADER><BODY>", GetParameterList(urlOriginalString));
|
||||
|
||||
sbHtmlPageBody.Append("<table id=\"session_details\" style=\"border-width:1; padding:2; width:100%\">");
|
||||
|
||||
sbHtmlPageBody.Append(AddRow("Enabled", sessionDetails.IsEnabled, url));
|
||||
sbHtmlPageBody.Append(AddRow("ConnectionType", sessionDetails.IsInitiator ? "initiator" : "acceptor"));
|
||||
sbHtmlPageBody.Append(AddRow("SessionTime", sessionDetails.IsSessionTime));
|
||||
sbHtmlPageBody.Append(AddRow("LoggedOn", sessionDetails.IsLoggedOn));
|
||||
sbHtmlPageBody.Append(AddRow("Next Incoming", sessionDetails.NextTargetMsgSeqNum, url));
|
||||
sbHtmlPageBody.Append(AddRow("Next Outgoing", sessionDetails.NextSenderMsgSeqNum, url));
|
||||
sbHtmlPageBody.Append(AddRow("SendRedundantResendRequests", sessionDetails.SendRedundantResendRequests, url));
|
||||
sbHtmlPageBody.Append(AddRow("CheckCompId", sessionDetails.CheckCompID, url));
|
||||
sbHtmlPageBody.Append(AddRow("CheckLatency", sessionDetails.CheckLatency, url));
|
||||
sbHtmlPageBody.Append(AddRow("MaxLatency", sessionDetails.MaxLatency, url));
|
||||
sbHtmlPageBody.Append(AddRow("LogonTimeout", sessionDetails.LogonTimeout, url));
|
||||
sbHtmlPageBody.Append(AddRow("LogoutTimeout", sessionDetails.LogoutTimeout, url));
|
||||
sbHtmlPageBody.Append(AddRow("ResetOnLogon", sessionDetails.ResetOnLogon, url));
|
||||
sbHtmlPageBody.Append(AddRow("ResetOnLogout", sessionDetails.ResetOnLogout, url));
|
||||
sbHtmlPageBody.Append(AddRow("ResetOnDisconnect", sessionDetails.ResetOnDisconnect, url));
|
||||
sbHtmlPageBody.Append(AddRow("RefreshOnLogon", sessionDetails.RefreshOnLogon, url));
|
||||
sbHtmlPageBody.Append(AddRow("MillisecondsInTimestamp", sessionDetails.MillisecondsInTimeStamp, url));
|
||||
sbHtmlPageBody.Append(AddRow("PersistMessages", sessionDetails.PersistMessages, url));
|
||||
|
||||
sbHtmlPageBody.Append("</table>");
|
||||
return sbHtmlPageBody.ToString();
|
||||
}
|
||||
|
||||
public static string RemoveQueryStringByKey(string url, string key)
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
|
||||
// this gets all the query string key value pairs as a collection
|
||||
var newQueryString = HttpUtility.ParseQueryString(uri.Query);
|
||||
|
||||
// this removes the key if exists
|
||||
newQueryString.Remove(key);
|
||||
|
||||
// this gets the page path from root without QueryString
|
||||
string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path);
|
||||
|
||||
return newQueryString.Count > 0
|
||||
? $"{pagePathWithoutQueryString}?{newQueryString}"
|
||||
: pagePathWithoutQueryString;
|
||||
}
|
||||
|
||||
public static string GetParameterList(string url)
|
||||
{
|
||||
return HttpUtility.ParseQueryString((new Uri(url).Query)).ToString();
|
||||
}
|
||||
|
||||
private static string AddRow(string colName, bool value, string url = "")
|
||||
{
|
||||
string valueAsStr = value ? "yes" : "no";
|
||||
string innerHtml = url.Length > 0
|
||||
? $"<a href=\" {url}&{colName}={!value} \">toggle</a>"
|
||||
: "";
|
||||
return AddRow(colName, valueAsStr, innerHtml);
|
||||
}
|
||||
|
||||
private static string AddRow(string colName, int value, string url = "")
|
||||
{
|
||||
string innerHtml = $"<a href=\" {url}&{colName}={value - 10} \"> << </a>" +
|
||||
$"<a href=\" {url}&{colName}={value - 1} \"> < </a>" +
|
||||
" | " +
|
||||
$"<a href=\" {url}&{colName}={value + 1} \"> > </a>" +
|
||||
$"<a href=\" {url}&{colName}={value + 10} \"> >> </a>";
|
||||
return AddRow(colName, value.ToString(), innerHtml);
|
||||
}
|
||||
private static string AddRow(string colName, string value, string innerHtml = "")
|
||||
{
|
||||
StringBuilder row = new StringBuilder();
|
||||
row.Append("<tr>");
|
||||
row.Append(AddCell(colName));
|
||||
row.Append(AddCell(value));
|
||||
row.Append(AddCell(innerHtml));
|
||||
row.Append("</tr>");
|
||||
return row.ToString();
|
||||
}
|
||||
|
||||
private static string AddCell(string cellContent, bool header = false)
|
||||
{
|
||||
string entryType = header ? "th" : "td";
|
||||
return "<" + entryType + " align=\"left\">" + cellContent + "</" + entryType + ">";
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (disposing)
|
||||
{
|
||||
if (_running)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
if (_connectionThread != null)
|
||||
{
|
||||
_connectionThread.Abort();
|
||||
_connectionThread = null;
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# BTS Spark FIX Adapter
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Text;
|
||||
|
||||
using QuickFix;
|
||||
using QuickFix.Fields;
|
||||
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.api.user;
|
||||
using bts.api.admin;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace bts.fix
|
||||
{
|
||||
class DropCopySession : SessionBase
|
||||
{
|
||||
protected IDisposable _fillsdisp;
|
||||
protected IDisposable _ordersdisp;
|
||||
protected static Lazy<OrderService> _orderservice = new Lazy<OrderService>(() => bts.api.Locator.locate<OrderService>(), true);
|
||||
|
||||
protected readonly ConcurrentDictionary<uint, OrderRecordImmutable> _order_cache = new ConcurrentDictionary<uint, OrderRecordImmutable>();
|
||||
protected readonly ConcurrentDictionary<uint, string> _fix_clorderids = new ConcurrentDictionary<uint, string>();
|
||||
public DropCopySession(IFixRouter router, SessionID sess) : base(router, sess)
|
||||
{
|
||||
}
|
||||
|
||||
override public void OnLogon(SessionID sessionID)
|
||||
{
|
||||
Log.assert_d(sessionID.Equals(_sess));
|
||||
var msg = sessionID.ToString() + " Logged on";
|
||||
log_info(msg);
|
||||
|
||||
subscribe_fills_and_orders(sessionID);
|
||||
}
|
||||
override public void OnLogout(SessionID sessionID)
|
||||
{
|
||||
Log.assert_d(sessionID.Equals(_sess));
|
||||
var msg = sessionID.ToString() + " Logged out";
|
||||
log_info(msg);
|
||||
|
||||
if (_ordersdisp != null)
|
||||
_ordersdisp.Dispose();
|
||||
if (_fillsdisp != null)
|
||||
_fillsdisp.Dispose();
|
||||
}
|
||||
|
||||
override public void FromApp(Message message, SessionID sessionID)
|
||||
{
|
||||
Log.assert_d(sessionID.Equals(_sess));
|
||||
MsgType type = Message.IdentifyType(message.ToString());
|
||||
switch (type.getValue())
|
||||
{
|
||||
case MsgType.ORDER_STATUS_REQUEST:
|
||||
case MsgType.SECURITYDEFINITIONREQUEST:
|
||||
log_info("IN: " + message);
|
||||
Crack(message, sessionID);
|
||||
break;
|
||||
default:
|
||||
log_info("UNSUPPORTED MESSAGE IN: " + type.getValue());
|
||||
throw new UnsupportedMessageType();
|
||||
}
|
||||
}
|
||||
|
||||
virtual public void OnMessage(QuickFix.FIX44.OrderStatusRequest req, SessionID s)
|
||||
{
|
||||
uint oid = uint.Parse(req.OrderID.getValue());
|
||||
if (_order_cache.TryGetValue(oid, out OrderRecordImmutable order))
|
||||
{
|
||||
var ostat = build_order_status(s, order);
|
||||
ostat.OrdStatusReqID = new OrdStatusReqID(req.OrdStatusReqID.getValue());
|
||||
_router.send(ostat, s);
|
||||
}
|
||||
}
|
||||
|
||||
virtual public void OnMessage(QuickFix.FIX44.SecurityDefinitionRequest req, SessionID s)
|
||||
{
|
||||
if (req.SecurityRequestType.getValue() == SecurityRequestType.REQUEST_LIST_SECURITIES)
|
||||
{
|
||||
var secs = _router.get_securities();
|
||||
if (req.IsSetSymbol())
|
||||
secs = secs.Where(x => x.Symbol.ToLower() == req.Symbol.getValue().ToLower());
|
||||
if (req.IsSetSecurityType())
|
||||
secs = secs.Where(x => security_types_equal(x.security_type(), req.SecurityType));
|
||||
if (req.IsSetSecurityExchange())
|
||||
secs = secs.Where(x => exchanges_equal(x.Exchange, req.SecurityExchange));
|
||||
|
||||
var def = new QuickFix.FIX44.SecurityDefinition(req.SecurityReqID,
|
||||
new SecurityResponseID(Guid.NewGuid().ToString()),
|
||||
new SecurityResponseType(SecurityResponseType.LIST_OF_SECURITIES_RETURNED_PER_REQUEST));
|
||||
_router.send(def, s);
|
||||
}
|
||||
else
|
||||
throw new IncorrectTagValue(req.SecurityRequestType.Tag);
|
||||
}
|
||||
|
||||
|
||||
|
||||
virtual protected void subscribe_fills_and_orders(SessionID sessionID)
|
||||
{
|
||||
var scheduler = TaskPoolScheduler.Default;
|
||||
var interval = TimeSpan.FromMilliseconds(500);
|
||||
var on_orders = _orderservice.Value.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(_adminobservable).Table().Cache();
|
||||
var on_fills = _orderservice.Value.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(_adminobservable).Table().Cache();
|
||||
|
||||
_ordersdisp = on_orders.Subscribe(o =>
|
||||
{
|
||||
foreach (var order in o.added)
|
||||
{
|
||||
var ostat = build_order_status(sessionID, order);
|
||||
_order_cache[order.Key] = order;
|
||||
_router.send(ostat, sessionID);
|
||||
}
|
||||
foreach (var order in o.changed)
|
||||
{
|
||||
var ostat = build_order_status(sessionID, order, true);
|
||||
_router.send(ostat, sessionID);
|
||||
}
|
||||
});
|
||||
|
||||
_fillsdisp = on_fills.Subscribe(f =>
|
||||
{
|
||||
foreach (var fill in f.added)
|
||||
{
|
||||
var fi = build_fill(sessionID, fill);
|
||||
_router.send(fi, sessionID);
|
||||
}
|
||||
foreach (var fill in f.changed)
|
||||
{
|
||||
var fi = build_fill(sessionID, fill, true);
|
||||
_router.send(fi, sessionID);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
virtual protected void process_order_state(in OrderStateImmutable state, ref uint filled, ref uint leaves, ref double avgprx,
|
||||
ref QuickFix.Fields.OrdStatus fstatus, ref NativeOrderStateImmutable? native, FillImmutable? fill = null)
|
||||
{
|
||||
filled = state.FilledQuantity;
|
||||
leaves = state.Quantity - state.FilledQuantity;
|
||||
avgprx = state.AvgPrice;
|
||||
fstatus = translate_status(state, ref native);
|
||||
}
|
||||
|
||||
protected bts.api.SecurityImmutable? resolve_execution_report_security(bts.api.SecurityImmutable? sec, QuickFix.FIX44.ExecutionReport report)
|
||||
{
|
||||
report.SecurityExchange = convert_exchange(sec.Value.Exchange);
|
||||
var sectype = sec.Value.security_type();
|
||||
report.SecurityType = convert_security_type(sectype);
|
||||
if (sec.Value.HasGlbx)
|
||||
report.SecurityDesc = new SecurityDesc(sec.Value.Glbx.SecurityDesc);
|
||||
|
||||
switch (sectype)
|
||||
{
|
||||
case bts.api.SecurityType.Future:
|
||||
report.MaturityMonthYear = new MaturityMonthYear(sec.Value.EarliestExpiration().ToString("yyyyMMdd"));
|
||||
break;
|
||||
case bts.api.SecurityType.Spread: //TODO this is not quite right for spreads of spreads
|
||||
var leg = sec.Value.Spread.Legs.OrderBy(x => x.Security.EarliestExpiration()).First();
|
||||
report.MaturityMonthYear = new MaturityMonthYear(leg.Security.EarliestExpiration().ToString("yyyyMMdd"));
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedMessageType();
|
||||
}
|
||||
return sec;
|
||||
}
|
||||
|
||||
virtual protected QuickFix.FIX44.ExecutionReport build_fill(SessionID sess, in FillImmutable fill, bool change = false)
|
||||
{
|
||||
var exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.PARTIAL_FILL);
|
||||
QuickFix.Fields.OrdStatus status = new QuickFix.Fields.OrdStatus();
|
||||
uint filled = 0; uint leaves = 0; double prx = 0.0;
|
||||
OrderRecordImmutable order; NativeOrderStateImmutable? native = null;
|
||||
if (_order_cache.TryGetValue(fill.OrderKey, out order))
|
||||
{
|
||||
process_order_state(order.State, ref filled, ref leaves, ref prx, ref status, ref native);
|
||||
if(Convert.ToDouble(order.State.Quantity) == fill.LastShares)
|
||||
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.FILL);
|
||||
}
|
||||
if (change)
|
||||
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.TRADE_CORRECT);
|
||||
|
||||
var sec = _router.lookup(fill.SecurityKey);
|
||||
QuickFix.FIX44.ExecutionReport report = new QuickFix.FIX44.ExecutionReport(
|
||||
new OrderID(fill.OrderKey.ToString()),
|
||||
new ExecID(fill.FillKey.ToString()),
|
||||
exec,
|
||||
status,
|
||||
new Symbol(sec?.Symbol ?? string.Empty),
|
||||
new QuickFix.Fields.Side(fill.Side == bts.api.Side.Buy ? QuickFix.Fields.Side.BUY : QuickFix.Fields.Side.SELL),
|
||||
new LeavesQty(leaves),
|
||||
new CumQty(filled),
|
||||
new AvgPx(Convert.ToDecimal(prx)));
|
||||
|
||||
report.Account = new Account(fill.Account);
|
||||
report.LastQty = new LastQty(Convert.ToDecimal(fill.LastShares));
|
||||
report.LastPx = new LastPx(Convert.ToDecimal(fill.LastPrice));
|
||||
var ts = fill.HasTimestamp ? fill.Timestamp.ToDateTime() : DateTime.MinValue;
|
||||
report.TransactTime = new TransactTime(ts, true);
|
||||
report.TradeDate = new TradeDate(ts.ToString("yyyyMMdd"));
|
||||
sec = resolve_execution_report_security(sec, report);
|
||||
if (native.HasValue && !string.IsNullOrEmpty(native.Value.ExchangeOrderId))
|
||||
report.SecondaryOrderID = new SecondaryOrderID(native.Value.ExchangeOrderId);
|
||||
|
||||
if (sec.HasValue && sec.Value.security_type() == bts.api.SecurityType.Spread ||
|
||||
sec.Value.security_type() == bts.api.SecurityType.Synthetic)
|
||||
{
|
||||
if (fill.FillKind == bts.api.orderman.Fill.Types.FillKind.Leg)
|
||||
report.MultiLegReportingType = new MultiLegReportingType('2');
|
||||
else
|
||||
report.MultiLegReportingType = new MultiLegReportingType('3');
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
virtual protected QuickFix.FIX44.ExecutionReport build_order_status(SessionID sess, in OrderRecordImmutable order, bool change = false)
|
||||
{
|
||||
var exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.NEW);
|
||||
if (_order_cache.TryGetValue(order.Key, out OrderRecordImmutable _))
|
||||
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.ORDER_STATUS);
|
||||
|
||||
QuickFix.Fields.OrdStatus status = new QuickFix.Fields.OrdStatus();
|
||||
uint filled = 0; uint leaves = 0; double prx = 0.0; NativeOrderStateImmutable? native = null;
|
||||
process_order_state(order.State, ref filled, ref leaves, ref prx, ref status, ref native);
|
||||
|
||||
var sec = _router.lookup(order.Def.SecurityKey);
|
||||
QuickFix.FIX44.ExecutionReport report = new QuickFix.FIX44.ExecutionReport(
|
||||
new OrderID(order.Key.ToString()),
|
||||
new ExecID("0"),
|
||||
exec,
|
||||
status,
|
||||
new Symbol(sec?.Symbol ?? string.Empty),
|
||||
new QuickFix.Fields.Side(order.Def.Side == bts.api.Side.Buy ? QuickFix.Fields.Side.BUY : QuickFix.Fields.Side.SELL),
|
||||
new LeavesQty(leaves),
|
||||
new CumQty(filled),
|
||||
new AvgPx(Convert.ToDecimal(prx)));
|
||||
|
||||
report.Account = new Account(order.Def.Account);
|
||||
report.OrderQty = new OrderQty(order.State.Quantity);
|
||||
report.Price = new Price(Convert.ToDecimal(order.State.Price));
|
||||
report.TimeInForce = new QuickFix.Fields.TimeInForce((char)order.Def.TimeInForce);
|
||||
var ts = order.State.LastChange.ToDateTime();
|
||||
report.TransactTime = new TransactTime(ts, true);
|
||||
if(!string.IsNullOrEmpty(order.State.LastMsg))
|
||||
report.Text = new Text(order.State.LastMsg);
|
||||
|
||||
sec = resolve_execution_report_security(sec, report);
|
||||
if (native.HasValue)
|
||||
{
|
||||
report.OrdType = new OrdType((char)native.Value.NativeType);
|
||||
if(!string.IsNullOrEmpty(native.Value.ExchangeOrderId))
|
||||
report.SecondaryOrderID = new SecondaryOrderID(native.Value.ExchangeOrderId);
|
||||
}
|
||||
//TODO Big race here, we need a correlation id of some sort
|
||||
if (_fix_clorderids.TryGetValue(order.Key, out string clorderid))
|
||||
{
|
||||
report.ClOrdID = new ClOrdID(clorderid);
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace bts.fix
|
||||
{
|
||||
interface IFIXSession : QuickFix.IApplication
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using QuickFix;
|
||||
using QuickFix.Fields;
|
||||
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
|
||||
namespace bts.fix
|
||||
{
|
||||
/* Some test fix
|
||||
order for 4 lot CLZ8
|
||||
8=FIX.4.49=15635=D49=SPARK56=CLIENT134=150=3073797=Y52=20000809-20:20:5011=900010081=ACC A21=255=SIM:CLZ854=138=440=259=044=7460=20000809-18:20:32207=SIM10=045
|
||||
|
||||
order for 4 lot CLZ8-CLH9
|
||||
8=FIX.4.49=15635=D49=SPARK56=CLIENT134=150=3073797=Y52=20000809-20:20:5011=900010081=ACC A21=255=SIM:CLZ8-CLH954=138=440=259=044=7460=20000809-18:20:32207=SIM10=045
|
||||
|
||||
*/
|
||||
|
||||
class OrderSession : DropCopySession
|
||||
{
|
||||
public OrderSession(IFixRouter router, SessionID sess) : base(router, sess)
|
||||
{
|
||||
}
|
||||
|
||||
override public void FromApp(Message message, SessionID sessionID)
|
||||
{
|
||||
Log.assert_d(sessionID.Equals(_sess));
|
||||
MsgType type = Message.IdentifyType(message.ToString());
|
||||
switch (type.getValue())
|
||||
{
|
||||
case MsgType.ORDER_STATUS_REQUEST: //handled in DropCopySession
|
||||
case MsgType.SECURITYDEFINITIONREQUEST: //handled in DropCopySession
|
||||
|
||||
case MsgType.NEWORDERSINGLE:
|
||||
case MsgType.ORDERCANCELREPLACEREQUEST:
|
||||
case MsgType.ORDERCANCELREQUEST:
|
||||
log_info("IN: " + message);
|
||||
Crack(message, sessionID);
|
||||
break;
|
||||
default:
|
||||
log_info("UNSUPPORTED MESSAGE IN: " + type.getValue());
|
||||
throw new UnsupportedMessageType();
|
||||
}
|
||||
}
|
||||
|
||||
virtual public void OnMessage(QuickFix.FIX44.OrderCancelRequest cancel, SessionID s)
|
||||
{
|
||||
var id = cancel.OrderID.getValue();
|
||||
if(!uint.TryParse(id, out uint key))
|
||||
throw new IncorrectTagValue(cancel.OrderID.Tag);
|
||||
var resp = _orderservice.Value.cancel_order(new Cancel { OrderKey = key });
|
||||
if (resp.ErrorCode != OrderError.Ok)
|
||||
{
|
||||
send_cancel_reject(s, "Cancel Rejected: " + resp.ErrorCode.ToString() + " - " + resp.Msg, cancel);
|
||||
}
|
||||
}
|
||||
|
||||
virtual public void OnMessage(QuickFix.FIX44.OrderCancelReplaceRequest replace, SessionID s)
|
||||
{
|
||||
var id = replace.OrderID.getValue();
|
||||
if (!uint.TryParse(id, out uint key))
|
||||
throw new IncorrectTagValue(replace.OrderID.Tag);
|
||||
double price = Convert.ToDouble(replace.Price.getValue());
|
||||
uint sz = Convert.ToUInt32(Math.Abs(replace.OrderQty.getValue()));
|
||||
var resp = _orderservice.Value.update_order(new Update
|
||||
{
|
||||
OrderKey = key,
|
||||
Price = price,
|
||||
Quantity = sz,
|
||||
});
|
||||
if (resp.ErrorCode != OrderError.Ok)
|
||||
{
|
||||
send_cancel_reject(s, "Cancel Rejected: " + resp.ErrorCode.ToString() + " - " + resp.Msg, replace);
|
||||
}
|
||||
}
|
||||
|
||||
virtual public void OnMessage(QuickFix.FIX44.NewOrderSingle create, SessionID s)
|
||||
{
|
||||
Venue ven = convert_exchange(create.SecurityExchange);
|
||||
if (ven == bts.api.Venue.Unset)
|
||||
throw new IncorrectTagValue(create.SecurityExchange.Tag);
|
||||
|
||||
var sec = _router.lookup(create.Symbol.getValue());
|
||||
if (!sec.HasValue)
|
||||
sec = _router.lookup(create.SecurityDesc.getValue());
|
||||
if (!sec.HasValue)
|
||||
throw new IncorrectTagValue(create.SecurityDesc.Tag);
|
||||
|
||||
OrderDefinition def = null;
|
||||
try
|
||||
{
|
||||
string manual = "Y";
|
||||
if(create.IsSetField(Tags.ManualOrderIndicator))
|
||||
manual = create.GetField(Tags.ManualOrderIndicator);
|
||||
def = new OrderDefinition()
|
||||
{
|
||||
Account = create.Account.getValue(),
|
||||
AccountId = lookup_account(create.Account.getValue()),
|
||||
SecurityKey = sec.Value.Id,
|
||||
Side = (api.Side)(int)create.Side.getValue(),
|
||||
TimeInForce = (api.orderman.TimeInForce)(int)create.TimeInForce.getValue(),
|
||||
User = _user_name,
|
||||
UserId = lookup_user(_user_name),
|
||||
ManualOrderIndicator = manual == "Y" ? bts.api.orderman.ManualOrderIndicator.Yes : bts.api.orderman.ManualOrderIndicator.No,
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new IncorrectTagValue(create.TimeInForce.Tag);
|
||||
}
|
||||
|
||||
set_ordertype(def, create, out bool price_req);
|
||||
Create cr = new Create();
|
||||
cr.NewOrder = def;
|
||||
try
|
||||
{
|
||||
if (price_req)
|
||||
cr.Price = Convert.ToDouble(create.Price.getValue());
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new IncorrectTagValue(create.Price.Tag);
|
||||
}
|
||||
try
|
||||
{
|
||||
cr.Quantity = Convert.ToUInt32(Math.Abs(create.OrderQty.getValue()));
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new IncorrectTagValue(create.OrderQty.Tag);
|
||||
}
|
||||
var resp = _orderservice.Value.create_order(cr);
|
||||
if (resp.ErrorCode != OrderError.Ok)
|
||||
{
|
||||
_router.send_biz_reject(s, "Order Rejected: " + resp.ErrorCode.ToString() + " - " + resp.Msg, create);
|
||||
}
|
||||
}
|
||||
|
||||
private void set_ordertype(OrderDefinition def, QuickFix.FIX44.NewOrderSingle req, out bool price_req)
|
||||
{
|
||||
price_req = false;
|
||||
switch (req.OrdType.getValue())
|
||||
{
|
||||
case '1':
|
||||
def.CustomDef = new CustomOrderDefinition { Market = new MarketOrderDefinition() };
|
||||
break;
|
||||
case '2':
|
||||
def.CustomDef = new CustomOrderDefinition { Limit = new LimitOrderDefinition() };
|
||||
price_req = true;
|
||||
break;
|
||||
case '3':
|
||||
def.CustomDef = new CustomOrderDefinition { Stop = new StopOrderDefinition() {StopPrice = Convert.ToDouble(req.StopPx.getValue()) } };
|
||||
break;
|
||||
case '4':
|
||||
def.CustomDef = new CustomOrderDefinition { StopLimit = new StopLimitOrderDefinition() { StopPrice = Convert.ToDouble(req.StopPx.getValue()) } };
|
||||
price_req = true;
|
||||
break;
|
||||
default:
|
||||
throw new IncorrectTagValue(req.OrdType.Tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Text;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using QuickFix;
|
||||
using QuickFix.Fields;
|
||||
|
||||
using mbts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.api.user;
|
||||
using bts.api.admin;
|
||||
|
||||
|
||||
namespace bts.fix
|
||||
{
|
||||
abstract class SessionBase : QuickFix.MessageCracker, IFIXSession
|
||||
{
|
||||
protected static AuthService _authservice = bts.api.Locator.locate<AuthService>();
|
||||
protected static Lazy<AdminService> _adminservice = new Lazy<AdminService>(() => bts.api.Locator.locate<AdminService>(), true);
|
||||
protected static Lazy<bts.metrics.ShellLatencyService> _latencyservice =
|
||||
new Lazy<bts.metrics.ShellLatencyService>(() => bts.api.Locator.locate<bts.metrics.ShellLatencyService>(), true);
|
||||
|
||||
protected string _user_name;
|
||||
protected AuthToken _token;
|
||||
|
||||
protected IDisposable _admindisp;
|
||||
protected IObservable<AdminModelWrapper> _adminobservable;
|
||||
protected AdminDataImmutable? _admin_data;
|
||||
|
||||
protected readonly IFixRouter _router;
|
||||
protected readonly SessionID _sess;
|
||||
|
||||
public SessionBase(IFixRouter router, SessionID sess)
|
||||
{
|
||||
_router = router;
|
||||
_sess = sess;
|
||||
|
||||
_clockdisp = _latencyservice.Value.subscribe_clock().Subscribe(x =>
|
||||
{
|
||||
var t = x.all.FirstOrDefault();
|
||||
if(t != null)
|
||||
_clock = t.Timestamp;
|
||||
});
|
||||
|
||||
_adminobservable = _adminservice.Value.subscribei_admin_data().Select(x =>
|
||||
{
|
||||
return new AdminModelWrapper(x.all.TryFirst(), _user_name);
|
||||
});
|
||||
_admindisp = _adminobservable.Subscribe(x => _admin_data = x.admin_data);
|
||||
}
|
||||
|
||||
abstract public void FromApp(Message message, SessionID sessionID);
|
||||
abstract public void OnLogon(SessionID sessionID); //subscribe to your stuff
|
||||
abstract public void OnLogout(SessionID sessionID); //unsub
|
||||
|
||||
virtual public void ToApp(Message message, SessionID sessionID)
|
||||
{
|
||||
Log.assert_d(sessionID.Equals(_sess));
|
||||
log_info("OUT: " + message);
|
||||
}
|
||||
|
||||
readonly IDisposable _clockdisp; ulong _clock = 0;
|
||||
virtual public void FromAdmin(Message message, SessionID sessionID)
|
||||
{
|
||||
Log.assert_d(sessionID.Equals(_sess));
|
||||
|
||||
MsgType type = Message.IdentifyType(message.ToString());
|
||||
switch (type.getValue())
|
||||
{
|
||||
case MsgType.LOGON:
|
||||
{
|
||||
try
|
||||
{
|
||||
QuickFix.FIX44.Logon msg = (QuickFix.FIX44.Logon)message;
|
||||
_user_name = msg.Username.getValue();
|
||||
string pass = string.Empty;
|
||||
if(msg.IsSetPassword())
|
||||
pass = msg.Password.getValue();
|
||||
var resp = _authservice.authenticate_login(new AuthenticateLoginRequest {
|
||||
Login = msg.Username.getValue(), Password = pass});
|
||||
if (resp != null && resp.IsValid)
|
||||
_token = resp.Token;
|
||||
else
|
||||
throw new RejectLogon("Logon rejected " + resp?.Reason ?? string.Empty);
|
||||
}
|
||||
catch (RejectLogon)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new RejectLogon("Logon rejected " + ex.ToString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MsgType.HEARTBEAT:
|
||||
{
|
||||
QuickFix.FIX44.Heartbeat hb = (QuickFix.FIX44.Heartbeat)message;
|
||||
handle_heartbeat(hb, sessionID);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_info("ADMININ: " + message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async protected void handle_heartbeat(QuickFix.FIX44.Heartbeat hb, SessionID sessionID)
|
||||
{
|
||||
var resp = await _latencyservice.Value.report_async(new bts.metrics.Report { HostName = "BTSFIX_" + sessionID.ToString(),
|
||||
Timestamp = _clock, User = _user_name });
|
||||
if (resp.success)
|
||||
_router.send(hb, sessionID);
|
||||
}
|
||||
|
||||
virtual public void ToAdmin(Message message, SessionID sessionID)
|
||||
{
|
||||
Log.assert_d(sessionID.Equals(_sess));
|
||||
log_info("ADMINOUT: " + message);
|
||||
}
|
||||
|
||||
virtual public void OnCreate(SessionID sessionID)
|
||||
{
|
||||
log_info("CREATE");
|
||||
}
|
||||
|
||||
virtual public void OnMessage(QuickFix.FIX44.BusinessMessageReject n, SessionID s)
|
||||
{
|
||||
//Log.assert(false, "biz reject wtf " + n.EncodedText.getValue());
|
||||
Log.FIX.warn(_sess.ToString() + " Business Reject " + n.BusinessRejectReason.getValue() + " " + n.Text.getValue());
|
||||
}
|
||||
virtual public void OnMessage(QuickFix.FIX44.News n, SessionID s)
|
||||
{
|
||||
log_info("news " + n.EncodedHeadline.getValue());
|
||||
}
|
||||
|
||||
|
||||
protected void log_info(string message, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0)
|
||||
{
|
||||
Log.FIX.info(_sess.ToString() + " " + message, file, line);
|
||||
}
|
||||
|
||||
protected void log_error(string message, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0)
|
||||
{
|
||||
Log.FIX.error(_sess.ToString() + " " + message, file, line);
|
||||
}
|
||||
|
||||
protected void send_cancel_reject(SessionID sess, string text, Message refmessage)
|
||||
{
|
||||
QuickFix.FIX44.OrderCancelReject rej = new QuickFix.FIX44.OrderCancelReject();
|
||||
if (refmessage is QuickFix.FIX44.OrderCancelRequest cancel)
|
||||
{
|
||||
if (cancel.IsSetClOrdID())
|
||||
rej.SetField(cancel.ClOrdID);
|
||||
if (cancel.IsSetOrigClOrdID())
|
||||
rej.SetField(cancel.OrigClOrdID);
|
||||
if (cancel.IsSetOrderID())
|
||||
rej.SetField(cancel.OrderID);
|
||||
rej.CxlRejResponseTo = new CxlRejResponseTo('1');
|
||||
rej.Text = new Text(text);
|
||||
_router.send(rej, sess);
|
||||
}
|
||||
else if (refmessage is QuickFix.FIX44.OrderCancelReplaceRequest replace)
|
||||
{
|
||||
if (replace.IsSetClOrdID())
|
||||
rej.SetField(replace.ClOrdID);
|
||||
if (replace.IsSetOrigClOrdID())
|
||||
rej.SetField(replace.OrigClOrdID);
|
||||
if (replace.IsSetOrderID())
|
||||
rej.SetField(replace.OrderID);
|
||||
rej.CxlRejResponseTo = new CxlRejResponseTo('2');
|
||||
rej.Text = new Text(text);
|
||||
_router.send(rej, sess);
|
||||
}
|
||||
else
|
||||
throw new UnsupportedMessageType();
|
||||
|
||||
}
|
||||
|
||||
protected bool security_types_equal(bts.api.SecurityType btstype, QuickFix.Fields.SecurityType fixtype)
|
||||
{
|
||||
return btstype == convert_security_type(fixtype);
|
||||
}
|
||||
|
||||
protected QuickFix.Fields.SecurityType convert_security_type(bts.api.SecurityType btstype)
|
||||
{
|
||||
switch (btstype)
|
||||
{
|
||||
case bts.api.SecurityType.Future:
|
||||
return new QuickFix.Fields.SecurityType("FUT");
|
||||
case bts.api.SecurityType.Option:
|
||||
return new QuickFix.Fields.SecurityType("OPT");
|
||||
case bts.api.SecurityType.Spread:
|
||||
return new QuickFix.Fields.SecurityType("MLEG");
|
||||
case bts.api.SecurityType.Stock:
|
||||
return new QuickFix.Fields.SecurityType("CS");
|
||||
case bts.api.SecurityType.Synthetic:
|
||||
return new QuickFix.Fields.SecurityType("SYNTH");
|
||||
default:
|
||||
return new QuickFix.Fields.SecurityType();
|
||||
}
|
||||
}
|
||||
|
||||
protected bts.api.SecurityType convert_security_type(QuickFix.Fields.SecurityType fixtype)
|
||||
{
|
||||
switch (fixtype.getValue())
|
||||
{
|
||||
case "FUT":
|
||||
return bts.api.SecurityType.Future;
|
||||
case "OPT":
|
||||
return bts.api.SecurityType.Option;
|
||||
case "MLEG":
|
||||
return bts.api.SecurityType.Spread;
|
||||
case "CS":
|
||||
return bts.api.SecurityType.Stock;
|
||||
case "SYNTH":
|
||||
return bts.api.SecurityType.Synthetic;
|
||||
default:
|
||||
return bts.api.SecurityType.Unset;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool exchanges_equal(bts.api.Venue btstype, QuickFix.Fields.SecurityExchange fixtype)
|
||||
{
|
||||
return btstype == convert_exchange(fixtype);
|
||||
}
|
||||
|
||||
protected QuickFix.Fields.SecurityExchange convert_exchange(bts.api.Venue btstype)
|
||||
{
|
||||
switch (btstype)
|
||||
{
|
||||
case bts.api.Venue.Bts:
|
||||
return new QuickFix.Fields.SecurityExchange("BTS");
|
||||
case bts.api.Venue.Cboe:
|
||||
return new QuickFix.Fields.SecurityExchange("XCBO");
|
||||
case bts.api.Venue.Cboecfe:
|
||||
return new QuickFix.Fields.SecurityExchange("XCBF");
|
||||
case bts.api.Venue.Glbx:
|
||||
return new QuickFix.Fields.SecurityExchange("XCME");
|
||||
case bts.api.Venue.Ice:
|
||||
return new QuickFix.Fields.SecurityExchange("IEPA");
|
||||
case bts.api.Venue.Sim:
|
||||
return new QuickFix.Fields.SecurityExchange("SIM");
|
||||
default:
|
||||
return new QuickFix.Fields.SecurityExchange();
|
||||
}
|
||||
}
|
||||
|
||||
protected bts.api.Venue convert_exchange(QuickFix.Fields.SecurityExchange fixtype)
|
||||
{
|
||||
switch (fixtype.getValue())
|
||||
{
|
||||
case "BTS":
|
||||
return bts.api.Venue.Bts;
|
||||
case "XCBO":
|
||||
return bts.api.Venue.Cboe;
|
||||
case "XCBF":
|
||||
return bts.api.Venue.Cboecfe;
|
||||
case "GLBX":
|
||||
case "XCME":
|
||||
case "XCBT":
|
||||
case "XCEC":
|
||||
case "XNYM":
|
||||
return bts.api.Venue.Glbx;
|
||||
case "IEPA":
|
||||
case "IFUS":
|
||||
case "IFEU":
|
||||
case "IFLL":
|
||||
return bts.api.Venue.Ice;
|
||||
case "SIM":
|
||||
return bts.api.Venue.Sim;
|
||||
default:
|
||||
return bts.api.Venue.Unset;
|
||||
}
|
||||
}
|
||||
|
||||
protected QuickFix.Fields.OrdStatus translate_status(in bts.api.orderman.OrderStateImmutable state,
|
||||
ref bts.api.orderman.NativeOrderStateImmutable? native)
|
||||
{
|
||||
native = null;
|
||||
if (state.HasMarket && state.Market.HasNativeState && state.Market.NativeState.NativeStatus != NativeOrderStatus.Unacknowledged)
|
||||
{
|
||||
native = state.Market.NativeState;
|
||||
return new OrdStatus((char)native?.NativeStatus);
|
||||
}
|
||||
else if (state.HasLimit && state.Limit.HasNativeState && state.Limit.NativeState.NativeStatus != NativeOrderStatus.Unacknowledged)
|
||||
{
|
||||
native = state.Limit.NativeState;
|
||||
return new OrdStatus((char)native?.NativeStatus);
|
||||
}
|
||||
else if (state.HasStop && state.Stop.HasNativeState && state.Stop.NativeState.NativeStatus != NativeOrderStatus.Unacknowledged)
|
||||
{
|
||||
native = state.Stop.NativeState;
|
||||
return new OrdStatus((char)native?.NativeStatus);
|
||||
}
|
||||
else if (state.HasStopLimit && state.StopLimit.HasNativeState && state.StopLimit.NativeState.NativeStatus != NativeOrderStatus.Unacknowledged)
|
||||
{
|
||||
native = state.StopLimit.NativeState;
|
||||
return new OrdStatus((char)native?.NativeStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (state.Status)
|
||||
{
|
||||
case OrderStatus.Canceled:
|
||||
return new OrdStatus(OrdStatus.CANCELED);
|
||||
case OrderStatus.Completed:
|
||||
return new OrdStatus(OrdStatus.FILLED);
|
||||
case OrderStatus.Disconnected:
|
||||
return new OrdStatus(OrdStatus.STOPPED);
|
||||
case OrderStatus.Error:
|
||||
return new OrdStatus(OrdStatus.REJECTED);
|
||||
case OrderStatus.Inactive:
|
||||
return new OrdStatus(OrdStatus.PENDING_NEW);
|
||||
case OrderStatus.PendingWorking:
|
||||
return new OrdStatus(OrdStatus.PENDING_NEW);
|
||||
case OrderStatus.RejectedBySpark:
|
||||
return new OrdStatus(OrdStatus.REJECTED);
|
||||
case OrderStatus.Working:
|
||||
{
|
||||
if (state.FilledQuantity < state.Quantity)
|
||||
return new OrdStatus(OrdStatus.PARTIALLY_FILLED);
|
||||
else
|
||||
return new OrdStatus(OrdStatus.NEW);
|
||||
}
|
||||
default:
|
||||
return new OrdStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected uint lookup_account(string acct)
|
||||
{
|
||||
if (_admin_data.HasValue)
|
||||
{
|
||||
var ac = _admin_data.Value.Accounts.Where(x => x.Name == acct).TryFirst();
|
||||
if(ac.HasValue)
|
||||
return ac.Value.Id;
|
||||
}
|
||||
throw new IncorrectTagValue(new Account().Tag);
|
||||
}
|
||||
|
||||
protected uint lookup_user(string user)
|
||||
{
|
||||
if (_admin_data.HasValue)
|
||||
{
|
||||
var ac = _admin_data.Value.Users.Where(x => x.Name == user).TryFirst();
|
||||
if (ac.HasValue)
|
||||
return ac.Value.Id;
|
||||
}
|
||||
throw new IncorrectTagValue(new Username().Tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#http://www.quickfixengine.org/quickfix/doc/html/configuration.html
|
||||
#https://www.onixs.biz/fix-dictionary.html
|
||||
|
||||
[DEFAULT]
|
||||
ConnectionType=acceptor
|
||||
SocketAcceptPort=5001
|
||||
SocketReuseAddress=Y
|
||||
StartTime=00:00:00
|
||||
EndTime=00:00:00
|
||||
FileLogPath=log
|
||||
UseDataDictionary=Y
|
||||
ResetOnLogon=Y
|
||||
ResetOnLogout=Y
|
||||
ResetOnDisconnect=Y
|
||||
|
||||
HeartBtInt=30
|
||||
LogonTimeout=30
|
||||
|
||||
SSLEnable=N
|
||||
# SSLEnable=Y
|
||||
# # It is recommended to install the certificate and refer to it by name instead of using filename + password
|
||||
# SSLCertificate=QuickFixn-TestServer.pfx
|
||||
# SSLCertificatePassword=QuickFixn-TestServer
|
||||
# # For production refer to certificate by name instead: SSLCertificate=CN=QuickFixn-TestServer
|
||||
# SSLCACertificate=QuickFixn-TestCA.cer
|
||||
# SSLCheckCertificateRevocation=N
|
||||
|
||||
|
||||
[SESSION]
|
||||
BeginString=FIX.4.4
|
||||
SenderCompID=SPARK
|
||||
TargetCompID=CLIENT1
|
||||
FileStorePath=store
|
||||
DataDictionary=./spec/fix/FIX44.xml
|
||||
|
||||
[SESSION]
|
||||
BeginString=FIX.4.4
|
||||
SenderCompID=SPARK
|
||||
TargetCompID=CLIENT2
|
||||
FileStorePath=store
|
||||
DataDictionary=./spec/fix/FIX44.xml
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks>
|
||||
|
||||
<Authors>Blue Trading Systems</Authors>
|
||||
<Company>Blue Trading Systems</Company>
|
||||
<Product>Spark</Product>
|
||||
<Description />
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<Version>1.0.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
|
||||
<PackageReference Include="System.Reactive" Version="3.1.1" />
|
||||
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
|
||||
<PackageReference Include="QuickFix.Net.NETCore" Version="1.8.1" />
|
||||
<PackageReference Include="QuickFix.Net.NETCore.FIX44" Version="1.8.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../spark_api/spark_api.csproj"/>
|
||||
<ProjectReference Include="../spark_core/spark_core.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="copy_fix" AfterTargets="PostBuildEvent" >
|
||||
<Copy SourceFiles="./spark_fix.cfg" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="./spec/fix/FIX44.xml" DestinationFolder="$(OutputPath)/spec/fix/" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
|
||||
<Target Name="copy_fix471" AfterTargets="PostBuildEvent" >
|
||||
<Copy SourceFiles="./spark_fix.cfg" DestinationFolder="$(OutputPath)/../net471" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="./spec/fix/FIX44.xml" DestinationFolder="$(OutputPath)/../net471/spec/fix/" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,828 @@
|
||||
<fix major="4" minor="0">
|
||||
<header>
|
||||
<field name="BeginString" required="Y"/>
|
||||
<field name="BodyLength" required="Y"/>
|
||||
<field name="MsgType" required="Y"/>
|
||||
<field name="SenderCompID" required="Y"/>
|
||||
<field name="TargetCompID" required="Y"/>
|
||||
<field name="OnBehalfOfCompID" required="N"/>
|
||||
<field name="DeliverToCompID" required="N"/>
|
||||
<field name="SecureDataLen" required="N"/>
|
||||
<field name="SecureData" required="N"/>
|
||||
<field name="MsgSeqNum" required="Y"/>
|
||||
<field name="SenderSubID" required="N"/>
|
||||
<field name="TargetSubID" required="N"/>
|
||||
<field name="OnBehalfOfSubID" required="N"/>
|
||||
<field name="DeliverToSubID" required="N"/>
|
||||
<field name="PossDupFlag" required="N"/>
|
||||
<field name="PossResend" required="N"/>
|
||||
<field name="SendingTime" required="Y"/>
|
||||
<field name="OrigSendingTime" required="N"/>
|
||||
</header>
|
||||
<trailer>
|
||||
<field name="SignatureLength" required="N"/>
|
||||
<field name="Signature" required="N"/>
|
||||
<field name="CheckSum" required="Y"/>
|
||||
</trailer>
|
||||
<messages>
|
||||
<message name="Heartbeat" msgtype="0" msgcat="admin">
|
||||
<field name="TestReqID" required="N"/>
|
||||
</message>
|
||||
<message name="Logon" msgtype="A" msgcat="admin">
|
||||
<field name="EncryptMethod" required="Y"/>
|
||||
<field name="HeartBtInt" required="Y"/>
|
||||
<field name="RawDataLength" required="N"/>
|
||||
<field name="RawData" required="N"/>
|
||||
</message>
|
||||
<message name="TestRequest" msgtype="1" msgcat="admin">
|
||||
<field name="TestReqID" required="Y"/>
|
||||
</message>
|
||||
<message name="ResendRequest" msgtype="2" msgcat="admin">
|
||||
<field name="BeginSeqNo" required="Y"/>
|
||||
<field name="EndSeqNo" required="Y"/>
|
||||
</message>
|
||||
<message name="Reject" msgtype="3" msgcat="admin">
|
||||
<field name="RefSeqNum" required="Y"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="SequenceReset" msgtype="4" msgcat="admin">
|
||||
<field name="GapFillFlag" required="N"/>
|
||||
<field name="NewSeqNo" required="Y"/>
|
||||
</message>
|
||||
<message name="Logout" msgtype="5" msgcat="admin">
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="Advertisement" msgtype="7" msgcat="app">
|
||||
<field name="AdvId" required="Y"/>
|
||||
<field name="AdvTransType" required="Y"/>
|
||||
<field name="AdvRefID" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="AdvSide" required="Y"/>
|
||||
<field name="Shares" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="TransactTime" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="IndicationofInterest" msgtype="6" msgcat="app">
|
||||
<field name="IOIid" required="Y"/>
|
||||
<field name="IOITransType" required="Y"/>
|
||||
<field name="IOIRefID" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="IOIShares" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="ValidUntilTime" required="N"/>
|
||||
<field name="IOIQltyInd" required="N"/>
|
||||
<field name="IOIOthSvc" required="N"/>
|
||||
<field name="IOINaturalFlag" required="N"/>
|
||||
<field name="IOIQualifier" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="News" msgtype="B" msgcat="app">
|
||||
<field name="OrigTime" required="N"/>
|
||||
<field name="Urgency" required="N"/>
|
||||
<field name="RelatdSym" required="N" repeating="Y"/>
|
||||
<group name="LinesOfText" required="Y">
|
||||
<field name="Text" required="Y"/>
|
||||
</group>
|
||||
<field name="RawDataLength" required="N"/>
|
||||
<field name="RawData" required="N"/>
|
||||
</message>
|
||||
<message name="Email" msgtype="C" msgcat="app">
|
||||
<field name="EmailType" required="Y"/>
|
||||
<field name="OrigTime" required="N"/>
|
||||
<field name="RelatdSym" required="N" repeating="Y"/>
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ClOrdID" required="N"/>
|
||||
<group name="LinesOfText" required="Y">
|
||||
<field name="Text" required="Y"/>
|
||||
</group>
|
||||
<field name="RawDataLength" required="N"/>
|
||||
<field name="RawData" required="N"/>
|
||||
</message>
|
||||
<message name="QuoteRequest" msgtype="R" msgcat="app">
|
||||
<field name="QuoteReqID" required="Y"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="PrevClosePx" required="N"/>
|
||||
<field name="Side" required="N"/>
|
||||
<field name="OrderQty" required="N"/>
|
||||
</message>
|
||||
<message name="Quote" msgtype="S" msgcat="app">
|
||||
<field name="QuoteReqID" required="N"/>
|
||||
<field name="QuoteID" required="Y"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="BidPx" required="Y"/>
|
||||
<field name="OfferPx" required="N"/>
|
||||
<field name="BidSize" required="N"/>
|
||||
<field name="OfferSize" required="N"/>
|
||||
<field name="ValidUntilTime" required="N"/>
|
||||
</message>
|
||||
<message name="NewOrderSingle" msgtype="D" msgcat="app">
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="Account" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="HandlInst" required="Y"/>
|
||||
<field name="ExecInst" required="N"/>
|
||||
<field name="MinQty" required="N"/>
|
||||
<field name="MaxFloor" required="N"/>
|
||||
<field name="ExDestination" required="N"/>
|
||||
<field name="ProcessCode" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="PrevClosePx" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="LocateReqd" required="N"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="OrdType" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="StopPx" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="IOIid" required="N"/>
|
||||
<field name="QuoteID" required="N"/>
|
||||
<field name="TimeInForce" required="N"/>
|
||||
<field name="ExpireTime" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<field name="Rule80A" required="N"/>
|
||||
<field name="ForexReq" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="ExecutionReport" msgtype="8" msgcat="app">
|
||||
<field name="OrderID" required="Y"/>
|
||||
<field name="ClOrdID" required="N"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="ExecID" required="Y"/>
|
||||
<field name="ExecTransType" required="Y"/>
|
||||
<field name="ExecRefID" required="N"/>
|
||||
<field name="OrdStatus" required="Y"/>
|
||||
<field name="OrdRejReason" required="N"/>
|
||||
<field name="Account" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="OrdType" required="N"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="StopPx" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="TimeInForce" required="N"/>
|
||||
<field name="ExpireTime" required="N"/>
|
||||
<field name="ExecInst" required="N"/>
|
||||
<field name="Rule80A" required="N"/>
|
||||
<field name="LastShares" required="Y"/>
|
||||
<field name="LastPx" required="Y"/>
|
||||
<field name="LastMkt" required="N"/>
|
||||
<field name="LastCapacity" required="N"/>
|
||||
<field name="CumQty" required="Y"/>
|
||||
<field name="AvgPx" required="Y"/>
|
||||
<field name="TradeDate" required="N"/>
|
||||
<field name="TransactTime" required="N"/>
|
||||
<field name="ReportToExch" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<group name="NoMiscFees" required="N">
|
||||
<field name="MiscFeeAmt" required="N"/>
|
||||
<field name="MiscFeeCurr" required="N"/>
|
||||
<field name="MiscFeeType" required="N"/>
|
||||
</group>
|
||||
<field name="NetMoney" required="N"/>
|
||||
<field name="SettlCurrAmt" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="DontKnowTrade" msgtype="Q" msgcat="app">
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ExecID" required="N"/>
|
||||
<field name="DKReason" required="Y"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="LastShares" required="Y"/>
|
||||
<field name="LastPx" required="Y"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="OrderCancelReplaceRequest" msgtype="G" msgcat="app">
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="OrigClOrdID" required="Y"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="Account" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="HandlInst" required="Y"/>
|
||||
<field name="ExecInst" required="N"/>
|
||||
<field name="MinQty" required="N"/>
|
||||
<field name="MaxFloor" required="N"/>
|
||||
<field name="ExDestination" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="OrdType" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="StopPx" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="TimeInForce" required="N"/>
|
||||
<field name="ExpireTime" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<field name="Rule80A" required="N"/>
|
||||
<field name="ForexReq" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="OrderCancelRequest" msgtype="F" msgcat="app">
|
||||
<field name="OrigClOrdID" required="Y"/>
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="CxlType" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="OrderCancelReject" msgtype="9" msgcat="app">
|
||||
<field name="OrderID" required="Y"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="CxlRejReason" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="OrderStatusRequest" msgtype="H" msgcat="app">
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
</message>
|
||||
<message name="Allocation" msgtype="J" msgcat="app">
|
||||
<field name="AllocID" required="Y"/>
|
||||
<field name="AllocTransType" required="Y"/>
|
||||
<field name="RefAllocID" required="N"/>
|
||||
<group name="NoOrders" required="Y">
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
</group>
|
||||
<group name="NoExecs" required="N">
|
||||
<field name="ExecID" required="N"/>
|
||||
<field name="LastShares" required="N"/>
|
||||
<field name="LastPx" required="N"/>
|
||||
<field name="LastMkt" required="N"/>
|
||||
</group>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Shares" required="Y"/>
|
||||
<field name="AvgPx" required="Y"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="AvgPrxPrecision" required="N"/>
|
||||
<field name="TradeDate" required="Y"/>
|
||||
<field name="TransactTime" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="NetMoney" required="N"/>
|
||||
<group name="NoMiscFees" required="N">
|
||||
<field name="MiscFeeAmt" required="N"/>
|
||||
<field name="MiscFeeCurr" required="N"/>
|
||||
<field name="MiscFeeType" required="N"/>
|
||||
</group>
|
||||
<field name="SettlCurrAmt" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="OpenClose" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
<group name="NoAllocs" required="Y">
|
||||
<field name="AllocAccount" required="Y"/>
|
||||
<field name="AllocShares" required="Y"/>
|
||||
<field name="ProcessCode" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<group name="NoDlvyInst" required="N">
|
||||
<field name="BrokerOfCredit" required="N"/>
|
||||
<field name="DlvyInst" required="N"/>
|
||||
</group>
|
||||
</group>
|
||||
</message>
|
||||
<message name="AllocationACK" msgtype="P" msgcat="app">
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="AllocID" required="Y"/>
|
||||
<field name="TradeDate" required="Y"/>
|
||||
<field name="TransactTime" required="N"/>
|
||||
<field name="AllocStatus" required="Y"/>
|
||||
<field name="AllocRejCode" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="NewOrderList" msgtype="E" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="ListSeqNo" required="Y"/>
|
||||
<field name="ListNoOrds" required="Y"/>
|
||||
<field name="ListExecInst" required="N"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="Account" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="HandlInst" required="Y"/>
|
||||
<field name="ExecInst" required="N"/>
|
||||
<field name="MinQty" required="N"/>
|
||||
<field name="MaxFloor" required="N"/>
|
||||
<field name="ExDestination" required="N"/>
|
||||
<field name="ProcessCode" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="PrevClosePx" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="LocateReqd" required="N"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="OrdType" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="StopPx" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="TimeInForce" required="N"/>
|
||||
<field name="ExpireTime" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<field name="Rule80A" required="N"/>
|
||||
<field name="ForexReq" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="ListStatus" msgtype="N" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="NoRpts" required="Y"/>
|
||||
<field name="RptSeq" required="Y"/>
|
||||
<group name="NoOrders" required="Y">
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="CumQty" required="Y"/>
|
||||
<field name="CxlQty" required="Y"/>
|
||||
<field name="AvgPx" required="Y"/>
|
||||
</group>
|
||||
</message>
|
||||
<message name="ListExecute" msgtype="L" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="ListCancelRequest" msgtype="K" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="ListStatusRequest" msgtype="M" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
</messages>
|
||||
<fields>
|
||||
<field number="1" name="Account" type="STRING"/>
|
||||
<field number="2" name="AdvId" type="INT"/>
|
||||
<field number="3" name="AdvRefID" type="INT"/>
|
||||
<field number="4" name="AdvSide" type="STRING">
|
||||
<value enum="B" description="BUY"/>
|
||||
<value enum="S" description="SELL"/>
|
||||
<value enum="X" description="CROSS"/>
|
||||
<value enum="T" description="TRADE"/>
|
||||
</field>
|
||||
<field number="5" name="AdvTransType" type="STRING">
|
||||
<value enum="N" description="NEW"/>
|
||||
<value enum="C" description="CANCEL"/>
|
||||
<value enum="R" description="REPLACE"/>
|
||||
</field>
|
||||
<field number="6" name="AvgPx" type="FLOAT"/>
|
||||
<field number="7" name="BeginSeqNo" type="INT"/>
|
||||
<field number="8" name="BeginString" type="STRING"/>
|
||||
<field number="9" name="BodyLength" type="INT"/>
|
||||
<field number="10" name="CheckSum" type="STRING"/>
|
||||
<field number="11" name="ClOrdID" type="STRING"/>
|
||||
<field number="12" name="Commission" type="FLOAT"/>
|
||||
<field number="13" name="CommType" type="STRING">
|
||||
<value enum="1" description="PER_SHARE"/>
|
||||
<value enum="2" description="PERCENTAGE"/>
|
||||
<value enum="3" description="ABSOLUTE"/>
|
||||
</field>
|
||||
<field number="14" name="CumQty" type="INT"/>
|
||||
<field number="15" name="Currency" type="STRING"/>
|
||||
<field number="16" name="EndSeqNo" type="INT"/>
|
||||
<field number="17" name="ExecID" type="INT"/>
|
||||
<field number="18" name="ExecInst" type="MULTIPLEVALUESTRING">
|
||||
<value enum="1" description="NOT_HELD"/>
|
||||
<value enum="2" description="WORK"/>
|
||||
<value enum="3" description="GO_ALONG"/>
|
||||
<value enum="4" description="OVER_THE_DAY"/>
|
||||
<value enum="5" description="HELD"/>
|
||||
<value enum="6" description="PARTICIPATE_DONT_INITIATE"/>
|
||||
<value enum="7" description="STRICT_SCALE"/>
|
||||
<value enum="8" description="TRY_TO_SCALE"/>
|
||||
<value enum="9" description="STAY_ON_BIDSIDE"/>
|
||||
<value enum="0" description="STAY_ON_OFFERSIDE"/>
|
||||
<value enum="A" description="NO_CROSS"/>
|
||||
<value enum="B" description="OK_TO_CROSS"/>
|
||||
<value enum="C" description="CALL_FIRST"/>
|
||||
<value enum="D" description="PERCENT_OF_VOLUME"/>
|
||||
<value enum="E" description="DO_NOT_INCREASE_DNI"/>
|
||||
<value enum="F" description="DO_NOT_REDUCE_DNR"/>
|
||||
<value enum="G" description="ALL_OR_NONE_AON"/>
|
||||
<value enum="I" description="INSTITUTIONS_ONLY"/>
|
||||
<value enum="L" description="LAST_PEG"/>
|
||||
<value enum="M" description="MIDPRICE_PEG"/>
|
||||
<value enum="N" description="NONNEGOTIABLE"/>
|
||||
<value enum="O" description="OPENING_PEG"/>
|
||||
<value enum="P" description="MARKET_PEG"/>
|
||||
<value enum="R" description="PRIMARY_PEG"/>
|
||||
<value enum="S" description="SUSPEND"/>
|
||||
</field>
|
||||
<field number="19" name="ExecRefID" type="INT"/>
|
||||
<field number="20" name="ExecTransType" type="STRING">
|
||||
<value enum="0" description="NEW"/>
|
||||
<value enum="1" description="CANCEL"/>
|
||||
<value enum="2" description="CORRECT"/>
|
||||
<value enum="3" description="STATUS"/>
|
||||
</field>
|
||||
<field number="21" name="HandlInst" type="STRING">
|
||||
<value enum="1" description="AUTOMATED_EXECUTION_ORDER_PRIVATE_NO_BROKER_INTERVENTION"/>
|
||||
<value enum="2" description="AUTOMATED_EXECUTION_ORDER_PUBLIC_BROKER_INTERVENTION_OK"/>
|
||||
<value enum="3" description="MANUAL_ORDER_BEST_EXECUTION"/>
|
||||
</field>
|
||||
<field number="22" name="IDSource" type="STRING">
|
||||
<value enum="1" description="CUSIP"/>
|
||||
<value enum="2" description="SEDOL"/>
|
||||
<value enum="3" description="QUIK"/>
|
||||
<value enum="4" description="ISIN_NUMBER"/>
|
||||
<value enum="5" description="RIC_CODE"/>
|
||||
</field>
|
||||
<field number="23" name="IOIid" type="INT"/>
|
||||
<field number="24" name="IOIOthSvc" type="STRING"/>
|
||||
<field number="25" name="IOIQltyInd" type="STRING">
|
||||
<value enum="L" description="LOW"/>
|
||||
<value enum="M" description="MEDIUM"/>
|
||||
<value enum="H" description="HIGH"/>
|
||||
</field>
|
||||
<field number="26" name="IOIRefID" type="INT"/>
|
||||
<field number="27" name="IOIShares" type="STRING"/>
|
||||
<field number="28" name="IOITransType" type="STRING">
|
||||
<value enum="N" description="NEW"/>
|
||||
<value enum="C" description="CANCEL"/>
|
||||
<value enum="R" description="REPLACE"/>
|
||||
</field>
|
||||
<field number="29" name="LastCapacity" type="STRING">
|
||||
<value enum="1" description="AGENT"/>
|
||||
<value enum="2" description="CROSS_AS_AGENT"/>
|
||||
<value enum="3" description="CROSS_AS_PRINCIPAL"/>
|
||||
<value enum="4" description="PRINCIPAL"/>
|
||||
</field>
|
||||
<field number="30" name="LastMkt" type="STRING"/>
|
||||
<field number="31" name="LastPx" type="FLOAT"/>
|
||||
<field number="32" name="LastShares" type="INT"/>
|
||||
<field number="33" name="LinesOfText" type="INT"/>
|
||||
<field number="34" name="MsgSeqNum" type="INT"/>
|
||||
<field number="35" name="MsgType" type="STRING"/>
|
||||
<field number="36" name="NewSeqNo" type="INT"/>
|
||||
<field number="37" name="OrderID" type="STRING"/>
|
||||
<field number="38" name="OrderQty" type="INT"/>
|
||||
<field number="39" name="OrdStatus" type="STRING">
|
||||
<value enum="0" description="NEW"/>
|
||||
<value enum="1" description="PARTIALLY_FILLED"/>
|
||||
<value enum="2" description="FILLED"/>
|
||||
<value enum="3" description="DONE_FOR_DAY"/>
|
||||
<value enum="4" description="CANCELED"/>
|
||||
<value enum="5" description="REPLACED"/>
|
||||
<value enum="6" description="PENDING_CANCELREPLACE"/>
|
||||
<value enum="7" description="STOPPED"/>
|
||||
<value enum="8" description="REJECTED"/>
|
||||
<value enum="9" description="SUSPENDED"/>
|
||||
<value enum="A" description="PENDING_NEW"/>
|
||||
<value enum="B" description="CALCULATED"/>
|
||||
<value enum="C" description="EXPIRED"/>
|
||||
</field>
|
||||
<field number="40" name="OrdType" type="STRING">
|
||||
<value enum="1" description="MARKET"/>
|
||||
<value enum="2" description="LIMIT"/>
|
||||
<value enum="3" description="STOP"/>
|
||||
<value enum="4" description="STOP_LIMIT"/>
|
||||
<value enum="5" description="MARKET_ON_CLOSE"/>
|
||||
<value enum="6" description="WITH_OR_WITHOUT"/>
|
||||
<value enum="7" description="LIMIT_OR_BETTER"/>
|
||||
<value enum="8" description="LIMIT_WITH_OR_WITHOUT"/>
|
||||
<value enum="9" description="ON_BASIS"/>
|
||||
<value enum="A" description="ON_CLOSE"/>
|
||||
<value enum="B" description="LIMIT_ON_CLOSE"/>
|
||||
<value enum="C" description="FOREX"/>
|
||||
<value enum="D" description="PREVIOUSLY_QUOTED"/>
|
||||
<value enum="E" description="PREVIOUSLY_INDICATED"/>
|
||||
<value enum="P" description="PEGGED"/>
|
||||
</field>
|
||||
<field number="41" name="OrigClOrdID" type="STRING"/>
|
||||
<field number="42" name="OrigTime" type="TIME"/>
|
||||
<field number="43" name="PossDupFlag" type="STRING">
|
||||
<value enum="Y" description="POSSIBLE_DUPLICATE"/>
|
||||
<value enum="N" description="ORIGINAL_TRANSMISSION"/>
|
||||
</field>
|
||||
<field number="44" name="Price" type="FLOAT"/>
|
||||
<field number="45" name="RefSeqNum" type="INT"/>
|
||||
<field number="46" name="RelatdSym" type="STRING"/>
|
||||
<field number="47" name="Rule80A" type="STRING">
|
||||
<value enum="A" description="AGENCY_SINGLE_ORDER"/>
|
||||
<value enum="D" description="PROGRAM_ORDER_INDEX_ARB_FOR_MEMBER_FIRMORG"/>
|
||||
<value enum="C" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_MEMBER_FIRMORG"/>
|
||||
<value enum="J" description="PROGRAM_ORDER_INDEX_ARB_FOR_INDIVIDUAL_CUSTOMER"/>
|
||||
<value enum="K" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_INDIVIDUAL_CUSTOMER"/>
|
||||
<value enum="U" description="PROGRAM_ORDER_INDEX_ARB_FOR_OTHER_AGENCY"/>
|
||||
<value enum="Y" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_OTHER_AGENCY"/>
|
||||
<value enum="M" description="PROGRAM_ORDER_INDEX_ARB_FOR_OTHER_MEMBER"/>
|
||||
<value enum="N" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_OTHER_MEMBER"/>
|
||||
<value enum="W" description="ALL_OTHER_ORDERS_AS_AGENT_FOR_OTHER_MEMBER"/>
|
||||
</field>
|
||||
<field number="48" name="SecurityID" type="STRING"/>
|
||||
<field number="49" name="SenderCompID" type="STRING"/>
|
||||
<field number="50" name="SenderSubID" type="STRING"/>
|
||||
<field number="52" name="SendingTime" type="TIME"/>
|
||||
<field number="53" name="Shares" type="INT"/>
|
||||
<field number="54" name="Side" type="STRING">
|
||||
<value enum="1" description="BUY"/>
|
||||
<value enum="2" description="SELL"/>
|
||||
<value enum="3" description="BUY_MINUS"/>
|
||||
<value enum="4" description="SELL_PLUS"/>
|
||||
<value enum="5" description="SELL_SHORT"/>
|
||||
<value enum="6" description="SELL_SHORT_EXEMPT"/>
|
||||
</field>
|
||||
<field number="55" name="Symbol" type="STRING"/>
|
||||
<field number="56" name="TargetCompID" type="STRING"/>
|
||||
<field number="57" name="TargetSubID" type="STRING"/>
|
||||
<field number="58" name="Text" type="STRING"/>
|
||||
<field number="59" name="TimeInForce" type="STRING">
|
||||
<value enum="0" description="DAY"/>
|
||||
<value enum="1" description="GOOD_TILL_CANCEL"/>
|
||||
<value enum="2" description="AT_THE_OPENING"/>
|
||||
<value enum="3" description="IMMEDIATE_OR_CANCEL"/>
|
||||
<value enum="4" description="FILL_OR_KILL"/>
|
||||
<value enum="5" description="GOOD_TILL_CROSSING"/>
|
||||
<value enum="6" description="GOOD_TILL_DATE"/>
|
||||
</field>
|
||||
<field number="60" name="TransactTime" type="TIME"/>
|
||||
<field number="61" name="Urgency" type="STRING">
|
||||
<value enum="0" description="NORMAL"/>
|
||||
<value enum="1" description="FLASH"/>
|
||||
<value enum="2" description="BACKGROUND"/>
|
||||
</field>
|
||||
<field number="62" name="ValidUntilTime" type="TIME"/>
|
||||
<field number="63" name="SettlmntTyp" type="STRING">
|
||||
<value enum="0" description="REGULAR"/>
|
||||
<value enum="1" description="CASH"/>
|
||||
<value enum="2" description="NEXT_DAY"/>
|
||||
<value enum="3" description="TPLUS2"/>
|
||||
<value enum="4" description="TPLUS3"/>
|
||||
<value enum="5" description="TPLUS4"/>
|
||||
<value enum="6" description="FUTURE"/>
|
||||
<value enum="7" description="WHEN_ISSUED"/>
|
||||
<value enum="8" description="SELLERS_OPTION"/>
|
||||
<value enum="9" description="TPLUS5"/>
|
||||
</field>
|
||||
<field number="64" name="FutSettDate" type="DATE"/>
|
||||
<field number="65" name="SymbolSfx" type="STRING"/>
|
||||
<field number="66" name="ListID" type="STRING"/>
|
||||
<field number="67" name="ListSeqNo" type="INT"/>
|
||||
<field number="68" name="ListNoOrds" type="INT"/>
|
||||
<field number="69" name="ListExecInst" type="STRING"/>
|
||||
<field number="70" name="AllocID" type="INT"/>
|
||||
<field number="71" name="AllocTransType" type="STRING">
|
||||
<value enum="0" description="NEW"/>
|
||||
<value enum="1" description="REPLACE"/>
|
||||
<value enum="2" description="CANCEL"/>
|
||||
</field>
|
||||
<field number="72" name="RefAllocID" type="INT"/>
|
||||
<field number="73" name="NoOrders" type="INT"/>
|
||||
<field number="74" name="AvgPrxPrecision" type="INT"/>
|
||||
<field number="75" name="TradeDate" type="DATE"/>
|
||||
<field number="76" name="ExecBroker" type="STRING"/>
|
||||
<field number="77" name="OpenClose" type="STRING"/>
|
||||
<field number="78" name="NoAllocs" type="INT"/>
|
||||
<field number="79" name="AllocAccount" type="STRING"/>
|
||||
<field number="80" name="AllocShares" type="INT"/>
|
||||
<field number="81" name="ProcessCode" type="STRING">
|
||||
<value enum="0" description="REGULAR"/>
|
||||
<value enum="1" description="SOFT_DOLLAR"/>
|
||||
<value enum="2" description="STEPIN"/>
|
||||
<value enum="3" description="STEPOUT"/>
|
||||
<value enum="4" description="SOFTDOLLAR_STEPIN"/>
|
||||
<value enum="5" description="SOFTDOLLAR_STEPOUT"/>
|
||||
<value enum="6" description="PLAN_SPONSOR"/>
|
||||
</field>
|
||||
<field number="82" name="NoRpts" type="INT"/>
|
||||
<field number="83" name="RptSeq" type="INT"/>
|
||||
<field number="84" name="CxlQty" type="INT"/>
|
||||
<field number="85" name="NoDlvyInst" type="INT"/>
|
||||
<field number="86" name="DlvyInst" type="STRING"/>
|
||||
<field number="87" name="AllocStatus" type="INT">
|
||||
<value enum="0" description="ACCEPTED"/>
|
||||
<value enum="1" description="REJECTED"/>
|
||||
<value enum="2" description="PARTIAL_ACCEPT"/>
|
||||
<value enum="3" description="RECEIVED"/>
|
||||
</field>
|
||||
<field number="88" name="AllocRejCode" type="INT">
|
||||
<value enum="0" description="UNKNOWN_ACCOUNT"/>
|
||||
<value enum="1" description="INCORRECT_QUANTITY"/>
|
||||
<value enum="2" description="INCORRECT_AVERAGE_PRICE"/>
|
||||
<value enum="3" description="UNKNOWN_EXECUTING_BROKER_MNEMONIC"/>
|
||||
<value enum="4" description="COMMISSION_DIFFERENCE"/>
|
||||
<value enum="5" description="UNKNOWN_ORDERID"/>
|
||||
<value enum="6" description="UNKNOWN_LISTID"/>
|
||||
<value enum="7" description="OTHER"/>
|
||||
</field>
|
||||
<field number="89" name="Signature" type="DATA"/>
|
||||
<field number="90" name="SecureDataLen" type="INT"/>
|
||||
<field number="91" name="SecureData" type="DATA"/>
|
||||
<field number="92" name="BrokerOfCredit" type="STRING"/>
|
||||
<field number="93" name="SignatureLength" type="INT"/>
|
||||
<field number="94" name="EmailType" type="STRING">
|
||||
<value enum="0" description="NEW"/>
|
||||
<value enum="1" description="REPLY"/>
|
||||
<value enum="2" description="ADMIN_REPLY"/>
|
||||
</field>
|
||||
<field number="95" name="RawDataLength" type="INT"/>
|
||||
<field number="96" name="RawData" type="DATA"/>
|
||||
<field number="97" name="PossResend" type="STRING"/>
|
||||
<field number="98" name="EncryptMethod" type="INT">
|
||||
<value enum="0" description="NONE_OTHER"/>
|
||||
<value enum="1" description="PKCS"/>
|
||||
<value enum="2" description="DES"/>
|
||||
<value enum="3" description="PKCSDES"/>
|
||||
<value enum="4" description="PGPDES"/>
|
||||
<value enum="5" description="PGPDESMD5"/>
|
||||
<value enum="6" description="PEMDESMD5"/>
|
||||
</field>
|
||||
<field number="99" name="StopPx" type="FLOAT"/>
|
||||
<field number="100" name="ExDestination" type="STRING">
|
||||
<value enum="0" description="NONE"/>
|
||||
<value enum="4" description="POSIT"/>
|
||||
</field>
|
||||
<field number="102" name="CxlRejReason" type="INT">
|
||||
<value enum="0" description="TOO_LATE_TO_CANCEL"/>
|
||||
<value enum="1" description="UNKNOWN_ORDER"/>
|
||||
</field>
|
||||
<field number="103" name="OrdRejReason" type="INT">
|
||||
<value enum="0" description="BROKER_OPTION"/>
|
||||
<value enum="1" description="UNKNOWN_SYMBOL"/>
|
||||
<value enum="2" description="EXCHANGE_CLOSED"/>
|
||||
<value enum="3" description="ORDER_EXCEEDS_LIMIT"/>
|
||||
<value enum="4" description="TOO_LATE_TO_ENTER"/>
|
||||
</field>
|
||||
<field number="104" name="IOIQualifier" type="STRING">
|
||||
<value enum="X" description="CROSSING_OPPORTUNITY"/>
|
||||
<value enum="O" description="AT_THE_OPEN"/>
|
||||
<value enum="M" description="MORE_BEHIND"/>
|
||||
<value enum="P" description="TAKING_A_POSITION"/>
|
||||
<value enum="V" description="VERSUS"/>
|
||||
<value enum="Q" description="CURRENT_QUOTE"/>
|
||||
<value enum="C" description="AT_THE_CLOSE"/>
|
||||
<value enum="S" description="PORTFOLIO_SHOWN"/>
|
||||
<value enum="I" description="IN_TOUCH_WITH"/>
|
||||
<value enum="W" description="INDICATION_WORKING_AWAY"/>
|
||||
<value enum="A" description="ALL_OR_NONE"/>
|
||||
<value enum="L" description="LIMIT"/>
|
||||
<value enum="T" description="THROUGH_THE_DAY"/>
|
||||
</field>
|
||||
<field number="105" name="WaveNo" type="STRING"/>
|
||||
<field number="106" name="Issuer" type="STRING"/>
|
||||
<field number="107" name="SecurityDesc" type="STRING"/>
|
||||
<field number="108" name="HeartBtInt" type="INT"/>
|
||||
<field number="109" name="ClientID" type="STRING"/>
|
||||
<field number="110" name="MinQty" type="INT"/>
|
||||
<field number="111" name="MaxFloor" type="INT"/>
|
||||
<field number="112" name="TestReqID" type="STRING"/>
|
||||
<field number="113" name="ReportToExch" type="STRING">
|
||||
<value enum="Y" description="YES"/>
|
||||
<value enum="N" description="NO"/>
|
||||
</field>
|
||||
<field number="114" name="LocateReqd" type="STRING">
|
||||
<value enum="Y" description="YES"/>
|
||||
<value enum="N" description="NO"/>
|
||||
</field>
|
||||
<field number="115" name="OnBehalfOfCompID" type="STRING"/>
|
||||
<field number="116" name="OnBehalfOfSubID" type="STRING"/>
|
||||
<field number="117" name="QuoteID" type="STRING"/>
|
||||
<field number="118" name="NetMoney" type="FLOAT"/>
|
||||
<field number="119" name="SettlCurrAmt" type="FLOAT"/>
|
||||
<field number="120" name="SettlCurrency" type="STRING"/>
|
||||
<field number="121" name="ForexReq" type="STRING">
|
||||
<value enum="Y" description="YES"/>
|
||||
<value enum="N" description="NO"/>
|
||||
</field>
|
||||
<field number="122" name="OrigSendingTime" type="TIME"/>
|
||||
<field number="123" name="GapFillFlag" type="STRING">
|
||||
<value enum="Y" description="GAP_FILL_MESSAGE_MSGSEQNUM_FIELD_VALID"/>
|
||||
<value enum="N" description="SEQUENCE_RESET_IGNORE_MSGSEQNUM"/>
|
||||
</field>
|
||||
<field number="124" name="NoExecs" type="INT"/>
|
||||
<field number="125" name="CxlType" type="STRING">
|
||||
<value enum="P" description="PARTIAL_CANCEL"/>
|
||||
<value enum="F" description="FULL_REMAINING_QUANTITY"/>
|
||||
</field>
|
||||
<field number="126" name="ExpireTime" type="TIME"/>
|
||||
<field number="127" name="DKReason" type="STRING">
|
||||
<value enum="A" description="UNKNOWN_SYMBOL"/>
|
||||
<value enum="B" description="WRONG_SIDE"/>
|
||||
<value enum="C" description="QUANTITY_EXCEEDS_ORDER"/>
|
||||
<value enum="D" description="NO_MATCHING_ORDER"/>
|
||||
<value enum="E" description="PRICE_EXCEEDS_LIMIT"/>
|
||||
<value enum="Z" description="OTHER" />
|
||||
</field>
|
||||
<field number="128" name="DeliverToCompID" type="STRING"/>
|
||||
<field number="129" name="DeliverToSubID" type="STRING"/>
|
||||
<field number="130" name="IOINaturalFlag" type="STRING">
|
||||
<value enum="Y" description="NATURAL"/>
|
||||
<value enum="N" description="NOT_NATURAL"/>
|
||||
</field>
|
||||
<field number="131" name="QuoteReqID" type="STRING"/>
|
||||
<field number="132" name="BidPx" type="FLOAT"/>
|
||||
<field number="133" name="OfferPx" type="FLOAT"/>
|
||||
<field number="134" name="BidSize" type="INT"/>
|
||||
<field number="135" name="OfferSize" type="INT"/>
|
||||
<field number="136" name="NoMiscFees" type="INT"/>
|
||||
<field number="137" name="MiscFeeAmt" type="FLOAT"/>
|
||||
<field number="138" name="MiscFeeCurr" type="STRING"/>
|
||||
<field number="139" name="MiscFeeType" type="STRING">
|
||||
<value enum="1" description="REGULATORY"/>
|
||||
<value enum="2" description="TAX"/>
|
||||
<value enum="3" description="LOCAL_COMMISSION"/>
|
||||
<value enum="4" description="EXCHANGE_FEES"/>
|
||||
<value enum="5" description="STAMP"/>
|
||||
<value enum="6" description="LEVY"/>
|
||||
<value enum="7" description="OTHER"/>
|
||||
</field>
|
||||
<field number="140" name="PrevClosePx" type="FLOAT"/>
|
||||
</fields>
|
||||
</fix>
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,313 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<fix type='FIXT' major='1' minor='1' servicepack='0'>
|
||||
<header>
|
||||
<field name='BeginString' required='Y'/>
|
||||
<field name='BodyLength' required='Y'/>
|
||||
<field name='MsgType' required='Y'/>
|
||||
<field name='SenderCompID' required='Y'/>
|
||||
<field name='TargetCompID' required='Y'/>
|
||||
<field name='OnBehalfOfCompID' required='N'/>
|
||||
<field name='DeliverToCompID' required='N'/>
|
||||
<field name='SecureDataLen' required='N'/>
|
||||
<field name='SecureData' required='N'/>
|
||||
<field name='MsgSeqNum' required='Y'/>
|
||||
<field name='SenderSubID' required='N'/>
|
||||
<field name='SenderLocationID' required='N'/>
|
||||
<field name='TargetSubID' required='N'/>
|
||||
<field name='TargetLocationID' required='N'/>
|
||||
<field name='OnBehalfOfSubID' required='N'/>
|
||||
<field name='OnBehalfOfLocationID' required='N'/>
|
||||
<field name='DeliverToSubID' required='N'/>
|
||||
<field name='DeliverToLocationID' required='N'/>
|
||||
<field name='PossDupFlag' required='N'/>
|
||||
<field name='PossResend' required='N'/>
|
||||
<field name='SendingTime' required='Y'/>
|
||||
<field name='OrigSendingTime' required='N'/>
|
||||
<field name='XmlDataLen' required='N'/>
|
||||
<field name='XmlData' required='N'/>
|
||||
<field name='MessageEncoding' required='N'/>
|
||||
<field name='LastMsgSeqNumProcessed' required='N'/>
|
||||
<component name='HopGrp' required='N'/>
|
||||
<field name='ApplVerID' required='N'/>
|
||||
<field name='CstmApplVerID' required='N'/>
|
||||
</header>
|
||||
<trailer>
|
||||
<field name='SignatureLength' required='N'/>
|
||||
<field name='Signature' required='N'/>
|
||||
<field name='CheckSum' required='Y'/>
|
||||
</trailer>
|
||||
<messages>
|
||||
<message msgcat='admin' msgtype='0' name='Heartbeat'>
|
||||
<field name='TestReqID' required='N'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='1' name='TestRequest'>
|
||||
<field name='TestReqID' required='Y'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='2' name='ResendRequest'>
|
||||
<field name='BeginSeqNo' required='Y'/>
|
||||
<field name='EndSeqNo' required='Y'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='3' name='Reject'>
|
||||
<field name='RefSeqNum' required='Y'/>
|
||||
<field name='RefTagID' required='N'/>
|
||||
<field name='RefMsgType' required='N'/>
|
||||
<field name='SessionRejectReason' required='N'/>
|
||||
<field name='Text' required='N'/>
|
||||
<field name='EncodedTextLen' required='N'/>
|
||||
<field name='EncodedText' required='N'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='4' name='SequenceReset'>
|
||||
<field name='GapFillFlag' required='N'/>
|
||||
<field name='NewSeqNo' required='Y'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='5' name='Logout'>
|
||||
<field name='Text' required='N'/>
|
||||
<field name='EncodedTextLen' required='N'/>
|
||||
<field name='EncodedText' required='N'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='A' name='Logon'>
|
||||
<field name='EncryptMethod' required='Y'/>
|
||||
<field name='HeartBtInt' required='Y'/>
|
||||
<field name='RawDataLength' required='N'/>
|
||||
<field name='RawData' required='N'/>
|
||||
<field name='ResetSeqNumFlag' required='N'/>
|
||||
<field name='NextExpectedMsgSeqNum' required='N'/>
|
||||
<field name='MaxMessageSize' required='N'/>
|
||||
<field name='TestMessageIndicator' required='N'/>
|
||||
<field name='Username' required='N'/>
|
||||
<field name='Password' required='N'/>
|
||||
<field name='DefaultApplVerID' required='Y'/>
|
||||
<component name='MsgTypeGrp' required='N'/>
|
||||
</message>
|
||||
</messages>
|
||||
<components>
|
||||
<component name='HopGrp'>
|
||||
<group name='NoHops' required='N'>
|
||||
<field name='HopCompID' required='N'/>
|
||||
<field name='HopSendingTime' required='N'/>
|
||||
<field name='HopRefID' required='N'/>
|
||||
</group>
|
||||
</component>
|
||||
<component name='MsgTypeGrp'>
|
||||
<group name='NoMsgTypes' required='N'>
|
||||
<field name='RefMsgType' required='N'/>
|
||||
<field name='MsgDirection' required='N'/>
|
||||
<field name='RefApplVerID' required='N'/>
|
||||
<field name='RefCstmApplVerID' required='N'/>
|
||||
</group>
|
||||
</component>
|
||||
</components>
|
||||
<fields>
|
||||
<field name='BeginSeqNo' number='7' type='SEQNUM'/>
|
||||
<field name='BeginString' number='8' type='STRING'/>
|
||||
<field name='BodyLength' number='9' type='LENGTH'/>
|
||||
<field name='CheckSum' number='10' type='STRING'/>
|
||||
<field name='EndSeqNo' number='16' type='SEQNUM'/>
|
||||
<field name='MsgSeqNum' number='34' type='SEQNUM'/>
|
||||
<field number='35' name='MsgType' type='STRING'>
|
||||
<value enum='0' description='HEARTBEAT'/>
|
||||
<value enum='1' description='TEST_REQUEST'/>
|
||||
<value enum='2' description='RESEND_REQUEST'/>
|
||||
<value enum='3' description='REJECT'/>
|
||||
<value enum='4' description='SEQUENCE_RESET'/>
|
||||
<value enum='5' description='LOGOUT'/>
|
||||
<value enum='6' description='INDICATION_OF_INTEREST'/>
|
||||
<value enum='7' description='ADVERTISEMENT'/>
|
||||
<value enum='8' description='EXECUTION_REPORT'/>
|
||||
<value enum='9' description='ORDER_CANCEL_REJECT'/>
|
||||
<value enum='A' description='LOGON'/>
|
||||
<value enum='B' description='NEWS'/>
|
||||
<value enum='C' description='EMAIL'/>
|
||||
<value enum='D' description='ORDER_SINGLE'/>
|
||||
<value enum='E' description='ORDER_LIST'/>
|
||||
<value enum='F' description='ORDER_CANCEL_REQUEST'/>
|
||||
<value enum='G' description='ORDER_CANCEL_REPLACE_REQUEST'/>
|
||||
<value enum='H' description='ORDER_STATUS_REQUEST'/>
|
||||
<value enum='J' description='ALLOCATION_INSTRUCTION'/>
|
||||
<value enum='K' description='LIST_CANCEL_REQUEST'/>
|
||||
<value enum='L' description='LIST_EXECUTE'/>
|
||||
<value enum='M' description='LIST_STATUS_REQUEST'/>
|
||||
<value enum='N' description='LIST_STATUS'/>
|
||||
<value enum='P' description='ALLOCATION_INSTRUCTION_ACK'/>
|
||||
<value enum='Q' description='DONT_KNOW_TRADE'/>
|
||||
<value enum='R' description='QUOTE_REQUEST'/>
|
||||
<value enum='S' description='QUOTE'/>
|
||||
<value enum='T' description='SETTLEMENT_INSTRUCTIONS'/>
|
||||
<value enum='V' description='MARKET_DATA_REQUEST'/>
|
||||
<value enum='W' description='MARKET_DATA_SNAPSHOT_FULL_REFRESH'/>
|
||||
<value enum='X' description='MARKET_DATA_INCREMENTAL_REFRESH'/>
|
||||
<value enum='Y' description='MARKET_DATA_REQUEST_REJECT'/>
|
||||
<value enum='Z' description='QUOTE_CANCEL'/>
|
||||
<value enum='a' description='QUOTE_STATUS_REQUEST'/>
|
||||
<value enum='b' description='MASS_QUOTE_ACKNOWLEDGEMENT'/>
|
||||
<value enum='c' description='SECURITY_DEFINITION_REQUEST'/>
|
||||
<value enum='d' description='SECURITY_DEFINITION'/>
|
||||
<value enum='e' description='SECURITY_STATUS_REQUEST'/>
|
||||
<value enum='f' description='SECURITY_STATUS'/>
|
||||
<value enum='g' description='TRADING_SESSION_STATUS_REQUEST'/>
|
||||
<value enum='h' description='TRADING_SESSION_STATUS'/>
|
||||
<value enum='i' description='MASS_QUOTE'/>
|
||||
<value enum='j' description='BUSINESS_MESSAGE_REJECT'/>
|
||||
<value enum='k' description='BID_REQUEST'/>
|
||||
<value enum='l' description='BID_RESPONSE'/>
|
||||
<value enum='m' description='LIST_STRIKE_PRICE'/>
|
||||
<value enum='n' description='XML_MESSAGE'/>
|
||||
<value enum='o' description='REGISTRATION_INSTRUCTIONS'/>
|
||||
<value enum='p' description='REGISTRATION_INSTRUCTIONS_RESPONSE'/>
|
||||
<value enum='q' description='ORDER_MASS_CANCEL_REQUEST'/>
|
||||
<value enum='r' description='ORDER_MASS_CANCEL_REPORT'/>
|
||||
<value enum='s' description='NEW_ORDER_CROSS'/>
|
||||
<value enum='t' description='CROSS_ORDER_CANCEL_REPLACE_REQUEST'/>
|
||||
<value enum='u' description='CROSS_ORDER_CANCEL_REQUEST'/>
|
||||
<value enum='v' description='SECURITY_TYPE_REQUEST'/>
|
||||
<value enum='w' description='SECURITY_TYPES'/>
|
||||
<value enum='x' description='SECURITY_LIST_REQUEST'/>
|
||||
<value enum='y' description='SECURITY_LIST'/>
|
||||
<value enum='z' description='DERIVATIVE_SECURITY_LIST_REQUEST'/>
|
||||
<value enum='AA' description='DERIVATIVE_SECURITY_LIST'/>
|
||||
<value enum='AB' description='NEW_ORDER_MULTILEG'/>
|
||||
<value enum='AC' description='MULTILEG_ORDER_CANCEL_REPLACE'/>
|
||||
<value enum='AD' description='TRADE_CAPTURE_REPORT_REQUEST'/>
|
||||
<value enum='AE' description='TRADE_CAPTURE_REPORT'/>
|
||||
<value enum='AF' description='ORDER_MASS_STATUS_REQUEST'/>
|
||||
<value enum='AG' description='QUOTE_REQUEST_REJECT'/>
|
||||
<value enum='AH' description='RFQ_REQUEST'/>
|
||||
<value enum='AI' description='QUOTE_STATUS_REPORT'/>
|
||||
<value enum='AJ' description='QUOTE_RESPONSE'/>
|
||||
<value enum='AK' description='CONFIRMATION'/>
|
||||
<value enum='AL' description='POSITION_MAINTENANCE_REQUEST'/>
|
||||
<value enum='AM' description='POSITION_MAINTENANCE_REPORT'/>
|
||||
<value enum='AN' description='REQUEST_FOR_POSITIONS'/>
|
||||
<value enum='AO' description='REQUEST_FOR_POSITIONS_ACK'/>
|
||||
<value enum='AP' description='POSITION_REPORT'/>
|
||||
<value enum='AQ' description='TRADE_CAPTURE_REPORT_REQUEST_ACK'/>
|
||||
<value enum='AR' description='TRADE_CAPTURE_REPORT_ACK'/>
|
||||
<value enum='AS' description='ALLOCATION_REPORT'/>
|
||||
<value enum='AT' description='ALLOCATION_REPORT_ACK'/>
|
||||
<value enum='AU' description='CONFIRMATION_ACK'/>
|
||||
<value enum='AV' description='SETTLEMENT_INSTRUCTION_REQUEST'/>
|
||||
<value enum='AW' description='ASSIGNMENT_REPORT'/>
|
||||
<value enum='AX' description='COLLATERAL_REQUEST'/>
|
||||
<value enum='AY' description='COLLATERAL_ASSIGNMENT'/>
|
||||
<value enum='AZ' description='COLLATERAL_RESPONSE'/>
|
||||
<value enum='BA' description='COLLATERAL_REPORT'/>
|
||||
<value enum='BB' description='COLLATERAL_INQUIRY'/>
|
||||
<value enum='BC' description='NETWORK_STATUS_REQUEST'/>
|
||||
<value enum='BD' description='NETWORK_STATUS_RESPONSE'/>
|
||||
<value enum='BE' description='USER_REQUEST'/>
|
||||
<value enum='BF' description='USER_RESPONSE'/>
|
||||
<value enum='BG' description='COLLATERAL_INQUIRY_ACK'/>
|
||||
<value enum='BH' description='CONFIRMATION_REQUEST'/>
|
||||
<value enum='BI' description='TRADING_SESSION_LIST_REQUEST'/>
|
||||
<value enum='BJ' description='TRADING_SESSION_LIST'/>
|
||||
<value enum='BK' description='SECURITY_LIST_UPDATE_REPORT'/>
|
||||
<value enum='BL' description='ADJUSTED_POSITION_REPORT'/>
|
||||
<value enum='BM' description='ALLOCATION_INSTRUCTION_ALERT'/>
|
||||
<value enum='BN' description='EXECUTION_ACKNOWLEDGEMENT'/>
|
||||
<value enum='BO' description='CONTRARY_INTENTION_REPORT'/>
|
||||
<value enum='BP' description='SECURITY_DEFINITION_UPDATE_REPORT'/>
|
||||
</field>
|
||||
<field name='NewSeqNo' number='36' type='SEQNUM'/>
|
||||
<field name='PossDupFlag' number='43' type='BOOLEAN'/>
|
||||
<field name='RefSeqNum' number='45' type='SEQNUM'/>
|
||||
<field name='SenderCompID' number='49' type='STRING'/>
|
||||
<field name='SenderSubID' number='50' type='STRING'/>
|
||||
<field name='SendingTime' number='52' type='UTCTIMESTAMP'/>
|
||||
<field name='TargetCompID' number='56' type='STRING'/>
|
||||
<field name='TargetSubID' number='57' type='STRING'/>
|
||||
<field name='Text' number='58' type='STRING'/>
|
||||
<field name='Signature' number='89' type='DATA'/>
|
||||
<field name='SecureDataLen' number='90' type='LENGTH'/>
|
||||
<field name='SecureData' number='91' type='DATA'/>
|
||||
<field name='SignatureLength' number='93' type='LENGTH'/>
|
||||
<field name='RawDataLength' number='95' type='LENGTH'/>
|
||||
<field name='RawData' number='96' type='DATA'/>
|
||||
<field name='PossResend' number='97' type='BOOLEAN'/>
|
||||
<field name='EncryptMethod' number='98' type='INT'>
|
||||
<value description='NONE_OTHER' enum='0'/>
|
||||
<value description='PKCS' enum='1'/>
|
||||
<value description='DES' enum='2'/>
|
||||
<value description='PKCS_DES' enum='3'/>
|
||||
<value description='PGP_DES' enum='4'/>
|
||||
<value description='PGP_DES_MD5' enum='5'/>
|
||||
<value description='PEM_DES_MD5' enum='6'/>
|
||||
</field>
|
||||
<field name='HeartBtInt' number='108' type='INT'/>
|
||||
<field name='TestReqID' number='112' type='STRING'/>
|
||||
<field name='OnBehalfOfCompID' number='115' type='STRING'/>
|
||||
<field name='OnBehalfOfSubID' number='116' type='STRING'/>
|
||||
<field name='OrigSendingTime' number='122' type='UTCTIMESTAMP'/>
|
||||
<field name='GapFillFlag' number='123' type='BOOLEAN'/>
|
||||
<field name='DeliverToCompID' number='128' type='STRING'/>
|
||||
<field name='DeliverToSubID' number='129' type='STRING'/>
|
||||
<field name='ResetSeqNumFlag' number='141' type='BOOLEAN'/>
|
||||
<field name='SenderLocationID' number='142' type='STRING'/>
|
||||
<field name='TargetLocationID' number='143' type='STRING'/>
|
||||
<field name='OnBehalfOfLocationID' number='144' type='STRING'/>
|
||||
<field name='DeliverToLocationID' number='145' type='STRING'/>
|
||||
<field name='XmlDataLen' number='212' type='LENGTH'/>
|
||||
<field name='XmlData' number='213' type='DATA'/>
|
||||
<field number='347' name='MessageEncoding' type='STRING'>
|
||||
<value enum='ISO-2022-JP' description='ISO_2022_JP'/>
|
||||
<value enum='EUC-JP' description='EUC_JP'/>
|
||||
<value enum='SHIFT_JIS' description='SHIFT_JIS'/>
|
||||
<value enum='UTF-8' description='UTF_8'/>
|
||||
</field>
|
||||
<field name='EncodedTextLen' number='354' type='LENGTH'/>
|
||||
<field name='EncodedText' number='355' type='DATA'/>
|
||||
<field name='LastMsgSeqNumProcessed' number='369' type='SEQNUM'/>
|
||||
<field name='RefTagID' number='371' type='INT'/>
|
||||
<field name='RefMsgType' number='372' type='STRING'/>
|
||||
<field name='SessionRejectReason' number='373' type='INT'>
|
||||
<value description='INVALID_TAG_NUMBER' enum='0'/>
|
||||
<value description='REQUIRED_TAG_MISSING' enum='1'/>
|
||||
<value description='SENDINGTIME_ACCURACY_PROBLEM' enum='10'/>
|
||||
<value description='INVALID_MSGTYPE' enum='11'/>
|
||||
<value description='XML_VALIDATION_ERROR' enum='12'/>
|
||||
<value description='TAG_APPEARS_MORE_THAN_ONCE' enum='13'/>
|
||||
<value description='TAG_SPECIFIED_OUT_OF_REQUIRED_ORDER' enum='14'/>
|
||||
<value description='REPEATING_GROUP_FIELDS_OUT_OF_ORDER' enum='15'/>
|
||||
<value description='INCORRECT_NUMINGROUP_COUNT_FOR_REPEATING_GROUP' enum='16'/>
|
||||
<value description='NON_DATA_VALUE_INCLUDES_FIELD_DELIMITER' enum='17'/>
|
||||
<value description='TAG_NOT_DEFINED_FOR_THIS_MESSAGE_TYPE' enum='2'/>
|
||||
<value description='UNDEFINED_TAG' enum='3'/>
|
||||
<value description='TAG_SPECIFIED_WITHOUT_A_VALUE' enum='4'/>
|
||||
<value description='VALUE_IS_INCORRECT' enum='5'/>
|
||||
<value description='INCORRECT_DATA_FORMAT_FOR_VALUE' enum='6'/>
|
||||
<value description='DECRYPTION_PROBLEM' enum='7'/>
|
||||
<value description='SIGNATURE_PROBLEM' enum='8'/>
|
||||
<value description='COMPID_PROBLEM' enum='9'/>
|
||||
<value description='OTHER' enum='99'/>
|
||||
</field>
|
||||
<field name='MaxMessageSize' number='383' type='LENGTH'/>
|
||||
<field name='NoMsgTypes' number='384' type='NUMINGROUP'/>
|
||||
<field name='MsgDirection' number='385' type='CHAR'>
|
||||
<value description='RECEIVE' enum='R'/>
|
||||
<value description='SEND' enum='S'/>
|
||||
</field>
|
||||
<field name='TestMessageIndicator' number='464' type='BOOLEAN'/>
|
||||
<field name='Username' number='553' type='STRING'/>
|
||||
<field name='Password' number='554' type='STRING'/>
|
||||
<field name='NoHops' number='627' type='NUMINGROUP'/>
|
||||
<field name='HopCompID' number='628' type='STRING'/>
|
||||
<field name='HopSendingTime' number='629' type='UTCTIMESTAMP'/>
|
||||
<field name='HopRefID' number='630' type='SEQNUM'/>
|
||||
<field name='NextExpectedMsgSeqNum' number='789' type='SEQNUM'/>
|
||||
<field name='ApplVerID' number='1128' type='STRING'>
|
||||
<value description='FIX27' enum='0'/>
|
||||
<value description='FIX30' enum='1'/>
|
||||
<value description='FIX40' enum='2'/>
|
||||
<value description='FIX41' enum='3'/>
|
||||
<value description='FIX42' enum='4'/>
|
||||
<value description='FIX43' enum='5'/>
|
||||
<value description='FIX44' enum='6'/>
|
||||
<value description='FIX50' enum='7'/>
|
||||
<value description='FIX50SP1' enum='8'/>
|
||||
<value description='FIX50SP2' enum='9'/>
|
||||
</field>
|
||||
<field name='CstmApplVerID' number='1129' type='STRING'/>
|
||||
<field name='RefApplVerID' number='1130' type='STRING'/>
|
||||
<field name='RefCstmApplVerID' number='1131' type='STRING'/>
|
||||
<field name='DefaultApplVerID' number='1137' type='STRING'/>
|
||||
</fields>
|
||||
</fix>
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
|
||||
<Platform>x64</Platform>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using CommandLine;
|
||||
|
||||
using bts.fix;
|
||||
|
||||
namespace bts
|
||||
{
|
||||
[Verb("fix", HelpText = "Fix command")]
|
||||
public class FIXCommandOptions
|
||||
{
|
||||
[Option('f', "config", Required = true)]
|
||||
public string config_file { get; set; }
|
||||
|
||||
[Option('i', "initiator", Default = false, Required = false)]
|
||||
public bool initiator{ get; set; }
|
||||
|
||||
[Option('w', "http", Required = false)]
|
||||
public string http{ get; set; }
|
||||
|
||||
[Option('o', "order_session", Default = false, Required = false)]
|
||||
public bool order_session{ get; set; }
|
||||
|
||||
[Option('d', "dropcopy_only", Default = false, Required = false)]
|
||||
public bool dropcopy_only { get; set; }
|
||||
}
|
||||
|
||||
public class FIXCommand
|
||||
{
|
||||
public static int Run(FIXCommandOptions opt)
|
||||
{
|
||||
if (opt.order_session)
|
||||
{
|
||||
Console.WriteLine("Starting FIX ORDERS Acceptor using " + opt.config_file);
|
||||
if(!string.IsNullOrEmpty(opt.http))
|
||||
Console.WriteLine(" Http server " + opt.http);
|
||||
return FixInit.start_acceptor(opt.config_file, true, (string.IsNullOrEmpty(opt.http) ? null : opt.http));
|
||||
}
|
||||
else if (opt.dropcopy_only)
|
||||
{
|
||||
Console.WriteLine("Starting FIX DROP Acceptor using " + opt.config_file);
|
||||
if (!string.IsNullOrEmpty(opt.http))
|
||||
Console.WriteLine(" Http server " + opt.http);
|
||||
return FixInit.start_acceptor(opt.config_file, false, (string.IsNullOrEmpty(opt.http) ? null : opt.http));
|
||||
}
|
||||
else if (opt.initiator)
|
||||
{
|
||||
Console.WriteLine("Initiator not supported yet");
|
||||
return -1;
|
||||
}
|
||||
string[] args = Parser.Default.FormatCommandLine(opt).Split(' ');
|
||||
var res = Parser.Default.ParseArguments<FIXCommandOptions>(args);
|
||||
var help = CommandLine.Text.HelpText.AutoBuild(res, h => h, r => r).ToString();
|
||||
Console.WriteLine(help);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,8 @@ namespace sparkcmd {
|
||||
SettingsManagerOptions,
|
||||
TestCommandOptions,
|
||||
OrderStressOptions,
|
||||
MBOCommandOptions
|
||||
MBOCommandOptions,
|
||||
FIXCommandOptions
|
||||
>(BTS.remove_config_arg(args))
|
||||
.MapResult<
|
||||
ConfigcheckOptions,
|
||||
@@ -55,6 +56,7 @@ namespace sparkcmd {
|
||||
TestCommandOptions,
|
||||
OrderStressOptions,
|
||||
MBOCommandOptions,
|
||||
FIXCommandOptions,
|
||||
int>(
|
||||
Configcheck.Run,
|
||||
ScriptClientCommand.Run,
|
||||
@@ -64,6 +66,7 @@ namespace sparkcmd {
|
||||
TestCommand.Run,
|
||||
OrderStress.Run,
|
||||
MBOCommand.Run,
|
||||
FIXCommand.Run,
|
||||
errors => 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
<ProjectReference Include="../spark_api/spark_api.csproj" />
|
||||
<!--<ProjectReference Include="..\spark\Spark.csproj" />-->
|
||||
<ProjectReference Include="..\spark_core\spark_core.csproj" />
|
||||
<ProjectReference Include="..\spark_fix\spark_fix.csproj" />
|
||||
<ProjectReference Include="..\spark_test\spark_test.csproj" />
|
||||
<ProjectReference Include="../spark_fix/spark_fix.csproj" />
|
||||
<!--<Proto3 Include="**/*.proto" />-->
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#http://www.quickfixengine.org/quickfix/doc/html/configuration.html
|
||||
#https://www.onixs.biz/fix-dictionary.html
|
||||
|
||||
[DEFAULT]
|
||||
ConnectionType=acceptor
|
||||
SocketAcceptPort=5001
|
||||
SocketReuseAddress=Y
|
||||
StartTime=00:00:00
|
||||
EndTime=00:00:00
|
||||
UseDataDictionary=Y
|
||||
ResetOnLogon=Y
|
||||
ResetOnLogout=Y
|
||||
ResetOnDisconnect=Y
|
||||
HeartBtInt=30
|
||||
LogonTimeout=30
|
||||
FileLogPath=data_files/fix/log/
|
||||
FileStorePath=data_files/fix/store/
|
||||
|
||||
SSLEnable=N
|
||||
# SSLEnable=Y
|
||||
# # It is recommended to install the certificate and refer to it by name instead of using filename + password
|
||||
# SSLCertificate=QuickFixn-TestServer.pfx
|
||||
# SSLCertificatePassword=QuickFixn-TestServer
|
||||
# # For production refer to certificate by name instead: SSLCertificate=CN=QuickFixn-TestServer
|
||||
# SSLCACertificate=QuickFixn-TestCA.cer
|
||||
# SSLCheckCertificateRevocation=N
|
||||
|
||||
|
||||
[SESSION]
|
||||
BeginString=FIX.4.4
|
||||
SenderCompID=SPARKFIX
|
||||
TargetCompID=CLIENT1
|
||||
DataDictionary=data_files/fix/spec/fix/FIX44.xml
|
||||
|
||||
[SESSION]
|
||||
BeginString=FIX.4.4
|
||||
SenderCompID=SPARKFIX
|
||||
TargetCompID=CLIENT2
|
||||
DataDictionary=data_files/fix/spec/fix/FIX44.xml
|
||||
|
||||
|
||||
@@ -0,0 +1,828 @@
|
||||
<fix major="4" minor="0">
|
||||
<header>
|
||||
<field name="BeginString" required="Y"/>
|
||||
<field name="BodyLength" required="Y"/>
|
||||
<field name="MsgType" required="Y"/>
|
||||
<field name="SenderCompID" required="Y"/>
|
||||
<field name="TargetCompID" required="Y"/>
|
||||
<field name="OnBehalfOfCompID" required="N"/>
|
||||
<field name="DeliverToCompID" required="N"/>
|
||||
<field name="SecureDataLen" required="N"/>
|
||||
<field name="SecureData" required="N"/>
|
||||
<field name="MsgSeqNum" required="Y"/>
|
||||
<field name="SenderSubID" required="N"/>
|
||||
<field name="TargetSubID" required="N"/>
|
||||
<field name="OnBehalfOfSubID" required="N"/>
|
||||
<field name="DeliverToSubID" required="N"/>
|
||||
<field name="PossDupFlag" required="N"/>
|
||||
<field name="PossResend" required="N"/>
|
||||
<field name="SendingTime" required="Y"/>
|
||||
<field name="OrigSendingTime" required="N"/>
|
||||
</header>
|
||||
<trailer>
|
||||
<field name="SignatureLength" required="N"/>
|
||||
<field name="Signature" required="N"/>
|
||||
<field name="CheckSum" required="Y"/>
|
||||
</trailer>
|
||||
<messages>
|
||||
<message name="Heartbeat" msgtype="0" msgcat="admin">
|
||||
<field name="TestReqID" required="N"/>
|
||||
</message>
|
||||
<message name="Logon" msgtype="A" msgcat="admin">
|
||||
<field name="EncryptMethod" required="Y"/>
|
||||
<field name="HeartBtInt" required="Y"/>
|
||||
<field name="RawDataLength" required="N"/>
|
||||
<field name="RawData" required="N"/>
|
||||
</message>
|
||||
<message name="TestRequest" msgtype="1" msgcat="admin">
|
||||
<field name="TestReqID" required="Y"/>
|
||||
</message>
|
||||
<message name="ResendRequest" msgtype="2" msgcat="admin">
|
||||
<field name="BeginSeqNo" required="Y"/>
|
||||
<field name="EndSeqNo" required="Y"/>
|
||||
</message>
|
||||
<message name="Reject" msgtype="3" msgcat="admin">
|
||||
<field name="RefSeqNum" required="Y"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="SequenceReset" msgtype="4" msgcat="admin">
|
||||
<field name="GapFillFlag" required="N"/>
|
||||
<field name="NewSeqNo" required="Y"/>
|
||||
</message>
|
||||
<message name="Logout" msgtype="5" msgcat="admin">
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="Advertisement" msgtype="7" msgcat="app">
|
||||
<field name="AdvId" required="Y"/>
|
||||
<field name="AdvTransType" required="Y"/>
|
||||
<field name="AdvRefID" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="AdvSide" required="Y"/>
|
||||
<field name="Shares" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="TransactTime" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="IndicationofInterest" msgtype="6" msgcat="app">
|
||||
<field name="IOIid" required="Y"/>
|
||||
<field name="IOITransType" required="Y"/>
|
||||
<field name="IOIRefID" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="IOIShares" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="ValidUntilTime" required="N"/>
|
||||
<field name="IOIQltyInd" required="N"/>
|
||||
<field name="IOIOthSvc" required="N"/>
|
||||
<field name="IOINaturalFlag" required="N"/>
|
||||
<field name="IOIQualifier" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="News" msgtype="B" msgcat="app">
|
||||
<field name="OrigTime" required="N"/>
|
||||
<field name="Urgency" required="N"/>
|
||||
<field name="RelatdSym" required="N" repeating="Y"/>
|
||||
<group name="LinesOfText" required="Y">
|
||||
<field name="Text" required="Y"/>
|
||||
</group>
|
||||
<field name="RawDataLength" required="N"/>
|
||||
<field name="RawData" required="N"/>
|
||||
</message>
|
||||
<message name="Email" msgtype="C" msgcat="app">
|
||||
<field name="EmailType" required="Y"/>
|
||||
<field name="OrigTime" required="N"/>
|
||||
<field name="RelatdSym" required="N" repeating="Y"/>
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ClOrdID" required="N"/>
|
||||
<group name="LinesOfText" required="Y">
|
||||
<field name="Text" required="Y"/>
|
||||
</group>
|
||||
<field name="RawDataLength" required="N"/>
|
||||
<field name="RawData" required="N"/>
|
||||
</message>
|
||||
<message name="QuoteRequest" msgtype="R" msgcat="app">
|
||||
<field name="QuoteReqID" required="Y"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="PrevClosePx" required="N"/>
|
||||
<field name="Side" required="N"/>
|
||||
<field name="OrderQty" required="N"/>
|
||||
</message>
|
||||
<message name="Quote" msgtype="S" msgcat="app">
|
||||
<field name="QuoteReqID" required="N"/>
|
||||
<field name="QuoteID" required="Y"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="BidPx" required="Y"/>
|
||||
<field name="OfferPx" required="N"/>
|
||||
<field name="BidSize" required="N"/>
|
||||
<field name="OfferSize" required="N"/>
|
||||
<field name="ValidUntilTime" required="N"/>
|
||||
</message>
|
||||
<message name="NewOrderSingle" msgtype="D" msgcat="app">
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="Account" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="HandlInst" required="Y"/>
|
||||
<field name="ExecInst" required="N"/>
|
||||
<field name="MinQty" required="N"/>
|
||||
<field name="MaxFloor" required="N"/>
|
||||
<field name="ExDestination" required="N"/>
|
||||
<field name="ProcessCode" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="PrevClosePx" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="LocateReqd" required="N"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="OrdType" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="StopPx" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="IOIid" required="N"/>
|
||||
<field name="QuoteID" required="N"/>
|
||||
<field name="TimeInForce" required="N"/>
|
||||
<field name="ExpireTime" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<field name="Rule80A" required="N"/>
|
||||
<field name="ForexReq" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="ExecutionReport" msgtype="8" msgcat="app">
|
||||
<field name="OrderID" required="Y"/>
|
||||
<field name="ClOrdID" required="N"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="ExecID" required="Y"/>
|
||||
<field name="ExecTransType" required="Y"/>
|
||||
<field name="ExecRefID" required="N"/>
|
||||
<field name="OrdStatus" required="Y"/>
|
||||
<field name="OrdRejReason" required="N"/>
|
||||
<field name="Account" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="OrdType" required="N"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="StopPx" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="TimeInForce" required="N"/>
|
||||
<field name="ExpireTime" required="N"/>
|
||||
<field name="ExecInst" required="N"/>
|
||||
<field name="Rule80A" required="N"/>
|
||||
<field name="LastShares" required="Y"/>
|
||||
<field name="LastPx" required="Y"/>
|
||||
<field name="LastMkt" required="N"/>
|
||||
<field name="LastCapacity" required="N"/>
|
||||
<field name="CumQty" required="Y"/>
|
||||
<field name="AvgPx" required="Y"/>
|
||||
<field name="TradeDate" required="N"/>
|
||||
<field name="TransactTime" required="N"/>
|
||||
<field name="ReportToExch" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<group name="NoMiscFees" required="N">
|
||||
<field name="MiscFeeAmt" required="N"/>
|
||||
<field name="MiscFeeCurr" required="N"/>
|
||||
<field name="MiscFeeType" required="N"/>
|
||||
</group>
|
||||
<field name="NetMoney" required="N"/>
|
||||
<field name="SettlCurrAmt" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="DontKnowTrade" msgtype="Q" msgcat="app">
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ExecID" required="N"/>
|
||||
<field name="DKReason" required="Y"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="LastShares" required="Y"/>
|
||||
<field name="LastPx" required="Y"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="OrderCancelReplaceRequest" msgtype="G" msgcat="app">
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="OrigClOrdID" required="Y"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="Account" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="HandlInst" required="Y"/>
|
||||
<field name="ExecInst" required="N"/>
|
||||
<field name="MinQty" required="N"/>
|
||||
<field name="MaxFloor" required="N"/>
|
||||
<field name="ExDestination" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="OrdType" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="StopPx" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="TimeInForce" required="N"/>
|
||||
<field name="ExpireTime" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<field name="Rule80A" required="N"/>
|
||||
<field name="ForexReq" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="OrderCancelRequest" msgtype="F" msgcat="app">
|
||||
<field name="OrigClOrdID" required="Y"/>
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="CxlType" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="OrderCancelReject" msgtype="9" msgcat="app">
|
||||
<field name="OrderID" required="Y"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="CxlRejReason" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="OrderStatusRequest" msgtype="H" msgcat="app">
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
</message>
|
||||
<message name="Allocation" msgtype="J" msgcat="app">
|
||||
<field name="AllocID" required="Y"/>
|
||||
<field name="AllocTransType" required="Y"/>
|
||||
<field name="RefAllocID" required="N"/>
|
||||
<group name="NoOrders" required="Y">
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="OrderID" required="N"/>
|
||||
<field name="ListID" required="N"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
</group>
|
||||
<group name="NoExecs" required="N">
|
||||
<field name="ExecID" required="N"/>
|
||||
<field name="LastShares" required="N"/>
|
||||
<field name="LastPx" required="N"/>
|
||||
<field name="LastMkt" required="N"/>
|
||||
</group>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="Shares" required="Y"/>
|
||||
<field name="AvgPx" required="Y"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="AvgPrxPrecision" required="N"/>
|
||||
<field name="TradeDate" required="Y"/>
|
||||
<field name="TransactTime" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="NetMoney" required="N"/>
|
||||
<group name="NoMiscFees" required="N">
|
||||
<field name="MiscFeeAmt" required="N"/>
|
||||
<field name="MiscFeeCurr" required="N"/>
|
||||
<field name="MiscFeeType" required="N"/>
|
||||
</group>
|
||||
<field name="SettlCurrAmt" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="OpenClose" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
<group name="NoAllocs" required="Y">
|
||||
<field name="AllocAccount" required="Y"/>
|
||||
<field name="AllocShares" required="Y"/>
|
||||
<field name="ProcessCode" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<group name="NoDlvyInst" required="N">
|
||||
<field name="BrokerOfCredit" required="N"/>
|
||||
<field name="DlvyInst" required="N"/>
|
||||
</group>
|
||||
</group>
|
||||
</message>
|
||||
<message name="AllocationACK" msgtype="P" msgcat="app">
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="AllocID" required="Y"/>
|
||||
<field name="TradeDate" required="Y"/>
|
||||
<field name="TransactTime" required="N"/>
|
||||
<field name="AllocStatus" required="Y"/>
|
||||
<field name="AllocRejCode" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="NewOrderList" msgtype="E" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="ListSeqNo" required="Y"/>
|
||||
<field name="ListNoOrds" required="Y"/>
|
||||
<field name="ListExecInst" required="N"/>
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="ClientID" required="N"/>
|
||||
<field name="ExecBroker" required="N"/>
|
||||
<field name="Account" required="N"/>
|
||||
<field name="SettlmntTyp" required="N"/>
|
||||
<field name="FutSettDate" required="N"/>
|
||||
<field name="HandlInst" required="Y"/>
|
||||
<field name="ExecInst" required="N"/>
|
||||
<field name="MinQty" required="N"/>
|
||||
<field name="MaxFloor" required="N"/>
|
||||
<field name="ExDestination" required="N"/>
|
||||
<field name="ProcessCode" required="N"/>
|
||||
<field name="Symbol" required="Y"/>
|
||||
<field name="SymbolSfx" required="N"/>
|
||||
<field name="SecurityID" required="N"/>
|
||||
<field name="IDSource" required="N"/>
|
||||
<field name="Issuer" required="N"/>
|
||||
<field name="SecurityDesc" required="N"/>
|
||||
<field name="PrevClosePx" required="N"/>
|
||||
<field name="Side" required="Y"/>
|
||||
<field name="LocateReqd" required="N"/>
|
||||
<field name="OrderQty" required="Y"/>
|
||||
<field name="OrdType" required="Y"/>
|
||||
<field name="Price" required="N"/>
|
||||
<field name="StopPx" required="N"/>
|
||||
<field name="Currency" required="N"/>
|
||||
<field name="TimeInForce" required="N"/>
|
||||
<field name="ExpireTime" required="N"/>
|
||||
<field name="Commission" required="N"/>
|
||||
<field name="CommType" required="N"/>
|
||||
<field name="Rule80A" required="N"/>
|
||||
<field name="ForexReq" required="N"/>
|
||||
<field name="SettlCurrency" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="ListStatus" msgtype="N" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="NoRpts" required="Y"/>
|
||||
<field name="RptSeq" required="Y"/>
|
||||
<group name="NoOrders" required="Y">
|
||||
<field name="ClOrdID" required="Y"/>
|
||||
<field name="CumQty" required="Y"/>
|
||||
<field name="CxlQty" required="Y"/>
|
||||
<field name="AvgPx" required="Y"/>
|
||||
</group>
|
||||
</message>
|
||||
<message name="ListExecute" msgtype="L" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="ListCancelRequest" msgtype="K" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
<message name="ListStatusRequest" msgtype="M" msgcat="app">
|
||||
<field name="ListID" required="Y"/>
|
||||
<field name="WaveNo" required="N"/>
|
||||
<field name="Text" required="N"/>
|
||||
</message>
|
||||
</messages>
|
||||
<fields>
|
||||
<field number="1" name="Account" type="STRING"/>
|
||||
<field number="2" name="AdvId" type="INT"/>
|
||||
<field number="3" name="AdvRefID" type="INT"/>
|
||||
<field number="4" name="AdvSide" type="STRING">
|
||||
<value enum="B" description="BUY"/>
|
||||
<value enum="S" description="SELL"/>
|
||||
<value enum="X" description="CROSS"/>
|
||||
<value enum="T" description="TRADE"/>
|
||||
</field>
|
||||
<field number="5" name="AdvTransType" type="STRING">
|
||||
<value enum="N" description="NEW"/>
|
||||
<value enum="C" description="CANCEL"/>
|
||||
<value enum="R" description="REPLACE"/>
|
||||
</field>
|
||||
<field number="6" name="AvgPx" type="FLOAT"/>
|
||||
<field number="7" name="BeginSeqNo" type="INT"/>
|
||||
<field number="8" name="BeginString" type="STRING"/>
|
||||
<field number="9" name="BodyLength" type="INT"/>
|
||||
<field number="10" name="CheckSum" type="STRING"/>
|
||||
<field number="11" name="ClOrdID" type="STRING"/>
|
||||
<field number="12" name="Commission" type="FLOAT"/>
|
||||
<field number="13" name="CommType" type="STRING">
|
||||
<value enum="1" description="PER_SHARE"/>
|
||||
<value enum="2" description="PERCENTAGE"/>
|
||||
<value enum="3" description="ABSOLUTE"/>
|
||||
</field>
|
||||
<field number="14" name="CumQty" type="INT"/>
|
||||
<field number="15" name="Currency" type="STRING"/>
|
||||
<field number="16" name="EndSeqNo" type="INT"/>
|
||||
<field number="17" name="ExecID" type="INT"/>
|
||||
<field number="18" name="ExecInst" type="MULTIPLEVALUESTRING">
|
||||
<value enum="1" description="NOT_HELD"/>
|
||||
<value enum="2" description="WORK"/>
|
||||
<value enum="3" description="GO_ALONG"/>
|
||||
<value enum="4" description="OVER_THE_DAY"/>
|
||||
<value enum="5" description="HELD"/>
|
||||
<value enum="6" description="PARTICIPATE_DONT_INITIATE"/>
|
||||
<value enum="7" description="STRICT_SCALE"/>
|
||||
<value enum="8" description="TRY_TO_SCALE"/>
|
||||
<value enum="9" description="STAY_ON_BIDSIDE"/>
|
||||
<value enum="0" description="STAY_ON_OFFERSIDE"/>
|
||||
<value enum="A" description="NO_CROSS"/>
|
||||
<value enum="B" description="OK_TO_CROSS"/>
|
||||
<value enum="C" description="CALL_FIRST"/>
|
||||
<value enum="D" description="PERCENT_OF_VOLUME"/>
|
||||
<value enum="E" description="DO_NOT_INCREASE_DNI"/>
|
||||
<value enum="F" description="DO_NOT_REDUCE_DNR"/>
|
||||
<value enum="G" description="ALL_OR_NONE_AON"/>
|
||||
<value enum="I" description="INSTITUTIONS_ONLY"/>
|
||||
<value enum="L" description="LAST_PEG"/>
|
||||
<value enum="M" description="MIDPRICE_PEG"/>
|
||||
<value enum="N" description="NONNEGOTIABLE"/>
|
||||
<value enum="O" description="OPENING_PEG"/>
|
||||
<value enum="P" description="MARKET_PEG"/>
|
||||
<value enum="R" description="PRIMARY_PEG"/>
|
||||
<value enum="S" description="SUSPEND"/>
|
||||
</field>
|
||||
<field number="19" name="ExecRefID" type="INT"/>
|
||||
<field number="20" name="ExecTransType" type="STRING">
|
||||
<value enum="0" description="NEW"/>
|
||||
<value enum="1" description="CANCEL"/>
|
||||
<value enum="2" description="CORRECT"/>
|
||||
<value enum="3" description="STATUS"/>
|
||||
</field>
|
||||
<field number="21" name="HandlInst" type="STRING">
|
||||
<value enum="1" description="AUTOMATED_EXECUTION_ORDER_PRIVATE_NO_BROKER_INTERVENTION"/>
|
||||
<value enum="2" description="AUTOMATED_EXECUTION_ORDER_PUBLIC_BROKER_INTERVENTION_OK"/>
|
||||
<value enum="3" description="MANUAL_ORDER_BEST_EXECUTION"/>
|
||||
</field>
|
||||
<field number="22" name="IDSource" type="STRING">
|
||||
<value enum="1" description="CUSIP"/>
|
||||
<value enum="2" description="SEDOL"/>
|
||||
<value enum="3" description="QUIK"/>
|
||||
<value enum="4" description="ISIN_NUMBER"/>
|
||||
<value enum="5" description="RIC_CODE"/>
|
||||
</field>
|
||||
<field number="23" name="IOIid" type="INT"/>
|
||||
<field number="24" name="IOIOthSvc" type="STRING"/>
|
||||
<field number="25" name="IOIQltyInd" type="STRING">
|
||||
<value enum="L" description="LOW"/>
|
||||
<value enum="M" description="MEDIUM"/>
|
||||
<value enum="H" description="HIGH"/>
|
||||
</field>
|
||||
<field number="26" name="IOIRefID" type="INT"/>
|
||||
<field number="27" name="IOIShares" type="STRING"/>
|
||||
<field number="28" name="IOITransType" type="STRING">
|
||||
<value enum="N" description="NEW"/>
|
||||
<value enum="C" description="CANCEL"/>
|
||||
<value enum="R" description="REPLACE"/>
|
||||
</field>
|
||||
<field number="29" name="LastCapacity" type="STRING">
|
||||
<value enum="1" description="AGENT"/>
|
||||
<value enum="2" description="CROSS_AS_AGENT"/>
|
||||
<value enum="3" description="CROSS_AS_PRINCIPAL"/>
|
||||
<value enum="4" description="PRINCIPAL"/>
|
||||
</field>
|
||||
<field number="30" name="LastMkt" type="STRING"/>
|
||||
<field number="31" name="LastPx" type="FLOAT"/>
|
||||
<field number="32" name="LastShares" type="INT"/>
|
||||
<field number="33" name="LinesOfText" type="INT"/>
|
||||
<field number="34" name="MsgSeqNum" type="INT"/>
|
||||
<field number="35" name="MsgType" type="STRING"/>
|
||||
<field number="36" name="NewSeqNo" type="INT"/>
|
||||
<field number="37" name="OrderID" type="STRING"/>
|
||||
<field number="38" name="OrderQty" type="INT"/>
|
||||
<field number="39" name="OrdStatus" type="STRING">
|
||||
<value enum="0" description="NEW"/>
|
||||
<value enum="1" description="PARTIALLY_FILLED"/>
|
||||
<value enum="2" description="FILLED"/>
|
||||
<value enum="3" description="DONE_FOR_DAY"/>
|
||||
<value enum="4" description="CANCELED"/>
|
||||
<value enum="5" description="REPLACED"/>
|
||||
<value enum="6" description="PENDING_CANCELREPLACE"/>
|
||||
<value enum="7" description="STOPPED"/>
|
||||
<value enum="8" description="REJECTED"/>
|
||||
<value enum="9" description="SUSPENDED"/>
|
||||
<value enum="A" description="PENDING_NEW"/>
|
||||
<value enum="B" description="CALCULATED"/>
|
||||
<value enum="C" description="EXPIRED"/>
|
||||
</field>
|
||||
<field number="40" name="OrdType" type="STRING">
|
||||
<value enum="1" description="MARKET"/>
|
||||
<value enum="2" description="LIMIT"/>
|
||||
<value enum="3" description="STOP"/>
|
||||
<value enum="4" description="STOP_LIMIT"/>
|
||||
<value enum="5" description="MARKET_ON_CLOSE"/>
|
||||
<value enum="6" description="WITH_OR_WITHOUT"/>
|
||||
<value enum="7" description="LIMIT_OR_BETTER"/>
|
||||
<value enum="8" description="LIMIT_WITH_OR_WITHOUT"/>
|
||||
<value enum="9" description="ON_BASIS"/>
|
||||
<value enum="A" description="ON_CLOSE"/>
|
||||
<value enum="B" description="LIMIT_ON_CLOSE"/>
|
||||
<value enum="C" description="FOREX"/>
|
||||
<value enum="D" description="PREVIOUSLY_QUOTED"/>
|
||||
<value enum="E" description="PREVIOUSLY_INDICATED"/>
|
||||
<value enum="P" description="PEGGED"/>
|
||||
</field>
|
||||
<field number="41" name="OrigClOrdID" type="STRING"/>
|
||||
<field number="42" name="OrigTime" type="TIME"/>
|
||||
<field number="43" name="PossDupFlag" type="STRING">
|
||||
<value enum="Y" description="POSSIBLE_DUPLICATE"/>
|
||||
<value enum="N" description="ORIGINAL_TRANSMISSION"/>
|
||||
</field>
|
||||
<field number="44" name="Price" type="FLOAT"/>
|
||||
<field number="45" name="RefSeqNum" type="INT"/>
|
||||
<field number="46" name="RelatdSym" type="STRING"/>
|
||||
<field number="47" name="Rule80A" type="STRING">
|
||||
<value enum="A" description="AGENCY_SINGLE_ORDER"/>
|
||||
<value enum="D" description="PROGRAM_ORDER_INDEX_ARB_FOR_MEMBER_FIRMORG"/>
|
||||
<value enum="C" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_MEMBER_FIRMORG"/>
|
||||
<value enum="J" description="PROGRAM_ORDER_INDEX_ARB_FOR_INDIVIDUAL_CUSTOMER"/>
|
||||
<value enum="K" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_INDIVIDUAL_CUSTOMER"/>
|
||||
<value enum="U" description="PROGRAM_ORDER_INDEX_ARB_FOR_OTHER_AGENCY"/>
|
||||
<value enum="Y" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_OTHER_AGENCY"/>
|
||||
<value enum="M" description="PROGRAM_ORDER_INDEX_ARB_FOR_OTHER_MEMBER"/>
|
||||
<value enum="N" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_OTHER_MEMBER"/>
|
||||
<value enum="W" description="ALL_OTHER_ORDERS_AS_AGENT_FOR_OTHER_MEMBER"/>
|
||||
</field>
|
||||
<field number="48" name="SecurityID" type="STRING"/>
|
||||
<field number="49" name="SenderCompID" type="STRING"/>
|
||||
<field number="50" name="SenderSubID" type="STRING"/>
|
||||
<field number="52" name="SendingTime" type="TIME"/>
|
||||
<field number="53" name="Shares" type="INT"/>
|
||||
<field number="54" name="Side" type="STRING">
|
||||
<value enum="1" description="BUY"/>
|
||||
<value enum="2" description="SELL"/>
|
||||
<value enum="3" description="BUY_MINUS"/>
|
||||
<value enum="4" description="SELL_PLUS"/>
|
||||
<value enum="5" description="SELL_SHORT"/>
|
||||
<value enum="6" description="SELL_SHORT_EXEMPT"/>
|
||||
</field>
|
||||
<field number="55" name="Symbol" type="STRING"/>
|
||||
<field number="56" name="TargetCompID" type="STRING"/>
|
||||
<field number="57" name="TargetSubID" type="STRING"/>
|
||||
<field number="58" name="Text" type="STRING"/>
|
||||
<field number="59" name="TimeInForce" type="STRING">
|
||||
<value enum="0" description="DAY"/>
|
||||
<value enum="1" description="GOOD_TILL_CANCEL"/>
|
||||
<value enum="2" description="AT_THE_OPENING"/>
|
||||
<value enum="3" description="IMMEDIATE_OR_CANCEL"/>
|
||||
<value enum="4" description="FILL_OR_KILL"/>
|
||||
<value enum="5" description="GOOD_TILL_CROSSING"/>
|
||||
<value enum="6" description="GOOD_TILL_DATE"/>
|
||||
</field>
|
||||
<field number="60" name="TransactTime" type="TIME"/>
|
||||
<field number="61" name="Urgency" type="STRING">
|
||||
<value enum="0" description="NORMAL"/>
|
||||
<value enum="1" description="FLASH"/>
|
||||
<value enum="2" description="BACKGROUND"/>
|
||||
</field>
|
||||
<field number="62" name="ValidUntilTime" type="TIME"/>
|
||||
<field number="63" name="SettlmntTyp" type="STRING">
|
||||
<value enum="0" description="REGULAR"/>
|
||||
<value enum="1" description="CASH"/>
|
||||
<value enum="2" description="NEXT_DAY"/>
|
||||
<value enum="3" description="TPLUS2"/>
|
||||
<value enum="4" description="TPLUS3"/>
|
||||
<value enum="5" description="TPLUS4"/>
|
||||
<value enum="6" description="FUTURE"/>
|
||||
<value enum="7" description="WHEN_ISSUED"/>
|
||||
<value enum="8" description="SELLERS_OPTION"/>
|
||||
<value enum="9" description="TPLUS5"/>
|
||||
</field>
|
||||
<field number="64" name="FutSettDate" type="DATE"/>
|
||||
<field number="65" name="SymbolSfx" type="STRING"/>
|
||||
<field number="66" name="ListID" type="STRING"/>
|
||||
<field number="67" name="ListSeqNo" type="INT"/>
|
||||
<field number="68" name="ListNoOrds" type="INT"/>
|
||||
<field number="69" name="ListExecInst" type="STRING"/>
|
||||
<field number="70" name="AllocID" type="INT"/>
|
||||
<field number="71" name="AllocTransType" type="STRING">
|
||||
<value enum="0" description="NEW"/>
|
||||
<value enum="1" description="REPLACE"/>
|
||||
<value enum="2" description="CANCEL"/>
|
||||
</field>
|
||||
<field number="72" name="RefAllocID" type="INT"/>
|
||||
<field number="73" name="NoOrders" type="INT"/>
|
||||
<field number="74" name="AvgPrxPrecision" type="INT"/>
|
||||
<field number="75" name="TradeDate" type="DATE"/>
|
||||
<field number="76" name="ExecBroker" type="STRING"/>
|
||||
<field number="77" name="OpenClose" type="STRING"/>
|
||||
<field number="78" name="NoAllocs" type="INT"/>
|
||||
<field number="79" name="AllocAccount" type="STRING"/>
|
||||
<field number="80" name="AllocShares" type="INT"/>
|
||||
<field number="81" name="ProcessCode" type="STRING">
|
||||
<value enum="0" description="REGULAR"/>
|
||||
<value enum="1" description="SOFT_DOLLAR"/>
|
||||
<value enum="2" description="STEPIN"/>
|
||||
<value enum="3" description="STEPOUT"/>
|
||||
<value enum="4" description="SOFTDOLLAR_STEPIN"/>
|
||||
<value enum="5" description="SOFTDOLLAR_STEPOUT"/>
|
||||
<value enum="6" description="PLAN_SPONSOR"/>
|
||||
</field>
|
||||
<field number="82" name="NoRpts" type="INT"/>
|
||||
<field number="83" name="RptSeq" type="INT"/>
|
||||
<field number="84" name="CxlQty" type="INT"/>
|
||||
<field number="85" name="NoDlvyInst" type="INT"/>
|
||||
<field number="86" name="DlvyInst" type="STRING"/>
|
||||
<field number="87" name="AllocStatus" type="INT">
|
||||
<value enum="0" description="ACCEPTED"/>
|
||||
<value enum="1" description="REJECTED"/>
|
||||
<value enum="2" description="PARTIAL_ACCEPT"/>
|
||||
<value enum="3" description="RECEIVED"/>
|
||||
</field>
|
||||
<field number="88" name="AllocRejCode" type="INT">
|
||||
<value enum="0" description="UNKNOWN_ACCOUNT"/>
|
||||
<value enum="1" description="INCORRECT_QUANTITY"/>
|
||||
<value enum="2" description="INCORRECT_AVERAGE_PRICE"/>
|
||||
<value enum="3" description="UNKNOWN_EXECUTING_BROKER_MNEMONIC"/>
|
||||
<value enum="4" description="COMMISSION_DIFFERENCE"/>
|
||||
<value enum="5" description="UNKNOWN_ORDERID"/>
|
||||
<value enum="6" description="UNKNOWN_LISTID"/>
|
||||
<value enum="7" description="OTHER"/>
|
||||
</field>
|
||||
<field number="89" name="Signature" type="DATA"/>
|
||||
<field number="90" name="SecureDataLen" type="INT"/>
|
||||
<field number="91" name="SecureData" type="DATA"/>
|
||||
<field number="92" name="BrokerOfCredit" type="STRING"/>
|
||||
<field number="93" name="SignatureLength" type="INT"/>
|
||||
<field number="94" name="EmailType" type="STRING">
|
||||
<value enum="0" description="NEW"/>
|
||||
<value enum="1" description="REPLY"/>
|
||||
<value enum="2" description="ADMIN_REPLY"/>
|
||||
</field>
|
||||
<field number="95" name="RawDataLength" type="INT"/>
|
||||
<field number="96" name="RawData" type="DATA"/>
|
||||
<field number="97" name="PossResend" type="STRING"/>
|
||||
<field number="98" name="EncryptMethod" type="INT">
|
||||
<value enum="0" description="NONE_OTHER"/>
|
||||
<value enum="1" description="PKCS"/>
|
||||
<value enum="2" description="DES"/>
|
||||
<value enum="3" description="PKCSDES"/>
|
||||
<value enum="4" description="PGPDES"/>
|
||||
<value enum="5" description="PGPDESMD5"/>
|
||||
<value enum="6" description="PEMDESMD5"/>
|
||||
</field>
|
||||
<field number="99" name="StopPx" type="FLOAT"/>
|
||||
<field number="100" name="ExDestination" type="STRING">
|
||||
<value enum="0" description="NONE"/>
|
||||
<value enum="4" description="POSIT"/>
|
||||
</field>
|
||||
<field number="102" name="CxlRejReason" type="INT">
|
||||
<value enum="0" description="TOO_LATE_TO_CANCEL"/>
|
||||
<value enum="1" description="UNKNOWN_ORDER"/>
|
||||
</field>
|
||||
<field number="103" name="OrdRejReason" type="INT">
|
||||
<value enum="0" description="BROKER_OPTION"/>
|
||||
<value enum="1" description="UNKNOWN_SYMBOL"/>
|
||||
<value enum="2" description="EXCHANGE_CLOSED"/>
|
||||
<value enum="3" description="ORDER_EXCEEDS_LIMIT"/>
|
||||
<value enum="4" description="TOO_LATE_TO_ENTER"/>
|
||||
</field>
|
||||
<field number="104" name="IOIQualifier" type="STRING">
|
||||
<value enum="X" description="CROSSING_OPPORTUNITY"/>
|
||||
<value enum="O" description="AT_THE_OPEN"/>
|
||||
<value enum="M" description="MORE_BEHIND"/>
|
||||
<value enum="P" description="TAKING_A_POSITION"/>
|
||||
<value enum="V" description="VERSUS"/>
|
||||
<value enum="Q" description="CURRENT_QUOTE"/>
|
||||
<value enum="C" description="AT_THE_CLOSE"/>
|
||||
<value enum="S" description="PORTFOLIO_SHOWN"/>
|
||||
<value enum="I" description="IN_TOUCH_WITH"/>
|
||||
<value enum="W" description="INDICATION_WORKING_AWAY"/>
|
||||
<value enum="A" description="ALL_OR_NONE"/>
|
||||
<value enum="L" description="LIMIT"/>
|
||||
<value enum="T" description="THROUGH_THE_DAY"/>
|
||||
</field>
|
||||
<field number="105" name="WaveNo" type="STRING"/>
|
||||
<field number="106" name="Issuer" type="STRING"/>
|
||||
<field number="107" name="SecurityDesc" type="STRING"/>
|
||||
<field number="108" name="HeartBtInt" type="INT"/>
|
||||
<field number="109" name="ClientID" type="STRING"/>
|
||||
<field number="110" name="MinQty" type="INT"/>
|
||||
<field number="111" name="MaxFloor" type="INT"/>
|
||||
<field number="112" name="TestReqID" type="STRING"/>
|
||||
<field number="113" name="ReportToExch" type="STRING">
|
||||
<value enum="Y" description="YES"/>
|
||||
<value enum="N" description="NO"/>
|
||||
</field>
|
||||
<field number="114" name="LocateReqd" type="STRING">
|
||||
<value enum="Y" description="YES"/>
|
||||
<value enum="N" description="NO"/>
|
||||
</field>
|
||||
<field number="115" name="OnBehalfOfCompID" type="STRING"/>
|
||||
<field number="116" name="OnBehalfOfSubID" type="STRING"/>
|
||||
<field number="117" name="QuoteID" type="STRING"/>
|
||||
<field number="118" name="NetMoney" type="FLOAT"/>
|
||||
<field number="119" name="SettlCurrAmt" type="FLOAT"/>
|
||||
<field number="120" name="SettlCurrency" type="STRING"/>
|
||||
<field number="121" name="ForexReq" type="STRING">
|
||||
<value enum="Y" description="YES"/>
|
||||
<value enum="N" description="NO"/>
|
||||
</field>
|
||||
<field number="122" name="OrigSendingTime" type="TIME"/>
|
||||
<field number="123" name="GapFillFlag" type="STRING">
|
||||
<value enum="Y" description="GAP_FILL_MESSAGE_MSGSEQNUM_FIELD_VALID"/>
|
||||
<value enum="N" description="SEQUENCE_RESET_IGNORE_MSGSEQNUM"/>
|
||||
</field>
|
||||
<field number="124" name="NoExecs" type="INT"/>
|
||||
<field number="125" name="CxlType" type="STRING">
|
||||
<value enum="P" description="PARTIAL_CANCEL"/>
|
||||
<value enum="F" description="FULL_REMAINING_QUANTITY"/>
|
||||
</field>
|
||||
<field number="126" name="ExpireTime" type="TIME"/>
|
||||
<field number="127" name="DKReason" type="STRING">
|
||||
<value enum="A" description="UNKNOWN_SYMBOL"/>
|
||||
<value enum="B" description="WRONG_SIDE"/>
|
||||
<value enum="C" description="QUANTITY_EXCEEDS_ORDER"/>
|
||||
<value enum="D" description="NO_MATCHING_ORDER"/>
|
||||
<value enum="E" description="PRICE_EXCEEDS_LIMIT"/>
|
||||
<value enum="Z" description="OTHER" />
|
||||
</field>
|
||||
<field number="128" name="DeliverToCompID" type="STRING"/>
|
||||
<field number="129" name="DeliverToSubID" type="STRING"/>
|
||||
<field number="130" name="IOINaturalFlag" type="STRING">
|
||||
<value enum="Y" description="NATURAL"/>
|
||||
<value enum="N" description="NOT_NATURAL"/>
|
||||
</field>
|
||||
<field number="131" name="QuoteReqID" type="STRING"/>
|
||||
<field number="132" name="BidPx" type="FLOAT"/>
|
||||
<field number="133" name="OfferPx" type="FLOAT"/>
|
||||
<field number="134" name="BidSize" type="INT"/>
|
||||
<field number="135" name="OfferSize" type="INT"/>
|
||||
<field number="136" name="NoMiscFees" type="INT"/>
|
||||
<field number="137" name="MiscFeeAmt" type="FLOAT"/>
|
||||
<field number="138" name="MiscFeeCurr" type="STRING"/>
|
||||
<field number="139" name="MiscFeeType" type="STRING">
|
||||
<value enum="1" description="REGULATORY"/>
|
||||
<value enum="2" description="TAX"/>
|
||||
<value enum="3" description="LOCAL_COMMISSION"/>
|
||||
<value enum="4" description="EXCHANGE_FEES"/>
|
||||
<value enum="5" description="STAMP"/>
|
||||
<value enum="6" description="LEVY"/>
|
||||
<value enum="7" description="OTHER"/>
|
||||
</field>
|
||||
<field number="140" name="PrevClosePx" type="FLOAT"/>
|
||||
</fields>
|
||||
</fix>
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,313 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<fix type='FIXT' major='1' minor='1' servicepack='0'>
|
||||
<header>
|
||||
<field name='BeginString' required='Y'/>
|
||||
<field name='BodyLength' required='Y'/>
|
||||
<field name='MsgType' required='Y'/>
|
||||
<field name='SenderCompID' required='Y'/>
|
||||
<field name='TargetCompID' required='Y'/>
|
||||
<field name='OnBehalfOfCompID' required='N'/>
|
||||
<field name='DeliverToCompID' required='N'/>
|
||||
<field name='SecureDataLen' required='N'/>
|
||||
<field name='SecureData' required='N'/>
|
||||
<field name='MsgSeqNum' required='Y'/>
|
||||
<field name='SenderSubID' required='N'/>
|
||||
<field name='SenderLocationID' required='N'/>
|
||||
<field name='TargetSubID' required='N'/>
|
||||
<field name='TargetLocationID' required='N'/>
|
||||
<field name='OnBehalfOfSubID' required='N'/>
|
||||
<field name='OnBehalfOfLocationID' required='N'/>
|
||||
<field name='DeliverToSubID' required='N'/>
|
||||
<field name='DeliverToLocationID' required='N'/>
|
||||
<field name='PossDupFlag' required='N'/>
|
||||
<field name='PossResend' required='N'/>
|
||||
<field name='SendingTime' required='Y'/>
|
||||
<field name='OrigSendingTime' required='N'/>
|
||||
<field name='XmlDataLen' required='N'/>
|
||||
<field name='XmlData' required='N'/>
|
||||
<field name='MessageEncoding' required='N'/>
|
||||
<field name='LastMsgSeqNumProcessed' required='N'/>
|
||||
<component name='HopGrp' required='N'/>
|
||||
<field name='ApplVerID' required='N'/>
|
||||
<field name='CstmApplVerID' required='N'/>
|
||||
</header>
|
||||
<trailer>
|
||||
<field name='SignatureLength' required='N'/>
|
||||
<field name='Signature' required='N'/>
|
||||
<field name='CheckSum' required='Y'/>
|
||||
</trailer>
|
||||
<messages>
|
||||
<message msgcat='admin' msgtype='0' name='Heartbeat'>
|
||||
<field name='TestReqID' required='N'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='1' name='TestRequest'>
|
||||
<field name='TestReqID' required='Y'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='2' name='ResendRequest'>
|
||||
<field name='BeginSeqNo' required='Y'/>
|
||||
<field name='EndSeqNo' required='Y'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='3' name='Reject'>
|
||||
<field name='RefSeqNum' required='Y'/>
|
||||
<field name='RefTagID' required='N'/>
|
||||
<field name='RefMsgType' required='N'/>
|
||||
<field name='SessionRejectReason' required='N'/>
|
||||
<field name='Text' required='N'/>
|
||||
<field name='EncodedTextLen' required='N'/>
|
||||
<field name='EncodedText' required='N'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='4' name='SequenceReset'>
|
||||
<field name='GapFillFlag' required='N'/>
|
||||
<field name='NewSeqNo' required='Y'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='5' name='Logout'>
|
||||
<field name='Text' required='N'/>
|
||||
<field name='EncodedTextLen' required='N'/>
|
||||
<field name='EncodedText' required='N'/>
|
||||
</message>
|
||||
<message msgcat='admin' msgtype='A' name='Logon'>
|
||||
<field name='EncryptMethod' required='Y'/>
|
||||
<field name='HeartBtInt' required='Y'/>
|
||||
<field name='RawDataLength' required='N'/>
|
||||
<field name='RawData' required='N'/>
|
||||
<field name='ResetSeqNumFlag' required='N'/>
|
||||
<field name='NextExpectedMsgSeqNum' required='N'/>
|
||||
<field name='MaxMessageSize' required='N'/>
|
||||
<field name='TestMessageIndicator' required='N'/>
|
||||
<field name='Username' required='N'/>
|
||||
<field name='Password' required='N'/>
|
||||
<field name='DefaultApplVerID' required='Y'/>
|
||||
<component name='MsgTypeGrp' required='N'/>
|
||||
</message>
|
||||
</messages>
|
||||
<components>
|
||||
<component name='HopGrp'>
|
||||
<group name='NoHops' required='N'>
|
||||
<field name='HopCompID' required='N'/>
|
||||
<field name='HopSendingTime' required='N'/>
|
||||
<field name='HopRefID' required='N'/>
|
||||
</group>
|
||||
</component>
|
||||
<component name='MsgTypeGrp'>
|
||||
<group name='NoMsgTypes' required='N'>
|
||||
<field name='RefMsgType' required='N'/>
|
||||
<field name='MsgDirection' required='N'/>
|
||||
<field name='RefApplVerID' required='N'/>
|
||||
<field name='RefCstmApplVerID' required='N'/>
|
||||
</group>
|
||||
</component>
|
||||
</components>
|
||||
<fields>
|
||||
<field name='BeginSeqNo' number='7' type='SEQNUM'/>
|
||||
<field name='BeginString' number='8' type='STRING'/>
|
||||
<field name='BodyLength' number='9' type='LENGTH'/>
|
||||
<field name='CheckSum' number='10' type='STRING'/>
|
||||
<field name='EndSeqNo' number='16' type='SEQNUM'/>
|
||||
<field name='MsgSeqNum' number='34' type='SEQNUM'/>
|
||||
<field number='35' name='MsgType' type='STRING'>
|
||||
<value enum='0' description='HEARTBEAT'/>
|
||||
<value enum='1' description='TEST_REQUEST'/>
|
||||
<value enum='2' description='RESEND_REQUEST'/>
|
||||
<value enum='3' description='REJECT'/>
|
||||
<value enum='4' description='SEQUENCE_RESET'/>
|
||||
<value enum='5' description='LOGOUT'/>
|
||||
<value enum='6' description='INDICATION_OF_INTEREST'/>
|
||||
<value enum='7' description='ADVERTISEMENT'/>
|
||||
<value enum='8' description='EXECUTION_REPORT'/>
|
||||
<value enum='9' description='ORDER_CANCEL_REJECT'/>
|
||||
<value enum='A' description='LOGON'/>
|
||||
<value enum='B' description='NEWS'/>
|
||||
<value enum='C' description='EMAIL'/>
|
||||
<value enum='D' description='ORDER_SINGLE'/>
|
||||
<value enum='E' description='ORDER_LIST'/>
|
||||
<value enum='F' description='ORDER_CANCEL_REQUEST'/>
|
||||
<value enum='G' description='ORDER_CANCEL_REPLACE_REQUEST'/>
|
||||
<value enum='H' description='ORDER_STATUS_REQUEST'/>
|
||||
<value enum='J' description='ALLOCATION_INSTRUCTION'/>
|
||||
<value enum='K' description='LIST_CANCEL_REQUEST'/>
|
||||
<value enum='L' description='LIST_EXECUTE'/>
|
||||
<value enum='M' description='LIST_STATUS_REQUEST'/>
|
||||
<value enum='N' description='LIST_STATUS'/>
|
||||
<value enum='P' description='ALLOCATION_INSTRUCTION_ACK'/>
|
||||
<value enum='Q' description='DONT_KNOW_TRADE'/>
|
||||
<value enum='R' description='QUOTE_REQUEST'/>
|
||||
<value enum='S' description='QUOTE'/>
|
||||
<value enum='T' description='SETTLEMENT_INSTRUCTIONS'/>
|
||||
<value enum='V' description='MARKET_DATA_REQUEST'/>
|
||||
<value enum='W' description='MARKET_DATA_SNAPSHOT_FULL_REFRESH'/>
|
||||
<value enum='X' description='MARKET_DATA_INCREMENTAL_REFRESH'/>
|
||||
<value enum='Y' description='MARKET_DATA_REQUEST_REJECT'/>
|
||||
<value enum='Z' description='QUOTE_CANCEL'/>
|
||||
<value enum='a' description='QUOTE_STATUS_REQUEST'/>
|
||||
<value enum='b' description='MASS_QUOTE_ACKNOWLEDGEMENT'/>
|
||||
<value enum='c' description='SECURITY_DEFINITION_REQUEST'/>
|
||||
<value enum='d' description='SECURITY_DEFINITION'/>
|
||||
<value enum='e' description='SECURITY_STATUS_REQUEST'/>
|
||||
<value enum='f' description='SECURITY_STATUS'/>
|
||||
<value enum='g' description='TRADING_SESSION_STATUS_REQUEST'/>
|
||||
<value enum='h' description='TRADING_SESSION_STATUS'/>
|
||||
<value enum='i' description='MASS_QUOTE'/>
|
||||
<value enum='j' description='BUSINESS_MESSAGE_REJECT'/>
|
||||
<value enum='k' description='BID_REQUEST'/>
|
||||
<value enum='l' description='BID_RESPONSE'/>
|
||||
<value enum='m' description='LIST_STRIKE_PRICE'/>
|
||||
<value enum='n' description='XML_MESSAGE'/>
|
||||
<value enum='o' description='REGISTRATION_INSTRUCTIONS'/>
|
||||
<value enum='p' description='REGISTRATION_INSTRUCTIONS_RESPONSE'/>
|
||||
<value enum='q' description='ORDER_MASS_CANCEL_REQUEST'/>
|
||||
<value enum='r' description='ORDER_MASS_CANCEL_REPORT'/>
|
||||
<value enum='s' description='NEW_ORDER_CROSS'/>
|
||||
<value enum='t' description='CROSS_ORDER_CANCEL_REPLACE_REQUEST'/>
|
||||
<value enum='u' description='CROSS_ORDER_CANCEL_REQUEST'/>
|
||||
<value enum='v' description='SECURITY_TYPE_REQUEST'/>
|
||||
<value enum='w' description='SECURITY_TYPES'/>
|
||||
<value enum='x' description='SECURITY_LIST_REQUEST'/>
|
||||
<value enum='y' description='SECURITY_LIST'/>
|
||||
<value enum='z' description='DERIVATIVE_SECURITY_LIST_REQUEST'/>
|
||||
<value enum='AA' description='DERIVATIVE_SECURITY_LIST'/>
|
||||
<value enum='AB' description='NEW_ORDER_MULTILEG'/>
|
||||
<value enum='AC' description='MULTILEG_ORDER_CANCEL_REPLACE'/>
|
||||
<value enum='AD' description='TRADE_CAPTURE_REPORT_REQUEST'/>
|
||||
<value enum='AE' description='TRADE_CAPTURE_REPORT'/>
|
||||
<value enum='AF' description='ORDER_MASS_STATUS_REQUEST'/>
|
||||
<value enum='AG' description='QUOTE_REQUEST_REJECT'/>
|
||||
<value enum='AH' description='RFQ_REQUEST'/>
|
||||
<value enum='AI' description='QUOTE_STATUS_REPORT'/>
|
||||
<value enum='AJ' description='QUOTE_RESPONSE'/>
|
||||
<value enum='AK' description='CONFIRMATION'/>
|
||||
<value enum='AL' description='POSITION_MAINTENANCE_REQUEST'/>
|
||||
<value enum='AM' description='POSITION_MAINTENANCE_REPORT'/>
|
||||
<value enum='AN' description='REQUEST_FOR_POSITIONS'/>
|
||||
<value enum='AO' description='REQUEST_FOR_POSITIONS_ACK'/>
|
||||
<value enum='AP' description='POSITION_REPORT'/>
|
||||
<value enum='AQ' description='TRADE_CAPTURE_REPORT_REQUEST_ACK'/>
|
||||
<value enum='AR' description='TRADE_CAPTURE_REPORT_ACK'/>
|
||||
<value enum='AS' description='ALLOCATION_REPORT'/>
|
||||
<value enum='AT' description='ALLOCATION_REPORT_ACK'/>
|
||||
<value enum='AU' description='CONFIRMATION_ACK'/>
|
||||
<value enum='AV' description='SETTLEMENT_INSTRUCTION_REQUEST'/>
|
||||
<value enum='AW' description='ASSIGNMENT_REPORT'/>
|
||||
<value enum='AX' description='COLLATERAL_REQUEST'/>
|
||||
<value enum='AY' description='COLLATERAL_ASSIGNMENT'/>
|
||||
<value enum='AZ' description='COLLATERAL_RESPONSE'/>
|
||||
<value enum='BA' description='COLLATERAL_REPORT'/>
|
||||
<value enum='BB' description='COLLATERAL_INQUIRY'/>
|
||||
<value enum='BC' description='NETWORK_STATUS_REQUEST'/>
|
||||
<value enum='BD' description='NETWORK_STATUS_RESPONSE'/>
|
||||
<value enum='BE' description='USER_REQUEST'/>
|
||||
<value enum='BF' description='USER_RESPONSE'/>
|
||||
<value enum='BG' description='COLLATERAL_INQUIRY_ACK'/>
|
||||
<value enum='BH' description='CONFIRMATION_REQUEST'/>
|
||||
<value enum='BI' description='TRADING_SESSION_LIST_REQUEST'/>
|
||||
<value enum='BJ' description='TRADING_SESSION_LIST'/>
|
||||
<value enum='BK' description='SECURITY_LIST_UPDATE_REPORT'/>
|
||||
<value enum='BL' description='ADJUSTED_POSITION_REPORT'/>
|
||||
<value enum='BM' description='ALLOCATION_INSTRUCTION_ALERT'/>
|
||||
<value enum='BN' description='EXECUTION_ACKNOWLEDGEMENT'/>
|
||||
<value enum='BO' description='CONTRARY_INTENTION_REPORT'/>
|
||||
<value enum='BP' description='SECURITY_DEFINITION_UPDATE_REPORT'/>
|
||||
</field>
|
||||
<field name='NewSeqNo' number='36' type='SEQNUM'/>
|
||||
<field name='PossDupFlag' number='43' type='BOOLEAN'/>
|
||||
<field name='RefSeqNum' number='45' type='SEQNUM'/>
|
||||
<field name='SenderCompID' number='49' type='STRING'/>
|
||||
<field name='SenderSubID' number='50' type='STRING'/>
|
||||
<field name='SendingTime' number='52' type='UTCTIMESTAMP'/>
|
||||
<field name='TargetCompID' number='56' type='STRING'/>
|
||||
<field name='TargetSubID' number='57' type='STRING'/>
|
||||
<field name='Text' number='58' type='STRING'/>
|
||||
<field name='Signature' number='89' type='DATA'/>
|
||||
<field name='SecureDataLen' number='90' type='LENGTH'/>
|
||||
<field name='SecureData' number='91' type='DATA'/>
|
||||
<field name='SignatureLength' number='93' type='LENGTH'/>
|
||||
<field name='RawDataLength' number='95' type='LENGTH'/>
|
||||
<field name='RawData' number='96' type='DATA'/>
|
||||
<field name='PossResend' number='97' type='BOOLEAN'/>
|
||||
<field name='EncryptMethod' number='98' type='INT'>
|
||||
<value description='NONE_OTHER' enum='0'/>
|
||||
<value description='PKCS' enum='1'/>
|
||||
<value description='DES' enum='2'/>
|
||||
<value description='PKCS_DES' enum='3'/>
|
||||
<value description='PGP_DES' enum='4'/>
|
||||
<value description='PGP_DES_MD5' enum='5'/>
|
||||
<value description='PEM_DES_MD5' enum='6'/>
|
||||
</field>
|
||||
<field name='HeartBtInt' number='108' type='INT'/>
|
||||
<field name='TestReqID' number='112' type='STRING'/>
|
||||
<field name='OnBehalfOfCompID' number='115' type='STRING'/>
|
||||
<field name='OnBehalfOfSubID' number='116' type='STRING'/>
|
||||
<field name='OrigSendingTime' number='122' type='UTCTIMESTAMP'/>
|
||||
<field name='GapFillFlag' number='123' type='BOOLEAN'/>
|
||||
<field name='DeliverToCompID' number='128' type='STRING'/>
|
||||
<field name='DeliverToSubID' number='129' type='STRING'/>
|
||||
<field name='ResetSeqNumFlag' number='141' type='BOOLEAN'/>
|
||||
<field name='SenderLocationID' number='142' type='STRING'/>
|
||||
<field name='TargetLocationID' number='143' type='STRING'/>
|
||||
<field name='OnBehalfOfLocationID' number='144' type='STRING'/>
|
||||
<field name='DeliverToLocationID' number='145' type='STRING'/>
|
||||
<field name='XmlDataLen' number='212' type='LENGTH'/>
|
||||
<field name='XmlData' number='213' type='DATA'/>
|
||||
<field number='347' name='MessageEncoding' type='STRING'>
|
||||
<value enum='ISO-2022-JP' description='ISO_2022_JP'/>
|
||||
<value enum='EUC-JP' description='EUC_JP'/>
|
||||
<value enum='SHIFT_JIS' description='SHIFT_JIS'/>
|
||||
<value enum='UTF-8' description='UTF_8'/>
|
||||
</field>
|
||||
<field name='EncodedTextLen' number='354' type='LENGTH'/>
|
||||
<field name='EncodedText' number='355' type='DATA'/>
|
||||
<field name='LastMsgSeqNumProcessed' number='369' type='SEQNUM'/>
|
||||
<field name='RefTagID' number='371' type='INT'/>
|
||||
<field name='RefMsgType' number='372' type='STRING'/>
|
||||
<field name='SessionRejectReason' number='373' type='INT'>
|
||||
<value description='INVALID_TAG_NUMBER' enum='0'/>
|
||||
<value description='REQUIRED_TAG_MISSING' enum='1'/>
|
||||
<value description='SENDINGTIME_ACCURACY_PROBLEM' enum='10'/>
|
||||
<value description='INVALID_MSGTYPE' enum='11'/>
|
||||
<value description='XML_VALIDATION_ERROR' enum='12'/>
|
||||
<value description='TAG_APPEARS_MORE_THAN_ONCE' enum='13'/>
|
||||
<value description='TAG_SPECIFIED_OUT_OF_REQUIRED_ORDER' enum='14'/>
|
||||
<value description='REPEATING_GROUP_FIELDS_OUT_OF_ORDER' enum='15'/>
|
||||
<value description='INCORRECT_NUMINGROUP_COUNT_FOR_REPEATING_GROUP' enum='16'/>
|
||||
<value description='NON_DATA_VALUE_INCLUDES_FIELD_DELIMITER' enum='17'/>
|
||||
<value description='TAG_NOT_DEFINED_FOR_THIS_MESSAGE_TYPE' enum='2'/>
|
||||
<value description='UNDEFINED_TAG' enum='3'/>
|
||||
<value description='TAG_SPECIFIED_WITHOUT_A_VALUE' enum='4'/>
|
||||
<value description='VALUE_IS_INCORRECT' enum='5'/>
|
||||
<value description='INCORRECT_DATA_FORMAT_FOR_VALUE' enum='6'/>
|
||||
<value description='DECRYPTION_PROBLEM' enum='7'/>
|
||||
<value description='SIGNATURE_PROBLEM' enum='8'/>
|
||||
<value description='COMPID_PROBLEM' enum='9'/>
|
||||
<value description='OTHER' enum='99'/>
|
||||
</field>
|
||||
<field name='MaxMessageSize' number='383' type='LENGTH'/>
|
||||
<field name='NoMsgTypes' number='384' type='NUMINGROUP'/>
|
||||
<field name='MsgDirection' number='385' type='CHAR'>
|
||||
<value description='RECEIVE' enum='R'/>
|
||||
<value description='SEND' enum='S'/>
|
||||
</field>
|
||||
<field name='TestMessageIndicator' number='464' type='BOOLEAN'/>
|
||||
<field name='Username' number='553' type='STRING'/>
|
||||
<field name='Password' number='554' type='STRING'/>
|
||||
<field name='NoHops' number='627' type='NUMINGROUP'/>
|
||||
<field name='HopCompID' number='628' type='STRING'/>
|
||||
<field name='HopSendingTime' number='629' type='UTCTIMESTAMP'/>
|
||||
<field name='HopRefID' number='630' type='SEQNUM'/>
|
||||
<field name='NextExpectedMsgSeqNum' number='789' type='SEQNUM'/>
|
||||
<field name='ApplVerID' number='1128' type='STRING'>
|
||||
<value description='FIX27' enum='0'/>
|
||||
<value description='FIX30' enum='1'/>
|
||||
<value description='FIX40' enum='2'/>
|
||||
<value description='FIX41' enum='3'/>
|
||||
<value description='FIX42' enum='4'/>
|
||||
<value description='FIX43' enum='5'/>
|
||||
<value description='FIX44' enum='6'/>
|
||||
<value description='FIX50' enum='7'/>
|
||||
<value description='FIX50SP1' enum='8'/>
|
||||
<value description='FIX50SP2' enum='9'/>
|
||||
</field>
|
||||
<field name='CstmApplVerID' number='1129' type='STRING'/>
|
||||
<field name='RefApplVerID' number='1130' type='STRING'/>
|
||||
<field name='RefCstmApplVerID' number='1131' type='STRING'/>
|
||||
<field name='DefaultApplVerID' number='1137' type='STRING'/>
|
||||
</fields>
|
||||
</fix>
|
||||
@@ -142,4 +142,4 @@ if (WIN32)
|
||||
include(${CMAKE_SOURCE_DIR}/tools/vs_filters.cmake)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
Fix {
|
||||
value {
|
||||
enable_acceptor: true
|
||||
allow_orders: true
|
||||
fix_config_file: 'spark_fix.cfg'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
syntax='proto2';
|
||||
|
||||
package bts.config;
|
||||
|
||||
message Fix {
|
||||
optional bool enable_acceptor = 5 [default = false];
|
||||
optional bool allow_orders = 10 [default = false];
|
||||
optional string fix_config_file = 20;
|
||||
}
|
||||
@@ -46,6 +46,7 @@ constexpr log::Subsystem DataFlow {Latency.ordinal+1 };
|
||||
constexpr log::Subsystem Plugin {DataFlow.ordinal+1 };
|
||||
constexpr log::Subsystem Sdk {Plugin.ordinal+1 };
|
||||
constexpr log::Subsystem Http {Sdk.ordinal+1 };
|
||||
constexpr log::Subsystem Fix {Http.ordinal+1 };
|
||||
|
||||
struct LoggerHandle {
|
||||
LoggerHandle(LoggerHandle const&) = delete;
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/md/MDConsumerHandle.h"
|
||||
#include "lib/md/MDClient.h"
|
||||
#include "lib/md/ExchangeOrderKey.h"
|
||||
|
||||
#include "lib/utils/LockBox.h"
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace bts {
|
||||
namespace md {
|
||||
|
||||
security_key default_key(const MDConsumerHandle& h)
|
||||
{
|
||||
auto&& key = h.key();
|
||||
return key;
|
||||
}
|
||||
|
||||
std::string desc_key(const MDConsumerHandle& h)
|
||||
{
|
||||
auto&& sec = h.get_normalized_security();
|
||||
auto&& desc = securities::descriptor(sec);
|
||||
return desc;
|
||||
}
|
||||
|
||||
template<auto Key_Func>
|
||||
class HandleIndex
|
||||
{
|
||||
MDClient& _client;
|
||||
int _sub;
|
||||
std::optional<MDConsumer> _consumer;
|
||||
|
||||
public:
|
||||
typedef decltype( Key_Func(std::declval<MDConsumerHandle>()) ) KeyType;
|
||||
utils::LockBox<std::unordered_map<KeyType,MDConsumerHandle>> handle_index;
|
||||
utils::LockBox<std::unordered_map<KeyType,SecurityPtr>> security_index;
|
||||
|
||||
|
||||
HandleIndex(md::MDClient& client) :
|
||||
_client(client),
|
||||
_sub(0),
|
||||
_consumer(std::nullopt),
|
||||
handle_index(),
|
||||
security_index()
|
||||
{
|
||||
_sub = _client.on_consumer().subscribe([=](const MDConsumer& c) { on_consumer(c); });
|
||||
on_consumer( _client.current_consumer() );
|
||||
}
|
||||
virtual ~HandleIndex()
|
||||
{
|
||||
_client.on_consumer().unsubscribe(_sub);
|
||||
}
|
||||
|
||||
private:
|
||||
void on_consumer(const MDConsumer& c)
|
||||
{
|
||||
MDConsumer temp(c);
|
||||
if (temp.all_heartbeats_ok())
|
||||
{
|
||||
auto existing = _consumer ? _consumer->id() : 0;
|
||||
LOG(md,Info, "Updating HandleIndex MDConsumer. Old {} new {}", existing, temp.id());
|
||||
// clear the index
|
||||
handle_index.lock([](auto& index) { index.clear(); });
|
||||
security_index.lock([](auto& index) { index.clear(); });
|
||||
// Swap to new consumer
|
||||
if (_consumer)
|
||||
{
|
||||
_consumer->swap(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
_consumer = temp;
|
||||
}
|
||||
// Subscribe to on_security
|
||||
auto id = _consumer->id();
|
||||
_consumer->on_security([this,id](const SecurityPtr& sec, MDConsumerHandle handle)
|
||||
{
|
||||
if (_consumer->id() == id)
|
||||
{
|
||||
handle_index.lock([this, handle](auto& index)
|
||||
{
|
||||
auto key = Key_Func(handle);
|
||||
index[ key ] = handle;
|
||||
});
|
||||
security_index.lock([this, handle, &sec](auto& index)
|
||||
{
|
||||
auto key = Key_Func(handle);
|
||||
index[ key ] = sec;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef HandleIndex<default_key> KeyHandleIndex;
|
||||
typedef HandleIndex<desc_key> DescHandleIndex;
|
||||
|
||||
}}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "MDClient.h"
|
||||
#include "ExchangeOrderKey.h"
|
||||
#include "MBOProcessor.h"
|
||||
#include "MDClient.h"
|
||||
#include "HandleIndex.h"
|
||||
|
||||
#include "lib/utils/LockBox.h"
|
||||
|
||||
@@ -21,55 +23,6 @@ using namespace bts::api::orderman;
|
||||
namespace bts {
|
||||
namespace md {
|
||||
|
||||
//
|
||||
// Simple class to handle MD recovery and maintain an index of SecurityKey=>MDConsumerHandle
|
||||
|
||||
class HandleIndex {
|
||||
MDClient& _client;
|
||||
int _sub;
|
||||
std::optional<MDConsumer> _consumer;
|
||||
|
||||
public:
|
||||
utils::LockBox<std::unordered_map<SecurityKey,MDConsumerHandle>> index;
|
||||
|
||||
HandleIndex(md::MDClient& client):
|
||||
_client(client),
|
||||
_sub(0),
|
||||
_consumer(std::nullopt),
|
||||
index() {
|
||||
_sub = _client.on_consumer().subscribe([=](const MDConsumer& c) { on_consumer(c); });
|
||||
on_consumer(_client.current_consumer());
|
||||
}
|
||||
|
||||
~HandleIndex() { _client.on_consumer().unsubscribe(_sub); }
|
||||
|
||||
|
||||
private:
|
||||
void on_consumer(const MDConsumer& c) {
|
||||
MDConsumer temp(c);
|
||||
if (temp.all_heartbeats_ok()) {
|
||||
auto existing = _consumer ? _consumer->id() : 0;
|
||||
LOG(md,Info, "Updateing MBOService MDConsumer. Old {} new {}", existing, temp.id());
|
||||
// clear the index
|
||||
index.lock([](auto& index) { index.clear(); });
|
||||
// Swap to new consumer
|
||||
if (_consumer) {
|
||||
_consumer->swap(temp);
|
||||
}
|
||||
else { _consumer = temp; }
|
||||
// Subscribe to on_security
|
||||
auto id = _consumer->id();
|
||||
_consumer->on_security([this,id](const SecurityPtr&, MDConsumerHandle handle) {
|
||||
if (_consumer->id() == id) {
|
||||
index.lock([handle](auto& index) {
|
||||
index[SecurityKey(handle.key())] = handle;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ActiveOrder {
|
||||
SecurityKey _security;
|
||||
SparkOrderKey _id;
|
||||
@@ -111,7 +64,7 @@ struct MBOServiceImpl {
|
||||
MBOServiceImpl(
|
||||
api::ServiceContext& ctx,
|
||||
utils::Dispatcher& d,
|
||||
md::MDClient& c,
|
||||
MDClient& c,
|
||||
MBOServiceBase::PiqPublisher&& pub
|
||||
):
|
||||
dispatcher(d),
|
||||
@@ -124,10 +77,10 @@ struct MBOServiceImpl {
|
||||
{}
|
||||
|
||||
utils::Dispatcher& dispatcher;
|
||||
md::MDClient& client;
|
||||
MDClient& client;
|
||||
OrderService orders;
|
||||
|
||||
HandleIndex handle_index;
|
||||
KeyHandleIndex handle_index;
|
||||
MBOServiceBase::PiqPublisher publish;
|
||||
utils::DispatcherTimer timer;
|
||||
|
||||
@@ -162,10 +115,10 @@ std::optional<uint32_t> search_orders(const ExchangeOrderKey oid, const std::vec
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool update_position(const HandleIndex& handles, ActiveOrder& order) {
|
||||
const auto& index = handles.index;
|
||||
bool update_position(const KeyHandleIndex& handles, ActiveOrder& order) {
|
||||
const auto& index = handles.handle_index;
|
||||
auto handle = index.lock([id=order.security_id()](const auto& index) {
|
||||
auto iter = index.find(id);
|
||||
auto iter = index.find(id.value());
|
||||
return iter != index.end() ? iter->second : MDConsumerHandle {};
|
||||
});
|
||||
|
||||
@@ -300,8 +253,8 @@ MBOServiceServer::~MBOServiceServer() { }
|
||||
void MBOServiceServer::get_orders_for_security(const MBOSecurityQuery& request, const api::Response<PositionData>& reply) {
|
||||
PositionData response;
|
||||
response.set_security_id(request.security_id());
|
||||
auto handle = _me->handle_index.index.lock([id=request.security_id()](const auto& index) {
|
||||
auto iter = index.find(SecurityKey(id));
|
||||
auto handle = _me->handle_index.handle_index.lock([id=request.security_id()](const auto& index) {
|
||||
auto iter = index.find(id);
|
||||
return iter != index.end() ? iter->second : MDConsumerHandle {};
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,11 @@ target_include_directories( sparkd PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY
|
||||
|
||||
set_source_files_properties(exchange_sim/ExchangeSim.cpp PROPERTIES COMPILE_FLAGS --std=c++14)
|
||||
set_source_files_properties(exchange_sim/ExchangeSimApplication.cpp PROPERTIES COMPILE_FLAGS --std=c++14)
|
||||
set_source_files_properties(fix/FixInit.cpp PROPERTIES COMPILE_FLAGS --std=c++14)
|
||||
set_source_files_properties(fix/FixRouter.cpp PROPERTIES COMPILE_FLAGS --std=c++14)
|
||||
set_source_files_properties(fix/SessionBase.cpp PROPERTIES COMPILE_FLAGS --std=c++14)
|
||||
set_source_files_properties(fix/OrderSession.cpp PROPERTIES COMPILE_FLAGS --std=c++14)
|
||||
set_source_files_properties(fix/DropCopySession.cpp PROPERTIES COMPILE_FLAGS --std=c++14)
|
||||
|
||||
# linking
|
||||
target_link_libraries( sparkd
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/Ports.pb.h"
|
||||
#include "lib/config/protos/fix.pb.h"
|
||||
#include "lib/config/protos/quickbus_cfg.pb.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/os/os.h"
|
||||
@@ -37,12 +38,14 @@
|
||||
#include "server/sparkd/support/ShellLatencyServer.h"
|
||||
#include "server/sparkd/user/AdminServiceServer.h"
|
||||
#include "server/sparkd/user/AuthServiceServer.h"
|
||||
#include "server/sparkd/fix/FixInit.h"
|
||||
#include "server/sparkd/subprocess/SubProcessRunner.h"
|
||||
|
||||
#include "lib/orderman/orders/algo/algo_orders/ProRataAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/ScratchAlgo.h"
|
||||
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
|
||||
namespace bts {
|
||||
namespace cmd {
|
||||
@@ -103,6 +106,12 @@ int Run::execute(std::vector<std::string> const &) {
|
||||
position::MarginServiceServer margin_service(sc, mdclient);
|
||||
metrics::ShellLatencyServer shell_latency_service(
|
||||
sc, stats, utils::Dispatcher::background());
|
||||
|
||||
if(config::get<config::Fix>().enable_acceptor())
|
||||
{
|
||||
threading::make_thread("SparkFix", [&sc, &mdclient]() { fix::FixInit::start_acceptor(sc, mdclient); })
|
||||
.detach();
|
||||
}
|
||||
|
||||
auto bg_health =
|
||||
std::make_shared<metrics::IntCounterMetric>("spark_background_thread");
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "RunFix.h"
|
||||
#include "server/sparkd/fix/FixInit.h"
|
||||
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/Ports.pb.h"
|
||||
#include "lib/config/protos/quickbus_cfg.pb.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/quickbus/quickbus.h"
|
||||
#include "lib/utils/Dispatcher.h"
|
||||
#include "lib/utils/thread_manager.h"
|
||||
|
||||
#include "lib/config/protos/toggles.pb.h"
|
||||
#include "lib/md/SyntheticSecurityService.h"
|
||||
#include "lib/md/MDClient.h"
|
||||
|
||||
|
||||
namespace bts {
|
||||
namespace cmd {
|
||||
|
||||
int RunFix::execute(std::vector<std::string> const &)
|
||||
{
|
||||
config::QuickbusNodeCfg qb_cfg;
|
||||
auto &ports = config::get<config::Ports>();
|
||||
qb_cfg.set_my_port(ports.qbus_port());
|
||||
qbus::Quickbus quickbus(qb_cfg,
|
||||
qbus::Quickbus::ThreadOptions::deferred_start);
|
||||
api::ServiceContext sc(&quickbus);
|
||||
|
||||
api::synthetic::SyntheticSecurityService synthetic_service(sc);
|
||||
md::MDClient mdclient(&utils::Dispatcher::background(), &synthetic_service);
|
||||
|
||||
threading::make_thread("SparkFix", [&sc, &mdclient]() { fix::FixInit::start_acceptor(sc, mdclient); })
|
||||
.detach();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string RunFix::help () {return "\t run fix acceptor";}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "ICmd.h"
|
||||
|
||||
namespace bts {
|
||||
namespace cmd {
|
||||
|
||||
class RunFix : public ICmd
|
||||
{
|
||||
public:
|
||||
int execute(std::vector<std::string> const &args);
|
||||
std::string help ();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "AdminModelWrapper.h"
|
||||
|
||||
#include "lib/api/service_helpers/AdminServiceUtils.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace bts
|
||||
{
|
||||
|
||||
AdminModelWrapper::AdminModelWrapper(const bts::api::admin::AdminData& data, const std::string& user_name) :
|
||||
data(data),
|
||||
_user_name(user_name),
|
||||
_allowed_accounts()
|
||||
{
|
||||
auto user = bts::admin::AdminServiceUtils::find_user(data, _user_name);
|
||||
if(user && user->has_user_data() && user->user_data().has_account_permissions())
|
||||
{
|
||||
for(const auto& a : user->user_data().account_permissions().account_permissions())
|
||||
{
|
||||
auto account = bts::admin::AdminServiceUtils::find_account(data, a.first);
|
||||
if(account)
|
||||
{
|
||||
if(a.second.has_trading_permission() && a.second.trading_permission().has_permissions())
|
||||
_allowed_accounts[account->name()] = 0;
|
||||
if(a.second.has_admin_permission() && a.second.admin_permission().has_permissions())
|
||||
_allowed_accounts[account->name()] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AdminModelWrapper::can_view_account(const std::string& account)
|
||||
{
|
||||
auto it = _allowed_accounts.find(account);
|
||||
return it != _allowed_accounts.end() || _user_name == "bts";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/services/AdminService.pb.h"
|
||||
#include "lib/api/services/AdminService.client.pb.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace bts
|
||||
{
|
||||
|
||||
//see AdminModelWrapper.cs
|
||||
|
||||
namespace api { namespace admin { class AdminData; } }
|
||||
class AdminModelWrapper
|
||||
{
|
||||
public:
|
||||
AdminModelWrapper(const bts::api::admin::AdminData& data, const std::string& user_name);
|
||||
|
||||
bool can_view_account(const std::string& account);
|
||||
const bts::api::admin::AdminData data;
|
||||
|
||||
private:
|
||||
std::string _user_name;
|
||||
std::unordered_map<std::string, int> _allowed_accounts;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
#include "DropCopySession.h"
|
||||
#include "FixRouter.h"
|
||||
|
||||
#include "lib/api/service_helpers/AdminServiceUtils.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
|
||||
#include <quickfix/fix44/OrderStatusRequest.h>
|
||||
#include <quickfix/fix44/SecurityDefinitionRequest.h>
|
||||
#include <quickfix/fix44/SecurityDefinition.h>
|
||||
#include <quickfix/fix44/ExecutionReport.h>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
#include <google/protobuf/util/time_util.h>
|
||||
|
||||
namespace bts {
|
||||
namespace fix {
|
||||
|
||||
struct DropCopySession::PImpl
|
||||
{
|
||||
rxcpp::observable<bts::api::orderman::OrdersUpdate> filtered_orders;
|
||||
rxcpp::observable<bts::api::orderman::FillsUpdate> filtered_fills;
|
||||
PImpl()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
DropCopySession::DropCopySession(bts::api::ServiceContext& sc, IFixRouter& rt, const FIX::SessionID& id) :
|
||||
SessionBase(sc, rt, id),
|
||||
_orderservice( sc.locate<api::orderman::OrderService>() ),
|
||||
_order_cache(),
|
||||
me(new PImpl())
|
||||
{
|
||||
}
|
||||
|
||||
DropCopySession::~DropCopySession()
|
||||
{
|
||||
}
|
||||
|
||||
void DropCopySession::onLogon(const FIX::SessionID& sessionID)
|
||||
{
|
||||
bts_assert(sessionID == _sess);
|
||||
LOG(Fix, Info, "Logged on {}", sessionID.toString());
|
||||
subscribe_fills_and_orders(sessionID);
|
||||
}
|
||||
|
||||
void DropCopySession::onLogout(const FIX::SessionID& sessionID)
|
||||
{
|
||||
me->filtered_orders.finally([] () {} );
|
||||
me->filtered_fills.finally([] () {} );
|
||||
|
||||
bts_assert(sessionID == _sess);
|
||||
LOG(Fix, Info, "Logged out {}", sessionID.toString());
|
||||
}
|
||||
|
||||
void DropCopySession::fromApp(const FIX::Message &message,const FIX::SessionID &sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType)
|
||||
{
|
||||
bts_assert(sessionID == _sess);
|
||||
auto mt = message.getHeader().getField(FIX::FIELD::MsgType);
|
||||
if(mt == FIX44::OrderStatusRequest::MsgType() ||
|
||||
mt == FIX44::SecurityDefinitionRequest::MsgType()
|
||||
)
|
||||
{
|
||||
LOG(Fix, Info, "DC IN: {}", message.toString() );
|
||||
crack(message, sessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Fix, Info, "DC IN: Unsupported message in {}", mt );
|
||||
throw FIX::UnsupportedMessageType(mt);
|
||||
}
|
||||
}
|
||||
|
||||
void DropCopySession::onMessage(const FIX44::OrderStatusRequest& req, const FIX::SessionID& s)
|
||||
{
|
||||
uint oid = atoi(req.getField(FIX::FIELD::OrderID).c_str());
|
||||
_order_cache.lock([&] (auto& order_cache)
|
||||
{
|
||||
auto it = order_cache.find(oid);
|
||||
if(it != order_cache.end())
|
||||
{
|
||||
auto&& ostat = build_order_status(s, *it->second);
|
||||
ostat.setField(FIX::FIELD::OrdStatusReqID, req.getField(FIX::FIELD::OrdStatusReqID));
|
||||
_router.send(ostat, s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DropCopySession::onMessage(const FIX44::SecurityDefinitionRequest& req, const FIX::SessionID& s)
|
||||
{
|
||||
if (req.getField(FIX::FIELD::SecurityRequestType) == std::to_string(FIX::SecurityRequestType_ALL_SECURITIES))
|
||||
{
|
||||
auto&& secs = _router.get_securities();
|
||||
// if (req.isSetField(FIX::FIELD::Symbol))
|
||||
// secs = secs.Where(x => x.Symbol.ToLower() == req.Symbol.getValue().ToLower());
|
||||
// if (req.IsSetSecurityType())
|
||||
// secs = secs.Where(x => security_types_equal(x.security_type(), req.SecurityType));
|
||||
// if (req.IsSetSecurityExchange())
|
||||
// secs = secs.Where(x => exchanges_equal(x.Exchange, req.SecurityExchange));
|
||||
|
||||
auto def = FIX44::SecurityDefinition(FIX::SecurityReqID(req.getField(FIX::FIELD::SecurityReqID)),
|
||||
FIX::SecurityResponseID(""),
|
||||
FIX::SecurityResponseType(FIX::SecurityResponseType_LIST_OF_SECURITIES_RETURNED_PER_REQUEST));
|
||||
_router.send(def, s);
|
||||
}
|
||||
else
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::SecurityRequestType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DropCopySession::subscribe_fills_and_orders(const FIX::SessionID sessionID)
|
||||
{
|
||||
//TODO put this in our C++ pipe and smoke it
|
||||
//var on_orders = _orderservice.Value.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(_adminobservable).Table().Cache();
|
||||
//var on_fills = _orderservice.Value.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(_adminobservable).Table().Cache();
|
||||
|
||||
me->filtered_orders = _adminobservable.combine_latest(_orderservice.subscribe_orders())
|
||||
.map( [] (auto& tuple) -> auto
|
||||
{
|
||||
std::shared_ptr<AdminModelWrapper> admin = std::get<0>(tuple);
|
||||
auto&& update = std::get<1>(tuple);
|
||||
|
||||
auto all = update.all();
|
||||
all.erase(std::remove_if(all.begin(), all.end(),
|
||||
[admin](auto& i) -> bool { return !admin->can_view_account(i->def().account()); }), all.end());
|
||||
|
||||
auto added = update.added();
|
||||
added.erase(std::remove_if(added.begin(), added.end(),
|
||||
[admin](auto& i) -> bool { return !admin->can_view_account(i->def().account()); }), added.end());
|
||||
|
||||
auto changed = update.changed();
|
||||
changed.erase(std::remove_if(changed.begin(), changed.end(),
|
||||
[admin](auto& i) -> bool { return !admin->can_view_account(i.current()->def().account()); }), changed.end());
|
||||
|
||||
auto removed = update.removed();
|
||||
removed.erase(std::remove_if(removed.begin(), removed.end(),
|
||||
[admin](auto& i) -> bool { return !admin->can_view_account(i->def().account()); }), removed.end());
|
||||
|
||||
bts::api::orderman::OrdersUpdate newupdate(all, added, changed, removed);
|
||||
return newupdate;
|
||||
});
|
||||
//.buffer_with_time( std::chrono::milliseconds(500) );
|
||||
|
||||
me->filtered_orders.subscribe([this, sessionID](const auto& update)
|
||||
{
|
||||
// for(auto& update : updatevec)
|
||||
// {
|
||||
for(auto& order : update.added())
|
||||
{
|
||||
auto&& ostat = build_order_status(sessionID, *order);
|
||||
_order_cache.lock( [&order] (auto& order_cache)
|
||||
{
|
||||
order_cache[order->key()] = order;
|
||||
});
|
||||
_router.send(ostat, sessionID);
|
||||
}
|
||||
for(auto& order : update.changed())
|
||||
{
|
||||
auto&& ostat = build_order_status(sessionID, *order.current(), true);
|
||||
_router.send(ostat, sessionID);
|
||||
}
|
||||
// }
|
||||
});
|
||||
|
||||
me->filtered_fills = _adminobservable.combine_latest(_orderservice.subscribe_fills())
|
||||
.map( [] (auto& tuple) -> auto
|
||||
{
|
||||
std::shared_ptr<AdminModelWrapper> admin = std::get<0>(tuple);
|
||||
auto&& update = std::get<1>(tuple);
|
||||
|
||||
auto all = update.all();
|
||||
all.erase(std::remove_if(all.begin(), all.end(),
|
||||
[admin](auto& i) -> bool { return !admin->can_view_account(i->account()); }), all.end());
|
||||
|
||||
auto added = update.added();
|
||||
added.erase(std::remove_if(added.begin(), added.end(),
|
||||
[admin](auto& i) -> bool { return !admin->can_view_account(i->account()); }), added.end());
|
||||
|
||||
auto changed = update.changed();
|
||||
changed.erase(std::remove_if(changed.begin(), changed.end(),
|
||||
[admin](auto& i) -> bool { return !admin->can_view_account(i.current()->account()); }), changed.end());
|
||||
|
||||
auto removed = update.removed();
|
||||
removed.erase(std::remove_if(removed.begin(), removed.end(),
|
||||
[admin](auto& i) -> bool { return !admin->can_view_account(i->account()); }), removed.end());
|
||||
|
||||
bts::api::orderman::FillsUpdate newupdate(all, added, changed, removed);
|
||||
return newupdate;
|
||||
});
|
||||
//.buffer_with_time( std::chrono::milliseconds(500) );
|
||||
|
||||
me->filtered_fills.subscribe([this, sessionID](const auto& update)
|
||||
{
|
||||
// for(auto& update : updatevec)
|
||||
// {
|
||||
for(auto& fill : update.added())
|
||||
{
|
||||
auto&& fi = build_fill(sessionID, *fill);
|
||||
_router.send(fi, sessionID);
|
||||
}
|
||||
for(auto& fill : update.changed())
|
||||
{
|
||||
auto&& fi = build_fill(sessionID, *fill.current(), true);
|
||||
_router.send(fi, sessionID);
|
||||
}
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
void DropCopySession::process_order_state(const bts::api::orderman::OrderState& state, uint& filled, uint& leaves, double& avgprx,
|
||||
FIX::OrdStatus& fstatus, bts::api::orderman::NativeOrderState& native, const bts::api::orderman::Fill* fill)
|
||||
{
|
||||
UNUSED(fill);
|
||||
filled = state.filled_quantity();
|
||||
leaves = state.quantity() - filled;
|
||||
avgprx = state.avg_price();;
|
||||
fstatus = translate_status(state, &native);
|
||||
}
|
||||
|
||||
md::SecurityPtr DropCopySession::resolve_execution_report_security(md::SecurityPtr sec, FIX44::ExecutionReport& report)
|
||||
{
|
||||
if(!sec)
|
||||
return nullptr;
|
||||
auto sectype = bts::admin::AdminServiceUtils::security_type(*sec);
|
||||
report.setField(FIX::FIELD::SecurityExchange, convert_exchange(sec->exchange()).getString());
|
||||
report.setField(FIX::FIELD::SecurityType, convert_security_type(sectype).getString());
|
||||
if(sec->has_glbx() || sec->has_ice())
|
||||
report.setField(FIX::FIELD::SecurityDesc, securities::descriptor(*sec));
|
||||
|
||||
if(sectype == bts::api::SecurityType::SECURITYTYPE_FUTURE)
|
||||
{
|
||||
auto dt = securities::expiration(*sec);
|
||||
report.setField(FIX::FIELD::MaturityMonthYear, boost::gregorian::to_iso_string(dt));
|
||||
}
|
||||
else if(sectype == bts::api::SecurityType::SECURITYTYPE_SPREAD)
|
||||
{
|
||||
//TODO this is not quite right for spreads of spreads
|
||||
auto dt = securities::expiration(*sec); //smallest
|
||||
report.setField(FIX::FIELD::MaturityMonthYear, boost::gregorian::to_iso_string(dt));
|
||||
}
|
||||
else
|
||||
throw FIX::UnsupportedMessageType();
|
||||
return sec;
|
||||
}
|
||||
|
||||
FIX44::ExecutionReport DropCopySession::build_fill(const FIX::SessionID& sess,
|
||||
const bts::api::orderman::Fill& fill, bool change)
|
||||
{
|
||||
UNUSED(sess);
|
||||
auto exec = FIX::ExecType(FIX::ExecType_PARTIAL_FILL);
|
||||
FIX::OrdStatus status;
|
||||
uint filled = 0; uint leaves = 0; double prx = 0.0;
|
||||
api::orderman::NativeOrderState native;
|
||||
|
||||
_order_cache.lock([&] (auto& order_cache)
|
||||
{
|
||||
auto orderit = order_cache.find(fill.order_key());
|
||||
if(orderit != order_cache.end())
|
||||
{
|
||||
const api::orderman::OrderRecord& order = *orderit->second;
|
||||
process_order_state(order.state(), filled, leaves, prx, status, native);
|
||||
if( ((double)order.state().quantity()) == fill.last_shares())
|
||||
exec = FIX::ExecType(FIX::ExecType_FILL);
|
||||
}
|
||||
});
|
||||
if (change)
|
||||
exec = FIX::ExecType(FIX::ExecType_TRADE_CORRECT);
|
||||
|
||||
auto sec = _router.lookup(fill.security_key());
|
||||
FIX44::ExecutionReport report (
|
||||
FIX::OrderID(std::to_string(fill.order_key())),
|
||||
FIX::ExecID(fill.fill_key()),
|
||||
exec,
|
||||
status,
|
||||
FIX::Side(fill.side() == bts::api::SIDE_BUY ? FIX::Side_BUY : FIX::Side_SELL),
|
||||
FIX::LeavesQty(leaves),
|
||||
FIX::CumQty(filled),
|
||||
FIX::AvgPx(prx)
|
||||
);
|
||||
|
||||
if(sec)
|
||||
report.setField(FIX::FIELD::Symbol, sec->symbol());
|
||||
report.setField(FIX::FIELD::Account, FIX::Account(fill.account()).getString() );
|
||||
report.setField(FIX::FIELD::LastQty, FIX::LastQty(fill.last_shares()).getString() );
|
||||
report.setField(FIX::FIELD::LastPx, FIX::LastPx(fill.last_price()).getString() );
|
||||
|
||||
auto ts = fill.has_timestamp() ? fill.timestamp() : google::protobuf::Timestamp();
|
||||
//report.setField(FIX::FIELD::TransactTime, FIX::TransactTime(ts, true).getString());
|
||||
//report.setField(FIX::FIELD::TradeDate, FIX::TradeDate(ts.ToString("yyyyMMdd"));
|
||||
report.setField(FIX::FIELD::TransactTime, google::protobuf::util::TimeUtil::ToString(ts));
|
||||
report.setField(FIX::FIELD::TradeDate, google::protobuf::util::TimeUtil::ToString(ts));
|
||||
|
||||
sec = resolve_execution_report_security(sec, report);
|
||||
|
||||
if (native.exchange_order_id() != "")
|
||||
report.setField(FIX::FIELD::SecondaryOrderID, FIX::SecondaryOrderID(native.exchange_order_id()).getString());
|
||||
|
||||
if (sec && (bts::admin::AdminServiceUtils::security_type(*sec) == bts::api::SECURITYTYPE_SPREAD ||
|
||||
bts::admin::AdminServiceUtils::security_type(*sec) == bts::api::SECURITYTYPE_SYNTHETIC) )
|
||||
{
|
||||
if (fill.fill_kind() == bts::api::orderman::Fill::FILL_KIND_LEG)
|
||||
report.setField(FIX::FIELD::MultiLegReportingType, FIX::MultiLegReportingType('2').getString());
|
||||
else
|
||||
report.setField(FIX::FIELD::MultiLegReportingType, FIX::MultiLegReportingType('3').getString());
|
||||
}
|
||||
return report;
|
||||
|
||||
}
|
||||
|
||||
FIX44::ExecutionReport DropCopySession::build_order_status(const FIX::SessionID& sess,
|
||||
const bts::api::orderman::OrderRecord& order, bool change )
|
||||
{
|
||||
UNUSED(sess);
|
||||
UNUSED(change);
|
||||
auto exec = FIX::ExecType(FIX::ExecType_NEW);
|
||||
_order_cache.lock( [&exec, &order] (auto& order_cache)
|
||||
{
|
||||
auto it = order_cache.find(order.key());
|
||||
if(it != order_cache.end())
|
||||
exec = FIX::ExecType(FIX::ExecType_ORDER_STATUS);
|
||||
});
|
||||
|
||||
FIX::OrdStatus status;
|
||||
uint filled = 0; uint leaves = 0; double prx = 0.0;
|
||||
api::orderman::NativeOrderState native;
|
||||
process_order_state(order.state(), filled, leaves, prx, status, native);
|
||||
|
||||
auto sec = _router.lookup(order.def().security_key());
|
||||
FIX44::ExecutionReport report(
|
||||
FIX::OrderID(std::to_string(order.key())),
|
||||
FIX::ExecID("0"),
|
||||
exec,
|
||||
status,
|
||||
FIX::Side(order.def().side() == bts::api::SIDE_BUY ? FIX::Side_BUY : FIX::Side_SELL),
|
||||
FIX::LeavesQty(leaves),
|
||||
FIX::CumQty(filled),
|
||||
FIX::AvgPx(prx));
|
||||
|
||||
if(sec)
|
||||
report.setField(FIX::Symbol(sec->symbol()));
|
||||
report.setField(FIX::FIELD::Account, FIX::Account(order.def().account()).getString() );
|
||||
report.setField(FIX::FIELD::OrderQty, FIX::OrderQty(order.state().quantity()).getString() );
|
||||
report.setField(FIX::FIELD::Price, FIX::Price(order.state().price()).getString() );
|
||||
report.setField(FIX::FIELD::TimeInForce, FIX::TimeInForce((char)order.def().time_in_force()).getString() );
|
||||
auto ts = order.state().last_change();
|
||||
report.setField(FIX::FIELD::TransactTime, google::protobuf::util::TimeUtil::ToString(ts));
|
||||
|
||||
if(order.state().last_msg() != "")
|
||||
report.setField(FIX::FIELD::Text, FIX::Text(order.state().last_msg()));
|
||||
sec = resolve_execution_report_security(sec, report);
|
||||
if (native.exchange_order_id() != "")
|
||||
{
|
||||
report.setField(FIX::FIELD::OrdType, FIX::OrdType((char)native.native_type()).getString());;
|
||||
report.setField(FIX::FIELD::SecondaryOrderID, FIX::SecondaryOrderID(native.exchange_order_id()).getString());;
|
||||
}
|
||||
//TODO Big race here, we need a correlation id of some sort
|
||||
_fix_clorderids.lock([&order, &report] (auto& fix_clorderids)
|
||||
{
|
||||
auto fixit = fix_clorderids.find(order.key());
|
||||
if(fixit != fix_clorderids.end())
|
||||
report.setField(FIX::FIELD::ClOrdID, FIX::ClOrdID(fixit->second).getString());
|
||||
});
|
||||
return report;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "SessionBase.h"
|
||||
#include "lib/md/SecurityPtr.h"
|
||||
|
||||
#include <quickfix/Message.h>
|
||||
#include <quickfix/SessionID.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace FIX44
|
||||
{
|
||||
class OrderStatusRequest;
|
||||
class SecurityDefinitionRequest;
|
||||
}
|
||||
|
||||
namespace bts {
|
||||
|
||||
namespace fix {
|
||||
|
||||
class DropCopySession : public SessionBase
|
||||
{
|
||||
public:
|
||||
DropCopySession(bts::api::ServiceContext&, IFixRouter&, const FIX::SessionID&);
|
||||
virtual ~DropCopySession();
|
||||
|
||||
void onLogon(const FIX::SessionID&);
|
||||
void onLogout(const FIX::SessionID&);
|
||||
|
||||
void fromApp(const FIX::Message &message,const FIX::SessionID &sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType);
|
||||
|
||||
void onMessage(const FIX44::OrderStatusRequest& m, const FIX::SessionID& s);
|
||||
void onMessage(const FIX44::SecurityDefinitionRequest& m, const FIX::SessionID& s);
|
||||
|
||||
protected:
|
||||
virtual void subscribe_fills_and_orders(const FIX::SessionID sessionID);
|
||||
virtual void process_order_state(const bts::api::orderman::OrderState& state, uint& filled, uint& leaves, double& avgprx,
|
||||
FIX::OrdStatus& fstatus, bts::api::orderman::NativeOrderState& native, const bts::api::orderman::Fill* fill = nullptr);
|
||||
|
||||
md::SecurityPtr resolve_execution_report_security(md::SecurityPtr sec, FIX44::ExecutionReport& report);
|
||||
virtual FIX44::ExecutionReport build_fill(const FIX::SessionID& sess, const bts::api::orderman::Fill& fill, bool change = false);
|
||||
virtual FIX44::ExecutionReport build_order_status(const FIX::SessionID& sess, const bts::api::orderman::OrderRecord& order, bool change = false);
|
||||
|
||||
protected:
|
||||
bts::api::orderman::OrderService _orderservice;
|
||||
|
||||
utils::LockBox< std::unordered_map<uint, std::shared_ptr<const bts::api::orderman::OrderRecord>> > _order_cache;
|
||||
utils::LockBox< std::unordered_map<uint, std::string> > _fix_clorderids;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::shared_ptr<PImpl> me;
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "FixInit.h"
|
||||
|
||||
#include "IFixSession.h"
|
||||
#include "FixRouter.h"
|
||||
#include "OrderSession.h"
|
||||
#include "DropCopySession.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/fix.pb.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
#include "lib/config/protos/logging.pb.h"
|
||||
#include "lib/utils/file_utils.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
|
||||
#include <quickfix/FileStore.h>
|
||||
#include <quickfix/SocketAcceptor.h>
|
||||
#include <quickfix/SessionSettings.h>
|
||||
#include <quickfix/FileLog.h>
|
||||
#include <quickfix/Message.h>
|
||||
#include <quickfix/SessionID.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
namespace bts
|
||||
{
|
||||
namespace fix
|
||||
{
|
||||
|
||||
boost::filesystem::path fix_path ()
|
||||
{
|
||||
auto store_root = os::bts_data_path();
|
||||
std::string base_path = store_root;
|
||||
boost::filesystem::path result = utils::normalize_data_path (base_path);
|
||||
result /= "fix";
|
||||
return result;
|
||||
}
|
||||
|
||||
int FixInit::start_acceptor(bts::api::ServiceContext& sc, md::MDClient& md)
|
||||
{
|
||||
std::string settings_file = os::sparkd_wd() + "/data_files/fix/";
|
||||
settings_file += config::get<config::Fix>().fix_config_file();
|
||||
bool orders_allowed = config::get<config::Fix>().allow_orders();
|
||||
|
||||
std::ifstream str{settings_file};
|
||||
FIX::SessionSettings settings( str );
|
||||
auto sess = settings.getSessions();
|
||||
int scount = sess.size();
|
||||
UNUSED(scount);
|
||||
|
||||
FixRouter application(md, [&sc, orders_allowed](IFixRouter& router, const auto& id)
|
||||
{
|
||||
if(orders_allowed)
|
||||
return std::shared_ptr<IFixSession>(new OrderSession(sc, router, id));
|
||||
else
|
||||
return std::shared_ptr<IFixSession>(new DropCopySession(sc, router, id));
|
||||
});
|
||||
FIX::FileStoreFactory storeFactory( settings );
|
||||
FIX::FileLogFactory logFactory( settings );
|
||||
FIX::SocketAcceptor acceptor( application, storeFactory, settings, logFactory );
|
||||
|
||||
acceptor.start();
|
||||
utils::Dispatcher::current().run();
|
||||
acceptor.stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace bts {
|
||||
namespace api { class ServiceContext; }
|
||||
namespace md { class MDClient; }
|
||||
namespace fix {
|
||||
|
||||
class FixInit
|
||||
{
|
||||
public:
|
||||
static int start_acceptor(bts::api::ServiceContext& sc, md::MDClient& md);
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,240 @@
|
||||
#include "FixRouter.h"
|
||||
|
||||
#include "IFixSession.h"
|
||||
#include "SecurityIndex.h"
|
||||
#include "lib/md/MDConsumerHandle.h"
|
||||
#include "lib/utils/LockBox.h"
|
||||
|
||||
#include <quickfix/FileStore.h>
|
||||
#include <quickfix/SocketAcceptor.h>
|
||||
#include <quickfix/SessionSettings.h>
|
||||
#include <quickfix/FileLog.h>
|
||||
#include <quickfix/Message.h>
|
||||
#include <quickfix/SessionID.h>
|
||||
#include <quickfix/fix44/Reject.h>
|
||||
#include <quickfix/fix44/BusinessMessageReject.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
namespace bts
|
||||
{
|
||||
namespace fix
|
||||
{
|
||||
|
||||
struct FixRouter::PImpl
|
||||
{
|
||||
utils::LockBox< boost::container::flat_map< FIX::SessionID, std::shared_ptr<IFixSession> > > sessions;
|
||||
SecurityIndex index;
|
||||
std::mutex lock;
|
||||
Factory factory;
|
||||
|
||||
PImpl(md::MDClient& md, Factory factory) :
|
||||
sessions(),
|
||||
index(md),
|
||||
lock(),
|
||||
factory(factory)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
FixRouter::FixRouter(md::MDClient& md, Factory factory) : me(new PImpl(md, factory))
|
||||
{
|
||||
}
|
||||
|
||||
FixRouter::~FixRouter()
|
||||
{
|
||||
}
|
||||
|
||||
void FixRouter::send(FIX::Message& mess, const FIX::SessionID& sess)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(me->lock);
|
||||
FIX::Session::sendToTarget(mess, sess);
|
||||
}
|
||||
catch(FIX::SessionNotFound)
|
||||
{
|
||||
bts_assert_msg(false, "session not found " + sess.toString());
|
||||
}
|
||||
catch(std::exception ex)
|
||||
{
|
||||
LOG(Fix, Error, "session error {} {}", sess.toString(), std::string(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void FixRouter::send_session_reject(const FIX::SessionID& sess, const std::string& text, const FIX::Message* refmessage)
|
||||
{
|
||||
FIX44::Reject rej;
|
||||
if(refmessage && refmessage->getHeader().isSetField(FIX::FIELD::MsgSeqNum))
|
||||
rej.setField(FIX::FIELD::RefSeqNum, refmessage->getHeader().getField(FIX::FIELD::MsgSeqNum));
|
||||
rej.setField(FIX::FIELD::Text, text);
|
||||
send(rej, sess);
|
||||
}
|
||||
|
||||
void FixRouter::send_biz_reject(const FIX::SessionID sess, const std::string& text, const FIX::Message& refmessage, int reason_code)
|
||||
{
|
||||
FIX44::BusinessMessageReject rej;
|
||||
rej.setField(FIX::FIELD::RefMsgType, refmessage.getHeader().getField(FIX::FIELD::MsgType));
|
||||
rej.setField(FIX::FIELD::RefSeqNum, refmessage.getHeader().getField(FIX::FIELD::MsgSeqNum));
|
||||
rej.setField(FIX::FIELD::BusinessRejectReason, std::to_string(reason_code));
|
||||
rej.setField(FIX::FIELD::Text, text);
|
||||
send(rej, sess);
|
||||
}
|
||||
|
||||
void FixRouter::fromAdmin(const FIX::Message& message, const FIX::SessionID& sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).fromAdmin(message, sessionID);
|
||||
}
|
||||
catch (FIX::FieldNotFound ex) { handle_field_not_found(ex, sessionID, message); }
|
||||
catch (FIX::UnsupportedMessageType) { throw; }
|
||||
catch (FIX::IncorrectTagValue) { throw; }
|
||||
catch (std::exception ex) { handle_session_exception(ex, sessionID, &message); }
|
||||
}
|
||||
|
||||
void FixRouter::fromApp(const FIX::Message &message,const FIX::SessionID &sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).fromApp(message, sessionID);
|
||||
}
|
||||
catch (FIX::FieldNotFound ex) { handle_field_not_found(ex, sessionID, message); }
|
||||
catch (FIX::UnsupportedMessageType) { throw; }
|
||||
catch (FIX::IncorrectTagValue) { throw; }
|
||||
catch (std::exception ex) { handle_session_exception(ex, sessionID, &message); }
|
||||
}
|
||||
|
||||
void FixRouter::onCreate(const FIX::SessionID& sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).onCreate(sessionID);
|
||||
}
|
||||
catch(std::exception ex)
|
||||
{
|
||||
handle_session_exception(ex, sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
void FixRouter::onLogon(const FIX::SessionID& sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).onLogon(sessionID);
|
||||
}
|
||||
catch (std::exception ex)
|
||||
{
|
||||
handle_session_exception(ex, sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
void FixRouter::onLogout(const FIX::SessionID& sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO stackoverflow?
|
||||
session(sessionID).onLogout(sessionID);
|
||||
}
|
||||
catch (std::exception ex)
|
||||
{
|
||||
handle_session_exception(ex, sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
void FixRouter::toAdmin(FIX::Message& message, const FIX::SessionID& sessionID)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).toAdmin(message, sessionID);
|
||||
}
|
||||
catch (FIX::FieldNotFound ex) { handle_field_not_found(ex, sessionID, message); }
|
||||
catch (FIX::UnsupportedMessageType) { throw; }
|
||||
catch (FIX::IncorrectTagValue) { throw; }
|
||||
catch (std::exception ex) { handle_session_exception(ex, sessionID, &message); }
|
||||
}
|
||||
|
||||
void FixRouter::toApp(FIX::Message& message, const FIX::SessionID& sessionID) throw(FIX::DoNotSend)
|
||||
{
|
||||
try
|
||||
{
|
||||
session(sessionID).toApp(message, sessionID);
|
||||
}
|
||||
catch (FIX::FieldNotFound ex) { handle_field_not_found(ex, sessionID, message); }
|
||||
catch (FIX::UnsupportedMessageType) { throw FIX::DoNotSend() ; }
|
||||
catch (FIX::IncorrectTagValue) { throw FIX::DoNotSend() ; }
|
||||
catch (std::exception ex) { handle_session_exception(ex, sessionID, &message); }
|
||||
}
|
||||
|
||||
IFixSession& FixRouter::session(const FIX::SessionID& sid)
|
||||
{
|
||||
auto ptr = me->sessions.lock([this, sid] (auto& sessions)
|
||||
{
|
||||
auto it = sessions.find(sid);
|
||||
if(it == sessions.end())
|
||||
{
|
||||
auto ptr = me->factory(*this, sid);
|
||||
sessions[sid] = ptr;
|
||||
return ptr;
|
||||
}
|
||||
else
|
||||
return it->second;
|
||||
});
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
void FixRouter::handle_session_exception(const std::exception& ex, const FIX::SessionID& sessionID, const FIX::Message* message)
|
||||
{
|
||||
std::string msg = "==ERROR in session " + sessionID.toString() + "== ";
|
||||
msg += (message ? message->getHeader().getField(FIX::FIELD::MsgType) : "");
|
||||
msg += std::string(ex.what());
|
||||
|
||||
LOG(Fix, Error, msg.c_str());
|
||||
//now shut this fucker down
|
||||
try
|
||||
{
|
||||
auto sess = FIX::Session::lookupSession(sessionID);
|
||||
if(sess != nullptr)
|
||||
sess->disconnect();
|
||||
}
|
||||
catch(...) { }
|
||||
}
|
||||
|
||||
void FixRouter::handle_field_not_found(const FIX::FieldNotFound& ex, const FIX::SessionID& sess, const FIX::Message& message)
|
||||
{
|
||||
if(ex.field > 0)
|
||||
{
|
||||
std::string text = "Field " + std::to_string(ex.field) + " was expected but not found";
|
||||
send_biz_reject(sess, text, message, 5 );
|
||||
}
|
||||
//QF does not include the tag number....
|
||||
//throw new FieldNotFoundException($"Field {ex.Field} was expected but not found", ex);
|
||||
else
|
||||
throw ex;
|
||||
}
|
||||
|
||||
std::vector<md::SecurityPtr> FixRouter::get_securities()
|
||||
{
|
||||
return me->index.get_securities();
|
||||
}
|
||||
|
||||
const md::SecurityPtr FixRouter::lookup(md::security_key key)
|
||||
{
|
||||
return me->index.lookup(key);
|
||||
}
|
||||
|
||||
const md::SecurityPtr FixRouter::lookup(const std::string& desc)
|
||||
{
|
||||
return me->index.lookup(desc);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "IFixSession.h"
|
||||
#include "IFixRouter.h"
|
||||
|
||||
#include <quickfix/MessageCracker.h>
|
||||
#include <quickfix/Message.h>
|
||||
#include <quickfix/SessionID.h>
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace bts {
|
||||
namespace api { class ServiceContext; }
|
||||
namespace md { class MDClient; }
|
||||
namespace fix {
|
||||
|
||||
class FixRouter : public IFixSession, public FIX::MessageCracker, public IFixRouter
|
||||
{
|
||||
public:
|
||||
typedef std::function< std::shared_ptr<IFixSession> (IFixRouter&, const FIX::SessionID&) > Factory;
|
||||
|
||||
FixRouter(md::MDClient&, Factory factory);
|
||||
~FixRouter();
|
||||
|
||||
void send(FIX::Message&, const FIX::SessionID&) override;
|
||||
void send_session_reject(const FIX::SessionID& sess, const std::string& text, const FIX::Message* refmessage = nullptr) override;
|
||||
void send_biz_reject(const FIX::SessionID sess, const std::string& text, const FIX::Message& refmessage, int reason_code = 0) override;
|
||||
|
||||
public:
|
||||
void fromAdmin(const FIX::Message& message, const FIX::SessionID& sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon);
|
||||
void fromApp(const FIX::Message &message,const FIX::SessionID &sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType);
|
||||
void toAdmin(FIX::Message&, const FIX::SessionID&);
|
||||
void toApp(FIX::Message&, const FIX::SessionID&) throw(FIX::DoNotSend);
|
||||
|
||||
void onCreate(const FIX::SessionID&);
|
||||
void onLogon(const FIX::SessionID&);
|
||||
void onLogout(const FIX::SessionID&);
|
||||
|
||||
private:
|
||||
IFixSession& session(const FIX::SessionID&);
|
||||
void handle_session_exception(const std::exception& ex, const FIX::SessionID& sessionID, const FIX::Message* message = nullptr);
|
||||
void handle_field_not_found(const FIX::FieldNotFound& ex, const FIX::SessionID& sess, const FIX::Message& message);
|
||||
|
||||
private:
|
||||
std::vector<md::SecurityPtr> get_securities() override;
|
||||
const md::SecurityPtr lookup(md::security_key key) override;
|
||||
const md::SecurityPtr lookup(const std::string& desc) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/md/common.h"
|
||||
#include "lib/md/SecurityPtr.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace FIX
|
||||
{
|
||||
class Message;
|
||||
class SessionID;
|
||||
}
|
||||
|
||||
namespace bts
|
||||
{
|
||||
|
||||
namespace api
|
||||
{
|
||||
class Security;
|
||||
}
|
||||
|
||||
namespace fix
|
||||
{
|
||||
class IFixRouter
|
||||
{
|
||||
public:
|
||||
virtual std::vector<md::SecurityPtr> get_securities() = 0;
|
||||
virtual const md::SecurityPtr lookup(md::security_key key) = 0;
|
||||
virtual const md::SecurityPtr lookup(const std::string& desc) = 0;
|
||||
|
||||
virtual void send(FIX::Message& mess, const FIX::SessionID& sess) = 0;
|
||||
virtual void send_session_reject(const FIX::SessionID& sess, const std::string& text, const FIX::Message* refmessage = nullptr) = 0;
|
||||
virtual void send_biz_reject(const FIX::SessionID sess, const std::string& text, const FIX::Message& refmessage, int reason_code = 0) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/utils/warn_ctrl.h"
|
||||
GCC_DIAG_OFF("-Woverloaded-virtual")
|
||||
GCC_DIAG_OFF("-Wdeprecated")
|
||||
GCC_DIAG_OFF("-Wignored-qualifiers")
|
||||
//GCC_DIAG_OFF("-Wunused-parameter")
|
||||
|
||||
#include <quickfix/Application.h>
|
||||
|
||||
namespace bts {
|
||||
namespace fix {
|
||||
|
||||
class IFixSession : public FIX::Application
|
||||
{
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,226 @@
|
||||
#include "OrderSession.h"
|
||||
#include "FixRouter.h"
|
||||
|
||||
#include "lib/api/service_helpers/AdminServiceUtils.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
|
||||
#include <quickfix/fix44/OrderCancelRequest.h>
|
||||
#include <quickfix/fix44/OrderCancelReplaceRequest.h>
|
||||
#include <quickfix/fix44/NewOrderSingle.h>
|
||||
#include <quickfix/fix44/OrderStatusRequest.h>
|
||||
#include <quickfix/fix44/SecurityDefinitionRequest.h>
|
||||
#include <quickfix/fix44/SecurityDefinition.h>
|
||||
#include <quickfix/fix44/ExecutionReport.h>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
#include <google/protobuf/util/time_util.h>
|
||||
|
||||
/*
|
||||
order for 4 lot CLZ9
|
||||
8=FIX.4.49=15635=D49=SPARKFIX56=CLIENT134=150=3073797=Y52=20000809-20:20:5011=900010081=ACC A21=255=SIM:CLZ954=138=440=259=044=5060=20000809-18:20:32207=SIM10=045
|
||||
|
||||
order for 4 lot CLZ9-CLH0
|
||||
8=FIX.4.49=15635=D49=SPARKFIX56=CLIENT134=150=3073797=Y52=20000809-20:20:5011=900010081=ACC A21=255=SIM:CLZ9-CLH054=138=440=259=044=7460=20000809-18:20:32207=SIM10=045
|
||||
*/
|
||||
|
||||
namespace bts {
|
||||
namespace fix {
|
||||
|
||||
OrderSession::OrderSession(bts::api::ServiceContext& sc, IFixRouter& rt, const FIX::SessionID& id) :
|
||||
DropCopySession(sc, rt, id)
|
||||
{
|
||||
}
|
||||
|
||||
OrderSession::~OrderSession()
|
||||
{
|
||||
}
|
||||
|
||||
void OrderSession::fromApp(const FIX::Message &message,const FIX::SessionID &sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType)
|
||||
{
|
||||
bts_assert_msg(sessionID == _sess, "invalid session");
|
||||
auto mt = message.getHeader().getField(FIX::FIELD::MsgType);
|
||||
if(mt == FIX44::OrderStatusRequest::MsgType() ||
|
||||
mt == FIX44::SecurityDefinitionRequest::MsgType() ||
|
||||
mt == FIX44::NewOrderSingle::MsgType() ||
|
||||
mt == FIX44::OrderCancelReplaceRequest::MsgType() ||
|
||||
mt == FIX44::OrderCancelRequest::MsgType()
|
||||
)
|
||||
{
|
||||
LOG(Fix, Info, "IN: {}", message.toString());
|
||||
crack(message, sessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Fix, Info, "UNSUPPORTED IN: {}", message.toString());
|
||||
throw FIX::UnsupportedMessageType();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OrderSession::onMessage(const FIX44::OrderCancelRequest& cancel, const FIX::SessionID& s)
|
||||
{
|
||||
auto id = cancel.getField(FIX::FIELD::OrderID);
|
||||
uint key = 0;
|
||||
try
|
||||
{
|
||||
key = atoi(id.c_str());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::OrderID);
|
||||
}
|
||||
api::orderman::Cancel can;
|
||||
can.set_order_key(key);
|
||||
_orderservice.cancel_order(can, [this, s, cancel] (const auto& resp)
|
||||
{
|
||||
if(!resp.success() || resp.value()->error_code() != bts::api::orderman::ORDER_ERROR_OK)
|
||||
{
|
||||
send_cancel_reject(s, "Cancel Rejected: " + resp.value()->msg(), cancel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OrderSession::onMessage(const FIX44::OrderCancelReplaceRequest& replace, const FIX::SessionID& s)
|
||||
{
|
||||
auto id = replace.getField(FIX::FIELD::OrderID);
|
||||
uint key = 0;
|
||||
try
|
||||
{
|
||||
key = atoi(id.c_str());
|
||||
}
|
||||
catch(...) { throw FIX::IncorrectTagValue(FIX::FIELD::OrderID); }
|
||||
double price = 0.0;
|
||||
try
|
||||
{
|
||||
price = atof(replace.getField(FIX::FIELD::Price).c_str());
|
||||
}
|
||||
catch(...) { throw FIX::IncorrectTagValue(FIX::FIELD::Price); }
|
||||
uint sz = 0;
|
||||
try
|
||||
{
|
||||
sz = (uint) abs( atoi (replace.getField(FIX::FIELD::OrderQty).c_str()) );
|
||||
}
|
||||
catch(...) { throw FIX::IncorrectTagValue(FIX::FIELD::OrderQty); }
|
||||
api::orderman::Update upd;
|
||||
upd.set_order_key(key);
|
||||
upd.set_price(price);
|
||||
upd.set_quantity(sz);
|
||||
_orderservice.update_order(upd, [this, s, replace] (const auto& resp)
|
||||
{
|
||||
if(!resp.success() || resp.value()->error_code() != bts::api::orderman::ORDER_ERROR_OK)
|
||||
{
|
||||
send_cancel_reject(s, "Cancel Rejected: " + resp.value()->msg(), replace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OrderSession::onMessage(const FIX44::NewOrderSingle& create, const FIX::SessionID& s)
|
||||
{
|
||||
api::Venue ven = convert_exchange(FIX::SecurityExchange(create.getField(FIX::FIELD::SecurityExchange)));
|
||||
if (ven == bts::api::VENUE_UNSET)
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::SecurityExchange);
|
||||
|
||||
auto sec = _router.lookup(create.getField(FIX::FIELD::Symbol));
|
||||
if (!sec)
|
||||
sec = _router.lookup(create.getField(FIX::FIELD::SecurityDesc));
|
||||
if (!sec)
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::SecurityDesc);
|
||||
|
||||
api::orderman::OrderDefinition def;
|
||||
try
|
||||
{
|
||||
std::string manual = "Y";
|
||||
if(create.isSetField(FIX::FIELD::ManualOrderIndicator))
|
||||
manual = create.getField(FIX::FIELD::ManualOrderIndicator);
|
||||
def.set_account(create.getField(FIX::FIELD::Account));
|
||||
def.set_account_id(lookup_account(def.account()));
|
||||
def.set_security_key(sec->id());
|
||||
def.set_side(create.getField(FIX::FIELD::Side) == "1" ? api::SIDE_BUY : api::SIDE_SELL);
|
||||
def.set_user(_user_name);
|
||||
def.set_user_id(lookup_user(_user_name));
|
||||
def.set_manual_order_indicator(manual == "Y" ? bts::api::orderman::MANUAL_ORDER_INDICATOR_YES : bts::api::orderman::MANUAL_ORDER_INDICATOR_NO);
|
||||
def.set_time_in_force( (api::orderman::TimeInForce) int(create.getField(FIX::FIELD::TimeInForce)[0] ) );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::TimeInForce);
|
||||
}
|
||||
|
||||
bool price_req = false;
|
||||
set_ordertype(def, create, price_req);
|
||||
api::orderman::Create cr;
|
||||
cr.mutable_new_order()->CopyFrom(def);
|
||||
try
|
||||
{
|
||||
if (price_req)
|
||||
cr.set_price( atof( create.getField(FIX::FIELD::Price).c_str() ) );;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::Price);
|
||||
}
|
||||
try
|
||||
{
|
||||
cr.set_quantity( (uint) abs(atoi( create.getField(FIX::FIELD::OrderQty).c_str() )) );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::OrderQty);
|
||||
}
|
||||
_orderservice.create_order(cr, [this, s, create] (const auto& resp)
|
||||
{
|
||||
if(!resp.success() || resp.value()->error_code() != bts::api::orderman::ORDER_ERROR_OK)
|
||||
{
|
||||
_router.send_biz_reject(s, "Order Rejected: " + resp.value()->msg(), create);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OrderSession::set_ordertype(api::orderman::OrderDefinition& def, const FIX44::NewOrderSingle& req, bool& price_req)
|
||||
{
|
||||
price_req = false;
|
||||
auto ot = req.getField(FIX::FIELD::OrdType);
|
||||
if(ot == "1")
|
||||
{
|
||||
def.mutable_custom_def()->mutable_market();
|
||||
}
|
||||
else if(ot == "2")
|
||||
{
|
||||
def.mutable_custom_def()->mutable_limit();
|
||||
price_req = true;
|
||||
}
|
||||
else if(ot == "3")
|
||||
{
|
||||
auto s = def.mutable_custom_def()->mutable_stop();
|
||||
double stoppx = 0.0;
|
||||
try
|
||||
{
|
||||
stoppx = atof(req.getField(FIX::FIELD::StopPx).c_str());
|
||||
}
|
||||
catch(...) { throw FIX::IncorrectTagValue(FIX::FIELD::StopPx); }
|
||||
s->set_stop_price(stoppx);
|
||||
}
|
||||
else if(ot == "4")
|
||||
{
|
||||
price_req = true;
|
||||
auto s = def.mutable_custom_def()->mutable_stop_limit();
|
||||
double stoppx = 0.0;
|
||||
try
|
||||
{
|
||||
stoppx = atof(req.getField(FIX::FIELD::StopPx).c_str());
|
||||
}
|
||||
catch(...) { throw FIX::IncorrectTagValue(FIX::FIELD::StopPx); }
|
||||
s->set_stop_price(stoppx);
|
||||
}
|
||||
else
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::OrdType);
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "DropCopySession.h"
|
||||
|
||||
namespace bts {
|
||||
namespace fix {
|
||||
|
||||
class OrderSession : public DropCopySession
|
||||
{
|
||||
public:
|
||||
OrderSession(bts::api::ServiceContext&, IFixRouter&, const FIX::SessionID&);
|
||||
virtual ~OrderSession();
|
||||
|
||||
void fromApp(const FIX::Message &message,const FIX::SessionID &sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType);
|
||||
|
||||
void onMessage(const FIX44::OrderCancelRequest& cancel, const FIX::SessionID& s);
|
||||
void onMessage(const FIX44::OrderCancelReplaceRequest& replace, const FIX::SessionID& s);
|
||||
void onMessage(const FIX44::NewOrderSingle& create, const FIX::SessionID& s);
|
||||
|
||||
protected:
|
||||
void set_ordertype(api::orderman::OrderDefinition& def, const FIX44::NewOrderSingle& req, bool& price_req);
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "SecurityIndex.h"
|
||||
|
||||
#include "lib/md/HandleIndex.h"
|
||||
|
||||
|
||||
namespace bts {
|
||||
namespace fix {
|
||||
|
||||
struct SecurityIndex::PImpl
|
||||
{
|
||||
md::DescHandleIndex index;
|
||||
md::KeyHandleIndex key_index;
|
||||
PImpl(md::MDClient& md) : index(md), key_index(md)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
SecurityIndex::SecurityIndex(md::MDClient& md) : me(new PImpl(md))
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<md::SecurityPtr> SecurityIndex::get_securities()
|
||||
{
|
||||
std::vector<md::SecurityPtr> list;
|
||||
me->index.security_index.lock([&list] (const auto& secmap)
|
||||
{
|
||||
for(const auto& s : secmap)
|
||||
list.push_back(s.second);
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
md::SecurityPtr SecurityIndex::lookup(const std::string& desc)
|
||||
{
|
||||
md::SecurityPtr ptr = nullptr;
|
||||
me->index.security_index.lock([&ptr, &desc] (const auto& secmap)
|
||||
{
|
||||
auto it = secmap.find(desc);
|
||||
if(it != secmap.end())
|
||||
ptr = it->second;
|
||||
});
|
||||
return ptr;
|
||||
}
|
||||
|
||||
md::SecurityPtr SecurityIndex::lookup(md::security_key key)
|
||||
{
|
||||
md::SecurityPtr ptr = nullptr;
|
||||
me->key_index.security_index.lock([&ptr, &key] (const auto& secmap)
|
||||
{
|
||||
auto it = secmap.find( key );
|
||||
if(it != secmap.end())
|
||||
ptr = it->second;
|
||||
});
|
||||
return ptr;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/md/common.h"
|
||||
#include "lib/md/SecurityPtr.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace api { class Security; }
|
||||
namespace md { class MDClient; }
|
||||
namespace fix {
|
||||
|
||||
//A Wrapper for HandleIndex that compiles in c++14 (because of Fix dynamic exception stuff)
|
||||
class SecurityIndex
|
||||
{
|
||||
public:
|
||||
SecurityIndex(md::MDClient&);
|
||||
|
||||
std::vector<md::SecurityPtr> get_securities();
|
||||
md::SecurityPtr lookup(const std::string& desc);
|
||||
md::SecurityPtr lookup(md::security_key key);
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::shared_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,335 @@
|
||||
#include "SessionBase.h"
|
||||
|
||||
#include "IFixRouter.h"
|
||||
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/api/service_helpers/AdminServiceUtils.h"
|
||||
#include "lib/utils/AuthToken.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
|
||||
#include <quickfix/Message.h>
|
||||
#include <quickfix/FixValues.h>
|
||||
#include <quickfix/SessionID.h>
|
||||
#include <quickfix/fix44/Logon.h>
|
||||
#include <quickfix/fix44/Heartbeat.h>
|
||||
#include <quickfix/fix44/BusinessMessageReject.h>
|
||||
#include <quickfix/fix44/OrderCancelReject.h>
|
||||
#include <quickfix/fix44/OrderCancelRequest.h>
|
||||
#include <quickfix/fix44/OrderCancelReplaceRequest.h>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
|
||||
|
||||
namespace bts {
|
||||
namespace fix {
|
||||
|
||||
SessionBase::SessionBase(bts::api::ServiceContext& sc, IFixRouter& router, const FIX::SessionID& sess) :
|
||||
_authservice( sc.locate<bts::api::user::AuthService>() ),
|
||||
_adminservice( sc.locate<bts::api::admin::AdminService>() ),
|
||||
_router(router),
|
||||
_sess(sess),
|
||||
|
||||
_admin_data(),
|
||||
_user_name(),
|
||||
_token()
|
||||
{
|
||||
_adminobservable = _adminservice.subscribe_admin_data() |
|
||||
rxcpp::operators::map(
|
||||
[this](const auto& update)
|
||||
{
|
||||
bts_assert(update.all().size() == 1);
|
||||
|
||||
auto adata = _admin_data.lock( [&update] (auto& ad)
|
||||
{
|
||||
ad = *update.all()[0];
|
||||
return ad;
|
||||
} );
|
||||
return std::shared_ptr<AdminModelWrapper>(new AdminModelWrapper(adata, _user_name) ); //TODO RACE
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
SessionBase::~SessionBase()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void SessionBase::fromAdmin(const FIX::Message& message, const FIX::SessionID& sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon)
|
||||
{
|
||||
bts_assert_msg(sessionID == _sess, "invalid session");
|
||||
|
||||
FIX::MsgType type;
|
||||
message.getHeader().getField(type);
|
||||
if(type.getString() == FIX44::Logon::MsgType())
|
||||
{
|
||||
try
|
||||
{
|
||||
LOG(Fix, Info, "LOGIN: {}", message.toString() );
|
||||
FIX44::Logon msg = (FIX44::Logon)message;
|
||||
if(msg.isSetField(FIX::FIELD::Username))
|
||||
_user_name = msg.getField(FIX::FIELD::Username);
|
||||
std::string pass;
|
||||
if(msg.isSetField(FIX::FIELD::Password))
|
||||
pass = msg.getField(FIX::FIELD::Password);
|
||||
|
||||
bts::api::user::AuthenticateLoginRequest auth;
|
||||
auth.set_login(_user_name);
|
||||
auth.set_password(pass);
|
||||
_authservice.authenticate_login(auth, [this, sessionID] (auto resp)
|
||||
{
|
||||
if (resp.success() && resp.value()->is_valid())
|
||||
{
|
||||
_token = resp.value()->token();
|
||||
}
|
||||
else
|
||||
{
|
||||
//I can't just throw here cuz c++
|
||||
std::string reason = "LOGIN FAILED " + resp.value()->reason();
|
||||
LOG(Fix, Error, reason.c_str());
|
||||
try
|
||||
{
|
||||
auto sess = FIX::Session::lookupSession(sessionID);
|
||||
if(sess != nullptr)
|
||||
sess->disconnect();
|
||||
}
|
||||
catch(...) {}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (std::exception ex)
|
||||
{
|
||||
throw FIX::RejectLogon("Logon rejected " + std::string(ex.what()));
|
||||
}
|
||||
}
|
||||
else if(type.getString() == FIX44::Heartbeat::MsgType())
|
||||
{
|
||||
FIX44::Heartbeat hb = (FIX44::Heartbeat)message;
|
||||
handle_heartbeat(hb, sessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Fix, Info, "ADMININ: {}", message.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
void SessionBase::handle_heartbeat(const FIX44::Heartbeat& hb, const FIX::SessionID& sessionID)
|
||||
{
|
||||
//TODO I'd previously sent this to the latency service when it was out of process, not sure what to do now
|
||||
UNUSED(hb);
|
||||
UNUSED(sessionID);
|
||||
}
|
||||
|
||||
void SessionBase::toAdmin(FIX::Message& m, const FIX::SessionID& sessionID)
|
||||
{
|
||||
bts_assert_msg(sessionID == _sess, "invalid session");
|
||||
LOG(Fix, Info, "ADMINOUT: {}", m.toString());
|
||||
}
|
||||
|
||||
void SessionBase::toApp(FIX::Message& m, const FIX::SessionID&) throw(FIX::DoNotSend)
|
||||
{
|
||||
LOG(Fix, Info, "OUT: {}", m.toString());
|
||||
}
|
||||
|
||||
void SessionBase::onCreate(const FIX::SessionID& s)
|
||||
{
|
||||
LOG(Fix, Info, "CREATE: {}", s.toString());
|
||||
}
|
||||
|
||||
void SessionBase::onMessage(const FIX44::BusinessMessageReject& n, const FIX::SessionID& s)
|
||||
{
|
||||
LOG(Fix, Warn, "{} Business Reject: {} {}", s.toString(), n.getField(FIX::FIELD::BusinessRejectReason), n.getField(FIX::FIELD::Text) );
|
||||
}
|
||||
|
||||
void SessionBase::send_cancel_reject(const FIX::SessionID& sess, const std::string& text, const FIX::Message& refmessage)
|
||||
{
|
||||
FIX44::OrderCancelReject rej;
|
||||
if(refmessage.isSetField(FIX::FIELD::ClOrdID))
|
||||
rej.setField(FIX::FIELD::ClOrdID, refmessage.getField(FIX::FIELD::ClOrdID));
|
||||
if(refmessage.isSetField(FIX::FIELD::OrigClOrdID))
|
||||
rej.setField(FIX::FIELD::OrigClOrdID, refmessage.getField(FIX::FIELD::OrigClOrdID));
|
||||
if(refmessage.isSetField(FIX::FIELD::OrderID))
|
||||
rej.setField(FIX::FIELD::OrderID, refmessage.getField(FIX::FIELD::OrderID));
|
||||
rej.setField(FIX::FIELD::Text, text);
|
||||
|
||||
if(refmessage.getField(FIX::FIELD::MsgType) == FIX44::OrderCancelRequest::MsgType())
|
||||
{
|
||||
rej.setField(FIX::FIELD::CxlRejResponseTo, "1");
|
||||
}
|
||||
else if(refmessage.getField(FIX::FIELD::MsgType) == FIX44::OrderCancelReplaceRequest::MsgType())
|
||||
{
|
||||
rej.setField(FIX::FIELD::CxlRejResponseTo, "2");
|
||||
}
|
||||
_router.send(rej, sess);
|
||||
}
|
||||
|
||||
bool SessionBase::security_types_equal(bts::api::SecurityType btstype, FIX::SecurityType fixtype)
|
||||
{
|
||||
return btstype == convert_security_type(fixtype);
|
||||
}
|
||||
|
||||
FIX::SecurityType SessionBase::convert_security_type(bts::api::SecurityType btstype)
|
||||
{
|
||||
switch (btstype)
|
||||
{
|
||||
case bts::api::SecurityType::SECURITYTYPE_FUTURE:
|
||||
return FIX::SecurityType("FUT");
|
||||
case bts::api::SecurityType::SECURITYTYPE_OPTION:
|
||||
return FIX::SecurityType("OPT");
|
||||
case bts::api::SecurityType::SECURITYTYPE_SPREAD:
|
||||
return FIX::SecurityType("MLEG");
|
||||
case bts::api::SecurityType::SECURITYTYPE_STOCK:
|
||||
return FIX::SecurityType("CS");
|
||||
case bts::api::SecurityType::SECURITYTYPE_SYNTHETIC:
|
||||
return FIX::SecurityType("SYNTH");
|
||||
default:
|
||||
return FIX::SecurityType();
|
||||
}
|
||||
}
|
||||
|
||||
bts::api::SecurityType SessionBase::convert_security_type(FIX::SecurityType fixtype)
|
||||
{
|
||||
std::string s = std::string(fixtype.getValue());
|
||||
if(s == "FUT")
|
||||
return bts::api::SecurityType::SECURITYTYPE_FUTURE;
|
||||
else if(s == "OPT")
|
||||
return bts::api::SecurityType::SECURITYTYPE_OPTION;
|
||||
else if(s == "MLEG")
|
||||
return bts::api::SecurityType::SECURITYTYPE_SPREAD;
|
||||
else if(s == "CS")
|
||||
return bts::api::SecurityType::SECURITYTYPE_STOCK;
|
||||
else if("SYNTH" == s)
|
||||
return bts::api::SecurityType::SECURITYTYPE_SYNTHETIC;
|
||||
else
|
||||
return bts::api::SecurityType::SECURITYTYPE_UNSET;
|
||||
}
|
||||
|
||||
bool SessionBase::exchanges_equal(bts::api::Venue btstype, FIX::SecurityExchange fixtype)
|
||||
{
|
||||
return btstype == convert_exchange(fixtype);
|
||||
}
|
||||
|
||||
FIX::SecurityExchange SessionBase::convert_exchange(bts::api::Venue btstype)
|
||||
{
|
||||
switch (btstype)
|
||||
{
|
||||
case bts::api::Venue::VENUE_BTS:
|
||||
return FIX::SecurityExchange("BTS");
|
||||
case bts::api::Venue::VENUE_CBOE:
|
||||
return FIX::SecurityExchange("XCBO");
|
||||
case bts::api::Venue::VENUE_CBOECFE:
|
||||
return FIX::SecurityExchange("XCBF");
|
||||
case bts::api::Venue::VENUE_GLBX:
|
||||
return FIX::SecurityExchange("XCME");
|
||||
case bts::api::Venue::VENUE_ICE:
|
||||
return FIX::SecurityExchange("IFUS");
|
||||
case bts::api::Venue::VENUE_SIM:
|
||||
return FIX::SecurityExchange("SIM");
|
||||
default:
|
||||
return FIX::SecurityExchange();
|
||||
}
|
||||
}
|
||||
|
||||
bts::api::Venue SessionBase::convert_exchange(FIX::SecurityExchange fixtype)
|
||||
{
|
||||
std::string s = std::string(fixtype.getValue());
|
||||
if("BTS" == s)
|
||||
return bts::api::Venue::VENUE_BTS;
|
||||
else if("XCBO" == s)
|
||||
return bts::api::Venue::VENUE_CBOE;
|
||||
else if("XCBF" == s)
|
||||
return bts::api::Venue::VENUE_CBOECFE;
|
||||
else if("GLBX" == s || "XCME" == s || "XCBT" == s || "XCEC" == s || "XNYM" == s)
|
||||
return bts::api::Venue::VENUE_GLBX;
|
||||
else if("IEPA" == s || "IFUS" == s || "IFEU" == s || "IFLL" == s || "IFCA" == s)
|
||||
return bts::api::Venue::VENUE_ICE;
|
||||
else if("SIM" == s)
|
||||
return bts::api::Venue::VENUE_SIM;
|
||||
else
|
||||
return bts::api::Venue::VENUE_UNSET;
|
||||
}
|
||||
|
||||
FIX::OrdStatus SessionBase::translate_status(const bts::api::orderman::OrderState& state,
|
||||
bts::api::orderman::NativeOrderState* native)
|
||||
{
|
||||
native->Clear();
|
||||
if (state.has_market() && state.market().has_native_state() && state.market().native_state().native_status() != bts::api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_UNACKNOWLEDGED)
|
||||
{
|
||||
*native = state.market().native_state();
|
||||
return FIX::OrdStatus((char)native->native_status());
|
||||
}
|
||||
else if (state.has_limit() && state.limit().has_native_state() && state.limit().native_state().native_status() != bts::api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_UNACKNOWLEDGED)
|
||||
{
|
||||
*native = state.limit().native_state();
|
||||
return FIX::OrdStatus((char)native->native_status());
|
||||
}
|
||||
else if (state.has_stop() && state.stop().has_native_state() && state.stop().native_state().native_status() != bts::api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_UNACKNOWLEDGED)
|
||||
{
|
||||
*native = state.stop().native_state();
|
||||
return FIX::OrdStatus((char)native->native_status());
|
||||
}
|
||||
else if (state.has_stop_limit() && state.stop_limit().has_native_state() && state.stop_limit().native_state().native_status() != bts::api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_UNACKNOWLEDGED)
|
||||
{
|
||||
*native = state.stop_limit().native_state();
|
||||
return FIX::OrdStatus((char)native->native_status());
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (state.status())
|
||||
{
|
||||
case bts::api::orderman::OrderStatus::ORDER_STATUS_CANCELED:
|
||||
return FIX::OrdStatus(FIX::OrdStatus_CANCELED);
|
||||
case bts::api::orderman::OrderStatus::ORDER_STATUS_COMPLETED:
|
||||
return FIX::OrdStatus(FIX::OrdStatus_FILLED);
|
||||
case bts::api::orderman::OrderStatus::ORDER_STATUS_DISCONNECTED:
|
||||
return FIX::OrdStatus(FIX::OrdStatus_STOPPED);
|
||||
case bts::api::orderman::OrderStatus::ORDER_STATUS_ERROR:
|
||||
return FIX::OrdStatus(FIX::OrdStatus_REJECTED);
|
||||
case bts::api::orderman::OrderStatus::ORDER_STATUS_INACTIVE:
|
||||
return FIX::OrdStatus(FIX::OrdStatus_PENDING_NEW);
|
||||
case bts::api::orderman::OrderStatus::ORDER_STATUS_PENDING_WORKING:
|
||||
return FIX::OrdStatus(FIX::OrdStatus_PENDING_NEW);
|
||||
case bts::api::orderman::OrderStatus::ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
return FIX::OrdStatus(FIX::OrdStatus_REJECTED);
|
||||
case bts::api::orderman::OrderStatus::ORDER_STATUS_WORKING:
|
||||
{
|
||||
if (state.filled_quantity() < state.quantity())
|
||||
return FIX::OrdStatus(FIX::OrdStatus_PARTIALLY_FILLED);
|
||||
else
|
||||
return FIX::OrdStatus(FIX::OrdStatus_NEW);
|
||||
}
|
||||
default:
|
||||
return FIX::OrdStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint SessionBase::lookup_account(const std::string& acct)
|
||||
{
|
||||
bts::api::admin::BTSAccount* a = nullptr;
|
||||
_admin_data.lock( [&] (auto& ad)
|
||||
{
|
||||
a = admin::AdminServiceUtils::find_account(ad, acct);
|
||||
});
|
||||
if(a)
|
||||
return a->id();
|
||||
else
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::Account);
|
||||
}
|
||||
|
||||
uint SessionBase::lookup_user(const std::string& user)
|
||||
{
|
||||
bts::api::admin::BTSUser* u = nullptr;
|
||||
_admin_data.lock( [&] (auto& ad)
|
||||
{
|
||||
u = admin::AdminServiceUtils::find_user(ad, user);
|
||||
});
|
||||
if(u)
|
||||
return u->id();
|
||||
else
|
||||
throw FIX::IncorrectTagValue(FIX::FIELD::Username);
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include "IFixSession.h"
|
||||
#include "AdminModelWrapper.h"
|
||||
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/api/services/AuthService.pb.h"
|
||||
#include "lib/api/services/AuthService.client.pb.h"
|
||||
#include "lib/api/services/AdminService.pb.h"
|
||||
#include "lib/api/services/AdminService.client.pb.h"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/api/services/OrderService.client.pb.h"
|
||||
#include "lib/domain_model/Security.pb.h"
|
||||
#include "lib/utils/LockBox.h"
|
||||
|
||||
#include <quickfix/MessageCracker.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
namespace FIX
|
||||
{
|
||||
class Message;
|
||||
class SessionID;
|
||||
}
|
||||
|
||||
namespace FIX44
|
||||
{
|
||||
class Heartbeat;
|
||||
class BusinessMessageReject;
|
||||
}
|
||||
|
||||
namespace bts {
|
||||
namespace fix {
|
||||
|
||||
class IFixRouter;
|
||||
|
||||
class SessionBase : public IFixSession, public FIX::MessageCracker
|
||||
{
|
||||
public:
|
||||
SessionBase(bts::api::ServiceContext&, IFixRouter&, const FIX::SessionID&);
|
||||
virtual ~SessionBase();
|
||||
|
||||
void fromAdmin(const FIX::Message& message, const FIX::SessionID& sessionID)
|
||||
throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon);
|
||||
void toAdmin(FIX::Message&, const FIX::SessionID&);
|
||||
void toApp(FIX::Message&, const FIX::SessionID&) throw(FIX::DoNotSend);
|
||||
|
||||
void onCreate(const FIX::SessionID& s);
|
||||
void onMessage(const FIX44::BusinessMessageReject& n, const FIX::SessionID& s);
|
||||
|
||||
protected:
|
||||
void handle_heartbeat(const FIX44::Heartbeat& hb, const FIX::SessionID& sessionID);
|
||||
void send_cancel_reject(const FIX::SessionID& sess, const std::string& text, const FIX::Message& refmessage);
|
||||
|
||||
bool security_types_equal(bts::api::SecurityType btstype, FIX::SecurityType fixtype);
|
||||
FIX::SecurityType convert_security_type(bts::api::SecurityType btstype);
|
||||
bts::api::SecurityType convert_security_type(FIX::SecurityType fixtype);
|
||||
|
||||
bool exchanges_equal(bts::api::Venue btstype, FIX::SecurityExchange fixtype);
|
||||
FIX::SecurityExchange convert_exchange(bts::api::Venue btstype);
|
||||
bts::api::Venue convert_exchange(FIX::SecurityExchange fixtype);
|
||||
|
||||
FIX::OrdStatus translate_status(const bts::api::orderman::OrderState& state,
|
||||
bts::api::orderman::NativeOrderState* native);
|
||||
|
||||
uint lookup_account(const std::string& acct);
|
||||
uint lookup_user(const std::string& user);
|
||||
|
||||
protected:
|
||||
bts::api::user::AuthService _authservice;
|
||||
bts::api::admin::AdminService _adminservice;
|
||||
rxcpp::observable<std::shared_ptr<AdminModelWrapper>> _adminobservable;
|
||||
IFixRouter& _router;
|
||||
FIX::SessionID _sess;
|
||||
|
||||
utils::LockBox<bts::api::admin::AdminData> _admin_data;
|
||||
std::string _user_name;
|
||||
bts::api::AuthToken _token;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "server/sparkd/cmds/QuickTest.h"
|
||||
#include "server/sparkd/cmds/Run.h"
|
||||
#include "server/sparkd/cmds/RunSim.h"
|
||||
#include "server/sparkd/cmds/RunFix.h"
|
||||
#include "server/sparkd/cmds/Span.h"
|
||||
#include "server/sparkd/cmds/Test.h"
|
||||
#include "server/sparkd/cmds/TestCeloxica.h"
|
||||
@@ -35,6 +36,7 @@ int main(int argc, char **argv) {
|
||||
REGISTER(ConfigCheck);
|
||||
REGISTER(KeyCode);
|
||||
REGISTER(RunSim);
|
||||
REGISTER(RunFix);
|
||||
REGISTER(Benchmark);
|
||||
REGISTER(QuickTest);
|
||||
REGISTER(AutoCert);
|
||||
|
||||
Reference in new issue
Block a user