RemoteOrderMon sample demonstrates how to use FIXRemoteReplicatorEngine instance to remotely synchronize entire (or part of) FIX message communication from a running production process and reconstruct the business logic in the remote process.
In this sample, the application outputs some simple statistics (count of orders per session) once the remote replicator application "catches up with" the remote production system.
Note this application can be used to do remote end of day statistics because we know after market is closed there should be no communication of application FIX messages (only administrative messages).
RemoteOrderMon takes a series of string type of command line arguments, as described below.
RemoteOrderMon RepositoryUri RepositoryUserName RepositoryPassword LogDir EnginePersistenceName1 address1 [EnginePersistenceName2 address2 ...]
Note RemoteOrderMon can monitor more than one processes. Below is the command line arguments that manages two processes of the RandomOrderMan sample (client and server). Both processes are running on the local machine.
qwrp://localhost:60000 administrator "" c:\temp\RTAnalyzer Tutorial_OrderMan_Client localhost Tutorial_OrderMan_Server localhost
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Diagnostics; using Teraspaces.QWFramework.IO; using Teraspaces.QWFramework.Logging; using Teraspaces.QWFIX; using Teraspaces.QWFIX.Schema; using Teraspaces.QWFIX.Application; using Teraspaces.QWFIX.Application.Repository; using Teraspaces.QWFIX.Scheduler; using Teraspaces.QWFIX.OrderManager; using Teraspaces.QWFIX.Utils; namespace RemoteOrderMon { class Program { static void PrintUsage() { Console.Error.WriteLine("Usage:"); Console.Error.WriteLine("RemoteOrderMon RepositoryUri RepositoryUserName RepositoryPassword LogDir EnginePersistenceName1 address1 [EnginePersistenceName2 address2 ...]"); } static void Main(string[] args) { // Initially "ApplicationLog.Default" is a "ConsoleApplicationLog". // We change it to "DummyApplicationLog" so that the output screen won't be messed up. //ApplicationLog.Default = new DummyApplicationLog(); // We allocate 500MB for FIX message cache. Modifying this value may affect the performance. FIXEngine.InitMessageCache(FIXEngineMessageCacheType.ManageCacheByMemoryUsage, 500); // Create repository FIXRemoteRepository repository = new FIXRemoteRepository( new SecureUri(args[0], args[1], args[2])); // Create Application FIXRemoteReplicatorApplication app = new FIXRemoteReplicatorApplication(repository); string logDir = args[3]; FIXRemoteReplicatorEngine[] remoteEngines = new FIXRemoteReplicatorEngine[(args.Length - 4) / 2]; int curEngine = 0; int curArg = 4; RemoteOrderMonCallBack callBack = new RemoteOrderMonCallBack(app); while (curArg < args.Length) { remoteEngines[curEngine] = new FIXRemoteReplicatorEngine( args[curArg], logDir, args[curArg + 1], callBack); curEngine++; curArg += 2; } app.RegisterFIXEngines(remoteEngines); app.Run(false); Thread.Sleep(Timeout.Infinite); } class RemoteOrderMonCallBack : IFIXRemoteReplicatorEngineCallBack { public RemoteOrderMonCallBack(FIXRemoteReplicatorApplication application) { this.application = application; } public void EngineInitialized(FIXEngine engine, int remoteEngineMessageCount) { FIXOrderManager.AddFIXEngine(engine, application.VisualLambdaLoader); foreach (FIXSession curSession in engine.Sessions) { FIXSessionOrderManager curSessionOM = FIXOrderManager.GetSessionOrderManager(curSession); if (curSessionOM != null) // Order management is supported on this session curSessionOM.QueryRestatedGTOrder += new QueryRestatedGTOrderHandler(SessionOM_QueryRestatedGTOrder); } } public void EngineResumed(FIXEngine engine, int remoteEngineMessageCount) { if (engine.MessageLog.Count == remoteEngineMessageCount) OnEngineSyncUpToDate(engine); } public void EngineStopped(FIXEngine engine) { } public void PreProcessMessage(FIXEngine engine, FIXMessage message) { } public void PostProcessMessage(FIXEngine engine, FIXMessage message, int localEngineMessageIndex, int remoteEngineMessageCount) { if (engine.MessageLog.Count == remoteEngineMessageCount) OnEngineSyncUpToDate(engine); } public void MessageCountUpdated(FIXEngine engine, int remoteEngineMessageCount) { if (engine.MessageLog.Count == remoteEngineMessageCount) OnEngineSyncUpToDate(engine); } public FIXRemoteReplicatorSessionSettings[] GetSyncSettings(FIXEngine engine) { FIXRemoteReplicatorSessionSettings[] ret = new FIXRemoteReplicatorSessionSettings[engine.Sessions.Count]; for (int i = 0; i < engine.Sessions.Count; i++) { FIXSession curSession = engine.Sessions[i]; ret[i] = new FIXRemoteReplicatorSessionSettings( curSession.Settings.Name, curSession.EndPoint.Direction, true, null); } return ret; } public void NewPersistenceCreated(FIXEngine engine) { } public void OutgoingMessageDelivered(FIXEngine engine, FIXSessionPersistence persistence, FIXMessageToken messageToken, int actualMsgSeqNum) { } void OnEngineSyncUpToDate(FIXEngine engine) { // Note this method gets called each time the the remote synchronizer "catches up" with // the remote process running in production mode (temporarily there is no new message // communication in remote process). // Usually at the end of day there will be no new FIX application message. Starting the // remote synchronization process at the end of day and when this method is called we // can assume the entire intraday order flow is synchronized. Console.WriteLine(string.Format("Engine <{0}> is up to date.", engine.EngineName)); foreach (FIXSession curSession in engine.Sessions) { FIXSessionOrderManager curSessionOM = FIXOrderManager.GetSessionOrderManager(curSession); if (curSessionOM != null) { foreach (FIXSessionOrderList curOrderList in curSessionOM.SessionOrderLists) { Console.Out.WriteLine(string.Format("Persistence={0} RegularOrderCount={1} ListOrderCount={2} CrossOrderCount={3}", curOrderList.SessionPersistence.PersistenceName, curOrderList.RegularOrders.Count, curOrderList.ListOrders.Count, curOrderList.CrossOrders.Count)); } } } } static bool SessionOM_QueryRestatedGTOrder(FIXMessage executionMessage, FIXEngineMessageHandlerStatus status, out FIXOrderType fixOrderType) { // Note in this demo we assume every execution with unknown ClOrdID is a restated GT order. FIXVersion fixVersion = executionMessage.MessageComponentSchema.Schema.FIXVersion; bool ret = false; if (fixVersion.Major == 4 && fixVersion.Minor <= 1) ret = true; else ret = (executionMessage.GetCharValue(FIXConstants.Tags.ExecType) == FIXConstants.ExecTypes.Restated); fixOrderType = FIXOrderType.Single; if (ret) { if (executionMessage.ContainsTag(FIXConstants.Tags.CrossID)) fixOrderType = FIXOrderType.CrossPopulated; else if (executionMessage.ContainsTag(FIXConstants.Tags.ListID)) fixOrderType = FIXOrderType.ListPopulated; } return true; } private FIXRemoteReplicatorApplication application; } } }