Compare commits
50
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a986dacc60 | ||
|
|
5b1bf06feb | ||
|
|
bc6dc44555 | ||
|
|
01e62b9aae | ||
|
|
64f2bd7a88 | ||
|
|
3e32f684e3 | ||
|
|
ff73756a55 | ||
|
|
1e64811d93 | ||
|
|
2488597cf2 | ||
|
|
2d8047ba80 | ||
|
|
3cb602b77e | ||
|
|
51e1005e79 | ||
|
|
ef7ea64973 | ||
|
|
0267c16e5a | ||
|
|
ca40e278ff | ||
|
|
1c5da7ad48 | ||
|
|
9bb3d5c569 | ||
|
|
92ba43bfad | ||
|
|
ddeb5e59f0 | ||
|
|
6b0b3e31cc | ||
|
|
3f6c904ccc | ||
|
|
d433919f1b | ||
|
|
75da9af39d | ||
|
|
22eff44926 | ||
|
|
cd6a052d0c | ||
|
|
3b6cf0b187 | ||
|
|
c170dd7a9e | ||
|
|
154775bc70 | ||
|
|
7f936f3ba9 | ||
|
|
9b30a947a8 | ||
|
|
2ee58d8f99 | ||
|
|
0537b3e9c7 | ||
|
|
8ae940bcbc | ||
|
|
966ccb5f2c | ||
|
|
fae9c3e59e | ||
|
|
cb1dfb257d | ||
|
|
76c3fe86c2 | ||
|
|
7e3b1de70f | ||
|
|
2a07e3cf3c | ||
|
|
6d0249825d | ||
|
|
63810e3966 | ||
|
|
4c013a8886 | ||
|
|
9509818978 | ||
|
|
4fdf3b8b60 | ||
|
|
ee9a58486d | ||
|
|
75d6939c8b | ||
|
|
fb1697b571 | ||
|
|
4080d606d8 | ||
|
|
7655fd47dd | ||
|
|
d029b0fce7 |
No files matched your search
@@ -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"
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -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
@@ -54,6 +54,6 @@ trace_data/
|
||||
/src/Spark.Admin.WebApp/src/bin/
|
||||
**/CMakeDeps.cmake
|
||||
**/CMakeDeps_win.cmake
|
||||
**/.devcontainer
|
||||
.devcontainer/
|
||||
*.Generated.cs
|
||||
*.lst
|
||||
+3
-3
@@ -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"
|
||||
}
|
||||
},
|
||||
|
||||
+1
-1
Submodule externals updated: cae5fd20da...1166db1211.
@@ -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'] } } } }
|
||||
]);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#define ENUM_BASIC_MEMBERS(type) \
|
||||
Value value; \
|
||||
\
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace bts::io::websocket {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
@@ -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 ¤cy, 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 ¤cy, 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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
@@ -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
|
||||
@@ -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());
|
||||
@@ -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
|
||||
@@ -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 ¶ms = 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};
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
#include "Channels.h"
|
||||
#include "lib/md/cme/MDP3Utils.h"
|
||||
#include "server/sparkd/md/cme/MDP3Utils.h"
|
||||
|
||||
namespace bts::cmds::md::cmds {
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
+1
-1
@@ -705,4 +705,4 @@ const api::Security *EMExecutionGateway::_get_security(
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
} // namespace bts
|
||||
+2
-2
@@ -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>
|
||||
|
||||
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+8
-11
@@ -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
|
||||
+2
-4
@@ -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
|
||||
+14
-15
@@ -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
|
||||
+2
-4
@@ -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
-3
@@ -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
|
||||
+2
-4
@@ -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
Reference in new issue
Block a user