Compare commits

...
Author SHA1 Message Date
Mohammad Arham a986dacc60 file restructuring for sdk 2024-04-17 16:17:26 +00:00
Mohammad Arham 5b1bf06feb active cme working small fixex and conf left 2024-04-17 01:13:23 -04:00
John Colvin bc6dc44555 Add Activ md libs to sparkd
WARNING: This breaks the sparkd clang build (only works with gcc)
2024-04-12 12:30:36 -04:00
John Colvin 01e62b9aae build: Upgrade gcc to v12 (explicitly) 2024-04-12 12:28:48 -04:00
Mohammad Arham 64f2bd7a88 removed quoting algo so can be loaded dynamically 2024-04-04 20:38:15 -04:00
Mohammad Arham 3e32f684e3 removed redundant code 2024-04-03 16:56:31 -04:00
Mohammad Arham ff73756a55 removed assert if delete order that is completed 2024-04-03 16:55:29 -04:00
Mohammad Arham 1e64811d93 changed error code 2024-04-03 16:54:51 -04:00
John Colvin 2488597cf2 fix build (linux api server): Remove srcs that moved to sparkd 2024-04-02 16:25:13 +00:00
John Colvin 2d8047ba80 build: Tidy up build/publish_spark scripts
In preparation for the first deployment of the "development" branch.
2024-04-02 01:47:47 -05:00
John Colvin 3cb602b77e dev: Remove irrelevant comment from devcontainer.json
It was added by accident
2024-04-02 06:10:25 +00:00
John Colvin 51e1005e79 dev: Fix compile-commands-dir in devcontainer.json
It should be '${workspaceFolder}/build/clang/debug'
2024-04-02 06:01:14 +00:00
John Colvin ef7ea64973 dev: Add -y to 'apt install libpcap' in Dockerfile
Fixes the docker build
2024-04-02 05:57:03 +00:00
Mohammad Arham 0267c16e5a Merge branch 'development' of bitbucket.org:bluetradesys/spark into development 2024-04-01 19:02:32 -04:00
Mohammad Arham ca40e278ff quoting crash fix and comments 2024-04-01 19:01:27 -04:00
John Colvin 1c5da7ad48 cleanup: Tidy up hdrs included in cpp algo sdk 2024-04-01 10:09:35 -04:00
John Colvin 9bb3d5c569 refactor: Move ChildOrder defs to cpp file
So deps can be omitted in header
2024-04-01 10:09:35 -04:00
John Colvin 92ba43bfad refactor: Move ccapi logger into its own file 2024-04-01 10:09:35 -04:00
John Colvin ddeb5e59f0 fix: Add missing exchs to risk webapp 2024-04-01 10:09:35 -04:00
John Colvin 6b0b3e31cc cleanup: Move nested Trade class into its own file
Cut down on dep clutter
2024-04-01 10:09:35 -04:00
John Colvin 3f6c904ccc cleanup: Get rid of redundant SecurityKey def
Make bts::Security an alias for bts::md::Security instead
2024-04-01 10:09:35 -04:00
John Colvin d433919f1b refactor: Move OrderBuilder defs to cpp file
So impl deps don't leak into the sdk through headers
2024-04-01 10:09:35 -04:00
John Colvin 75da9af39d improvement: Add fmt impl for SecurityKey 2024-04-01 10:09:34 -04:00
John Colvin 22eff44926 cleanup: Tidy up Book.h 2024-04-01 10:09:34 -04:00
John Colvin cd6a052d0c cleanup: Tidy up IAlgoOrder, add fmt impl for ChildState
Part of a larger effort to polish the sdk for laguna
2024-04-01 10:09:34 -04:00
John Colvin 3b6cf0b187 cleanup: Remove unnecessary includes 2024-04-01 10:09:34 -04:00
John Colvin c170dd7a9e refactor: Include ostream in EnumMacros.h
So it doesn't need to be explicitly included to support ENUM_CLASS_DEF
2024-04-01 10:09:34 -04:00
John Colvin 154775bc70 refactor: Move misplaced code from libspark to sparkd
This will reduce the dependency load on libspark and the c++ algo sdk.
This has been on the backburner for a while; we're doing it now so we
can deliver a clean sdk to Laguna.

Migrating the following code:
  * general server-side md utils (MarketDataGateway, etc.)
  * emumba dependent code, including md impl for:
    - deribit
  * ccapi dependent code, including md impl for
    - binance
    - binance_us
    - binance_usds_futures
    - ftx
    - coinbase
    - okx
    - deribit
  * md recording

Note: I first began this migration to eliminate the c++ algo sdk's
wependency on emumba. There seemed to be a conflict related to ssl
versions (the build in the sdk docker would fail with unresolved
external symbols for OPENSSL macros)
2024-04-01 10:09:34 -04:00
John Colvin 7f936f3ba9 fix: Inline defs in hdr file 2024-04-01 10:09:24 -04:00
John Colvin 9b30a947a8 fix build: Add a few includes, suppress some werrors
Not sure how this slipped through the cracks, but I'm guessing the build
broke when we upgraded the cpp compilers (to clang 17 and gcc 13).

* Include <cstdint> in a few places
* Suppress -Wstringop-overflow in sparkd-gcc-release build
* Change const char * param to explicit std::string for proper type
  deduction in call to 'std::make_tuple'. For some reason, the gcc build
  was failing with an array-bounds werror (changing the arg fixed it).
2024-04-01 10:09:24 -04:00
John Colvin 2ee58d8f99 dev: Add devcontainer.json (for linux) 2024-04-01 10:09:24 -04:00
John Colvin 0537b3e9c7 cleanup: Remove mongodb.js file that was added by accident 2024-04-01 10:09:24 -04:00
John Colvin 8ae940bcbc fix: Change "init" call to "initAsync" in spark_api_server main
WebsocketDataSync.init was refactored to initAsync
2024-04-01 10:09:23 -04:00
Mohammad Arham 966ccb5f2c fixed order entry 2024-04-01 07:48:36 -04:00
Mohammad Arham fae9c3e59e quantity connected, fills reported, limit set as default 2024-04-01 07:46:36 -04:00
Mohammad Arham cb1dfb257d updated defaults to have max_order_msgs_per_window 2024-03-29 16:00:36 -04:00
Mohammad Arham 76c3fe86c2 quote algo quantity hooked 2024-03-29 14:42:15 -04:00
Mohammad Arham 7e3b1de70f bug fix 2024-03-29 00:38:33 -04:00
Mohammad Arham 2a07e3cf3c custom orders working on api 2024-03-29 00:37:04 -04:00
Mohammad Arham 6d0249825d quotingalgo working 2024-03-29 00:35:49 -04:00
Mohammad Arham 63810e3966 build fix 2024-03-26 18:07:48 -04:00
Mohammad Arham 4c013a8886 reorganise code 2024-03-26 18:07:01 -04:00
Mohammad Arham 9509818978 set theos 2024-03-26 18:06:07 -04:00
Mohammad Arham 4fdf3b8b60 just cleaner code 2024-03-26 02:10:06 -04:00
Mohammad Arham ee9a58486d cleaner code same logic 2024-03-25 02:52:51 -04:00
Mohammad Arham 75d6939c8b added new rules for onix decode ticks 2024-03-25 01:48:22 -04:00
Mohammad Arham fb1697b571 spelling fix 2024-03-24 23:17:02 -04:00
Mohammad Arham 4080d606d8 fixed get trades 2024-03-24 23:16:38 -04:00
Mohammad Arham 7655fd47dd fixed spelling 2024-03-24 23:14:11 -04:00
Taha Afzal d029b0fce7 SimExecution works with GLBX now 2024-03-18 23:54:19 -04:00
292 changed files with 3759 additions and 1984 deletions

No files matched your search

+122
View File
@@ -0,0 +1,122 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
# Note installations that take a while (more than 10s)
RUN apt-get update
RUN apt-get install -y ca-certificates
RUN apt-get install -y ccache
RUN apt-get install -y curl
RUN apt-get install -y fish
RUN apt-get install -y gdb
# git-all: ~250s
RUN apt-get install -y git-all
RUN apt-get install -y gnupg
# liblttng-ust-dev: ~30s
RUN apt-get install -y liblttng-ust-dev
RUN apt-get install -y libssh2-1-dev
RUN apt-get install -y locales
RUN apt-get install -y ninja-build
# openssh-server: ~40s
RUN apt-get install -y openssh-server
RUN apt-get install -y pkg-config
RUN apt-get install -y software-properties-common
RUN apt-get install -y sudo
RUN apt-get install -y tzdata
RUN apt-get install -y vim
RUN apt-get install -y zip
# Python: ~30s
RUN apt-get install -y \
python3-pip \
python3-venv \
&& python3 -m pip install --upgrade pip
# GCC
ARG gcc_major=13
RUN \
add-apt-repository ppa:ubuntu-toolchain-r/test \
&& apt-get update \
&& apt-get install -y g++-$gcc_major
# Clang
ARG clang_major=17
RUN \
curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key 2> /dev/null \
| gpg --dearmor - \
| tee /usr/share/keyrings/llvm-snapshot.gpg > /dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg] http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-$clang_major main" \
| tee -a "/etc/apt/sources.list.d/llvm.list" \
&& apt-get update \
&& apt-get install -y \
clang-$clang_major \
clang-format-$clang_major \
clangd-$clang_major
# CMake
RUN \
curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc 2> /dev/null \
| gpg --dearmor - \
| tee /usr/share/keyrings/kitware-archive-keyring.gpg > /dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" \
| tee /etc/apt/sources.list.d/kitware.list > /dev/null \
&& apt-get update \
&& apt-get install -y cmake
# Dotnet
RUN apt-get update && apt-get install -y dotnet7
# Node
ARG node_major=18
RUN mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$node_major.x nodistro main" \
| tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& apt-get install -y nodejs
# Emumba (deps)
RUN apt-get update && apt-get -y install rapidjson-dev
# Emumba
COPY emumba /emumba
RUN dpkg -i /emumba/emumba-deribit-connector-3.3.1-Linux.LICENSE_DISABLED.deb \
&& rm -r /emumba
# Pcap
RUN apt-get update && apt-get install -y libpcap0.8-dev
# Set up default executables (update-alternatives)
RUN \
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-$clang_major 100; \
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-$clang_major 100; \
update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-$clang_major 100; \
update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-$clang_major 100; \
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-$gcc_major 100; \
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-$gcc_major 100
# Add compiler symlinks for ccache
RUN \
ln -s /usr/bin/ccache /usr/local/bin/clang; \
ln -s /usr/bin/ccache /usr/local/bin/clang++; \
ln -s /usr/bin/ccache /usr/local/bin/clang-$clang_major; \
ln -s /usr/bin/ccache /usr/local/bin/clang++-$clang_major; \
ln -s /usr/bin/ccache /usr/local/bin/gcc; \
ln -s /usr/bin/ccache /usr/local/bin/g++
RUN locale-gen en_US.UTF-8
ARG docker_user="bts"
ARG docker_uid="1000"
ARG docker_pass="bts"
RUN \
groupadd -g "$docker_uid" "$docker_user" \
&& useradd \
-m \
-u "$docker_uid" \
-g "$docker_uid" \
-G sudo \
--password "$(openssl passwd -1 "$docker_pass")" \
"$docker_user"
+48
View File
@@ -0,0 +1,48 @@
{
"dockerFile": "Dockerfile",
// Creates local dir to mount to HOME in docker
"initializeCommand": "mkdir -p ${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId}",
"hostRequirements": {
"memory": "4gb",
"storage": "6gb"
},
"postCreateCommand": "/home/bts/spark/docker/sparkd/dev/devcontainer_post_create.sh",
"containerUser": "bts",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/bts/spark,type=bind",
"workspaceFolder": "/home/bts/spark",
"updateRemoteUserUID": true,
"shutdownAction": "none",
// Note: If running multiple containers on the same host, each must use a
// unique set of ports. Usually we just add 1000 to each port as needed. For
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
"runArgs": [
"--shm-size",
"2gb",
"-p",
"8000:8000",
"-p",
"8001:8001",
"-p",
"8002:8002",
"-p",
"8003:8003"
],
"mounts": [
"source=${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId},target=/home/bts,type=bind"
],
"customizations": {
"vscode": {
"settings": {
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}/build/clang/debug"
]
},
"extensions": [
"eamodio.gitlens",
"llvm-vs-code-extensions.vscode-clangd",
"ms-vscode.cmake-tools",
"ms-vscode.cpptools"
]
}
}
}
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
this_dir="$(realpath "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")"
sudo dpkg -i "$this_dir/emumba-deribit-connector-3.3.1-Linux.LICENSE_DISABLED.deb"
+1 -1
View File
@@ -54,6 +54,6 @@ trace_data/
/src/Spark.Admin.WebApp/src/bin/
**/CMakeDeps.cmake
**/CMakeDeps_win.cmake
**/.devcontainer
.devcontainer/
*.Generated.cs
*.lst
+3 -3
View File
@@ -113,8 +113,8 @@
"description": "Debug build compiled w/ gcc",
"binaryDir": "${sourceDir}/build/gcc/debug",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_C_COMPILER": "gcc-12",
"CMAKE_CXX_COMPILER": "g++-12",
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -Wno-stringop-overread -DDEBUG",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/gcc/debug/sparkd"
}
@@ -143,7 +143,7 @@
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -Wno-stringop-overread -Wno-maybe-uninitialized -fno-omit-frame-pointer -DNDEBUG -O3",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers -Wno-stringop-overflow --std=c++20 -fdiagnostics-color -Wno-stringop-overread -Wno-maybe-uninitialized -fno-omit-frame-pointer -DNDEBUG -O3",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/gcc/release/sparkd"
}
},
-45
View File
@@ -1,45 +0,0 @@
/* global use, db */
// MongoDB Playground
// To disable this template go to Settings | MongoDB | Use Default Template For Playground.
// Make sure you are connected to enable completions and to be able to run a playground.
// Use Ctrl+Space inside a snippet or a string literal to trigger completions.
// The result of the last command run in a playground is shown on the results panel.
// By default the first 20 documents will be returned with a cursor.
// Use 'console.log()' to print to the debug output.
// For more documentation on playgrounds please refer to
// https://www.mongodb.com/docs/mongodb-vscode/playgrounds/
// Select the database to use.
use('mongodbVSCodePlaygroundDB');
// Insert a few documents into the sales collection.
db.getCollection('sales').insertMany([
{ 'item': 'abc', 'price': 10, 'quantity': 2, 'date': new Date('2014-03-01T08:00:00Z') },
{ 'item': 'jkl', 'price': 20, 'quantity': 1, 'date': new Date('2014-03-01T09:00:00Z') },
{ 'item': 'xyz', 'price': 5, 'quantity': 10, 'date': new Date('2014-03-15T09:00:00Z') },
{ 'item': 'xyz', 'price': 5, 'quantity': 20, 'date': new Date('2014-04-04T11:21:39.736Z') },
{ 'item': 'abc', 'price': 10, 'quantity': 10, 'date': new Date('2014-04-04T21:23:13.331Z') },
{ 'item': 'def', 'price': 7.5, 'quantity': 5, 'date': new Date('2015-06-04T05:08:13Z') },
{ 'item': 'def', 'price': 7.5, 'quantity': 10, 'date': new Date('2015-09-10T08:43:00Z') },
{ 'item': 'abc', 'price': 10, 'quantity': 5, 'date': new Date('2016-02-06T20:20:13Z') },
]);
// Run a find command to view items sold on April 4th, 2014.
const salesOnApril4th = db.getCollection('sales').find({
date: { $gte: new Date('2014-04-04'), $lt: new Date('2014-04-05') }
}).count();
// Print a message to the output window.
console.log(`${salesOnApril4th} sales occurred in 2014.`);
// Here we run an aggregation and open a cursor to the results.
// Use '.toArray()' to exhaust the cursor to return the whole result set.
// You can use '.hasNext()/.next()' to iterate through the cursor page by page.
db.getCollection('sales').aggregate([
// Find all of the sales that occurred in 2014.
{ $match: { date: { $gte: new Date('2014-01-01'), $lt: new Date('2015-01-01') } } },
// Group the total sales for each product.
{ $group: { _id: '$item', totalSaleAmount: { $sum: { $multiply: ['$price', '$quantity'] } } } }
]);
+22 -2
View File
@@ -17,9 +17,9 @@ type Tab =
| PositionManagementTab
| MarginTab
let venues = [| Venue.DERIBIT; Venue.GLBX; Venue.ICE; Venue.SIM; |]
let venues = [| Venue.BINANCE; Venue.BinanceUs; Venue.BinanceUsdsFutures; Venue.COINBASE; Venue.DERIBIT; Venue.GLBX; Venue.ICE; Venue.SIM; |]
let securityTypes = [| SecurityType.FUTURE; SecurityType.OPTION; SecurityType.SPREAD; |]
let securityTypes = [| SecurityType.SPOT; SecurityType.FUTURE; SecurityType.OPTION; SecurityType.SPREAD; |]
let emptyAccount = Access.Accounts.empty
@@ -35,6 +35,26 @@ type CMEExchangeField =
| ClearingAccount of string
| MiscTagMapping of JSMap<string>
type BINANCEExchangeField =
| CTI of CTICode
| Origin of CustomerOrFirm
| ClearingAccount of string
type BinanceUsExchangeField =
| CTI of CTICode
| Origin of CustomerOrFirm
| ClearingAccount of string
type BinanceUsdsFUTURESExchangeField =
| CTI of CTICode
| Origin of CustomerOrFirm
| ClearingAccount of string
type COINBASEExchangeField =
| CTI of CTICode
| Origin of CustomerOrFirm
| ClearingAccount of string
type DERIBITExchangeField =
| CTI of CTICode
| Origin of CustomerOrFirm
@@ -797,6 +797,7 @@ let venueToString (venue:bts.api.Venue) =
| bts.api.Venue.BINANCE -> "BINANCE"
| bts.api.Venue.BinanceUs -> "BinanceUs"
| bts.api.Venue.BinanceUsdsFutures -> "BinanceUsdsFutures"
| bts.api.Venue.COINBASE -> "COINBASE"
| bts.api.Venue.FTX -> "FTX"
| bts.api.Venue.OKX -> "OKX"
+1 -1
View File
@@ -36,7 +36,7 @@ namespace Spark.Api.MarketData {
public override int GetHashCode() =>
security.Id.GetHashCode();
public static Object ToJsonObj(BookTuple obj) => new {
secuirty = obj.security.ToString(),
security = obj.security.ToString(),
book = obj.book.ToJsonObj()
};
public static bool operator ==(BookTuple left, BookTuple right) =>
@@ -67,7 +67,10 @@ namespace mbts.co2 {
[DllImport("libspark")]
internal unsafe static extern int md_consumer_read_trades(
IntPtr consumer, UserSymbol channel, ref long cursor, raw_receive_pb cb);
[DllImport("libspark")]
internal unsafe static extern int md_consumer_read_trades_linux(
IntPtr consumer, string symbol, int venue, int type, ref long cursor, raw_receive_pb cb);
[DllImport("libspark")]
internal unsafe static extern int md_consumer_read_rfqs(
IntPtr consumer, UserSymbol channel, ref long cursor, raw_receive_pb cb);
@@ -23,7 +23,7 @@ namespace mbts.co2 {
public unsafe IList<TradeMessage> read_trades(ref long cursor) {
IList<TradeMessage> trades = Array.Empty<TradeMessage>();
Co2NativeMethods.md_consumer_read_trades(_consumer.Handle, _symbol, ref cursor, callback);
Co2NativeMethods.md_consumer_read_trades_linux(_consumer.Handle, _symbol.symbol,(int)_symbol.exchange,(int)_symbol.type , ref cursor, callback);
return trades;
void callback(byte* buffer, ulong size) =>
+1 -1
View File
@@ -28,7 +28,7 @@ namespace mbts.spark {
public TickDisplaySettingsImmutable qty_display => security_md.quantity_display;
public static System.Object ToJsonObj(TradeInfo obj) => new {
secuirty = obj.security.Exchange.ToString() + "." + obj.security.ToString(),
security = obj.security.Exchange.ToString() + "." + obj.security.ToString(),
price = obj.trade.price(),
timestamp = obj.trade.Time,
quantity = obj.volume,
+31 -7
View File
@@ -1,6 +1,8 @@
#nullable enable
namespace Spark.Api.Orders {
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.position;
@@ -14,6 +16,8 @@ namespace Spark.Api.Orders {
using Spark.Core.Reactive;
using System;
using System.Collections.Generic;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Threading.Tasks;
@@ -55,7 +59,7 @@ namespace Spark.Api.Orders {
o => o.order.ParentKey,
(o, r) => new OrderRecordTupleWithRoot(o, r),
o => new OrderRecordTupleWithRoot(o),
o => o.root.order.Key)
o => o.key)
.Cache()
.ToObservable());
@@ -127,11 +131,29 @@ namespace Spark.Api.Orders {
});
return r.value;
} catch {
var r = new Response();
r.ErrorCode = OrderError.Undefined;
var r = new Response {
ErrorCode = OrderError.Undefined
};
return r;
}
}
public async Task<Response> TrySetPrecomputedTheosAsync(
List<PrecomputedTheoPoint> ptp_list
) {
bts.api.orderman.PrecomputedTheos server_theos = new();
server_theos.Theos.AddRange(ptp_list);
var r = await _order_service.set_precomputed_theos_async(server_theos);
var r2 = new Response {
ErrorCode = OrderError.Ok,
Msg = r.ToString()
};
return r2;
}
public async Task<RegisteredDynamicOrderTypes> GetRegisteredDynamicOrderTypesAsync(){
Result<RegisteredDynamicOrderTypes> r = await _order_service.get_registered_dynamic_order_types_async(new bts.api.VoidMessage());
return r.value;
}
public async Task<Response> TryUpdateOrderAsync(
CustomOrderDefinition custom,
double price, double quantity, uint order_id
@@ -146,8 +168,9 @@ namespace Spark.Api.Orders {
});
return r.value;
} catch {
var r = new Response();
r.ErrorCode = OrderError.Undefined;
var r = new Response {
ErrorCode = OrderError.Undefined
};
return r;
}
}
@@ -159,8 +182,9 @@ namespace Spark.Api.Orders {
});
return r.value;
} catch {
var r = new Response();
r.ErrorCode = OrderError.Undefined;
var r = new Response {
ErrorCode = OrderError.Undefined
};
return r;
}
}
+1 -1
View File
@@ -44,7 +44,7 @@
KeepAliveIntervalSecs = 0.1,
};
gwy.init(sync);
sync.init(gwy);
await sync.initAsync(gwy);
var errors = Errors.None;
Log.Api.info("Starting websocket server...");
// try {
@@ -151,6 +151,15 @@ namespace bts.websocket_core {
_orders_client = value;
}
}
IReadOnlyDictionary<string, DynamicOrderDescriptor>? _dynamic_descriptors;
IReadOnlyDictionary<string, DynamicOrderDescriptor> Dynamic_Descriptors{
get => Log.notnull<IReadOnlyDictionary<string, DynamicOrderDescriptor>>(_dynamic_descriptors);
set {
Log.isnull<IReadOnlyDictionary<string, DynamicOrderDescriptor>>(_dynamic_descriptors);
_dynamic_descriptors=value.ToDictionary(k=>k.Key.ToLower(),v => v.Value);
}
}
WebsocketConnectionGateway? gateway;
WebsocketConnectionGateway Gateway {
get => Log.notnull<WebsocketConnectionGateway>(gateway);
@@ -178,10 +187,10 @@ namespace bts.websocket_core {
/// Should be called in the start, stores pointer for gtwy and creates dictionary for clients, starts broadcast thread
/// </summary>
/// <param name="gtwy"></param>
public void init(WebsocketConnectionGateway gtwy) {
public async Task initAsync(WebsocketConnectionGateway gtwy) {
Gateway = gtwy;
clients = new ClientsStore();
TimeSpan md_throttle = new TimeSpan(10000);
TimeSpan md_throttle = TimeSpan.FromSeconds(0.1);
MD_Source = new MDSource(md_throttle);
Md_Client = new(MD_Source);
_securities_sub = MD_Source.subscribe_securities().Subscribe(sec_map => _securities = sec_map);
@@ -197,6 +206,10 @@ namespace bts.websocket_core {
var sec_table_src = MD_Source.subscribe_securities()
.Table(s => s.security.Id)
.Select(s => s.security, s => s.Id);
var r = await Orders_Client.GetRegisteredDynamicOrderTypesAsync();
Dynamic_Descriptors = r
.DynamicOrderDesc
.ToDictionary(d => d.DynamicOrderType);
Synth_Sec_Client = new(synth_sec_service, sec_table_src);
@@ -270,6 +283,10 @@ namespace bts.websocket_core {
handle_update_order(req);
break;
}
case WebsocketRequest.REQUEST_TYPES.SET_PRECOMPUTED_THEOS: {
handle_set_theos(req);
break;
}
case WebsocketRequest.REQUEST_TYPES.UNKOWN: {
///send error
break;
@@ -284,30 +301,21 @@ namespace bts.websocket_core {
System.Console.WriteLine("error for req", client_id, ex.ToString());
}
}
public async void handle_delete_order(WebsocketRequest req) {
(bool suc, string status) = await WebsocketRestHandler.DeleteOrderAsync(req, Orders_Client);
if (!suc) {
public async Task HandleRestRequest(WebsocketRequest req, Func<WebsocketRequest, OrdersClient, Task<(bool success, uint? id, string status)>> handlerAsync) {
(bool success, uint? id, string status) = await handlerAsync(req, Orders_Client);
if (!success){
handle_incorrect_request(req, status);
return;
}
handle_correct_request(req.id, status, null, req.client_id);
}
public async void handle_enter_order(WebsocketRequest req) {
(bool suc, uint? id, string status) = await WebsocketRestHandler.EnterOrderAsync(req, Orders_Client, _securities);
if (!suc) {
handle_incorrect_request(req, status);
return;
}
handle_correct_request(req.id, status, new { order_key = id }, req.client_id);
}
public async void handle_update_order(WebsocketRequest req) {
(bool suc, string status) = await WebsocketRestHandler.UpdateOrderAsync(req, Orders_Client);
if (!suc) {
handle_incorrect_request(req, status);
return;
}
handle_correct_request(req.id, status, null, req.client_id);
handle_correct_request(req.id, status, id != null ? new { order_key = id } : null, req.client_id);
}
public async void handle_delete_order(WebsocketRequest req) { await HandleRestRequest(req,WebsocketRestHandler.DeleteOrderAsync);}
public async void handle_set_theos(WebsocketRequest req) { await HandleRestRequest(req,(req, Orders_Client) => WebsocketRestHandler.SetTheosAsync(req, Orders_Client, _securities));}
public async void handle_enter_order(WebsocketRequest req) { await HandleRestRequest(req,(req, Orders_Client) => WebsocketRestHandler.EnterOrderAsync(
req, Orders_Client, _securities,Dynamic_Descriptors
));}
public async void handle_update_order(WebsocketRequest req) { await HandleRestRequest(req,(req, Orders_Client) => WebsocketRestHandler.UpdateOrderAsync(req,Orders_Client,Dynamic_Descriptors));}
public void handle_subscription(WebsocketRequest req) {
WebsocketSubscriptionHandler? hdlr;
string status;
@@ -1,6 +1,10 @@
namespace bts.websocket_core {
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.md;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -27,9 +31,67 @@ namespace bts.websocket_core {
public static Object to_json<T>(IList<T> list)
where T : IJsonFormatter<T> =>
to_json(list, T.ToJsonObj);
public struct ListWrapper : IEnumerable<string> {
public struct PrecomputedTheoPointWrapper {
UInt32 SecId{get;init;}
public double driver{get;init;}
UInt32 DriverSecId{get;init;}
public double theo{get;init;}
public double delta{get;init;}
public double gamma{get;init;}
public double vega{get;init;}
public double theta{get;init;}
public double rho{get;init;}
public double vol{get;init;}
public Venue venue{get;init;}
public String exchange_security_name{get;init;}
public String driver_exchange_security_name{get;init;}
public static bool GetTheoPoint(PrecomputedTheoPointWrapper inp, [NotNullWhen(true)] out PrecomputedTheoPoint? p, IReadOnlyDictionary<uint, mbts.SecurityMD> _securites){
p = null;
if (!_securites.Values.TryGetFirst(sc_md =>
(
sc_md.security.Exchange == inp.venue &&
sc_md.security.ToString() == inp.exchange_security_name),
out mbts.SecurityMD sec
)) {
return false;
}
if (!_securites.Values.TryGetFirst(sc_md =>
(
sc_md.security.Exchange == inp.venue &&
sc_md.security.ToString() == inp.driver_exchange_security_name),
out mbts.SecurityMD driver_sec
)) {
return false;
}
p = new(){
SecId = sec.id,
Driver=inp.driver,
DriverSecId=driver_sec.id,
Theo=inp.theo,
Delta=inp.delta,
Gamma=inp.gamma,
Vega=inp.vega,
Theta=inp.theta,
Rho=inp.rho,
Vol=inp.vol,
};
return true;
}
public static bool TryParse(string input, [NotNullWhen(true)] out PrecomputedTheoPointWrapper? P) {
P = null;
try {
var temp = JsonConvert.DeserializeObject<PrecomputedTheoPointWrapper>(input);
P = temp;
return true;
} catch {
return false;
}
}
}
public struct StringListWrapper : IEnumerable<string> {
public List<string> Lis { get; set; }
public ListWrapper(List<string> l) {
public StringListWrapper(List<string> l) {
Lis = l;
}
@@ -49,14 +111,24 @@ namespace bts.websocket_core {
public string[] getlwrarr() {
return Lis.Select(v => v.ToLower()).ToArray();
}
public static bool TryParse(string input, [NotNullWhen(true)] out ListWrapper? l) {
public static bool TryParse(string input, [NotNullWhen(true)] out StringListWrapper? l) {
l = null;
try {
var temp = JsonConvert.DeserializeObject<List<string>>(input);
var temp = JsonConvert.DeserializeObject<List<Object>>(input);
if (temp == null) {
return false;
}
l = new ListWrapper(temp);
List<string> stringList = new List<string>();
foreach (object obj in temp)
{
string? val = obj.ToString();
if(val==null){
return false;
}
stringList.Add(val);
}
l = new StringListWrapper(stringList);
return true;
} catch {
return false;
@@ -71,26 +143,33 @@ namespace bts.websocket_core {
}
public static bool TryParse<T>(JObject filter, string key, out T[]? list, out string error) where T : struct {
T[] values = new T[] { };
list = null;
ListWrapper? wrap;
if (TryParse<ListWrapper>(filter, key, out wrap, out error)) {
if (TryParse<StringListWrapper>(filter, key, out StringListWrapper? wrap, out error)) {
if (wrap != null) {
var values = new List<T>();
foreach (var item in wrap) {
T? obj;
if (TryParse<T>(item, key, out obj, out error) && obj != null) {
values = values.ToList().Append<T>((T)obj).ToArray();
if (TryParse<T>(item, key, out T? obj, out error) && obj != null) {
values.Add(obj.Value);
} else {
return false;
}
}
list = values;
list = values.ToArray();
}
return true;
}
return false;
}
public static bool TryParse<T>(JObject filter, string key, out T? obj, [NotNullWhen(false)] out string error) where T : struct {
obj = null;
var value = filter.GetValueOrDefault(key)?.ToString();
if (value == null) {
error = $"{key} not present";
return true;
}
return TryParse<T>(value, key, out obj, out error);
}
public static bool TryParse<T>(string value, string key, [NotNullWhen(true)] out T? obj, [NotNull] out string error) where T : struct {
error = "";
if (typeof(T).IsEnum && Enum.TryParse<T>(value, true, out var result)) {
@@ -100,7 +179,10 @@ namespace bts.websocket_core {
if (typeof(T) == typeof(double) && double.TryParse(value, out var d)) {
obj = (T)(object)d;
return true;
}
if (typeof(T) == typeof(bool) && bool.TryParse(value, out var b)) {
obj = (T)(object)b;
return true;
}
if (typeof(T) == typeof(uint) && uint.TryParse(value, out var uin)) {
obj = (T)(object)uin;
@@ -118,10 +200,14 @@ namespace bts.websocket_core {
obj = (T)(object)new StringWrapper(value);
return true;
}
if (typeof(T) == typeof(ListWrapper) && ListWrapper.TryParse(value, out var l)) {
if (typeof(T) == typeof(StringListWrapper) && StringListWrapper.TryParse(value, out var l)) {
obj = (T)(object)l;
return true;
}
if (typeof(T)==typeof(PrecomputedTheoPointWrapper) && PrecomputedTheoPointWrapper.TryParse(value, out var p)){
obj=(T)(object)p;
return true;
}
if (typeof(T) == typeof(DateTime) && DateTime.TryParseExact(value, "yyyy/MM/dd", CultureInfo.InvariantCulture,
DateTimeStyles.None, out var dt)) {
obj = (T)(object)dt;
@@ -132,16 +218,7 @@ namespace bts.websocket_core {
obj = null;
return false;
}
public static bool TryParse<T>(JObject filter, string key, out T? obj, [NotNullWhen(false)] out string error) where T : struct {
obj = null;
var value = filter.GetValueOrDefault(key)?.ToString();
if (value == null) {
error = $"{key} not present";
return true;
}
return TryParse<T>(value, key, out obj, out error);
}
}
}
@@ -12,8 +12,8 @@ namespace bts.websocket_core {
UNSUBSCRIBE,
ENTER_ORDER,
DELETE_ORDER,
UPDATE_ORDER,
SET_PRECOMPUTED_THEOS,
DELETE,
PATCH,
UNKOWN = 0
@@ -6,109 +6,352 @@ namespace bts.websocket_core {
using mbts;
using Microsoft.VisualBasic;
using mbts.spark;
using Newtonsoft.Json.Linq;
using Spark.Api.Orders;
using System;
using System.Diagnostics.CodeAnalysis;
using bts.spark;
using bts.utils;
public class WebsocketRestHandler {
public static (bool,DynamicOrderParam?,string) TryGetDynamicParam(JObject custom,string key, DynamicOrderParamDescriptor descriptor, IReadOnlyDictionary<string, DynamicOrderDescriptor> Dynamic_Descriptors) {
var rv = new DynamicOrderParam();
string error = "";
switch (descriptor.ParamType.DescriptorCase) {
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Array:
if (custom[key] is not JArray arr_obj) {
return (false, null, key + " not correct");
}
rv.AsArray = new DynamicOrderArray();
foreach (var p in arr_obj.ToList()){
var child_key = key+"_child";
DynamicOrderParamDescriptor child_descriptor=new() {
ParamName = child_key,
ParamType = descriptor.ParamType.Array.ElementType
};
JObject child = new() {
[child_key] = p
};
(bool suc, DynamicOrderParam? param, error) = TryGetDynamicParam(child,child_key,child_descriptor,Dynamic_Descriptors);
if(!suc){
return (suc,null,error);
}
rv.AsArray.Element.Add(param);
}
return (true,rv,error);
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated:
return (false,null,key+"Enumerated DynamicOrderTypeDescriptors not enabled on api");
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive:
switch (descriptor.ParamType.Primitive) {
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveBool:
if (!WebsocketJsonParsers.TryParse<bool>(custom, key, out bool? b, out error) || b == null) {
return (false, null, error);
}
rv.AsBool = (bool)b;
return (true,rv,error);
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveDouble:
if (!WebsocketJsonParsers.TryParse<double>(custom, key, out double? d, out error) || d == null) {
return (false, null, error);
}
rv.AsDouble = (double)d;
return (true,rv,error);
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt:
if (!WebsocketJsonParsers.TryParse<int>(custom, key, out int? i, out error) || i == null) {
return (false, null, error);
}
rv.AsInt32 = (int)i;
return (true,rv,error);
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef:
if (custom[key] is not JObject child_custom) {
return (false, null, key + " not correct");
}
(bool suc,var child_custom_def,error) = TryGetCustomDef(child_custom,Dynamic_Descriptors);
if(!suc || child_custom_def==null){
return (false,null,error);
}
rv.AsOrderDef = child_custom_def;
return(true,rv,error);
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString:
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(custom,key,out WebsocketJsonParsers.StringWrapper? s,out error)|| s == null) {
return (false, null, error);
}
rv.AsString = s.Value.Val;
return (true,rv,error);
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveSecurityKey:
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint:
if (!WebsocketJsonParsers.TryParse<uint>(custom, key, out uint? u, out error) || u == null) {
return (false, null, error);
}
rv.AsUint32 = (uint)u;
return (true,rv,error);
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveQuantity:
if (WebsocketJsonParsers.TryParse<uint>(custom, key, out uint? q, out error) && q != null) {
rv.AsQuantity = new QuantityMeasure() {
Absolute = (uint)q
};
return (true, rv, error);
} else if (WebsocketJsonParsers.TryParse<double>(custom, key, out double? qd, out error) && qd != null){
rv.AsQuantity = new QuantityMeasure() {
Ratio = (double)qd
};
return (true, rv, error);
}
else{
return (false,null,key+" not correct");
}
}
return (false,null,key+" not correct");
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Structure:
if (custom[key] is not JObject obj) {
return (false, null, key + " not correct");
}
rv.AsStruct = new DynamicOrderStruct();
foreach (var prop in obj.Properties()) {
var child_key = prop.Name;
var check = descriptor.ParamType.Structure.Field.TryGetFirst(v => v?.ParamName == child_key,out DynamicOrderParamDescriptor? p);
if(!check || p == null){
return (false,null,key+" "+child_key+" not correct");
}
var child_descriptor = new DynamicOrderParamDescriptor {
ParamName = child_key,
ParamType = p.ParamType
};
(bool success, DynamicOrderParam? param, error) = TryGetDynamicParam(obj, child_key, child_descriptor, Dynamic_Descriptors);
if (!success) {
return (success, null, error);
}
rv.AsStruct.Field[child_key] = param;
}
return (true,rv,error);
}
return (false,null,key+" not correct");
}
readonly static Lazy<AdminService> _admin = new Lazy<AdminService>(() => Locator.locate<AdminService>(), true);
private static (bool, CustomOrderDefinition?, string) TryGetCustomDef(WebsocketRequest req) {
string status = "";
private static (bool, CustomOrderDefinition?, string) TryGetCustomDef(WebsocketRequest req, IReadOnlyDictionary<string, DynamicOrderDescriptor> Dynamic_Descriptors){
if (req.parameters["options"] is not JObject custom) {
return (false, null, "options not correct");
}
return TryGetCustomDef(custom,Dynamic_Descriptors);
}
private static (bool, CustomOrderDefinition?, string) TryGetCustomDef(JObject custom, IReadOnlyDictionary<string, DynamicOrderDescriptor> Dynamic_Descriptors) {
CustomOrderDefinition customdef;
JObject? custom = req.parameters["custom"] as JObject;
if (!WebsocketJsonParsers.TryParse<CustomOrderDefinition.CustomOneofCase>(
req.parameters,
"order_type",
out CustomOrderDefinition.CustomOneofCase? order_type,
out status)
|| order_type == null) {
if (!WebsocketJsonParsers.TryParse<CustomOrderDefinition.CustomOneofCase>(custom, "order_type", out CustomOrderDefinition.CustomOneofCase? order_type, out string status) || order_type == null) {
return (false, null, status);
}
switch (order_type) {
case CustomOrderDefinition.CustomOneofCase.Stop:{
customdef = new CustomOrderDefinition() {
Stop = new StopOrderDefinition()
};
break;
}
case CustomOrderDefinition.CustomOneofCase.StopLimit:{
if (!WebsocketJsonParsers.TryParse<double>(custom, "stop_price", out double? sp, out status) || sp == null) {
return (false, null, status);
}
customdef = new CustomOrderDefinition() {
StopLimit = new StopLimitOrderDefinition(){
StopPrice=(double)sp
}
};
break;
}
case CustomOrderDefinition.CustomOneofCase.Iceberg: {
if (!WebsocketJsonParsers.TryParse<double>(custom, "show_quantity", out double? sq, out status) || sq == null) {
return (false, null, status);
}
if (custom["child"] is not JObject child) {
return (false, null, "Iceberg child not correct");
}
(var check, var childdef, status) = TryGetCustomDef(child,Dynamic_Descriptors);
if(!check){
return (false, null, status);
}
// mbts.spark.utilities.OrderDataHelper
customdef = new CustomOrderDefinition() {
Iceberg = new IcebergOrderDefinition() {
ShowQuantity = (double)sq,
Child = childdef
}
};
break;
}
case CustomOrderDefinition.CustomOneofCase.TimeBox:{
return (false,null,"timebox not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Spreader: {
// if (custom == null) {
// message = "custom not present";
// return false;
// }
return (false,null,"spreaders not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Tick:{
return (false,null,"Ticks not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Lean:{
return (false,null,"Lean not enabled on api");
// if (!WebsocketJsonParsers.TryParse<double>(custom, "", out double? sq, out status) || sq == null) {
// return (false, null, status);
// }
// if (custom["child"] is not JObject child) {
// return (false, null, "Lean child not correct");
// }
// (var check, var childdef, status) = TryGetCustomDef(child,Dynamic_Descriptors);
// if(!check){
// return (false, null, status);
// }
// customdef = new CustomOrderDefinition() {
// Lean = new LeanOrderDefinition(){
// DetachOnToward = false,
// CrossThreshold = 0,
// MaxChaseTicks = 0,
// Child = childdef
// }
// };
}
case CustomOrderDefinition.CustomOneofCase.Join:{
return (false,null,"join not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Top:{
return (false,null,"top not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Trigger:{
return (false,null,"trigger not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Cover: {
if (
!WebsocketJsonParsers.TryParse<uint>( custom, "hedge_ticks", out uint? ht, out status) || ht == null ||
!WebsocketJsonParsers.TryParse<bool>(custom, "cancel_on_external_hedge_cancel", out bool? coehc, out status) || coehc == null ||
!WebsocketJsonParsers.TryParse<bool>(custom, "cancel_hedges_on_cancel", out bool? choc, out status) || choc == null ||
!WebsocketJsonParsers.TryParse<bool>(custom, "detach_hedges", out bool? dh, out status) || dh == null
) {
return (false, null, status);
}
if (custom["child"] is not JObject child) {
return (false, null, "Cover child not correct");
}
(var check, var childdef, status) = TryGetCustomDef(child,Dynamic_Descriptors);
if(!check){
return (false, null, status);
}
if (custom["hedge"] is not JObject hedge) {
return (false, null, "Cover hedge not correct");
}
(check, var hedgedef, status) = TryGetCustomDef(hedge,Dynamic_Descriptors);
if(!check){
return (false, null, status);
}
customdef = new CustomOrderDefinition() {
Cover = new CoverOrderDefinition() {
HedgeTicks = (uint)ht,
CancelOnExternalHedgeCancel = (bool)coehc,
CancelHedgesOnCancel = (bool)choc,
DetachHedges = (bool)dh,
Hedge = hedgedef,
Child = childdef
}
};
break;
}
case CustomOrderDefinition.CustomOneofCase.Offset:{
if (!WebsocketJsonParsers.TryParse<uint>(custom, "tick_offset", out uint? to, out status) || to == null) {
return (false, null, status);
}
if (custom["child"] is not JObject child) {
return (false, null, "offset child not correct");
}
(var check, var childdef, status) = TryGetCustomDef(child,Dynamic_Descriptors);
if(!check){
return (false, null, status);
}
// mbts.spark.utilities.OrderDataHelper
customdef = new CustomOrderDefinition() {
Offset = new OffsetOrderDefinition() {
TickOffsetOnDeploy = (uint)to,
Child = childdef
}
};
break;
}
case CustomOrderDefinition.CustomOneofCase.Oco:{
return (false,null,"Oco not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Pricer:{
return (false,null,"pricer not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Composite:{
return (false,null,"Composite not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.GlbxIceberg:{
if (!WebsocketJsonParsers.TryParse<uint>(custom, "max_show_quantity", out uint? msq, out status) || msq == null) {
return (false, null, status);
}
// mbts.spark.utilities.OrderDataHelper
customdef = new CustomOrderDefinition() {
GlbxIceberg = new GLBXIcebergOrderDefinition() {
MaxShowQuantity = (uint)msq,
}
};
break;
}
case CustomOrderDefinition.CustomOneofCase.Splitter:{
return (false,null,"splitter not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.QueueHolder:{
return (false,null,"QueueHolder not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Scratch:{
return (false,null,"Scratch not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Prorata:{
return (false,null,"Prorata not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.DeleteAndCover:{
return (false,null,"DeleteAndCover not enabled on api");
}
case CustomOrderDefinition.CustomOneofCase.Limit: {
customdef = new CustomOrderDefinition() {
Limit = new LimitOrderDefinition()
};
break;
}
}
case CustomOrderDefinition.CustomOneofCase.Dynamic: {
if (custom == null) {
return (false, null, "custom not present");
}
if(!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(custom, "dynamic_order_type", out WebsocketJsonParsers.StringWrapper? dynamic_order_type, out status) || dynamic_order_type == null ||
!Dynamic_Descriptors.TryGetValue(dynamic_order_type.Value.Val, out var desc)){
return (false, null, status);
}
var def = new DynamicOrderDefinition() {
DynamicOrderType = desc.DynamicOrderType
};
// case CustomOrderDefinition.CustomOneofCase.Spreader: {
// // if (custom == null) {
// // message = "custom not present";
// // return false;
// // }
// //ENTER SPREADER CODE HERE
// break;
// }
case CustomOrderDefinition.CustomOneofCase.Iceberg: {
if (custom == null) {
return (false, null, "custom not present");
}
double? sq;
if (!WebsocketJsonParsers.TryParse<double>(custom, "show_quantity", out sq, out status) || sq == null) {
return (false, null, status);
}
customdef = new CustomOrderDefinition() {
Iceberg = new IcebergOrderDefinition() {
ShowQuantity = (double)sq,
Child = new CustomOrderDefinition() {
Limit = new LimitOrderDefinition()
}
foreach (var p in desc.ParamDesc) {
(bool suc, var d, var error)=TryGetDynamicParam(custom,p.ParamName,p,Dynamic_Descriptors);
if(!suc){
return (suc,null,error);
}
};
break;
}
case CustomOrderDefinition.CustomOneofCase.Cover: {
if (custom == null) {
return (false, null, "custom not present");
}
if (!WebsocketJsonParsers.TryParse<uint>(
custom,
"hedge_ticks",
out uint? ht,
out status)
|| ht == null) {
return (false, null, status);
}
if (!WebsocketJsonParsers.TryParse<bool>(
custom,
"cancel_on_external_hedge_cancel",
out bool? coehc,
out status)
|| coehc == null) {
return (false, null, status);
}
if (!WebsocketJsonParsers.TryParse<bool>(
custom,
"cancel_hedges_on_cancel",
out bool? choc,
out status)
|| choc == null) {
return (false, null, status);
}
if (!WebsocketJsonParsers.TryParse<bool>(
custom,
"detach_hedges",
out bool? dh,
out status)
|| dh == null) {
return (false, null, status);
def.OrderParams[p.ParamName] = d;
}
customdef = new CustomOrderDefinition() {
Cover = new CoverOrderDefinition() {
Hedge = new CustomOrderDefinition() {
Limit = new LimitOrderDefinition()
},
HedgeTicks = (uint)ht,
CancelOnExternalHedgeCancel = (bool)coehc,
CancelHedgesOnCancel = (bool)choc,
DetachHedges = (bool)dh,
Child = new CustomOrderDefinition() {
Limit = new LimitOrderDefinition()
}
}
Dynamic = def
};
break;
}
@@ -124,26 +367,20 @@ namespace bts.websocket_core {
};
return (true, customdef, status);
}
private async static Task<(bool, OrderDefinition?, string)> TryGetOrderDefAsync(WebsocketRequest req, IReadOnlyDictionary<uint, SecurityMD> securities) {
private async static Task<(bool, OrderDefinition?, string)> TryGetOrderDefAsync(WebsocketRequest req, IReadOnlyDictionary<uint, SecurityMD> securities, IReadOnlyDictionary<string, DynamicOrderDescriptor> Dynamic_Descriptors) {
if (req.parameters == null) {
return (false, null, "parameters not present");
}
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(
req.parameters,
"user",
out WebsocketJsonParsers.StringWrapper? usr,
out string status)
|| usr == null) {
return (false, null, status);
}
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(
req.parameters,
"account",
out WebsocketJsonParsers.StringWrapper? act,
out status)
|| act == null) {
if (
!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(req.parameters,"user",out WebsocketJsonParsers.StringWrapper? usr,out string status) || usr == null ||
!WebsocketJsonParsers.TryParse<Venue>(req.parameters, "venue", out Venue? exchange, out status) || exchange == null ||
!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(req.parameters, "account", out WebsocketJsonParsers.StringWrapper? act, out status) || act == null ||
!WebsocketJsonParsers.TryParse<Side>(req.parameters, "side", out Side? side, out status) || side == null ||
!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(req.parameters, "exchange_security_name", out WebsocketJsonParsers.StringWrapper? exchange_securty_name, out status) || exchange_securty_name == null
) {
return (false, null, status);
}
Result<AdminData> adminResult = await _admin.Value.get_admin_data_async(new VoidMessage());
uint user_id;
uint account_id;
@@ -160,23 +397,11 @@ namespace bts.websocket_core {
user_id = (uint)user.Id;
BTSAccount? account = admin.Accounts.FirstOrDefault(x => x.Name.ToLowerInvariant() == accountname.ToLowerInvariant());
if (account == null) {
return (false, null, "account not corrent");
return (false, null, "account not correct");
}
account_id = (uint)account.Id;
if (!WebsocketJsonParsers.TryParse<Side>(
req.parameters,
"side",
out Side? side,
out status)
|| side == null) {
return (false, null, status);
}
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(req.parameters, "exchange_securty_name", out WebsocketJsonParsers.StringWrapper? exchange_securty_name, out status) || exchange_securty_name == null) {
return (false, null, status);
}
if (!WebsocketJsonParsers.TryParse<Venue>(req.parameters, "venue", out Venue? exchange, out status) || exchange == null) {
return (false, null, status);
}
var _securites = securities;
if (!_securites.Values.TryGetFirst(sc_md =>
(
@@ -187,73 +412,100 @@ namespace bts.websocket_core {
return (false, null, "security not found");
}
(var check, var customdef, status) = TryGetCustomDef(req);
(var check, var customdef, status) = TryGetCustomDef(req, Dynamic_Descriptors);
if (check == false) {
return (false, null, status);
}
return (true, new OrderDefinition {
TimeInForce = TimeInForce.Gtc,
TimeInForce = TimeInForce.Day,
SecurityKey = sc_md.id,
Side = (Side)side,
UserId = user_id,
AccountId = account_id,
CustomDef = customdef
CustomDef = customdef,
Account = account.Name,
User = user.Name
}, status);
}
public static async Task<(bool success, string status)> DeleteOrderAsync(WebsocketRequest req, OrdersClient oc) {
if (req.parameters == null) {
return (false, "parameters not present");
static bool TryGetPoints(WebsocketRequest req, [NotNullWhen(true)] out List<PrecomputedTheoPoint>? theos, out string message, IReadOnlyDictionary<uint, SecurityMD> securities) {
theos = null;
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.PrecomputedTheoPointWrapper>(req.parameters, "points", out WebsocketJsonParsers.PrecomputedTheoPointWrapper[]? wrapper_theos, out message) || wrapper_theos == null) {
return false;
}
if (!WebsocketJsonParsers.TryParse<uint>(req.parameters, "order_id", out uint? key, out string status) || key == null) {
return (false, status);
theos = wrapper_theos.Select(wrapper => {
if (WebsocketJsonParsers.PrecomputedTheoPointWrapper.GetTheoPoint(wrapper, out PrecomputedTheoPoint? point, securities)) {
return point;
}
return null;
})
.Where(p => p != null)
.Cast<PrecomputedTheoPoint>()
.ToList();
return true;
}
public static async Task<(bool success, uint? id, string status)> SetTheosAsync(WebsocketRequest req, OrdersClient oc, IReadOnlyDictionary<uint, SecurityMD> securities) {
if (req.parameters == null) {
return (false, null, "parameters not present");
}
var log_txt = $"SetTheosAsync req {req.ToString()}";
bts.Log.Gen.warn(log_txt);
var _securites = securities;
if (!TryGetPoints(req, out List<PrecomputedTheoPoint>? theos, out string message, _securites)) {
return (false, null, message);
}
Response res = await oc.TrySetPrecomputedTheosAsync(theos);
return (res.ErrorCode != OrderError.Ok, null, res.Msg);
}
public static async Task<(bool success, uint? id, string status)> DeleteOrderAsync(WebsocketRequest req, OrdersClient oc) {
if (req.parameters == null) {
return (false, null, "parameters not present");
}
if (!WebsocketJsonParsers.TryParse<uint>(req.parameters, "order_key", out uint? key, out string status) || key == null) {
return (false, null, status);
}
Response res = await oc.TryDeleteOrderAsync((uint)key);
status = res.ErrorCode.ToString();
return (res.ErrorCode != OrderError.Ok, status);
return (res.ErrorCode != OrderError.Ok, null, status);
}
public static async Task<(bool success, string status)> UpdateOrderAsync(WebsocketRequest request, OrdersClient oc) {
public static async Task<(bool success, uint? id, string status)> UpdateOrderAsync(WebsocketRequest request, OrdersClient oc, IReadOnlyDictionary<string, DynamicOrderDescriptor> Dynamic_Descriptors) {
if (request.parameters == null) {
return (false, "parameters not present");
return (false, null, "parameters not present");
}
if (!WebsocketJsonParsers.TryParse<uint>(request.parameters, "order_id", out uint? key, out string status) || key == null) {
return (false, status);
}
if (!WebsocketJsonParsers.TryParse(request.parameters, "quantity", out double? quantity, out status) || quantity == null) {
return (false, status);
if (
!WebsocketJsonParsers.TryParse<uint>(request.parameters, "order_key", out uint? key, out string status) || key == null ||
!WebsocketJsonParsers.TryParse(request.parameters, "quantity", out double? quantity, out status) || quantity == null ||
!WebsocketJsonParsers.TryParse(request.parameters, "price", out double? price, out status) || price == null
) {
return (false, null, status);
}
if (!WebsocketJsonParsers.TryParse(request.parameters, "price", out double? price, out status) || price == null) {
return (false, status);
}
(var check, var customdef, status) = TryGetCustomDef(request);
(var check, var customdef, status) = TryGetCustomDef(request, Dynamic_Descriptors);
if (check == false || customdef == null) {
return (false, status);
return (false, null, status);
}
Response res = await oc.TryUpdateOrderAsync(customdef, (double)price, (double)quantity, (uint)key);
status = res.ErrorCode.ToString();
return (res.ErrorCode != OrderError.Ok, status);
return (res.ErrorCode != OrderError.Ok, null, status);
}
public static async Task<(bool success, uint? orderId, string status)> EnterOrderAsync(
WebsocketRequest request, OrdersClient ordersClient, IReadOnlyDictionary<uint, SecurityMD> securities) {
double? quantity = null;
double? price = null;
WebsocketRequest request, OrdersClient ordersClient, IReadOnlyDictionary<uint, SecurityMD> securities, IReadOnlyDictionary<string, DynamicOrderDescriptor> Dynamic_Descriptors) {
OrderDefinition? orderDefinition = null;
uint? orderId = null;
string status = string.Empty;
if (!WebsocketJsonParsers.TryParse(request.parameters, "quantity", out quantity, out status) || quantity == null) {
(bool success, orderDefinition, status) = await TryGetOrderDefAsync(request, securities, Dynamic_Descriptors);
if (
!success || orderDefinition == null ||
!WebsocketJsonParsers.TryParse(request.parameters, "quantity", out double? quantity, out status) || quantity == null ||
!WebsocketJsonParsers.TryParse(request.parameters, "price", out double? price, out status) || price == null ) {
return (false, null, status);
}
if (!WebsocketJsonParsers.TryParse(request.parameters, "price", out price, out status) || price == null) {
return (false, null, status);
}
(bool success, orderDefinition, status) = await TryGetOrderDefAsync(request, securities);
if (!success || orderDefinition == null) {
return (success, null, status);
}
var result = await ordersClient.TryEnterOrderAsync(orderDefinition, (uint)price, (double)quantity);
if (result.ErrorCode != OrderError.Ok) {
@@ -100,36 +100,28 @@ namespace bts.websocket_core {
}
static bool TryGetTradesFilter(WebsocketRequest req, [NotNullWhen(true)] out TradesSubscriptionFilter? trades_filter, out string message) {
SecurityCriteria? cri;
trades_filter = null;
JObject? filter = req.parameters?["filter"] as JObject;
if (filter == null) {
message = "filter not present";
return false;
}
if (!TryGetFilter(req, out cri, out message)) {
if (!TryGetFilter(req, out SecurityCriteria? cri, out message)) {
return false;
}
trades_filter = new TradesSubscriptionFilter(cri);
return true;
}
static bool TryGetFillsFilter(WebsocketRequest req, [NotNullWhen(true)] out FillsSubscriptionFilter? order_filter, out string message) {
SecurityCriteria? cri;
order_filter = null;
JObject? filter = req.parameters?["filter"] as JObject;
if (filter == null) {
if (req.parameters?["filter"] is not JObject filter) {
message = "filter not present";
return false;
}
if (!TryGetFilter(req, out cri, out message)) {
return false;
}
uint[]? accounts;
if (!WebsocketJsonParsers.TryParse<uint>(filter, "accounts", out accounts, out message)) {
return false;
}
OrderStatus[]? statuses;
if (!WebsocketJsonParsers.TryParse<OrderStatus>(filter, "statuses", out statuses, out message)) {
if (!TryGetFilter(req, out SecurityCriteria? cri, out message) ||
!WebsocketJsonParsers.TryParse<uint>(filter, "accounts", out uint[]? accounts, out message) ||
!WebsocketJsonParsers.TryParse<OrderStatus>(filter, "statuses", out OrderStatus[]? statuses, out message)
) {
return false;
}
order_filter = new FillsSubscriptionFilter(
@@ -138,22 +130,16 @@ namespace bts.websocket_core {
return true;
}
static bool TryGetOrdersFilter(WebsocketRequest req, [NotNullWhen(true)] out OrdersSubscriptionFilter? order_filter, out string message) {
SecurityCriteria? cri;
order_filter = null;
JObject? filter = req.parameters?["filter"] as JObject;
if (filter == null) {
if (req.parameters?["filter"] is not JObject filter) {
message = "filter not present";
return false;
}
if (!TryGetFilter(req, out cri, out message)) {
return false;
}
uint[]? accounts;
if (!WebsocketJsonParsers.TryParse<uint>(filter, "accounts", out accounts, out message)) {
return false;
}
OrderStatus[]? statuses;
if (!WebsocketJsonParsers.TryParse<OrderStatus>(filter, "statuses", out statuses, out message)) {
if (
!TryGetFilter(req, out SecurityCriteria? cri, out message) ||
!WebsocketJsonParsers.TryParse<uint>(filter, "accounts", out uint[]? accounts, out message) ||
!WebsocketJsonParsers.TryParse<OrderStatus>(filter, "statuses", out OrderStatus[]? statuses, out message)
) {
return false;
}
order_filter = new OrdersSubscriptionFilter(
@@ -165,39 +151,19 @@ namespace bts.websocket_core {
static bool TryGetFilter(WebsocketRequest req, [NotNullWhen(true)] out SecurityCriteria? cri, out string message) {
cri = null;
JObject? filter = req.parameters?["filter"] as JObject;
if (filter == null) {
if (req.parameters?["filter"] is not JObject filter) {
message = "filter not present";
return false;
}
Venue[]? exchanges;
if (!WebsocketJsonParsers.TryParse<Venue>(filter, "venue", out exchanges, out message)) {
return false;
}
SecurityType[]? types;
if (!WebsocketJsonParsers.TryParse<SecurityType>(filter, "kind", out types, out message)) {
return false;
}
PutOrCall[]? callputs;
if (!WebsocketJsonParsers.TryParse<PutOrCall>(filter, "call_or_put", out callputs, out message)) {
return false;
}
DateTime[]? expiration;
if (!WebsocketJsonParsers.TryParse<DateTime>(filter, "expiration", out expiration, out message)) {
return false;
}
// string[]? symbols = (string[]?)(filter.GetValueOrDefault("symbol")?.ToString()?.Split(",").Select(s => s.ToUpper()));
WebsocketJsonParsers.ListWrapper? symbols;
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.ListWrapper>(filter, "symbol", out symbols, out message)) {
return false;
}
WebsocketJsonParsers.ListWrapper? names;
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.ListWrapper>(filter, "name", out names, out message)) {
return false;
}
// filter.GetValueOrDefault("symbol")?.ToString()?.Split(",");
double[]? strikes;
if (!WebsocketJsonParsers.TryParse<double>(filter, "strikes", out strikes, out message)) {
if (
!WebsocketJsonParsers.TryParse<Venue>(filter, "venue", out Venue[]? exchanges, out message) ||
!WebsocketJsonParsers.TryParse<SecurityType>(filter, "kind", out SecurityType[]? types, out message) ||
!WebsocketJsonParsers.TryParse<PutOrCall>(filter, "call_or_put", out PutOrCall[]? callputs, out message) ||
!WebsocketJsonParsers.TryParse<DateTime>(filter, "expiration", out DateTime[]? expiration, out message) ||
!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringListWrapper>(filter, "symbol", out WebsocketJsonParsers.StringListWrapper? symbols, out message) ||
!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringListWrapper>(filter, "name", out WebsocketJsonParsers.StringListWrapper? names, out message) ||
!WebsocketJsonParsers.TryParse<double>(filter, "strikes", out double[]? strikes, out message)
) {
return false;
}
var clause = new SecurityCriteriaClause(
@@ -223,13 +189,12 @@ namespace bts.websocket_core {
out string status) {
hdlr = null;
SUBSCRIPTION_TYPE? type;
if (!WebsocketJsonParsers.TryParse<SUBSCRIPTION_TYPE>(req.parameters, "type", out type, out status) || status != "") {
return false;
}
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(req.parameters, "subscription_id", out WebsocketJsonParsers.StringWrapper? id, out status) || id == null) {
if (
!WebsocketJsonParsers.TryParse<SUBSCRIPTION_TYPE>(req.parameters, "type", out SUBSCRIPTION_TYPE? type, out status) || status != "" ||
!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(req.parameters, "subscription_id", out WebsocketJsonParsers.StringWrapper? id, out status) || id == null) {
return false;
}
Action<WebsocketSubscriptionHandler> sub;
switch (type) {
case SUBSCRIPTION_TYPE.BOOKS: {
+13 -11
View File
@@ -6,17 +6,19 @@ package bts.api.md;
option csharp_namespace = "bts.api.md";
message GlbxActivePlatform {
string host = 1;
string user = 2;
string password = 3;
}
message GlbxOnixPlatform {
string temp =1; // will add everything related to onix here.
}
message GlbxMarketDataConfig {
uint32 unique_check_value = 1;
//uint32 num_depth_levels = 1;
//uint32 depth_throttle_ms = 2;
bts.api.domain.SecurityCriteria security_criteria = 3;
// Market data isn't subscribed for securities listed here (even if they are
// included by 'security_criteria'). There are some securities that don't
// fit into our system just yet, such as 'BTCBIDR', which trades in prices
// larger than our cpp 'domain::Price' class supports.
//repeated string excluded_securities = 4;
//string url_rest_base = 5;
//string url_ws_base = 6;
bts.api.domain.SecurityCriteria security_criteria = 2;
oneof Platform {
GlbxActivePlatform glbx_active_platform = 10;
GlbxOnixPlatform glbx_onix_platform = 11;
}
}
@@ -93,10 +93,6 @@ function(get_cpp_sources dest)
md/MDConsumerInterop.cpp
md/MDProducer.cpp
md/MDProducerHandle.cpp
md/MarketDataError.cpp
md/MarketDataGateway.cpp
md/MarketDataRegistrationFactory.cpp
md/MarketDataSubscription.cpp
md/MemorySegmentUtils.cpp
md/PriceSize.cpp
md/Product.cpp
@@ -113,8 +109,6 @@ function(get_cpp_sources dest)
md/cme/MDP3Utils.cpp
md/common.cpp
md/ice/ICEUtils.cpp
md/recording/MDRecorder.cpp
md/recording/MDReplayer.cpp
md/relay/QueryServer.cpp
md/relay/RelayClient.cpp
md/relay/RelayRecorder.cpp
@@ -25,8 +25,23 @@ function(get_link_libraries dest)
Packet++
Common++
ssh2
ACE-5.7.5
ActivBase_x86-64_ubuntu22-04_gcc123_mdid
ActivOneApi_x86-64_ubuntu22-04_gcc123_mdid
ActivAgentApplication_x86-64_ubuntu22-04_gcc123_mdid
ActivDatabase_x86-64_ubuntu22-04_gcc123_mdid
ActivFramework_x86-64_ubuntu22-04_gcc123_mdid
ActivServices_x86-64_ubuntu22-04_gcc123_mdid
BoostAtomic_x86-64_ubuntu22-04_gcc123_mdid
BoostChrono_x86-64_ubuntu22-04_gcc123_mdid
BoostProgramOptions_x86-64_ubuntu22-04_gcc123_mdid
BoostFilesystem_x86-64_ubuntu22-04_gcc123_mdid
-lm
-lgcc_s
-lc
-lrt
-ldl
-lstdc++
-lpcap
-static-libgcc
-static-libstdc++
@@ -87,10 +87,6 @@ function(get_cpp_sources dest)
md/MDConsumerInterop.cpp
md/MDProducer.cpp
md/MDProducerHandle.cpp
md/MarketDataError.cpp
md/MarketDataGateway.cpp
md/MarketDataRegistrationFactory.cpp
md/MarketDataSubscription.cpp
md/MemorySegmentUtils.cpp
md/PriceSize.cpp
md/Product.cpp
+3 -1
View File
@@ -17,7 +17,9 @@ JetFIXSimConnection {
raw_data: "1JE9X"
use_cme_secure_logon: false
seconds_to_pause : 1
max_connection_attempts: 9999999
max_order_msgs_per_window: 300
window_duration_msecs: 1000
max_connection_attempts: 9999999
}
sim_ice {
name: "sim_ice"
+2
View File
@@ -1,5 +1,7 @@
#pragma once
#include <ostream>
#define ENUM_BASIC_MEMBERS(type) \
Value value; \
\
+11
View File
@@ -23,7 +23,18 @@ TimeStamp::TimeStamp(uint32_t year, uint32_t month, uint32_t day) {
value_ =
std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
}
bool TimeStamp::from_ymd(
std::string yearMonthDay, TimeStamp *result) {
uint32_t year, month, day;
std::istringstream ss(yearMonthDay);
char dash; // to consume the dash character in the input
// Use stringstream to extract year, month, and day from the input string
ss >> year >> dash >> month >> dash >> day;
if(ss.fail())
return false;
return TimeStamp::from_ymd(year,month,day,result);
}
bool TimeStamp::from_ymd(
std::uint32_t year, std::uint32_t month, std::uint32_t day, TimeStamp *result) {
if (!is_valid_ymd(year, month, day)) {
+3
View File
@@ -41,6 +41,9 @@ struct TimeStamp {
std::uint32_t month,
std::uint32_t day,
TimeStamp *result);
static bool from_ymd(
std::string yearMonthDay,
TimeStamp *result);
constexpr std::uint64_t nanos() const;
constexpr std::uint64_t micros() const;
+11 -2
View File
@@ -1,5 +1,14 @@
#pragma once
#include "lib/core/FMT_IMPL.h"
#include "lib/utils/ValueWrapper.h"
#include <cstdint>
namespace bts {
using SecurityKey = utils::ValueWrapper<struct SecurityKeyTag, uint32_t>;
}
using SecurityKey = utils::ValueWrapper<struct SecurityKeyTag, std::uint32_t>;
} // namespace bts
FMT_IMPL(bts::SecurityKey, k, "{}", k.value());
-2
View File
@@ -69,8 +69,6 @@ inline void from_json(const nlohmann::json &j, JsonRpcRequestT<ParamsT> &req) {
j.at("method").get_to(req.method);
j.at("params").get_to(req.params);
std::vector<std::string> vec;
*vec.begin();
vec.begin()->size();
auto id_j = j.find("id");
if (id_j == j.end()) {
req.id = std::nullopt;
+2 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <ostream>
#include <string>
#include <vector>
namespace bts::io::websocket {
+87
View File
@@ -0,0 +1,87 @@
#include "logging.h"
#include "LoggerHub.h"
#include "lib/core/LockGuard.h"
#include "lib/utils/bts_assert.h"
#include <boost/circular_buffer.hpp>
#include <boost/container/flat_set.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <ccapi_cpp/ccapi_logger.h>
#include <ccapi_cpp/service/ccapi_market_data_service_deribit.h>
#include <fmt/format.h>
#include <google/protobuf/io/gzip_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <mutex>
#include <string>
namespace ccapi {
class CcapiLogger final : public Logger {
public:
void logMessage(
const std::string &severity,
const std::string & /*thread_id*/,
const std::string & /*time_iso*/,
const std::string &file_name,
const std::string &line_num,
const std::string &msg) override {
auto level = to_bts_level_(severity);
if (!bts::log::_thread_logger.logger->is_loggable(bts::log::md, level)) {
return;
}
// Add file name to cache to preserve it in memory until the log is
// [asynchronously] committed
const auto &cached_file_name = store_file_name_(file_name);
bts::log::_thread_logger.logger->enqueue(
bts::log::log_ts(), bts::log::md, level, cached_file_name.c_str(),
stoi(line_num), "{}", msg);
}
static bts::log::Level to_bts_level_(const std::string &ccapi_severity) {
bts_assert(!ccapi_severity.empty());
// TRACE and DEBUG translate to Diag; all other levels have identical
// names in ccapi and spark
if (ccapi_severity[0] == 'T' || ccapi_severity[0] == 'D') {
return bts::Diag;
}
if (ccapi_severity[0] == 'I') {
return bts::Info;
}
if (ccapi_severity[0] == 'W') {
return bts::Warn;
}
if (ccapi_severity[0] == 'E') {
return bts::Error;
}
if (ccapi_severity[0] == 'F') {
return bts::Fatal;
}
bts_assert(false);
return bts::Fatal;
}
private:
std::mutex instance_mx_;
std::unordered_set<std::string> file_name_store_;
// This buffer is intended to keep file name strings in memory until the log
// is [asynchronously] committed. There is no mechanism for determining when
// the log is actually committed; we just hope that the log system won't be
// flooded to the point that file names are pushed out of the buffer before
// their corresponding logs are committed.
//
// Note: Another implementation, which would guarentee file names are
// preserved, is to maintain a set of unique file names. Naturally, it's
// size would be limited, but not without cost in additional overhead for
// lookups.
const std::string &store_file_name_(const std::string &file_name) {
LOCK_GUARD(instance_mx_);
return *file_name_store_.insert(file_name).first;
}
};
} // namespace ccapi
+6 -74
View File
@@ -1,17 +1,16 @@
#include "logging.h"
#include "LoggerHub.h"
#include "lib/config/config.h"
#include "lib/config/protos/deployment_type.pb.h"
#include "lib/config/protos/logging.pb.h"
#include "lib/config/protos/system_config.pb.h"
#include "lib/core/LockGuard.h"
#include "lib/os/os.h"
#include "lib/utils/Dispatcher.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/bts_chrono.h"
#include "lib/utils/file_utils.h"
#include "lib/utils/thread_manager.h"
#ifdef SPARK_ENABLE_CCAPI
#include "lib/logging/ccapi_logger.h"
#endif
#include <boost/circular_buffer.hpp>
#include <boost/container/flat_set.hpp>
@@ -19,16 +18,12 @@
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <fmt/format.h>
#include <string>
#ifdef SPARK_ENABLE_CCAPI
#include <ccapi_cpp/ccapi_logger.h>
#include <ccapi_cpp/service/ccapi_market_data_service_deribit.h>
#endif
#include <fstream>
#include <google/protobuf/io/gzip_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <mutex>
#include <string>
namespace bts {
namespace log {
@@ -223,72 +218,9 @@ extern "C" BTS_EXPORT void Logger_log(
#ifdef SPARK_ENABLE_CCAPI
namespace ccapi {
class SparkLoggerAdaptor final : public Logger {
public:
void logMessage(
const std::string &severity,
const std::string & /*thread_id*/,
const std::string & /*time_iso*/,
const std::string &file_name,
const std::string &line_num,
const std::string &msg) override {
auto level = to_bts_level_(severity);
if (!bts::log::_thread_logger.logger->is_loggable(bts::log::md, level)) {
return;
}
// Add file name to cache to preserve it in memory until the log is
// [asynchronously] committed
const auto &cached_file_name = store_file_name_(file_name);
bts::log::_thread_logger.logger->enqueue(
bts::log::log_ts(), bts::log::md, level, cached_file_name.c_str(),
stoi(line_num), "{}", msg);
}
static bts::log::Level to_bts_level_(const std::string &ccapi_severity) {
bts_assert(!ccapi_severity.empty());
// TRACE and DEBUG translate to Diag; all other levels have identical
// names in ccapi and spark
if (ccapi_severity[0] == 'T' || ccapi_severity[0] == 'D') {
return bts::Diag;
}
if (ccapi_severity[0] == 'I') {
return bts::Info;
}
if (ccapi_severity[0] == 'W') {
return bts::Warn;
}
if (ccapi_severity[0] == 'E') {
return bts::Error;
}
if (ccapi_severity[0] == 'F') {
return bts::Fatal;
}
bts_assert(false);
return bts::Fatal;
}
private:
std::mutex instance_mx_;
std::unordered_set<std::string> file_name_store_;
// This buffer is intended to keep file name strings in memory until the log
// is [asynchronously] committed. There is no mechanism for determining when
// the log is actually committed; we just hope that the log system won't be
// flooded to the point that file names are pushed out of the buffer before
// their corresponding logs are committed.
//
// Note: Another implementation, which would guarentee file names are
// preserved, is to maintain a set of unique file names. Naturally, it's
// size would be limited, but not without cost in additional overhead for
// lookups.
const std::string &store_file_name_(const std::string &file_name) {
LOCK_GUARD(instance_mx_);
return *file_name_store_.insert(file_name).first;
}
};
SparkLoggerAdaptor spark_logger_adaptor;
Logger *Logger::logger = &spark_logger_adaptor;
CcapiLogger ccapi_logger;
Logger *Logger::logger = &ccapi_logger;
} // namespace ccapi
#endif
+12 -6
View File
@@ -1,9 +1,15 @@
#pragma once
#include "lib/utils/ValueWrapper.h"
namespace bts {
namespace md {
#include <cstdint>
namespace bts::md {
using ExchangeOrderKey =
utils::ValueWrapper<struct ExchangeOrderKeyTag, uint64_t>;
using SparkOrderKey = utils::ValueWrapper<struct SparkOrderKeyTag, uint64_t>;
} // namespace md
} // namespace bts
utils::ValueWrapper<struct ExchangeOrderKeyTag, std::uint64_t>;
using SparkOrderKey =
utils::ValueWrapper<struct SparkOrderKeyTag, std::uint64_t>;
} // namespace bts::md
+8 -2
View File
@@ -1,6 +1,12 @@
#pragma once
#include "lib/utils/ValueWrapper.h"
#include <cstdint>
namespace bts {
using ExchangeSecurityKey =
utils::ValueWrapper<struct ExchangeSecurityKeyTag, uint64_t>;
}
utils::ValueWrapper<struct ExchangeSecurityKeyTag, std::uint64_t>;
} // namespace bts
+2 -2
View File
@@ -1,4 +1,4 @@
#ifdef SPARK_ENABLE_CCAPI
//#ifdef SPARK_ENABLE_CCAPI
#include "GeneralMarketDataListener.h"
#include "lib/api/domain/Currency.pb.h"
#include "lib/api/domain/SecurityType.pb.h"
@@ -464,4 +464,4 @@ void GeneralMarketDataListener::_possibly_match_with_sim_orders(
}
} // namespace bts::md
#endif
//#endif
+1 -1
View File
@@ -537,7 +537,7 @@ class SyntheticConsumerInst {
IncompleteStrategy to_strategy(const shared_ptr<api::Security> &security) {
IncompleteStrategy res;
res.security = security;
auto meta = _ss->get_metadata(SecurityKey(security->id()));
auto meta = _ss->get_metadata(bts::SecurityKey(security->id()));
// If metadata is set copy it otherwise we use the default proto
if (meta) {
res.strategy.CopyFrom(meta->strategy());
+26
View File
@@ -128,6 +128,32 @@ CAPI bool md_consumer_is_heart_beating(const ColdHeader *header) {
return is_heart_beat_ok(*header);
}
typedef void (*raw_receive_pb)(const char *, size_t);
CAPI int md_consumer_read_trades_linux(
MDConsumer *consumer,
const char* symbol,
int venue_i,
int type_i,
int64_t &cursor,
raw_receive_pb cb) {
auto type = static_cast<SecurityType>(type_i);
auto venue = static_cast<Venue>(venue_i);
bts_assert(consumer);
google::protobuf::Arena arena;
co2::TradeList *ptrades =
google::protobuf::Arena::CreateMessage<co2::TradeList>(&arena);
auto channel =
consumer->get_channel(symbol, type, venue);
if (channel.is_initialized()) {
cursor = channel.read_trades(*ptrades->mutable_items(), cursor);
std::string buffer;
ptrades->SerializeToString(&buffer);
cb(buffer.c_str(), buffer.size());
return ptrades->items_size();
}
return 0;
}
typedef void (*raw_receive_pb)(const char *, size_t);
CAPI int md_consumer_read_trades(
MDConsumer *consumer,
+7 -6
View File
@@ -2,6 +2,7 @@
#include "../common.h"
#include "lib/core/FMT_IMPL.h"
#include "lib/core/TimeStamp.h"
#include "lib/io/JsonDumper.h"
#include "lib/io/JsonReader.h"
#include "lib/utils/ValueWrapper.h"
@@ -9,8 +10,8 @@
#include "lib/utils/bts_assert.h"
#include "lib/utils/bts_export.h"
#include "lib/utils/bts_no_copy.h"
#include "lib/core/TimeStamp.h"
#include "lib/utils/warn_ctrl.h"
#include "lib/domain_model/SecurityKey.h"
#include <nlohmann/json.hpp>
#include <sstream>
@@ -19,10 +20,13 @@
namespace bts {
namespace md {
using SecurityKey = utils::ValueWrapper<struct SecurityKeyTag, uint32_t>;
using SecurityKey = bts::SecurityKey;
using ExchangeKey = utils::ValueWrapper<struct ExchangeKeyTag, uint64_t>;
GCC_DIAG_OFF("-Weffc++") // No Virtual Destructors by design
#ifndef __clang__
GCC_DIAG_OFF("-Wstringop-truncation")
#endif
#define STRING_PROPERTY(NAME) \
const char *NAME() const { return _##NAME; } \
@@ -129,7 +133,7 @@ struct BTS_EXPORT FixedSpreadLeg {
double leg_delta;
};
template <int LEGCOUNT = 16>
template <int LEGCOUNT = 64>
class FixedSpreadSecurity : public FixedSecurity {
char _summary[128];
@@ -216,7 +220,6 @@ SecurityType securitytype_of(const Security &);
size_t num_legs(const Security &);
uint64_t exchange_id(const Security &security);
void to_json(nlohmann::json &j, const FixedSpreadLeg &f);
void to_json(nlohmann::json &j, const FixedSecurity &f);
@@ -386,5 +389,3 @@ void with_security_like(const api::Security &security, C cb) {
} // namespace md
} // namespace bts
FMT_IMPL(bts::md::SecurityKey, k, "{}", *k);
-16
View File
@@ -1,16 +0,0 @@
#pragma once
#include "lib/api/domain/Venue.pb.h"
#include "lib/md/common.h"
#include <bits/stdint-uintn.h>
namespace bts::md {
// Conveniant class
class CCSecurityIDFactory {
public:
std::optional<uint64_t> get_id(
bts::md::Venue venue, const std::string &exchange_id);
std::optional<uint64_t> get_id(bts::md::Venue venue, uint64_t &exchange_id);
};
} // namespace bts::md
@@ -1,82 +0,0 @@
#pragma once
#include <ccapi_cpp/ccapi_subscription.h>
#include <string>
#include <vector>
namespace bts::md::deribit {
class DeribitSubscriptionFactory {
public:
/**
* @brief Creates a Subscription for the specified instrument \p name
*
* @param name The name of the instrument for which to subscribe
*/
static ccapi::Subscription instrument_sub(
const std::string &name, JsonRpcId id) {
const auto corr_id = CCApiCorrelationID::from_parts(
VENUE_NAME, DERIBIT_MSG_TYPE_NAME(DeribitMessageType::INSTRUMENT),
name);
JsonRpcRequest rpc_req;
rpc_req.method = JSON_RPC_GET_INSTRUMENT_METHOD;
rpc_req.params["instrument_name"] = name;
rpc_req.id = id;
return {
CCAPI_VENUE_NAME, "", CCAPI_GENERIC_PUBLIC_SUBSCRIPTION,
json{rpc_req}.dump(2), corr_id.to_string()};
}
/**
* @brief Creates a Subscription for instruments of the specified \p currency
*
* @param currency The currency for which to get instruments
*/
static ccapi::Subscription instruments_sub(
const string &currency, DeribitInstrumentKind kind, JsonRpcId id) {
const auto corr_id = CCApiCorrelationID::from_parts(
VENUE_NAME, DeribitMessageType::INSTRUMENTS, currency, kind);
JsonRpcRequest rpc_req;
rpc_req.method = JSON_RPC_GET_INSTRUMENTS_METHOD;
rpc_req.params["currency"] = currency;
if (kind != DeribitInstrumentKind::NONE) {
rpc_req.params["kind"] = kind;
}
rpc_req.id = id;
return {
CCAPI_VENUE_NAME, "", CCAPI_GENERIC_PUBLIC_SUBSCRIPTION,
json{rpc_req}.dump(2), corr_id.to_string()};
}
/**
* @brief Creates a Subscription for combos of the specified \p currency
*
* @param currency The currency for which to get instruments
*/
static ccapi::Subscription combos_sub(const string &currency, JsonRpcId id) {
const auto corr_id = CCApiCorrelationID::from_parts(
VENUE_NAME, DeribitMessageType::COMBOS, currency);
JsonRpcRequest rpc_req;
rpc_req.method = JSON_RPC_GET_COMBOS_METHOD;
rpc_req.params["currency"] = currency;
rpc_req.id = id;
return {
CCAPI_VENUE_NAME, "", CCAPI_GENERIC_PUBLIC_SUBSCRIPTION,
json{rpc_req}.dump(2), corr_id.to_string()};
}
static ccapi::Subscription combo_details_sub(
const string &combo_name, JsonRpcId id) {
auto corr_id = CCApiCorrelationID::from_parts(
VENUE_NAME, DERIBIT_MSG_TYPE_NAME(DeribitMessageType::COMBO_DETAILS),
combo_name);
JsonRpcRequest rpc_req;
rpc_req.method = JSON_RPC_GET_COMBO_DETAILS_METHOD;
rpc_req.params["combo_id"] = combo_name;
rpc_req.id = id;
return {
CCAPI_VENUE_NAME, "", CCAPI_GENERIC_PUBLIC_SUBSCRIPTION,
json{rpc_req}.dump(2), corr_id.to_string()};
}
};
} // namespace bts::md::deribit
+102
View File
@@ -0,0 +1,102 @@
#include "ChildOrder.h"
namespace bts::orderman {
ChildOrder::ChildOrder()
: _child_order(nullptr), _parent_order_services(nullptr),
_child_order_services(nullptr), _active(false) {}
ChildOrder::ChildOrder(Order &child, OrderServices &parent_services)
: _child_order(&child), _parent_order_services(&parent_services),
_child_order_services(), _active(false) {}
// This is used in attach, where the child order is already activated
// and ready to go.
ChildOrder::ChildOrder(
OrderServices &child_services, OrderServices &parent_services)
: _child_order(child_services._owner),
_parent_order_services(&parent_services),
_child_order_services(&child_services), _active(true) {}
ChildOrder::ChildOrder(ChildOrder &&src) { operator=(std::move(src)); }
ChildOrder::ChildOrder(ChildOrder const &src) { operator=(src); }
ChildOrder::ChildOrder(
api::orderman::OrderDefinition const &def, OrderServices &parent_services)
: _child_order(&parent_services.make_child(def)),
_parent_order_services(&parent_services), _child_order_services(),
_active(false) {}
ChildOrder &ChildOrder::operator=(ChildOrder &&src) {
_child_order = src._child_order;
_parent_order_services = src._parent_order_services;
_child_order_services = src._child_order_services;
_active = src._active;
src._child_order = nullptr;
src._parent_order_services = nullptr;
src._child_order_services = nullptr;
src._active = false;
return *this;
}
ChildOrder &ChildOrder::operator=(ChildOrder const &src) {
_child_order = src._child_order;
_parent_order_services = src._parent_order_services;
_child_order_services = src._child_order_services;
_active = src._active;
return *this;
}
bool ChildOrder::empty() const { return _child_order == nullptr; }
OrderKey ChildOrder::key() const {
bts_assert_d(_child_order);
return _child_order->key();
}
bool ChildOrder::is_active() const { return _active; }
void ChildOrder::warm_up_activate() {
bts_assert(!_active);
_parent_order_services->warm_up_activate_child(*_child_order);
}
void ChildOrder::activate(Price p, Quantity q) {
bts_assert(_child_order);
bts_assert(!_active);
_parent_order_services->activate_child(*_child_order, p, q);
_child_order_services = _child_order->_services.get();
_active = true;
}
void ChildOrder::mark_detached_from_parent() {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->mark_detached_from_parent();
}
void ChildOrder::update(Price p, Quantity q) {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->update(p, q);
}
void ChildOrder::update(api::orderman::Update const &update) {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->update(update);
}
void ChildOrder::detach() {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->detach();
}
void ChildOrder::cancel() {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->cancel();
}
} // namespace bts::orderman
+98 -146
View File
@@ -3,173 +3,57 @@
#include "lib/orderman/OrderServices.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/utils/bts_assert.h"
#include <memory>
namespace bts {
namespace orderman {
namespace bts::orderman {
class ChildOrder {
public:
ChildOrder()
: _child_order(nullptr), _parent_order_services(nullptr),
_child_order_services(nullptr), _active(false) {}
ChildOrder();
ChildOrder(Order &child, OrderServices &parent_services)
: _child_order(&child), _parent_order_services(&parent_services),
_child_order_services(), _active(false) {}
ChildOrder(Order &child, OrderServices &parent_services);
// This is used in attach, where the child order is already activated
// and ready to go.
ChildOrder(OrderServices &child_services, OrderServices &parent_services)
: _child_order(child_services._owner),
_parent_order_services(&parent_services),
_child_order_services(&child_services), _active(true) {}
ChildOrder(OrderServices &child_services, OrderServices &parent_services);
ChildOrder(ChildOrder &&src) { operator=(std::move(src)); }
ChildOrder(ChildOrder const &src) { operator=(src); }
ChildOrder(ChildOrder &&src);
ChildOrder(ChildOrder const &src);
ChildOrder(
api::orderman::OrderDefinition const &def,
OrderServices &parent_services)
: _child_order(&parent_services.make_child(def)),
_parent_order_services(&parent_services), _child_order_services(),
_active(false) {}
OrderServices &parent_services);
ChildOrder &operator=(ChildOrder &&src) {
_child_order = src._child_order;
_parent_order_services = src._parent_order_services;
_child_order_services = src._child_order_services;
_active = src._active;
ChildOrder &operator=(ChildOrder &&src);
src._child_order = nullptr;
src._parent_order_services = nullptr;
src._child_order_services = nullptr;
src._active = false;
return *this;
}
ChildOrder &operator=(ChildOrder const &src);
ChildOrder &operator=(ChildOrder const &src) {
_child_order = src._child_order;
_parent_order_services = src._parent_order_services;
_child_order_services = src._child_order_services;
_active = src._active;
return *this;
}
bool empty() const { return _child_order == nullptr; }
OrderKey key() const {
bts_assert_d(_child_order);
return _child_order->key();
}
bool is_active() const { return _active; }
void warm_up_activate() {
bts_assert(!_active);
_parent_order_services->warm_up_activate_child(*_child_order);
}
// Activate
void activate(Price p, Quantity q) {
bts_assert(_child_order);
bts_assert(!_active);
_parent_order_services->activate_child(*_child_order, p, q);
_child_order_services = _child_order->_services.get();
_active = true;
}
bool empty() const;
OrderKey key() const;
bool is_active() const;
void warm_up_activate();
void activate(Price p, Quantity q);
template <class CB>
void activate(Price p, Quantity q, CB const &on_activate_result) {
bts_assert(_child_order);
bts_assert(!_active);
void activate(Price p, Quantity q, CB const &on_activate_result);
auto on_activate_result_wrapper = [this, on_activate_result](
OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_activate_result](auto const &) { on_activate_result(r); });
};
void mark_detached_from_parent();
_parent_order_services->activate_child(
*_child_order, p, q, on_activate_result_wrapper);
_child_order_services = _child_order->_services.get();
_active = true;
}
// End Activate
// Mark detached
void mark_detached_from_parent() {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->mark_detached_from_parent();
}
// Update
void update(Price p, Quantity q) {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->update(p, q);
}
template <class CB> void update(Price p, Quantity q, CB const &on_result) {
bts_assert(_child_order);
bts_assert(_active);
auto on_result_w = [this, on_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_result](auto const &) { on_result(r); });
};
_child_order_services->update(p, q, on_result_w);
}
void update(api::orderman::Update const &update) {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->update(update);
}
void update(Price p, Quantity q);
void update(api::orderman::Update const &update);
template <class CB>
void update(api::orderman::Update const &update, CB const &on_result) {
bts_assert(_child_order);
bts_assert(_active);
auto on_result_w = [this, on_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_result](auto const &) { on_result(r); });
};
_child_order_services->update(update, on_result_w);
}
// End Update
void update(Price p, Quantity q, CB const &on_result);
template <class CB>
void update(api::orderman::Update const &update, CB const &on_result);
// Detach
void detach() {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->detach();
}
template <class CB> void detach(CB const &on_result) {
bts_assert(_child_order);
bts_assert(_active);
auto on_result_w = [this, on_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_result](auto const &) { on_result(r); });
};
_child_order_services->detach(on_result);
}
// End Detach
void detach();
template <class CB>
void detach(CB const &on_result);
// Cancel
void cancel() {
bts_assert(_child_order);
bts_assert(_active);
_child_order_services->cancel();
}
template <class CB> void cancel(CB const &on_result) {
bts_assert(_child_order);
bts_assert(_active);
auto on_result_w = [this, on_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_result](auto const &) { on_result(r); });
};
_child_order_services->cancel(on_result_w);
}
// End Cancel
void cancel();
template <class CB>
void cancel(CB const &on_result);
private:
friend class OrderBuilder;
@@ -179,5 +63,73 @@ class ChildOrder {
bool _active;
};
} // namespace orderman
} // namespace bts
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
template <class CB>
void ChildOrder::activate(Price p, Quantity q, CB const &on_activate_result) {
bts_assert(_child_order);
bts_assert(!_active);
auto on_activate_result_wrapper =
[this, on_activate_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_activate_result](auto const &) { on_activate_result(r); });
};
_parent_order_services->activate_child(
*_child_order, p, q, on_activate_result_wrapper);
_child_order_services = _child_order->_services.get();
_active = true;
}
template <class CB>
void ChildOrder::update(Price p, Quantity q, CB const &on_result) {
bts_assert(_child_order);
bts_assert(_active);
auto on_result_w = [this, on_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_result](auto const &) { on_result(r); });
};
_child_order_services->update(p, q, on_result_w);
}
template <class CB>
void ChildOrder::update(
api::orderman::Update const &update, CB const &on_result) {
bts_assert(_child_order);
bts_assert(_active);
auto on_result_w = [this, on_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_result](auto const &) { on_result(r); });
};
_child_order_services->update(update, on_result_w);
}
// Detach
template <class CB>
void ChildOrder::detach(CB const &on_result) {
bts_assert(_child_order);
bts_assert(_active);
auto on_result_w = [this, on_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_result](auto const &) { on_result(r); });
};
_child_order_services->detach(on_result);
}
// End Detach
template <class CB>
void ChildOrder::cancel(CB const &on_result) {
bts_assert(_child_order);
bts_assert(_active);
auto on_result_w = [this, on_result](OrderOpResult const &r) {
_parent_order_services->call_with_reentrancy_guard(
[r, on_result](auto const &) { on_result(r); });
};
_child_order_services->cancel(on_result_w);
}
} // namespace bts::orderman
+131 -3
View File
@@ -1,8 +1,136 @@
#include "OrderBuilder.h"
namespace bts {
namespace orderman {
namespace bts::orderman {
using bts::api::orderman::OrderDefinition;
using std::size_t;
std::atomic<size_t> OrderBuilder::_current_builder_id(0);
struct OrderBuilder::PImpl {
PImpl()
: parent_key(null_order_key()), definition(), reserve_sz(0),
order_stash(), builder_id(), order_services(nullptr) {}
PImpl(
OrderKey parent_key,
const api::orderman::OrderDefinition &def,
IOrderFactory &factory,
std::size_t reserve_sz = 1)
: parent_key(parent_key), definition(def), reserve_sz(reserve_sz),
order_stash(), builder_id(++_current_builder_id),
order_services(nullptr) {
bts_assert(parent_key != null_order_key());
while (order_stash.size() != reserve_sz)
order_stash.push_back(&factory.create(parent_key, definition));
}
PImpl(
OrderKey parent_key,
const api::orderman::OrderDefinition &def,
std::size_t reserve_sz = 1)
: parent_key(parent_key), definition(def), reserve_sz(reserve_sz),
order_stash(), builder_id(++_current_builder_id),
order_services(nullptr) {
bts_assert(parent_key != null_order_key());
}
PImpl &operator=(PImpl &&src) {
move_here(src);
return *this;
}
PImpl(PImpl &&src) { operator=(std::move(src)); }
OrderKey parent_key;
bts::api::orderman::OrderDefinition definition;
std::size_t reserve_sz;
std::vector<Order *> order_stash;
std::size_t builder_id;
OrderServices *order_services;
ChildOrder build() {
bts_assert_msg(
order_services != nullptr,
"OrderBuilder::activate() must be called before orders can be built");
ChildOrder result;
if (order_stash.size()) {
result = ChildOrder(*order_stash.back(), *order_services);
order_stash.pop_back();
} else {
LOG(OrderMan, Warn,
"Ran out of pre-allocated child orders for order {}. Will "
"allocate directly. "
"This will have an impact on performance",
parent_key);
result = ChildOrder(
order_services->order_factory().create(parent_key, definition),
*order_services);
}
if (reserve_sz > order_stash.size()) {
auto need = reserve_sz - order_stash.size();
order_services->send_order_create_req(
builder_id, parent_key, definition, need);
}
return result;
}
void move_here(PImpl &src) {
order_services = src.order_services;
parent_key = src.parent_key;
definition = src.definition;
reserve_sz = src.reserve_sz;
order_stash = src.order_stash;
builder_id = src.builder_id;
src.order_stash.clear();
src.builder_id = 0;
src.order_services = nullptr;
bts_assert(parent_key != null_order_key());
}
};
OrderBuilder::OrderBuilder() : me_(std::make_unique<PImpl>()) {}
OrderBuilder::OrderBuilder(
OrderKey parent_key,
OrderDefinition const &def,
IOrderFactory &factory,
size_t reserve_sz)
: me_(std::make_unique<PImpl>(parent_key, def, factory, reserve_sz)) {}
OrderBuilder::OrderBuilder(
OrderKey parent_key,
bts::api::orderman::OrderDefinition const &def,
std::size_t reserve_sz)
: me_(std::make_unique<PImpl>(parent_key, def, reserve_sz)) {}
OrderBuilder::OrderBuilder(OrderBuilder &&src) = default;
OrderBuilder &OrderBuilder::operator=(OrderBuilder &&rhs) = default;
OrderBuilder::~OrderBuilder() = default;
bool OrderBuilder::is_activated() const {
return me_->order_services != nullptr;
}
} // namespace bts
void OrderBuilder::activate(OrderServices &order_services) {
bts_assert(!me_->order_services);
me_->order_services = &order_services;
me_->order_services->subscribe_to_order_creation_reply(
me_->builder_id, [this](auto const &order_ev) {
me_->order_stash.insert(
me_->order_stash.end(), order_ev.orders.begin(),
order_ev.orders.end());
});
}
ChildOrder OrderBuilder::build() { return me_->build(); }
OrderServices &OrderBuilder::services() {
bts_assert(me_->order_services != nullptr);
return *me_->order_services;
}
} // namespace bts::orderman
+19 -96
View File
@@ -1,123 +1,46 @@
#pragma once
#include "lib/api/services/OrderService.pb.h"
#include "lib/logging/logging.h"
#include "lib/orderman/ChildOrder.h"
#include "lib/orderman/OrderServices.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include <atomic>
#include <memory>
namespace bts {
namespace orderman {
#include <atomic>
namespace bts::orderman {
class OrderBuilder {
public:
OrderBuilder()
: _parent_key(null_order_key()), _definition(), _reserve_sz(0),
_order_stash(), _builder_id(), _order_services(nullptr) {}
OrderBuilder(OrderBuilder &&src) { operator=(std::move(src)); }
OrderBuilder();
OrderBuilder(OrderBuilder &&src);
OrderBuilder(
OrderKey parent_key,
api::orderman::OrderDefinition const &def,
IOrderFactory &factory,
size_t reserve_sz = 1)
: _parent_key(parent_key), _definition(def), _reserve_sz(reserve_sz),
_order_stash(), _builder_id(++_current_builder_id),
_order_services(nullptr)
{
bts_assert(parent_key != null_order_key());
while (_order_stash.size() != reserve_sz)
_order_stash.push_back(&factory.create(parent_key, _definition));
}
size_t reserve_sz = 1);
OrderBuilder(
OrderKey parent_key,
api::orderman::OrderDefinition const &def,
size_t reserve_sz = 1)
: _parent_key(parent_key), _definition(def), _reserve_sz(reserve_sz),
_order_stash(), _builder_id(++_current_builder_id),
_order_services(nullptr)
size_t reserve_sz = 1);
{
bts_assert(parent_key != null_order_key());
}
~OrderBuilder();
bool is_activated() { return _order_services != nullptr; }
OrderBuilder &operator=(OrderBuilder &&src);
void activate(OrderServices &order_services) {
bts_assert(!_order_services);
_order_services = &order_services;
_order_services->subscribe_to_order_creation_reply(
_builder_id, [this](auto const &order_ev) {
_order_stash.insert(
_order_stash.end(), order_ev.orders.begin(),
order_ev.orders.end());
});
}
ChildOrder build() {
bts_assert_msg(
_order_services,
"OrderBuilder::activate() must be called before orders can be built");
ChildOrder result;
if (_order_stash.size()) {
result = ChildOrder(*_order_stash.back(), *_order_services);
_order_stash.pop_back();
} else {
LOG(OrderMan, Warn,
"Ran out of pre-allocated child orders for order {}. Will "
"allocate directly. "
"This will have an impact on performance",
_parent_key);
result = ChildOrder(
_order_services->order_factory().create(_parent_key, _definition),
*_order_services);
}
if (_reserve_sz > _order_stash.size()) {
auto need = _reserve_sz - _order_stash.size();
_order_services->send_order_create_req(
_builder_id, _parent_key, _definition, need);
}
return result;
}
OrderBuilder &operator=(OrderBuilder &&src) {
move_here(src);
return *this;
}
OrderServices &services() {
bts_assert(_order_services != nullptr);
return *_order_services;
}
bool is_activated() const;
void activate(OrderServices &order_services);
ChildOrder build();
OrderServices &services();
private:
void move_here(OrderBuilder &src) {
_order_services = src._order_services;
_parent_key = src._parent_key;
_definition = src._definition;
_reserve_sz = src._reserve_sz;
_order_stash = src._order_stash;
_builder_id = src._builder_id;
src._order_stash.clear();
src._builder_id = 0;
src._order_services = nullptr;
bts_assert(_parent_key != null_order_key());
}
static std::atomic<std::size_t> _current_builder_id;
OrderKey _parent_key;
api::orderman::OrderDefinition _definition;
size_t _reserve_sz;
std::vector<Order *> _order_stash;
size_t _builder_id;
OrderServices *_order_services;
struct PImpl;
std::unique_ptr<PImpl> me_;
static std::atomic<size_t> _current_builder_id;
void move_here(OrderBuilder &src);
};
} // namespace orderman
} // namespace bts
} // namespace bts::orderman
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include "lib/domain_model/common.h"
#include "lib/md/protos/co2.pb.h"
#include "lib/utils/bts_chrono.h"
namespace bts::orderman {
struct Trade {
TimeStamp ts;
Price price;
Quantity quantity;
Quantity total_volume;
co2::AggressorSide aggressor_side;
};
} // namespace bts::orderman
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "lib/orderman/Trade.h"
#include <boost/container/small_vector.hpp>
namespace bts::orderman {
typedef boost::container::small_vector<Trade, 5> TradeVector;
} // namespace bts::orderman
+3 -2
View File
@@ -5,7 +5,7 @@
#include "lib/md/IntPrice.h"
#include "lib/md/MDConsumer.h"
#include "lib/md/MDConsumerHandle.h"
#include "lib/md/cme/MDP3Utils.h"
#include "server/sparkd/md/cme/MDP3Utils.h"
#include "lib/mercury/actors/Actor.h"
#include "lib/mercury/actors/IPollableActor.h"
#include "lib/mercury/events/TimerEvent.h"
@@ -51,6 +51,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
void init() override {
LOG(OrderMan, Warn, "Starting MDSource actor");
// TODO: why is this hitting?
bts_assert_msg(
_md_consumer.all_heartbeats_ok(),
"Marketdata heartbeats are bad. MDConsumer will not recover from "
@@ -159,7 +160,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
for (auto &per_sec_ev : c.securities) {
if (trade.key() == per_sec_ev.key().value()) {
trade_count++;
per_sec_ev.trades.emplace_back(TickerUpdateEvent::Trade{
per_sec_ev.trades.emplace_back(Trade{
datetime::utc_ll_to_timepoint(trade.time()),
Price(md::IntPrice(
trade.mantissa(), (int8_t)trade.exponent())
+2 -22
View File
@@ -1,24 +1,9 @@
#include "CCAPIDeribitHelpers.h"
#include "CCAPIDeribitOrderMux.h"
#include "CCAPIOrderState.h"
#include "CCAPISingleOrderManager.h"
#include "ccapi_cpp/ccapi_session.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/core/TimeStamp.h"
#include "lib/domain_model/Security.pb.h"
#include "lib/io/JsonReader.h"
#include "lib/logging/logging.h"
#include "lib/md/SecurityOracle.h"
#include "lib/md/binance/BinanceSpot.h"
#include "lib/orderman/CancelOrderMsg.h"
#include "lib/orderman/CancelReplaceOrderMsg.h"
#include "lib/orderman/CreateOrderMsg.h"
#include "lib/orderman/RFQMsg.h"
#include "lib/orderman/ccapi/CCAPIOrderCallbackMsgs.h"
#include "lib/orderman/ccapi/ICCAPIOrderMux.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/bts_assert.h"
#include <bits/stdint-uintn.h>
#include <boost/lockfree/spsc_queue.hpp>
#include <ccapi_cpp/ccapi_event.h>
@@ -27,16 +12,11 @@
#include <ccapi_cpp/ccapi_request.h>
#include <ccapi_cpp/ccapi_subscription.h>
#include <ccapi_cpp/ccapi_util_private.h>
#include <chrono>
#include <google/protobuf/timestamp.pb.h>
#include <google/protobuf/util/time_util.h>
#include <memory>
#include <nlohmann/json_fwd.hpp>
#include <optional>
#include <ostream>
#include <random>
#include <string>
#include <unordered_map>
#include <vector>
namespace bts::orderman {
@@ -155,4 +135,4 @@ bool CCAPIDeribitHelpers::parse_working_position(
return true;
}
} // namespace bts::orderman
} // namespace bts::orderman
@@ -1,3 +1,5 @@
#ifdef SPARK_ENABLE_CCAPI
#include "CCAPIDeribitOrderMux.h"
#include "CCAPIOrderState.h"
#include "CCAPISingleOrderManager.h"
@@ -953,4 +955,6 @@ void CCAPIDeribitOrderMux::_send_buffered_orders(uint32_t sec_id) {
"n_buffered_orders: {}",
me->buffered_creates.size());
}
} // namespace bts::orderman
} // namespace bts::orderman
#endif
@@ -1,4 +1,6 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "ICCAPIOrderMux.h"
#include "lib/md/SecurityOracle.h"
#include "lib/orderman/CreateOrderMsg.h"
@@ -72,4 +74,6 @@ class CCAPIDeribitOrderMux : public ICCAPIOrderMux {
void _handle_generic_pvt_request(const nlohmann::json &json_obj);
};
} // namespace bts::orderman
} // namespace bts::orderman
#endif
+17 -22
View File
@@ -1,35 +1,30 @@
#pragma once
#include <memory>
#include "boost/container/small_vector.hpp"
#include "lib/md/protos/co2.pb.h"
#include "lib/mercury/events/Event.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/utils/bts_chrono.h"
#include "lib/orderman/TradeVector.h"
namespace bts {
namespace orderman {
namespace bts::orderman {
class TickerUpdateEvent : public mercury::Event<TickerUpdateEvent> {
SecurityKey _key;
SecurityKey key_;
public:
using SynopsisPolicy = mercury::NoSynopsis;
SecurityKey key() const { return _key; };
TradeVector trades;
struct Trade {
TimeStamp ts;
Price price;
Quantity quantity;
Quantity total_volume;
co2::AggressorSide aggressor_side;
};
TickerUpdateEvent(SecurityKey key);
boost::container::small_vector<TickerUpdateEvent::Trade, 5> trades;
TickerUpdateEvent(SecurityKey key) : _key(key), trades() {}
SecurityKey key() const;
};
typedef boost::container::small_vector<TickerUpdateEvent::Trade, 5> TradeVector;
} // namespace orderman
} // namespace bts
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
inline SecurityKey TickerUpdateEvent::key() const { return key_; };
inline TickerUpdateEvent::TickerUpdateEvent(SecurityKey key)
: key_(key), trades() {}
} // namespace bts::orderman
@@ -1,3 +1,5 @@
#ifdef SPARK_ENABLE_CCAPI
#include "CCAPIExecutionGateway.h"
#include "lib/api/service_helpers/AdminServiceUtils.h"
#include "lib/api/services/AdminService.pb.h"
@@ -492,4 +494,6 @@ SingleOrderMux *CCAPIExecutionGateway::_find_mux_from_orderkey(OrderKey key) {
return _find_mux(it->second.routing_record);
}
} // namespace bts::orderman
} // namespace bts::orderman
#endif
@@ -1,6 +1,7 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "IExecutionGateway.h"
#include "lib/orderman/gateways/IExecutionGateway.h"
#include "lib/api/services/AdminService.pb.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/domain_model/Security.pb.h"
@@ -10,7 +11,7 @@
#include "lib/orderman/RFQMsg.h"
#include "lib/orderman/ccapi/CCAPIOrderState.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "server/sparkd/md/platforms/emumba/IEMOrderMuxer.h"
#include <bits/stdint-uintn.h>
#include <cstdint>
@@ -73,3 +74,5 @@ class CCAPIExecutionGateway : public IExecutionGateway {
} // namespace orderman
} // namespace bts
#endif
@@ -2,7 +2,7 @@
#include "lib/domain_model/Security.pb.h"
#include "lib/md/SecurityOracle.h"
#include "lib/orderman/gateways/CCAPIExecutionGateway.h"
#include "lib/orderman/gateways/EMExecutionGateway.h"
// #include "lib/orderman/gateways/EMExecutionGateway.h"
#include "lib/orderman/gateways/FakeExecutionGateway.h"
#include "lib/orderman/gateways/GLBXExecutionGateway.h"
#include "lib/orderman/gateways/GLBXiLink3ExecutionGateway.h"
@@ -34,9 +34,11 @@ std::unique_ptr<IExecutionGateway> ExecutionGatewayFactory::create_gateway(
return std::make_unique<ilink3::GLBXiLink3ExecutionGateway>(
conn, pl, o);
return std::make_unique<GLBXExecutionGateway>(conn, pl, o);
} else if (conn.venue() == bts::api::VENUE_DERIBIT)
// return std::make_unique<EMExecutionGateway>(o);
} else if (conn.venue() == bts::api::VENUE_DERIBIT) {
#ifdef SPARK_ENABLE_CCAPI
return std::make_unique<CCAPIExecutionGateway>(conn.venue(), o);
#endif
}
return std::make_unique<FakeExecutionGateway>(o);
}
@@ -1,5 +1,4 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "lib/core/EnumMacros.h"
#include "lib/core/FMT_IMPL.h"
@@ -26,7 +25,6 @@ ENUM_CLASS_DEF(
#undef FOR_EACH_GLBX_ILINK3_EXECUTION_GATEWAY_STATE
} // namespace bts::orderman::ilink3
#endif
FMT_IMPL(
bts::orderman::ilink3::GLBXiLink3ExecutionGatewayState, s, "{}", s.name());
@@ -228,13 +228,19 @@ struct SEGSecurityData {
const CancelReplaceOrderMsg &cxlr_msg =
std::get<CancelReplaceOrderMsg>(msg);
SEGOrderState *seg_os = find_order(cxlr_msg.create_fields.key);
bts_assert(seg_os != nullptr);
if(seg_os == nullptr){
LOG(OrderMan, Info, "SimExecutionGateway::apply_order_action tried updating unkown order");
return {ActionResult::NoOp, nullptr};
}
auto action_result = seg_os->apply_change(cxlr_msg);
return {action_result, seg_os};
} else if (std::holds_alternative<CancelOrderMsg>(msg)) {
const CancelOrderMsg &cxl_msg = std::get<CancelOrderMsg>(msg);
SEGOrderState *seg_os = find_order(cxl_msg.key);
bts_assert(seg_os != nullptr);
if(seg_os == nullptr){
LOG(OrderMan, Info, "SimExecutionGateway::apply_order_action tried canceling unkown order");
return {ActionResult::NoOp, nullptr};
}
auto action_result = seg_os->apply_cxl(cxl_msg);
return {action_result, seg_os};
}
@@ -247,11 +253,12 @@ struct SEGSecurityData {
}
}
void remove_inactive_orders() {
orders.erase(
std::remove_if(
orders.begin(), orders.end(),
[](const auto &ord) { return !ord.active(); }),
orders.end());
return;
// orders.erase(
// std::remove_if(
// orders.begin(), orders.end(),
// [](const auto &ord) { return !ord.active(); }),
// orders.end());
}
void record_trade(const SEGTradeEntry &seg_te) { trades.push_back(seg_te); }
};
@@ -354,8 +361,20 @@ void SimExecutionGateway::send_fix(const jetfix::OutFIXPtr &p, trace::CorID) {
//bts_assert(false);
}
// This is where the messages are sent to the exchange. We simply write the
// operation to a concurrent queue and dequeue in the market data thread
int msg_type(const OrderMsg &msg) {
// if (std::holds_alternative<CreateOrderMsg>(msg)) {
// return 1;
// } else if (std::holds_alternative<CancelReplaceOrderMsg>(msg)) {
// return 2;
// } else if (std::holds_alternative<CancelOrderMsg>(msg)) {
// return 3;
// }
return msg.index();
}
// This is where the messages are received from the client. For sim,
// we simply write the operation to a concurrent queue (buffered_order_ops)
// and dequeue in the market data thread (when the market data source needs the matched_books)
// (the resulting execution rerports are written to another conrrent queue)
// which is then dequeued on dispatch_incoming_fix_messages)
void SimExecutionGateway::send_msgs(
@@ -367,9 +386,10 @@ void SimExecutionGateway::send_msgs(
"SimExecutionGateway::send_msgs tid: {} tname: {} count: {}",
bts::os::get_thread_id(), bts::os::get_thread_name(), msg_vec.size());
for (auto &m : msg_vec) {
auto mtype = msg_type(m);
LOG(OrderMan, Info,
"SimExecutionGateway::send_msgs tid: {} tname: {} m: {}",
bts::os::get_thread_id(), bts::os::get_thread_name(), m);
"SimExecutionGateway::send_msgs tid: {} tname: {} m_type: {}",
bts::os::get_thread_id(), bts::os::get_thread_name(), mtype);
me->buffered_order_ops.push(m);
}
}
@@ -401,15 +421,16 @@ void SimExecutionGateway::set_producers(
void SimExecutionGateway::do_work() {}
// this gets called by GeneralMarketDataListener, so this can alter the based on order activity
bool SimExecutionGateway::get_matched_book(
bts::md::SecurityKey k,
std::array<md::PriceSize, md::COCONUT_MAX_DEPTH> &bids,
std::array<md::PriceSize, md::COCONUT_MAX_DEPTH> &asks) {
std::array<md::PriceSize, md::COCONUT_MAX_DEPTH> &out_bids,
std::array<md::PriceSize, md::COCONUT_MAX_DEPTH> &out_asks) {
bts::SecurityKey key = k;
// security entry is added here
SEGSecurityData &seg_sec = *_find_add_security_entry(key, true, true);
// this is where the book is updated
to_book(bids, asks, seg_sec.real_book);
to_book(out_bids, out_asks, seg_sec.real_book);
if (seg_sec.orders.empty() && seg_sec.trades.empty() &&
me->buffered_order_ops.empty())
return false;
@@ -424,6 +445,10 @@ bool SimExecutionGateway::get_matched_book(
OrderMsg ord_msg;
// all ord_msgs are popped and enqueued in the security's SEGSecurityData
while (me->buffered_order_ops.try_pop(ord_msg)) {
// LOG(OrderMan, Info,
// "SimExecutionGateway::get_matched_book ord_msg dequeued from "
// "get_matched_book, ord_msg: {}",
// ord_msg);
std::optional<SecurityKey> op_sec_key = _extract_sec_key(ord_msg);
if (!op_sec_key.has_value()) {
LOG(OrderMan, Error,
@@ -443,7 +468,7 @@ bool SimExecutionGateway::get_matched_book(
}
// this takes care of everything !
_process_transaction();
from_book(seg_sec.matched_book, bids, asks);
from_book(seg_sec.matched_book, out_bids, out_asks);
// cleanup
for (SecurityKey key : me->touched_secs) {
SEGSecurityData &t_seg_sec = *_find_add_security_entry(key, false, true);
@@ -4,6 +4,8 @@
#include "lib/domain_model/Security.pb.h"
#include "lib/logging/logging.h"
#include <memory>
#include <sstream>
#include <streambuf>
namespace bts::orderman {
@@ -52,10 +54,10 @@ void SimExecutionGatewayUniqueWrapper::send_fix(
}
void SimExecutionGatewayUniqueWrapper::send_msgs(
std::vector<OrderMsg> const &msgs, trace::CorID cid) {
for (const auto &m : msgs) {
LOG(OrderMan, Info, "SimExecutionGatewayUniqueWrapper::send_msgs m: {}",
m);
}
LOG(OrderMan, Info,
"SimExecutionGatewayUniqueWrapper::send_msgs tid: {} tname: {} count: "
"{}",
bts::os::get_thread_id(), bts::os::get_thread_name(), msgs.size());
me->gateway->send_msgs(msgs, cid);
}
-2
View File
@@ -1,5 +1,4 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "lib/core/EnumMacros.h"
#include "lib/core/FMT_IMPL.h"
@@ -27,6 +26,5 @@ ENUM_CLASS_DEF(IL3State, FOR_EACH_IL3_STATE)
#undef FOR_EACH_IL3_STATE
} // namespace bts::orderman::ilink3
#endif
FMT_IMPL(bts::orderman::ilink3::IL3State, s, "{}", s.name());
+21 -21
View File
@@ -1,35 +1,35 @@
#pragma once
#include "lib/orderman/orderman_utils/orderman_common.h"
#include <memory>
// fwd
namespace bts {
namespace orderman {
namespace bts::orderman {
class Order;
}
namespace api {
namespace orderman {
} // namespace bts::orderman
namespace bts::api::orderman {
class OrderDefinition;
class OrderRecord;
} // namespace orderman
} // namespace api
} // namespace bts
// end fwd
} // namespace bts::api::orderman
namespace bts {
namespace orderman {
namespace bts::orderman {
inline OrderKey null_order_key() { return OrderKey(0); }
constexpr OrderKey null_order_key();
class IOrderFactory {
public:
Order &create(api::orderman::OrderDefinition const &def) {
return create(null_order_key(), def);
}
virtual Order &
create(OrderKey parent, api::orderman::OrderDefinition const &) = 0;
Order &create(api::orderman::OrderDefinition const &def);
virtual Order &create(
OrderKey parent, api::orderman::OrderDefinition const &) = 0;
};
} // namespace orderman
} // namespace bts
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
constexpr OrderKey null_order_key() { return OrderKey{0}; }
inline Order &IOrderFactory::create(api::orderman::OrderDefinition const &def) {
return create(null_order_key(), def);
}
} // namespace bts::orderman
+71 -34
View File
@@ -1,50 +1,35 @@
#pragma once
#include "lib/config/protos/market_data.pb.h"
#include "lib/domain_model/common.h"
#include "lib/core/FMT_IMPL.h"
#include "lib/md/PriceSize.h"
#include <memory>
namespace bts {
namespace orderman {
#include <array>
namespace bts::orderman {
using BookSide = std::array<md::PriceSize, 5>;
class Book {
public:
Book() : _security_key(), _book() {}
Book();
Book(SecurityKey security_key);
Book(SecurityKey security_key, const BookSide& bid, const BookSide& ask);
Book(const Book& rhs);
Book(SecurityKey key) : _security_key(key), _book() {}
Book& operator=(Book const& rhs);
Book(SecurityKey key, BookSide const &bid, BookSide const &ask)
: _security_key(key), _book{bid, ask} {}
constexpr SecurityKey security_key() const;
Book(Book const &rhs) : _security_key(rhs._security_key), _book(rhs._book) {}
const BookSide& bid() const;
BookSide& bid();
Book &operator=(Book const &rhs) {
_security_key = rhs._security_key;
_book = rhs._book;
return *this;
}
const BookSide& ask() const;
BookSide& ask();
SecurityKey security_key() const { return _security_key; };
size_t n_levels() const;
BookSide const &bid() const { return _book[0]; }
BookSide const &ask() const { return _book[1]; }
BookSide &bid() { return _book[0]; }
BookSide &ask() { return _book[1]; }
size_t n_levels() const { return std::tuple_size<BookSide>(); }
BookSide const &side(api::Side bs) const {
bts_assert_d(bs != api::SIDE_UNDEFINED);
return _book[bs - api::SIDE_BUY];
}
BookSide &side(api::Side bs) {
bts_assert_d(bs != api::SIDE_UNDEFINED);
return _book[bs - api::SIDE_BUY];
}
const BookSide& side(api::Side bs) const;
BookSide& side(api::Side bs);
private:
friend class MDSource;
@@ -52,5 +37,57 @@ class Book {
std::array<BookSide, 2> _book;
};
} // namespace orderman
} // namespace bts
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
inline Book::Book() : _security_key(), _book() {}
inline Book::Book(SecurityKey security_key)
: _security_key(security_key), _book() {}
inline Book::Book(
SecurityKey security_key, const BookSide& bid, const BookSide& ask)
: _security_key(security_key), _book{bid, ask} {}
inline Book::Book(const Book& rhs)
: _security_key(rhs._security_key), _book(rhs._book) {}
inline Book& Book::operator=(const Book& rhs) {
_security_key = rhs._security_key;
_book = rhs._book;
return *this;
}
constexpr SecurityKey Book::security_key() const { return _security_key; };
inline const BookSide& Book::bid() const { return _book[0]; }
inline const BookSide& Book::ask() const { return _book[1]; }
inline BookSide& Book::bid() { return _book[0]; }
inline BookSide& Book::ask() { return _book[1]; }
inline std::size_t Book::n_levels() const {
return std::tuple_size<BookSide>();
}
inline const BookSide& Book::side(api::Side bs) const {
bts_assert_d(bs != api::SIDE_UNDEFINED);
return _book[bs - api::SIDE_BUY];
}
inline BookSide& Book::side(api::Side bs) {
bts_assert_d(bs != api::SIDE_UNDEFINED);
return _book[bs - api::SIDE_BUY];
}
} // namespace bts::orderman
// Formats bbo
FMT_IMPL(
bts::orderman::Book,
b,
"({}) {} <=> {} ({})",
b.bid()[0].size(),
b.bid()[0].price(),
b.ask()[0].price(),
b.ask()[0].size());
+15 -10
View File
@@ -578,6 +578,19 @@ OrderOpResult GLBXNativeOrder::on_update(Price p, Quantity q) {
OrderOpResult GLBXNativeOrder::on_update(api::orderman::Update const &update) {
auto p = Price(update.price());
auto q = Quantity(update.quantity());
auto send_update_in_sim_mode = [&]() {
if (me->in_sim_mode) {
CancelReplaceOrderMsg cxl_repl_msg = {};
_create_create_msg(cxl_repl_msg.create_fields);
Price new_p = Price(update.price());
Quantity new_q = bts::orderman::get_update_quantity(update);
cxl_repl_msg.create_fields.price = new_p;
cxl_repl_msg.create_fields.quantity = new_q;
me->services->queue_order_msg(*me->session_services, cxl_repl_msg);
}
};
LOG(OrderMan, Info, "GLBXNativeOrder::on_update update: {}",
utils::pb::to_string(update));
auto scoped_log =
me->services->log_scope("NativeOrder::on_update", key().value());
@@ -640,16 +653,7 @@ OrderOpResult GLBXNativeOrder::on_update(api::orderman::Update const &update) {
me->services->report_definition(me->definition);
me->encoder->update_cancel_replace_stop_px(
stop_px, me->price_encoding_precision);
if (me->in_sim_mode) {
CancelReplaceOrderMsg cxl_repl_msg = {};
_create_create_msg(cxl_repl_msg.create_fields);
Price new_p = Price(update.price());
Quantity new_q = bts::orderman::get_update_quantity(update);
cxl_repl_msg.create_fields.price = new_p;
cxl_repl_msg.create_fields.quantity = new_q;
me->services->queue_order_msg(
*me->session_services, cxl_repl_msg);
}
send_update_in_sim_mode();
}
} else if (
update.custom().custom_case() ==
@@ -693,6 +697,7 @@ OrderOpResult GLBXNativeOrder::on_update(api::orderman::Update const &update) {
}
me->encoder->update_cancel_replace_encoding(q, *me->services);
me->encoder->send_cancel_replace(*me->services, *me->session_services);
send_update_in_sim_mode();
me->services->governor(*me->session_services).record_sent();
update_working_position();
@@ -1,18 +0,0 @@
#include "lib/orderman/orders/algo/IAlgoOrder.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
namespace bts {
namespace orderman {
#include "lib/orderman/orders/algo/IAlgoOrder.h"
IAlgoOrder::~IAlgoOrder() {}
OrderOpResult IAlgoOrder::do_before_launch(IBeforeLaunchAlgoServices &) {
return {};
}
void IAlgoOrder::do_launch_attached(IAlgoServices &serv, OrderKey, ChildState) {
serv.terminate_error("Algo does not implement attachment functionality");
}
} // namespace orderman
} // namespace bts
+103 -26
View File
@@ -1,17 +1,20 @@
#pragma once
#include "lib/core/FMT_IMPL.h"
#include "lib/md/protos/co2.pb.h"
#include "lib/orderman/calculations/ITheoCalculator.h"
#include "lib/orderman/events/TickerUpdateEvent.h"
#include "lib/orderman/orders/Order.h"
#include "lib/orderman/orders/OrderOpResult.h"
#include "lib/orderman/orders/algo/PositionUpdate.h"
#include <memory>
#include <string>
namespace bts {
namespace orderman {
namespace bts::orderman {
enum class ChildStatus { WORKING, COMPLETED, CANCELED };
constexpr std::string ChildStatus_Name(ChildStatus child_status);
struct ChildState {
Price price;
Quantity quantity;
@@ -29,46 +32,46 @@ class IAlgoServices;
class IAlgoOrder {
public:
virtual ~IAlgoOrder();
virtual ~IAlgoOrder() = default;
// --------------------------------------------------------------
// Events Handlers - These will be optionally invoked if subscribed to
// --------------------------------------------------------------
// The child order specified by the order key has changed
virtual void on_child_change(OrderKey, ChildState const &) {}
virtual void on_child_change(
OrderKey child_key, const ChildState& child_state);
// The book for the specified security has changed
virtual void on_book(SecurityKey, Book const &) {}
virtual void on_book(SecurityKey security_key, const Book& book);
// Trades stream for the specified security
virtual void on_ticker(SecurityKey, TradeVector const &){};
virtual void on_ticker(SecurityKey security_key, const TradeVector& trades);
// The trading status for the specified security has changed
virtual void on_status(SecurityKey, co2::TradingStatus){};
virtual void on_status(SecurityKey security_key, co2::TradingStatus status);
// The settlement price for the specified security has changed
virtual void on_settlement(SecurityKey, Price){};
virtual void on_settlement(SecurityKey security_key, Price price);
// The current position for the security, account combination
virtual void on_position(PositionUpdate const &){};
virtual void on_position(const PositionUpdate& position);
// A signal sent ahead of a book update, indicating that one or
// more trades between the two price levels specified for a buy or
// sell aggressor.
virtual void on_trade_summary_signal(
SecurityKey,
api::Side /* aggressor */,
Price /* last_price */,
Quantity /* last_quantity */,
uint /* number of price levels */) {}
SecurityKey security_key,
api::Side aggressor,
Price last_price,
Quantity last_quantity,
uint num_price_levels);
// The calculator has updated itself, theos need to be recomputed
virtual void on_theos(
analytics::ITheoCalculator const * /*theo_calculator*/) {}
virtual void on_theos(const analytics::ITheoCalculator* theo_calculator);
// A periodic timer has fired
virtual void on_timer() {}
virtual void on_timer();
// --------------------------------------------------------------
// Hooks - These allow you to customize the behaviour of your algo
@@ -80,10 +83,10 @@ class IAlgoOrder {
// so it can be used for any expensive initialization
// operations. The implementation should return an error code if it
// detects an invalid parameter in the algo definition.
virtual OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &);
virtual OrderOpResult do_before_launch(IBeforeLaunchAlgoServices&);
// Override this method to activate your algo
virtual void do_launch(IAlgoServices &) = 0;
virtual void do_launch(IAlgoServices& services) = 0;
// Override this method to activate your algo using an existing
// working order as a child. Attached child orders are
@@ -91,12 +94,13 @@ class IAlgoOrder {
// changes for the attached order. Note that any fills reported
// from this call through IAlgoServices::update_fill_state() will
// be included in the fills_at_attach_time count field.
virtual void do_launch_attached(IAlgoServices &, OrderKey child, ChildState);
virtual void do_launch_attached(
IAlgoServices& services, OrderKey child_key, ChildState child_state);
// Implement this method to respond to updates to your algo. The
// implementation should validate the update and either apply it,
// if valid or terminate with an error otherwise
virtual void do_update(api::orderman::Update const &) {}
virtual void do_update(api::orderman::Update const& update);
// Override this method to provide custom handling of a cancel
// request. The default behaviour is to cancel both this order and
@@ -104,8 +108,81 @@ class IAlgoOrder {
// for example to detach from specific children before canceling or
// to implement custom cleanup behaviour when canceled.
enum class OnCancelResponse { IGNORE, HONOR };
virtual OnCancelResponse on_cancel() { return OnCancelResponse::HONOR; }
virtual OnCancelResponse on_cancel();
};
} // namespace orderman
} // namespace bts
//////////////////////////////////////////////////
// Definition
//////////////////////////////////////////////////
constexpr std::string ChildStatus_Name(ChildStatus child_status) {
switch (child_status) {
case ChildStatus::WORKING:
return "WORKING";
case ChildStatus::COMPLETED:
return "COMPLETED";
case ChildStatus::CANCELED:
return "CANCELED";
default:
return "UNKNOWN";
}
}
inline void IAlgoOrder::on_child_change(
OrderKey /*child_key*/, const ChildState& /*child_state*/) {}
inline void IAlgoOrder::on_book(
SecurityKey /*security_key*/, const Book& /*book*/) {}
inline void IAlgoOrder::on_ticker(
SecurityKey /*security_key*/, const TradeVector& /*trades*/) {}
inline void IAlgoOrder::on_status(
SecurityKey /*security_key*/, co2::TradingStatus /*status*/) {}
inline void IAlgoOrder::on_settlement(
SecurityKey /*security_key*/, Price /*price*/) {}
inline void IAlgoOrder::on_position(const PositionUpdate& /*position*/) {}
inline void IAlgoOrder::on_trade_summary_signal(
SecurityKey /*security_key*/,
api::Side /*aggressor*/,
Price /*last_price*/,
Quantity /*last_quantity*/,
uint /*num_price_levels*/) {}
inline void IAlgoOrder::on_theos(
analytics::ITheoCalculator const* /*theo_calculator*/) {}
inline void IAlgoOrder::on_timer() {}
inline OrderOpResult IAlgoOrder::do_before_launch(IBeforeLaunchAlgoServices&) {
return {};
}
inline void IAlgoOrder::do_launch_attached(
IAlgoServices& /*services*/,
OrderKey /*child_key*/,
ChildState /*child_state*/) {}
inline void IAlgoOrder::do_update(api::orderman::Update const& /*update*/) {}
inline IAlgoOrder::OnCancelResponse IAlgoOrder::on_cancel() {
return OnCancelResponse::HONOR;
}
} // namespace bts::orderman
FMT_IMPL(
bts::orderman::ChildState,
st,
"{} {} @ {}, filled {} @ {} (last {} @ {})",
bts::orderman::ChildStatus_Name(st.status),
st.price,
st.quantity,
st.total_fills,
st.avg_price,
st.last_fills,
st.last_price);
@@ -278,11 +278,14 @@ PyramidAlgo::_get_creation_params(
// error checking
if (rv.top_spread.value() != serv.security().id())
return std::make_tuple(
rv, false, "Algo can only be launched from the Top Spread");
rv, false,
std::string{"Algo can only be launched from the Top Spread"});
if (rv.cover_spreads.size() < 2)
return std::make_tuple(rv, false, "Not enough cover spreads");
return std::make_tuple(
rv, false, std::string{"Not enough cover spreads"});
if (rv.cover_spreads[0].value() == 0 || rv.cover_spreads[1].value() == 0)
return std::make_tuple(rv, false, "Not enough cover spreads");
return std::make_tuple(
rv, false, std::string{"Not enough cover spreads"});
return std::make_tuple(rv, true, std::string(""));
}
@@ -0,0 +1,361 @@
#include "lib/orderman/orders/algo/algo_orders/QuotingAlgo.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/domain_model/Security.h"
#include "lib/domain_model/Security.pb.h"
#include "lib/domain_model/SecurityKey.h"
#include "lib/domain_model/common.h"
#include "lib/logging/logging.h"
#include "lib/md/PriceSize.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orderman_utils/Book.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/orderman/orderman_utils/orderman_utils.h"
#include "lib/orderman/orders/algo/IAlgoOrder.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/orders/algo/algo_orders/QuotingAlgo.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/bts_assert.h"
#include <bits/stdint-intn.h>
#include <bits/stdint-uintn.h>
#include <boost/circular_buffer.hpp>
#include <boost/tokenizer.hpp>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <fcntl.h>
#include <google/protobuf/util/time_util.h>
#include <memory>
#include <optional>
#include <stdint.h>
#include <string>
namespace bts {
namespace orderman {
using namespace api::orderman;
using namespace google::protobuf;
using namespace boost;
template <class T>
bool from_string(const std::string &str, T *out) {
return google::protobuf::TextFormat::ParseFromString(str, out);
}
//All information required from user
struct QuotingAlgoParams {
SecurityKey security_key_id;
std::string security_name_str;
size_t n_ticks;
bool quote_around_market;
bool quote_around_theo;
ChildTemplate *child_template = nullptr;
api::Side side;
QuotingAlgoParams(
SecurityKey security_key_id,
std::string security_name_str,
size_t n_ticks,
bool quote_around_market,
bool quote_around_theo,
ChildTemplate* child,
api::Side side
)
: security_key_id(security_key_id), security_name_str(security_name_str),
n_ticks(n_ticks), quote_around_market(quote_around_market),
quote_around_theo(quote_around_theo), child_template(child),side(side) {}
std::string print() {
std::string sep_obj = ", ";
std::string sep_key_val = ":";
std::string s = "security_name" + sep_key_val + security_name_str;
auto add_to_s = [&](std::string desc, std::string val) {
if (s != "") {
s += sep_obj;
}
s += desc + sep_key_val + val;
};
add_to_s("security_key_id", std::to_string(security_key_id.value()));
add_to_s("quote_around_market", std::to_string(quote_around_market));
add_to_s("quote_around_theo", std::to_string(quote_around_theo));
add_to_s("n_ticks", std::to_string(n_ticks));
return s;
}
};
// enum class QuotingAlgoParams::State { WORKING, DONE };
// All state information needed for this algo
struct QuotingAlgo::PImpl {
OrderKey self_key = null_order_key();
OrderDefinition self_def;
google::protobuf::uint32 self_security_key;
IBeforeLaunchAlgoServices *servg;
bts::orderman::OrderKey child_order_key=null_order_key();
Price child_order_price=Price(0);
google::protobuf::TextFormat::Printer printer;
double tick_size;
IAlgoServices *algo = nullptr;
analytics::ITheoCalculator const *theo_calc = nullptr;
SecurityKey driver_sec_key;
bool subbed_to_driver = false;
std::optional<Book> driver_book = std::nullopt;
std::optional<Book> sec_book = std::nullopt;
std::unique_ptr<QuotingAlgoParams> algo_params;
std::optional<double> current_mid;
PImpl() { printer.SetSingleLineMode(true); }
};
QuotingAlgo::QuotingAlgo() : me(new PImpl()) {}
QuotingAlgo::~QuotingAlgo() {}
IAlgoServices &QuotingAlgo::_algo() const { return *me->algo; }
//create a child template so we can
// quickly launch our child when the time comes.
ChildTemplate *_get_child_template(
IBeforeLaunchAlgoServices &serv,
const std::optional<CustomOrderDefinition> &cod,
SecurityKey key,
api::Side side) {
OrderDefinition rv = serv.definition();
rv.set_security_key(key.value());
rv.set_side(side);
if(cod.has_value()){
rv.mutable_custom_def()->CopyFrom(cod.value());
return &(serv.make_child_template(rv));
}
//if child order not defined defaults to limit order
rv.mutable_custom_def()->mutable_limit()->MergeFrom(
api::orderman::LimitOrderDefinition());
return &(serv.make_child_template(rv));
}
// converts paramters recieved from the user and saves them in out_p returns
// true if all parameters have values
bool _convert_params(
IBeforeLaunchAlgoServices &serv,
const CustomOrderDefinition &cod,
std::unique_ptr<QuotingAlgoParams> &out_p) {
const DynamicOrderDefinition &def = cod.dynamic();
const auto &params = def.order_params();
auto quote_around_theo = get_if<bool>(params, "quote_around_theo");
auto quote_around_market = get_if<bool>(params, "quote_around_market");
auto n_ticks = get_if<uint32_t>(params, "n_ticks");
auto custom_ord = get_if<CustomOrderDefinition>(params,"child_order");
if ( !n_ticks.has_value() || !quote_around_theo.has_value() || !quote_around_market.has_value() || quote_around_theo.value() == quote_around_market.value()){
LOG(OrderMan, Info,
"QuotingAlgo::convert_params failed n_ticks:{}, quote_around_theo:{}, quote_around_market:{}",
n_ticks.has_value(), quote_around_theo.has_value(), quote_around_market.has_value());
return false;
}
auto key = SecurityKey(serv.security().id());
auto side = serv.definition().side();
ChildTemplate *child_template =
_get_child_template(serv, custom_ord, key , side);
out_p = std::make_unique<QuotingAlgoParams>(
key,
serv.security().glbx().security_desc(),
n_ticks.value(),
quote_around_market.value(),
quote_around_theo.value(),
child_template,
side
);
return true;
}
// This is called outside of the latency sensitive path
// once. Expensive initialization should be done here, rather than
// in do_launch()
OrderOpResult QuotingAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
try {
auto &def = serv.definition();
me->servg = &serv;
me->self_key = serv.order_key();
LOG(OrderMan, Info, "QuotingAlgo::store_and_publish_if_complete rec: {}",
utils::pb::to_string(serv.definition()));
// Validate our parameters, returning an error if we find a
// problem. If an error is returned here the order will be
// abandoned.
if (def.has_custom_def() && def.custom_def().has_dynamic() &&
_convert_params(serv, def.custom_def(), me->algo_params)) {
me->tick_size = serv.security().contract_spec().low_tick();
me->self_def = serv.definition();
me->self_security_key = serv.security().id();
return {};
}
// diagnostics
LOG(OrderMan, Info,
"QuotingAlgo::do_before_launch Has custom: {}, has dynamic: {}",
def.has_custom_def(), def.custom_def().has_dynamic());
return {
OrderError::ORDER_ERROR_INVALID_PARAMETER,
std::string("Parameters are not well formed")};
} catch (...) {
bts_assert(false);
}
}
// Called in the latency sensitive path, once, when the algo is first launched.
void QuotingAlgo::do_launch(IAlgoServices &algo_services) {
// Cache our algo services
me->algo = &algo_services;
// Subscribe to market data
me->algo->subscribe_book(me->algo_params->security_key_id);
if(me->algo_params->quote_around_theo){
me->algo->subscribe_theos();
}
}
void QuotingAlgo::do_launch_attached( // not needed right now
IAlgoServices &algo_services,
OrderKey,
ChildState) {
me->algo = &algo_services;
_cxl_and_end(true, "QuotingAlgo can not be attached to working orders");
}
void QuotingAlgo::on_theos(const analytics::ITheoCalculator *theo_calculator){
if(theo_calculator==NULL){
return;
}
me->theo_calc = theo_calculator;
if (me->subbed_to_driver) {
const md::PriceSize &driver_bid = me->driver_book->bid().at(0);
const md::PriceSize &driver_ask = me->driver_book->ask().at(0);
double driver = (driver_bid.price() + driver_ask.price())/2;
bts::api::orderman::PrecomputedTheoPoint result;
if(!theo_calculator->get_theo(me->algo_params->security_key_id.value(),driver, result)){
LOG(OrderMan, Error, "QuotingAlgo::on_theos CANT GET THEOS");
return;
}
me->current_mid = result.theo();
quote(me->current_mid.value());
return;
}
auto driver_sec = me->theo_calc->get_driver_sec(me->algo_params->security_key_id.value());
if (driver_sec.has_value()) {
LOG(OrderMan, Info,
"QuotingAlgo::on_theos order_key: {} subscribing to driver sec_key: {}",
me->algo->order_key(), driver_sec.value());
me->subbed_to_driver = true;
me->driver_sec_key = SecurityKey(driver_sec.value());
me->algo->subscribe_book(SecurityKey(me->driver_sec_key));
}
}
// Called when the book for the specified security has changed
void QuotingAlgo::on_book(SecurityKey sec_key, Book const &book) {
if (sec_key == me->driver_sec_key) {
me->driver_book = book;
return;
}
if (sec_key != me->algo_params->security_key_id)
return;
me->sec_book = book;
if(!me->algo_params->quote_around_market)
return;
const md::PriceSize &bid = me->sec_book->bid().at(0);
const md::PriceSize &ask = me->sec_book->ask().at(0);
double mid_price=(bid.price() + ask.price())/2;
quote(mid_price);
}
// Round the price to the nearest tick
double normalizePrice(double price, double tickSize) {
double roundedPrice = round(price / tickSize) * tickSize;
return roundedPrice;
}
void QuotingAlgo::quote(double price){
double normalized= normalizePrice(price,me->tick_size);
double offset = me->algo_params->n_ticks*me->tick_size;
double price_with_offset = normalized + offset*(me->algo_params->side==api::Side::SIDE_BUY?-1:1);
_fire_orders(price_with_offset);
}
void QuotingAlgo::_fire_orders(double price){
if(price == me->child_order_price.value()){
return;//avoid unnecessary updates
}
me->child_order_price = Price(price);
if(me->child_order_key==null_order_key())
{
me->child_order_key =
_algo().launch_child(*me->algo_params->child_template, bts::Price(price), bts::Quantity(me->algo->quantity()));
return;
}
Update update;
update.set_price(price);
update.set_quantity(me->algo->quantity().value());
_algo().update_child(me->child_order_key,update);
}
void QuotingAlgo::on_ticker(SecurityKey, TradeVector const &) {}
void QuotingAlgo::report_state() {}
void QuotingAlgo::do_update(
api::orderman::Update const &upd) { // not needed right now
LOG(OrderMan, Warn,
"QuotingAlgo::do_update order: {} update not allowed, please "
"cxl and re-enter {} upd key: {}",
me->algo->order_key().value(), upd.order_key());
_cxl_and_end(true, " update not allowed");
}
void QuotingAlgo::on_child_change(OrderKey key, const ChildState &state) {
if(me->child_order_key==key){
me->algo->update_fill_state(state.total_fills, state.last_price);
if(state.status != bts::orderman::ChildStatus::WORKING){
me->algo->complete();
}
}
}
void QuotingAlgo::on_status(SecurityKey key, co2::TradingStatus s) {
LOG(OrderMan, Info, "QuotingAlgo::on_status key: {} status: {}", key.value(),
static_cast<int>(s));
}
// updates states every 500 ms, state visible if algo tree checked on front
// end
void QuotingAlgo::on_timer() {}
void QuotingAlgo::_cxl_and_end(bool is_error, const std::string &err_msg) {
if (is_error)
me->algo->terminate_error(err_msg);
else
me->algo->complete();
}
} // namespace orderman
} // namespace bts
@@ -0,0 +1,39 @@
#pragma once
#include "lib/domain_model/Security.pb.h"
#include "lib/orderman/orderman_utils/Book.h"
#include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
class QuotingAlgo : public IAlgoOrder {
public:
QuotingAlgo();
~QuotingAlgo();
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_child_change(OrderKey key, const ChildState &state) override;
void on_status(SecurityKey, co2::TradingStatus) override;
void on_timer() override;
void on_book(SecurityKey, Book const &) override;
void on_theos(const analytics::ITheoCalculator *theo_calculator) override;
void on_ticker(SecurityKey, TradeVector const &) override;
void report_state();
void run_algo();
void quote(double price);
private:
struct PImpl;
enum class State;
std::unique_ptr<PImpl> me;
private:
IAlgoServices &_algo() const;
void _fire_orders(double price);
void _cxl_and_end(bool is_error, const std::string &err_msg);
};
} // namespace orderman
} // namespace bts
@@ -2,13 +2,13 @@
#include "DormanAlgo.h"
#include "OnSignalAlgo.h"
#include "PyramidAlgo.h"
#include "QuotingAlgo.h"
#include "SniperAlgo.h"
#include "TASAlgo.h"
#include "TASCounterAlgo.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/config/config.h"
#include "lib/config/protos/toggles.pb.h"
#include "lib/logging/logging.h"
#include "lib/orderman/orderman_utils/orderman_utils.h"
#include "lib/orderman/orders/algo/algo_orders/LimitCoverAlgo.h"
#include "lib/orderman/orders/algo/algo_orders/TaskingEyeAlgo.h"
@@ -16,6 +16,7 @@
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverAdapter.h"
#include "lib/orderman/orders/algo/algo_orders/tests/ExecutionTesterAlgo.h"
#include "lib/plugin/SparkServices.h"
#include <vector>
using namespace bts;
using namespace api::orderman;
@@ -232,6 +233,18 @@ extern "C" void init_limit_cover(bts::SparkServices &srv) {
lc_desc, []() { return std::make_unique<orderman::LimitCoverAlgo>(); });
}
extern "C" void init_quoting_algo_plugin(bts::SparkServices &srv) {
DynamicOrderDescriptor quoting_algo;
quoting_algo.set_dynamic_order_type("QuotingAlgo");
*quoting_algo.add_param_desc() = param_uint32("n_ticks");
*quoting_algo.add_param_desc() = param_bool("quote_around_theo");
*quoting_algo.add_param_desc() = param_bool("quote_around_market");
*quoting_algo.add_param_desc() = param_order("child_order");
srv.register_dynamic_algo_order(
quoting_algo, []() { return std::make_unique<orderman::QuotingAlgo>(); });
}
extern "C" void init_on_signal_plugin(bts::SparkServices &srv) {
DynamicOrderDescriptor on_signal;
on_signal.set_dynamic_order_type("OnSignal");
@@ -360,6 +373,7 @@ extern "C" void init_spark_plugin(bts::SparkServices &srv) {
// init_sniper_plugin(srv);
init_task_eye_plugin(srv);
init_vol_algo_plugin(srv);
// init_quoting_algo_plugin(srv);
}
} // namespace dorman
@@ -30,7 +30,7 @@ struct ContractMarketData {
double running_sum = 0;
Book book;
double estimated_settle = 0.0;
void on_trade(const TickerUpdateEvent::Trade &t) {
void on_trade(const Trade &t) {
tot_vol += t.quantity.value();
running_sum += t.price.value() * t.quantity.value();
}
@@ -92,7 +92,7 @@ double round_to_nearest(double price, double tick_size);
long long t1_minus_t2_usecs(const Timestamp &, const Timestamp &);
long long t_minus_now_usecs(const google::protobuf::Timestamp &);
bool trade_within_time_win(
const Timestamp &, const Timestamp &, const TickerUpdateEvent::Trade &t);
const Timestamp &, const Timestamp &, const Trade &t);
bool now_within_time_win(const Timestamp &, const Timestamp &);
TASCounterAlgo::TASCounterAlgo() : me(new PImpl()) {}
@@ -458,9 +458,7 @@ bool now_within_time_win(const Timestamp &beg, const Timestamp &end) {
return within_time_win(beg, end, util::TimeUtil::GetCurrentTime());
}
bool trade_within_time_win(
const Timestamp &beg,
const Timestamp &end,
const TickerUpdateEvent::Trade &t) {
const Timestamp &beg, const Timestamp &end, const Trade &t) {
auto ts = to_pf_ts(t.ts);
return within_time_win(beg, end, ts);
}
@@ -508,7 +508,7 @@ TEST_CASE("Dorman profit-orders", "[algo][dorman]") {
// "[algo][dorman][!hide]") { Idea for future test
// }
TickerUpdateEvent::Trade mkTrade(Price p, Quantity q) {
Trade mkTrade(Price p, Quantity q) {
return {
Timer::now(), p, q, Quantity{q.value() + 9999},
co2::AGGRESSOR_SIDE_NONE};
+10 -9
View File
@@ -1,17 +1,17 @@
#pragma once
#include "lib/api/services/OrderService.pb.h"
#include <functional>
#include <memory>
#include <string>
namespace bts {
namespace orderman {
namespace bts::orderman {
class Order;
class IAlgoOrder;
class DynamicOrderRegistry;
} // namespace orderman
} // namespace bts::orderman
namespace bts {
enum class AlgoRegistrationResult { SUCCESS, ERROR };
@@ -21,15 +21,16 @@ class SparkServices {
~SparkServices();
void register_dynamic_order(
api::orderman::DynamicOrderDescriptor const &desc,
std::function<std::unique_ptr<orderman::Order>()> const &builder);
const api::orderman::DynamicOrderDescriptor &desc,
const std::function<std::unique_ptr<orderman::Order>()> &builder);
AlgoRegistrationResult register_dynamic_algo_order(
api::orderman::DynamicOrderDescriptor const &desc,
std::function<std::unique_ptr<orderman::IAlgoOrder>()> const &builder);
const api::orderman::DynamicOrderDescriptor &desc,
const std::function<std::unique_ptr<orderman::IAlgoOrder>()> &builder);
private:
struct PImpl;
std::unique_ptr<PImpl> me;
};
} // namespace bts
+6 -3
View File
@@ -2,7 +2,10 @@
namespace bts {
class SparkServices;
namespace plugin {
void init_plugins(SparkServices &);
}
} // namespace bts
namespace bts::plugin {
void init_plugins(SparkServices &);
} // namespace bts::plugin
+2
View File
@@ -1,4 +1,6 @@
#pragma once
#include <cstdint>
#include <fstream>
#include <iostream>
#include <iterator>
+4 -3
View File
@@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
@@ -21,16 +22,16 @@ using FilePicker =
void begin_sftp_download(
const std::string host,
uint32_t port,
std::uint32_t port,
const std::string &user,
const std::string &password,
uint32_t time_out_secs,
std::uint32_t time_out_secs,
const FilePicker &picker,
std::function<void(DownloadResult const &)> const);
DownloadResult sftp_download(
const std::string host,
uint32_t port,
std::uint32_t port,
const std::string &user,
const std::string &password,
const FilePicker &on_choose);
@@ -1,6 +1,6 @@
#include "spark/lib/logging/logging.h"
#include "spark/lib/md/MDProducer.h"
#include "spark/lib/md/cme/MDP3Utils.h"
#include "server/sparkd/md/cme/MDP3Utils.h"
#include "spark/lib/tracing/bts_trace.h"
#include "spark/lib/utils/PBUtils.h"
#include "spark/lib/utils/bts_chrono.h"
+2 -2
View File
@@ -9,8 +9,6 @@
#include "lib/md/MBOServiceServer.h"
#include "lib/md/MDClient.h"
#include "lib/md/SyntheticSecurityService.h"
#include "lib/md/recording/MDRecorder.h"
#include "lib/md/recording/MDReplayer.h"
#include "lib/md/relay/QueryServer.h"
#include "lib/md/service/MarketDataServiceServer.h"
#include "lib/metrics/metrics.h"
@@ -34,6 +32,7 @@
#include "server/sparkd/http/WebServer.h"
#include "server/sparkd/md/MarketDataAuxiliaryServiceServer.h"
#include "server/sparkd/md/platforms/mdp3_onixs/TradeSummarySignalDispatcher.h"
#include "server/sparkd/md/recording/MDRecorder.h"
#include "server/sparkd/position/MarginServiceServer.h"
#include "server/sparkd/position/PositionServiceServer.h"
#include "server/sparkd/position/import/BasePositionServiceServer.h"
@@ -43,6 +42,7 @@
#include "server/sparkd/support/ShellLatencyServer.h"
#include "server/sparkd/user/AdminServiceServer.h"
#include "server/sparkd/user/AuthServiceServer.h"
#include <thread>
namespace bts::cmds {
@@ -1,9 +1,8 @@
#ifdef SPARK_ENABLE_CCAPI
#include "LiveLadderDemo.h"
#include "lib/api/md/MarketDataConfig.pb.h"
#include "lib/md/MarketDataGateway.h"
#include "lib/md/MarketDataRegistration.h"
#include "lib/md/listeners/LiveLadderPrinter.h"
#include "server/sparkd/md/MarketDataGateway.h"
#include "server/sparkd/md/MarketDataRegistration.h"
#include "server/sparkd/md/listeners/LiveLadderPrinter.h"
#include <vector>
namespace bts::cmds {
@@ -24,4 +23,3 @@ LiveLadderDemo::LiveLadderDemo(const api::md::MarketDataConfig &) {
}
} // namespace bts::cmds
#endif
+1 -1
View File
@@ -1,5 +1,5 @@
#include "Channels.h"
#include "lib/md/cme/MDP3Utils.h"
#include "server/sparkd/md/cme/MDP3Utils.h"
namespace bts::cmds::md::cmds {
+1 -1
View File
@@ -3,13 +3,13 @@
#include "lib/config/protos/market_data.pb.h"
#include "lib/domain/SecurityCriteria.h"
#include "lib/logging/logging.h"
#include "lib/md/MarketDataGateway.h"
#include "lib/md/SymbolLoader.h"
#include "lib/md/UserSymbol.h"
#include "lib/md/relay/RelayRecorder.h"
#include "lib/os/os.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/thread_manager.h"
#include "server/sparkd/md/MarketDataGateway.h"
#include "server/sparkd/md/platforms/IMDPlatform.h"
#include "server/sparkd/md/platforms/ice_onixs/ICEMain.h"
#include "server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h"
@@ -705,4 +705,4 @@ const api::Security *EMExecutionGateway::_get_security(
}
} // namespace orderman
} // namespace bts
} // namespace bts
@@ -1,12 +1,12 @@
#pragma once
#include "IExecutionGateway.h"
#include "lib/api/services/AdminService.pb.h"
#include "lib/orderman/CancelOrderMsg.h"
#include "lib/orderman/CancelReplaceOrderMsg.h"
#include "lib/orderman/CreateOrderMsg.h"
#include "lib/orderman/RFQMsg.h"
#include "server/sparkd/md/platforms/emumba/IEMOrderMuxer.h"
#include "lib/orderman/gateways/IExecutionGateway.h"
#include <bits/stdint-uintn.h>
#include <cstdint>
+7
View File
@@ -1,3 +1,4 @@
#include "server/sparkd/md/cme/CMEActivSession.h"
#include "lib/testing/catch_test_runner.h"
#include "lib/utils/init_spark_lib.h"
#include "server/sparkd/cmds/AdminReport.h"
@@ -10,6 +11,9 @@
#include "server/sparkd/cmds/crypto/CryptoCmd.h"
#include <CLI11/CLI11.hpp>
#include <iostream>
#include <string>
using namespace bts;
int main(int argc, char **argv) {
@@ -30,6 +34,9 @@ int main(int argc, char **argv) {
app.add_subcommand(CLI::App_p(new cmds::Run()));
app.add_subcommand(CLI::App_p(new cmds::KeyCmd()));
app.add_subcommand(CLI::App_p(new cmds::Web()));
app.add_subcommand(
"test", "Run unit test(s)"); // so this shows up in help
CLI11_PARSE(app, argc, argv);
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,3 @@
#if !defined(_WIN32) //&& defined(SPARK_ENABLE_CCAPI)
#include "MarketDataGateway.h"
#include "lib/api/md/EmumbaDeribitMarketDataConfig.pb.h"
#include "lib/api/md/MarketDataConfig.pb.h"
@@ -7,39 +6,33 @@
#include "lib/collections/Contains.h"
#include "lib/config/protos/system_config.pb.h"
#include "lib/core/FMT.h"
#include "lib/domain/FutureSecurity.h"
#include "lib/domain/OptionSecurity.h"
#include "lib/domain/Security.h"
#include "lib/domain/SecurityID.h"
#include "lib/domain/SpotSecurity.h"
#include "lib/domain_model/Security.pb.h"
#include "lib/io/ProtoPrinter.h"
#include "lib/logging/logging.h"
#include "lib/md/GeneralMarketDataListener.h"
#include "lib/md/GeneralSecurityIndex.h"
#include "lib/md/IMarketDataListener.h"
#include "lib/md/MDProducer.h"
#include "lib/md/MarketDataListenerID.h"
#include "lib/md/MarketDataRegistration.h"
#include "lib/md/MarketDataRegistrationError.h"
#include "lib/md/MarketDataRegistrationFactory.h"
#include "lib/md/MarketDataSubscription.h"
#include "lib/md/binance/BinanceSubscription.h"
#include "lib/md/binance_us/BinanceUsSubscription.h"
#include "lib/md/binance_usds_futures/BinanceUsdsFuturesSubscription.h"
#include "lib/md/coinbase/CoinbaseSubscription.h"
#include "lib/md/deribit/DeribitSubscription.h"
#include "lib/md/ftx/FTXSubscription.h"
#include "lib/md/okx/OkxSubscription.h"
#include "lib/md/recording/MDReplayer.h"
#include "lib/md/vendor/emumba/EmumbaDeribitData.h"
#include "lib/persistence/IDGenerator.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/bts_fatal.h"
#include "lib/utils/thread_manager.h"
#include "server/sparkd/md/platforms/emumba/EmumbaMain.h"
#include "server/sparkd/md/MarketDataListenerID.h"
#include "server/sparkd/md/MarketDataRegistration.h"
#include "server/sparkd/md/MarketDataRegistrationError.h"
#include "server/sparkd/md/MarketDataRegistrationFactory.h"
#include "server/sparkd/md/MarketDataSubscription.h"
#include "server/sparkd/md/binance/BinanceSubscription.h"
#include "server/sparkd/md/binance_us/BinanceUsSubscription.h"
#include "server/sparkd/md/binance_usds_futures/BinanceUsdsFuturesSubscription.h"
#include "server/sparkd/md/coinbase/CoinbaseSubscription.h"
#include "server/sparkd/md/deribit/DeribitSubscription.h"
#include "server/sparkd/md/emumba/EmumbaDeribitData.h"
#include "server/sparkd/md/ftx/FTXSubscription.h"
#include "server/sparkd/md/okx/OkxSubscription.h"
#include "server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h"
#include "server/sparkd/md/recording/MDReplayer.h"
#include "server/sparkd/md/cme/CMEActivSession.h"
#include <bits/stdint-uintn.h>
#include <boost/filesystem.hpp>
@@ -47,7 +40,6 @@
#include <memory>
#include <optional>
#include <unordered_map>
#include <utility>
#include <vector>
namespace bts::md {
@@ -135,15 +127,19 @@ void MarketDataGateway::run() {
}
LOG(md, Info, "MarketDataGateway::run starting t_id:{} t_name:{}",
os::get_thread_id(), os::get_thread_name());
_start_cc(me_->md_config);
if (!me_->md_config.emumba_deribit().empty()) {
bts_assert(me_->md_config.emumba_deribit().size() == 1);
_start_emumba_deribit(me_->md_config.emumba_deribit().at(0));
}
if (!me_->md_config.glbx().empty()) {
bts_assert(me_->md_config.glbx().size() == 1);
_start_onix_glbx(me_->md_config.glbx().at(0));
_start_glbx(me_->md_config.glbx().at(0));
}
_start_replays(me_->md_config);
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -239,7 +235,26 @@ void MarketDataGateway::unsubscribe(MarketDataListenerID id) {
LOG(md, Info, "Unsubscribing listener {}", id);
me_->listeners.erase(id);
}
/// glbx //////////////////////////////////////////////////////////////////////
/// onix active //////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static void run_active(bts::api::md::GlbxMarketDataConfig cfg, persistence::IDGenerator *sec_id_store) {
LOG(md, Info, "MarketDataGateway::run_active t_id:{} t_name", os::get_thread_id(),
os::get_thread_name());
bts::md::GeneralSecurityIndex sec_index;
bts::md::MDProducer md_producer;
std::shared_ptr<GeneralMarketDataListener> listener =std::make_shared<bts::md::GeneralMarketDataListener>(sec_index,md_producer,bts::api::VENUE_GLBX);
bts::md::cme::CMEActivSession::run(cfg.glbx_active_platform().host(), cfg.glbx_active_platform().user(), cfg.glbx_active_platform().password(), listener, sec_id_store);
// Timers
// auto &disp = utils::Dispatcher::current();
// utils::DispatcherTimer heartbeat_timer(disp, 250, [&]() {
// for (auto &p : platforms)
// p->send_heartbeats();
// });
// heartbeat_timer.start();
// disp.run();
}
/// onix glbx //////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static void run_onixs() {
LOG(md, Info, "MarketDataGateway::run_onixs t_id:{} t_name",
@@ -268,8 +283,29 @@ static void run_onixs() {
disp.run();
}
void MarketDataGateway::_start_onix_glbx(
bts::api::md::GlbxMarketDataConfig cfg) {
void MarketDataGateway::_start_active_glbx(bts::api::md::GlbxMarketDataConfig cfg){
LOG(md, Info,
"MarketDataGateway::_start_active_glbx starting t_id:{} t_name:{}. "
"cfg:\n{}",
os::get_thread_id(), os::get_thread_name(), cfg.DebugString());
LOG(md, Info,
"MarketDataGateway::_start_active_glbx");
bts::md::run_active(cfg, &me_->sec_id_store);
}
void MarketDataGateway::_start_glbx( bts::api::md::GlbxMarketDataConfig cfg){
LOG(md, Info,
"MarketDataGateway::_start_glbx starting t_id:{} t_name:{}. "
"cfg:\n{}",
os::get_thread_id(), os::get_thread_name(), cfg.DebugString());
if(cfg.has_glbx_active_platform()){
_start_active_glbx(cfg);
}
else if(cfg.has_glbx_onix_platform()){
_start_onix_glbx(cfg);
}
}
void MarketDataGateway::_start_onix_glbx( bts::api::md::GlbxMarketDataConfig cfg){
LOG(md, Info,
"MarketDataGateway::_start_onix_glbx starting t_id:{} t_name:{}. "
"cfg:\n{}",
@@ -351,4 +387,3 @@ void MarketDataGateway::_start_replays(
}
} // namespace bts::md
#endif
@@ -1,8 +1,8 @@
#pragma once
#if !defined(_WIN32) && defined(SPARK_ENABLE_CCAPI)
#include "lib/api/md/GlbxMarketDataConfig.pb.h"
#include "MarketDataListenerID.h"
#include "server/sparkd/md/MarketDataListenerID.h"
#include <memory>
namespace bts::api::md {
@@ -35,8 +35,11 @@ class MarketDataGateway {
bts::api::md::EmumbaDeribitMarketDataConfig cfg);
static void _start_onix_glbx(
bts::api::md::GlbxMarketDataConfig cfg);
void _start_glbx(
bts::api::md::GlbxMarketDataConfig cfg);
void _start_active_glbx(
bts::api::md::GlbxMarketDataConfig cfg);
void _start_replays(const bts::api::md::MarketDataConfig &cfg);
};
} // namespace bts::md
#endif
@@ -1,12 +1,12 @@
#pragma once
#ifndef _WIN32
#include "IMarketDataListener.h"
#include "lib/md/IMarketDataListener.h"
#include "lib/domain/Book.h"
#include "lib/domain/Security.h"
#include "lib/domain/SecurityMarketDataStatistics.h"
#include "lib/domain/SecurityState.h"
#include "lib/domain/TradeRecord.h"
#include "lib/md/MarketDataError.h"
#include "server/sparkd/md/MarketDataError.h"
namespace bts::md {
@@ -29,24 +29,23 @@ class MarketDataListener : public IMarketDataListener {
// Definition
//////////////////////////////////////////////////
void MarketDataListener::on_securities(
inline void MarketDataListener::on_securities(
const std::vector<const domain::Security *> & /*complete_security_list*/) {}
void MarketDataListener::on_security_state(
inline void MarketDataListener::on_security_state(
const domain::SecurityState & /*security_state*/) {}
void MarketDataListener::on_security_statistics(
inline void MarketDataListener::on_security_statistics(
const domain::SecurityMarketDataStatistics & /*security_stats*/) {}
void MarketDataListener::on_book(const domain::Book & /*book*/) {}
inline void MarketDataListener::on_book(const domain::Book & /*book*/) {}
void MarketDataListener::on_trades(
inline void MarketDataListener::on_trades(
const std::vector<const domain::TradeRecord *> & /*trades*/) {}
void MarketDataListener::on_rfqs(
inline void MarketDataListener::on_rfqs(
const std::vector<const domain::RFQ *> & /*rfqs*/) {}
void MarketDataListener::on_error(const MarketDataError & /*error*/) {}
inline void MarketDataListener::on_error(const MarketDataError & /*error*/) {}
} // namespace bts::md
#endif
@@ -1,14 +1,12 @@
#if !defined(_WIN32) && defined(SPARK_ENABLE_CCAPI)
#include "MarketDataSubscription.h"
#include "lib/api/md/MarketDataConfig.pb.h"
#include "lib/md/binance/BinanceSubscription.h"
#include "lib/md/binance_us/BinanceUsSubscription.h"
#include "lib/md/binance_usds_futures/BinanceUsdsFuturesSubscription.h"
#include "lib/md/coinbase/CoinbaseSubscription.h"
#include "lib/md/deribit/DeribitSubscription.h"
#include "lib/md/ftx/FTXSubscription.h"
#include "lib/md/okx/OkxSubscription.h"
#include <variant>
#include "server/sparkd/md/binance/BinanceSubscription.h"
#include "server/sparkd/md/binance_us/BinanceUsSubscription.h"
#include "server/sparkd/md/binance_usds_futures/BinanceUsdsFuturesSubscription.h"
#include "server/sparkd/md/coinbase/CoinbaseSubscription.h"
#include "server/sparkd/md/deribit/DeribitSubscription.h"
#include "server/sparkd/md/ftx/FTXSubscription.h"
#include "server/sparkd/md/okx/OkxSubscription.h"
#include <vector>
namespace bts::md {
@@ -60,4 +58,3 @@ void MarketDataSubscription::add(okx::OkxSubscription sub) {
}
} // namespace bts::md
#endif
@@ -1,5 +1,5 @@
#pragma once
#if !defined(_WIN32) && defined(SPARK_ENABLE_CCAPI)
#include <memory>
namespace bts::md::binance {
@@ -57,4 +57,3 @@ class MarketDataSubscription {
};
} // namespace bts::md
#endif
@@ -1,10 +1,10 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "lib/core/EnumMacros.h"
#include "lib/core/FMT_IMPL.h"
#include <fmt/format.h>
#include <nlohmann/json.hpp>
#include <ostream>
namespace bts::md::binance {
@@ -23,5 +23,3 @@ ENUM_CLASS_DEF(BinanceContractType, FOR_ALL_VALUES)
} // namespace bts::md::binance
FMT_IMPL(bts::md::binance::BinanceContractType, t, "{}", t.name());
#endif
@@ -1,20 +1,20 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "lib/io/JsonDumper.h"
#include "lib/io/JsonReader.h"
#include "lib/md/binance/BinanceFilterType.h"
#include "lib/md/binance/BinanceIcebergPartsFilter.h"
#include "lib/md/binance/BinanceLotSizeFilter.h"
#include "lib/md/binance/BinanceMarketLotSizeFilter.h"
#include "lib/md/binance/BinanceMaxNumAlgoOrdersFilter.h"
#include "lib/md/binance/BinanceMaxNumOrdersFilter.h"
#include "lib/md/binance/BinanceMaxPositionFilter.h"
#include "lib/md/binance/BinanceMinNotionalFilter.h"
#include "lib/md/binance/BinanceNotionalFilter.h"
#include "lib/md/binance/BinancePercentPriceBySideFilter.h"
#include "lib/md/binance/BinancePercentPriceFilter.h"
#include "lib/md/binance/BinancePriceFilter.h"
#include "lib/md/binance/BinanceTrailingDeltaFilter.h"
#include "server/sparkd/md/binance/BinanceFilterType.h"
#include "server/sparkd/md/binance/BinanceIcebergPartsFilter.h"
#include "server/sparkd/md/binance/BinanceLotSizeFilter.h"
#include "server/sparkd/md/binance/BinanceMarketLotSizeFilter.h"
#include "server/sparkd/md/binance/BinanceMaxNumAlgoOrdersFilter.h"
#include "server/sparkd/md/binance/BinanceMaxNumOrdersFilter.h"
#include "server/sparkd/md/binance/BinanceMaxPositionFilter.h"
#include "server/sparkd/md/binance/BinanceMinNotionalFilter.h"
#include "server/sparkd/md/binance/BinanceNotionalFilter.h"
#include "server/sparkd/md/binance/BinancePercentPriceBySideFilter.h"
#include "server/sparkd/md/binance/BinancePercentPriceFilter.h"
#include "server/sparkd/md/binance/BinancePriceFilter.h"
#include "server/sparkd/md/binance/BinanceTrailingDeltaFilter.h"
#include <nlohmann/json.hpp>
#include <string>
@@ -125,4 +125,3 @@ inline std::ostream &operator<<(
}
} // namespace bts::md::binance
#endif
@@ -1,10 +1,10 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "lib/core/EnumMacros.h"
#include "lib/core/FMT_IMPL.h"
#include <fmt/format.h>
#include <nlohmann/json.hpp>
#include <ostream>
namespace bts::md::binance {
@@ -40,5 +40,3 @@ ENUM_CLASS_DEF(BinanceFilterType, FOR_ALL_VALUES)
} // namespace bts::md::binance
FMT_IMPL(bts::md::binance::BinanceFilterType, t, "{}", t.name());
#endif
@@ -1,9 +1,8 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "lib/io/JsonMacros.h"
#include "lib/io/JsonReader.h"
#include "lib/md/binance/BinanceFilterType.h"
#include "server/sparkd/md/binance/BinanceFilterType.h"
#include <iostream>
#include <nlohmann/json.hpp>
@@ -34,4 +33,3 @@ OSTREAM_JSON_IMPL(BinanceIcebergPartsFilter);
#undef FOR_EACH
} // namespace bts::md::binance
#endif
@@ -1,10 +1,10 @@
#pragma once
#ifdef SPARK_ENABLE_CCAPI
#include "lib/core/EnumMacros.h"
#include "lib/core/FMT_IMPL.h"
#include <fmt/format.h>
#include <nlohmann/json.hpp>
#include <ostream>
namespace bts::md::binance {
@@ -25,5 +25,3 @@ ENUM_CLASS_DEF(BinanceInstrumentStatus, FOR_ALL_VALUES)
} // namespace bts::md::binance
FMT_IMPL(bts::md::binance::BinanceInstrumentStatus, s, "{}", s.name());
#endif
Loaded 100 of 292 files, more files were not shown because too many files have changed in this diff. Show more