Compare commits
95
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a397d7b18e | ||
|
|
9e1d6a2fe7 | ||
|
|
8722b70518 | ||
|
|
80d7e34bf1 | ||
|
|
bca760cec9 | ||
|
|
5f1e58df68 | ||
|
|
c1df312f88 | ||
|
|
8473311a52 | ||
|
|
0a83143cf3 | ||
|
|
8ab69c919b | ||
|
|
99531bcfa0 | ||
|
|
6bd516d4f2 | ||
|
|
1a1d771e1b | ||
|
|
13b15f257b | ||
|
|
109de67a06 | ||
|
|
050fcf7c6e | ||
|
|
e7b6bf40d9 | ||
|
|
3193bc6467 | ||
|
|
f534ef3e39 | ||
|
|
bbd9693718 | ||
|
|
aadeae3201 | ||
|
|
b72e8aed02 | ||
|
|
0820cc3e9a | ||
|
|
b1ba9a1315 | ||
|
|
39ce4323a1 | ||
|
|
d9fcd290d9 | ||
|
|
05939b02c5 | ||
|
|
aa91638fe4 | ||
|
|
065fc54f73 | ||
|
|
95fb13f58b | ||
|
|
4eb2931cb8 | ||
|
|
5326015a48 | ||
|
|
fcbe319470 | ||
|
|
5ad21c88b2 | ||
|
|
3a9c1d557b | ||
|
|
6fb5b83249 | ||
|
|
95830fafdf | ||
|
|
d68b03bb91 | ||
|
|
e7062ce01e | ||
|
|
932738f98e | ||
|
|
1426b00e83 | ||
|
|
e724409b58 | ||
|
|
9cf828e9a4 | ||
|
|
41408fb609 | ||
|
|
4c21e32838 | ||
|
|
385f3ea402 | ||
|
|
8d6d8cab18 | ||
|
|
01615f824a | ||
|
|
c6779ddc90 | ||
|
|
46eff65435 | ||
|
|
cb0dfa50b3 | ||
|
|
2429e6019c | ||
|
|
e0bb48b0c7 | ||
|
|
a38b0dbc79 | ||
|
|
73065ab48a | ||
|
|
bd07c96504 | ||
|
|
dc4033875a | ||
|
|
130794c538 | ||
|
|
b1743f0b1c | ||
|
|
33492c9131 | ||
|
|
d434778a82 | ||
|
|
4c6cc9b3e0 | ||
|
|
6aaeddb7e9 | ||
|
|
1685a13994 | ||
|
|
54633dd6db | ||
|
|
510f163e5b | ||
|
|
ee4779da73 | ||
|
|
28e98259f6 | ||
|
|
82bc402216 | ||
|
|
ff70e9694b | ||
|
|
525d0eda30 | ||
|
|
e9e85b44c9 | ||
|
|
2654b2e446 | ||
|
|
be45938b51 | ||
|
|
663f9ecc52 | ||
|
|
a6edbbe7b5 | ||
|
|
51276d161c | ||
|
|
3fa699b4c9 | ||
|
|
c9396bd6af | ||
|
|
b80f96fa5e | ||
|
|
b7dea6817c | ||
|
|
f4dc550f1a | ||
|
|
bc978ed065 | ||
|
|
202c5faffd | ||
|
|
65d55a7618 | ||
|
|
57c3ec34c2 | ||
|
|
c6cd5fcf93 | ||
|
|
177b96b453 | ||
|
|
292f66a774 | ||
|
|
8c837f9f64 | ||
|
|
85f97ae552 | ||
|
|
7ca0d10eb0 | ||
|
|
ca8aed1f2b | ||
|
|
b32d63fcda | ||
|
|
54d9d5e2f3 |
No files matched your search
@@ -34,3 +34,4 @@ trace_data/
|
||||
/doc/user/public/*
|
||||
.ionide/*
|
||||
/.ignore
|
||||
/build
|
||||
Vendored
+8
-3
@@ -5,7 +5,9 @@
|
||||
"includePath": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
"${workspaceRoot}",
|
||||
"${workspaceFolder}/src",
|
||||
"${workspaceRoot}/externals/linux/include"
|
||||
],
|
||||
"defines": [],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
@@ -13,13 +15,16 @@
|
||||
"path": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
"${workspaceRoot}",
|
||||
"${workspaceFolder}/src",
|
||||
"${workspaceRoot}/externals/linux/include"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
"cppStandard": "c++17",
|
||||
"compileCommands": "${workspaceFolder}/build/ninja_debug/compile_commands.json"
|
||||
},
|
||||
{
|
||||
"name": "Windows",
|
||||
|
||||
Vendored
+30
-2
@@ -1,13 +1,40 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Spark Local",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/ninja_debug/server/sparkd",
|
||||
"args": [
|
||||
// "cmecert",
|
||||
"test",
|
||||
"-a",
|
||||
"[ilink3]"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceRoot}/build/ninja_debug/server/",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SparkVM Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": [
|
||||
"run"
|
||||
"run"
|
||||
//"test",
|
||||
//"-a",
|
||||
//"[ilink3]"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/server/",
|
||||
@@ -44,7 +71,8 @@
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
"/home/bts/spark/": "${workspaceRoot}",
|
||||
"/home/bts/spark_host/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
Vendored
+19
-10
@@ -14,24 +14,21 @@
|
||||
"focus": false,
|
||||
"panel": "shared"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "server_build",
|
||||
"label": "server_build",
|
||||
"type": "shell",
|
||||
"command": ".\\tools\\buildsparkd.cmd",
|
||||
"problemMatcher": {
|
||||
"base": "$gcc",
|
||||
"problemMatcher": {
|
||||
"base": "$gcc",
|
||||
"owner": "cpp",
|
||||
"fileLocation": [
|
||||
"relative",
|
||||
"${workspaceFolder}"
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"pattern": {
|
||||
"regexp": "(.?)(home)(.?)(bts)(.?)(spark)(.?)(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)",
|
||||
"regexp": "(.?)(home)(.?)(bts)(.?)(spark)(.?)(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)",
|
||||
"file": 8,
|
||||
"line": 9,
|
||||
"column": 10,
|
||||
@@ -49,6 +46,18 @@
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "linux build (Debug)",
|
||||
"type": "shell",
|
||||
"command": "cd build/ninja_debug; time ninja",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
+++
|
||||
title="Client API"
|
||||
template="client.html"
|
||||
redirect_to = "client/getting-started/overview/"
|
||||
sort_by="weight"
|
||||
+++
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
+++
|
||||
title="Getting Started"
|
||||
template="client.html"
|
||||
sort_by="weight"
|
||||
weight = 1
|
||||
+++
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
+++
|
||||
title = "Install Prerequisites"
|
||||
template="client.html"
|
||||
weight = 1
|
||||
weight = 10
|
||||
+++
|
||||
|
||||
## Coming Soon
|
||||
# C#/.NET
|
||||
|
||||
We target [.NET Standard 2.0](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) so you can use .NET Framework 4.6.1+ or .NET Core 2.0+. Simply add a reference to the [Spark Nuget package](https://nuget.org) to get started.
|
||||
|
||||
```
|
||||
dotnet new console -lang c#
|
||||
dotnet add package spark_sdk
|
||||
dotnet build
|
||||
```
|
||||
|
||||
# C++
|
||||
|
||||
The easiest way to get started with C++ is to use the Spark Server SDK to develop your application. Follow the instructions to [download and install the Spark Server SDK](/server/getting-started/install) and open the client_sdk sample project. If you want to run your C++ Client SDK application on Windows [contact us](mailto://api@bluetradesys.com) to get the required headers and binaries.
|
||||
|
||||
# JavaScript
|
||||
|
||||
We use JavaScript internally with the Spark Client API over a websocket connection but the documentation and packaging isn't ready yet. If this is something you're interested in get in touch with us at [api@bluetradesys.com](mailto:api@bluetradesys.com).
|
||||
|
||||
|
||||
|
||||
* Install .net core
|
||||
@@ -0,0 +1,31 @@
|
||||
+++
|
||||
title = "Overview"
|
||||
weight = 1
|
||||
+++
|
||||
|
||||
The Spark Client SDK is a high level command and control API which can be used to build scripts or integrate Spark into your existing ecosystem. The API is divided into several independent **"services"** responsible for different aspects of the system. A Service can define both [remote procedure call (RPC)](/client/getting-started/services#rpc) and incremental [streaming](/client/getting-started/services#streams) data.
|
||||
|
||||
The [Spark Desktop](https://www.bluetradingsystems.com/bts-spark/) is built on top of this API so you can be assured that it is complete and well tested.
|
||||
|
||||
Spark uses [Google Protocol Buffers](https://developers.google.com/protocol-buffers/) as an interface definition language similar to [GRPC](https://grpc.io) but backed by our custom high-performance TCP protocol. We currently support C#/.NET and C++ <!--and Javascript--> but the architecture is designed for easy extension so contact us about your specific business needs.
|
||||
|
||||
## Orders
|
||||
|
||||
Use the [OrderService](/client/services/orderservice) to send and manage orders including native exchange orders, spreader orders, and custom Algo orders created using the [Spark Server SDK](https://api.bluetradesys.com/server/).
|
||||
|
||||
## Spreader
|
||||
|
||||
Use the [SyntheticSecurityService](/client/services/syntheticsecurityservice) to create and manage synthetic spreads. Once created these spreads show up in the security and market data feeds just like exchange listed spreads.
|
||||
|
||||
## Position
|
||||
|
||||
Use the [PositionService](/client/services/positionservice) to get your position and P&L reports or add fills manually to keep the Spark system up to date with an external system.
|
||||
|
||||
## Administration
|
||||
|
||||
Use the [AdminService](/client/services/adminservice) to manage users and control limits.
|
||||
|
||||
## Market Data
|
||||
|
||||
For .NET subscribe to market data though the high level MarketDataClient interface. From C++ or JavaScript use the MarketDataRelay service directly.
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
+++
|
||||
title = "Services"
|
||||
weight = 20
|
||||
+++
|
||||
|
||||
The Spark Client SDK uses [Google Protocol Buffers](https://developers.google.com/protocol-buffers/) to define the interface for each service in a language independent manner. Services have both functions which return a single result and streams of data which return values continuously while subscribed. This page describes the mapping from the Protocol Buffer definition to the concrete definition in each supported language.
|
||||
|
||||
# ServiceContext
|
||||
|
||||
To connect your client to the Spark Server you need to get or create a `ServiceContext` instance. A `ServiceContext` represents a connection to the Spark Server and a background thread for handling messages on that connection. Generally there should be a single `ServiceContext` per process. All of the methods on the `ServiceContext` are safe thread safe.
|
||||
|
||||
Construct a ServiceContext by calling the default constructor. The ServiceContext will find the Spark Server by loading the server configuration over an HTTP connection. To determine the URL it will search the following locations and use the first one that is present.
|
||||
|
||||
* A command line argument `-configserver <url>`
|
||||
* An environment variable `BTSCONFIG`
|
||||
* `%APPDATA%/bts/conf/spark.conf` (Windows)
|
||||
* /sparkdata/conf/spark.conf (Linux)
|
||||
* $HOME/sparkdata/conf/spark.conf (Linux)
|
||||
|
||||
If your application is running on a system with the BTS Client installed this will probably work without any further configuration. If you plan to run your client application on a separate machine we recommend setting the `BTSCONFIG` environment variable.
|
||||
|
||||
<div class="info">
|
||||
<i class="fa fa-lightbulb-o fa-fw" aria-hidden="true"></i>
|
||||
If you are using the Spark Server SDK to run a local copy of the Spark server set <p><strong>BTSCONFIG = http://localhost:9000/config/</strong></p>
|
||||
</div>
|
||||
|
||||
To get a proxy for a given service call ServiceContext.locate<T>("qualifier") where T is the type of the service and "qualifier" is an optional qualifier used to disambiguate between 2 instances of the same service. The locate call cannot fail but it may return a disconnected proxy. When you make a call on the proxy you should check the call result to see if the method call succeeded.
|
||||
|
||||
# RPC
|
||||
|
||||
Here is the OrderService reduced to a single method definition. A RPC method always takes exactly 1 argument and returns 1 value. In this example the `create_order` call takes a `Create` instance and returns a `Response`.
|
||||
|
||||
```C
|
||||
service OrderService {
|
||||
rpc create_order(Create) returns (Response);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The `Create` and `Response` types are also defined as protocol buffer messages. Each field can either be a primitive type another message or a list or map.
|
||||
|
||||
```C
|
||||
message Create {
|
||||
OrderDefinition new_order = 1;
|
||||
double price = 2;
|
||||
uint32 quantity = 3;
|
||||
}
|
||||
|
||||
message Response {
|
||||
uint32 order_key = 1;
|
||||
OrderError error_code = 2;
|
||||
string msg = 3;
|
||||
}
|
||||
```
|
||||
|
||||
Each RPC call generates both synchronous (blocking) and asynchronous variants. In C# the asynchronous calls have the `_async` suffix and return a `Task`. In C++ the call is overloaded to either return a value synchronously or accept a callback function to be called on the ServiceContext thread when the call completes. <!-- In Javascript only async methods returning a Promise are generated because it never makes sense to make a blocking call in the browser. -->
|
||||
|
||||
To call the resulting service locate a proxy using the [ServiceContext](#servicecontext) and then call `cancel_order` or `cancel_order_async`. If you are calling the Spark API from a GUI application you should always prefer the `_async` version of the calls.
|
||||
|
||||
|
||||
### C# example
|
||||
```c#
|
||||
var ctx = new ServiceContext();
|
||||
var orders = ctx.locate<OrderService>();
|
||||
var response =
|
||||
await orders.create_order_async(
|
||||
new Create { price = 2.25, quantity = 42 });
|
||||
if (response.success) {
|
||||
Console.WriteLine($"success: {response.value}");
|
||||
}
|
||||
else {
|
||||
Console.WriteLine($"Error: {response.error_message}");
|
||||
}
|
||||
```
|
||||
### C++ example
|
||||
```cpp
|
||||
auto ctx = ServiceContext{};
|
||||
auto orders = ctx.locate<OrderService>();
|
||||
Create c;
|
||||
c.set_price(2.25);
|
||||
c.set_quantity(42);
|
||||
// Instead of providing an _async method C++ uses overloading.
|
||||
// To make a syncronous call simply omit the callback
|
||||
orders.create_order(c, [=](const Result<Response>& response) {
|
||||
if(response.success()) {
|
||||
std::cout << "success.";
|
||||
}
|
||||
else {
|
||||
std::cout << "error: " << response.error_message();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
# Streams
|
||||
|
||||
Streams allow you to subscribe to live updating data. Each stream represents a changing collection of protocol buffer objects. Similar to a database row each item has an `id` which uniquely identifies it within the collection. Updates from the Spark server can add, remove or replace entries in the collection. Since only the changed items are sent over the wire the stream can remain responsive even when it represents a large collection.
|
||||
|
||||
Streams are implemented using the language bindings for [ReativeX](http://reactivex.io/) which provide utility functions to drill down to the specific data your application needs. In C# we provide both mutable and immutable versions of the generated data types (C++ users can rely on `const`).
|
||||
|
||||
When defined in a proto file streams use the `stream` keyword on the result type.
|
||||
```C
|
||||
service OrderService {
|
||||
...
|
||||
rpc fills (FillsSub) returns (stream Fill);
|
||||
}
|
||||
```
|
||||
|
||||
### C# example
|
||||
```c#
|
||||
var sc = new ServiceContext();
|
||||
var orders = sc.locate<OrderService>();
|
||||
var fills_observable = orders.subscribei_fills();
|
||||
var unsub = fills_observable.Subscribe(update => {
|
||||
Console.WriteLine(
|
||||
"Added {0} items, Removed {1} items, Updated {2} items. Total items {3}",
|
||||
update.added.Count(),
|
||||
update.removed.Count(),
|
||||
update.changes.Count(),
|
||||
update.all.Count());
|
||||
});
|
||||
|
||||
Thread.Sleep(TimeSpan.FromMinutes(5));
|
||||
unsub.Dispose();
|
||||
```
|
||||
### C++ Example
|
||||
```cpp
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
+++
|
||||
title="Samples"
|
||||
sort_by="weight"
|
||||
weight = 30
|
||||
+++
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
+++
|
||||
title = "AdminService"
|
||||
+++
|
||||
@@ -0,0 +1,3 @@
|
||||
+++
|
||||
title = "OrderService"
|
||||
+++
|
||||
@@ -0,0 +1,3 @@
|
||||
+++
|
||||
title = "PositionService"
|
||||
+++
|
||||
@@ -0,0 +1,3 @@
|
||||
+++
|
||||
title = "SyntheticService"
|
||||
+++
|
||||
@@ -0,0 +1,6 @@
|
||||
+++
|
||||
title="Services"
|
||||
#sort_by="weight"
|
||||
weight = 10
|
||||
+++
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
+++
|
||||
title = "Market Data"
|
||||
+++
|
||||
@@ -1,6 +1,5 @@
|
||||
+++
|
||||
title="Algo API"
|
||||
template="documentation.html"
|
||||
redirect_to = "server/getting-started/overview/"
|
||||
sort_by="weight"
|
||||
+++
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
+++
|
||||
title="Getting Started"
|
||||
template="documentation.html"
|
||||
sort_by="weight"
|
||||
weight = 1
|
||||
+++
|
||||
|
||||
@@ -71,12 +71,12 @@ The first step is to start the spark_sdk Docker container with the examples dire
|
||||
|
||||
Windows Command Prompt:
|
||||
{% clipable() %}
|
||||
docker run --rm -it -v %CD%:/examples -u bts -w /examples --shm-size 1024m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
|
||||
docker run --rm -it -v %CD%:/examples -u bts -w /examples --shm-size 2048m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
|
||||
{% end %}
|
||||
|
||||
**Powershell** / Linux / Mac:
|
||||
{% clipable() %}
|
||||
docker run --rm -it -v ${PWD}:/examples -u bts -w /examples --shm-size 1024m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
|
||||
docker run --rm -it -v ${PWD}:/examples -u bts -w /examples --shm-size 2048m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
|
||||
{% end %}
|
||||
|
||||
### Attach
|
||||
|
||||
@@ -5,7 +5,7 @@ weight = 2
|
||||
|
||||
## Request Access 🔑
|
||||
|
||||
To start using the Server SDK you will need to request access though our sales department. Please email <a href="mailto:info@bluetradesys.com">info@bluetradesys.com</a> or call <a href="tel:919-913-0850">(919) 913-0850</a> and we will provide download credentials.
|
||||
To start using the Server SDK you will need to request access though our sales department. Please email <a href="mailto:api@bluetradesys.com">api@bluetradesys.com</a> or call <a href="tel:919-913-0850">(919) 913-0850</a> and we will provide download credentials.
|
||||
|
||||
## Download
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
+++
|
||||
title="Reference"
|
||||
template="documentation.html"
|
||||
sort_by="weight"
|
||||
weight = 2
|
||||
+++
|
||||
|
||||
@@ -6,3 +6,9 @@ weight = 40
|
||||
## I need to make a call that might take a few seconds. What is the best way to do this?
|
||||
|
||||
Create a background thread in [do_before_launch](/server/ref/initialization#before-launch) or [do_launch](/server/ref/initialization#launch-launch-attached) to do the work. Use [on_timer](/server/ref/subscriptions#timer) to poll until the result is available.
|
||||
|
||||
## I don't see the header files in the .zip file. Where are they?
|
||||
|
||||
The SDK headers are baked into the docker image along with the build tools. Once you've loaded the image with `docker load < spark_sdk.img` you can run this command to copy them out to your local file system `docker run --rm -it -v $PWD:/out spark_sdk /bin/tar -cvf /out/headers.tar /sdk/include` This will copy the headers to the current directory in headers.tar. We still recommend doing the actual build inside the docker environment but having a copy of the headers can be useful for setting up your development environment.
|
||||
|
||||
|
||||
File renamed without changes.
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
@@ -1,6 +1,5 @@
|
||||
+++
|
||||
title="Samples"
|
||||
template="documentation.html"
|
||||
sort_by="weight"
|
||||
weight = 3
|
||||
+++
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
{% extends "index.html" %}
|
||||
|
||||
{% block extra_nav_class %}{% endblock extra_nav_class %}
|
||||
{% block extra_content_class %}content--reversed{% endblock extra_content_class %}
|
||||
|
||||
{% block content %}
|
||||
{% set section = get_section(path="client/_index.md") %}
|
||||
<div class="documentation">
|
||||
<aside class="documentation__sidebar">
|
||||
<ul>
|
||||
{% for p in section.subsections %}
|
||||
{% set subsection = get_section(path=p) %}
|
||||
<li>
|
||||
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
|
||||
<ul>
|
||||
{% for page in subsection.pages %}
|
||||
<li class="{% if current_path == page.path %}active{% endif %}">
|
||||
<a href="{{page.permalink}}">{{page.title}}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</aside>
|
||||
<div class="documentation__content">
|
||||
{% block doc_content %}
|
||||
{% endblock doc_content %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -1,36 +0,0 @@
|
||||
{% extends "index.html" %}
|
||||
|
||||
{% block extra_nav_class %}{% endblock extra_nav_class %}
|
||||
{% block extra_content_class %}content--reversed{% endblock extra_content_class %}
|
||||
|
||||
{% block content %}
|
||||
{%if page.extra.section %}
|
||||
{% set section = get_section(path="{{page.extra.section}}/_index.md") %}
|
||||
{% else %}
|
||||
{% set section = get_section(path="server/_index.md") %}
|
||||
{% endif %}
|
||||
<div class="documentation">
|
||||
<aside class="documentation__sidebar">
|
||||
<ul>
|
||||
{% for p in section.subsections %}
|
||||
{% set subsection = get_section(path=p) %}
|
||||
<li>
|
||||
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
|
||||
<ul>
|
||||
{% for page in subsection.pages %}
|
||||
<li class="{% if current_path == page.path %}active{% endif %}">
|
||||
<a href="{{page.permalink}}">{{page.title}}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</aside>
|
||||
<div class="documentation__content">
|
||||
{% block doc_content %}
|
||||
{% endblock doc_content %}
|
||||
{% block next_link %} {% endblock next_link %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-gb">
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
@@ -14,6 +14,7 @@
|
||||
<link rel="stylesheet" href="{{ get_url(path="site.css") }}"/>
|
||||
<link rel="icon" href="{{ get_url(path="favicon.ico") }}">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<script> dataLayer = [{ 'visitorType': 'geek' }]; </script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -36,13 +37,13 @@
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="content {% block extra_content_class %}{% endblock extra_content_class %}">
|
||||
<div class="content content--reversed">
|
||||
{% block content %}
|
||||
<div class="hero">
|
||||
<h1>Spark SDK</h1>
|
||||
<p class="hero__tagline">
|
||||
Spark consists of a client UI
|
||||
(<a class="white" href="https://www.bluetradingsystems.com/bts-spark#ladder-algo-screen">Spark Client</a>),
|
||||
(<a href="https://www.bluetradingsystems.com/bts-spark#ladder-algo-screen">Spark Client</a>),
|
||||
a high-performance professional futures front end, connected via our proprietary protocol over TCP to our Spark Server, which in turn connects to execution venues via their native protocols.
|
||||
Use the Spark SDK to extend Spark Server with your custom Algos, then use the Client SDK to launch and control them.
|
||||
</p>
|
||||
@@ -64,7 +65,7 @@
|
||||
<a href="{{ get_url(path="@/server/_index.md") }}" class="button">Get started</a>
|
||||
</div>
|
||||
|
||||
<div class="selling-point disabled">
|
||||
<div class="selling-point">
|
||||
<h2>
|
||||
Client SDK
|
||||
<i class="fa fa-gamepad" aria-hidden="true"></i>
|
||||
@@ -72,19 +73,9 @@
|
||||
<p>
|
||||
The Spark client SDK allows you to integrate spark with your existing ecosystem. The Spark Client is built entirely on this simple, powerful API.
|
||||
</p>
|
||||
<a href="" class="button" disabled>Get started</a>
|
||||
<a href="" class="button">Get started</a>
|
||||
</div>
|
||||
|
||||
<!-- <div class="selling-point disabled">
|
||||
<h2>
|
||||
Microsoft Excel
|
||||
<i class="fa fa-table" aria-hidden="true"></i>
|
||||
</h2>
|
||||
<p>
|
||||
More of your business is built on excel than probably should be. Continue this worrying trend with the Spark Excel API.
|
||||
</p>
|
||||
<a href="" class="button" disabled>Get started</a>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -94,6 +85,15 @@
|
||||
© Blue Trading Systems
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
if (location.hostname == "api.bluetradesys.com") {
|
||||
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-56FHG8R');
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="{{ get_url(path="elasticlunr.min.js") }}"></script>
|
||||
<script type="text/javascript" src="{{ get_url(path="search_index.en.js") }}"></script>
|
||||
<script type="text/javascript" src="{{ get_url(path="search.js") }}"></script>
|
||||
|
||||
@@ -1,13 +1,39 @@
|
||||
{% extends "documentation.html" %}
|
||||
{% extends "index.html" %}
|
||||
{# this has access to the "page" variable but documentation.html does not #}
|
||||
|
||||
{% block title %}{{ page.title }} | {{ super() }} {% endblock title %}
|
||||
{% block doc_content %}
|
||||
<h1>{{page.title}}</h1>
|
||||
{{page.content | safe}}
|
||||
{% endblock doc_content %}
|
||||
|
||||
{% block next_link %}
|
||||
<p>
|
||||
{% endblock next_link %}
|
||||
|
||||
{% block content %}
|
||||
{% set section = get_section(path=page.ancestors[1]) %}
|
||||
{#
|
||||
{#<div>{{ section.title }} </div>#}
|
||||
<div class="documentation">
|
||||
<aside class="documentation__sidebar">
|
||||
<ul>
|
||||
{% for p in section.subsections %}
|
||||
{% set subsection = get_section(path=p) %}
|
||||
<li>
|
||||
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
|
||||
<ul>
|
||||
{% for page in subsection.pages %}
|
||||
<li class="{% if current_path == page.path %}active{% endif %}">
|
||||
<a href="{{page.permalink}}">{{page.title}}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</aside>
|
||||
<div class="documentation__content">
|
||||
<h1>{{page.title}}</h1>
|
||||
{{page.content | safe}}
|
||||
|
||||
{# next/pre links #}
|
||||
<p>
|
||||
{% if page.lighter %}
|
||||
<a href="{{ page.lighter.permalink }}">
|
||||
<i class="fa fa-chevron-left fa-fw" aria-hidden="true"></i>
|
||||
@@ -21,5 +47,7 @@
|
||||
<i class="fa fa-chevron-right fa-fw" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endblock next_link %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -1 +1 @@
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/_index.md;content/bts_spark/Help/_index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Launcher/_index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Logging-In-to-the-System/_index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Market/_index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/_index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/Settings/_index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Spreader/_index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Trading/OrderTypes/index.md;content/bts_spark/Trading/_index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
|
||||
set(sources /home/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/_index.md;content/bts_spark/Help/_index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Launcher/_index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Logging-In-to-the-System/_index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Market/_index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/_index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/Settings/_index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Spreader/_index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Trading/OrderTypes/index.md;content/bts_spark/Trading/_index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
|
||||
@@ -4,6 +4,15 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 2.15.3
|
||||
### Bug Fixes
|
||||
- Fixed a bug where some CME currency spreads exhibited incorrect tick values
|
||||
## 2.15.2
|
||||
### User Visible Changes
|
||||
- Excel RTD plugin can now receive ICE market data
|
||||
## 2.15.1
|
||||
### Bug Fixes
|
||||
- Fixed a FIX log parsing issue that resulted in exchange gateway logon problems
|
||||
## 2.15.0
|
||||
### User Visible Changes
|
||||
- Added ability to hide Ladder status messages via Ladder Settings -> Left Panel tab -> Show/Hide groupbox -> Status Message
|
||||
|
||||
+1
-1
Submodule docker/linux updated: 68f43cef8d...0df20ec20a.
+1
-1
Submodule externals updated: 6d8f64ca5c...c695e96940.
@@ -19,7 +19,8 @@
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
<Version>$(major).$(minor).$(patch)</Version>
|
||||
<Version Condition="'$(version)'=='..'">1.0.0</Version>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../spark_api/spark_api.csproj" />
|
||||
|
||||
@@ -292,7 +292,9 @@ namespace mbts.spark {
|
||||
int count = 0;
|
||||
double factored_base = log10_diff > 0 ? reduced_target_base * Math.Pow(10, Math.Ceiling(log10_diff)) : reduced_target_base;
|
||||
do {
|
||||
if (factored_base % raw_base == 0)
|
||||
// Note that the modulus operator is not used here due to rounding errors.
|
||||
// Such a rounding errror was the cause of the bug https://bluetradesys.atlassian.net/browse/SP-283.
|
||||
if (MathUtils.is_int(factored_base / raw_base))
|
||||
return new TickFactors(factored_base, factored_base / raw_base);
|
||||
factored_base *= 10;
|
||||
} while (++count < 10);
|
||||
|
||||
@@ -3,17 +3,19 @@
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks>
|
||||
<Authors>Blue Trading Systems</Authors>
|
||||
<Company>Blue Trading Systems</Company>
|
||||
<Product>Spark</Product>
|
||||
<Description />
|
||||
<PackageId>Spark_API</PackageId>
|
||||
<Product>Spark</Product>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
<Version>$(major).$(minor).$(patch)</Version>
|
||||
<Version Condition="'$(version)'=='..'">0.0.0</Version>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
|
||||
@@ -24,12 +26,10 @@
|
||||
<Proto3 Include="**/*.proto" />
|
||||
<Proto3 Include="../../lib/api/*.proto" />
|
||||
<Proto3 Include="../../lib/api/services/*.proto" />
|
||||
<!--Proto3 Remove="../../lib/api/services/MarketDataRelay.proto" /-->
|
||||
<Proto3 Include="../../lib/md/protos/co2.proto" />
|
||||
<Proto3 Include="../../lib/domain_model/Security.proto" />
|
||||
<Proto3 Include="../../lib/sparkproto/SparkProtos.proto" />
|
||||
<Proto3 Update="../../lib/api/services/OutOfProcessURLListener.proto">
|
||||
<!-- <CompileOptions>internal_access:</CompileOptions> -->
|
||||
</Proto3>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,10 @@
|
||||
using Google.Protobuf;
|
||||
namespace bts.api {
|
||||
|
||||
public sealed partial class Product : ICustomDiagnosticMessage {
|
||||
public string ToDiagnosticString() {
|
||||
return $"{this.Symbol}:{this.Type}:{this.Exchange}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,8 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
<Version>$(major).$(minor).$(patch)</Version>
|
||||
<Version Condition="'$(version)'=='..'">0.0.0</Version>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -66,9 +66,23 @@ namespace bts.excel.rtd
|
||||
if (string.IsNullOrEmpty(contract))
|
||||
return Observable.Return(new []{"Can't Parse Contract: " + contract ?? string.Empty});
|
||||
|
||||
Func<SecurityImmutable, bool> where = (x => x.Exchange == ven && x.Symbol == symbol && x.security_type() == sectype &&
|
||||
x.Glbx.SecurityDesc == contract);
|
||||
Func<SecurityImmutable, bool> desc = null;
|
||||
switch(ven)
|
||||
{
|
||||
case Venue.Sim:
|
||||
case Venue.Glbx:
|
||||
desc = (x=> x.Glbx.SecurityDesc == contract);
|
||||
break;
|
||||
case Venue.Ice:
|
||||
desc = (x=> x.Ice.SecurityDesc == contract);
|
||||
break;
|
||||
default:
|
||||
desc = (x => true);
|
||||
break;
|
||||
}
|
||||
|
||||
Func<SecurityImmutable, bool> where = (x => x.Exchange == ven && x.Symbol == symbol && x.security_type() == sectype && desc(x) );
|
||||
|
||||
var stream = market_data(ven).subscribe_combined(where);
|
||||
return stream;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
<Version>$(major).$(minor).$(patch)</Version>
|
||||
<Version Condition="'$(version)'=='..'">1.0.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Binary file not shown.
@@ -19,6 +19,8 @@ namespace bts {
|
||||
public bool admin { get; set; }
|
||||
[Option(nameof(latency))]
|
||||
public bool latency { get; set; }
|
||||
[Option("md")]
|
||||
public bool market_data { get; set; }
|
||||
}
|
||||
|
||||
public class ServiceTest {
|
||||
@@ -35,9 +37,24 @@ namespace bts {
|
||||
if (o.latency) {
|
||||
latency_test();
|
||||
}
|
||||
if (o.market_data) {
|
||||
market_data();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void market_data() {
|
||||
using (var ctx = new ServiceContext()) {
|
||||
var md = ctx.locate<MarketDataService>();
|
||||
md.subscribe_securities().Table().Subscribe(update => {
|
||||
Console.WriteLine($"got {update.state.Count} securities");
|
||||
});
|
||||
while(true) {
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string try_get(string[] args, int index) {
|
||||
if (args.Length < index) {
|
||||
return args[index];
|
||||
|
||||
@@ -1 +1 @@
|
||||
set(sources files/cert_secure_logon.txt;files/futures.coconut.xz;files/onixs_ice/ConnectivityConfiguration.APITest.xml;files/onixs_ice/ConnectivityConfiguration.PerfTest.xml;files/onixs_ice/ConnectivityConfiguration.Production.xml;files/perf_trace/compiled.css;files/perf_trace/index.html;files/perf_trace/jquery-3.3.1.min.js;files/perf_trace/main.js;files/perf_trace/perf_log.sql;files/perf_trace/perf_trace_analysis.sql;files/perf_trace/plotly-latest.min.js)
|
||||
set(sources files/cert_secure_logon.txt;files/cert_secure_logon.txt.orig;files/futures.coconut.xz;files/onixs_ice/ConnectivityConfiguration.APITest.xml;files/onixs_ice/ConnectivityConfiguration.PerfTest.xml;files/onixs_ice/ConnectivityConfiguration.Production.xml;files/perf_trace/compiled.css;files/perf_trace/index.html;files/perf_trace/jquery-3.3.1.min.js;files/perf_trace/main.js;files/perf_trace/perf_log.sql;files/perf_trace/perf_trace_analysis.sql;files/perf_trace/plotly-latest.min.js)
|
||||
@@ -1,12 +1,13 @@
|
||||
Session ID Access ID Secret Key Creation Date Expiration Date Environment
|
||||
Session ID Access ID Secret Key Creation Date Expiration Date Environment
|
||||
|
||||
M4D lLtjH74y1Kh5XUJTLyH2 f1K_tROQp2lTIplVp34WxKa7xDUgfFnIteolfik061o 2019-09-03 2020-09-03 CERT
|
||||
M4D lLtjH74y1Kh5XUJTLyH2 f1K_tROQp2lTIplVp34WxKa7xDUgfFnIteolfik061o 2019-09-03 2020-09-03 CERT
|
||||
|
||||
MC1 VEMlkDuLz74OVHSuacQB IuHyYT3b-9KUo3WsuXykt-fXsjzp7L-JOrdCSQZLNl0 2019-09-03 2020-09-03 CERT
|
||||
MC1 VEMlkDuLz74OVHSuacQB IuHyYT3b-9KUo3WsuXykt-fXsjzp7L-JOrdCSQZLNl0 2019-09-03 2020-09-03 CERT
|
||||
|
||||
MC5 77lzSUwPLlM9obaXh2mK z6aA9qfASDKfZkYTVKFqJd-78i0HZ2Q9ec3CKCYIqHg 2019-09-03 2020-09-03 CERT
|
||||
MC5 77lzSUwPLlM9obaXh2mK z6aA9qfASDKfZkYTVKFqJd-78i0HZ2Q9ec3CKCYIqHg 2019-09-03 2020-09-03 CERT
|
||||
|
||||
MC7 RphzI3rn71oGUEiYnX9e y6OvvPPyoyj2S8YAnE1gFduXT8v29HIMjO5EngYxSNg 2019-09-03 2020-09-03 CERT
|
||||
MC7 RphzI3rn71oGUEiYnX9e y6OvvPPyoyj2S8YAnE1gFduXT8v29HIMjO5EngYxSNg 2019-09-03 2020-09-03 CERT
|
||||
|
||||
MC8 PYBIm2TI9gFPIxmHbuYl 6o-ZolGH2DdeQOlGtJ5Zqoh_kg7VdbxkmjeOHmigKwU 2019-09-03 2020-09-03 CERT
|
||||
MC8 PYBIm2TI9gFPIxmHbuYl 6o-ZolGH2DdeQOlGtJ5Zqoh_kg7VdbxkmjeOHmigKwU 2019-09-03 2020-09-03 CERT
|
||||
|
||||
NGG 1OXPqNxPhbqbzm0kF92e qlUrwY1KG1Pyq-TcbJgMqYXmBTQoAi-wEEBERRDRZPE 2019-12-20 2020-12-20 NEWRELEASE
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityMapper.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;md/vendor/OnixsUtils.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.cpp;orLine truncated
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/Product.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityMapper.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/service/MarketDataServiceServer.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;md/vendor/OnixsUtils.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metricsLine truncated
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityMapper.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;md/vendor/OnixsUtils.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthSessionManager.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;Line truncated
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/Product.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityMapper.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/service/MarketDataServiceServer.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;md/vendor/OnixsUtils.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthSessionManager.cpp;utils/AuthToken.cLine truncated
|
||||
@@ -580,7 +580,14 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
|
||||
rv.set_session(conn_ptr->name());
|
||||
rv.set_operator_id(cme_user_data->operator_id());
|
||||
rv.set_location(cme_user_data->location());
|
||||
|
||||
// smp_id should be 0 or 8 bytes so it can be encoded as a 64 bit number for ilink3
|
||||
rv.set_smp_id(cme_user_data->smp_id());
|
||||
if (!rv.smp_id().empty() && rv.smp_id().size() < 8) {
|
||||
std::string smp_id = rv.smp_id();
|
||||
while(smp_id.size() < 8) smp_id += "0";
|
||||
rv.set_smp_id(smp_id);
|
||||
}
|
||||
if (cme_user_data->smp_instruction()== "O")
|
||||
rv.set_smp_instruction(api::orderman::SelfMatchPreventionInstruction::SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_RESTING);
|
||||
else if (cme_user_data->smp_instruction()== "N")
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
syntax="proto3";
|
||||
package bts.api;
|
||||
option csharp_namespace="bts.api";
|
||||
option cc_enable_arenas=true;
|
||||
import "lib/api/apioptions.proto";
|
||||
import "lib/api/apicore.proto";
|
||||
import "lib/domain_model/Security.proto";
|
||||
import "lib/md/protos/co2.proto";
|
||||
|
||||
message ProductList {
|
||||
repeated bts.api.Product products = 1;
|
||||
}
|
||||
|
||||
// Should this cary int prices?
|
||||
message PriceEntry {
|
||||
double price = 1;
|
||||
uint32 direct = 10;
|
||||
uint32 implied = 11;
|
||||
}
|
||||
|
||||
message BookEntry {
|
||||
uint32 key = 1 [(bts.api.key)=true];
|
||||
repeated PriceEntry bid = 10;
|
||||
repeated PriceEntry ask = 20;
|
||||
}
|
||||
|
||||
message TickerEntry {
|
||||
uint32 key = 1 [(bts.api.key)=true];
|
||||
int64 time = 2;
|
||||
double price = 3;
|
||||
uint32 size = 4;
|
||||
uint32 total_volume = 5;
|
||||
bts.co2.AggressorSide aggressor_side = 6;
|
||||
bool from_spread = 7;
|
||||
bool gap = 10;
|
||||
}
|
||||
|
||||
|
||||
service MarketDataService {
|
||||
rpc get_products(bts.api.VoidMessage) returns (ProductList);
|
||||
// stream all securities
|
||||
rpc securities (bts.api.VoidMessage) returns (stream bts.api.Security);
|
||||
rpc book (bts.api.Product) returns (stream BookEntry);
|
||||
rpc trades(bts.api.VoidMessage) returns (stream TickerEntry);
|
||||
// subscribe_misc(Product)
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
|
||||
|
||||
#nusers: 1
|
||||
#nusers: 2
|
||||
#allow concurrent : true
|
||||
#expires: 2020-Mar-31 00:00:00 637212096000000000
|
||||
#generation_time: 2020-Jan-06 18:56:23.406931 5248825356261457214
|
||||
#expires: 2021-Jan-01 00:00:00 637450560000000000
|
||||
#generation_time: 2020-Apr-27 21:55:11.126045 5248922231538648354
|
||||
EncryptedKeyCode {
|
||||
value {
|
||||
keycode: "Ixb/z6hwFQmojpm4GwNEpQmsqpYT9OTHbcKtROIqj38GwGqyVn54JMDB04V048quO+NAI78DKJXsUWgjDH0y5HQlwN6Lq48s/bdwKqR1OTBPr6xoTImtCb+l5b+8fUGlg7t3X8gfMdxohHgcUi9k9n7ZrnNvqcjdYKoP0CaTZT8zHuBQx3f4Lqrz32gq9rJ5skRjH6ZqNblNdlsF+kOAqXCkvI5p0QfyKgtCodr33qsW2cbqY5gUD9KwsnmFVa8/zuFBucsoM7dHB47yRaIeOFFu7xGayAdAHA8SwLyoxE5y+adIX1SYnAiqAu1raZpeXgRE1LNQk3c7uhUjt0coag=="
|
||||
keycode: "jVEAueEfjx2MHzAOgJMSl8z9zwK9sUXp6iKr2+zTvgNrrZevi2I5pK1OCkHhE5IQZuRde5MyL8VpgBmZg/AnNVBo6719LT06MQBExbWKpx+2/LWF/JDv8m2sBUKdfze40vnPIGhXnLl/9mg74gD6uJgRU8uLEq3Hi5Xolqdi+kLKu9Qy4yuxy1hcYzdWPmYHvGcHGlaPH3jMN96m1DIcNYoV+RvoqlIv5zVDDWRWBMIiiwqrxlpwRJBw3R0pVpRVjUYqWw9dpfkIYevpCyvw+O4hqeGKuXCylie6wHiRPIa5Pxoxu3e5K2E1epGG6I7F56hQv+e1YPk1w4vwuEACWA=="
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,28 @@ OrdermanCfg {
|
||||
rule: "DeploymentType.type == 'dev'"
|
||||
value {
|
||||
executor {
|
||||
thread_name: "oman_orders"
|
||||
poll: false
|
||||
hosted_actor: "MDSource"
|
||||
hosted_actor: "OrderManager"
|
||||
hosted_actor: "ExecutionGateway"
|
||||
thread_name: "oman0"
|
||||
poll: false
|
||||
hosted_actor: { type: "MDSource" }
|
||||
hosted_actor: { type: "OrderManagerShard" qualifier: "om0" }
|
||||
}
|
||||
executor {
|
||||
thread_name: "oman1"
|
||||
poll: false
|
||||
hosted_actor: { type: "MDSource" }
|
||||
hosted_actor: { type: "OrderManagerShard" qualifier: "catch_all" }
|
||||
}
|
||||
executor {
|
||||
thread_name: "oman_gw"
|
||||
poll: false
|
||||
hosted_actor: { type: "Gateway" qualifier: "gw0" }
|
||||
}
|
||||
executor {
|
||||
thread_name: "oman_slow"
|
||||
poll: false
|
||||
hosted_actor: "*"
|
||||
poll: false
|
||||
hosted_actor: { type : "*" }
|
||||
}
|
||||
use_gateway: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +31,44 @@ OrdermanCfg {
|
||||
value {
|
||||
executor {
|
||||
thread_name: "oman_orders"
|
||||
poll: true
|
||||
hosted_actor: "MDSource"
|
||||
hosted_actor: "OrderManager"
|
||||
hosted_actor: "ExecutionGateway"
|
||||
poll: true
|
||||
hosted_actor: { type: "MDSource" }
|
||||
hosted_actor: { type: "OrderManager" }
|
||||
}
|
||||
executor {
|
||||
thread_name: "oman_gw"
|
||||
poll: true
|
||||
hosted_actor: { type: "Gateway" qualifier: "gw0" }
|
||||
}
|
||||
executor {
|
||||
thread_name: "oman_slow"
|
||||
poll: false
|
||||
hosted_actor: "*"
|
||||
poll: false
|
||||
hosted_actor: { type: "*" }
|
||||
}
|
||||
use_gateway: true
|
||||
}
|
||||
}
|
||||
|
||||
OrdermanShardCfg {
|
||||
value {
|
||||
shard {
|
||||
qualifier: "om0"
|
||||
user_regex: "^.*(bts|cqg).*$"
|
||||
}
|
||||
shard {
|
||||
qualifier: "catch_all"
|
||||
user_regex: "^((?!(bts|cqg)).)*$"
|
||||
is_unknown_order_catch_all: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GatewayCfg {
|
||||
value {
|
||||
#note that multiple gws are possible but not well tested as of 4/23/2020
|
||||
gateway {
|
||||
qualifier: "gw0"
|
||||
connection: "*"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,7 @@ message JetFIX {
|
||||
|
||||
optional uint32 max_connection_attempts = 346 [default=10];
|
||||
optional bool auto_fix_seq_num = 347 [default=false];
|
||||
optional bool use_ilink3 = 348 [default=false] ;
|
||||
}
|
||||
|
||||
repeated Connection connection = 10;
|
||||
@@ -116,4 +117,4 @@ message CMECannedSecureLogon {
|
||||
"MC8 AbEEtBn63WIAuItPDz1c n3ndjYb03Hx6wBO3me35HoONyDgO5TZKWk0tDSaX9KA 2018-04-05 2019-04-05 CERT \n"
|
||||
"T39 UWggBgHWAe07aqW3ps2I tbb3it0rUQSSTa0TC9QcRwkrHEMDLWhaJH4VQ0FqGB4 2018-04-05 2019-04-05 NEWRELEASE \n"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,35 @@ syntax='proto2';
|
||||
package bts.config;
|
||||
|
||||
message OrdermanCfg {
|
||||
message HostedActor {
|
||||
required string type = 1;
|
||||
optional string qualifier = 2;
|
||||
}
|
||||
message Executor {
|
||||
required string thread_name = 1;
|
||||
optional bool poll = 2 [ default = false];
|
||||
repeated string hosted_actor = 3;
|
||||
//repeated string hosted_actor = 3;
|
||||
repeated HostedActor hosted_actor = 4;
|
||||
}
|
||||
repeated Executor executor = 1;
|
||||
optional uint32 default_iceberg_pause_secs = 2 [default = 0];
|
||||
optional string fallback_cust_order_handling_inst = 3 [default = "Y"];
|
||||
optional bool use_gateway = 4 [default = true];
|
||||
}
|
||||
|
||||
message OrdermanShardCfg {
|
||||
message OrderManagerShard {
|
||||
required string qualifier = 1;
|
||||
required string user_regex = 2;
|
||||
optional bool is_unknown_order_catch_all = 3;
|
||||
}
|
||||
repeated OrderManagerShard shard = 1;
|
||||
}
|
||||
|
||||
message GatewayCfg {
|
||||
message Gateway {
|
||||
required string qualifier = 1;
|
||||
repeated string connection = 2;
|
||||
}
|
||||
repeated Gateway gateway = 1;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ class FIXEncodingStream {
|
||||
auto msg_offset = _buffer.size();
|
||||
|
||||
_current_msg = FIXFragment(_buffer);
|
||||
_begin_string_bm = _current_msg.add<fix::BeginString>("FIX.4.2");
|
||||
_begin_string_bm = _current_msg.add<fix::BeginString>(std::string_view {"FIX.4.2"});
|
||||
_body_length_bm = _current_msg.add<fix::BodyLength>(0, 6);
|
||||
|
||||
char mt_encoding[4];
|
||||
|
||||
@@ -85,6 +85,7 @@ typedef StringField<307> UnderlyingSecurityDesc;
|
||||
typedef StringField<336> TradingSessionID;
|
||||
typedef StringField<35> MsgType;
|
||||
typedef StringField<37> OrderID;
|
||||
typedef BigIntField<37> OrderIDu64;
|
||||
typedef StringField<41> OrigClOrdID;
|
||||
typedef StringField<440> ClearingAccount;
|
||||
typedef StringField<48> SecurityID;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <memory>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <string_view>
|
||||
|
||||
#include "lib/jetfix/codec/ReaderCursor.h"
|
||||
#include "lib/utils/meta_programming.h"
|
||||
#include "lib/utils/datetime.h"
|
||||
@@ -203,6 +205,13 @@ public:
|
||||
static std::pair<uint32_t,uint32_t> write (char*buffer,std::string const &str)
|
||||
{return write (buffer,str.data (),str.size ());}
|
||||
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
static uint32_t max_value_size (std::string_view const&str) {return str.size ();}
|
||||
static std::pair<uint32_t,uint32_t> write (char*buffer,std::string_view const &str)
|
||||
{return write (buffer,str.data (),str.size ());}
|
||||
#endif
|
||||
|
||||
static std::pair<uint32_t,uint32_t> write (char*buffer,const char*str,uint32_t sz)
|
||||
{
|
||||
memcpy (buffer,str,sz);
|
||||
|
||||
@@ -49,6 +49,7 @@ enum class MessageTypes : uint32_t
|
||||
TestRequest = '1',
|
||||
TradingSessionStatus = 'h',
|
||||
TradingSessionStatusRequest = 'g',
|
||||
MDIncrementalUpdate = 'X',
|
||||
ICETraderLogin = ('U' << 16) + ('C' << 8) + 'G',
|
||||
ICETraderLoginResponse = ('U' << 16) + ('C' << 8) + 'I',
|
||||
ICETraderLogout = ('U' << 16) + ('C' << 8) + 'H',
|
||||
|
||||
@@ -40,13 +40,13 @@ OutFIXPtr make_seq_reset(config::JetFIX::Connection const &session_cfg,
|
||||
|
||||
enum class Session::State {
|
||||
WLOGON,
|
||||
RECOVER,
|
||||
WTR_REPLY,
|
||||
READY,
|
||||
TERMINATED,
|
||||
WLOGOUT,
|
||||
CERT_MODE
|
||||
};
|
||||
RECOVER,
|
||||
WTR_REPLY,
|
||||
READY,
|
||||
TERMINATED,
|
||||
WLOGOUT,
|
||||
CERT_MODE
|
||||
};
|
||||
|
||||
struct Session::RecoveryState {
|
||||
RecoveryState() : buffered_msgs(), resend_request(0, 0) {}
|
||||
@@ -59,14 +59,14 @@ struct Session::PImpl {
|
||||
PImpl(config::JetFIX::Connection const &cfg, IConnection &connection,
|
||||
IFIXStore &store, ICoreGatewayEventSink &sink, Clock const &clock,
|
||||
trace::PerfLog &perf_log)
|
||||
: cfg(cfg), state(State::TERMINATED), connection(connection),
|
||||
store(store), event_sink(sink), now(clock), perf_log(perf_log),
|
||||
begin_of_this_session(), last_seq_num(), last_seq_num_processed(),
|
||||
last_sent_msg_timestamp(), last_sent_sync_timestamp(),
|
||||
last_rcvd_msg_timestamp(), start_of_logon(), start_of_logout(),
|
||||
current_time(std::make_shared<std::string>(
|
||||
datetime::FIX_timestamp(datetime::now()))),
|
||||
recovery_state() {}
|
||||
: cfg(cfg), state(State::TERMINATED), connection(connection),
|
||||
store(store), event_sink(sink), now(clock), perf_log(perf_log),
|
||||
begin_of_this_session(), last_seq_num(), last_seq_num_processed(),
|
||||
last_sent_msg_timestamp(), last_sent_sync_timestamp(),
|
||||
last_rcvd_msg_timestamp(), start_of_logon(), start_of_logout(),
|
||||
current_time(std::make_shared<std::string>(
|
||||
datetime::FIX_timestamp(datetime::now()))),
|
||||
recovery_state() {}
|
||||
|
||||
config::JetFIX::Connection cfg;
|
||||
State state;
|
||||
@@ -90,7 +90,7 @@ struct Session::PImpl {
|
||||
Session::Session(config::JetFIX::Connection const &cfg, IConnection &connection,
|
||||
IFIXStore &store, ICoreGatewayEventSink &sink,
|
||||
Clock const &clock, trace::PerfLog &perf_log)
|
||||
: me(new PImpl(cfg, connection, store, sink, clock, perf_log)) {}
|
||||
: me(new PImpl(cfg, connection, store, sink, clock, perf_log)) {}
|
||||
|
||||
Session::~Session() {
|
||||
if (me->state != State::TERMINATED)
|
||||
@@ -248,16 +248,16 @@ void Session::on_logon(InMsgBuffer const &msg) {
|
||||
} else {
|
||||
if (their_last <= our_last_processed)
|
||||
terminate_session(
|
||||
JetFIXError::INCOMING_SN_TOO_SMALL,
|
||||
(boost::format(
|
||||
"Incoming sequence number was %d "
|
||||
"but the last one we processed was %d. "
|
||||
"Most likely the FIX sequencer numbers file "
|
||||
"($SPARKDATA/data/jetfix/connection_name/current/seqnum) "
|
||||
"was deleted. Update this file with the correct "
|
||||
"incoming sequence number (second column)") %
|
||||
their_last % our_last_processed)
|
||||
.str());
|
||||
JetFIXError::INCOMING_SN_TOO_SMALL,
|
||||
(boost::format(
|
||||
"Incoming sequence number was %d "
|
||||
"but the last one we processed was %d. "
|
||||
"Most likely the FIX sequencer numbers file "
|
||||
"($SPARKDATA/data/jetfix/connection_name/current/seqnum) "
|
||||
"was deleted. Update this file with the correct "
|
||||
"incoming sequence number (second column)") %
|
||||
their_last % our_last_processed)
|
||||
.str());
|
||||
|
||||
else if (their_last == (our_last_processed + 1)) {
|
||||
transition(State::WTR_REPLY);
|
||||
@@ -329,9 +329,9 @@ void Session::on_test_request(InMsgBuffer const &msg) {
|
||||
switch (me->state) {
|
||||
case State::WLOGON:
|
||||
terminate_session(
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (char)msg.type())
|
||||
.str());
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (char)msg.type())
|
||||
.str());
|
||||
break;
|
||||
case State::RECOVER:
|
||||
internal_send(make_hb(me->cfg, msg.get_test_req_id()));
|
||||
@@ -363,8 +363,8 @@ void Session::on_resend_request(InMsgBuffer const &msg) {
|
||||
switch (me->state) {
|
||||
case State::WLOGON:
|
||||
terminate_session(
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (int)msg.type()).str());
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (int)msg.type()).str());
|
||||
break;
|
||||
case State::RECOVER:
|
||||
case State::WTR_REPLY:
|
||||
@@ -386,12 +386,12 @@ void Session::on_resend_request(InMsgBuffer const &msg) {
|
||||
send_seq_reset(msg.get_begin_seq_no(), msg.get_end_seq_no());
|
||||
else
|
||||
logout(
|
||||
JetFIXError::UNEXPECTED_RESEND_REQUEST,
|
||||
(boost::format("Received resend request for messages "
|
||||
"%d to %d but my initial sequence number is %d ") %
|
||||
msg.get_begin_seq_no() % msg.get_end_seq_no() %
|
||||
me->begin_of_this_session)
|
||||
.str());
|
||||
JetFIXError::UNEXPECTED_RESEND_REQUEST,
|
||||
(boost::format("Received resend request for messages "
|
||||
"%d to %d but my initial sequence number is %d ") %
|
||||
msg.get_begin_seq_no() % msg.get_end_seq_no() %
|
||||
me->begin_of_this_session)
|
||||
.str());
|
||||
break;
|
||||
}
|
||||
case State::WLOGOUT:
|
||||
@@ -408,8 +408,8 @@ void Session::on_seq_reset(InMsgBuffer const &msg) {
|
||||
switch (me->state) {
|
||||
case State::WLOGON:
|
||||
terminate_session(
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (int)msg.type()).str());
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (int)msg.type()).str());
|
||||
break;
|
||||
case State::RECOVER:
|
||||
buffer_or_deliver(msg);
|
||||
@@ -439,8 +439,8 @@ void Session::on_hb(InMsgBuffer const &msg) {
|
||||
switch (me->state) {
|
||||
case State::WLOGON:
|
||||
terminate_session(
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (int)msg.type()).str());
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (int)msg.type()).str());
|
||||
break;
|
||||
case State::RECOVER:
|
||||
buffer(msg);
|
||||
@@ -471,8 +471,8 @@ void Session::on_app(InMsgBuffer const &msg) {
|
||||
switch (me->state) {
|
||||
case State::WLOGON:
|
||||
terminate_session(
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (int)msg.type()).str());
|
||||
JetFIXError::EXPECTED_LOGON,
|
||||
(boost::format("Received message type %c ") % (int)msg.type()).str());
|
||||
break;
|
||||
case State::RECOVER:
|
||||
buffer_or_deliver(msg);
|
||||
@@ -595,8 +595,8 @@ void Session::terminate_session(JetFIXError::Code reason) {
|
||||
|
||||
bool match_with_outgoing_sn_too_small(std::string const &msg, uint32_t *sn) {
|
||||
std::string rx_str =
|
||||
".*Sequence number received lower than expected. Expected "
|
||||
"\\[(\\d+)\\] Received \\d+.*";
|
||||
".*Sequence number received lower than expected. Expected "
|
||||
"\\[(\\d+)\\] Received \\d+.*";
|
||||
std::regex rx(rx_str);
|
||||
|
||||
std::smatch matches;
|
||||
@@ -609,7 +609,7 @@ bool match_with_outgoing_sn_too_small(std::string const &msg, uint32_t *sn) {
|
||||
|
||||
bool match_with_incoming_sn_too_small(std::string const &msg, uint32_t *sn) {
|
||||
std::string rx_str = ".*Incoming sequence number was (\\d+) "
|
||||
"but the last one we processed was .*";
|
||||
"but the last one we processed was .*";
|
||||
|
||||
std::regex rx(rx_str);
|
||||
|
||||
@@ -728,7 +728,7 @@ std::pair<uint32_t, uint32_t> Session::find_first_incoming_msg_gap() {
|
||||
else
|
||||
return std::make_pair(me->last_seq_num_processed + 1,
|
||||
me->recovery_state->buffered_msgs.begin()->first -
|
||||
1);
|
||||
1);
|
||||
}
|
||||
|
||||
bool Session::resend_request_active() {
|
||||
@@ -786,9 +786,9 @@ OutFIXPtr make_logon(uint32_t seq_num, uint32_t last_seq_num_processed,
|
||||
if(session_cfg.platform() == bts::config::JetFIX_Connection_JetFIXPlatform_ICE ||
|
||||
session_cfg.platform() == bts::config::JetFIX_Connection_JetFIXPlatform_SIMICE)
|
||||
{
|
||||
buffer.add<ICEApplicationSystemName>(application);
|
||||
buffer.add<ICEApplicationSystemVersion>("1.0");
|
||||
buffer.add<ICEApplicationSystemVendor>("BTS");
|
||||
buffer.add<ICEApplicationSystemName>(std::string_view{application});
|
||||
buffer.add<ICEApplicationSystemVersion>(std::string_view{"1.0"});
|
||||
buffer.add<ICEApplicationSystemVendor>(std::string_view{"BTS"});
|
||||
|
||||
buffer.add<UserName>(session_cfg.user_name());
|
||||
buffer.add<RawDataLength>(session_cfg.raw_data().size());
|
||||
@@ -799,9 +799,9 @@ OutFIXPtr make_logon(uint32_t seq_num, uint32_t last_seq_num_processed,
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.add<ApplicationSystemName>(application);
|
||||
buffer.add<ApplicationSystemVersion>("1.0");
|
||||
buffer.add<ApplicationSystemVendor>("BTS");
|
||||
buffer.add<ApplicationSystemName>(std::string_view{application});
|
||||
buffer.add<ApplicationSystemVersion>(std::string_view{"1.0"});
|
||||
buffer.add<ApplicationSystemVendor>(std::string_view{"BTS"});
|
||||
|
||||
if (session_cfg.has_trading_session_id()) {
|
||||
buffer.add<fix::NoTradingSessions>(1);
|
||||
@@ -824,12 +824,12 @@ OutFIXPtr make_logon(uint32_t seq_num, uint32_t last_seq_num_processed,
|
||||
std::to_string(session_cfg.seconds_between_hbs()),
|
||||
session_cfg.sender_locationid(), last_seq_num_str, application, "1.0",
|
||||
"BTS");
|
||||
#ifndef BTS_EDGE
|
||||
#ifndef BTS_EDGE
|
||||
LOG(Jetfix, Info, "Canonical string for secure logon: {}", ld.canonical);
|
||||
#endif
|
||||
#endif
|
||||
buffer.add<EncodedTextLen>(ld.access_key.size());
|
||||
buffer.add<EncodedText>(ld.access_key);
|
||||
buffer.add<EncryptedPasswordMethod>("CME-1-SHA-256");
|
||||
buffer.add<EncryptedPasswordMethod>(std::string_view{"CME-1-SHA-256"});
|
||||
buffer.add<EncryptedPasswordLen>(ld.signature.size());
|
||||
buffer.add<EncryptedPassword>(ld.signature);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
|
||||
namespace bts {
|
||||
namespace jetfix {
|
||||
@@ -114,16 +114,17 @@ std::string store_path(std::string base_path,
|
||||
return result.string();
|
||||
}
|
||||
|
||||
bool is_valid_store_line(std::string const&line) {
|
||||
std::regex rx("\\d\\d\\d\\d\\d\\d\\d\\d-"
|
||||
"\\d\\d:\\d\\d:\\d\\d\\."
|
||||
"\\d\\d\\d . 8=FIX\\.4\\.2.*");
|
||||
return std::regex_match(line, rx);
|
||||
static std::regex store_line_rx("\\d\\d\\d\\d\\d\\d\\d\\d-"
|
||||
"\\d\\d:\\d\\d:\\d\\d\\."
|
||||
"\\d\\d\\d . 8=FIX\\.4\\.2.*");
|
||||
|
||||
bool is_valid_store_line(std::string const &line) {
|
||||
return std::regex_match(line, store_line_rx);
|
||||
}
|
||||
|
||||
std::map<uint32_t, InMsgBuffer>
|
||||
load_replay(config::JetFIX::Connection const &cfg, std::string const &path,
|
||||
uint32_t ) {
|
||||
uint32_t) {
|
||||
std::map<uint32_t, InMsgBuffer> result;
|
||||
if (boost::filesystem::exists(path)) {
|
||||
|
||||
@@ -137,7 +138,7 @@ load_replay(config::JetFIX::Connection const &cfg, std::string const &path,
|
||||
if (!f.eof()) {
|
||||
if (!is_valid_store_line(line_buffer))
|
||||
continue;
|
||||
|
||||
|
||||
if (line_buffer[22] == '<') {
|
||||
auto ts =
|
||||
datetime::parse_FIX_timestamp(line_buffer.substr(0, 21));
|
||||
|
||||
@@ -81,9 +81,9 @@ TEST_CASE ("JetFIX Field2Tests", "[jetfix]")
|
||||
std::string out (20, ' ');
|
||||
typedef StringField<123> Field;
|
||||
|
||||
REQUIRE(Field::max_value_size ("hello")==5);
|
||||
REQUIRE(Field::max_value_size (std::string_view{"hello"})==5);
|
||||
|
||||
auto result = Field::write (&out[0],"hello");
|
||||
auto result = Field::write (&out[0],std::string_view{"hello"});
|
||||
REQUIRE (result.first==5);
|
||||
REQUIRE (result.second=='h'+'e'+'l'+'l'+'o');
|
||||
REQUIRE (out.compare (0,5,"hello") == 0);
|
||||
@@ -112,7 +112,7 @@ TEST_CASE ("JetFIX FIXFragmentTests" , "[jetfix]")
|
||||
FIXFragment g (0);
|
||||
g.add<CharField<1>> ('a');
|
||||
g.add<FloatField<2>> (1.2,-1,6);
|
||||
g.add<StringField<3>> ("hello");
|
||||
g.add<StringField<3>> (std::string_view{"hello"});
|
||||
g.add<IntField<4>> (123,3);
|
||||
const char*t_s = "20150427-19:05:18.922";
|
||||
g.add<TimeField<5>> (datetime::parse_FIX_timestamp (t_s));
|
||||
@@ -141,14 +141,14 @@ TEST_CASE ("JetFIX FIXFragmentTests" , "[jetfix]")
|
||||
FIXFragment g;
|
||||
auto c_bm = g.add<CharField<1>> ('a');
|
||||
auto f_bm = g.add<FloatField<2>> (1.2,-1,6);
|
||||
auto s_bm = g.add<StringField<3>> ("hello");
|
||||
auto s_bm = g.add<StringField<3>> (std::string_view{"hello"});
|
||||
auto i_bm = g.add<IntField<4>> (123,3);
|
||||
auto t_bm = g.add<TimeField<5>> (datetime::parse_FIX_timestamp (t_s1));
|
||||
auto b_bm = g.add<BoolField<6>> (true);
|
||||
|
||||
g.overwrite (c_bm,'b');
|
||||
g.overwrite (f_bm,1.3,-1,6);
|
||||
g.overwrite (s_bm,"olleh");
|
||||
g.overwrite (s_bm,std::string_view {"olleh"});
|
||||
g.overwrite (i_bm,321,3);
|
||||
g.overwrite (t_bm,(datetime::parse_FIX_timestamp (t_s2)));
|
||||
g.overwrite (b_bm,false);
|
||||
@@ -172,11 +172,11 @@ TEST_CASE ("JetFIX FIXFragmentTests" , "[jetfix]")
|
||||
FIXFragment g1;;
|
||||
g1.add<CharField<1>> ('a');
|
||||
g1.add<FloatField<2>> (1.2,-1,6);
|
||||
g1.add<StringField<3>> ("hello");
|
||||
g1.add<StringField<3>> (std::string_view{"hello"});
|
||||
auto chk1 = g1.checksum ();
|
||||
|
||||
FIXFragment g2;
|
||||
g2.add<StringField<4>> ("goodbye");
|
||||
g2.add<StringField<4>> (std::string_view{"goodbye"});
|
||||
g2.add<IntField<5>> (321,3);
|
||||
auto chk2 = g2.checksum ();
|
||||
|
||||
|
||||
@@ -172,12 +172,12 @@ TEST_CASE ("JetFIX FileStoreTests", "[jetfix]")
|
||||
|
||||
FIXEncodingStream stream (c);
|
||||
auto &buf = stream.new_msg (MessageTypes::Logon);
|
||||
buf.add<fix::TargetLocationID> ("US,NC");
|
||||
buf.add<fix::TargetLocationID> (std::string_view {"US,NC"});
|
||||
buf.add<fix::EncryptMethod> (0);
|
||||
buf.add<fix::HeartBtInt>(30);
|
||||
buf.add<fix::ApplicationSystemName> ("O2");
|
||||
buf.add<fix::ApplicationSystemVersion> ("1.0");
|
||||
buf.add<fix::ApplicationSystemVendor> ("BTS");
|
||||
buf.add<fix::ApplicationSystemName> (std::string_view{"O2"});
|
||||
buf.add<fix::ApplicationSystemVersion> (std::string_view {"1.0"});
|
||||
buf.add<fix::ApplicationSystemVendor> (std::string_view {"BTS"});
|
||||
stream.close ();
|
||||
stream.stamp (2,3,datetime::FIX_timestamp (datetime::now ()));
|
||||
auto fix = stream.to_str ();
|
||||
@@ -213,12 +213,12 @@ TEST_CASE ("JetFIX FileStoreTests", "[jetfix]")
|
||||
auto msg_vec = std::make_shared<OutFIX>(c);
|
||||
auto &stream = msg_vec->fix.back ();
|
||||
auto &buf = stream.new_msg (MessageTypes::Logon);
|
||||
buf.add<fix::TargetLocationID> ("US,NC");
|
||||
buf.add<fix::TargetLocationID> (std::string_view{"US,NC"});
|
||||
buf.add<fix::EncryptMethod> (0);
|
||||
buf.add<fix::HeartBtInt>(30);
|
||||
buf.add<fix::ApplicationSystemName> ("O2");
|
||||
buf.add<fix::ApplicationSystemVersion> ("1.0");
|
||||
buf.add<fix::ApplicationSystemVendor> ("BTS");
|
||||
buf.add<fix::ApplicationSystemName> (std::string_view{"O2"});
|
||||
buf.add<fix::ApplicationSystemVersion> (std::string_view{"1.0"});
|
||||
buf.add<fix::ApplicationSystemVendor> (std::string_view{"BTS"});
|
||||
stream.close ();
|
||||
stream.stamp (2,3,datetime::FIX_timestamp (datetime::now ()));
|
||||
|
||||
@@ -255,12 +255,12 @@ TEST_CASE ("JetFIX FileStoreTests", "[jetfix]")
|
||||
msg_vec->emplace_back (c);
|
||||
auto &stream = msg_vec->back ();
|
||||
auto &buf = stream.new_msg (MessageTypes::Logon);
|
||||
buf.add<fix::TargetLocationID> ("US,NC");
|
||||
buf.add<fix::TargetLocationID> (std::string_view{"US,NC"});
|
||||
buf.add<fix::EncryptMethod> (0);
|
||||
buf.add<fix::HeartBtInt>(30);
|
||||
buf.add<fix::ApplicationSystemName> ("O2");
|
||||
buf.add<fix::ApplicationSystemVersion> ("1.0");
|
||||
buf.add<fix::ApplicationSystemVendor> ("BTS");
|
||||
buf.add<fix::ApplicationSystemName> (std::string_view{"O2"});
|
||||
buf.add<fix::ApplicationSystemVersion> (std::string_view{"1.0"});
|
||||
buf.add<fix::ApplicationSystemVendor> (std::string_view{"BTS"});
|
||||
stream.close ();
|
||||
stream.stamp (i,3,datetime::FIX_timestamp (datetime::now () - boost::gregorian::days (1)));
|
||||
fs.store (InMsgBuffer (stream.to_str ()));
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef _WIN32
|
||||
#include "Product.h"
|
||||
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace bts {
|
||||
namespace api {
|
||||
std::string product_to_string(const api::Product& p) {
|
||||
std::string buffer = p.symbol();
|
||||
buffer += ":";
|
||||
buffer += SecurityType_Name(p.type());
|
||||
buffer += ":";
|
||||
buffer += Venue_Name(p.exchange());
|
||||
return buffer;
|
||||
}
|
||||
std::optional<api::Product> string_to_product(const std::string& s) {
|
||||
boost::tokenizer<boost::char_separator<char>> tok { s };
|
||||
std::vector<std::string> parts;
|
||||
for (const auto& t : tok) {
|
||||
parts.push_back(t);
|
||||
}
|
||||
if (parts.size() != 3) return std::nullopt;
|
||||
SecurityType type = SecurityType::SECURITYTYPE_UNSET;
|
||||
if (!SecurityType_Parse(parts[1], &type)) return std::nullopt;
|
||||
Venue v = Venue::VENUE_UNSET;
|
||||
if (!Venue_Parse(parts[2], &v)) return std::nullopt;
|
||||
Product p;
|
||||
p.set_symbol(parts[0]);
|
||||
p.set_type(type);
|
||||
p.set_exchange(v);
|
||||
return {p};
|
||||
}
|
||||
|
||||
|
||||
}}//ns
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "lib/domain_model/Security.pb.h"
|
||||
#include "lib/utils/optional.h"
|
||||
#include <string>
|
||||
|
||||
namespace bts {
|
||||
namespace api {
|
||||
|
||||
std::string product_to_string(const api::Product&);
|
||||
std::optional<api::Product> string_to_product(const std::string&);
|
||||
|
||||
|
||||
} }
|
||||
@@ -0,0 +1,116 @@
|
||||
#ifndef _WIN32
|
||||
#include "MarketDataServiceServer.h"
|
||||
#include "lib/metrics/metrics.h"
|
||||
#include "lib/md/UserSymbol.h"
|
||||
#include "lib/md/get_symbols.h"
|
||||
#include "lib/md/Product.h"
|
||||
#include "lib/utils/AuthToken.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
|
||||
|
||||
namespace bts {
|
||||
namespace md {
|
||||
|
||||
struct MarketDataServiceImpl {
|
||||
MarketDataServiceImpl(api::MarketDataServiceBase::SecuritiesStatefulPublisher);
|
||||
utils::Dispatcher& dispatcher;
|
||||
api::MarketDataServiceBase::SecuritiesStatefulPublisher security_publisher;
|
||||
std::atomic<uint32_t> sec_outstanding;
|
||||
void add_security(const SecurityPtr& sec) {
|
||||
++sec_outstanding;
|
||||
dispatcher.post([this,sec]() {
|
||||
security_publisher.add_or_update(*sec);
|
||||
auto flush = (--sec_outstanding) == 0;
|
||||
if (flush) {
|
||||
security_publisher.flush();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
MarketDataServiceImpl::MarketDataServiceImpl(
|
||||
api::MarketDataServiceBase::SecuritiesStatefulPublisher security_pub) :
|
||||
dispatcher(utils::Dispatcher::get_named("MDService")),
|
||||
security_publisher(security_pub),
|
||||
sec_outstanding(0)
|
||||
{
|
||||
}
|
||||
|
||||
MarketDataService::MarketDataService(
|
||||
MDClient& md,
|
||||
api::ServiceContext ctx,
|
||||
metrics::MetricsContainer&) :
|
||||
api::AuthenticatedMarketDataServiceBase(ctx)
|
||||
{
|
||||
_me = std::make_shared<MarketDataServiceImpl>(make_securities_stateful_publisher());
|
||||
md.current_consumer().on_security([me=_me](const SecurityPtr& sec, MDConsumerHandle) {
|
||||
me->add_security(sec);
|
||||
});
|
||||
}
|
||||
|
||||
// This method duplicates MarketDataRelayServer::request_products()
|
||||
void MarketDataService::get_products(
|
||||
const api::AuthToken&,
|
||||
const ::bts::api::VoidMessage&,
|
||||
const api::Response<::bts::api::ProductList>& reply) {
|
||||
|
||||
api::ProductList list;
|
||||
std::set<md::UserSymbol> syms;
|
||||
md::get_system_md_symbols(syms);
|
||||
for(const auto& i : syms) {
|
||||
auto prod = list.add_products();
|
||||
prod->set_exchange(i.exchange());
|
||||
prod->set_type(i.type());
|
||||
prod->set_symbol(i.symbol());
|
||||
}
|
||||
reply(list);
|
||||
}
|
||||
|
||||
api::StreamAccessResponse authorized(bool is_authorized) {
|
||||
api::StreamAccessResponse r;
|
||||
r.set_authorized(is_authorized);
|
||||
return r;
|
||||
}
|
||||
|
||||
void MarketDataService::allow_securities(
|
||||
const bts::api::AuthToken&,
|
||||
const std::string& qualifier,
|
||||
const api::Response<bts::api::StreamAccessResponse>& reply) {
|
||||
// Non-empty qualifier is invalid and rejected
|
||||
reply(authorized(qualifier.empty()));
|
||||
}
|
||||
|
||||
void MarketDataService::allow_book(
|
||||
const bts::api::AuthToken& token,
|
||||
const std::string& qualifier,
|
||||
const api::Response<bts::api::StreamAccessResponse>& reply) {
|
||||
api::user::TokenPayload payload;
|
||||
if (api::auth::decode_token(token, &payload)) {
|
||||
auto product = api::string_to_product(qualifier);
|
||||
if (product) {
|
||||
// todo: start streaming
|
||||
reply(authorized(true));
|
||||
}
|
||||
}
|
||||
reply(authorized(false));
|
||||
}
|
||||
|
||||
void MarketDataService::allow_trades(
|
||||
const bts::api::AuthToken& token,
|
||||
const std::string& qualifier,
|
||||
const api::Response<bts::api::StreamAccessResponse>& reply) {
|
||||
api::user::TokenPayload payload;
|
||||
if (api::auth::decode_token(token, &payload)) {
|
||||
auto product = api::string_to_product(qualifier);
|
||||
if (product) {
|
||||
// todo: start streaming
|
||||
reply(authorized(true));
|
||||
}
|
||||
}
|
||||
reply(authorized(false));
|
||||
}
|
||||
|
||||
}} //namespace
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "lib/utils/Dispatcher.h"
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/api/services/MarketDataService.server.pb.h"
|
||||
#include "lib/md/MDClient.h"
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace metrics { class MetricsContainer; }
|
||||
namespace md {
|
||||
|
||||
struct MarketDataServiceImpl;
|
||||
|
||||
class MarketDataService : public api::AuthenticatedMarketDataServiceBase {
|
||||
std::shared_ptr<MarketDataServiceImpl> _me;
|
||||
public:
|
||||
MarketDataService(
|
||||
MDClient& md,
|
||||
api::ServiceContext ctx,
|
||||
metrics::MetricsContainer& stats);
|
||||
|
||||
void get_products(
|
||||
const api::AuthToken&,
|
||||
const ::bts::api::VoidMessage&,
|
||||
const api::Response<::bts::api::ProductList>&) override;
|
||||
|
||||
void allow_securities(
|
||||
const bts::api::AuthToken&,
|
||||
const std::string& qualifier,
|
||||
const api::Response<bts::api::StreamAccessResponse>& reply) override;
|
||||
|
||||
void allow_book(
|
||||
const bts::api::AuthToken& token,
|
||||
const std::string& qualifier,
|
||||
const api::Response<bts::api::StreamAccessResponse>& reply) override;
|
||||
|
||||
void allow_trades(
|
||||
const bts::api::AuthToken& token,
|
||||
const std::string& qualifier,
|
||||
const api::Response<bts::api::StreamAccessResponse>& reply) override;
|
||||
};
|
||||
|
||||
}} //namespace
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "ActorThread.h"
|
||||
#include "lib/utils/thread_manager.h"
|
||||
#include "lib/utils/type_name.h"
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
ActorThread::ActorThread(const config::OrdermanCfg::Executor &cfg, metrics::MetricsContainer &stats)
|
||||
: cfg(cfg), thread(), executor(), loop_metric(), done(false)
|
||||
{
|
||||
std::array<double, 30> buckets;
|
||||
auto i = 0;
|
||||
for (auto &b : buckets)
|
||||
b = (i += 1000);
|
||||
|
||||
loop_metric = std::make_shared<metrics::HistogramMetric<30>>(
|
||||
fmt::format("spark_metrics_{}_thread_loop_ns", cfg.thread_name()),
|
||||
buckets);
|
||||
stats.publish(loop_metric);
|
||||
}
|
||||
|
||||
ActorThread::~ActorThread()
|
||||
{
|
||||
done = true;
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void ActorThread::start(trace::PerfLog &log)
|
||||
{
|
||||
if (cfg.poll())
|
||||
thread = threading::make_thread(cfg.thread_name(), [this, &log]()
|
||||
{
|
||||
for (;;)
|
||||
executor.do_work(log);
|
||||
});
|
||||
else
|
||||
{
|
||||
thread = threading::make_thread(cfg.thread_name(), [this, &log]()
|
||||
{
|
||||
const auto inner_iter = 1000;
|
||||
while (!done)
|
||||
{
|
||||
auto start = Timer::now();
|
||||
for (auto i = 0; i < inner_iter; i++)
|
||||
executor.do_work(log);
|
||||
auto stop = Timer::now();
|
||||
loop_metric->observe(ns_diff(stop, start) / inner_iter);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ActorThread* ActorThread::find_thread_for_actor(const std::string& actor_name,
|
||||
const std::vector<std::unique_ptr<ActorThread>>& all_executors,
|
||||
const std::string& qualifier)
|
||||
{
|
||||
for (auto const &ex : all_executors)
|
||||
{
|
||||
for (auto const &hosted : ex->cfg.hosted_actor())
|
||||
{
|
||||
if (hosted.type() == "*" || boost::algorithm::to_lower_copy(hosted.type()) ==
|
||||
boost::algorithm::to_lower_copy(actor_name))
|
||||
{
|
||||
if(qualifier != "")
|
||||
{
|
||||
if(boost::algorithm::to_lower_copy(qualifier) == boost::algorithm::to_lower_copy(hosted.qualifier()))
|
||||
return ex.get();
|
||||
}
|
||||
else
|
||||
return ex.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "lib/mercury/executor/Executor.h"
|
||||
#include "lib/metrics/metrics.h"
|
||||
#include "lib/utils/thread_manager.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/jetfix.pb.h"
|
||||
#include "lib/config/protos/orderman_cfg.pb.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
struct ActorThread
|
||||
{
|
||||
ActorThread(const config::OrdermanCfg::Executor &cfg, metrics::MetricsContainer &stats);
|
||||
|
||||
void start(trace::PerfLog &log);
|
||||
~ActorThread();
|
||||
|
||||
static ActorThread* find_thread_for_actor(const std::string&actor_name,
|
||||
const std::vector<std::unique_ptr<ActorThread>>& all_executors,
|
||||
const std::string& qualifier = "");
|
||||
|
||||
|
||||
config::OrdermanCfg::Executor cfg;
|
||||
std::thread thread;
|
||||
mercury::Executor executor;
|
||||
std::shared_ptr<metrics::HistogramMetric<30>> loop_metric;
|
||||
std::atomic<bool> done;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -59,7 +59,8 @@ void AsyncFactory::init() {
|
||||
response.reserve(ev.n_buffers);
|
||||
while (response.size() < ev.n_buffers)
|
||||
response.push_back(std::make_shared<jetfix::OutFIX>(*ev.cfg));
|
||||
this->publish(me->fix_buffer_stream, std::move(response), ev.cfg);
|
||||
this->publish(me->fix_buffer_stream, std::move(response), ev.cfg,
|
||||
ev.request_qualifier);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class GLBXExecutionGateway : public IExecutionGateway {
|
||||
void on_securities(const mercury::Update<NewSecuritiesEvent> &) override;
|
||||
void on_admin_data(const mercury::Update<NewAdminDataEvent> &) override;
|
||||
void send_fix(jetfix::OutFIXPtr const &fix, trace::CorID trace) override;
|
||||
void send_msgs(std::vector<OrderMsg> const&, trace::CorID) override {bts_assert(false);}
|
||||
|
||||
OnExeReport& on_exe_report() override;
|
||||
OnGwStatusChange& on_gw_status_change() override;
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
#include "lib/orderman/actors/GLBXiLink3ExecutionGateway.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/orderman_cfg.pb.h"
|
||||
#include "lib/jetfix/common/OutFIX.h"
|
||||
#include "lib/orderman/actors/GLBXExecutionGateway.h"
|
||||
#include "lib/orderman/orderman_utils/fix_order_codec.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
struct GLBXiLink3ExecutionGateway::PImpl {
|
||||
PImpl(config::JetFIX::Connection const &cfg, trace::PerfLog &l,
|
||||
md::SecurityOracle o)
|
||||
: cfg(cfg), gw(cfg, l, o) {}
|
||||
config::JetFIX::Connection const cfg;
|
||||
GLBXExecutionGateway gw;
|
||||
};
|
||||
|
||||
GLBXiLink3ExecutionGateway::GLBXiLink3ExecutionGateway(
|
||||
config::JetFIX::Connection const &cfg, trace::PerfLog &log,
|
||||
md::SecurityOracle o)
|
||||
: me(new PImpl(cfg, log, o)) {}
|
||||
|
||||
GLBXiLink3ExecutionGateway::~GLBXiLink3ExecutionGateway() {}
|
||||
|
||||
void GLBXiLink3ExecutionGateway::start() { me->gw.start(); }
|
||||
|
||||
void GLBXiLink3ExecutionGateway::dispatch_incoming_fix_messages() {
|
||||
me->gw.dispatch_incoming_fix_messages();
|
||||
}
|
||||
|
||||
void GLBXiLink3ExecutionGateway::on_securities(
|
||||
const mercury::Update<NewSecuritiesEvent> &ev) {
|
||||
me->gw.on_securities(ev);
|
||||
}
|
||||
|
||||
void GLBXiLink3ExecutionGateway::on_admin_data(
|
||||
const mercury::Update<NewAdminDataEvent> &ev) {
|
||||
me->gw.on_admin_data(ev);
|
||||
}
|
||||
|
||||
static std::string
|
||||
get_cust_order_handling_inst(api::orderman::RoutingRecord const &rr) {
|
||||
if (rr.cust_order_handling_inst().size())
|
||||
return rr.cust_order_handling_inst();
|
||||
else
|
||||
return config::get<config::OrdermanCfg>()
|
||||
.fallback_cust_order_handling_inst();
|
||||
}
|
||||
|
||||
template <class F, class MSG>
|
||||
static void add_clorid(jetfix::FIXFragment &fix,MSG const&msg) {
|
||||
std::string clordid(clordid_sz(), ' ');
|
||||
encode_clorid(msg.key, SecurityKey(msg.security->id()), &clordid);
|
||||
fix.add<F>(clordid);
|
||||
}
|
||||
|
||||
template <class MSG>
|
||||
static void encode_common_fields(jetfix::FIXFragment &fix, MSG const &msg) {
|
||||
using namespace jetfix;
|
||||
using namespace api::orderman;
|
||||
auto &rr = *msg.routing_record;
|
||||
|
||||
fix.add<fix::SenderSubID>(msg.routing_record->operator_id());
|
||||
fix.add<fix::SenderLocationID>(msg.routing_record->location());
|
||||
fix.add<fix::Account>(rr.account());
|
||||
fix.add<fix::Side>(msg.side);
|
||||
fix.add<fix::ManualOrderIndicator>(msg.manual_order_indicator);
|
||||
fix.add<fix::SecurityDesc>(msg.security->glbx().security_desc());
|
||||
add_clorid<fix::ClOrdID>(fix,msg);
|
||||
fix.add<fix::TransactTimeStr>(datetime::FIX_timestamp(datetime::now()));
|
||||
}
|
||||
|
||||
static void encode_create_fields(jetfix::FIXFragment &fix,
|
||||
CreateOrderMsg const &create_fields) {
|
||||
using namespace jetfix;
|
||||
using namespace api::orderman;
|
||||
|
||||
encode_common_fields(fix, create_fields);
|
||||
|
||||
auto &rr = *create_fields.routing_record;
|
||||
fix.add<fix::CustomerOrFirm>(rr.customer_or_firm());
|
||||
fix.add<fix::CtiCode>(rr.cti_code());
|
||||
fix.add<fix::CustOrderHandlingInst>(get_cust_order_handling_inst(rr));
|
||||
if (rr.smp_id().size()) {
|
||||
fix.add<fix::SelfMatchPreventionID>(rr.smp_id());
|
||||
if (rr.smp_instruction() !=
|
||||
api::orderman::SELF_MATCH_PREVENTION_INSTRUCTION_UNDEFINED)
|
||||
fix.add<fix::SelfMatchPreventionInstruction>(rr.smp_instruction());
|
||||
}
|
||||
fix.add<fix::OrderQty>(create_fields.quantity.value());
|
||||
fix.add<fix::OrdType>(create_fields.order_type);
|
||||
fix.add<fix::TimeInForce>(create_fields.time_in_force);
|
||||
|
||||
switch (create_fields.order_type) {
|
||||
case NATIVE_ORDER_TYPE_MARKET:
|
||||
break;
|
||||
case NATIVE_ORDER_TYPE_LIMIT:
|
||||
fix.add<fix::Price>(create_fields.price.value(), 8, -1);
|
||||
break;
|
||||
case NATIVE_ORDER_TYPE_STOP:
|
||||
fix.add<fix::StopPx>(create_fields.stop_px.value(), 8, -1);
|
||||
break;
|
||||
case NATIVE_ORDER_TYPE_STOP_LIMIT:
|
||||
fix.add<fix::Price>(create_fields.price.value(), 8, -1);
|
||||
fix.add<fix::StopPx>(create_fields.stop_px.value(), 8, -1);
|
||||
break;
|
||||
default:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (create_fields.display_quantity.value())
|
||||
fix.add<fix::MaxShow>(create_fields.display_quantity.value());
|
||||
}
|
||||
|
||||
void GLBXiLink3ExecutionGateway::send_msgs(std::vector<OrderMsg> const &msgs,
|
||||
trace::CorID corid) {
|
||||
using namespace jetfix;
|
||||
auto fix = std::make_shared<jetfix::OutFIX>(me->cfg);
|
||||
auto &stream = fix->first_stream();
|
||||
|
||||
for (auto &m : msgs) {
|
||||
|
||||
// NewOrderSingle
|
||||
auto create_ptr = std::get_if<CreateOrderMsg>(&m);
|
||||
if (create_ptr) {
|
||||
auto &create_fix = stream.new_msg(jetfix::MessageTypes::NewOrderSingle);
|
||||
encode_create_fields(create_fix, *create_ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
// CancelReplace
|
||||
auto cancel_replace_ptr = std::get_if<CancelReplaceOrderMsg>(&m);
|
||||
if (cancel_replace_ptr) {
|
||||
auto &update_fix = stream.new_msg(jetfix::MessageTypes::CancelReplace);
|
||||
auto create_ptr = &cancel_replace_ptr->create_fields;
|
||||
encode_create_fields(update_fix, *create_ptr);
|
||||
update_fix.add<fix::OrderIDu64>(
|
||||
cancel_replace_ptr->exchange_order_id.value());
|
||||
add_clorid<fix::OrigClOrdID>(update_fix,*create_ptr);
|
||||
add_clorid<fix::CorrelationClOrdID>(update_fix,*create_ptr);
|
||||
update_fix.add<fix::InFlightMitigation>(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Cancel
|
||||
auto cancel_ptr = std::get_if<CancelOrderMsg>(&m);
|
||||
if (cancel_ptr) {
|
||||
auto &cancel_fix = stream.new_msg(jetfix::MessageTypes::Cancel);
|
||||
encode_common_fields(cancel_fix, *cancel_ptr);
|
||||
cancel_fix.add<fix::OrderIDu64>(cancel_ptr->exchange_order_id.value());
|
||||
add_clorid<fix::OrigClOrdID>(cancel_fix,*cancel_ptr);
|
||||
add_clorid<fix::CorrelationClOrdID>(cancel_fix,*cancel_ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream.close();
|
||||
me->gw.send_fix(fix, corid);
|
||||
}
|
||||
|
||||
OnExeReport &GLBXiLink3ExecutionGateway::on_exe_report() {
|
||||
return me->gw.on_exe_report();
|
||||
}
|
||||
|
||||
OnGwStatusChange &GLBXiLink3ExecutionGateway::on_gw_status_change() {
|
||||
return me->gw.on_gw_status_change();
|
||||
}
|
||||
|
||||
OnCancelReject &GLBXiLink3ExecutionGateway::on_cancel_reject() {
|
||||
return me->gw.on_cancel_reject();
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "IExecutionGateway.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
|
||||
class GLBXiLink3ExecutionGateway : public IExecutionGateway {
|
||||
public:
|
||||
GLBXiLink3ExecutionGateway(config::JetFIX::Connection const &, trace::PerfLog &,
|
||||
md::SecurityOracle);
|
||||
virtual ~GLBXiLink3ExecutionGateway();
|
||||
|
||||
void start() override;
|
||||
void dispatch_incoming_fix_messages() override;
|
||||
void on_securities(const mercury::Update<NewSecuritiesEvent> &) override;
|
||||
void on_admin_data(const mercury::Update<NewAdminDataEvent> &) override;
|
||||
void send_fix(jetfix::OutFIXPtr const &, trace::CorID ) override {bts_assert(false);}
|
||||
void send_msgs(std::vector<OrderMsg> const&, trace::CorID) override;
|
||||
|
||||
OnExeReport& on_exe_report() override;
|
||||
OnGwStatusChange& on_gw_status_change() override;
|
||||
OnCancelReject &on_cancel_reject() override;
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -17,6 +17,7 @@ class ICEExecutionGateway : public IExecutionGateway {
|
||||
void on_securities(const mercury::Update<NewSecuritiesEvent> &) override;
|
||||
void on_admin_data(const mercury::Update<NewAdminDataEvent> &) override;
|
||||
void send_fix(const jetfix::OutFIXPtr& fix, trace::CorID trace) override;
|
||||
void send_msgs(std::vector<OrderMsg> const&, trace::CorID) override {bts_assert(false);}
|
||||
|
||||
OnExeReport& on_exe_report() override;
|
||||
OnGwStatusChange& on_gw_status_change() override;
|
||||
|
||||
@@ -1,17 +1,53 @@
|
||||
|
||||
#include "IExecutionGateway.h"
|
||||
#include "ICEExecutionGateway.h"
|
||||
#include "GLBXExecutionGateway.h"
|
||||
#include "ICEExecutionGateway.h"
|
||||
#include "lib/orderman/actors/gateway/GatewayProxy.h"
|
||||
#include "lib/utils/ValueWrapper.h"
|
||||
#include "lib/orderman/actors/GLBXiLink3ExecutionGateway.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/orderman_cfg.pb.h"
|
||||
#include <utility>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
std::unique_ptr<IExecutionGateway>
|
||||
ExecutionGatewayFactory::create_gateway(const config::JetFIX::Connection& conn, trace::PerfLog& pl, md::SecurityOracle o)
|
||||
std::atomic<bool> ExecutionGatewayFactory::_registration_open = true;
|
||||
std::map<std::string, std::shared_ptr<IExecutionGateway>> ExecutionGatewayFactory::_proxies;
|
||||
|
||||
void ExecutionGatewayFactory::register_proxy(const config::JetFIX::Connection& conn, std::shared_ptr<IExecutionGateway> prox)
|
||||
{
|
||||
if(conn.platform() == bts::config::JetFIX_Connection_JetFIXPlatform_ICE ||
|
||||
conn.platform() == bts::config::JetFIX_Connection_JetFIXPlatform_SIMICE)
|
||||
bts_assert_msg(_registration_open, "attempt to register gw after construction");
|
||||
auto it = _proxies.insert(std::make_pair(conn.name(), prox));
|
||||
bts_assert_msg(it.second, "duplicate gw proxy");
|
||||
}
|
||||
|
||||
std::shared_ptr<IExecutionGateway>
|
||||
ExecutionGatewayFactory::create_gateway(const config::JetFIX::Connection &conn,
|
||||
trace::PerfLog &pl,
|
||||
md::SecurityOracle o)
|
||||
{
|
||||
if(config::get<config::OrdermanCfg>().use_gateway())
|
||||
{
|
||||
_registration_open = false;
|
||||
auto it = _proxies.find(conn.name());
|
||||
bts_assert_msg(it != _proxies.end(), "can't find gw proxy.");
|
||||
return it->second;
|
||||
}
|
||||
else
|
||||
return create_local_gateway(conn, pl, o);
|
||||
}
|
||||
|
||||
std::unique_ptr<IExecutionGateway>
|
||||
ExecutionGatewayFactory::create_local_gateway(const config::JetFIX::Connection &conn,
|
||||
trace::PerfLog &pl,
|
||||
md::SecurityOracle o)
|
||||
{
|
||||
if (conn.platform() == bts::config::JetFIX_Connection_JetFIXPlatform_ICE ||
|
||||
conn.platform() == bts::config::JetFIX_Connection_JetFIXPlatform_SIMICE)
|
||||
return std::make_unique<ICEExecutionGateway>(conn, pl, o);
|
||||
else if (conn.use_ilink3())
|
||||
return std::make_unique<GLBXiLink3ExecutionGateway>(conn, pl, o);
|
||||
else
|
||||
return std::make_unique<GLBXExecutionGateway>(conn, pl, o);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include "lib/utils/bts_no_copy.h"
|
||||
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
|
||||
namespace bts {
|
||||
namespace trace {struct CorID;class PerfLog;}
|
||||
@@ -18,6 +21,38 @@ class NewAdminDataEvent;
|
||||
class ExecutionReport;
|
||||
|
||||
|
||||
struct CreateOrderMsg {
|
||||
OrderKey key;
|
||||
Price price;
|
||||
Quantity quantity;
|
||||
api::Security const *security;
|
||||
api::Side side;
|
||||
Price stop_px;
|
||||
Quantity display_quantity;
|
||||
api::orderman::NativeOrderType order_type;
|
||||
api::orderman::TimeInForce time_in_force;
|
||||
api::orderman::ManualOrderIndicator manual_order_indicator;
|
||||
api::orderman::RoutingRecord const *routing_record;
|
||||
};
|
||||
|
||||
struct CancelReplaceOrderMsg {
|
||||
CreateOrderMsg create_fields;
|
||||
ExchangeOrderIDu64 exchange_order_id;
|
||||
};
|
||||
|
||||
struct CancelOrderMsg {
|
||||
OrderKey key;
|
||||
ExchangeOrderIDu64 exchange_order_id;
|
||||
api::orderman::ManualOrderIndicator manual_order_indicator;
|
||||
std::string_view clordid;
|
||||
api::Security const *security;
|
||||
api::orderman::RoutingRecord const *routing_record;
|
||||
api::Side side;
|
||||
};
|
||||
|
||||
using OrderMsg =
|
||||
std::variant<CreateOrderMsg, CancelReplaceOrderMsg, CancelOrderMsg>;
|
||||
|
||||
using OnGwStatusChange = std::function<void(bool)>;
|
||||
using OnExeReport = std::function<void(ExecutionReport const &, trace::CorID const &)>;
|
||||
using OnCancelReject = std::function<void(OrderKey, std::string const &)>;
|
||||
@@ -33,7 +68,7 @@ class IExecutionGateway {
|
||||
virtual void on_securities(const mercury::Update<NewSecuritiesEvent> &) = 0;
|
||||
virtual void on_admin_data(const mercury::Update<NewAdminDataEvent> &) = 0;
|
||||
virtual void send_fix(const jetfix::OutFIXPtr&, trace::CorID) = 0;
|
||||
|
||||
virtual void send_msgs(std::vector<OrderMsg> const&, trace::CorID) = 0;
|
||||
virtual OnExeReport& on_exe_report() = 0;
|
||||
virtual OnGwStatusChange& on_gw_status_change() = 0;
|
||||
virtual OnCancelReject &on_cancel_reject() = 0;
|
||||
@@ -44,8 +79,16 @@ private:
|
||||
class ExecutionGatewayFactory
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<IExecutionGateway> create_gateway(const config::JetFIX::Connection&, trace::PerfLog&, md::SecurityOracle);
|
||||
|
||||
static std::shared_ptr<IExecutionGateway>
|
||||
create_gateway(const config::JetFIX::Connection&, trace::PerfLog&, md::SecurityOracle);
|
||||
static std::unique_ptr<IExecutionGateway>
|
||||
create_local_gateway(const config::JetFIX::Connection&, trace::PerfLog&, md::SecurityOracle);
|
||||
|
||||
private:
|
||||
friend class GatewayProxy;
|
||||
static void register_proxy(const config::JetFIX::Connection&, std::shared_ptr<IExecutionGateway>);
|
||||
static std::map<std::string, std::shared_ptr<IExecutionGateway>> _proxies;
|
||||
static std::atomic<bool> _registration_open;
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
|
||||
@@ -31,9 +31,9 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
public:
|
||||
MDSource(md::MDConsumer &consumer, metrics::MetricsContainer &stats,
|
||||
trace::PerfLog &log)
|
||||
: _handles(), _status_subs(), _md_consumer(consumer),
|
||||
: _handles(), _pending_handles(), _status_subs(), _md_consumer(consumer),
|
||||
_book_update(), _ticker_update(), _status_update(),
|
||||
_perf_log(log), _stats(stats), _loop_timer() {
|
||||
_perf_log(log), _stats(stats), _loop_timer(), _counter(0) {
|
||||
|
||||
_trades_buffer.Reserve(1000);
|
||||
auto cme_channels = md::get_configured_cme_channels();
|
||||
@@ -169,6 +169,47 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
// Publish stats
|
||||
auto stop = utils::TimeStamp::now();
|
||||
_loop_timer->observe((start - stop).nanoseconds());
|
||||
|
||||
_counter += 1;
|
||||
if (_counter % (2<<20) == 0) {
|
||||
// Invoke periodic work every 2M calls on on_poll
|
||||
// This is approximately 20-30 seconds
|
||||
do_periodic_work();
|
||||
}
|
||||
}
|
||||
|
||||
void do_periodic_work() {
|
||||
if (!_pending_handles.empty()) {
|
||||
for (auto iter = _pending_handles.begin(); iter != _pending_handles.end(); ++iter) {
|
||||
if (iter->handle->is_initialized()) {
|
||||
auto handle = *(iter->handle);
|
||||
LOG(OrderMan, Info, "Found md for {}. Pending handle removed.", handle.key());
|
||||
switch(iter->type) {
|
||||
case MDRequestType::BOOK:
|
||||
_handles.push_back({utils::TimeStamp(), handle,
|
||||
get_external_event_channel(handle.get_usersymbol()),
|
||||
std::make_unique<std::string>(securities::descriptor(
|
||||
handle.get_normalized_security()))});
|
||||
break;
|
||||
case MDRequestType::STATUS:
|
||||
for (auto& h : _status_subs) {
|
||||
// no need to reset status
|
||||
if (h.handle.key() == handle.key()) break;
|
||||
}
|
||||
_status_subs.push_back(
|
||||
StatusRecord{ handle, co2::TRADINGSTATUS_UNKNOWN });
|
||||
case MDRequestType::TICKER:
|
||||
// We aren't currently monitoring for pending ticker requests
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_pending_handles.erase(
|
||||
std::remove_if(_pending_handles.begin(),
|
||||
_pending_handles.end(),
|
||||
[](const auto& h) { return h.handle->is_initialized(); }),
|
||||
_pending_handles.end());
|
||||
}
|
||||
}
|
||||
|
||||
void on_book_sub(SecurityKey sk,
|
||||
@@ -184,14 +225,23 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
|
||||
LOG(OrderMan, Warn,
|
||||
"Starting to publish book updates for security key: {}", k);
|
||||
auto handle = _md_consumer.get_handle(k);
|
||||
|
||||
_handles.push_back({utils::TimeStamp(), handle,
|
||||
get_external_event_channel(handle.get_usersymbol()),
|
||||
std::make_unique<std::string>(securities::descriptor(
|
||||
handle.get_normalized_security()))});
|
||||
if (handle.is_synthetic())
|
||||
synthetic_count_metric.increment();
|
||||
auto mhandle = _md_consumer.get_managed_handle(k);
|
||||
if (mhandle->is_initialized()) {
|
||||
auto handle = *mhandle;
|
||||
_handles.push_back({utils::TimeStamp(), handle,
|
||||
get_external_event_channel(handle.get_usersymbol()),
|
||||
std::make_unique<std::string>(securities::descriptor(
|
||||
handle.get_normalized_security()))});
|
||||
if (handle.is_synthetic())
|
||||
synthetic_count_metric.increment();
|
||||
}
|
||||
else {
|
||||
// A pending handle is a handle for security we are reasonbly sure exists
|
||||
// but we haven't yet found live market data for.
|
||||
LOG(OrderMan, Warn, "No market data for {}. Pending handle added.", k);
|
||||
_pending_handles.push_back({ mhandle, MDRequestType::BOOK });
|
||||
}
|
||||
}
|
||||
|
||||
const char *get_external_event_channel(const md::UserSymbol &user_symbol) {
|
||||
@@ -205,7 +255,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
metrics::CounterMetric<int> &channel_count_metric) {
|
||||
|
||||
auto user_symbol = _md_consumer.get_handle(sk.value()).get_usersymbol();
|
||||
for (auto &ch : _channels)
|
||||
for (auto &ch : _channels) {
|
||||
if (ch.user_symbol == user_symbol) {
|
||||
auto it =
|
||||
std::find_if(ch.securities.begin(), ch.securities.end(),
|
||||
@@ -222,6 +272,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a channel we are not subscribed to
|
||||
channel_count_metric.increment();
|
||||
@@ -250,8 +301,14 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto handle = _md_consumer.get_handle(k);
|
||||
_status_subs.push_back(StatusRecord{ handle, co2::TRADINGSTATUS_UNKNOWN });
|
||||
auto handle = _md_consumer.get_managed_handle(k);
|
||||
if (handle->is_initialized()) {
|
||||
_status_subs.push_back(StatusRecord{ *handle, co2::TRADINGSTATUS_UNKNOWN });
|
||||
}
|
||||
else {
|
||||
LOG(OrderMan, Warn, "No market data for {}. Pending handle added.", k);
|
||||
_pending_handles.push_back({ handle, MDRequestType::STATUS });
|
||||
}
|
||||
}
|
||||
|
||||
struct HandleRecord {
|
||||
@@ -261,6 +318,11 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
std::unique_ptr<std::string> symbol;
|
||||
};
|
||||
boost::container::small_vector<HandleRecord, 100> _handles;
|
||||
struct PendingRecord {
|
||||
std::shared_ptr<md::MDConsumerHandle> handle;
|
||||
MDRequestType type;
|
||||
};
|
||||
boost::container::small_vector<PendingRecord, 4> _pending_handles;
|
||||
|
||||
struct ChannelRecord {
|
||||
int64_t cursor;
|
||||
@@ -285,6 +347,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
metrics::MetricsContainer &_stats;
|
||||
std::shared_ptr<metrics::HistogramMetric<30>> _loop_timer;
|
||||
std::map<md::UserSymbol, std::string> _symbol_to_channel;
|
||||
uint64_t _counter;
|
||||
};
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -0,0 +1,160 @@
|
||||
#include "Gateway.h"
|
||||
|
||||
#include "GatewayProxy.h"
|
||||
#include "lib/mercury/executor/Executor.h"
|
||||
#include "lib/mercury/events/Update.h"
|
||||
#include "lib/mercury/events/Stream.h"
|
||||
#include "lib/orderman/events/NewSecuritiesEvent.h"
|
||||
#include "lib/orderman/events/NewAdminDataEvent.h"
|
||||
#include "lib/orderman/events/SendFixEvent.h"
|
||||
#include "lib/orderman/events/SendOrderMsgsEvent.h"
|
||||
#include "lib/orderman/events/GatewayStartEvent.h"
|
||||
#include "lib/orderman/events/GatewayStatusChangeEvent.h"
|
||||
#include "lib/orderman/events/CancelRejectEvent.h"
|
||||
#include "lib/orderman/events/NewExecutionReportEvent.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_common.h"
|
||||
#include "lib/orderman/orderman_utils/ExecutionReport.h"
|
||||
#include "lib/jetfix/common/OutFIXPtr.h"
|
||||
#include "lib/jetfix/common/OutFIX.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "lib/utils/type_name.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/mercury_cfg.pb.h"
|
||||
#include "lib/config/protos/jetfix.pb.h"
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
void Gateway::spawn_gateways(const std::vector<std::unique_ptr<ActorThread>>& actor_threads,
|
||||
trace::PerfLog& pl, md::SecurityOracle oracle)
|
||||
{
|
||||
if(!config::get<config::OrdermanCfg>().use_gateway())
|
||||
return;
|
||||
bts_assert_msg(config::get<config::OrdermanCfg>().use_gateway(), "invalid gw config");
|
||||
std::map<std::string, config::JetFIX::Connection> connections;
|
||||
for(auto& conn : config::get<config::JetFIX>().connection())
|
||||
connections[conn.name()] = conn;
|
||||
|
||||
bool spawned_one = false;
|
||||
for(auto& e : config::get<config::OrdermanCfg>().executor())
|
||||
{
|
||||
for(auto& h : e.hosted_actor())
|
||||
{
|
||||
if(h.type() == "Gateway")
|
||||
{
|
||||
auto thread = ActorThread::find_thread_for_actor(h.type(), actor_threads, h.qualifier());
|
||||
for(auto& g : config::get<config::GatewayCfg>().gateway())
|
||||
{
|
||||
if(g.qualifier() == h.qualifier())
|
||||
{
|
||||
for(auto& cc : g.connection())
|
||||
{
|
||||
if(cc == "*")
|
||||
{
|
||||
for(auto& allconn : connections)
|
||||
{
|
||||
thread->executor.spawn<Gateway>(allconn.second, pl, oracle);
|
||||
thread->executor.spawn<GatewayProxy>(allconn.second, pl, oracle);
|
||||
spawned_one = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& conn = connections[cc];
|
||||
thread->executor.spawn<Gateway>(conn, pl, oracle);
|
||||
thread->executor.spawn<GatewayProxy>(conn, pl, oracle);
|
||||
connections.erase(cc);
|
||||
spawned_one = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bts_assert_msg(spawned_one, "no Gateway spawned");
|
||||
}
|
||||
|
||||
struct Gateway::PImpl
|
||||
{
|
||||
PImpl(const config::JetFIX::Connection& conn, trace::PerfLog& pl, md::SecurityOracle orc)
|
||||
: gw(ExecutionGatewayFactory::create_local_gateway(conn, pl, orc)),
|
||||
gateway_name(GatewayName(conn.name()))
|
||||
{
|
||||
}
|
||||
std::unique_ptr<IExecutionGateway> gw;
|
||||
GatewayName gateway_name;
|
||||
std::atomic<bool> started;
|
||||
|
||||
mercury::Stream<NewExecutionReportEvent> new_executionreport_pub;
|
||||
mercury::Stream<GatewayStatusChangeEvent> gatewaystatus_pub;
|
||||
mercury::Stream<CancelRejectEvent> cancelreject_pub;
|
||||
};
|
||||
|
||||
Gateway::Gateway(const config::JetFIX::Connection& conn, trace::PerfLog& pl, md::SecurityOracle orc)
|
||||
: me(new PImpl(conn, pl, orc))
|
||||
{
|
||||
}
|
||||
|
||||
Gateway::~Gateway()
|
||||
{
|
||||
}
|
||||
|
||||
void Gateway::init()
|
||||
{
|
||||
me->new_executionreport_pub = make_stream<NewExecutionReportEvent>(me->gateway_name);
|
||||
me->gw->on_exe_report() = [this](const auto& er, const auto& t)
|
||||
{
|
||||
publish(me->new_executionreport_pub, &er, t);
|
||||
};
|
||||
me->gatewaystatus_pub = make_stream<GatewayStatusChangeEvent>(me->gateway_name);
|
||||
me->gw->on_gw_status_change() = [this](bool b)
|
||||
{
|
||||
publish(me->gatewaystatus_pub, me->gateway_name, b);
|
||||
};
|
||||
me->cancelreject_pub = make_stream<CancelRejectEvent>(me->gateway_name);
|
||||
me->gw->on_cancel_reject() = [this](const auto& k,const auto& msg)
|
||||
{
|
||||
publish(me->cancelreject_pub, k, msg);
|
||||
};
|
||||
subscribe<SendFixEvent>(me->gateway_name, [this](const mercury::Update<SendFixEvent>& update) //for GWNAME
|
||||
{
|
||||
if(update.events)
|
||||
for (const auto& ev : *update.events)
|
||||
me->gw->send_fix(ev.fix, ev.corid);
|
||||
});
|
||||
subscribe<SendOrderMsgsEvent>(me->gateway_name, [this](const mercury::Update<SendOrderMsgsEvent>& update)
|
||||
{
|
||||
if(update.events)
|
||||
for (const auto& ev : *update.events)
|
||||
me->gw->send_msgs(*ev.msgs, ev.corid);
|
||||
});
|
||||
|
||||
subscribe<NewAdminDataEvent>([this](const auto& admin_update)
|
||||
{
|
||||
me->gw->on_admin_data(admin_update);
|
||||
});
|
||||
subscribe<NewSecuritiesEvent>([this](const auto& secs)
|
||||
{
|
||||
me->gw->on_securities(secs);
|
||||
});
|
||||
subscribe<GatewayStartEvent>(me->gateway_name, [this](const auto& ev)
|
||||
{
|
||||
if (ev.events && !me->started) //this is kind of a hack because messages go through once at startup with a null events
|
||||
{
|
||||
me->started = true;
|
||||
me->gw->start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Gateway::on_poll(trace::PerfLog&)
|
||||
{
|
||||
me->gw->dispatch_incoming_fix_messages();
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/actors/IExecutionGateway.h"
|
||||
#include "lib/jetfix/common/OutFIXPtr.h"
|
||||
#include "lib/md/SecurityOracle.h"
|
||||
#include "lib/utils/bts_no_copy.h"
|
||||
#include "lib/mercury/actors/Actor.h"
|
||||
#include "lib/mercury/actors/IPollableActor.h"
|
||||
#include "lib/tracing/PerfLog.h"
|
||||
#include "lib/tracing/bts_trace.h"
|
||||
#include "lib/utils/TimeStamp.h"
|
||||
#include "lib/orderman/actors/ActorThread.h"
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace trace {struct CorID;class PerfLog;}
|
||||
namespace orderman {
|
||||
|
||||
class Gateway : public mercury::Actor, public mercury::IPollableActor
|
||||
{
|
||||
public:
|
||||
static void spawn_gateways(const std::vector<std::unique_ptr<ActorThread>>& actor_threads,
|
||||
trace::PerfLog&, md::SecurityOracle);
|
||||
public:
|
||||
Gateway(const config::JetFIX::Connection&, trace::PerfLog&, md::SecurityOracle);
|
||||
virtual ~Gateway();
|
||||
|
||||
void init() override;
|
||||
void on_poll(trace::PerfLog&) override;
|
||||
|
||||
private:
|
||||
BTS_NO_COPY(Gateway);
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,114 @@
|
||||
#include "GatewayProxy.h"
|
||||
|
||||
#include "lib/mercury/broker/BrokerProxy.h"
|
||||
#include "lib/mercury/events/Update.h"
|
||||
#include "lib/mercury/events/Stream.h"
|
||||
#include "lib/orderman/events/NewSecuritiesEvent.h"
|
||||
#include "lib/orderman/events/NewAdminDataEvent.h"
|
||||
#include "lib/orderman/events/SendFixEvent.h"
|
||||
#include "lib/orderman/events/SendOrderMsgsEvent.h"
|
||||
#include "lib/orderman/events/GatewayStatusChangeEvent.h"
|
||||
#include "lib/orderman/events/GatewayStartEvent.h"
|
||||
#include "lib/orderman/events/CancelRejectEvent.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_common.h"
|
||||
#include "lib/jetfix/common/OutFIXPtr.h"
|
||||
#include "lib/jetfix/common/OutFIX.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/config/config.h"
|
||||
|
||||
#include <variant>
|
||||
#include <tuple>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
struct GatewayProxy::PImpl
|
||||
{
|
||||
PImpl(const config::JetFIX::Connection &cfg, trace::PerfLog &l, md::SecurityOracle o)
|
||||
: cfg(cfg), perf_log(l), sec_oracle(o), gateway_name(cfg.name())
|
||||
{
|
||||
}
|
||||
const config::JetFIX::Connection& cfg;
|
||||
trace::PerfLog& perf_log;
|
||||
md::SecurityOracle sec_oracle;
|
||||
GatewayName gateway_name;
|
||||
|
||||
mercury::Stream<GatewayStartEvent> start_pub;
|
||||
mercury::Stream<SendFixEvent> send_fix_pub;
|
||||
mercury::Stream<SendOrderMsgsEvent> send_ordermsgs_pub;
|
||||
|
||||
OnExeReport on_exe_report;
|
||||
OnGwStatusChange on_gw_status_change;
|
||||
OnCancelReject on_cancel_reject;
|
||||
};
|
||||
|
||||
|
||||
GatewayProxy::GatewayProxy(const config::JetFIX::Connection& conn, trace::PerfLog& pl, md::SecurityOracle md)
|
||||
: me(new PImpl(conn, pl, md))
|
||||
{
|
||||
ExecutionGatewayFactory::register_proxy(conn, std::shared_ptr<GatewayProxy>(this));
|
||||
}
|
||||
|
||||
GatewayProxy::~GatewayProxy() { }
|
||||
|
||||
//actor init
|
||||
void GatewayProxy::init()
|
||||
{
|
||||
me->start_pub = make_stream<GatewayStartEvent>(me->gateway_name);
|
||||
me->send_fix_pub = make_stream<SendFixEvent>(me->gateway_name);
|
||||
me->send_ordermsgs_pub = make_stream<SendOrderMsgsEvent>(me->gateway_name);
|
||||
}
|
||||
|
||||
//start the gateway
|
||||
void GatewayProxy::start()
|
||||
{
|
||||
publish<GatewayStartEvent>(me->start_pub, me->gateway_name);
|
||||
}
|
||||
|
||||
void GatewayProxy::dispatch_incoming_fix_messages()
|
||||
{
|
||||
//called on GW thread and sent back through queue
|
||||
}
|
||||
|
||||
void GatewayProxy::on_securities(const mercury::Update<NewSecuritiesEvent>& )
|
||||
{
|
||||
//the actual gateway subscribes to these, no-op here
|
||||
}
|
||||
|
||||
void GatewayProxy::on_admin_data(const mercury::Update<NewAdminDataEvent>& )
|
||||
{
|
||||
//the actual gateway subscribes to these, no-op here
|
||||
}
|
||||
|
||||
void GatewayProxy::send_fix(const jetfix::OutFIXPtr& fixptr, trace::CorID corid)
|
||||
{
|
||||
//jetfix::OutFIXPtr copy(new jetfix::OutFIX(*fixptr)); //copy not necessary see OrderManager::reload_fix_buffer
|
||||
publish<SendFixEvent>(me->send_fix_pub, fixptr, corid);
|
||||
}
|
||||
|
||||
void GatewayProxy::send_msgs(const std::vector<OrderMsg>& msgs, trace::CorID corid)
|
||||
{
|
||||
SendOrderMsgsEvent::MsgsPtr copy(new SendOrderMsgsEvent::Msgs(msgs));
|
||||
publish<SendOrderMsgsEvent>(me->send_ordermsgs_pub, copy, corid);
|
||||
}
|
||||
|
||||
OnExeReport& GatewayProxy::on_exe_report()
|
||||
{
|
||||
bts_assert_msg(false, "GatewayProxy");
|
||||
return me->on_exe_report;
|
||||
}
|
||||
|
||||
OnGwStatusChange& GatewayProxy::on_gw_status_change()
|
||||
{
|
||||
bts_assert_msg(false, "GatewayProxy");
|
||||
return me->on_gw_status_change;
|
||||
}
|
||||
|
||||
OnCancelReject& GatewayProxy::on_cancel_reject()
|
||||
{
|
||||
bts_assert_msg(false, "GatewayProxy");
|
||||
return me->on_cancel_reject;
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/actors/IExecutionGateway.h"
|
||||
#include "lib/mercury/actors/Actor.h"
|
||||
#include "lib/jetfix/common/OutFIXPtr.h"
|
||||
#include "lib/md/SecurityOracle.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/jetfix.pb.h"
|
||||
#include "lib/utils/bts_no_copy.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace trace {struct CorID;class PerfLog;}
|
||||
namespace orderman {
|
||||
|
||||
class GatewayProxy : public mercury::Actor, public IExecutionGateway
|
||||
{
|
||||
public:
|
||||
GatewayProxy(const config::JetFIX::Connection&, trace::PerfLog&, md::SecurityOracle);
|
||||
virtual ~GatewayProxy();
|
||||
|
||||
//Actor
|
||||
virtual void init();
|
||||
|
||||
//IExecutionGateway
|
||||
virtual void start();
|
||||
virtual void dispatch_incoming_fix_messages();
|
||||
virtual void on_securities(const mercury::Update<NewSecuritiesEvent> &);
|
||||
virtual void on_admin_data(const mercury::Update<NewAdminDataEvent> &);
|
||||
virtual void send_fix(const jetfix::OutFIXPtr&, trace::CorID);
|
||||
virtual void send_msgs(std::vector<OrderMsg> const&, trace::CorID);
|
||||
|
||||
virtual OnExeReport& on_exe_report();
|
||||
virtual OnGwStatusChange& on_gw_status_change();
|
||||
virtual OnCancelReject &on_cancel_reject();
|
||||
private:
|
||||
BTS_NO_COPY(GatewayProxy);
|
||||
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "IL3ConnectionHandler.h"
|
||||
#include <string>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
struct IL3Connection {
|
||||
virtual void connect(IL3ConnectionHandler&) = 0;
|
||||
virtual void disconnect() = 0;
|
||||
// Switch connection but do not connect()
|
||||
virtual void failover() = 0;
|
||||
virtual void send(const std::string_view) = 0;
|
||||
};
|
||||
|
||||
}}}//namespace
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
struct IL3ConnectionHandler {
|
||||
virtual void on_connect_success() = 0;
|
||||
virtual void on_disconnect() = 0;
|
||||
// Send available data and length. Handler will return what it consumed
|
||||
virtual size_t on_data(const std::string_view&) = 0;
|
||||
};
|
||||
|
||||
}}}//namespace
|
||||
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "IL3GeneratedFull.h"
|
||||
#include "IL3MessageFramer.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
template <class Handler, class T>
|
||||
void handle(Handler& h, const char* data, uint16_t len) {
|
||||
h.handle(
|
||||
T {
|
||||
const_cast<char*>(data),
|
||||
sizeof(FramingHeader) + iLinkBinary::MessageHeader::encodedLength(),
|
||||
len,
|
||||
T::sbeBlockLength(),
|
||||
T::sbeSchemaVersion() });
|
||||
}
|
||||
|
||||
|
||||
template <class H>
|
||||
void dispatch_message(H& handler, const FramingHeader* header, const char* data) {
|
||||
using namespace iLinkBinary;
|
||||
bts_assert_d(header->encoding_type == 0XCAFE);
|
||||
MessageHeader mheader {
|
||||
(char*)data, sizeof(FramingHeader), header->message_size,
|
||||
MessageHeader::sbeSchemaVersion() };
|
||||
|
||||
switch(mheader.templateId()) {
|
||||
case 501: handle<H, NegotiationResponse501>(handler, data, header->message_size); break;
|
||||
case 502: handle<H, NegotiationReject502>(handler, data, header->message_size); break;
|
||||
case 504: handle<H, EstablishmentAck504>(handler, data, header->message_size); break;
|
||||
case 505: handle<H, EstablishmentReject505>(handler, data, header->message_size); break;
|
||||
case 506: handle<H, Sequence506>(handler, data, header->message_size); break;
|
||||
case 507: handle<H, Terminate507>(handler, data, header->message_size); break;
|
||||
case 509: handle<H, Retransmission509>(handler, data, header->message_size); break;
|
||||
case 510: handle<H, RetransmitReject510>(handler, data, header->message_size); break;
|
||||
case 513: handle<H, NotApplied513>(handler, data, header->message_size); break;
|
||||
case 519: handle<H, PartyDetailsDefinitionRequestAck519>(handler, data, header->message_size); break;
|
||||
case 521: handle<H, BusinessReject521>(handler, data, header->message_size); break;
|
||||
case 522: handle<H, ExecutionReportNew522>(handler, data, header->message_size); break;
|
||||
case 523: handle<H, ExecutionReportReject523>(handler, data, header->message_size); break;
|
||||
case 524: handle<H, ExecutionReportElimination524>(handler, data, header->message_size); break;
|
||||
case 525: handle<H, ExecutionReportTradeOutright525>(handler, data, header->message_size); break;
|
||||
case 526: handle<H, ExecutionReportTradeSpread526>(handler, data, header->message_size); break;
|
||||
case 527: handle<H, ExecutionReportTradeSpreadLeg527>(handler, data, header->message_size); break;
|
||||
case 531: handle<H, ExecutionReportModify531>(handler, data, header->message_size); break;
|
||||
case 532: handle<H, ExecutionReportStatus532>(handler, data, header->message_size); break;
|
||||
case 534: handle<H, ExecutionReportCancel534>(handler, data, header->message_size); break;
|
||||
case 535: handle<H, OrderCancelReject535>(handler, data, header->message_size); break;
|
||||
case 536: handle<H, OrderCancelReplaceReject536>(handler, data, header->message_size); break;
|
||||
// case 538: handle<H, PartyDetailsListReport538>(handler, data, header->message_size); break;
|
||||
case 539: handle<H, ExecutionAck539>(handler, data, header->message_size); break;
|
||||
// case 544: handle<H, NewOrderCross544>(handler, data, header->message_size); break;
|
||||
// case 545: handle<H, MassQuoteAck545>(handler, data, header->message_size); break;
|
||||
// case 546: handle<H, RequestForQuoteAck546>(handler, data, header->message_size); break;
|
||||
// case 547: handle<H, QuoteCancelAck547>(handler, data, header->message_size); break;
|
||||
case 548: handle<H, ExecutionReportTradeAddendumOutright548>(handler, data, header->message_size); break;
|
||||
case 549: handle<H, ExecutionReportTradeAddendumSpread549>(handler, data, header->message_size); break;
|
||||
case 550: handle<H, ExecutionReportTradeAddendumSpreadLeg550>(handler, data, header->message_size); break;
|
||||
// case 558: handle<H, iLinkBinary::OrderMassActionReport558>(handler, data, header->message_size); break;
|
||||
// case 561: handle<H, iLinkBinary::SecurityDefinitionResponse561>(handler, data, header->message_size); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This handler handles exchange messages for testing
|
||||
template <class H>
|
||||
void dispatch_exchange_message(H& handler, const FramingHeader* header, const char* data) {
|
||||
using namespace iLinkBinary;
|
||||
bts_assert_d(header->encoding_type == 0XCAFE);
|
||||
MessageHeader mheader {
|
||||
(char*)data, sizeof(FramingHeader), header->message_size,
|
||||
MessageHeader::sbeSchemaVersion() };
|
||||
// Rather than trying to be exhaustive like the client
|
||||
// we will only add handlers for the messages we use for testing
|
||||
switch(mheader.templateId()) {
|
||||
case 500: handle<H, Negotiate500>(handler, data, header->message_size); break;
|
||||
case 503: handle<H, Establish503>(handler, data, header->message_size); break;
|
||||
case 506: handle<H, Sequence506>(handler, data, header->message_size); break;
|
||||
case 508: handle<H, RetransmitRequest508>(handler, data, header->message_size); break;
|
||||
case 518: handle<H, PartyDetailsDefinitionRequest518>(handler, data, header->message_size); break;
|
||||
case 514: handle<H, NewOrderSingle514>(handler, data, header->message_size); break;
|
||||
case 515: handle<H, OrderCancelReplaceRequest515>(handler, data, header->message_size); break;
|
||||
case 516: handle<H, OrderCancelRequest516>(handler, data, header->message_size); break;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Handler, class Dispatcher>
|
||||
size_t consume_messages(
|
||||
Handler& handler, const char* data, size_t len, Dispatcher dispatch) {
|
||||
// void(*dispatch)(Handler&, const FramingHeader*, const char*)) {
|
||||
size_t read = 0;
|
||||
while(len > sizeof(FramingHeader)) {
|
||||
// read the FramingHeader
|
||||
const FramingHeader* header = reinterpret_cast<const FramingHeader*>(data);
|
||||
// Check if we can consume the message
|
||||
auto msize = header->message_size;
|
||||
if (msize <= len) {
|
||||
read += msize;
|
||||
dispatch(handler, header, data);
|
||||
data += msize;
|
||||
len -= msize;
|
||||
}
|
||||
else {
|
||||
return read;
|
||||
}
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
}}}//namespace
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Message templates
|
||||
|
||||
// BTS -> CME
|
||||
#include "generated/Negotiate500.h"
|
||||
#include "generated/Establish503.h"
|
||||
#include "generated/RetransmitRequest508.h"
|
||||
#include "generated/NewOrderSingle514.h"
|
||||
#include "generated/OrderCancelReplaceRequest515.h"
|
||||
#include "generated/OrderCancelRequest516.h"
|
||||
// #include "generated/MassQuote517.h"
|
||||
#include "generated/PartyDetailsDefinitionRequest518.h"
|
||||
// #include "generated/QuoteCancel528.h"
|
||||
// #include "generated/OrderMassActionRequest529.h"
|
||||
// #include "generated/OrderMassStatusRequest530.h"
|
||||
// #include "generated/OrderStatusRequest533.h"
|
||||
// #include "generated/PartyDetailsListRequest537.h"
|
||||
// #include "generated/RequestForQuote543.h"
|
||||
// #include "generated/SecurityDefinitionRequest560.h"
|
||||
|
||||
// CME -> BTS
|
||||
#include "generated/NegotiationResponse501.h"
|
||||
#include "generated/NegotiationReject502.h"
|
||||
#include "generated/EstablishmentAck504.h"
|
||||
#include "generated/EstablishmentReject505.h"
|
||||
#include "generated/Sequence506.h"
|
||||
#include "generated/Terminate507.h"
|
||||
#include "generated/Retransmission509.h"
|
||||
#include "generated/RetransmitReject510.h"
|
||||
#include "generated/NotApplied513.h"
|
||||
#include "generated/PartyDetailsDefinitionRequestAck519.h"
|
||||
#include "generated/BusinessReject521.h"
|
||||
#include "generated/ExecutionReportNew522.h"
|
||||
#include "generated/ExecutionReportReject523.h"
|
||||
#include "generated/ExecutionReportElimination524.h"
|
||||
#include "generated/ExecutionReportTradeOutright525.h"
|
||||
#include "generated/ExecutionReportTradeSpread526.h"
|
||||
#include "generated/ExecutionReportTradeSpreadLeg527.h"
|
||||
#include "generated/ExecutionReportModify531.h"
|
||||
#include "generated/ExecutionReportStatus532.h"
|
||||
#include "generated/ExecutionReportCancel534.h"
|
||||
#include "generated/OrderCancelReject535.h"
|
||||
#include "generated/OrderCancelReplaceReject536.h"
|
||||
// #include "generated/PartyDetailsListReport538.h"
|
||||
#include "generated/ExecutionAck539.h"
|
||||
// #include "generated/NewOrderCross544.h"
|
||||
// #include "generated/MassQuoteAck545.h"
|
||||
// #include "generated/RequestForQuoteAck546.h"
|
||||
// #include "generated/QuoteCancelAck547.h"
|
||||
#include "generated/ExecutionReportTradeAddendumOutright548.h"
|
||||
#include "generated/ExecutionReportTradeAddendumSpread549.h"
|
||||
#include "generated/ExecutionReportTradeAddendumSpreadLeg550.h"
|
||||
// #include "generated/OrderMassActionReport558.h"
|
||||
// #include "generated/SecurityDefinitionResponse561.h"
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
namespace iLinkBinary {
|
||||
class Negotiate500;
|
||||
class Establish503;
|
||||
class RetransmitRequest508;
|
||||
class NewOrderSingle514;
|
||||
class OrderCancelReplaceRequest515;
|
||||
class OrderCancelRequest516;
|
||||
class PartyDetailsDefinitionRequest518;
|
||||
class NegotiationResponse501;
|
||||
class NegotiationReject502;
|
||||
class EstablishmentAck504;
|
||||
class EstablishmentReject505;
|
||||
class Sequence506;
|
||||
class Terminate507;
|
||||
class Retransmission509;
|
||||
class RetransmitReject510;
|
||||
class NotApplied513;
|
||||
class PartyDetailsDefinitionRequestAck519;
|
||||
class BusinessReject521;
|
||||
class ExecutionReportNew522;
|
||||
class ExecutionReportReject523;
|
||||
class ExecutionReportElimination524;
|
||||
class ExecutionReportTradeOutright525;
|
||||
class ExecutionReportTradeSpread526;
|
||||
class ExecutionReportTradeSpreadLeg527;
|
||||
class ExecutionReportModify531;
|
||||
class ExecutionReportStatus532;
|
||||
class ExecutionReportCancel534;
|
||||
class OrderCancelReject535;
|
||||
class OrderCancelReplaceReject536;
|
||||
class ExecutionAck539;
|
||||
class ExecutionReportTradeAddendumOutright548;
|
||||
class ExecutionReportTradeAddendumSpread549;
|
||||
class ExecutionReportTradeAddendumSpreadLeg550;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include "IL3Connection.h"
|
||||
|
||||
#include "generated/MessageHeader.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
struct FramingHeader {
|
||||
uint16_t message_size;
|
||||
uint16_t encoding_type;
|
||||
};
|
||||
|
||||
class IL3MessageFramer {
|
||||
size_t _buffer_size;
|
||||
size_t _next;
|
||||
std::unique_ptr<char[]> _buffer;
|
||||
|
||||
public:
|
||||
IL3MessageFramer():
|
||||
_buffer_size(16384),
|
||||
_next(0),
|
||||
_buffer(new char[_buffer_size])
|
||||
{
|
||||
memset(_buffer.get(), 0, _buffer_size);
|
||||
}
|
||||
~IL3MessageFramer() = default;
|
||||
|
||||
//
|
||||
// Framing
|
||||
template <class Msg>
|
||||
Msg frame(size_t extra=0) {
|
||||
auto size =
|
||||
sizeof(FramingHeader) +
|
||||
iLinkBinary::MessageHeader::encodedLength() +
|
||||
Msg::sbeBlockLength() +
|
||||
extra;
|
||||
auto end = _next + size;
|
||||
if (end > _buffer_size) {
|
||||
// Guaranteed to be enough space in buffer by after the realloc call
|
||||
reallocate(end);
|
||||
}
|
||||
bts_assert_d(_buffer[_next] == 0);
|
||||
FramingHeader* fhead = reinterpret_cast<FramingHeader*>(&_buffer[_next]);
|
||||
fhead->message_size = size;
|
||||
fhead->encoding_type = 0xCAFE;
|
||||
Msg msg;
|
||||
msg.wrapAndApplyHeader(&_buffer[_next], sizeof(FramingHeader), size+sizeof(FramingHeader));
|
||||
_next = end;
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool overflow() const {
|
||||
return _buffer[_next] != 0;
|
||||
}
|
||||
|
||||
void flush(IL3Connection& connection) {
|
||||
if (_next > 0) {
|
||||
std::string_view data(_buffer.get(), _next);
|
||||
connection.send(data);
|
||||
// If we're careful to set every field we could technically avoid this memset (NARRATOR: they are not that careful)
|
||||
memset(_buffer.get(), 0, _next);
|
||||
_next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void reallocate(size_t sz) {
|
||||
sz = std::max(sz, _buffer_size*2);
|
||||
std::unique_ptr<char[]> new_buffer(new char[sz]);
|
||||
std::memcpy(new_buffer.get(), _buffer.get(), _buffer_size);
|
||||
_buffer_size = sz;
|
||||
_buffer.swap(new_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
}}} //namespace
|
||||
@@ -0,0 +1,198 @@
|
||||
#include "IL3Reactor.h"
|
||||
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "IL3ReceiveBuffer.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <netinet/tcp.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
#include <queue>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
struct Reactor::Connection : public IL3Connection {
|
||||
int epoll_fd;
|
||||
int fd;
|
||||
bool is_connected;
|
||||
IL3ConnectionHandler* handler;
|
||||
std::queue<TCPEndpoint> end_points;
|
||||
IL3ReceiveBuffer rc_buffer;
|
||||
|
||||
Connection(int epoll_fd, const std::vector<TCPEndpoint>& end_points):
|
||||
epoll_fd(epoll_fd), fd(0), is_connected(false), handler(nullptr),
|
||||
end_points(std::deque<TCPEndpoint> { end_points.begin(), end_points.end() }),
|
||||
rc_buffer()
|
||||
{ }
|
||||
~Connection() {
|
||||
if (is_connected) disconnect();
|
||||
}
|
||||
|
||||
void connect(IL3ConnectionHandler& h) override {
|
||||
bts_assert(!is_connected);
|
||||
handler = &h;
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
int flag = 1;
|
||||
auto result = ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int));
|
||||
bts_assert(result == 0);
|
||||
bts_assert(fd >= 0);
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
memset(&serv_addr, '0', sizeof(serv_addr));
|
||||
|
||||
auto end_point = current_end_point();
|
||||
auto& host = end_point.host;
|
||||
auto port = end_point.port;
|
||||
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_port = htons(port);
|
||||
result = inet_pton(AF_INET, host.c_str(), &serv_addr.sin_addr);
|
||||
bts_assert(result > 0);
|
||||
|
||||
result = ::connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
|
||||
if (result < 0) {
|
||||
auto er = errno;
|
||||
char msg[256];
|
||||
LOG(OrderMan, Error, "Failed to connect: {} {}:{}",
|
||||
std::string(strerror_r(er, msg, sizeof(msg))),host,port);
|
||||
disconnect();
|
||||
}
|
||||
else {
|
||||
int flag = 1;
|
||||
result = ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag,
|
||||
sizeof(int));
|
||||
bts_assert(result == 0);
|
||||
|
||||
epoll_event ev;
|
||||
ev.events = EPOLLIN | EPOLLHUP | EPOLLERR | EPOLLRDHUP;
|
||||
ev.data.ptr = this;
|
||||
auto result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
|
||||
bts_assert_msg(result == 0, "epoll");
|
||||
is_connected = true;
|
||||
handler->on_connect_success();
|
||||
}
|
||||
}
|
||||
|
||||
void disconnect() override {
|
||||
if (fd) close(fd);
|
||||
fd = 0;
|
||||
is_connected = false;
|
||||
handler->on_disconnect();
|
||||
}
|
||||
|
||||
void send(const std::string_view data) override {
|
||||
if (!is_connected) {
|
||||
LOG(OrderMan, Error, "Attempted to send on closed connection. {}:{}", current_end_point().host, current_end_point().port);
|
||||
return;
|
||||
}
|
||||
auto result = write(fd, data.data(), data.size());
|
||||
if (result < 0) {
|
||||
auto er = errno;
|
||||
char msg[256];
|
||||
auto host = current_end_point().host;
|
||||
auto port = current_end_point().port;
|
||||
LOG(OrderMan, Warn, "Could not write to reactor connection. {}:{}",
|
||||
std::string(strerror_r(er, msg, sizeof(msg))),host,port);
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void on_data(const std::string_view data) {
|
||||
bts_assert_d(is_connected);
|
||||
// Copy input data into a permanent buffer
|
||||
rc_buffer.append(data);
|
||||
bts_assert_d(rc_buffer.unread() > 0);
|
||||
auto read = handler->on_data(rc_buffer.data());
|
||||
rc_buffer.advance(read);
|
||||
}
|
||||
|
||||
void failover() override {
|
||||
bts_assert(!is_connected);
|
||||
next_end_point();
|
||||
}
|
||||
|
||||
TCPEndpoint current_end_point() {
|
||||
return end_points.front();
|
||||
}
|
||||
|
||||
void next_end_point() {
|
||||
auto next = end_points.front();
|
||||
end_points.pop();
|
||||
end_points.push(next);
|
||||
}
|
||||
};
|
||||
|
||||
struct Reactor::PImpl {
|
||||
int epoll_fd = -1;
|
||||
std::vector<std::unique_ptr<Connection>> connections;
|
||||
};
|
||||
|
||||
Reactor::Reactor() : me(new PImpl()) {
|
||||
me->epoll_fd = epoll_create(100);
|
||||
bts_assert(me->epoll_fd >= 0);
|
||||
}
|
||||
Reactor::~Reactor() {
|
||||
me->connections.clear();
|
||||
close(me->epoll_fd);
|
||||
me->epoll_fd = -1;
|
||||
}
|
||||
|
||||
IL3Connection& Reactor::register_connection(const std::vector<TCPEndpoint>& end_points) {
|
||||
me->connections.emplace_back(
|
||||
std::make_unique<Connection>(me->epoll_fd, end_points));
|
||||
return *me->connections.back();
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void non_inline_assert() {
|
||||
bts_assert(false);
|
||||
}
|
||||
|
||||
constexpr bool has_error(int events) {
|
||||
return events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP);
|
||||
}
|
||||
|
||||
void Reactor::do_work() {
|
||||
std::array<epoll_event, 100> ev_list;
|
||||
auto ready = epoll_wait(me->epoll_fd, ev_list.data(), ev_list.size(), 0);
|
||||
if (ready == -1) {
|
||||
if (errno == EINTR) return;
|
||||
else non_inline_assert();
|
||||
}
|
||||
|
||||
for (int i = 0; i < ready; ++i) {
|
||||
auto& ev = ev_list[i];
|
||||
auto handler = (Connection*)(ev.data.ptr);
|
||||
if (ev.events & EPOLLIN) {
|
||||
char buffer[4096];
|
||||
auto sz = read(handler->fd, buffer, sizeof(buffer));
|
||||
if (sz > 0) {
|
||||
bts_assert_d(handler->is_connected);
|
||||
handler->on_data(std::string_view(buffer, sz));
|
||||
}
|
||||
else if (has_error(ev.events)) {
|
||||
handler->disconnect();
|
||||
}
|
||||
}
|
||||
else if (has_error(ev.events)) {
|
||||
// if EPOLLIN was set along with
|
||||
// EPOLLHUP/EPOLLERR/EPOLLRDHUP, we pretend it is a normal
|
||||
// read so that we drain the bytes in the socket. The error
|
||||
// or hup condition would remain and we would catch in the
|
||||
// next iteration.
|
||||
handler->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}} //namespace
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "IL3Connection.h"
|
||||
#include "IL3ConnectionHandler.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
struct TCPEndpoint {
|
||||
std::string host;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
class Reactor {
|
||||
struct PImpl;
|
||||
struct Connection;
|
||||
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
public:
|
||||
Reactor();
|
||||
~Reactor();
|
||||
|
||||
IL3Connection& register_connection(const std::vector<TCPEndpoint>&);
|
||||
|
||||
void do_work();
|
||||
};
|
||||
|
||||
}}} //namespace
|
||||
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
class IL3ReceiveBuffer {
|
||||
std::list<std::vector<char>> buffers;
|
||||
size_t read_pos, write_pos;
|
||||
size_t buffer_size;
|
||||
|
||||
public:
|
||||
IL3ReceiveBuffer(size_t size = 1024*1024) : buffers(), read_pos(0), write_pos(0), buffer_size(size) {
|
||||
add_buffer(size);
|
||||
}
|
||||
|
||||
void append(const std::string_view& data) {
|
||||
// If we have enough space append the data
|
||||
if (data.size() <= available_write_space()) {
|
||||
void* write_ptr = buffers.back().data() + write_pos;
|
||||
memcpy(write_ptr, data.data(), data.size());
|
||||
write_pos += data.size();
|
||||
}
|
||||
else {
|
||||
// If we don't have enough space...
|
||||
// Compute the minimum size of the new buffer
|
||||
auto min_size = unread() + data.size();
|
||||
// allocate a new buffer
|
||||
const auto& prev_buffer = buffers.back();
|
||||
auto prev_read = read_pos;
|
||||
auto prev_write = write_pos;
|
||||
add_buffer(std::max(buffer_size, min_size));
|
||||
// copy unread portion of old buffer + the new data into the new buffer
|
||||
auto unread_sz = prev_write - prev_read;
|
||||
memcpy(buffers.back().data(), prev_buffer.data()+prev_read, unread_sz);
|
||||
write_pos = unread_sz;
|
||||
bts_assert_d(data.size() <= available_write_space());
|
||||
append(data);
|
||||
}
|
||||
}
|
||||
|
||||
void advance(size_t consumed) {
|
||||
read_pos += consumed;
|
||||
bts_assert_d(read_pos <= write_pos);
|
||||
}
|
||||
|
||||
size_t unread() const {
|
||||
return write_pos - read_pos;
|
||||
}
|
||||
|
||||
const std::string_view data() const {
|
||||
return std::string_view(buffers.back().data()+read_pos, unread());
|
||||
}
|
||||
|
||||
private:
|
||||
size_t available_write_space() const {
|
||||
return buffers.back().size() - write_pos;
|
||||
}
|
||||
|
||||
void add_buffer(size_t size) {
|
||||
buffers.emplace_back(std::vector<char>());
|
||||
buffers.back().resize(size);
|
||||
read_pos = write_pos = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}}} //namespace
|
||||
@@ -0,0 +1,57 @@
|
||||
#if 0
|
||||
#include "ILink3ExecutionGateway.h"
|
||||
#include "ILink3Utils.h"
|
||||
#include "ILinkGW.h"
|
||||
#include "lib/logging/logging.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
enum class State {
|
||||
INIT,
|
||||
UNRECOVERABLE
|
||||
};
|
||||
|
||||
struct ILink3ExecutionGateway::PImpl {
|
||||
State state;
|
||||
ILinkConnection& con;
|
||||
trace::PerfLog& perf;
|
||||
md::SecurityOracle oracle;
|
||||
|
||||
OnExeReport on_exe_report;
|
||||
OnGwStatusChange on_gw_status_change;
|
||||
OnCancelReject on_cancel_reject;
|
||||
};
|
||||
|
||||
ILink3ExecutionGateway::ILink3ExecutionGateway(
|
||||
ILinkConnection& c,
|
||||
trace::PerfLog& p,
|
||||
md::SecurityOracle o) :
|
||||
me(new PImpl{State::INIT, c, p, o, {}, {}, {}})
|
||||
{
|
||||
me->state = State::INIT;
|
||||
}
|
||||
|
||||
void ILink3ExecutionGateway::start() {
|
||||
}
|
||||
void ILink3ExecutionGateway::dispatch_incoming_fix_messages() {
|
||||
}
|
||||
void ILink3ExecutionGateway::on_securities(const mercury::Update<NewSecuritiesEvent>&) {
|
||||
}
|
||||
void ILink3ExecutionGateway::on_admin_data(const mercury::Update<NewAdminDataEvent>&) {
|
||||
}
|
||||
void ILink3ExecutionGateway::send_fix(const jetfix::OutFIXPtr&, trace::CorID) {
|
||||
LOG(OrderMan, Error, "ILink3Gateway cannot accept fix");
|
||||
me->state = State::UNRECOVERABLE;
|
||||
me->on_gw_status_change(false);
|
||||
}
|
||||
void ILink3ExecutionGateway::send_msgs(std::vector<OrderMsg> const&, trace::CorID) {
|
||||
// the real one
|
||||
}
|
||||
|
||||
OnExeReport& ILink3ExecutionGateway::on_exe_report() { return me->on_exe_report; }
|
||||
OnGwStatusChange& ILink3ExecutionGateway::on_gw_status_change() { return me->on_gw_status_change; }
|
||||
OnCancelReject& ILink3ExecutionGateway::on_cancel_reject() { return me->on_cancel_reject; }
|
||||
|
||||
}} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "../IExecutionGateway.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
struct ILinkConnection;
|
||||
|
||||
struct ILink3ExecutionGateway : public IExecutionGateway {
|
||||
ILink3ExecutionGateway(
|
||||
ILinkConnection&,
|
||||
trace::PerfLog&,
|
||||
md::SecurityOracle);
|
||||
virtual ~ILink3ExecutionGateway() {};
|
||||
|
||||
void start() override;
|
||||
void dispatch_incoming_fix_messages() override;
|
||||
void on_securities(const mercury::Update<NewSecuritiesEvent> &) override;
|
||||
void on_admin_data(const mercury::Update<NewAdminDataEvent> &) override;
|
||||
void send_fix(const jetfix::OutFIXPtr&, trace::CorID) override;
|
||||
void send_msgs(std::vector<OrderMsg> const&, trace::CorID) override;
|
||||
|
||||
OnExeReport& on_exe_report() override;
|
||||
OnGwStatusChange& on_gw_status_change() override;
|
||||
OnCancelReject &on_cancel_reject() override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "ILink3Utils.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
iLinkBinary::SideReq::Value map_side(api::Side side) {
|
||||
switch(side) {
|
||||
case api::Side::SIDE_BUY:
|
||||
return iLinkBinary::SideReq::Buy;
|
||||
case api::Side::SIDE_SELL:
|
||||
return iLinkBinary::SideReq::Sell;
|
||||
default:
|
||||
return iLinkBinary::SideReq::NULL_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
iLinkBinary::OrderTypeReq::Value map_order_type(api::orderman::NativeOrderType ot) {
|
||||
using namespace api::orderman;
|
||||
using namespace iLinkBinary;
|
||||
switch(ot) {
|
||||
case NativeOrderType::NATIVE_ORDER_TYPE_MARKET:
|
||||
return OrderTypeReq::MarketwithProtection;
|
||||
case NativeOrderType::NATIVE_ORDER_TYPE_LIMIT:
|
||||
return OrderTypeReq::Limit;
|
||||
case NativeOrderType::NATIVE_ORDER_TYPE_STOP:
|
||||
return OrderTypeReq::StopwithProtection;
|
||||
case NativeOrderType::NATIVE_ORDER_TYPE_STOP_LIMIT:
|
||||
return OrderTypeReq::StopLimit;
|
||||
case NativeOrderType::NATIVE_ORDER_TYPE_MARKET_LIMIT:
|
||||
return OrderTypeReq::MarketWithLeftoverAsLimit;
|
||||
default:
|
||||
return iLinkBinary::OrderTypeReq::NULL_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
iLinkBinary::TimeInForce::Value map_time_in_force(api::orderman::TimeInForce tif) {
|
||||
using namespace api::orderman;
|
||||
switch(tif) {
|
||||
case TimeInForce::TIME_IN_FORCE_DAY:
|
||||
return iLinkBinary::TimeInForce::Day;
|
||||
case TimeInForce::TIME_IN_FORCE_GTC:
|
||||
return iLinkBinary::TimeInForce::GoodTillCancel;
|
||||
case TimeInForce::TIME_IN_FORCE_IOC:
|
||||
return iLinkBinary::TimeInForce::FillOrKill;
|
||||
default:
|
||||
return iLinkBinary::TimeInForce::NULL_VALUE;
|
||||
}
|
||||
}
|
||||
iLinkBinary::ManualOrdIndReq::Value map_manual_order_indicator(api::orderman::ManualOrderIndicator moi) {
|
||||
using namespace api::orderman;
|
||||
using namespace iLinkBinary;
|
||||
switch(moi) {
|
||||
case ManualOrderIndicator::MANUAL_ORDER_INDICATOR_NO:
|
||||
return ManualOrdIndReq::Automated;
|
||||
case ManualOrderIndicator::MANUAL_ORDER_INDICATOR_YES:
|
||||
return ManualOrdIndReq::Manual;
|
||||
default:
|
||||
return ManualOrdIndReq::NULL_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
iLinkBinary::ClearingAcctType::Value map_customer_or_firm(api::orderman::CustomerOrFirm c) {
|
||||
using namespace api::orderman;
|
||||
using namespace iLinkBinary;
|
||||
switch(c) {
|
||||
case CustomerOrFirm::CUSTOMER_OR_FIRM_CUSTOMER:
|
||||
return ClearingAcctType::Customer;
|
||||
case CustomerOrFirm::CUSTOMER_OR_FIRM_FIRM:
|
||||
return ClearingAcctType::Firm;
|
||||
default:
|
||||
return ClearingAcctType::NULL_VALUE;
|
||||
}
|
||||
}
|
||||
iLinkBinary::SMPI::Value map_smp_instruction(api::orderman::SelfMatchPreventionInstruction smpi) {
|
||||
using namespace api::orderman;
|
||||
using namespace iLinkBinary;
|
||||
switch(smpi) {
|
||||
case SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_RESTING:
|
||||
return SMPI::CancelOldest;
|
||||
case SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_AGRESSING:
|
||||
return SMPI::CancelNewest;
|
||||
default:
|
||||
return SMPI::NULL_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
const char* translate_business_reject_reason(int reason) {
|
||||
switch(reason) {
|
||||
case 0: return "Other";
|
||||
case 1: return "Unknown ID (unknown PartyDetailsListReqID being referenced in business message)";
|
||||
case 2: return "Unknown Security";
|
||||
case 3: return "Unsupported Message Type (for example using messages specific to cash markets for futures)";
|
||||
case 5: return "Required Field Missing";
|
||||
case 6: return "Not Authorized";
|
||||
case 8: return "Throttle Limit Exceeded (volume controls)";
|
||||
case 100: return "Value is incorrect (out of range) for this tag (for example using a value outside defined range of integer) ";
|
||||
case 101: return "Incorrect data format for value (for example using ASCII character outside 32-126 in string field)";
|
||||
case 102: return "Rejected due to Kill Switch";
|
||||
case 103: return "Rejected due to Risk Management API";
|
||||
case 104: return "Rejected due to Globex Credit Controls";
|
||||
case 105: return "Not Authorized to trade Deliverable Swap Futures";
|
||||
case 106: return "Not Authorized to trade Interest Rate Swap Futures";
|
||||
case 107: return "Rejected due to Inline Credit Controls";
|
||||
case 108: return "Invalid PartyDetailsListReqID (reusing already existing PartyDetailsListReqID while creating Party Details Definition Request)";
|
||||
case 109: return "Incoming message could not be decoded. (Check schema version)";
|
||||
case 110: return "Same repeating group entry appears more than once";
|
||||
case 111: return "Exceeded maximum number of allowable Party Details Definition Requests";
|
||||
case 112: return "Technical Error in Registering Party Details";
|
||||
case 113: return "Rejected due to Cross Venue Risk";
|
||||
case 114: return "Order Status Not Available";
|
||||
case 115: return "Enum Not Supported";
|
||||
case 116: return "Order Status Not Found";
|
||||
case 117: return "Mass Order Status Cannot be Completed";
|
||||
case 118: return "Exceeded Maximum Number of Allowable RequestingPartyID's in Party Details List Request";
|
||||
case 119: return "Duplicate Order Threshold Exceeded";
|
||||
case 120: return "On-Demand Message Rejected Due to Preceding Party Details Definition Request Being Rejected";
|
||||
case 121: return "Message Rejected Since PartyDetailsListReqID Does Not Match with Immediately Preceding Party Details Definition Request sent to MSGW when using On-demand administrative information";
|
||||
case 122: return "Party Details Definition Request sent to MSGW Being Rejected Due to Corresponding Business Message Being Rejected";
|
||||
case 123: return "Another Message Sent in Between Party Details Definition Request and Business Message when using On-demand administrative information";
|
||||
case 124: return "Cannot Have More Than One In-Flight Mass Order Status Request in Progress";
|
||||
case 125: return "Exceeded Maximum Number of In-Flight Order Status Requests";
|
||||
case 126: return "Cannot Have More Than One In-Flight Party Details List Request in Progress";
|
||||
case 127: return "Party Details List Request is Missing Requesting Party ID and Party ID";
|
||||
case 128: return "Party Details List Request cannot contain both RequestingPartyID and PartyID";
|
||||
case 129: return "Party Details Definition Request Being Rejected Since Another Message was sent in Between On-Demand Message";
|
||||
case 130: return "Buy Side Firm ID Does Not Match Sell Side Firm ID in New Order Cross";
|
||||
case 131: return "Message Type Not Supported on Backup Instance";
|
||||
case 132: return "New Order Cross Does Not Contain Buy Side Followed by Sell Side";
|
||||
}
|
||||
return "Unknown or invalid business reject reason.";
|
||||
}
|
||||
|
||||
const char* translate_negotiation_reject(int reason) {
|
||||
// https://www.cmegroup.com/confluence/display/EPICSANDBOX/Negotiation+Reject
|
||||
// 01/13/2020
|
||||
switch(reason) {
|
||||
case 0: return "HMACNotAuthenticated: failed authentication because identity is not recognized";
|
||||
case 1: return "HMACNotAvailable: HMAC component is not responding (5sec)";
|
||||
case 2: return "InvalidUUID: UUID is not greater than the one last used or value sent by the client is out of acceptable range (MIN, MAX)";
|
||||
case 3: return "InvalidTimestamp: Timestamp is not greater than the one last used or value sent by the client is out of acceptable range (MIN, MAX)";
|
||||
case 4: return "RequiredHMACSignatureMissing: empty bytes in HMACSignature field";
|
||||
case 5: return "RequiredAccessKeyIDMissing: empty bytes in AccessKeyID field";
|
||||
case 6: return "RequiredSessionMissing: empty bytes in Session field";
|
||||
case 7: return "RequiredFirmMissing: empty bytes in Firm field";
|
||||
case 8: return "RequiredUUIDMisssing: null value in UUID field";
|
||||
case 9: return "RequiredRequestTimestampMissing: null value in RequestTimestamp field";
|
||||
case 10: return "SessionBlocked: session and firm are not authorized for this port";
|
||||
case 12: return "InvalidAccessKeyID: contains non-printable ASCII character";
|
||||
case 13: return "InvalidSession: contains non-printable ASCII character";
|
||||
case 14: return "InvalidFirm: contains non-printable ASCII character";
|
||||
case 15: return "Volume Controls - exceeding TPS limit as defined for volume controls (reject action)";
|
||||
case 16: return "SplitMessageRejected - Messages queued due to split message penalty being rejected because of logout or disconnect";
|
||||
case 17: return "SplitMessageQueue - Reached threshold of messages queued due to split message penalty";
|
||||
case 27: return "DesignatedBackup - Using Designated backup before designated primary not allowed";
|
||||
}
|
||||
return "Unknown or invalid Negotiate reject reason.";
|
||||
}
|
||||
|
||||
const char* translate_establish_reject(int reason) {
|
||||
switch(reason) {
|
||||
case 0: return "HMACNotAuthenticated: failed authentication because identity is not recognized";
|
||||
case 1: return "HMACNotAvailable: HMAC component is not responding (5sec)";
|
||||
case 2: return "InvalidUUID: UUID is not greater than the one last used or value sent by the client is out of acceptable range (MIN, MAX)";
|
||||
case 3: return "InvalidTimestamp: Timestamp is not greater than the one last used or value sent by the client is out of acceptable range (MIN, MAX)";
|
||||
case 4: return "RequiredHMACSignatureMissing: empty bytes in HMACSignature field";
|
||||
case 5: return "RequiredAccessKeyIDMissing: empty bytes in AccessKeyID field";
|
||||
case 6: return "RequiredSessionMissing: empty bytes in Session field";
|
||||
case 7: return "RequiredFirmMissing: empty bytes in Firm field";
|
||||
case 8: return "RequiredUUIDMisssing: null value in UUID field";
|
||||
case 9: return "RequiredRequestTimestampMissing: null value in RequestTimestamp field";
|
||||
case 10: return "SessionBlocked: session and firm are not authorized for this port";
|
||||
case 11: return "InvalidKeepAliveInterval: value is out of acceptable range (MIN, MAX)";
|
||||
case 12: return "InvalidAccessKeyID: contains non-printable ASCII character";
|
||||
case 13: return "InvalidSession: contains non-printable ASCII character";
|
||||
case 14: return "InvalidFirm: contains non-printable ASCII character";
|
||||
case 15: return "Volume Controls - exceeding TPS limit as defined for volume controls (reject action)";
|
||||
case 16: return "SplitMessageRejected - Messages queued due to split message penalty being rejected because of logout or disconnect";
|
||||
case 17: return "SplitMessageQueue - Reached threshold of messages queued due to split message penalty";
|
||||
case 18: return "RequiredTradingSystemNameMissing: empty bytes in TradingSystemName";
|
||||
case 19: return "RequiredTradingSystemVersionMissing: empty bytes in TradingSystemVersion";
|
||||
case 20: return "RequiredTradingSystemVendorMissing: empty bytes in TradingSystemVendor";
|
||||
case 21: return "RequiredKeepAliveIntervalMissing: null value in KeepAliveInterval field";
|
||||
case 22: return "RequiredNextSeqNoMissing: empty bytes in NextSeqNo field";
|
||||
case 23: return "InvalidTradingSystemName: contains non-prinatable ASCII character";
|
||||
case 24: return "InvalidTradingSystemVersion: contains non-prinatable ASCII character";
|
||||
case 25: return "InvalidTradingSystemVendor: contains non-prinatable ASCII character";
|
||||
case 26: return "DesignatedBackup - Using Designated backup before designated primary not allowed";
|
||||
case 27: return "DesignatedBackup - Using Designated backup before designated primary not allowed";
|
||||
}
|
||||
return "Unknown or invalid Establish reject reason.";
|
||||
}
|
||||
|
||||
}}}//namespace
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/domain_model/common.h"
|
||||
#include "lib/domain_model/Security.pb.h"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
|
||||
#include "generated/SideReq.h"
|
||||
#include "generated/OrderTypeReq.h"
|
||||
#include "generated/TimeInForce.h"
|
||||
#include "generated/ManualOrdInd.h"
|
||||
#include "generated/ManualOrdIndReq.h"
|
||||
#include "generated/ClearingAcctType.h"
|
||||
#include "generated/SMPI.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
iLinkBinary::SideReq::Value map_side(api::Side side);
|
||||
|
||||
inline int64_t to_price9(Price p) {
|
||||
return p.value() * 1E9;
|
||||
}
|
||||
|
||||
iLinkBinary::OrderTypeReq::Value map_order_type(api::orderman::NativeOrderType ot);
|
||||
iLinkBinary::TimeInForce::Value map_time_in_force(api::orderman::TimeInForce tif);
|
||||
iLinkBinary::ManualOrdIndReq::Value map_manual_order_indicator(api::orderman::ManualOrderIndicator moi) ;
|
||||
iLinkBinary::ClearingAcctType::Value map_customer_or_firm(api::orderman::CustomerOrFirm c) ;
|
||||
iLinkBinary::SMPI::Value map_smp_instruction(api::orderman::SelfMatchPreventionInstruction smpi) ;
|
||||
const char* translate_business_reject_reason(int reason) ;
|
||||
const char* translate_negotiation_reject(int reason) ;
|
||||
const char* translate_establish_reject(int reason) ;
|
||||
|
||||
}}}//namespace
|
||||
@@ -0,0 +1,471 @@
|
||||
#include "ILinkGW.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/SecureLogon.h"
|
||||
#include "ILink3Utils.h"
|
||||
#include "IL3GeneratedFull.h"
|
||||
#include "IL3Dispatcher.h"
|
||||
|
||||
/*
|
||||
# Some notes about session binding and initialization: #
|
||||
|
||||
## Official documentation:
|
||||
https://www.cmegroup.com/confluence/display/EPICSANDBOX/iLink+3+Binary+Order+Entry+-+Session+Layer#iLink3BinaryOrderEntry-SessionLayer-WeeklyReset
|
||||
|
||||
## Begining of week (new session?)
|
||||
- config.session = X
|
||||
- config.outgoing_sequence = 1
|
||||
-> Negotiate session=X
|
||||
<- NegotiateResponse session=X, oldsession=Y, oldSeq=N
|
||||
if oldSeq > 0 -> ReplayRequest
|
||||
-> Establish seq=1
|
||||
<- EstablishAck oldsession=Y, oldSeq=N
|
||||
|
||||
## Middle of week (same session)
|
||||
- config.session = X
|
||||
- config.outgoing_sequence > 1
|
||||
-> Establish seq=N
|
||||
*/
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
log::CharPtrWrapper wrap(const char* str) { return log::CharPtrWrapper{ str }; }
|
||||
|
||||
ILinkGW::ILinkGW(IL3Connection& con, const Session& session, const Config& conf) :
|
||||
_config(conf),
|
||||
_x_state(State::INIT),
|
||||
_buffer(),
|
||||
_on_state_change([](auto){}),
|
||||
_connection(con),
|
||||
_session(session),
|
||||
_msg_count(0),
|
||||
_time()
|
||||
{
|
||||
}
|
||||
|
||||
void ILinkGW::connect() {
|
||||
bts_assert(state() == State::INIT);
|
||||
set_state(State::CONNECTING);
|
||||
_connection.connect(*this); // -> on_connect_success
|
||||
}
|
||||
|
||||
// This determines the session uuid
|
||||
void ILinkGW::send_negotiate() {
|
||||
// add 2 bytes for the empty 'credentials' (39008) length which must be present
|
||||
auto msg = _buffer.frame<iLinkBinary::Negotiate500>(
|
||||
iLinkBinary::Negotiate500::credentialsHeaderLength());
|
||||
msg.uUID(_session.uuid);
|
||||
msg.requestTimestamp(_time.now());
|
||||
msg.putSession(_config.cme_session);
|
||||
// We need this call to set the length of the credential block to 0
|
||||
// This behavior is specific to "data" in the xml template
|
||||
msg.putCredentials("", 0);
|
||||
msg.putFirm(_config.firm);
|
||||
|
||||
const auto ld = glbx::calculate_hmac(
|
||||
_config.cme_session,
|
||||
std::to_string(msg.requestTimestamp()),
|
||||
std::to_string(msg.uUID()),
|
||||
_config.cme_session,
|
||||
_config.firm
|
||||
);
|
||||
msg.putAccessKeyID(ld.access_key);
|
||||
msg.putHMACSignature(ld.hmac);
|
||||
// expect -> NegotiationResponse501 or NegotiationReject502
|
||||
}
|
||||
|
||||
void ILinkGW::send_establish() {
|
||||
// add 2 bytes for the empty 'credentials' (39008) length which must be present
|
||||
auto msg = _buffer.frame<iLinkBinary::Establish503>(2);
|
||||
msg.uUID(_session.uuid);
|
||||
msg.requestTimestamp(_time.now());
|
||||
msg.putSession(_config.cme_session);
|
||||
msg.putCredentials("", 0);
|
||||
msg.putFirm(_config.firm);
|
||||
|
||||
msg.putTradingSystemName(std::string_view("SPARK"));
|
||||
msg.putTradingSystemVersion(std::string_view("1.0"));
|
||||
msg.putTradingSystemVendor(std::string_view("BTS"));
|
||||
// On primary connection, the expected message sequence number of the next business message from customer to CME
|
||||
msg.nextSeqNo(_session.to_sequence);
|
||||
msg.keepAliveInterval(60000);
|
||||
|
||||
const auto ld = glbx::calculate_hmac(
|
||||
_config.cme_session,
|
||||
std::to_string(msg.requestTimestamp()),
|
||||
std::to_string(msg.uUID()),
|
||||
_config.cme_session,
|
||||
_config.firm
|
||||
);
|
||||
msg.putAccessKeyID(ld.access_key);
|
||||
msg.putHMACSignature(ld.hmac);
|
||||
// expect -> EstablishmentAck504 or EstablishmentReject505
|
||||
}
|
||||
|
||||
void ILinkGW::send_heartbeat() { send_sequence(); }
|
||||
void ILinkGW::send_sequence() {
|
||||
auto msg = _buffer.frame<iLinkBinary::Sequence506>();
|
||||
msg.uUID(_session.uuid);
|
||||
msg.nextSeqNo(_session.to_sequence);
|
||||
msg.faultToleranceIndicator(iLinkBinary::FTI::NULL_VALUE);
|
||||
msg.keepAliveIntervalLapsed(iLinkBinary::KeepAliveLapsed::NotLapsed);
|
||||
}
|
||||
|
||||
void ILinkGW::send_retransmit_request(uint64_t uuid, uint32_t from, size_t count) {
|
||||
using namespace iLinkBinary;
|
||||
auto msg = _buffer.frame<RetransmitRequest508>();
|
||||
msg.uUID(_session.uuid);
|
||||
msg.lastUUID(uuid);
|
||||
msg.requestTimestamp(_time.now());
|
||||
msg.fromSeqNo(from);
|
||||
msg.msgCount(count);
|
||||
}
|
||||
|
||||
uint64_t ILinkGW::send_party_details(const api::orderman::RoutingRecord& route) {
|
||||
using namespace iLinkBinary;
|
||||
auto msg = _buffer.frame<PartyDetailsDefinitionRequest518>(
|
||||
// 2 repeating group header.
|
||||
// 1 for party details and 1 for trade reg
|
||||
// This seems wildly unsafe but the api doesn't seem to want you to know
|
||||
// how to size buffers ahead of time
|
||||
2 * PartyDetailsDefinitionRequest518::NoPartyDetails::sbeHeaderSize() +
|
||||
// N * size of each block
|
||||
1 * PartyDetailsDefinitionRequest518::NoPartyDetails::sbeBlockLength() +
|
||||
1 * PartyDetailsDefinitionRequest518::NoTrdRegPublications::sbeBlockLength()
|
||||
);
|
||||
msg.partyDetailsListReqID(++_msg_count);
|
||||
msg.sendingTimeEpoch(_time.now());
|
||||
msg.listUpdateAction(ListUpdAct::Add);
|
||||
msg.seqNum(next_seq());
|
||||
// msg.putMemo("") -- not supported
|
||||
if (route.smp_id().empty()) {
|
||||
msg.selfMatchPreventionID(
|
||||
PartyDetailsDefinitionRequest518::selfMatchPreventionIDNullValue());
|
||||
}
|
||||
else {
|
||||
bts_assert_d(route.smp_id().size() == 8);
|
||||
const uint64_t* smp_id = reinterpret_cast<const uint64_t*>(&route.smp_id());
|
||||
msg.selfMatchPreventionID(*smp_id);
|
||||
}
|
||||
msg.cmtaGiveupCD(CmtaGiveUpCD::NULL_VALUE);
|
||||
msg.custOrderCapacity(CustOrderCapacity::NULL_VALUE);
|
||||
msg.clearingAccountType(map_customer_or_firm(route.customer_or_firm()));
|
||||
msg.selfMatchPreventionInstruction(map_smp_instruction(route.smp_instruction()));
|
||||
msg.avgPxIndicator(AvgPxInd::NULL_VALUE);
|
||||
msg.clearingTradePriceType(SLEDS::NULL_VALUE);
|
||||
msg.custOrderHandlingInst(CustOrdHandlInst::NULL_VALUE);
|
||||
msg.executor(PartyDetailsDefinitionRequest518::executorNullValue());
|
||||
msg.iDMShortCode(PartyDetailsDefinitionRequest518::iDMShortCodeNullValue());
|
||||
|
||||
auto details = msg.noPartyDetailsCount(1);
|
||||
details.next();
|
||||
details.partyDetailRole(PartyDetailRole::CustomerAccount);
|
||||
msg.noTrdRegPublicationsCount(0);
|
||||
// In the future we may check for existing party details definitions and return that id
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool needs_stop_px(api::orderman::NativeOrderType order_type) {
|
||||
return order_type == api::orderman::NativeOrderType::NATIVE_ORDER_TYPE_STOP ||
|
||||
order_type == api::orderman::NativeOrderType::NATIVE_ORDER_TYPE_STOP_LIMIT;
|
||||
}
|
||||
|
||||
void ILinkGW::send_order(const CreateOrderMsg& create) {
|
||||
bts_assert_d(create.routing_record);
|
||||
bts_assert_d(create.security);
|
||||
bts_assert_d(create.security->has_glbx());
|
||||
|
||||
auto party = send_party_details(*create.routing_record);
|
||||
auto msg = _buffer.frame<iLinkBinary::NewOrderSingle514>();
|
||||
|
||||
msg.price().mantissa(to_price9(create.price));
|
||||
msg.orderQty(create.quantity.value());
|
||||
msg.securityID(create.security->glbx().exchange_id());
|
||||
msg.side(map_side(create.side));
|
||||
msg.seqNum(next_seq());
|
||||
msg.putSenderID(create.routing_record->operator_id());
|
||||
// msg.putClOrdID(create.clordid);
|
||||
msg.partyDetailsListReqID(party);
|
||||
msg.orderRequestID(++_msg_count);
|
||||
msg.sendingTimeEpoch(_time.now());
|
||||
if (needs_stop_px(create.order_type)) {
|
||||
msg.stopPx().mantissa(to_price9(create.stop_px));
|
||||
}
|
||||
else msg.stopPx().mantissa(iLinkBinary::PRICENULL9::mantissaNullValue());
|
||||
msg.putLocation(create.routing_record->location());
|
||||
msg.minQty(iLinkBinary::NewOrderSingle514::minQtyNullValue());
|
||||
msg.displayQty(create.display_quantity.value());
|
||||
msg.expireDate(iLinkBinary::NewOrderSingle514::expireDateNullValue());
|
||||
msg.ordType(map_order_type(create.order_type));
|
||||
msg.timeInForce(map_time_in_force(create.time_in_force));
|
||||
msg.manualOrderIndicator(map_manual_order_indicator(create.manual_order_indicator));
|
||||
msg.execInst().clear();
|
||||
msg.executionMode(iLinkBinary::ExecMode::NULL_VALUE);
|
||||
msg.liquidityFlag(iLinkBinary::BooleanNULL::NULL_VALUE);
|
||||
msg.managedOrder(iLinkBinary::BooleanNULL::NULL_VALUE);
|
||||
msg.shortSaleType(iLinkBinary::ShortSaleType::NULL_VALUE);
|
||||
}
|
||||
|
||||
void ILinkGW::send_cancel_replace(const CancelReplaceOrderMsg& replace) {
|
||||
const auto& create = replace.create_fields;
|
||||
bts_assert_d(create.routing_record);
|
||||
bts_assert_d(create.security);
|
||||
bts_assert_d(create.security->has_glbx());
|
||||
|
||||
auto party = send_party_details(*create.routing_record);
|
||||
auto msg = _buffer.frame<iLinkBinary::OrderCancelReplaceRequest515>();
|
||||
|
||||
msg.orderID(replace.exchange_order_id.value());
|
||||
|
||||
msg.price().mantissa(to_price9(create.price));
|
||||
msg.orderQty(create.quantity.value());
|
||||
msg.securityID(create.security->glbx().exchange_id());
|
||||
msg.side(map_side(create.side));
|
||||
msg.seqNum(next_seq());
|
||||
msg.putSenderID(create.routing_record->operator_id());
|
||||
// msg.putClOrdID(create.clordid);
|
||||
msg.partyDetailsListReqID(party);
|
||||
msg.orderRequestID(++_msg_count);
|
||||
msg.sendingTimeEpoch(_time.now());
|
||||
if (needs_stop_px(create.order_type)) {
|
||||
msg.stopPx().mantissa(to_price9(create.stop_px));
|
||||
}
|
||||
else msg.stopPx().mantissa(iLinkBinary::PRICENULL9::mantissaNullValue());
|
||||
msg.putLocation(create.routing_record->location());
|
||||
msg.minQty(iLinkBinary::NewOrderSingle514::minQtyNullValue());
|
||||
msg.displayQty(create.display_quantity.value());
|
||||
msg.expireDate(iLinkBinary::NewOrderSingle514::expireDateNullValue());
|
||||
msg.ordType(map_order_type(create.order_type));
|
||||
msg.timeInForce(map_time_in_force(create.time_in_force));
|
||||
msg.manualOrderIndicator(map_manual_order_indicator(create.manual_order_indicator));
|
||||
msg.oFMOverride(iLinkBinary::OFMOverrideReq::Disabled);
|
||||
msg.execInst().clear();
|
||||
msg.executionMode(iLinkBinary::ExecMode::NULL_VALUE);
|
||||
msg.liquidityFlag(iLinkBinary::BooleanNULL::NULL_VALUE);
|
||||
msg.managedOrder(iLinkBinary::BooleanNULL::NULL_VALUE);
|
||||
msg.shortSaleType(iLinkBinary::ShortSaleType::NULL_VALUE);
|
||||
}
|
||||
|
||||
void ILinkGW::send_cancel(const CancelOrderMsg& cancel) {
|
||||
bts_assert_d(cancel.routing_record);
|
||||
bts_assert_d(cancel.security);
|
||||
bts_assert_d(cancel.security->has_glbx());
|
||||
|
||||
auto party = send_party_details(*cancel.routing_record);
|
||||
auto msg = _buffer.frame<iLinkBinary::OrderCancelRequest516>();
|
||||
|
||||
msg.orderID(cancel.exchange_order_id.value());
|
||||
msg.partyDetailsListReqID(party);
|
||||
msg.manualOrderIndicator(map_manual_order_indicator(cancel.manual_order_indicator));
|
||||
msg.seqNum(next_seq());
|
||||
msg.putSenderID(cancel.routing_record->operator_id());
|
||||
msg.putClOrdID(cancel.clordid);
|
||||
msg.orderRequestID(++_msg_count);
|
||||
msg.sendingTimeEpoch(_time.now());
|
||||
msg.putLocation(cancel.routing_record->location());
|
||||
msg.securityID(cancel.security->glbx().exchange_id());
|
||||
msg.side(map_side(cancel.side));
|
||||
msg.liquidityFlag(iLinkBinary::BooleanNULL::NULL_VALUE);
|
||||
}
|
||||
|
||||
void ILinkGW::flush() {
|
||||
_buffer.flush(_connection);
|
||||
}
|
||||
|
||||
const char* state_string(State s) {
|
||||
switch(s) {
|
||||
case State::INIT: return "INIT";
|
||||
case State::CONNECTING: return "CONNECTING";
|
||||
case State::NEGOTATING: return "NEGOTATING";
|
||||
case State::ESTABLISHING: return "ESTABLISHING";
|
||||
case State::RETRANSMISSING_LAST: return "RETRANSMITTING LAST";
|
||||
case State::RETRANSMITTING: return "RETRANSMITTING";
|
||||
case State::READY: return "READY";
|
||||
case State::ERROR: return "ERROR";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
log::CharPtrWrapper wstate_string(State s) { return wrap(state_string(s)); }
|
||||
|
||||
void ILinkGW::set_state(State s) {
|
||||
LOG(OrderMan, Diag, "ILinkGW {}: {}->{}",
|
||||
_session.uuid, wstate_string(_x_state), wstate_string(s));
|
||||
_x_state = s;
|
||||
if (_on_state_change) _on_state_change(s);
|
||||
}
|
||||
|
||||
//
|
||||
// IConnectionHandler
|
||||
|
||||
void ILinkGW::on_connect_success() {
|
||||
if (_session.from_sequence == 1) {
|
||||
LOG(OrderMan, Info, "Creating new iLink session. {}", _session.uuid);
|
||||
set_state(State::NEGOTATING);
|
||||
// Sequence = 1 indicates a new session
|
||||
// so we have to go through a negotiate
|
||||
// to create the session at CME
|
||||
send_negotiate();
|
||||
}
|
||||
else {
|
||||
// We only need to establish the current sequence number
|
||||
LOG(OrderMan, Info, "re-establishing existing iLink session. {}", _session.uuid);
|
||||
set_state(State::ESTABLISHING);
|
||||
send_establish();
|
||||
}
|
||||
flush();
|
||||
}
|
||||
void ILinkGW::on_disconnect() {
|
||||
LOG(OrderMan, Error, "Disconnected.");
|
||||
set_state(State::ERROR);
|
||||
if (_config.failover_on_disconnect) {
|
||||
LOG(OrderMan, Error, "Attempting failover");
|
||||
_connection.failover();
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ILinkGW::on_retransmit_complete() {
|
||||
if (state() == State::RETRANSMISSING_LAST) {
|
||||
send_establish();
|
||||
flush();
|
||||
}
|
||||
_rt_session = std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
size_t ILinkGW::on_data(const std::string_view& data) {
|
||||
return consume_messages(*this, data.data(), data.size(), dispatch_message<ILinkGW>);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::string to_string(const T& t) {
|
||||
std::stringstream buf;
|
||||
buf << t;
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
// Message Handlers
|
||||
void ILinkGW::handle(const iLinkBinary::NegotiationResponse501& msg) {
|
||||
auto cme_prev_uuid = msg.previousUUID();
|
||||
auto cme_prev_seq = msg.previousSeqNo();
|
||||
// Check if cme sent us a previous session
|
||||
if (cme_prev_uuid != iLinkBinary::NegotiationResponse501::previousUUIDNullValue()) {
|
||||
auto my_session = _config.previous_session.value_or(Session {0,0,0});
|
||||
if (my_session.uuid == cme_prev_uuid) {
|
||||
// There is a previous session and we knew about it
|
||||
// Replay any missing messages
|
||||
if (my_session.from_sequence < cme_prev_seq) {
|
||||
set_state(State::RETRANSMISSING_LAST);
|
||||
send_retransmit_request(cme_prev_uuid, my_session.from_sequence);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// there is a previous session we didn't know about
|
||||
// It could be an automatically created start of week session
|
||||
// replay all messages
|
||||
set_state(State::RETRANSMISSING_LAST);
|
||||
send_retransmit_request(cme_prev_uuid, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// There is no previous session, continue to negotiate
|
||||
send_establish();
|
||||
}
|
||||
flush();
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::NegotiationReject502& msg) {
|
||||
LOG(OrderMan, Error, "Negotiate Reject: {} '{}'.\nRaw: \"{}\"",
|
||||
msg.errorCodes(),
|
||||
std::string(translate_negotiation_reject(msg.errorCodes())),
|
||||
to_string(msg));
|
||||
set_state(State::ERROR);
|
||||
_connection.disconnect();
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::EstablishmentAck504&) {
|
||||
// This message may indicate missed messages from *this* session. (different from NegotiationResponse501)
|
||||
// If no missed messagse send the Sequence
|
||||
send_sequence();
|
||||
flush();
|
||||
set_state(State::READY);
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::EstablishmentReject505& msg) {
|
||||
LOG(OrderMan, Error, "Establishment Reject: {} '{}'. NextSeq: {}.\nRaw: \"{}\"",
|
||||
msg.errorCodes(),
|
||||
std::string(translate_establish_reject(msg.errorCodes())),
|
||||
msg.nextSeqNo(),
|
||||
to_string(msg));
|
||||
set_state(State::ERROR);
|
||||
_connection.disconnect();
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::Sequence506& s) {
|
||||
if (s.keepAliveIntervalLapsed() == iLinkBinary::KeepAliveLapsed::Lapsed) {
|
||||
send_heartbeat();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::Terminate507& msg) {
|
||||
LOG(OrderMan, Error, "Terminated by exchange. Code: {} Reason: {}",
|
||||
msg.errorCodes(),
|
||||
wrap(msg.reason())
|
||||
);
|
||||
set_state(State::ERROR);
|
||||
_connection.disconnect();
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::Retransmission509&) {
|
||||
// Accepted retransmit
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::RetransmitReject510&) {
|
||||
// ?!
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::NotApplied513&) {
|
||||
// Sequence number problem?
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::PartyDetailsDefinitionRequestAck519&) {
|
||||
// Future:
|
||||
// We can handle the party details
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::BusinessReject521& msg) {
|
||||
LOG(OrderMan, Error, "Business Reject: {} '{}'.\nRaw: \"{}\"",
|
||||
msg.businessRejectReason(),
|
||||
wrap(translate_business_reject_reason(msg.businessRejectReason())),
|
||||
to_string(msg));
|
||||
set_state(State::ERROR);
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportNew522& msg) {
|
||||
if (msg.uUID() != _session.uuid) {
|
||||
// handle retransmit
|
||||
return;
|
||||
}
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportReject523&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportElimination524&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportTradeOutright525&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportTradeSpread526&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportTradeSpreadLeg527&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportModify531&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportStatus532&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportCancel534&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::OrderCancelReject535&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::OrderCancelReplaceReject536&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionAck539&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportTradeAddendumOutright548&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportTradeAddendumSpread549&) {
|
||||
}
|
||||
void ILinkGW::handle(const iLinkBinary::ExecutionReportTradeAddendumSpreadLeg550&) {
|
||||
}
|
||||
|
||||
}}} //namespace
|
||||
@@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "../IExecutionGateway.h"
|
||||
#include "TimeKeeper.h"
|
||||
#include "IL3Connection.h"
|
||||
#include "IL3ConnectionHandler.h"
|
||||
#include "IL3MessageFramer.h"
|
||||
|
||||
#include "generated/MessageHeader.h"
|
||||
#include "IL3GeneratedFwd.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
class ExecutionReport;
|
||||
namespace ilink3 {
|
||||
|
||||
struct Session {
|
||||
uint64_t uuid;
|
||||
// bts->cme
|
||||
uint32_t to_sequence;
|
||||
// cme->bts
|
||||
uint32_t from_sequence;
|
||||
};
|
||||
|
||||
struct RetransmitState {
|
||||
uint64_t uuid;
|
||||
uint32_t sequence;
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
std::optional<Session> previous_session;
|
||||
std::string cme_session; // NGG
|
||||
std::string firm; // 004
|
||||
bool failover_on_disconnect;
|
||||
};
|
||||
|
||||
enum class State {
|
||||
INIT,
|
||||
CONNECTING,
|
||||
NEGOTATING,
|
||||
ESTABLISHING,
|
||||
RETRANSMISSING_LAST, // of Session
|
||||
RETRANSMITTING, // of Session
|
||||
READY,
|
||||
ERROR
|
||||
};
|
||||
typedef std::function<void(State)> OnStateChange;
|
||||
typedef std::function<void(const ExecutionReport&)> OnExecutionReport;
|
||||
|
||||
// Does not allow re-entrance
|
||||
class ILinkGW : IL3ConnectionHandler {
|
||||
const Config _config;
|
||||
State _x_state;
|
||||
IL3MessageFramer _buffer;
|
||||
OnStateChange _on_state_change;
|
||||
OnExecutionReport _on_exe_report;
|
||||
OnExecutionReport _on_replay_exe_report;
|
||||
IL3Connection& _connection;
|
||||
Session _session;
|
||||
std::optional<RetransmitState> _rt_session;
|
||||
uint32_t _msg_count;
|
||||
TimeKeeper _time;
|
||||
public:
|
||||
ILinkGW(IL3Connection&, const Session&, const Config&);
|
||||
|
||||
void connect();
|
||||
void send_negotiate();
|
||||
void send_establish();
|
||||
void send_heartbeat();
|
||||
void send_sequence();
|
||||
void send_retransmit_request(uint64_t uuid, uint32_t from, size_t count = 0);
|
||||
uint64_t send_party_details(const api::orderman::RoutingRecord&);
|
||||
void send_order(const CreateOrderMsg&);
|
||||
void send_cancel_replace(const CancelReplaceOrderMsg&);
|
||||
void send_cancel(const CancelOrderMsg&);
|
||||
void flush();
|
||||
|
||||
// state change
|
||||
State state() { return _x_state; }
|
||||
void subscribe_state(OnStateChange o) { _on_state_change = o; }
|
||||
void subscribe_execution_reports(OnExecutionReport sub, OnExecutionReport rsub) {
|
||||
_on_exe_report = sub;
|
||||
_on_replay_exe_report = rsub;
|
||||
}
|
||||
|
||||
private: // Functions
|
||||
void set_state(State);
|
||||
uint32_t next_seq() { return ++_session.to_sequence; }
|
||||
|
||||
void on_connect_success() override;
|
||||
void on_disconnect() override;
|
||||
void on_retransmit_complete();
|
||||
|
||||
//
|
||||
// Message handlers
|
||||
|
||||
size_t on_data(const std::string_view&) override;
|
||||
|
||||
public:
|
||||
void handle(const iLinkBinary::NegotiationResponse501&);
|
||||
void handle(const iLinkBinary::NegotiationReject502&);
|
||||
void handle(const iLinkBinary::EstablishmentAck504&);
|
||||
void handle(const iLinkBinary::EstablishmentReject505&);
|
||||
void handle(const iLinkBinary::Sequence506&);
|
||||
void handle(const iLinkBinary::Terminate507&);
|
||||
void handle(const iLinkBinary::Retransmission509&);
|
||||
void handle(const iLinkBinary::RetransmitReject510&);
|
||||
void handle(const iLinkBinary::NotApplied513&);
|
||||
void handle(const iLinkBinary::PartyDetailsDefinitionRequestAck519&);
|
||||
void handle(const iLinkBinary::BusinessReject521&);
|
||||
void handle(const iLinkBinary::ExecutionReportNew522&);
|
||||
void handle(const iLinkBinary::ExecutionReportReject523&);
|
||||
void handle(const iLinkBinary::ExecutionReportElimination524&);
|
||||
void handle(const iLinkBinary::ExecutionReportTradeOutright525&);
|
||||
void handle(const iLinkBinary::ExecutionReportTradeSpread526&);
|
||||
void handle(const iLinkBinary::ExecutionReportTradeSpreadLeg527&);
|
||||
void handle(const iLinkBinary::ExecutionReportModify531&);
|
||||
void handle(const iLinkBinary::ExecutionReportStatus532&);
|
||||
void handle(const iLinkBinary::ExecutionReportCancel534&);
|
||||
void handle(const iLinkBinary::OrderCancelReject535&);
|
||||
void handle(const iLinkBinary::OrderCancelReplaceReject536&);
|
||||
void handle(const iLinkBinary::ExecutionAck539&);
|
||||
void handle(const iLinkBinary::ExecutionReportTradeAddendumOutright548&);
|
||||
void handle(const iLinkBinary::ExecutionReportTradeAddendumSpread549&);
|
||||
void handle(const iLinkBinary::ExecutionReportTradeAddendumSpreadLeg550&);
|
||||
|
||||
};
|
||||
|
||||
}}} //namespace
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "lib/utils/datetime.h"
|
||||
#include "lib/utils/TimeStamp.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace ilink3 {
|
||||
|
||||
struct TimeKeeper {
|
||||
uint64_t unix_time;
|
||||
utils::TimeStamp start;
|
||||
|
||||
TimeKeeper() :
|
||||
unix_time(datetime::unix_nano_now()),
|
||||
start(utils::TimeStamp::now())
|
||||
{}
|
||||
|
||||
uint64_t now() const {
|
||||
auto now = utils::TimeStamp::now();
|
||||
auto delta = now - start;
|
||||
return unix_time + delta.microseconds();
|
||||
}
|
||||
};
|
||||
|
||||
}}} //namespace
|
||||
@@ -0,0 +1,144 @@
|
||||
/* Generated SBE (Simple Binary Encoding) message codec */
|
||||
#ifndef _ILINKBINARY_AVGPXIND_H_
|
||||
#define _ILINKBINARY_AVGPXIND_H_
|
||||
|
||||
#if defined(SBE_HAVE_CMATH)
|
||||
/* cmath needed for std::numeric_limits<double>::quiet_NaN() */
|
||||
# include <cmath>
|
||||
# define SBE_FLOAT_NAN std::numeric_limits<float>::quiet_NaN()
|
||||
# define SBE_DOUBLE_NAN std::numeric_limits<double>::quiet_NaN()
|
||||
#else
|
||||
/* math.h needed for NAN */
|
||||
# include <math.h>
|
||||
# define SBE_FLOAT_NAN NAN
|
||||
# define SBE_DOUBLE_NAN NAN
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
# define SBE_CONSTEXPR constexpr
|
||||
# define SBE_NOEXCEPT noexcept
|
||||
#else
|
||||
# define SBE_CONSTEXPR
|
||||
# define SBE_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
# define SBE_CONSTEXPR_14 constexpr
|
||||
#else
|
||||
# define SBE_CONSTEXPR_14
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
# include <string_view>
|
||||
# define SBE_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
# define SBE_NODISCARD
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_LIMIT_MACROS)
|
||||
# define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) _byteswap_ushort(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) _byteswap_ulong(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) _byteswap_uint64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) (v)
|
||||
#else
|
||||
# error "Byte Ordering of platform not determined. Set __BYTE_ORDER__ manually before including this file."
|
||||
#endif
|
||||
|
||||
#if defined(SBE_NO_BOUNDS_CHECK)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (false)
|
||||
#elif defined(_MSC_VER)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (exp)
|
||||
#else
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (__builtin_expect(exp,c))
|
||||
#endif
|
||||
|
||||
#define SBE_NULLVALUE_INT8 (std::numeric_limits<std::int8_t>::min)()
|
||||
#define SBE_NULLVALUE_INT16 (std::numeric_limits<std::int16_t>::min)()
|
||||
#define SBE_NULLVALUE_INT32 (std::numeric_limits<std::int32_t>::min)()
|
||||
#define SBE_NULLVALUE_INT64 (std::numeric_limits<std::int64_t>::min)()
|
||||
#define SBE_NULLVALUE_UINT8 (std::numeric_limits<std::uint8_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT16 (std::numeric_limits<std::uint16_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT32 (std::numeric_limits<std::uint32_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT64 (std::numeric_limits<std::uint64_t>::max)()
|
||||
|
||||
|
||||
namespace iLinkBinary {
|
||||
|
||||
class AvgPxInd
|
||||
{
|
||||
public:
|
||||
enum Value
|
||||
{
|
||||
NoAveragePricing = (std::uint8_t)0,
|
||||
TradeispartofanAveragePriceGroupIdentifiedbytheAvgPxGrpID = (std::uint8_t)1,
|
||||
TradeispartofaNotionalValueAveragePriceGroup = (std::uint8_t)3,
|
||||
NULL_VALUE = (std::uint8_t)255
|
||||
};
|
||||
|
||||
static AvgPxInd::Value get(const std::uint8_t value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0: return NoAveragePricing;
|
||||
case 1: return TradeispartofanAveragePriceGroupIdentifiedbytheAvgPxGrpID;
|
||||
case 3: return TradeispartofaNotionalValueAveragePriceGroup;
|
||||
case 255: return NULL_VALUE;
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum AvgPxInd [E103]");
|
||||
}
|
||||
|
||||
static const char* c_str(const AvgPxInd::Value value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case NoAveragePricing: return "NoAveragePricing";
|
||||
case TradeispartofanAveragePriceGroupIdentifiedbytheAvgPxGrpID: return "TradeispartofanAveragePriceGroupIdentifiedbytheAvgPxGrpID";
|
||||
case TradeispartofaNotionalValueAveragePriceGroup: return "TradeispartofaNotionalValueAveragePriceGroup";
|
||||
case NULL_VALUE: return "NULL_VALUE";
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum AvgPxInd [E103]:");
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
friend std::basic_ostream<CharT, Traits>& operator << (
|
||||
std::basic_ostream<CharT, Traits>& os, AvgPxInd::Value m)
|
||||
{
|
||||
return os << AvgPxInd::c_str(m);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
/* Generated SBE (Simple Binary Encoding) message codec */
|
||||
#ifndef _ILINKBINARY_BOOLEANFLAG_H_
|
||||
#define _ILINKBINARY_BOOLEANFLAG_H_
|
||||
|
||||
#if defined(SBE_HAVE_CMATH)
|
||||
/* cmath needed for std::numeric_limits<double>::quiet_NaN() */
|
||||
# include <cmath>
|
||||
# define SBE_FLOAT_NAN std::numeric_limits<float>::quiet_NaN()
|
||||
# define SBE_DOUBLE_NAN std::numeric_limits<double>::quiet_NaN()
|
||||
#else
|
||||
/* math.h needed for NAN */
|
||||
# include <math.h>
|
||||
# define SBE_FLOAT_NAN NAN
|
||||
# define SBE_DOUBLE_NAN NAN
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
# define SBE_CONSTEXPR constexpr
|
||||
# define SBE_NOEXCEPT noexcept
|
||||
#else
|
||||
# define SBE_CONSTEXPR
|
||||
# define SBE_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
# define SBE_CONSTEXPR_14 constexpr
|
||||
#else
|
||||
# define SBE_CONSTEXPR_14
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
# include <string_view>
|
||||
# define SBE_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
# define SBE_NODISCARD
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_LIMIT_MACROS)
|
||||
# define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) _byteswap_ushort(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) _byteswap_ulong(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) _byteswap_uint64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) (v)
|
||||
#else
|
||||
# error "Byte Ordering of platform not determined. Set __BYTE_ORDER__ manually before including this file."
|
||||
#endif
|
||||
|
||||
#if defined(SBE_NO_BOUNDS_CHECK)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (false)
|
||||
#elif defined(_MSC_VER)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (exp)
|
||||
#else
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (__builtin_expect(exp,c))
|
||||
#endif
|
||||
|
||||
#define SBE_NULLVALUE_INT8 (std::numeric_limits<std::int8_t>::min)()
|
||||
#define SBE_NULLVALUE_INT16 (std::numeric_limits<std::int16_t>::min)()
|
||||
#define SBE_NULLVALUE_INT32 (std::numeric_limits<std::int32_t>::min)()
|
||||
#define SBE_NULLVALUE_INT64 (std::numeric_limits<std::int64_t>::min)()
|
||||
#define SBE_NULLVALUE_UINT8 (std::numeric_limits<std::uint8_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT16 (std::numeric_limits<std::uint16_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT32 (std::numeric_limits<std::uint32_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT64 (std::numeric_limits<std::uint64_t>::max)()
|
||||
|
||||
|
||||
namespace iLinkBinary {
|
||||
|
||||
class BooleanFlag
|
||||
{
|
||||
public:
|
||||
enum Value
|
||||
{
|
||||
False = (std::uint8_t)0,
|
||||
True = (std::uint8_t)1,
|
||||
NULL_VALUE = (std::uint8_t)255
|
||||
};
|
||||
|
||||
static BooleanFlag::Value get(const std::uint8_t value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0: return False;
|
||||
case 1: return True;
|
||||
case 255: return NULL_VALUE;
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum BooleanFlag [E103]");
|
||||
}
|
||||
|
||||
static const char* c_str(const BooleanFlag::Value value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case False: return "False";
|
||||
case True: return "True";
|
||||
case NULL_VALUE: return "NULL_VALUE";
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum BooleanFlag [E103]:");
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
friend std::basic_ostream<CharT, Traits>& operator << (
|
||||
std::basic_ostream<CharT, Traits>& os, BooleanFlag::Value m)
|
||||
{
|
||||
return os << BooleanFlag::c_str(m);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
/* Generated SBE (Simple Binary Encoding) message codec */
|
||||
#ifndef _ILINKBINARY_BOOLEANNULL_H_
|
||||
#define _ILINKBINARY_BOOLEANNULL_H_
|
||||
|
||||
#if defined(SBE_HAVE_CMATH)
|
||||
/* cmath needed for std::numeric_limits<double>::quiet_NaN() */
|
||||
# include <cmath>
|
||||
# define SBE_FLOAT_NAN std::numeric_limits<float>::quiet_NaN()
|
||||
# define SBE_DOUBLE_NAN std::numeric_limits<double>::quiet_NaN()
|
||||
#else
|
||||
/* math.h needed for NAN */
|
||||
# include <math.h>
|
||||
# define SBE_FLOAT_NAN NAN
|
||||
# define SBE_DOUBLE_NAN NAN
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
# define SBE_CONSTEXPR constexpr
|
||||
# define SBE_NOEXCEPT noexcept
|
||||
#else
|
||||
# define SBE_CONSTEXPR
|
||||
# define SBE_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
# define SBE_CONSTEXPR_14 constexpr
|
||||
#else
|
||||
# define SBE_CONSTEXPR_14
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
# include <string_view>
|
||||
# define SBE_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
# define SBE_NODISCARD
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_LIMIT_MACROS)
|
||||
# define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) _byteswap_ushort(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) _byteswap_ulong(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) _byteswap_uint64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) (v)
|
||||
#else
|
||||
# error "Byte Ordering of platform not determined. Set __BYTE_ORDER__ manually before including this file."
|
||||
#endif
|
||||
|
||||
#if defined(SBE_NO_BOUNDS_CHECK)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (false)
|
||||
#elif defined(_MSC_VER)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (exp)
|
||||
#else
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (__builtin_expect(exp,c))
|
||||
#endif
|
||||
|
||||
#define SBE_NULLVALUE_INT8 (std::numeric_limits<std::int8_t>::min)()
|
||||
#define SBE_NULLVALUE_INT16 (std::numeric_limits<std::int16_t>::min)()
|
||||
#define SBE_NULLVALUE_INT32 (std::numeric_limits<std::int32_t>::min)()
|
||||
#define SBE_NULLVALUE_INT64 (std::numeric_limits<std::int64_t>::min)()
|
||||
#define SBE_NULLVALUE_UINT8 (std::numeric_limits<std::uint8_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT16 (std::numeric_limits<std::uint16_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT32 (std::numeric_limits<std::uint32_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT64 (std::numeric_limits<std::uint64_t>::max)()
|
||||
|
||||
|
||||
namespace iLinkBinary {
|
||||
|
||||
class BooleanNULL
|
||||
{
|
||||
public:
|
||||
enum Value
|
||||
{
|
||||
False = (std::uint8_t)0,
|
||||
True = (std::uint8_t)1,
|
||||
NULL_VALUE = (std::uint8_t)255
|
||||
};
|
||||
|
||||
static BooleanNULL::Value get(const std::uint8_t value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0: return False;
|
||||
case 1: return True;
|
||||
case 255: return NULL_VALUE;
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum BooleanNULL [E103]");
|
||||
}
|
||||
|
||||
static const char* c_str(const BooleanNULL::Value value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case False: return "False";
|
||||
case True: return "True";
|
||||
case NULL_VALUE: return "NULL_VALUE";
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum BooleanNULL [E103]:");
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
friend std::basic_ostream<CharT, Traits>& operator << (
|
||||
std::basic_ostream<CharT, Traits>& os, BooleanNULL::Value m)
|
||||
{
|
||||
return os << BooleanNULL::c_str(m);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,141 @@
|
||||
/* Generated SBE (Simple Binary Encoding) message codec */
|
||||
#ifndef _ILINKBINARY_CLEARINGACCTTYPE_H_
|
||||
#define _ILINKBINARY_CLEARINGACCTTYPE_H_
|
||||
|
||||
#if defined(SBE_HAVE_CMATH)
|
||||
/* cmath needed for std::numeric_limits<double>::quiet_NaN() */
|
||||
# include <cmath>
|
||||
# define SBE_FLOAT_NAN std::numeric_limits<float>::quiet_NaN()
|
||||
# define SBE_DOUBLE_NAN std::numeric_limits<double>::quiet_NaN()
|
||||
#else
|
||||
/* math.h needed for NAN */
|
||||
# include <math.h>
|
||||
# define SBE_FLOAT_NAN NAN
|
||||
# define SBE_DOUBLE_NAN NAN
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
# define SBE_CONSTEXPR constexpr
|
||||
# define SBE_NOEXCEPT noexcept
|
||||
#else
|
||||
# define SBE_CONSTEXPR
|
||||
# define SBE_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
# define SBE_CONSTEXPR_14 constexpr
|
||||
#else
|
||||
# define SBE_CONSTEXPR_14
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
# include <string_view>
|
||||
# define SBE_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
# define SBE_NODISCARD
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_LIMIT_MACROS)
|
||||
# define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) _byteswap_ushort(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) _byteswap_ulong(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) _byteswap_uint64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) (v)
|
||||
#else
|
||||
# error "Byte Ordering of platform not determined. Set __BYTE_ORDER__ manually before including this file."
|
||||
#endif
|
||||
|
||||
#if defined(SBE_NO_BOUNDS_CHECK)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (false)
|
||||
#elif defined(_MSC_VER)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (exp)
|
||||
#else
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (__builtin_expect(exp,c))
|
||||
#endif
|
||||
|
||||
#define SBE_NULLVALUE_INT8 (std::numeric_limits<std::int8_t>::min)()
|
||||
#define SBE_NULLVALUE_INT16 (std::numeric_limits<std::int16_t>::min)()
|
||||
#define SBE_NULLVALUE_INT32 (std::numeric_limits<std::int32_t>::min)()
|
||||
#define SBE_NULLVALUE_INT64 (std::numeric_limits<std::int64_t>::min)()
|
||||
#define SBE_NULLVALUE_UINT8 (std::numeric_limits<std::uint8_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT16 (std::numeric_limits<std::uint16_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT32 (std::numeric_limits<std::uint32_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT64 (std::numeric_limits<std::uint64_t>::max)()
|
||||
|
||||
|
||||
namespace iLinkBinary {
|
||||
|
||||
class ClearingAcctType
|
||||
{
|
||||
public:
|
||||
enum Value
|
||||
{
|
||||
Customer = (std::uint8_t)0,
|
||||
Firm = (std::uint8_t)1,
|
||||
NULL_VALUE = (std::uint8_t)255
|
||||
};
|
||||
|
||||
static ClearingAcctType::Value get(const std::uint8_t value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0: return Customer;
|
||||
case 1: return Firm;
|
||||
case 255: return NULL_VALUE;
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum ClearingAcctType [E103]");
|
||||
}
|
||||
|
||||
static const char* c_str(const ClearingAcctType::Value value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case Customer: return "Customer";
|
||||
case Firm: return "Firm";
|
||||
case NULL_VALUE: return "NULL_VALUE";
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum ClearingAcctType [E103]:");
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
friend std::basic_ostream<CharT, Traits>& operator << (
|
||||
std::basic_ostream<CharT, Traits>& os, ClearingAcctType::Value m)
|
||||
{
|
||||
return os << ClearingAcctType::c_str(m);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
/* Generated SBE (Simple Binary Encoding) message codec */
|
||||
#ifndef _ILINKBINARY_CMTAGIVEUPCD_H_
|
||||
#define _ILINKBINARY_CMTAGIVEUPCD_H_
|
||||
|
||||
#if defined(SBE_HAVE_CMATH)
|
||||
/* cmath needed for std::numeric_limits<double>::quiet_NaN() */
|
||||
# include <cmath>
|
||||
# define SBE_FLOAT_NAN std::numeric_limits<float>::quiet_NaN()
|
||||
# define SBE_DOUBLE_NAN std::numeric_limits<double>::quiet_NaN()
|
||||
#else
|
||||
/* math.h needed for NAN */
|
||||
# include <math.h>
|
||||
# define SBE_FLOAT_NAN NAN
|
||||
# define SBE_DOUBLE_NAN NAN
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
# define SBE_CONSTEXPR constexpr
|
||||
# define SBE_NOEXCEPT noexcept
|
||||
#else
|
||||
# define SBE_CONSTEXPR
|
||||
# define SBE_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
# define SBE_CONSTEXPR_14 constexpr
|
||||
#else
|
||||
# define SBE_CONSTEXPR_14
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
# include <string_view>
|
||||
# define SBE_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
# define SBE_NODISCARD
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_LIMIT_MACROS)
|
||||
# define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) _byteswap_ushort(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) _byteswap_ulong(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) _byteswap_uint64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) (v)
|
||||
#else
|
||||
# error "Byte Ordering of platform not determined. Set __BYTE_ORDER__ manually before including this file."
|
||||
#endif
|
||||
|
||||
#if defined(SBE_NO_BOUNDS_CHECK)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (false)
|
||||
#elif defined(_MSC_VER)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (exp)
|
||||
#else
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (__builtin_expect(exp,c))
|
||||
#endif
|
||||
|
||||
#define SBE_NULLVALUE_INT8 (std::numeric_limits<std::int8_t>::min)()
|
||||
#define SBE_NULLVALUE_INT16 (std::numeric_limits<std::int16_t>::min)()
|
||||
#define SBE_NULLVALUE_INT32 (std::numeric_limits<std::int32_t>::min)()
|
||||
#define SBE_NULLVALUE_INT64 (std::numeric_limits<std::int64_t>::min)()
|
||||
#define SBE_NULLVALUE_UINT8 (std::numeric_limits<std::uint8_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT16 (std::numeric_limits<std::uint16_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT32 (std::numeric_limits<std::uint32_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT64 (std::numeric_limits<std::uint64_t>::max)()
|
||||
|
||||
|
||||
namespace iLinkBinary {
|
||||
|
||||
class CmtaGiveUpCD
|
||||
{
|
||||
public:
|
||||
enum Value
|
||||
{
|
||||
GiveUp = (char)71,
|
||||
SGXoffset = (char)83,
|
||||
NULL_VALUE = (char)0
|
||||
};
|
||||
|
||||
static CmtaGiveUpCD::Value get(const char value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 71: return GiveUp;
|
||||
case 83: return SGXoffset;
|
||||
case 0: return NULL_VALUE;
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum CmtaGiveUpCD [E103]");
|
||||
}
|
||||
|
||||
static const char* c_str(const CmtaGiveUpCD::Value value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case GiveUp: return "GiveUp";
|
||||
case SGXoffset: return "SGXoffset";
|
||||
case NULL_VALUE: return "NULL_VALUE";
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum CmtaGiveUpCD [E103]:");
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
friend std::basic_ostream<CharT, Traits>& operator << (
|
||||
std::basic_ostream<CharT, Traits>& os, CmtaGiveUpCD::Value m)
|
||||
{
|
||||
return os << CmtaGiveUpCD::c_str(m);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
/* Generated SBE (Simple Binary Encoding) message codec */
|
||||
#ifndef _ILINKBINARY_CUSTORDHANDLINST_H_
|
||||
#define _ILINKBINARY_CUSTORDHANDLINST_H_
|
||||
|
||||
#if defined(SBE_HAVE_CMATH)
|
||||
/* cmath needed for std::numeric_limits<double>::quiet_NaN() */
|
||||
# include <cmath>
|
||||
# define SBE_FLOAT_NAN std::numeric_limits<float>::quiet_NaN()
|
||||
# define SBE_DOUBLE_NAN std::numeric_limits<double>::quiet_NaN()
|
||||
#else
|
||||
/* math.h needed for NAN */
|
||||
# include <math.h>
|
||||
# define SBE_FLOAT_NAN NAN
|
||||
# define SBE_DOUBLE_NAN NAN
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
# define SBE_CONSTEXPR constexpr
|
||||
# define SBE_NOEXCEPT noexcept
|
||||
#else
|
||||
# define SBE_CONSTEXPR
|
||||
# define SBE_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
# define SBE_CONSTEXPR_14 constexpr
|
||||
#else
|
||||
# define SBE_CONSTEXPR_14
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
# include <string_view>
|
||||
# define SBE_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
# define SBE_NODISCARD
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_LIMIT_MACROS)
|
||||
# define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) _byteswap_ushort(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) _byteswap_ulong(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) _byteswap_uint64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)
|
||||
# define SBE_LITTLE_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_16(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_32(v) (v)
|
||||
# define SBE_BIG_ENDIAN_ENCODE_64(v) (v)
|
||||
#else
|
||||
# error "Byte Ordering of platform not determined. Set __BYTE_ORDER__ manually before including this file."
|
||||
#endif
|
||||
|
||||
#if defined(SBE_NO_BOUNDS_CHECK)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (false)
|
||||
#elif defined(_MSC_VER)
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (exp)
|
||||
#else
|
||||
# define SBE_BOUNDS_CHECK_EXPECT(exp,c) (__builtin_expect(exp,c))
|
||||
#endif
|
||||
|
||||
#define SBE_NULLVALUE_INT8 (std::numeric_limits<std::int8_t>::min)()
|
||||
#define SBE_NULLVALUE_INT16 (std::numeric_limits<std::int16_t>::min)()
|
||||
#define SBE_NULLVALUE_INT32 (std::numeric_limits<std::int32_t>::min)()
|
||||
#define SBE_NULLVALUE_INT64 (std::numeric_limits<std::int64_t>::min)()
|
||||
#define SBE_NULLVALUE_UINT8 (std::numeric_limits<std::uint8_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT16 (std::numeric_limits<std::uint16_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT32 (std::numeric_limits<std::uint32_t>::max)()
|
||||
#define SBE_NULLVALUE_UINT64 (std::numeric_limits<std::uint64_t>::max)()
|
||||
|
||||
|
||||
namespace iLinkBinary {
|
||||
|
||||
class CustOrdHandlInst
|
||||
{
|
||||
public:
|
||||
enum Value
|
||||
{
|
||||
FCMprovidedscreen = (char)67,
|
||||
Otherprovidedscreen = (char)68,
|
||||
FCMAPIorFIX = (char)71,
|
||||
AlgoEngine = (char)72,
|
||||
DeskElectronic = (char)87,
|
||||
ClientElectronic = (char)89,
|
||||
NULL_VALUE = (char)0
|
||||
};
|
||||
|
||||
static CustOrdHandlInst::Value get(const char value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 67: return FCMprovidedscreen;
|
||||
case 68: return Otherprovidedscreen;
|
||||
case 71: return FCMAPIorFIX;
|
||||
case 72: return AlgoEngine;
|
||||
case 87: return DeskElectronic;
|
||||
case 89: return ClientElectronic;
|
||||
case 0: return NULL_VALUE;
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum CustOrdHandlInst [E103]");
|
||||
}
|
||||
|
||||
static const char* c_str(const CustOrdHandlInst::Value value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case FCMprovidedscreen: return "FCMprovidedscreen";
|
||||
case Otherprovidedscreen: return "Otherprovidedscreen";
|
||||
case FCMAPIorFIX: return "FCMAPIorFIX";
|
||||
case AlgoEngine: return "AlgoEngine";
|
||||
case DeskElectronic: return "DeskElectronic";
|
||||
case ClientElectronic: return "ClientElectronic";
|
||||
case NULL_VALUE: return "NULL_VALUE";
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown value for enum CustOrdHandlInst [E103]:");
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
friend std::basic_ostream<CharT, Traits>& operator << (
|
||||
std::basic_ostream<CharT, Traits>& os, CustOrdHandlInst::Value m)
|
||||
{
|
||||
return os << CustOrdHandlInst::c_str(m);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Loaded 100 of 267 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user