Compare commits

..
27 Commits
Author SHA1 Message Date
John Colvin 19fef8492d Update externals to remove useless nugets with filenames that are too long
Too long for Windows to clone externals (at least for the Windows Server
box running jenkins)
2026-08-12 14:05:07 -04:00
arham 7521b01022 bug fixes for api stream 2026-08-09 22:13:09 +00:00
arham d0faa95d7a add: fee adjusted stream 2026-08-09 22:12:56 +00:00
arham 36aa2caa2a fractional quantities spreader working 2026-07-30 05:12:58 +00:00
arham 97e7343110 fix: add add raydium and orca to crypto in spreader 2026-07-26 19:28:21 +00:00
arham 5dccc2e9c3 fix: synth name 2026-07-26 19:27:30 +00:00
arham 365012102e basic create synthetic working 2026-07-22 10:25:41 +00:00
arham c3d14c8e31 api server works in default docker 2026-07-02 04:50:44 +00:00
arham 5cc80db09d config change 2026-07-02 04:50:01 +00:00
arham 12c2deb833 market orders for hedge order in spreader 2026-07-02 04:49:44 +00:00
arham 2222fb8e3e add okx to conf 2026-06-30 23:37:26 +00:00
arham 266fdf93e5 fix null check 2026-06-30 23:36:59 +00:00
arham fd121bd85b emnative order hadles limit orders now 2026-06-19 03:13:13 +00:00
arham 1ffaf7c05b Add: rakurai dex execution 2026-06-19 03:12:52 +00:00
arham 5a87288aaa quantity fix 2026-06-15 18:33:37 +00:00
arham a1d62e552c Add:orca raydoum to risk ui 2026-06-11 13:40:10 +00:00
arham 80656a0890 Fix: orca and raydium show up in the api server 2026-06-08 04:10:49 +00:00
arham 188daf2b84 fix: error in cpp 2026-05-31 23:39:38 +00:00
arham 5908adcb09 add: cpp to dex handler 2026-05-15 13:36:09 +00:00
arham 36cf021b70 Add: libs for rakurai to work 2026-05-15 13:35:32 +00:00
arham d45e0e08a2 fix: compiler error 2026-05-15 13:00:25 +00:00
arham 888e751243 raydium orca working 2026-04-24 06:35:50 +00:00
arham bbe3350b4e Add: raydium and orca to frond end 2026-04-24 02:33:24 -04:00
arham 80ea6ad16e add orca nad radium to benue options 2026-04-23 23:46:14 +00:00
arham 0bc0e821f3 basic rakurai hookup done 2026-04-23 17:07:51 +00:00
arham 1b82d07a0d fix: fixed api rounding issues 2026-04-23 17:07:51 +00:00
arham a83404f20f Add:: added orca and raydium 2026-04-23 17:07:51 +00:00
149 changed files with 4439 additions and 15823 deletions

No files matched your search

+9 -9
View File
@@ -183,10 +183,11 @@
"windows": {
"program": "PubSubServer.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json"
],
"cwd": "build/Debug/bin/net8.0",
"env": {
"DOTNET_ENVIRONMENT": "${input:pubSubServerEnvironment}",
},
"stopAtEntry": false,
"console": "integratedTerminal"
},
@@ -448,15 +449,14 @@
"default": "Local",
},
{
"id": "pubSubServerEnvironment",
"id": "pubSubServerConfig",
"type": "pickString",
"description": "PubSubServer Configuration",
"description": "PubSub Configuration",
"options": [
"Development",
"EchoServer",
"Local",
"PubSubServer",
"SimpleEchoClient",
],
"default": "Development",
"default": "PubSubServer",
},
{
"id": "sdkDemoEnvironment",
+8 -4
View File
@@ -19,6 +19,9 @@ services:
- "${SPARKD__CONFIG_PORT}:${SPARKD__CONFIG_PORT}"
- "${SPARKD__QUICKBUS_PORT}:${SPARKD__QUICKBUS_PORT}"
- "${SPARKD__SIM_FIX_PORT}:${SPARKD__SIM_FIX_PORT}"
- 8008:8008
- 8080:8080
- 9443:9443
entrypoint: ${SPARKD__MAIN_ENTRYPOINT}
deploy:
resources:
@@ -72,6 +75,9 @@ services:
- "${SPARKD__CONFIG_PORT}:${SPARKD__CONFIG_PORT}"
- "${SPARKD__QUICKBUS_PORT}:${SPARKD__QUICKBUS_PORT}"
- "${SPARKD__SIM_FIX_PORT}:${SPARKD__SIM_FIX_PORT}"
- 8008:8008
- 8080:8080
- 9443:9443
entrypoint: ${SPARKD__MAIN_ENTRYPOINT}
deploy:
restart_policy:
@@ -81,8 +87,6 @@ services:
limits:
memory: "${SPARKD__MEMORY:-4gb}"
sparkd-multicast:
environment:
- TZ=America/Chicago
volumes:
- ${SPARKD__DATA_DIR:-sparkd_data}:/home/bts/.local/share/bts/sparkd/data
- ${SPARKD__MD_RECORDING_DIR:-md_recording}:/md_recording
@@ -95,7 +99,7 @@ services:
- CONFIG_DIR=${SPARKD__CONFIG_DIR}
- PYTHON_REQUIREMENTS=${SPARKD__PYTHON_REQUIREMENTS}
- SPARKD_ENTRYPOINT=${SPARKD__SPARKD_ENTRYPOINT}
shm_size: "${SPARKD__SHM_SIZE:-8192mb}"
shm_size: "${SPARKD__SHM_SIZE:-2048mb}"
env_file:
- .env
- ${SPARKD__CONFIG_DIR}/.env
@@ -107,7 +111,7 @@ services:
delay: 3s
resources:
limits:
memory: "${SPARKD__MEMORY:-16gb}"
memory: "${SPARKD__MEMORY:-4gb}"
sparkd-tail:
image: bluetradingsystems/linux_dotnet8
volumes:
@@ -19,10 +19,12 @@ RUN add-apt-repository ppa:ubuntu-toolchain-r/test \
RUN apt-get update \
&& apt-get install -y dotnet8
RUN apt-get update && apt-get install -y gdb
RUN apt-get update && apt-get install -y libpq-dev
RUN apt-get update && apt-get install -y libsodium-dev
RUN apt-get update && apt-get install -y fish
RUN apt-get update && apt-get install -y vim
RUN apt-get update && apt-get install -y less
RUN apt-get update && apt-get install -y vim
RUN locale-gen en_US.UTF-8
@@ -98,7 +98,7 @@ RUN apt-get update && apt-get install -y libpcap0.8-dev
RUN apt-get update && apt-get -y install libzstd-dev
# Rakurai
ARG RAKURAI_DEB_URL="https://api.bitbucket.org/2.0/repositories/bluetradesys/spark/downloads/rakurai-dex-connector_0.0.5_amd64.deb"
ARG RAKURAI_DEB_URL="https://api.bitbucket.org/2.0/repositories/bluetradesys/spark/downloads/rakurai-dex-connector_0.0.8_amd64.deb"
RUN --mount=type=secret,id=bitbucket_access_token \
set -eux \
&& apt-get update \
@@ -15,18 +15,21 @@ static class ArgumentComparer {
}
var obj1 = value1.GetValue(env);
var obj2 = value2.GetValue(env);
if (obj1 is null) {
if (obj2 is null) {
if (isNullLike(obj1)) {
if (isNullLike(obj2)) {
return 0;
}
return -1;
}
if (obj2 is null) {
if (isNullLike(obj2)) {
return 1;
}
return compareObjs(obj1, obj2);
return compareObjs(obj1!, obj2!);
}
static bool isNullLike(object? value) =>
value is null || DBNull.Value.Equals(value);
static int compareObjs(object obj1, object obj2) {
try {
var typ1 = obj1.GetType();
@@ -15,7 +15,7 @@ namespace bts.BlueOrb {
private int _size = 0;
public TimeSeriesStream(ViewStream stream)
: this(stream, Int32.MaxValue) { }
: this(stream, 1000) { }
public TimeSeriesStream(ViewStream stream, int size) {
_stream = stream;
@@ -44,7 +44,7 @@ namespace bts.BlueOrb {
ar.setValue("id", _id++);
_table.add_row(ar);
}
var dp_out = BlueDataUtility.build_dp(_table, myLastValue as DataPacket);
var dp_out = BlueDataUtility.build_dp_inc(_table, myLastValue as DataPacket);
myFireEvent(dp_out);
}
}
@@ -117,6 +117,9 @@ namespace BCG {
addWorker(op.w);
else
removeWorker(op.w);
}
catch (InvalidOperationException ex) {
bts.Log.Gen.error($"ViewStreamDatabase worker collection was modified during enumeration: {ex}");
} finally {
_pending_worker_ops.Clear();
_is_worker_call_in_progress = false;
@@ -1,5 +1,7 @@
namespace bts.BlueOrb {
using System;
using System.Collections.Generic;
using System.IO;
using BCG;
@@ -10,7 +12,14 @@ namespace bts.BlueOrb {
[LiveMarketDataStream]
public class CrossExchangeSymbolMatcherStream : ViewStream {
public CrossExchangeSymbolMatcherStream([MarketDataClientParam] IMarketDataClient mdClient, IWorkScheduler scheduler) {
const string CanonicalSymbolColumn = "canonical_symbol";
readonly CrossExchangeSymbolAliasResolver symbolAliasResolver;
public CrossExchangeSymbolMatcherStream([MarketDataClientParam] IMarketDataClient mdClient, IWorkScheduler scheduler)
: this(mdClient, scheduler, null) { }
public CrossExchangeSymbolMatcherStream([MarketDataClientParam] IMarketDataClient mdClient, IWorkScheduler scheduler, string? symbolAliasConfigFile) {
symbolAliasResolver = CrossExchangeSymbolAliasResolver.Load(ResolveSymbolAliasConfigFile(symbolAliasConfigFile));
ViewStream r;
ViewStream okx_usdc;
ViewStream okx_usdt;
@@ -24,6 +33,9 @@ namespace bts.BlueOrb {
r = new InstrumentsStream(mdClient, scheduler);
r = new DateTimeExpanstionStream(r,
"expiration");
r = new MapStream(r,
_add_canonical_symbol_schema,
_add_canonical_symbol_row);
okx_usdc = new FilterStream(r,
"venue='Okx' and sec~='USDC'");
okx_usdc = new AddColumnsStream(okx_usdc,
@@ -63,7 +75,7 @@ namespace bts.BlueOrb {
r,
"");
r = new FilterStream(r,
"left_venue!=right_venue and left_symbol=right_symbol and left_cur=right_cur and left_sec_type=right_sec_type");
"left_venue!=right_venue and left_canonical_symbol=right_canonical_symbol and left_cur=right_cur and left_sec_type=right_sec_type");
perps = new FilterStream(r,
"left_expiration_year=2100 and right_expiration_year=2100");
perps = new FilterStream(perps,
@@ -78,5 +90,127 @@ namespace bts.BlueOrb {
mySetRealSource(r);
}
static string ResolveSymbolAliasConfigFile(string? symbolAliasConfigFile) {
if (!string.IsNullOrWhiteSpace(symbolAliasConfigFile)) {
return symbolAliasConfigFile;
}
var home = Environment.GetEnvironmentVariable("HOME");
if (!string.IsNullOrWhiteSpace(home)) {
var sparkdConf = Path.Combine(home, ".local", "share", "bts", "sparkd", "conf", "cross_exchange_symbol_aliases.conf");
if (File.Exists(sparkdConf)) {
return sparkdConf;
}
}
var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (!string.IsNullOrWhiteSpace(appData)) {
var appDataConf = Path.Combine(appData, "bts", "conf", "cross_exchange_symbol_aliases.conf");
if (File.Exists(appDataConf)) {
return appDataConf;
}
}
return !string.IsNullOrWhiteSpace(home)
? Path.Combine(home, ".local", "share", "bts", "sparkd", "conf", "cross_exchange_symbol_aliases.conf")
: "cross_exchange_symbol_aliases.conf";
}
IBlueTableSchema _add_canonical_symbol_schema(IBlueTableSchema schema) =>
BlueDataUtility.add_column(schema, CanonicalSymbolColumn, typeof(string));
IBlueRow _add_canonical_symbol_row(IBlueRow row) {
var rv = new MutableBlueRow(_add_canonical_symbol_schema(row.schema), row);
rv.setValue(CanonicalSymbolColumn, symbolAliasResolver.Canonicalize(row.get<string>("symbol")));
return rv;
}
}
public sealed class CrossExchangeSymbolAliasResolver {
static readonly System.Text.RegularExpressions.Regex TokenRegex =
new(@"[A-Za-z0-9]+|[^A-Za-z0-9]+", System.Text.RegularExpressions.RegexOptions.Compiled);
readonly IReadOnlyDictionary<string, string> aliases;
CrossExchangeSymbolAliasResolver(IReadOnlyDictionary<string, string> aliases) =>
this.aliases = aliases;
public static CrossExchangeSymbolAliasResolver Load(string configFilePath) {
if (!File.Exists(configFilePath)) {
Log.Gen.warn($"CrossExchangeSymbolMatcherStream alias config not found: {configFilePath}");
return new CrossExchangeSymbolAliasResolver(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase));
}
var aliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var rawLine in File.ReadLines(configFilePath)) {
var line = StripComments(rawLine).Trim();
if (line.Length == 0) {
continue;
}
if (!TryParseAlias(line, out var alias, out var canonical)) {
Log.Gen.warn($"CrossExchangeSymbolMatcherStream skipping invalid alias line: '{rawLine}'");
continue;
}
aliases[NormalizeToken(alias)] = NormalizeToken(canonical);
}
return new CrossExchangeSymbolAliasResolver(aliases);
}
public string Canonicalize(string? symbol) {
if (string.IsNullOrWhiteSpace(symbol)) {
return string.Empty;
}
var rv = new System.Text.StringBuilder(symbol.Length);
foreach (System.Text.RegularExpressions.Match match in TokenRegex.Matches(symbol)) {
var token = match.Value;
if (IsToken(token)) {
var normalized = NormalizeToken(token);
rv.Append(aliases.TryGetValue(normalized, out var canonical) ? canonical : normalized);
} else {
rv.Append(token);
}
}
return rv.ToString();
}
static bool TryParseAlias(string line, out string alias, out string canonical) {
var match = System.Text.RegularExpressions.Regex.Match(line, @"^(?<alias>.+?)\s*(?:=|:|->)\s*(?<canonical>.+)$");
if (match.Success) {
alias = match.Groups["alias"].Value.Trim();
canonical = match.Groups["canonical"].Value.Trim();
return alias.Length > 0 && canonical.Length > 0;
}
var parts = line.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 2) {
alias = parts[0];
canonical = parts[1];
return true;
}
alias = string.Empty;
canonical = string.Empty;
return false;
}
static string StripComments(string line) {
var commentIdx = line.IndexOf('#');
var slashIdx = line.IndexOf("//", StringComparison.Ordinal);
if (slashIdx >= 0 && (commentIdx < 0 || slashIdx < commentIdx)) {
commentIdx = slashIdx;
}
return commentIdx >= 0 ? line[..commentIdx] : line;
}
static bool IsToken(string token) =>
token.Length > 0 && char.IsLetterOrDigit(token[0]);
static string NormalizeToken(string token) =>
token.Trim().ToUpperInvariant();
}
}
@@ -0,0 +1,31 @@
namespace bts.BlueOrb {
using BCG;
using Bts.DataStreamCore;
using Bts.PubSub;
using Spark.Api.MarketData;
[LiveMarketDataStream]
public class FeeAdjustedCrossExchangeArbDiscoveryStream : ViewStream {
public FeeAdjustedCrossExchangeArbDiscoveryStream(
[MarketDataClientParam] IMarketDataClient mdClient,
IWorkScheduler scheduler) {
ViewStream r = new CrossExchangeArbDiscoveryStream(mdClient, scheduler);
r = new AddColumnsStream(r, "fee:0.0002");
r = new AddColumnsStream(r, "capital_available:100000");
r = new MathStream(r, """
left_leg_cost = left_bid_price*left_bid_qty*left_cpp;
right_leg_cost = right_bid_price*right_bid_qty*right_cpp;
total_cost = left_leg_cost + right_leg_cost;
total_fees = total_cost * fee;
fee_adjusted_arb = arb-total_fees;
possible_arb = fee_adjusted_arb*(Min(capital_available,total_cost)/total_cost);
""");
r = new FilterStream(r, "arb>0");
r = new TimeSeriesStream(r);
r = new ProjectStream(r,
"spread_name, left_sec_name,left_venue,right_sec_name,right_venue,arb, fee_adjusted_arb, timestamp");
mySetRealSource(r);
}
}
}
+11 -1
View File
@@ -17,7 +17,7 @@ type Tab =
| PositionManagementTab
| MarginTab
let venues = [| Venue.BINANCE; Venue.BinanceUs; Venue.BinanceUsdsFutures; Venue.BYBIT; Venue.CBOE; Venue.COINBASE; Venue.DERIBIT; Venue.GATEIO; Venue.OKX; Venue.GLBX; Venue.ICE; Venue.TAIFEX; Venue.TWSE; Venue.SIM; |]
let venues = [| Venue.BINANCE; Venue.BinanceUs; Venue.BinanceUsdsFutures; Venue.BYBIT; Venue.CBOE; Venue.COINBASE; Venue.DERIBIT; Venue.GATEIO; Venue.OKX; Venue.ORCA; Venue.RAYDIUM; Venue.GLBX; Venue.ICE; Venue.TAIFEX; Venue.TWSE; Venue.SIM; |]
let securityTypes = [| SecurityType.SPOT; SecurityType.FUTURE; SecurityType.OPTION; SecurityType.SPREAD; |]
@@ -94,6 +94,16 @@ type OkxExchangeField =
| Origin of CustomerOrFirm
| ClearingAccount of string
type OrcaExchangeField =
| CTI of CTICode
| Origin of CustomerOrFirm
| ClearingAccount of string
type RaydiumExchangeField =
| CTI of CTICode
| Origin of CustomerOrFirm
| ClearingAccount of string
type SIMExchangeField =
| CTI of CTICode
| Origin of CustomerOrFirm
@@ -801,6 +801,8 @@ let venueToString (venue:bts.api.Venue) =
| bts.api.Venue.GATEIO -> "GATEIO"
| bts.api.Venue.FTX -> "FTX"
| bts.api.Venue.OKX -> "OKX"
| bts.api.Venue.ORCA -> "ORCA"
| bts.api.Venue.RAYDIUM -> "RAYDIUM"
| bts.api.Venue.TAIFEX -> "TAIFEX"
| bts.api.Venue.TWSE -> "TWSE"
+22 -4
View File
@@ -37,6 +37,20 @@ protobuf_generate_cpp(
)
add_executable(sparkd ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_PROTO_CXX_SOURCES})
find_package(PkgConfig REQUIRED)
pkg_check_modules(
emumba-deribit-connector-library
REQUIRED
IMPORTED_TARGET
emumba-deribit-connector
)
pkg_check_modules(
rakurai-dex-connector-library
REQUIRED
IMPORTED_TARGET
rakurai-dex-connector
)
# include paths
target_include_directories(
sparkd
@@ -44,11 +58,9 @@ target_include_directories(
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>/include
$<BUILD_INTERFACE:${emumba-deribit-connector-library_INCLUDE_DIRS}>
$<BUILD_INTERFACE:${rakurai-dex-connector-library_INCLUDE_DIRS}>
)
find_package(PkgConfig REQUIRED)
pkg_check_modules(emumba-deribit-connector-library REQUIRED emumba-deribit-connector)
# This dir contains rapidjsonConfig.cmake
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
find_package(rapidjson REQUIRED)
@@ -127,7 +139,8 @@ target_link_libraries(
python_interop
python_interop_module
databento::databento
${emumba-deribit-connector-library_LIBRARIES}
PkgConfig::emumba-deribit-connector-library
PkgConfig::rakurai-dex-connector-library
tbb
quickfix
benchmark
@@ -140,6 +153,11 @@ target_link_libraries(
-static-libstdc++
-pthread)
# Both vendor static connector archives currently export the same global
# `print_books` symbol. Allow that duplicate so sparkd can link until the
# upstream packages make the symbol internal or rename it.
target_link_options(${PROJECT_NAME} PRIVATE -Wl,--allow-multiple-definition)
# Onixs
target_link_libraries(
${PROJECT_NAME}
+3
View File
@@ -40,6 +40,9 @@ ARG SOURCE_DIR
ARG PYTHON_REQUIREMENTS="requirements.txt"
ARG SPARKD_ENTRYPOINT
ENV DOTNET_ENVIRONMENT=Development
RUN dotnet dev-certs https --trust
USER "root"
RUN apt-get update && apt-get install -y liblttng-ust-dev
RUN apt-get update && apt-get install -y libpcap0.8-dev
@@ -0,0 +1,3 @@
SPARKD__CONFIG_DIR=src/app_sparkd/configurations/dex_dev
SPARKD__PYTHON_REQUIREMENTS=src/app_sparkd/configurations/dex_dev/requirements.txt
SPARKD__SHM_SIZE=2gb
@@ -0,0 +1,2 @@
* Connects to raydium and orca for dex
* Execution is SIM
@@ -0,0 +1,22 @@
{
"app_logging": {
"severity": "trace",
"file": false,
"console": true
},
"dex": [
{
"id": "orca_clmm",
"pool_ids": ["Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE", "4HppGTweoGQ8ZZ6UcCgwJKfi5mJD9Dqwy6htCpnbfBLW"
]
}
],
"cluster_mode": false,
"wallet_pub": "HFru3Rc3Rf6UjZtrYPsBSKvmq5uyha2DWefV77rFXU64",
"wallet_pvt": [168,123,234,72,218,209,180,225,42,193,240,65,11,15,105,91,95,255,231,214,188,119,90,45,119,144,177,207,203,245,250,27,241,137,138,138,103,247,64,134,70,212,107,134,75,97,87,167,53,32,169,108,75,126,254,17,213,216,74,14,245,28,231,161],
"md_server_uri_1": "ws://185.191.117.159:8130",
"node_oms_uri_1": "ws://185.191.117.159:8133",
"md_server_uri_2": "ws://189.1.171.199:8130",
"node_oms_uri_2": "ws://189.1.171.199:8133",
"core": 1
}
@@ -0,0 +1,6 @@
{
"account": "DEX_DEV",
"operator_id": "DEX_DEV",
"json_conf_file_name": "config.json",
"default_priority_fee": 1000
}
@@ -0,0 +1,6 @@
{
"account": "DEX_DEV",
"operator_id": "DEX_DEV",
"json_conf_file_name": "config.json",
"default_priority_fee": 1000
}
@@ -0,0 +1,5 @@
numpy==2.2.6
pybit==5.11.0
requests==2.31.0
websocket-client==1.8.0
websockets==15.0.1
@@ -0,0 +1,76 @@
JetFIX {
value {
connection {
name: "orca_1"
venue: VENUE_ORCA
max_order_msgs_per_window: 300
window_duration_msecs: 1000
}
connection {
name: "okx_1"
venue: VENUE_OKX
max_order_msgs_per_window: 300
window_duration_msecs: 1000
}
}
}
PublisherOptions {
value {
network_interface: "0.0.0.0"
heartbeat_ms: 5000
heartbeat_boot_seconds: 60
skip_options: true
hot_initial: 1024
hot_growth: 1024
cold_initial: 8192
cold_growth: 2048
channel_prefix: ""
disable_heartbeat: true
}
}
MarketDataConfig {
value {
dex {
venue: VENUE_ORCA
num_depth_levels: 10
json_conf_file_name: "config.json"
security_criteria {
clauses {
security_types: SECURITY_TYPE_SPOT
symbols: @SymbolLists.list[Binance].symbol
name_regexps: "USDC|USDT$"
}
}
}
okx {
num_depth_levels: 5
url_rest_base: "https://www.okx.com"
url_ws_base: "wss://ws.okx.com:8443"
security_criteria {
clauses {
security_types: SECURITY_TYPE_SPOT
symbols: @SymbolLists.list[Okx].symbol
}
}
is_application_ping_pong_enabled: true
application_ping_interval_ms: 20000
application_pong_timeout_ms: 10000
is_protocol_ping_pong_enabled: true
protocol_ping_interval_ms: 60000
protocol_pong_timeout_ms: 30000
}
dex {
venue: VENUE_RAYDIUM
num_depth_levels: 10
json_conf_file_name: "config.json"
security_criteria {
clauses {
security_types: SECURITY_TYPE_SPOT
symbols: @SymbolLists.list[Binance].symbol
name_regexps: "USDC|USDT$"
}
}
}
}
}
@@ -0,0 +1,76 @@
SymbolLists {
value {
list {
key: "Binance"
symbol: "SOL"
symbol: "USDC"
#symbol: "BNB"
symbol: "BTC"
#symbol: "DOGE"
#symbol: "EIGEN"
symbol: "ETH"
symbol: "XRP"
}
list {
key: "ORCA"
#symbol: "AAVE"
#symbol: "AVAX"
#symbol: "BNB"
symbol: "BTC"
symbol: "USDC"
symbol: "SOL"
symbol: "ETH"
symbol: "XRP"
}
list {
key: "RAYDIUM"
symbol: "SOL"
symbol: "USDC"
symbol: "ETH"
symbol: "BTC"
symbol: "XRP"
}
list {
key: "BinanceUsdsFutures"
symbol: "SOL"
symbol: "BTC"
symbol: "USDC"
symbol: "ETH"
symbol: "XRP"
}
list {
key: "Okx"
symbol: "SOL"
symbol: "USDC"
symbol: "BTC"
symbol: "ETH"
symbol: "XRP"
}
}
}
Symbols {
value {
# venues {
# name: "VENUE_BINANCE"
# spot: @SymbolLists.list[Binance].symbol
# }
#venues {
# name: "VENUE_BINANCE_USDS_FUTURES"
# future: @SymbolLists.list[BinanceUsdsFutures].symbol
#}
venues {
name: "VENUE_ORCA"
spot: @SymbolLists.list[ORCA].symbol
}
# venues {
# name: "VENUE_RAYDIUM"
# spot: @SymbolLists.list[RAYDIUM].symbol
# }
venues {
name: "VENUE_OKX"
spot: @SymbolLists.list[Okx].symbol
# future: @SymbolLists.list[Okx].symbol
}
}
}
+3
View File
@@ -42,7 +42,10 @@ roll_log() {
# Acquire sudo permission once for the script (needed for a few calls)
echo bts | sudo -S -v
data_root="${HOME:-/home/bts}/.local/share/bts/sparkd"
api_server_data_root="${HOME:-/home/bts}/.local/share/bts/spark_api_server"
sudo -n chown -R bts:bts "$data_root"
sudo -n mkdir -p "$api_server_data_root"
sudo -n chown -R bts:bts "$api_server_data_root"
load_env "$data_root/conf/.env.local"
init_timezone "$TZ"
roll_log "$data_root/data/logging.sparkd/log"
@@ -25,9 +25,12 @@ EnumOptionNames<api::Venue> VenueOption::names() {
NAME_PAIR(api::VENUE_TWSE),
NAME_PAIR(api::VENUE_BYBIT),
NAME_PAIR(api::VENUE_GATEIO),
NAME_PAIR(api::VENUE_OKX)};
NAME_PAIR(api::VENUE_OKX),
NAME_PAIR(api::VENUE_ORCA),
NAME_PAIR(api::VENUE_RAYDIUM)};
}
std::string VenueOption::name(api::Venue venue) {
switch (venue) {
case api::VENUE_BTS:
@@ -58,6 +61,10 @@ std::string VenueOption::name(api::Venue venue) {
return "cboe";
case api::VENUE_OKX:
return "okx";
case api::VENUE_RAYDIUM:
return "raydium";
case api::VENUE_ORCA:
return "orca";
case api::VENUE_TAIFEX:
return "taifex";
case api::VENUE_TWSE:
@@ -4,6 +4,7 @@
#include "binance/spots/spots_gateway.h"
#include "binance/usds_futures/usds_futures_gateway.h"
#include "okx_execution/okx_execution_gateway.h"
#include "sparkd/execution/rakurai/rakurai_execution_gateway.h"
#include "onixs_fix/OnixFIXAdapter.h"
#include "python_interop/plugin_manager.h"
#include "spark/api/domain/Venue.h"
@@ -109,8 +110,7 @@ struct ExecutionGatewayProvider::PImpl {
return make_gw<ICEExecutionGateway>(conn, conn, *l, o);
case api::VENUE_GLBX:
if (conn.use_ilink3()) {
return make_gw<ilink3::GLBXiLink3ExecutionGateway>(
conn, conn, *l, o);
make_gw<ilink3::GLBXiLink3ExecutionGateway>(conn, conn, *l, o);
}
return make_gw<GLBXExecutionGateway>(conn, conn, *l, o);
case api::VENUE_DERIBIT:
@@ -120,6 +120,18 @@ struct ExecutionGatewayProvider::PImpl {
conn, conn.venue(), std::make_unique<OnixFIXAdapter>(o), o);
case api::VENUE_OKX:
return make_gw<OkxExecutionGateway>(conn, o);
case api::VENUE_ORCA:
case api::VENUE_RAYDIUM: {
auto cfg_j = load_config_json(conn.name());
bts_assert_msgf(
cfg_j,
"Failed to load execution gateway config. name: {}",
conn.name());
auto cfg = cfg_j->get<rakurai::GatewayConfig>();
cfg.expand_env();
return make_gw<rakurai::RakuraiExecutionGateway>(
conn, o, std::move(cfg), conn);
}
case api::VENUE_BINANCE: {
auto cfg =
parse_config_json<binance::GatewayConfig>(conn.name());
@@ -0,0 +1,742 @@
#include "sparkd/execution/rakurai/rakurai_execution_gateway.h"
#include "spark/config/config.h"
#include "spark/config/protos/system_config.pb.h"
#include "spark/domain/DomainSecurityUtils.h"
#include "spark/domain/Price.h"
#include "spark/domain/Quantity.h"
#include "spark/domain_model/Security.h"
#include "spark/logging/logging.h"
#include "spark/md/SecurityOracle.h"
#include "spark/orderman/CancelOrderMsg.h"
#include "spark/orderman/CancelReplaceOrderMsg.h"
#include "spark/orderman/CreateOrderMsg.h"
#include "spark/orderman/RFQMsg.h"
#include "spark/orderman/events/NewAdminDataEvent.h"
#include "spark/orderman/events/NewSecuritiesEvent.h"
#include "spark/orderman/orderman_utils/CancelReject.h"
#include "spark/orderman/orderman_utils/ExecutionReport.h"
#include "spark/orderman/orderman_utils/orderman_common.h"
#include "spark/utils/bts_assert.h"
#include "sparkd/rakurai/RakuraiSharedRuntime.h"
#include <boost/filesystem/path.hpp>
#include <cmath>
#include <cstdint>
#include <deque>
#include <limits>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace bts::orderman::rakurai {
namespace {
#define LOG_FN(level, fn, ...) \
LOG(OrderMan, level, "RakuraiExecutionGateway::" fn ". " __VA_ARGS__)
#define WARN_FN(fn, ...) LOG_FN(Warn, fn, __VA_ARGS__)
using ConnectorNewOrder = ::rakurai::dex_connector::order_entry::new_order;
using ConnectorOrderState = ::rakurai::dex_connector::order_entry::order_state;
using ConnectorSide = ::rakurai::dex_connector::order_entry::side;
std::optional<std::string> extract_short_pool_id(const api::Security& security) {
std::string desc;
if (!domain::DomainSecurityUtils::get_security_desc(security, desc)) {
return std::nullopt;
}
const auto pos = desc.rfind('-');
if (pos == std::string::npos || pos + 1 >= desc.size()) {
return std::nullopt;
}
return desc.substr(pos + 1);
}
api::orderman::NativeOrderStatus to_order_status(ConnectorOrderState state) {
using Status = ::rakurai::dex_connector::data_types::txn_status_t;
switch (state.tx_status) {
case Status::accepted:
return api::orderman::NATIVE_ORDER_STATUS_NEW;
case Status::confirmed:
return state.is_pass ? api::orderman::NATIVE_ORDER_STATUS_FILLED
: api::orderman::NATIVE_ORDER_STATUS_REJECTED;
case Status::failed:
return api::orderman::NATIVE_ORDER_STATUS_REJECTED;
case Status::pending:
return api::orderman::NATIVE_ORDER_STATUS_PENDING_NEW;
}
return api::orderman::NATIVE_ORDER_STATUS_UNDEFINED;
}
api::orderman::ExecType to_exec_type(ConnectorOrderState state) {
using Status = ::rakurai::dex_connector::data_types::txn_status_t;
switch (state.tx_status) {
case Status::accepted:
return api::orderman::EXEC_TYPE_NEW;
case Status::confirmed:
return state.is_pass ? api::orderman::EXEC_TYPE_FILL
: api::orderman::EXEC_TYPE_REJECTED;
case Status::failed:
return api::orderman::EXEC_TYPE_REJECTED;
case Status::pending:
return api::orderman::EXEC_TYPE_PENDING_NEW;
}
return api::orderman::EXEC_TYPE_UNDEFINED;
}
std::string txn_status_name(
::rakurai::dex_connector::data_types::txn_status_t status) {
using Status = ::rakurai::dex_connector::data_types::txn_status_t;
switch (status) {
case Status::accepted:
return "accepted";
case Status::confirmed:
return "confirmed";
case Status::failed:
return "failed";
case Status::pending:
return "pending";
}
return "unknown";
}
std::string json_for_log(const nlohmann::json& j) {
try {
return j.dump();
} catch (const std::exception& ex) {
return fmt::format("<json dump failed: {}>", ex.what());
}
}
} // namespace
struct RakuraiExecutionGateway::PImpl {
struct ActiveOrder {
CreateOrderMsg create_msg;
trace::CorID cor_id;
std::array<std::uint8_t, 32> pool_id{};
double quantity_scale = 1.0;
std::uint64_t raw_quantity = 0;
std::uint64_t priority_fee = 0;
std::uint64_t client_id = 0;
std::uint64_t txn_id = 0;
};
struct BufferedReport {
std::shared_ptr<const ExecutionReport> report;
trace::CorID cor_id;
};
explicit PImpl(
md::SecurityOracle,
GatewayConfig cfg,
config::JetFIX_Connection jetfix_connection)
: config(std::move(cfg)),
jetfix_connection(std::make_shared<config::JetFIX_Connection>(
std::move(jetfix_connection))) {
config.expand_env();
venue = this->jetfix_connection->venue();
bts_assert(venue == api::VENUE_ORCA || venue == api::VENUE_RAYDIUM);
LOG_FN(
Info,
"ctor",
"session:{} venue:{} account:{} operator_id:{} json_conf_file_name:{} default_priority_fee:{} connector_config:{}",
this->jetfix_connection->name(),
api::Venue_Name(venue),
config.account,
config.operator_id,
config.json_conf_file_name,
config.default_priority_fee,
json_for_log(config.connector_config));
}
GatewayConfig config;
std::shared_ptr<config::JetFIX_Connection> jetfix_connection;
api::Venue venue = api::VENUE_UNSET;
std::mutex mutex;
std::unordered_map<OrderKey, ActiveOrder> active_orders;
std::unordered_map<std::uint64_t, OrderKey> client_id_to_order_key;
std::deque<OrderKey> pending_client_bindings;
std::deque<BufferedReport> buffered_reports;
std::size_t order_update_subscription_id = 0;
std::size_t status_subscription_id = 0;
bool started = false;
bool connected = false;
OnExeReport exe_report;
OnGwStatusChange gw_status_change;
OnCancelReject cancel_reject;
OnExchangePositionState exchange_position_state;
OnExchangeOpenOrdersState exchange_open_orders;
void start() {
{
std::lock_guard<std::mutex> lock(mutex);
if (started) {
LOG_FN(
Info, "start", "already started. venue:{}",
api::Venue_Name(venue));
return;
}
started = true;
}
LOG_FN(
Info,
"start",
"starting. session:{} venue:{} account:{} operator_id:{} json_conf_file_name:{} default_priority_fee:{}",
jetfix_connection->name(),
api::Venue_Name(venue),
config.account,
config.operator_id,
config.json_conf_file_name,
config.default_priority_fee);
auto& runtime = bts::rakurai::RakuraiSharedRuntime::instance();
order_update_subscription_id =
runtime.add_order_update_subscriber([this](ConnectorOrderState state) {
this->on_order_update(state);
});
status_subscription_id =
runtime.add_status_subscriber([this](bool is_connected_now) {
this->on_status_change(is_connected_now);
});
try {
if (!config.json_conf_file_name.empty()) {
const auto config_path =
boost::filesystem::path{
config::get<config::SystemConfig>().config_path()} /
config.json_conf_file_name;
LOG_FN(
Info,
"start",
"ensuring shared runtime from file. path:{}",
config_path.string());
runtime.ensure_started_from_file(config_path.string());
} else {
LOG_FN(
Info,
"start",
"ensuring shared runtime from inline config. config:{}",
json_for_log(config.connector_config));
runtime.ensure_started_from_json(
config.connector_config, jetfix_connection->name());
}
LOG_FN(
Info,
"start",
"shared runtime start request submitted. order_update_sub_id:{} status_sub_id:{} connected_now:{}",
order_update_subscription_id,
status_subscription_id,
runtime.is_connected());
} catch (...) {
runtime.remove_order_update_subscriber(order_update_subscription_id);
runtime.remove_status_subscriber(status_subscription_id);
order_update_subscription_id = 0;
status_subscription_id = 0;
{
std::lock_guard<std::mutex> lock(mutex);
started = false;
}
throw;
}
}
void stop() {
LOG_FN(
Info,
"stop",
"stopping. order_update_sub_id:{} status_sub_id:{}",
order_update_subscription_id,
status_subscription_id);
auto& runtime = bts::rakurai::RakuraiSharedRuntime::instance();
if (order_update_subscription_id != 0) {
runtime.remove_order_update_subscriber(order_update_subscription_id);
order_update_subscription_id = 0;
}
if (status_subscription_id != 0) {
runtime.remove_status_subscriber(status_subscription_id);
status_subscription_id = 0;
}
}
void on_status_change(bool is_connected_now) {
LOG_FN(
Info,
"on_status_change",
"status update. venue:{} old_connected:{} new_connected:{} active_orders:{} pending_bindings:{} buffered_reports:{}",
api::Venue_Name(venue),
connected,
is_connected_now,
active_orders.size(),
pending_client_bindings.size(),
buffered_reports.size());
{
std::lock_guard<std::mutex> lock(mutex);
connected = is_connected_now;
}
if (gw_status_change) {
gw_status_change(is_connected_now);
}
}
void on_order_update(ConnectorOrderState order_state) {
std::lock_guard<std::mutex> lock(mutex);
LOG_FN(
Info,
"on_order_update",
"incoming order update. client_id:{} txn_id:{} txn_status:{} is_pass:{} active_orders:{} pending_bindings:{} mapped_clients:{}",
order_state.client_id,
order_state.txn_id,
txn_status_name(order_state.tx_status),
order_state.is_pass,
active_orders.size(),
pending_client_bindings.size(),
client_id_to_order_key.size());
auto key_it = client_id_to_order_key.find(order_state.client_id);
if (key_it == client_id_to_order_key.end()) {
if (pending_client_bindings.empty()) {
LOG_FN(
Warn,
"on_order_update",
"dropping order update with unknown client_id:{} because there are no pending client bindings",
order_state.client_id);
return;
}
const auto key = pending_client_bindings.front();
pending_client_bindings.pop_front();
client_id_to_order_key.emplace(order_state.client_id, key);
LOG_FN(
Info,
"on_order_update",
"bound client_id:{} to order_key:{}",
order_state.client_id,
key.value());
key_it = client_id_to_order_key.find(order_state.client_id);
}
auto order_it = active_orders.find(key_it->second);
if (order_it == active_orders.end()) {
LOG_FN(
Warn,
"on_order_update",
"dropping order update for client_id:{} mapped_order_key:{} because active order was not found",
order_state.client_id,
key_it->second.value());
return;
}
auto& active = order_it->second;
active.client_id = order_state.client_id;
active.txn_id = order_state.txn_id;
auto report = build_report_locked(active, order_state);
buffered_reports.push_back({std::move(report), active.cor_id});
LOG_FN(
Info,
"on_order_update",
"buffered execution report. order_key:{} client_id:{} txn_id:{} buffered_reports:{} order_status:{} exec_type:{}",
active.create_msg.key.value(),
active.client_id,
active.txn_id,
buffered_reports.size(),
api::orderman::NativeOrderStatus_Name(
buffered_reports.back().report->order_status),
api::orderman::ExecType_Name(buffered_reports.back().report->exec_type));
const auto order_status = buffered_reports.back().report->order_status;
if (order_status == api::orderman::NATIVE_ORDER_STATUS_FILLED ||
order_status == api::orderman::NATIVE_ORDER_STATUS_REJECTED) {
LOG_FN(
Info,
"on_order_update",
"removing terminal active order. order_key:{} client_id:{} status:{}",
active.create_msg.key.value(),
order_state.client_id,
api::orderman::NativeOrderStatus_Name(order_status));
client_id_to_order_key.erase(order_state.client_id);
active_orders.erase(order_it);
}
}
void send_create(const CreateOrderMsg& msg, trace::CorID cor_id) {
std::lock_guard<std::mutex> lock(mutex);
std::string security_desc = "<null>";
if (msg.security != nullptr) {
std::string desc;
if (domain::DomainSecurityUtils::get_security_desc(*msg.security, desc)) {
security_desc = std::move(desc);
} else {
security_desc = "<unresolved>";
}
}
LOG_FN(
Info,
"send_create",
"create request. order_key:{} venue:{} side:{} qty:{} price:{} tif:{} order_type:{} connected:{} security_present:{} desc:{}",
msg.key.value(),
api::Venue_Name(msg.venue()),
api::Side_Name(msg.side),
msg.quantity.full_value(),
msg.price,
api::orderman::TimeInForce_Name(msg.time_in_force),
api::orderman::NativeOrderType_Name(msg.order_type),
connected,
msg.security != nullptr,
security_desc);
if (msg.security == nullptr) {
enqueue_reject_locked(msg, cor_id, "Rakurai order missing security");
return;
}
if (msg.venue() != venue) {
enqueue_reject_locked(
msg,
cor_id,
fmt::format(
"Rakurai gateway venue mismatch. expected:{} actual:{}",
api::Venue_Name(venue),
api::Venue_Name(msg.venue())));
return;
}
if (msg.order_type != api::orderman::NATIVE_ORDER_TYPE_MARKET) {
enqueue_reject_locked(
msg,
cor_id,
"Rakurai DEX gateway currently supports market orders only");
return;
}
if (!connected) {
enqueue_reject_locked(
msg, cor_id, "Rakurai execution session is not connected");
return;
}
const auto short_pool_id = extract_short_pool_id(*msg.security);
if (!short_pool_id.has_value()) {
enqueue_reject_locked(
msg, cor_id, "Unable to derive Rakurai pool alias from security");
return;
}
const auto metadata =
bts::rakurai::RakuraiSharedRuntime::instance().find_pool_metadata(
venue, *short_pool_id);
if (!metadata.has_value()) {
LOG_FN(
Warn,
"send_create",
"pool metadata missing. venue:{} short_pool_id:{}",
api::Venue_Name(venue),
*short_pool_id);
enqueue_reject_locked(
msg,
cor_id,
"Rakurai pool metadata not available yet; wait for pool definitions");
return;
}
const auto raw_qty_f =
msg.quantity.full_value() * metadata->quantity_scale;
if (!std::isfinite(raw_qty_f) || raw_qty_f <= 0.0) {
enqueue_reject_locked(
msg, cor_id, "Rakurai order quantity must be positive");
return;
}
ConnectorNewOrder order;
order.order_def.pool_id = metadata->security.pool_id;
order.order_def.dex = metadata->security.venue;
order.order_def.order_side =
msg.side == api::SIDE_BUY ? ConnectorSide::buy : ConnectorSide::sell;
order.qty = static_cast<std::uint64_t>(std::llround(raw_qty_f));
order.priority_fee = config.default_priority_fee;
LOG_FN(
Info,
"send_create",
"launching new order. order_key:{} short_pool_id:{} raw_qty:{} priority_fee:{} dex:{} quantity_scale:{}",
msg.key.value(),
*short_pool_id,
order.qty,
order.priority_fee,
static_cast<int>(metadata->security.venue),
metadata->quantity_scale);
try {
bts::rakurai::RakuraiSharedRuntime::instance().launch_new_order(order);
} catch (const std::exception& ex) {
WARN_FN(
"send_create",
"launch_new_order threw. order_key:{} reason:{}",
msg.key.value(),
std::string(ex.what()));
enqueue_reject_locked(msg, cor_id, ex.what());
return;
}
active_orders.emplace(
msg.key,
ActiveOrder{
msg,
cor_id,
metadata->security.pool_id,
metadata->quantity_scale,
order.qty,
order.priority_fee});
pending_client_bindings.push_back(msg.key);
LOG_FN(
Info,
"send_create",
"create request accepted locally. order_key:{} active_orders:{} pending_bindings:{}",
msg.key.value(),
active_orders.size(),
pending_client_bindings.size());
}
void send_cancel_reject(OrderKey key, const std::string& reason) {
WARN_FN(
"send_cancel_reject",
"cancel reject. order_key:{} reason:{}",
key.value(),
reason);
if (cancel_reject) {
cancel_reject({key, CancelRejectReason::Other, reason});
}
}
std::shared_ptr<const ExecutionReport> build_report_locked(
const ActiveOrder& active,
ConnectorOrderState state) const {
auto report = std::make_shared<ExecutionReport>();
report->account_name = account_name_for(active.create_msg);
report->operator_id = operator_id_for(active.create_msg);
report->order_key = active.create_msg.key;
report->msg_seq_num = 0;
report->security = active.create_msg.security;
report->leg_security = nullptr;
report->side = active.create_msg.side;
report->time_in_force = active.create_msg.time_in_force;
report->order_type = active.create_msg.order_type;
report->manual_order_indicator = active.create_msg.manual_order_indicator;
report->exchange = venue;
report->cfg = jetfix_connection.get();
report->exchange_order_id = ExchangeOrderID64(
active.client_id != 0 ? active.client_id : state.txn_id);
report->quantity = active.create_msg.quantity;
report->price = active.create_msg.price;
report->stop_price = active.create_msg.stop_px;
report->max_show = active.create_msg.display_quantity;
report->transact_time =
ExchangeTimeStamp64(static_cast<std::uint64_t>(datetime::unix_nano_now()));
report->exec_type = to_exec_type(state);
report->order_status = to_order_status(state);
report->filled_quantity =
report->order_status == api::orderman::NATIVE_ORDER_STATUS_FILLED
? active.create_msg.quantity
: Quantity(0.0f);
report->last_shares = report->filled_quantity;
report->last_price = active.create_msg.price;
report->avg_price = active.create_msg.price;
report->msg = fmt::format(
"Rakurai txn_status={} txn_id={} pass={}",
txn_status_name(state.tx_status),
state.txn_id,
state.is_pass);
LOG_FN(
Info,
"build_report_locked",
"built report. order_key:{} client_id:{} txn_id:{} exec_type:{} order_status:{} message:{}",
active.create_msg.key.value(),
active.client_id,
state.txn_id,
api::orderman::ExecType_Name(report->exec_type),
api::orderman::NativeOrderStatus_Name(report->order_status),
report->msg);
return report;
}
void enqueue_reject_locked(
const CreateOrderMsg& msg,
trace::CorID cor_id,
const std::string& reason) {
auto report = std::make_shared<ExecutionReport>();
report->account_name = account_name_for(msg);
report->operator_id = operator_id_for(msg);
report->order_key = msg.key;
report->msg_seq_num = 0;
report->security = msg.security;
report->leg_security = nullptr;
report->side = msg.side;
report->time_in_force = msg.time_in_force;
report->order_type = msg.order_type;
report->manual_order_indicator = msg.manual_order_indicator;
report->exchange = venue;
report->cfg = jetfix_connection.get();
report->exchange_order_id = ExchangeOrderID64(0);
report->quantity = msg.quantity;
report->filled_quantity = Quantity(0.0f);
report->last_shares = Quantity(0.0f);
report->price = msg.price;
report->stop_price = msg.stop_px;
report->max_show = msg.display_quantity;
report->last_price = Price(std::numeric_limits<double>::quiet_NaN());
report->avg_price = Price(std::numeric_limits<double>::quiet_NaN());
report->transact_time =
ExchangeTimeStamp64(static_cast<std::uint64_t>(datetime::unix_nano_now()));
report->exec_type = api::orderman::EXEC_TYPE_REJECTED;
report->order_status = api::orderman::NATIVE_ORDER_STATUS_REJECTED;
report->msg = reason;
buffered_reports.push_back({std::move(report), cor_id});
WARN_FN(
"enqueue_reject_locked",
"buffered reject. order_key:{} reason:{} buffered_reports:{}",
msg.key.value(),
reason,
buffered_reports.size());
}
AccountName account_name_for(const CreateOrderMsg& msg) const {
const auto account = msg.routing_record != nullptr &&
!msg.routing_record->account().empty()
? msg.routing_record->account()
: config.account;
AccountName result;
result->copy_from(account);
return result;
}
OperatorID operator_id_for(const CreateOrderMsg& msg) const {
const auto operator_id =
msg.routing_record != nullptr &&
!msg.routing_record->operator_id().empty()
? msg.routing_record->operator_id()
: config.operator_id;
OperatorID result;
orderman::to_array(operator_id, &result.value());
return result;
}
};
RakuraiExecutionGateway::RakuraiExecutionGateway(
md::SecurityOracle sec_oracle,
GatewayConfig config,
config::JetFIX_Connection jetfix_connection)
: me_(std::make_unique<PImpl>(
std::move(sec_oracle),
std::move(config),
std::move(jetfix_connection))) {}
RakuraiExecutionGateway::~RakuraiExecutionGateway() { me_->stop(); }
void RakuraiExecutionGateway::start() { me_->start(); }
void RakuraiExecutionGateway::dispatch_incoming_fix_messages() {
std::deque<PImpl::BufferedReport> reports;
{
std::lock_guard<std::mutex> lock(me_->mutex);
reports.swap(me_->buffered_reports);
}
if (!reports.empty()) {
LOG_FN(
Info,
"dispatch_incoming_fix_messages",
"dispatching buffered reports. count:{} has_callback:{}",
reports.size(),
me_->exe_report != nullptr);
}
if (!me_->exe_report) {
return;
}
for (auto& buffered : reports) {
LOG_FN(
Info,
"dispatch_incoming_fix_messages",
"publishing execution report. order_key:{} exec_type:{} order_status:{} msg:{}",
buffered.report->order_key.value(),
api::orderman::ExecType_Name(buffered.report->exec_type),
api::orderman::NativeOrderStatus_Name(buffered.report->order_status),
buffered.report->msg);
me_->exe_report(std::move(buffered.report), buffered.cor_id);
}
}
void RakuraiExecutionGateway::on_securities(
const mercury::Update<NewSecuritiesEvent>& update) {
LOG_FN(
Info,
"on_securities",
"received securities update. type:{} has_events:{} count:{}",
static_cast<int>(update.event_type),
update.events != nullptr,
update.events != nullptr ? update.events->size() : 0);
}
void RakuraiExecutionGateway::on_admin_data(
const mercury::Update<NewAdminDataEvent>& update) {
LOG_FN(
Info,
"on_admin_data",
"received admin data update. type:{} has_events:{} count:{}",
static_cast<int>(update.event_type),
update.events != nullptr,
update.events != nullptr ? update.events->size() : 0);
}
void RakuraiExecutionGateway::send_fix(const jetfix::OutFIXPtr&, trace::CorID) {
WARN_FN("send_fix", "FIX transport is not supported for Rakurai");
}
void RakuraiExecutionGateway::send_msgs(
std::vector<OrderMsg> const& msgs,
trace::CorID cor_id) {
LOG_FN(
Info,
"send_msgs",
"received outbound message batch. count:{}",
msgs.size());
for (const auto& msg : msgs) {
std::visit(
[&](const auto& order_msg) {
using T = std::decay_t<decltype(order_msg)>;
if constexpr (std::is_same_v<T, CreateOrderMsg>) {
me_->send_create(order_msg, cor_id);
} else if constexpr (std::is_same_v<T, CancelOrderMsg>) {
me_->send_cancel_reject(
order_msg.key,
"Rakurai DEX gateway does not support order cancel");
} else if constexpr (std::is_same_v<T, CancelReplaceOrderMsg>) {
me_->send_cancel_reject(
order_msg.create_fields.key,
"Rakurai DEX gateway does not support order replace");
} else if constexpr (std::is_same_v<T, RFQMsg>) {
WARN_FN("send_msgs", "RFQ is not supported for Rakurai");
}
},
msg);
}
}
OnExeReport& RakuraiExecutionGateway::on_exe_report() { return me_->exe_report; }
OnGwStatusChange& RakuraiExecutionGateway::on_gw_status_change() {
return me_->gw_status_change;
}
OnCancelReject& RakuraiExecutionGateway::on_cancel_reject() {
return me_->cancel_reject;
}
OnExchangePositionState& RakuraiExecutionGateway::on_exchange_position_state() {
return me_->exchange_position_state;
}
OnExchangeOpenOrdersState& RakuraiExecutionGateway::on_exchange_open_orders() {
return me_->exchange_open_orders;
}
} // namespace bts::orderman::rakurai
@@ -0,0 +1,76 @@
#pragma once
#include "spark/io/JsonReader.h"
#include "spark/orderman/gateways/IExecutionGateway.h"
#include "spark/os/os.h"
#include <memory>
#include <nlohmann/json.hpp>
namespace bts::config {
class JetFIX_Connection;
}
namespace bts::md {
class SecurityOracle;
}
namespace bts::orderman::rakurai {
struct GatewayConfig {
std::string account;
std::string operator_id;
std::string json_conf_file_name;
std::uint64_t default_priority_fee = 1000;
nlohmann::json connector_config;
void expand_env() {
bts::os::expand_env_in_place(account);
bts::os::expand_env_in_place(operator_id);
bts::os::expand_env_in_place(json_conf_file_name);
}
};
inline void from_json(const nlohmann::json& j, GatewayConfig& cfg) {
cfg.connector_config = j;
const bts::io::JsonReader jr{&j};
jr.read_or("account", cfg.account);
jr.read_or("operator_id", cfg.operator_id);
jr.read_or("json_conf_file_name", cfg.json_conf_file_name);
jr.read_or("default_priority_fee", cfg.default_priority_fee);
}
class RakuraiExecutionGateway : public IExecutionGateway {
public:
explicit RakuraiExecutionGateway(
bts::md::SecurityOracle sec_oracle,
GatewayConfig config,
bts::config::JetFIX_Connection jetfix_connection);
~RakuraiExecutionGateway() override;
void start() override;
void dispatch_incoming_fix_messages() override;
void on_securities(
const bts::mercury::Update<bts::orderman::NewSecuritiesEvent>&)
override;
void on_admin_data(
const bts::mercury::Update<bts::orderman::NewAdminDataEvent>&) override;
void send_fix(const bts::jetfix::OutFIXPtr&, bts::trace::CorID) override;
void send_msgs(
std::vector<bts::orderman::OrderMsg> const&,
bts::trace::CorID) override;
bts::orderman::OnExeReport& on_exe_report() override;
bts::orderman::OnGwStatusChange& on_gw_status_change() override;
bts::orderman::OnCancelReject& on_cancel_reject() override;
bts::orderman::OnExchangePositionState&
on_exchange_position_state() override;
bts::orderman::OnExchangeOpenOrdersState& on_exchange_open_orders() override;
private:
struct PImpl;
std::unique_ptr<PImpl> me_;
};
} // namespace bts::orderman::rakurai
@@ -25,6 +25,7 @@
#include "sparkd/md/ccapi/CCApiMarketDataProvider.h"
#include "sparkd/md/databento/DatabentoMarketDataProvider.h"
#include "sparkd/md/emumba/EmumbaMarketDataProvider.h"
#include "sparkd/md/rakurai/RakuraiMarketDataProvider.h"
#include "sparkd/md/taifex/TaifexMarketDataProvider.h"
#include <bits/stdint-uintn.h>
@@ -81,6 +82,8 @@ struct MarketDataGateway::PImpl {
std::make_unique<OnixsMarketDataProvider>(this->config));
providers.push_back(std::make_unique<DatabentoMarketDataProvider>(
this->config, this->sec_id_store));
providers.push_back(std::make_unique<RakuraiMarketDataProvider>(
this->config, this->sec_id_store));
providers.push_back(std::make_unique<TaifexMarketDataProvider>(
this->config, this->sec_id_store));
providers.push_back(std::make_unique<PyMarketDataProvider>(
@@ -0,0 +1,816 @@
#include "sparkd/md/rakurai/RakuraiDexDataHandler.h"
#include "spark/api/domain/SecurityType.pb.h"
#include "spark/domain/Book.h"
#include "spark/domain/Price.h"
#include "spark/domain/Quantity.h"
#include "spark/domain/Security.h"
#include "spark/logging/logging.h"
#include "spark/persistence/SqliteStore.h"
#include "spark/utils/PBUtils.h"
#include "sparkd/md/MarketDataError.h"
#include <dex_handler.h>
#include <dex_listener.h>
#include <dex_utils.h>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <algorithm>
#include <array>
#include <cstdint>
#include <csignal>
#include <execinfo.h>
#include <fstream>
#include <iomanip>
#include <list>
#include <memory>
#include <mutex>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <unistd.h>
#include <vector>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
namespace bts::md::rakurai {
namespace {
using ConnectorVenue = ::rakurai::dex_connector::data_types::venue;
using ConnectorSecurity = ::rakurai::dex_connector::data_types::security;
using ConnectorBookLevel = ::rakurai::dex_connector::data_types::book_level;
using ConnectorDexConfig = ::rakurai::dex_connector::configuration::dex_config;
struct ParsedWsUri {
std::string scheme;
std::string host;
std::string port;
std::string path;
};
struct WsProbeResult {
bool tcp_connected = false;
bool sent_request = false;
bool received_response = false;
std::string summary;
};
std::optional<ParsedWsUri> parse_ws_uri(const std::string& uri) {
const auto scheme_pos = uri.find("://");
if (scheme_pos == std::string::npos) {
return std::nullopt;
}
ParsedWsUri parsed;
parsed.scheme = uri.substr(0, scheme_pos);
auto remainder = uri.substr(scheme_pos + 3);
const auto path_pos = remainder.find('/');
if (path_pos == std::string::npos) {
parsed.path = "/";
} else {
parsed.path = remainder.substr(path_pos);
remainder = remainder.substr(0, path_pos);
}
const auto colon_pos = remainder.rfind(':');
if (colon_pos == std::string::npos) {
parsed.host = remainder;
parsed.port = parsed.scheme == "wss" ? "443" : "80";
} else {
parsed.host = remainder.substr(0, colon_pos);
parsed.port = remainder.substr(colon_pos + 1);
}
if (parsed.host.empty() || parsed.port.empty()) {
return std::nullopt;
}
return parsed;
}
WsProbeResult probe_ws_endpoint(const std::string& uri) {
WsProbeResult result;
const auto parsed = parse_ws_uri(uri);
if (!parsed.has_value()) {
result.summary = "invalid websocket URI";
return result;
}
if (parsed->scheme != "ws") {
result.summary = "probe only supports ws:// URIs";
return result;
}
struct addrinfo hints {};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo* addr_list = nullptr;
const int gai_rc = ::getaddrinfo(
parsed->host.c_str(), parsed->port.c_str(), &hints, &addr_list);
if (gai_rc != 0) {
result.summary =
std::string("DNS resolution failed: ") + ::gai_strerror(gai_rc);
return result;
}
int fd = -1;
for (auto* ai = addr_list; ai != nullptr; ai = ai->ai_next) {
fd = ::socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (fd < 0) {
continue;
}
timeval timeout {};
timeout.tv_sec = 3;
timeout.tv_usec = 0;
::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
::setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
if (::connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) {
result.tcp_connected = true;
break;
}
::close(fd);
fd = -1;
}
::freeaddrinfo(addr_list);
if (fd < 0) {
result.summary = "TCP connect failed";
return result;
}
const std::string request =
"GET " + parsed->path + " HTTP/1.1\r\n" +
"Host: " + parsed->host + ":" + parsed->port + "\r\n" +
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n";
const ssize_t sent = ::send(fd, request.data(), request.size(), 0);
if (sent == static_cast<ssize_t>(request.size())) {
result.sent_request = true;
} else {
result.summary = "failed to send websocket probe request";
::close(fd);
return result;
}
std::array<char, 1024> buffer {};
const ssize_t received = ::recv(fd, buffer.data(), buffer.size() - 1, 0);
::close(fd);
if (received <= 0) {
result.summary = "no HTTP response received";
return result;
}
result.received_response = true;
std::string response(buffer.data(), static_cast<std::size_t>(received));
const auto line_end = response.find("\r\n");
if (line_end != std::string::npos) {
response.resize(line_end);
}
result.summary = response;
return result;
}
void validate_ws_endpoint_or_throw(
const std::string& label, const std::string& uri) {
const auto probe = probe_ws_endpoint(uri);
LOG(md, Info,
"Rakurai websocket probe. endpoint:{} uri:{} tcp_connected:{} sent_request:{} received_response:{} summary:{}",
label,
uri,
probe.tcp_connected,
probe.sent_request,
probe.received_response,
probe.summary);
if (!probe.tcp_connected || !probe.sent_request || !probe.received_response) {
throw std::runtime_error(
std::string("Rakurai websocket endpoint probe failed for ") + label +
" (" + uri + "): " + probe.summary);
}
if (probe.summary.rfind("HTTP/1.1 101", 0) != 0 &&
probe.summary.rfind("HTTP/1.0 101", 0) != 0) {
throw std::runtime_error(
std::string("Rakurai websocket endpoint returned unexpected handshake for ") +
label + " (" + uri + "): " + probe.summary);
}
}
void write_crash_text(std::string_view text) {
if(::write(STDERR_FILENO, text.data(), text.size()) ==-1) {
// If write fails, there's not much we can do, but we don't want to get
// stuck in a loop trying to write. So we just ignore the error.
};
}
void rakurai_crash_signal_handler(int signo, siginfo_t* info, void* /*ucontext*/) {
constexpr int max_frames = 64;
void* frames[max_frames];
const int frame_count = ::backtrace(frames, max_frames);
write_crash_text("\n=== Rakurai crash signal caught ===\n");
if (signo == SIGSEGV) {
write_crash_text("signal: SIGSEGV\n");
} else if (signo == SIGBUS) {
write_crash_text("signal: SIGBUS\n");
} else {
write_crash_text("signal: unknown\n");
}
if (info != nullptr) {
if (info->si_addr != nullptr) {
write_crash_text("fault address captured\n");
}
if (info->si_code != 0) {
write_crash_text("signal code captured\n");
}
}
write_crash_text("backtrace:\n");
::backtrace_symbols_fd(frames, frame_count, STDERR_FILENO);
_Exit(128 + signo);
}
void install_rakurai_crash_signal_handlers() {
static std::once_flag once;
std::call_once(once, []() {
struct sigaction sa {};
sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
sa.sa_sigaction = rakurai_crash_signal_handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, nullptr);
sigaction(SIGBUS, &sa, nullptr);
});
}
std::string bytes_to_hex(const std::array<std::uint8_t, 32>& bytes) {
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (std::uint8_t byte : bytes) {
oss << std::setw(2) << static_cast<int>(byte);
}
return oss.str();
}
std::string short_hex(const std::array<std::uint8_t, 32>& bytes) {
const auto hex = bytes_to_hex(bytes);
return hex.substr(0, std::min<std::size_t>(hex.size(), 12));
}
const char* connector_venue_name(ConnectorVenue venue) {
switch (venue) {
case ConnectorVenue::orca_clmm:
return "orca_clmm";
case ConnectorVenue::orca_amm:
return "orca_amm";
case ConnectorVenue::raydium_amm:
return "raydium_amm";
case ConnectorVenue::raydium_cpmm:
return "raydium_cpmm";
case ConnectorVenue::raydium_clmm:
return "raydium_clmm";
// case ConnectorVenue::meterora_dlmm:
// return "meterora_dlmm";
default:
return "invalid";
}
}
bool venue_matches(api::domain::Venue spark_venue, ConnectorVenue connector_venue) {
switch (spark_venue) {
case api::domain::VENUE_ORCA:
return connector_venue == ConnectorVenue::orca_clmm ||
connector_venue == ConnectorVenue::orca_amm;
case api::domain::VENUE_RAYDIUM:
return connector_venue == ConnectorVenue::raydium_amm ||
connector_venue == ConnectorVenue::raydium_cpmm ||
connector_venue == ConnectorVenue::raydium_clmm;
default:
return false;
}
}
std::string token_display_name(const ::rakurai::dex_connector::data_types::token_info& token) {
return token.name.empty() ? short_hex(token.mint_address) : token.name;
}
std::string build_security_desc(
const ConnectorSecurity& security, std::string_view short_pool_id) {
return fmt::format(
"{}-{}-{}",
token_display_name(security.token_a),
token_display_name(security.token_b),
short_pool_id);
}
double quantity_scale_from_decimals(std::uint8_t decimals) {
double scale = 1.0;
for (std::uint8_t i = 0; i < decimals; ++i) {
scale *= 10.0;
}
return scale;
}
domain::BookLevel to_book_level(
const ConnectorBookLevel& source,
double quantity_scale) {
domain::BookLevel level;
level.price = domain::Price::from_double(source.price);
const auto normalized_quantity =
quantity_scale > 0.0
? static_cast<double>(source.quantity) / quantity_scale
: static_cast<double>(source.quantity);
level.total_quantity =
domain::Quantity(static_cast<float>(normalized_quantity));
level.direct_quantity = level.total_quantity;
level.n_orders = 0;
return level;
}
std::list<ConnectorDexConfig> parse_dex_configs(
const rapidjson::Document& config,
api::domain::Venue venue_filter) {
std::list<ConnectorDexConfig> dex_config_list;
if (!config.HasMember("dex") || !config["dex"].IsArray()) {
return dex_config_list;
}
for (const auto& dex_value : config["dex"].GetArray()) {
if (!dex_value.IsObject() || !dex_value.HasMember("id")) {
continue;
}
ConnectorDexConfig dex_config;
const std::string dex = dex_value["id"].GetString();
if (dex == "orca_clmm") {
if (venue_filter != api::domain::VENUE_ORCA) {
continue;
}
dex_config.dex_name = ConnectorVenue::orca_clmm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::clmm;
} else if (dex == "orca_amm") {
if (venue_filter != api::domain::VENUE_ORCA) {
continue;
}
dex_config.dex_name = ConnectorVenue::orca_amm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::amm;
} else if (dex == "raydium_amm") {
if (venue_filter != api::domain::VENUE_RAYDIUM) {
continue;
}
dex_config.dex_name = ConnectorVenue::raydium_amm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::amm;
} else if (dex == "raydium_cpmm") {
if (venue_filter != api::domain::VENUE_RAYDIUM) {
continue;
}
dex_config.dex_name = ConnectorVenue::raydium_cpmm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::cpmm;
} else if (dex == "raydium_clmm") {
if (venue_filter != api::domain::VENUE_RAYDIUM) {
continue;
}
dex_config.dex_name = ConnectorVenue::raydium_clmm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::clmm;
} else {
LOG(md, Warn, "Skipping unsupported Rakurai dex config '{}'", dex);
continue;
}
if (dex_value.HasMember("pool_ids") && dex_value["pool_ids"].IsArray()) {
for (const auto& pool_value : dex_value["pool_ids"].GetArray()) {
dex_config.interested_pools.emplace_back(
::rakurai::dex_connector::solanaAddressToBytes(
pool_value.GetString()));
}
}
dex_config_list.push_back(dex_config);
}
return dex_config_list;
}
class ListenerAdapter final : public ::rakurai::dex_connector::dex_listener {
public:
ListenerAdapter(
std::shared_ptr<IMarketDataListener> listener,
std::shared_ptr<persistence::IDGenerator> sec_id_store,
api::domain::Venue venue,
std::uint32_t num_depth_levels,
const std::vector<std::string>& excluded_securities)
: listener_(std::move(listener)),
sec_id_store_(std::move(sec_id_store)),
venue_(venue),
depth_limit_(
num_depth_levels == 0
? domain::Book::max_depth()
: std::min<std::size_t>(
num_depth_levels, domain::Book::max_depth())),
excluded_securities_(
excluded_securities.begin(), excluded_securities.end()) {}
std::string get_or_add_short_pool_id(const std::string& pool_id) {
constexpr std::string_view kPoolAliasNamespace = "rakurai_pool_alias";
const auto [exists, rowid, _] =
pool_alias_store_.retrieve_one(std::string(kPoolAliasNamespace), pool_id);
if (exists) {
return std::to_string(rowid);
}
const auto new_id =
pool_alias_store_.insert(std::string(kPoolAliasNamespace), pool_id, "");
return std::to_string(new_id);
}
void on_pool_definition(
ConnectorVenue connector_venue,
const std::vector<ConnectorSecurity*>& securities) override {
std::lock_guard<std::mutex> lock(callback_mutex_);
LOG(md, Info,
"Rakurai listener on_pool_definition start. venue:{} connector_venue:{} securities:{}",
int(venue_),
std::string(connector_venue_name(connector_venue)),
securities.size());
if (!venue_matches(venue_, connector_venue)) {
LOG(md, Info,
"Rakurai listener on_pool_definition skipped. venue:{} connector_venue:{}",
int(venue_),
std::string(connector_venue_name(connector_venue)));
return;
}
std::vector<const domain::Security*> emitted;
emitted.reserve(securities.size());
for (const ConnectorSecurity* security : securities) {
if (security == nullptr || !venue_matches(venue_, security->venue)) {
continue;
}
const auto pool_id = bytes_to_hex(security->pool_id);
const auto short_pool_id = get_or_add_short_pool_id(pool_id);
const auto desc = build_security_desc(*security, short_pool_id);
const auto symbol = token_display_name(security->token_a);
// const auto token_a_cpp = security->token_a.decimals;
const auto currency = token_display_name(security->token_b);
if (excluded_securities_.contains(pool_id) ||
excluded_securities_.contains(short_pool_id) ||
excluded_securities_.contains(desc)) {
continue;
}
domain::Security emitted_security;
emitted_security.security = domain::SpotSecurity{};
emitted_security.security_common.venue = venue_;
emitted_security.security_common.exchange_sec_id = short_pool_id;
emitted_security.security_common.symbol = symbol;
emitted_security.security_common.currency = currency;
emitted_security.security_common.asset = symbol;
emitted_security.security_common.group =
connector_venue_name(security->venue);
emitted_security.security_common.desc = desc;
emitted_security.security_common.min_price_increment =
security->min_tick > 0.0 ? security->min_tick : 0.0;
emitted_security.security_common.min_quantity_increment =
static_cast<double>(security->min_trade_amount);
emitted_security.security_common.min_quantity =
static_cast<double>(security->min_trade_amount);
*emitted_security.security_common.id = sec_id_store_->get_or_add(
venue_, api::domain::SECURITY_TYPE_SPOT, pool_id);
auto [it, is_new] = securities_.try_emplace(
pool_id, std::make_unique<domain::Security>(std::move(emitted_security)));
if (!is_new) {
*it->second = std::move(emitted_security);
}
pool_to_security_id_[pool_id] = it->second->id();
pool_quantity_scales_[pool_id] =
quantity_scale_from_decimals(security->token_a.decimals);
emitted.push_back(it->second.get());
}
if (emitted.empty()) {
LOG(md, Info,
"Rakurai listener on_pool_definition produced no securities. venue:{} connector_venue:{}",
int(venue_),
std::string(connector_venue_name(connector_venue)));
return;
}
LOG(md, Info,
"Rakurai listener on_pool_definition emitting securities. venue:{} connector_venue:{} emitted:{} tracked:{}",
int(venue_),
std::string(connector_venue_name(connector_venue)),
emitted.size(),
securities_.size());
listener_->on_securities(emitted);
LOG(md, Info,
"Rakurai listener on_pool_definition complete. venue:{} connector_venue:{} emitted:{}",
int(venue_),
std::string(connector_venue_name(connector_venue)),
emitted.size());
}
void on_book(
ConnectorVenue connector_venue,
const std::array<std::uint8_t, 32>& pool_id,
const std::vector<ConnectorBookLevel>& bids,
const std::vector<ConnectorBookLevel>& asks) override {
std::lock_guard<std::mutex> lock(callback_mutex_);
LOG(md, Diag,
"Rakurai listener on_book start. venue:{} connector_venue:{} pool:{} bids:{} asks:{}",
int(venue_),
std::string(connector_venue_name(connector_venue)),
short_hex(pool_id),
bids.size(),
asks.size());
if (!venue_matches(venue_, connector_venue)) {
return;
}
const auto pool_key = bytes_to_hex(pool_id);
const auto it = pool_to_security_id_.find(pool_key);
if (it == pool_to_security_id_.end()) {
LOG(md, Warn,
"Received Rakurai book before security definition for pool {}",
pool_key);
return;
}
const auto quantity_scale_it = pool_quantity_scales_.find(pool_key);
const auto quantity_scale =
quantity_scale_it != pool_quantity_scales_.end()
? quantity_scale_it->second
: 1.0;
auto book = domain::Book::empty(it->second);
const auto bid_depth = std::min<std::size_t>(depth_limit_, bids.size());
const auto ask_depth = std::min<std::size_t>(depth_limit_, asks.size());
for (std::size_t i = 0; i < bid_depth; ++i) {
book.bids()[i] = to_book_level(bids[i], quantity_scale);
}
for (std::size_t i = 0; i < ask_depth; ++i) {
book.asks()[i] = to_book_level(asks[i], quantity_scale);
}
listener_->on_book(book);
LOG(md, Diag,
"Rakurai listener on_book complete. venue:{} connector_venue:{} pool:{}",
int(venue_),
std::string(connector_venue_name(connector_venue)),
short_hex(pool_id));
}
void on_order_update(
::rakurai::dex_connector::order_entry::order_state order_state) override {
std::lock_guard<std::mutex> lock(callback_mutex_);
LOG(md, Info,
"Rakurai order update. client_id:{} txn_id:{} status:{}",
order_state.client_id,
order_state.txn_id,
static_cast<int>(order_state.tx_status));
}
void on_ata_verification_status(
const std::array<std::uint8_t, 32>& token_id,
const std::string& ata_address,
::rakurai::dex_connector::ata_status status) override {
std::lock_guard<std::mutex> lock(callback_mutex_);
LOG(md, Info,
"Rakurai ATA verification. token:{} ata:{} status:{}",
short_hex(token_id),
ata_address,
static_cast<int>(status));
}
private:
std::shared_ptr<IMarketDataListener> listener_;
std::shared_ptr<persistence::IDGenerator> sec_id_store_;
api::domain::Venue venue_;
std::size_t depth_limit_;
std::mutex callback_mutex_;
std::unordered_set<std::string> excluded_securities_;
persistence::SqliteStore pool_alias_store_{"rakurai_pool_alias"};
std::unordered_map<std::string, std::unique_ptr<domain::Security>> securities_;
std::unordered_map<std::string, domain::SecurityID> pool_to_security_id_;
std::unordered_map<std::string, double> pool_quantity_scales_;
};
} // namespace
void RakuraiDexDataHandler::run(
std::shared_ptr<IMarketDataListener> listener,
std::shared_ptr<persistence::IDGenerator> sec_id_store,
api::domain::Venue venue,
const std::string& config_path,
std::uint32_t num_depth_levels,
const std::vector<std::string>& excluded_securities) {
try {
install_rakurai_crash_signal_handlers();
LOG(md, Info,
"Rakurai DEX handler run start. venue:{} config:{} depth:{} excluded:{}",
int(venue),
config_path,
num_depth_levels,
excluded_securities.size());
std::ifstream config_file(config_path);
if (!config_file.is_open()) {
throw std::runtime_error("Unable to open Rakurai config: " + config_path);
}
rapidjson::IStreamWrapper isw{config_file};
rapidjson::Document config;
if (config.ParseStream(isw).HasParseError()) {
throw std::runtime_error(
"Unable to parse Rakurai config: " + config_path);
}
LOG(md, Info,
"Rakurai DEX handler config parsed. venue:{} config:{}",
int(venue),
config_path);
::rakurai::dex_connector::configuration::logging_config logging_config;
::rakurai::dex_connector::configuration::session_config session_config;
::rakurai::dex_connector::configuration::wallet wallet_config;
bool cluster_mode = false;
if (config.HasMember("app_logging") && config["app_logging"].IsObject()) {
const auto& app_logging = config["app_logging"];
if (app_logging.HasMember("severity")) {
logging_config.severity = app_logging["severity"].GetString();
}
if (app_logging.HasMember("console")) {
logging_config.is_console_enabled = app_logging["console"].GetBool();
}
if (app_logging.HasMember("file")) {
logging_config.is_file_enabled = app_logging["file"].GetBool();
}
if (app_logging.HasMember("queuesize")) {
logging_config.queue_size = app_logging["queuesize"].GetUint();
}
}
if (config.HasMember("cluster_mode")) {
cluster_mode = config["cluster_mode"].GetBool();
}
auto add_uri = [&](const char* md_key, const char* oms_key) {
if (!config.HasMember(md_key) || !config.HasMember(oms_key)) {
throw std::runtime_error(
std::string("Incomplete Rakurai uri config in ") + config_path);
}
::rakurai::dex_connector::configuration::uri_config uri_config;
uri_config.md_server_uri = config[md_key].GetString();
uri_config.node_oms_uri = config[oms_key].GetString();
session_config.uri.push_back(uri_config);
};
add_uri("md_server_uri_1", "node_oms_uri_1");
if (cluster_mode) {
add_uri("md_server_uri_2", "node_oms_uri_2");
}
LOG(md, Info,
"Rakurai DEX handler uri config prepared. venue:{} cluster_mode:{} uri_count:{}",
int(venue),
cluster_mode,
session_config.uri.size());
std::size_t uri_index = 1;
for (const auto& uri_config : session_config.uri) {
const auto index = std::to_string(uri_index);
validate_ws_endpoint_or_throw(
"md_server_uri_" + index, uri_config.md_server_uri);
validate_ws_endpoint_or_throw(
"node_oms_uri_" + index, uri_config.node_oms_uri);
++uri_index;
}
if (config.HasMember("wallet_pub")) {
wallet_config.wallet_pub_key =
::rakurai::dex_connector::solanaAddressToBytes(
config["wallet_pub"].GetString());
}
if (config.HasMember("wallet_pvt") && config["wallet_pvt"].IsArray()) {
const auto& wallet_pvt = config["wallet_pvt"];
if (wallet_pvt.Size() != wallet_config.wallet_pvt_key.size()) {
throw std::runtime_error(
"Rakurai wallet_pvt must contain 64 integer entries");
}
for (rapidjson::SizeType i = 0; i < wallet_pvt.Size(); ++i) {
wallet_config.wallet_pvt_key[i] =
static_cast<std::uint8_t>(wallet_pvt[i].GetUint());
}
} else {
throw std::runtime_error("Missing Rakurai wallet_pvt in " + config_path);
}
if (config.HasMember("core")) {
session_config.core = config["core"].GetUint();
}
LOG(md, Info,
"Rakurai DEX handler session config prepared. venue:{} core:{} console:{} file:{} queue:{} severity:{}",
int(venue),
session_config.core,
logging_config.is_console_enabled,
logging_config.is_file_enabled,
logging_config.queue_size,
logging_config.severity);
auto dex_configs = parse_dex_configs(config, venue);
if (dex_configs.empty()) {
LOG(md, Warn,
"No Rakurai DEX subscriptions matched venue {} in {}",
int(venue),
config_path);
return;
}
LOG(md, Info,
"Rakurai DEX subscriptions prepared. venue:{} dex_configs:{}",
int(venue),
dex_configs.size());
ListenerAdapter adapter(
listener,
sec_id_store,
venue,
num_depth_levels,
excluded_securities);
LOG(md, Info,
"Rakurai listener adapter constructed. venue:{} listener_present:{} sec_id_store_present:{}",
int(venue),
listener != nullptr,
sec_id_store != nullptr);
::rakurai::dex_connector::dex_handler handler(
dex_configs,
wallet_config,
logging_config,
session_config.uri,
session_config.core,
cluster_mode,
&adapter);
LOG(md, Info,
"Rakurai dex_handler constructed. venue:{} uri_count:{} dex_configs:{} cluster_mode:{}",
int(venue),
session_config.uri.size(),
dex_configs.size(),
cluster_mode);
auto trading_session =
std::make_unique<
::rakurai::dex_connector::trading_session_interface>(
handler.create_new_trading_session());
LOG(md, Info,
"Rakurai trading session created. venue:{} session_present:{}",
int(venue),
trading_session != nullptr);
LOG(md, Info,
"Rakurai dex_handler about to run. venue:{}",
int(venue));
handler.run();
LOG(md, Info,
"Rakurai dex_handler run returned. venue:{}",
int(venue));
} catch (const std::exception& ex) {
LOG(md, Error, "Rakurai DEX handler failed: {}", std::string(ex.what()));
if (listener) {
MarketDataError error;
error.type = MarketDataError::DISCONNECTION;
error.message = ex.what();
listener->on_error(error);
}
}
}
} // namespace bts::md::rakurai
@@ -0,0 +1,25 @@
#pragma once
#include "spark/api/domain/Venue.pb.h"
#include "spark/md/IMarketDataListener.h"
#include "spark/persistence/IDGenerator.h"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
namespace bts::md::rakurai {
class RakuraiDexDataHandler {
public:
static void run(
std::shared_ptr<IMarketDataListener> listener,
std::shared_ptr<persistence::IDGenerator> sec_id_store,
api::domain::Venue venue,
const std::string& config_path,
std::uint32_t num_depth_levels,
const std::vector<std::string>& excluded_securities);
};
} // namespace bts::md::rakurai
@@ -0,0 +1,74 @@
#include "sparkd/md/rakurai/RakuraiDexSubscription.h"
#include "spark/config/config.h"
#include "spark/config/protos/system_config.pb.h"
#include "spark/logging/logging.h"
#include "sparkd/rakurai/RakuraiSharedRuntime.h"
#include <boost/filesystem/path.hpp>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace bts::md {
using Path = boost::filesystem::path;
using bts::config::SystemConfig;
struct RakuraiDexSubscription::PImpl {
api::domain::Venue venue;
std::string json_conf_file_name;
std::uint32_t num_depth_levels = 0;
std::vector<std::string> excluded_securities;
std::shared_ptr<persistence::IDGenerator> sec_id_store;
std::size_t subscription_id = 0;
};
RakuraiDexSubscription::RakuraiDexSubscription(
api::domain::Venue venue,
api::md::DexMarketDataConfig config,
std::shared_ptr<persistence::IDGenerator> sec_id_store)
: me_(std::make_unique<PImpl>()) {
me_->venue = venue;
me_->json_conf_file_name = config.json_conf_file_name();
me_->num_depth_levels = config.num_depth_levels();
me_->excluded_securities.assign(
config.excluded_securities().begin(),
config.excluded_securities().end());
me_->sec_id_store = std::move(sec_id_store);
}
RakuraiDexSubscription::~RakuraiDexSubscription() = default;
void RakuraiDexSubscription::start(
std::shared_ptr<IMarketDataListener> listener) {
const auto config_path =
Path{config::get<SystemConfig>().config_path()} / me_->json_conf_file_name;
LOG(md, Info,
"Starting Rakurai DEX market data. venue:{} config:{}",
int(me_->venue),
config_path.string());
me_->subscription_id =
rakurai::RakuraiSharedRuntime::instance().add_market_data_subscriber(
std::move(listener),
me_->sec_id_store,
me_->venue,
me_->num_depth_levels,
me_->excluded_securities);
rakurai::RakuraiSharedRuntime::instance().ensure_started_from_file(
config_path.string());
}
void RakuraiDexSubscription::stop() {
if (me_->subscription_id != 0) {
rakurai::RakuraiSharedRuntime::instance().remove_market_data_subscriber(
me_->subscription_id);
me_->subscription_id = 0;
}
}
} // namespace bts::md
@@ -0,0 +1,31 @@
#pragma once
#include "spark/api/domain/Venue.pb.h"
#include "spark/api/md/DexMarketDataConfig.pb.h"
#include "spark/md/IMarketDataSource.h"
#include <memory>
namespace bts::persistence {
class IDGenerator;
}
namespace bts::md {
class RakuraiDexSubscription : public IMarketDataSource {
public:
RakuraiDexSubscription(
api::domain::Venue venue,
api::md::DexMarketDataConfig config,
std::shared_ptr<persistence::IDGenerator> sec_id_store);
~RakuraiDexSubscription() override;
void start(std::shared_ptr<IMarketDataListener> listener) override;
void stop() override;
private:
struct PImpl;
std::unique_ptr<PImpl> me_;
};
} // namespace bts::md
@@ -0,0 +1,58 @@
#include "RakuraiMarketDataProvider.h"
#include "spark/api/domain/Venue.pb.h"
#include "spark/api/md/MarketDataConfig.pb.h"
#include "spark/md/IMarketDataSource.h"
#include "spark/persistence/IDGenerator.h"
#include "spark/logging/logging.h"
#include "sparkd/md/rakurai/RakuraiDexSubscription.h"
#include <memory>
#include <vector>
namespace bts::md {
using bts::api::md::MarketDataConfig;
using bts::persistence::IDGenerator;
using std::pair;
using std::shared_ptr;
using std::string;
using std::vector;
struct RakuraiMarketDataProvider::PImpl {
MarketDataConfig config;
shared_ptr<IDGenerator> sec_id_store;
PImpl(MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
: config(std::move(config)), sec_id_store(std::move(sec_id_store)) {}
};
RakuraiMarketDataProvider::RakuraiMarketDataProvider(
MarketDataConfig config, shared_ptr<IDGenerator> sec_id_store)
: me_(std::make_unique<PImpl>(std::move(config), std::move(sec_id_store))) {
}
RakuraiMarketDataProvider::~RakuraiMarketDataProvider() = default;
vector<pair<api::domain::Venue, shared_ptr<IMarketDataSource>>>
RakuraiMarketDataProvider::get_sources() {
vector<pair<api::domain::Venue, shared_ptr<IMarketDataSource>>> rv;
for (const auto &dex : me_->config.dex()) {
switch (dex.venue()) {
case api::domain::VENUE_ORCA:
case api::domain::VENUE_RAYDIUM:
rv.push_back(
{dex.venue(),
std::make_shared<RakuraiDexSubscription>(
dex.venue(), dex, me_->sec_id_store)});
break;
default:
LOG(md, Warn,
"Skipping unsupported Rakurai dex venue:{}",
int(dex.venue()));
break;
}
}
return rv;
}
} // namespace bts::md
@@ -0,0 +1,34 @@
#pragma once
#include "spark/md/IMarketDataProvider.h"
#include <memory>
namespace bts::persistence {
class IDGenerator;
}
namespace bts::api::md {
class MarketDataConfig;
}
namespace bts::md {
class RakuraiMarketDataProvider : public IMarketDataProvider {
public:
explicit RakuraiMarketDataProvider(
api::md::MarketDataConfig config,
std::shared_ptr<persistence::IDGenerator> sec_id_store);
~RakuraiMarketDataProvider();
std::vector<
std::pair<api::domain::Venue, std::shared_ptr<IMarketDataSource>>>
get_sources() override;
private:
struct PImpl;
std::unique_ptr<PImpl> me_;
};
} // namespace bts::md
@@ -0,0 +1,867 @@
#include "sparkd/rakurai/RakuraiSharedRuntime.h"
#include "spark/api/domain/SecurityType.pb.h"
#include "spark/domain/Book.h"
#include "spark/domain/Price.h"
#include "spark/domain/Quantity.h"
#include "spark/domain/Security.h"
#include "spark/logging/logging.h"
#include "spark/md/IMarketDataListener.h"
#include "spark/persistence/IDGenerator.h"
#include "spark/persistence/SqliteStore.h"
#include "sparkd/md/MarketDataError.h"
#include <boost/filesystem/path.hpp>
#include <dex_handler.h>
#include <dex_listener.h>
#include <nlohmann/json.hpp>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
namespace bts::rakurai {
namespace {
using ConnectorVenue = ::rakurai::dex_connector::data_types::venue;
using ConnectorSecurity = ::rakurai::dex_connector::data_types::security;
using ConnectorBookLevel = ::rakurai::dex_connector::data_types::book_level;
using ConnectorDexConfig = ::rakurai::dex_connector::configuration::dex_config;
const char* connector_venue_name(ConnectorVenue venue) {
switch (venue) {
case ConnectorVenue::orca_clmm:
return "orca_clmm";
case ConnectorVenue::orca_amm:
return "orca_amm";
case ConnectorVenue::raydium_amm:
return "raydium_amm";
case ConnectorVenue::raydium_cpmm:
return "raydium_cpmm";
case ConnectorVenue::raydium_clmm:
return "raydium_clmm";
default:
return "invalid";
}
}
bool venue_matches(api::domain::Venue spark_venue, ConnectorVenue connector_venue) {
switch (spark_venue) {
case api::domain::VENUE_ORCA:
return connector_venue == ConnectorVenue::orca_clmm ||
connector_venue == ConnectorVenue::orca_amm;
case api::domain::VENUE_RAYDIUM:
return connector_venue == ConnectorVenue::raydium_amm ||
connector_venue == ConnectorVenue::raydium_cpmm ||
connector_venue == ConnectorVenue::raydium_clmm;
default:
return false;
}
}
bool venue_matches(api::Venue spark_venue, ConnectorVenue connector_venue) {
switch (spark_venue) {
case api::VENUE_ORCA:
return connector_venue == ConnectorVenue::orca_clmm ||
connector_venue == ConnectorVenue::orca_amm;
case api::VENUE_RAYDIUM:
return connector_venue == ConnectorVenue::raydium_amm ||
connector_venue == ConnectorVenue::raydium_cpmm ||
connector_venue == ConnectorVenue::raydium_clmm;
default:
return false;
}
}
std::string bytes_to_hex(const std::array<std::uint8_t, 32>& bytes) {
static constexpr char kHex[] = "0123456789abcdef";
std::string out;
out.reserve(bytes.size() * 2);
for (std::uint8_t byte : bytes) {
out.push_back(kHex[(byte >> 4) & 0x0f]);
out.push_back(kHex[byte & 0x0f]);
}
return out;
}
std::string short_hex(const std::array<std::uint8_t, 32>& bytes) {
const auto hex = bytes_to_hex(bytes);
return hex.substr(0, std::min<std::size_t>(hex.size(), 12));
}
std::string token_display_name(
const ::rakurai::dex_connector::data_types::token_info& token) {
return token.name.empty() ? short_hex(token.mint_address) : token.name;
}
std::string build_security_desc(
const ConnectorSecurity& security,
std::string_view short_pool_id) {
return fmt::format(
"{}-{}-{}",
token_display_name(security.token_a),
token_display_name(security.token_b),
short_pool_id);
}
double quantity_scale_from_decimals(std::uint8_t decimals) {
double scale = 1.0;
for (std::uint8_t i = 0; i < decimals; ++i) {
scale *= 10.0;
}
return scale;
}
domain::BookLevel to_book_level(
const ConnectorBookLevel& source,
double quantity_scale) {
domain::BookLevel level;
level.price = domain::Price::from_double(source.price);
const auto normalized_quantity =
quantity_scale > 0.0
? static_cast<double>(source.quantity) / quantity_scale
: static_cast<double>(source.quantity);
level.total_quantity =
domain::Quantity(static_cast<float>(normalized_quantity));
level.direct_quantity = level.total_quantity;
level.n_orders = 0;
return level;
}
std::list<ConnectorDexConfig> parse_dex_configs(const nlohmann::json& config) {
std::list<ConnectorDexConfig> dex_config_list;
const auto dex_it = config.find("dex");
if (dex_it == config.end() || !dex_it->is_array()) {
return dex_config_list;
}
for (const auto& dex_value : *dex_it) {
if (!dex_value.is_object()) {
continue;
}
ConnectorDexConfig dex_config;
const std::string dex = dex_value.value("id", "");
if (dex == "orca_clmm") {
dex_config.dex_name = ConnectorVenue::orca_clmm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::clmm;
} else if (dex == "orca_amm") {
dex_config.dex_name = ConnectorVenue::orca_amm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::amm;
} else if (dex == "raydium_amm") {
dex_config.dex_name = ConnectorVenue::raydium_amm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::amm;
} else if (dex == "raydium_cpmm") {
dex_config.dex_name = ConnectorVenue::raydium_cpmm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::cpmm;
} else if (dex == "raydium_clmm") {
dex_config.dex_name = ConnectorVenue::raydium_clmm;
dex_config.pool_typ =
::rakurai::dex_connector::data_types::pool_type::clmm;
} else {
LOG(md, Warn, "Skipping unsupported Rakurai dex config '{}'", dex);
continue;
}
const auto pools_it = dex_value.find("pool_ids");
if (pools_it != dex_value.end() && pools_it->is_array()) {
for (const auto& pool_value : *pools_it) {
dex_config.interested_pools.emplace_back(
::rakurai::dex_connector::solanaAddressToBytes(
pool_value.get<std::string>()));
}
}
dex_config_list.push_back(std::move(dex_config));
}
return dex_config_list;
}
nlohmann::json load_json_file(const std::string& path) {
LOG(md, Info, "Rakurai shared runtime loading config file: {}", path);
std::ifstream config_file(path);
if (!config_file.is_open()) {
throw std::runtime_error("Unable to open Rakurai config: " + path);
}
nlohmann::json config;
config_file >> config;
const auto nested_config_it = config.find("json_conf_file_name");
if (nested_config_it != config.end() && nested_config_it->is_string() &&
!config.contains("dex")) {
const auto nested_name = nested_config_it->get<std::string>();
auto nested_path = boost::filesystem::path(nested_name);
if (nested_path.is_relative()) {
nested_path = boost::filesystem::path(path).parent_path() / nested_path;
}
LOG(md, Info,
"Rakurai shared runtime resolved wrapper config '{}' -> '{}'. wrapper:{}",
path,
nested_path.string(),
config.dump());
return load_json_file(nested_path.string());
}
LOG(md, Info,
"Rakurai shared runtime loaded connector config from '{}': {}",
path,
config.dump());
return config;
}
} // namespace
struct RakuraiSharedRuntime::PImpl {
struct MarketDataSubscriber {
std::size_t id = 0;
std::shared_ptr<md::IMarketDataListener> listener;
std::shared_ptr<persistence::IDGenerator> sec_id_store;
api::domain::Venue venue = api::domain::VENUE_UNSPECIFIED;
std::size_t depth_limit = domain::Book::max_depth();
std::unordered_set<std::string> excluded_securities;
std::unordered_map<std::string, std::unique_ptr<domain::Security>>
securities;
std::unordered_map<std::string, domain::SecurityID> pool_to_security_id;
std::unordered_map<std::string, double> pool_quantity_scales;
std::mutex mutex;
};
struct ListenerAdapter final : public ::rakurai::dex_connector::dex_listener {
explicit ListenerAdapter(PImpl& owner) : owner(owner) {}
void on_pool_definition(
ConnectorVenue venue,
const std::vector<ConnectorSecurity*>& securities) override {
owner.on_pool_definition(venue, securities);
}
void on_book(
ConnectorVenue venue,
const std::array<std::uint8_t, 32>& pool_id,
const std::vector<ConnectorBookLevel>& bids,
const std::vector<ConnectorBookLevel>& asks) override {
owner.on_book(venue, pool_id, bids, asks);
}
void on_order_update(
::rakurai::dex_connector::order_entry::order_state order_state)
override {
owner.on_order_update(order_state);
}
void on_ata_verification_status(
const std::array<std::uint8_t, 32>& token_id,
const std::string& ata_address,
::rakurai::dex_connector::ata_status status) override {
LOG(md, Info,
"Rakurai ATA verification. token:{} ata:{} status:{}",
short_hex(token_id),
ata_address,
static_cast<int>(status));
}
PImpl& owner;
};
mutable std::mutex mutex;
std::optional<nlohmann::json> config;
std::string config_label;
std::optional<std::thread> run_thread;
std::unique_ptr<::rakurai::dex_connector::dex_handler> handler;
std::unique_ptr<::rakurai::dex_connector::trading_session_interface>
trading_session;
persistence::SqliteStore pool_alias_store{"rakurai_pool_alias"};
ListenerAdapter listener{*this};
std::unordered_map<std::size_t, std::shared_ptr<MarketDataSubscriber>>
market_data_subscribers;
std::unordered_map<std::size_t, OrderUpdateCallback> order_update_subscribers;
std::unordered_map<std::size_t, StatusCallback> status_subscribers;
std::unordered_map<std::string, PoolMetadata> pool_metadata;
std::unordered_map<std::string, std::string> short_pool_to_full_pool;
std::unordered_set<std::string> expected_pool_ids;
std::unordered_set<std::string> resolved_pool_ids;
std::size_t next_subscriber_id = 1;
bool started = false;
bool connected = false;
~PImpl() {
if (run_thread.has_value() && run_thread->joinable()) {
run_thread->detach();
}
}
std::size_t next_id_locked() { return next_subscriber_id++; }
void ensure_started(const nlohmann::json& cfg, const std::string& label) {
std::lock_guard<std::mutex> lock(mutex);
LOG(md, Info,
"Rakurai shared runtime ensure_started requested. label:{} config:{}",
label,
cfg.dump());
if (!config.has_value()) {
config = cfg;
config_label = label;
} else if (*config != cfg) {
throw std::runtime_error(
"Rakurai shared runtime already configured with a different config source: " +
config_label + " vs " + label);
}
if (started) {
LOG(md, Info,
"Rakurai shared runtime already started. label:{} connected:{}",
config_label,
connected);
return;
}
started = true;
LOG(md, Info,
"Rakurai shared runtime launching thread. label:{}",
config_label);
run_thread.emplace([this]() { run(); });
}
std::size_t add_market_data_subscriber(
std::shared_ptr<md::IMarketDataListener> listener_in,
std::shared_ptr<persistence::IDGenerator> sec_id_store,
api::domain::Venue venue,
std::uint32_t num_depth_levels,
const std::vector<std::string>& excluded_securities) {
auto sub = std::make_shared<MarketDataSubscriber>();
sub->listener = std::move(listener_in);
sub->sec_id_store = std::move(sec_id_store);
sub->venue = venue;
sub->depth_limit =
num_depth_levels == 0
? domain::Book::max_depth()
: std::min<std::size_t>(
num_depth_levels, domain::Book::max_depth());
sub->excluded_securities.insert(
excluded_securities.begin(), excluded_securities.end());
std::vector<PoolMetadata> snapshot;
{
std::lock_guard<std::mutex> lock(mutex);
sub->id = next_id_locked();
market_data_subscribers.emplace(sub->id, sub);
snapshot.reserve(pool_metadata.size());
for (const auto& [_, metadata] : pool_metadata) {
if (venue_matches(venue, metadata.security.venue)) {
snapshot.push_back(metadata);
}
}
}
replay_pool_definitions(*sub, snapshot);
return sub->id;
}
void remove_market_data_subscriber(std::size_t id) {
std::lock_guard<std::mutex> lock(mutex);
market_data_subscribers.erase(id);
}
std::size_t add_order_update_subscriber(OrderUpdateCallback callback) {
std::lock_guard<std::mutex> lock(mutex);
const auto id = next_id_locked();
order_update_subscribers.emplace(id, std::move(callback));
return id;
}
void remove_order_update_subscriber(std::size_t id) {
std::lock_guard<std::mutex> lock(mutex);
order_update_subscribers.erase(id);
}
std::size_t add_status_subscriber(StatusCallback callback) {
bool current_connected = false;
std::size_t id = 0;
{
std::lock_guard<std::mutex> lock(mutex);
id = next_id_locked();
status_subscribers.emplace(id, callback);
current_connected = connected;
}
callback(current_connected);
return id;
}
void remove_status_subscriber(std::size_t id) {
std::lock_guard<std::mutex> lock(mutex);
status_subscribers.erase(id);
}
bool is_connected() const {
std::lock_guard<std::mutex> lock(mutex);
return connected;
}
void launch_new_order(const RakuraiSharedRuntime::NewOrder& order) {
std::lock_guard<std::mutex> lock(mutex);
if (!connected || trading_session == nullptr) {
throw std::runtime_error("Rakurai execution session is not connected");
}
trading_session->launch_new_order(order);
}
std::optional<PoolMetadata> find_pool_metadata(
api::Venue venue,
const std::string& short_pool_id) const {
std::lock_guard<std::mutex> lock(mutex);
const auto pool_it = short_pool_to_full_pool.find(short_pool_id);
if (pool_it == short_pool_to_full_pool.end()) {
return std::nullopt;
}
const auto metadata_it = pool_metadata.find(pool_it->second);
if (metadata_it == pool_metadata.end() ||
!venue_matches(venue, metadata_it->second.security.venue)) {
return std::nullopt;
}
return metadata_it->second;
}
void run() {
try {
nlohmann::json connector_config;
{
std::lock_guard<std::mutex> lock(mutex);
connector_config = *config;
}
auto dex_configs = parse_dex_configs(connector_config);
if (dex_configs.empty()) {
throw std::runtime_error("No Rakurai dex pools configured");
}
LOG(md, Info,
"Rakurai shared runtime parsed config. dex_configs:{} config:{}",
dex_configs.size(),
connector_config.dump());
{
std::lock_guard<std::mutex> lock(mutex);
expected_pool_ids.clear();
resolved_pool_ids.clear();
for (const auto& dex_config : dex_configs) {
for (const auto& pool_id : dex_config.interested_pools) {
expected_pool_ids.insert(bytes_to_hex(pool_id));
}
}
}
::rakurai::dex_connector::configuration::logging_config logging_config;
if (connector_config.contains("app_logging")) {
const auto& app_logging = connector_config.at("app_logging");
logging_config.severity =
app_logging.value("severity", logging_config.severity);
logging_config.is_console_enabled =
app_logging.value("console", logging_config.is_console_enabled);
logging_config.is_file_enabled =
app_logging.value("file", logging_config.is_file_enabled);
logging_config.queue_size =
app_logging.value("queuesize", logging_config.queue_size);
}
::rakurai::dex_connector::configuration::wallet wallet_config;
wallet_config.wallet_pub_key =
::rakurai::dex_connector::solanaAddressToBytes(
connector_config.at("wallet_pub").get<std::string>());
const auto& wallet_pvt = connector_config.at("wallet_pvt");
if (!wallet_pvt.is_array() ||
wallet_pvt.size() != wallet_config.wallet_pvt_key.size()) {
throw std::runtime_error(
"Rakurai wallet_pvt must contain 64 integer entries");
}
for (std::size_t i = 0; i < wallet_config.wallet_pvt_key.size(); ++i) {
wallet_config.wallet_pvt_key[i] =
wallet_pvt.at(i).get<std::uint8_t>();
}
std::list<::rakurai::dex_connector::configuration::uri_config> uris;
auto add_uri = [&](const char* md_key, const char* oms_key) {
::rakurai::dex_connector::configuration::uri_config uri;
uri.md_server_uri =
connector_config.at(md_key).get<std::string>();
uri.node_oms_uri =
connector_config.at(oms_key).get<std::string>();
uris.push_back(std::move(uri));
};
add_uri("md_server_uri_1", "node_oms_uri_1");
const bool cluster_mode =
connector_config.value("cluster_mode", false);
if (cluster_mode) {
add_uri("md_server_uri_2", "node_oms_uri_2");
}
const auto core = connector_config.value("core", 0U);
{
std::lock_guard<std::mutex> lock(mutex);
handler = std::make_unique<::rakurai::dex_connector::dex_handler>(
dex_configs,
wallet_config,
logging_config,
uris,
core,
cluster_mode,
&listener);
trading_session = std::make_unique<
::rakurai::dex_connector::trading_session_interface>(
handler->create_new_trading_session());
connected = false;
}
handler->run();
} catch (const std::exception& ex) {
LOG(md, Error, "Rakurai shared runtime failed: {}", std::string(ex.what()));
notify_md_error(ex.what());
}
{
std::lock_guard<std::mutex> lock(mutex);
connected = false;
}
notify_status(false);
}
void notify_status(bool is_connected_now) {
std::vector<StatusCallback> callbacks;
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto& [_, callback] : status_subscribers) {
callbacks.push_back(callback);
}
}
for (auto& callback : callbacks) {
callback(is_connected_now);
}
}
void notify_md_error(const std::string& message) {
std::vector<std::shared_ptr<md::IMarketDataListener>> listeners;
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto& [_, sub] : market_data_subscribers) {
listeners.push_back(sub->listener);
}
}
for (const auto& listener_ptr : listeners) {
if (!listener_ptr) {
continue;
}
md::MarketDataError error;
error.type = md::MarketDataError::DISCONNECTION;
error.message = message;
listener_ptr->on_error(error);
}
}
void load_aliases_locked() {
if (!short_pool_to_full_pool.empty()) {
return;
}
constexpr std::string_view kPoolAliasNamespace = "rakurai_pool_alias";
for (const auto& [ns, rows] : pool_alias_store.retrieve_all()) {
if (ns != kPoolAliasNamespace) {
continue;
}
for (const auto& [full_pool_id, alias] : rows) {
if (!alias.empty()) {
short_pool_to_full_pool.emplace(alias, full_pool_id);
}
}
}
}
std::string get_or_add_short_pool_id_locked(const std::string& pool_id) {
constexpr std::string_view kPoolAliasNamespace = "rakurai_pool_alias";
const auto [exists, rowid, _] =
pool_alias_store.retrieve_one(std::string(kPoolAliasNamespace), pool_id);
if (exists) {
const auto alias = std::to_string(rowid);
short_pool_to_full_pool.emplace(alias, pool_id);
return alias;
}
const auto new_id =
pool_alias_store.insert(std::string(kPoolAliasNamespace), pool_id, "");
const auto alias = std::to_string(new_id);
short_pool_to_full_pool.emplace(alias, pool_id);
return alias;
}
void on_pool_definition(
ConnectorVenue connector_venue,
const std::vector<ConnectorSecurity*>& securities) {
std::vector<std::shared_ptr<MarketDataSubscriber>> subscribers;
std::vector<PoolMetadata> touched;
bool should_notify_ready = false;
std::size_t resolved_pool_count = 0;
std::size_t expected_pool_count = 0;
{
std::lock_guard<std::mutex> lock(mutex);
load_aliases_locked();
for (const auto* security : securities) {
if (security == nullptr) {
continue;
}
const auto pool_id = bytes_to_hex(security->pool_id);
pool_metadata[pool_id] = PoolMetadata{
*security,
pool_id,
get_or_add_short_pool_id_locked(pool_id),
quantity_scale_from_decimals(security->token_a.decimals)};
resolved_pool_ids.insert(pool_id);
touched.push_back(pool_metadata.at(pool_id));
}
const bool has_trading_session = trading_session != nullptr;
const bool has_pool_definitions = !resolved_pool_ids.empty();
const bool all_expected_pools_resolved =
expected_pool_ids.empty()
? has_pool_definitions
: std::all_of(
expected_pool_ids.begin(),
expected_pool_ids.end(),
[this](const std::string& pool_id) {
return resolved_pool_ids.contains(pool_id);
});
if (!connected && has_trading_session && all_expected_pools_resolved) {
connected = true;
should_notify_ready = true;
resolved_pool_count = resolved_pool_ids.size();
expected_pool_count = expected_pool_ids.size();
}
for (const auto& [_, sub] : market_data_subscribers) {
if (venue_matches(sub->venue, connector_venue)) {
subscribers.push_back(sub);
}
}
}
if (should_notify_ready) {
LOG(md, Info,
"Rakurai shared runtime ready. resolved_pools:{} expected_pools:{}",
resolved_pool_count,
expected_pool_count);
notify_status(true);
}
for (const auto& sub : subscribers) {
replay_pool_definitions(*sub, touched);
}
}
void replay_pool_definitions(
MarketDataSubscriber& sub,
const std::vector<PoolMetadata>& metadata_list) {
std::lock_guard<std::mutex> lock(sub.mutex);
std::vector<const domain::Security*> emitted;
for (const auto& metadata : metadata_list) {
if (!venue_matches(sub.venue, metadata.security.venue)) {
continue;
}
const auto& security = metadata.security;
const auto& pool_id = metadata.full_pool_id;
const auto desc = build_security_desc(security, metadata.short_pool_id);
const auto symbol = token_display_name(security.token_a);
const auto currency = token_display_name(security.token_b);
if (sub.excluded_securities.contains(pool_id) ||
sub.excluded_securities.contains(metadata.short_pool_id) ||
sub.excluded_securities.contains(desc)) {
continue;
}
domain::Security emitted_security;
emitted_security.security = domain::SpotSecurity{};
emitted_security.security_common.venue = sub.venue;
emitted_security.security_common.exchange_sec_id = metadata.short_pool_id;
emitted_security.security_common.symbol = symbol;
emitted_security.security_common.currency = currency;
emitted_security.security_common.asset = symbol;
emitted_security.security_common.group =
connector_venue_name(security.venue);
emitted_security.security_common.desc = desc;
emitted_security.security_common.min_price_increment =
security.min_tick > 0.0 ? security.min_tick : 0.0;
emitted_security.security_common.min_quantity_increment =
static_cast<double>(security.min_trade_amount);
emitted_security.security_common.min_quantity =
static_cast<double>(security.min_trade_amount);
*emitted_security.security_common.id = sub.sec_id_store->get_or_add(
sub.venue, api::domain::SECURITY_TYPE_SPOT, pool_id);
auto [it, is_new] = sub.securities.try_emplace(
pool_id, std::make_unique<domain::Security>(std::move(emitted_security)));
if (!is_new) {
*it->second = std::move(emitted_security);
}
sub.pool_to_security_id[pool_id] = it->second->id();
sub.pool_quantity_scales[pool_id] = metadata.quantity_scale;
emitted.push_back(it->second.get());
}
if (!emitted.empty() && sub.listener) {
sub.listener->on_securities(emitted);
}
}
void on_book(
ConnectorVenue connector_venue,
const std::array<std::uint8_t, 32>& pool_id,
const std::vector<ConnectorBookLevel>& bids,
const std::vector<ConnectorBookLevel>& asks) {
std::vector<std::shared_ptr<MarketDataSubscriber>> subscribers;
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto& [_, sub] : market_data_subscribers) {
if (venue_matches(sub->venue, connector_venue)) {
subscribers.push_back(sub);
}
}
}
const auto pool_key = bytes_to_hex(pool_id);
for (const auto& sub : subscribers) {
std::lock_guard<std::mutex> lock(sub->mutex);
const auto it = sub->pool_to_security_id.find(pool_key);
if (it == sub->pool_to_security_id.end()) {
continue;
}
const auto quantity_scale_it = sub->pool_quantity_scales.find(pool_key);
const auto quantity_scale =
quantity_scale_it != sub->pool_quantity_scales.end()
? quantity_scale_it->second
: 1.0;
auto book = domain::Book::empty(it->second);
const auto bid_depth =
std::min<std::size_t>(sub->depth_limit, bids.size());
const auto ask_depth =
std::min<std::size_t>(sub->depth_limit, asks.size());
for (std::size_t i = 0; i < bid_depth; ++i) {
book.bids()[i] = to_book_level(bids[i], quantity_scale);
}
for (std::size_t i = 0; i < ask_depth; ++i) {
book.asks()[i] = to_book_level(asks[i], quantity_scale);
}
if (sub->listener) {
sub->listener->on_book(book);
}
}
}
void on_order_update(
::rakurai::dex_connector::order_entry::order_state order_state) {
std::vector<OrderUpdateCallback> callbacks;
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto& [_, callback] : order_update_subscribers) {
callbacks.push_back(callback);
}
}
for (auto& callback : callbacks) {
callback(order_state);
}
}
};
RakuraiSharedRuntime& RakuraiSharedRuntime::instance() {
static RakuraiSharedRuntime runtime;
return runtime;
}
RakuraiSharedRuntime::RakuraiSharedRuntime()
: me_(std::make_unique<PImpl>()) {}
RakuraiSharedRuntime::~RakuraiSharedRuntime() = default;
std::size_t RakuraiSharedRuntime::add_market_data_subscriber(
std::shared_ptr<md::IMarketDataListener> listener,
std::shared_ptr<persistence::IDGenerator> sec_id_store,
api::domain::Venue venue,
std::uint32_t num_depth_levels,
const std::vector<std::string>& excluded_securities) {
return me_->add_market_data_subscriber(
std::move(listener),
std::move(sec_id_store),
venue,
num_depth_levels,
excluded_securities);
}
void RakuraiSharedRuntime::remove_market_data_subscriber(std::size_t id) {
me_->remove_market_data_subscriber(id);
}
std::size_t RakuraiSharedRuntime::add_order_update_subscriber(
OrderUpdateCallback callback) {
return me_->add_order_update_subscriber(std::move(callback));
}
void RakuraiSharedRuntime::remove_order_update_subscriber(std::size_t id) {
me_->remove_order_update_subscriber(id);
}
std::size_t RakuraiSharedRuntime::add_status_subscriber(StatusCallback callback) {
return me_->add_status_subscriber(std::move(callback));
}
void RakuraiSharedRuntime::remove_status_subscriber(std::size_t id) {
me_->remove_status_subscriber(id);
}
void RakuraiSharedRuntime::ensure_started_from_file(
const std::string& config_path) {
me_->ensure_started(load_json_file(config_path), config_path);
}
void RakuraiSharedRuntime::ensure_started_from_json(
const nlohmann::json& config,
const std::string& config_label) {
me_->ensure_started(config, config_label);
}
bool RakuraiSharedRuntime::is_connected() const { return me_->is_connected(); }
void RakuraiSharedRuntime::launch_new_order(const NewOrder& order) {
me_->launch_new_order(order);
}
std::optional<RakuraiSharedRuntime::PoolMetadata>
RakuraiSharedRuntime::find_pool_metadata(
api::Venue venue,
const std::string& short_pool_id) const {
return me_->find_pool_metadata(venue, short_pool_id);
}
} // namespace bts::rakurai
@@ -0,0 +1,78 @@
#pragma once
#include "spark/api/domain/Venue.pb.h"
#include "spark/domain_model/Security.pb.h"
#include <dex_utils.h>
#include <order_entry_common.h>
#include <cstddef>
#include <functional>
#include <memory>
#include <nlohmann/json_fwd.hpp>
#include <optional>
#include <string>
#include <vector>
namespace bts::md {
class IMarketDataListener;
}
namespace bts::persistence {
class IDGenerator;
}
namespace bts::rakurai {
class RakuraiSharedRuntime {
public:
using OrderUpdate = ::rakurai::dex_connector::order_entry::order_state;
using NewOrder = ::rakurai::dex_connector::order_entry::new_order;
using OrderUpdateCallback = std::function<void(OrderUpdate)>;
using StatusCallback = std::function<void(bool)>;
struct PoolMetadata {
::rakurai::dex_connector::data_types::security security;
std::string full_pool_id;
std::string short_pool_id;
double quantity_scale = 1.0;
};
static RakuraiSharedRuntime& instance();
std::size_t add_market_data_subscriber(
std::shared_ptr<md::IMarketDataListener> listener,
std::shared_ptr<persistence::IDGenerator> sec_id_store,
api::domain::Venue venue,
std::uint32_t num_depth_levels,
const std::vector<std::string>& excluded_securities);
void remove_market_data_subscriber(std::size_t id);
std::size_t add_order_update_subscriber(OrderUpdateCallback callback);
void remove_order_update_subscriber(std::size_t id);
std::size_t add_status_subscriber(StatusCallback callback);
void remove_status_subscriber(std::size_t id);
void ensure_started_from_file(const std::string& config_path);
void ensure_started_from_json(
const nlohmann::json& config,
const std::string& config_label);
bool is_connected() const;
void launch_new_order(const NewOrder& order);
std::optional<PoolMetadata> find_pool_metadata(
api::Venue venue,
const std::string& short_pool_id) const;
private:
RakuraiSharedRuntime();
~RakuraiSharedRuntime();
RakuraiSharedRuntime(const RakuraiSharedRuntime&) = delete;
RakuraiSharedRuntime& operator=(const RakuraiSharedRuntime&) = delete;
struct PImpl;
std::unique_ptr<PImpl> me_;
};
} // namespace bts::rakurai
@@ -254,7 +254,7 @@ namespace mbts.spark {
add_col(
Columns.Price,
DefaultColumnHeaders.Price,
r => _get_row(r).security.spec_or_default().format(r.fill.LastPrice),
r => _get_row(r).security.spec_or_default().format(r.fill.AvgPrice),
new DoubleMinMax<FillTuple>(r => r.fill.LastPrice),
DataGridFilterKnownTemplates.DoubleMinMax,
fs => fs.price,
@@ -8,15 +8,13 @@ using bts.api;
using mbts.analytics;
using mbts;
namespace BCG
{
namespace BCG {
/// For each NMSheetSymbol in an NMSheet gets all the
/// data together (theos, position, vol change etc)
/// and fires a callback.
/// This is where all the data is gathered, the rest
/// is all access to this
public class NMSheetSingleOptionMainDataSource : IDisposable
{
public class NMSheetSingleOptionMainDataSource : IDisposable {
private ThreadedBlackboard _threaded_bb;
private readonly ITag<NMSheetDataRequest> _in_bb_main_sheet_request_tag;
private readonly ITag<ExchangeSecurityMap> _in_bb_secmap_tag =
@@ -26,7 +24,6 @@ namespace BCG
private readonly ITag<OptionTheoInterpolationData> _in_bb_precomputed_sparse_theos_tag;
private readonly ITag<IDictionary<NMSheetExpiration, double>> _in_bb_vol_shift_tag;
private readonly ITag<NMSheetSettings> _in_bb_sheet_settings_tag;
private readonly ITag<IEnumerable<ITradeLogRow>> _in_bb_trade_log_tag = SparkTags.TradeLogTag;
private readonly ITag<VolSurfaceNonVolInput> _in_exp_param_tag = SparkTags.VolSurfaceNonVolInputTag;
private readonly ITag<bool> _in_bb_main_sheet_visibility_tag;
private ITag<SparkTags.Nothing> _in_bb_onefive_value_tag = SparkTags.TC2Tags.VoidTag,
@@ -34,6 +31,7 @@ namespace BCG
_in_bb_symbol_theo_shift_tag = SparkTags.TC2Tags.VoidTag,
_in_bb_rollover_data_tag = SparkTags.TC2Tags.VoidTag,
_in_bb_presaved_aw2_theos_tag = SparkTags.TC2Tags.VoidTag,
_in_bb_position_tag = SparkTags.TC2Tags.VoidTag,
_in_bb_option_driver_detailed_price_tag = SparkTags.TC2Tags.VoidTag;
private readonly NMSheetSymbol _nmsheet_symbol;
private Action<Pair<NMSheetSymbol, NMSheetSingleOptionData>> _cb;
@@ -42,8 +40,7 @@ namespace BCG
private string _electronic_equivelant_symbol;
private NMSheetArchivedVolProvider _archived_vol_provider;
public NMSheetSingleOptionMainDataSource(object req_id, ThreadedBlackboard threaded_bb, NMSheetSymbol option, Action<Pair<NMSheetSymbol, NMSheetSingleOptionData>> callback)
{
public NMSheetSingleOptionMainDataSource(object req_id, ThreadedBlackboard threaded_bb, NMSheetSymbol option, Action<Pair<NMSheetSymbol, NMSheetSingleOptionData>> callback) {
_threaded_bb = threaded_bb;
_nmsheet_symbol = option;
_electronic_equivelant_symbol = option.option_name;
@@ -59,8 +56,7 @@ namespace BCG
_in_bb_sheet_settings_tag = SparkTags.TC2Tags.NMSheetSettingsTag(req_id.ToString());
_in_bb_main_sheet_visibility_tag = SparkTags.TC2Tags.NMSheetVisibilityTag(req_id.ToString());
_archived_vol_provider = new NMSheetArchivedVolProvider(req_id.ToString(), _threaded_bb, option,
() =>
{
() => {
_rollover_data = null;
_compute_and_fire();
});
@@ -80,7 +76,7 @@ namespace BCG
_threaded_bb.subscribe(_in_bb_rollover_data_tag);
_threaded_bb.subscribe(_in_bb_presaved_aw2_theos_tag);
_threaded_bb.subscribe(_in_exp_param_tag);
_threaded_bb.subscribe(_in_bb_trade_log_tag);
_threaded_bb.subscribe(_in_bb_position_tag);
_threaded_bb.subscribe(_in_bb_sheet_settings_tag);
_threaded_bb.subscribe(_in_bb_onefive_value_tag);
_threaded_bb.subscribe(_in_bb_qs_tag);
@@ -88,17 +84,15 @@ namespace BCG
_threaded_bb.subscribe(_in_bb_option_driver_detailed_price_tag);
}
private void _react_to_bb(object tag, object value)
{
private void _react_to_bb(object tag, object value) {
if (_in_bb_main_sheet_request_tag.Equals(tag) || _in_bb_secmap_tag.Equals(tag) || _in_bb_live_theos_tag.Equals(tag) || /*_in_bb_live_driver_bbo_tag.Equals(tag) ||*/ _in_bb_precomputed_sparse_theos_tag.Equals(tag) ||
_in_bb_vol_shift_tag.Equals(tag) || _in_bb_symbol_theo_shift_tag.Equals(tag) || _in_exp_param_tag.Equals(tag) || _in_bb_presaved_aw2_theos_tag.Equals(tag) ||
_in_bb_trade_log_tag.Equals(tag) || _in_bb_rollover_data_tag.Equals(tag) || _in_bb_sheet_settings_tag.Equals(tag) || _in_bb_qs_tag.Equals(tag) ||
_in_bb_position_tag.Equals(tag) || _in_bb_rollover_data_tag.Equals(tag) || _in_bb_sheet_settings_tag.Equals(tag) || _in_bb_qs_tag.Equals(tag) ||
_in_bb_main_sheet_visibility_tag.Equals(tag))
_compute_and_fire();
}
private NMSheetDriverData _get_driver(DriverMD d)
{
private NMSheetDriverData _get_driver(DriverMD d) {
double dr_pr = d.get_driver();
if (d.driver_security.HasValue)
return new NMSheetDriverData(d.driver_security.symbol_or_default(),
@@ -108,10 +102,8 @@ namespace BCG
return new NMSheetDriverData(String.Empty, Venue.Unset, DateTime.MinValue, dr_pr, dr_pr, false);
}
private void _compute_and_fire()
{
if (_is_main_sheet_active())
{
private void _compute_and_fire() {
if (_is_main_sheet_active()) {
var timer = Stopwatch.StartNew();
NMSheetDataRequest data_request = _threaded_bb.read_state(_in_bb_main_sheet_request_tag) ?? new NMSheetDataRequest(new List<Triplet<NMSheetExpiration, bool, double>>());
data_request = NMSheetDataRequest.restrict_for_symbol(data_request, _nmsheet_symbol);
@@ -120,8 +112,7 @@ namespace BCG
NMSheetSettings sheet_settings = _threaded_bb.read_state(_in_bb_sheet_settings_tag) ?? new NMSheetSettings();
ExpStrikeKeyedData<mbts.spark.TheoPoint> shared_live_theo_data_map = _threaded_bb.read_state(_in_bb_live_theos_tag);
if (data_request != null && live_driver_map != null && shared_live_theo_data_map != null)
{
if (data_request != null && live_driver_map != null && shared_live_theo_data_map != null) {
ExpStrikeKeyedData<mbts.spark.TheoPoint> output_theo_data_map = _limit_to_requested_securities(shared_live_theo_data_map, data_request, sheet_settings);
output_theo_data_map = _adjust_for_freezes(output_theo_data_map, data_request);
_add_vol_shift(output_theo_data_map);
@@ -147,17 +138,14 @@ namespace BCG
}
}
private bool _is_main_sheet_active()
{
private bool _is_main_sheet_active() {
return _threaded_bb.read_state(_in_bb_main_sheet_visibility_tag, true);
}
private ExpStrikeKeyedData<mbts.spark.TheoPoint> _limit_to_requested_securities(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data, NMSheetDataRequest data_request, NMSheetSettings sheet_settings)
{
private ExpStrikeKeyedData<mbts.spark.TheoPoint> _limit_to_requested_securities(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data, NMSheetDataRequest data_request, NMSheetSettings sheet_settings) {
HashSet<DateTime> selected_exp_map = new HashSet<DateTime>();
HashSet<double> selected_strike_map = new HashSet<double>(sheet_settings.setting_unselected_strikes);
foreach (Triplet<NMSheetExpiration, bool, double> t in data_request.expiration_request_list)
{
foreach (Triplet<NMSheetExpiration, bool, double> t in data_request.expiration_request_list) {
NMSheetExpiration nm_exp = t.first;
if (nm_exp != null && nm_exp.option_name == _nmsheet_symbol.option_name)
selected_exp_map.Add(nm_exp.option_expiration);
@@ -165,14 +153,12 @@ namespace BCG
return theo_data.clone_filtered_set(selected_exp_map, true, selected_strike_map, false);
}
private ExpStrikeKeyedData<Triplet<mbts.spark.TheoPoint, uint, uint>> _attach_sec_keys(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_map)
{
private ExpStrikeKeyedData<Triplet<mbts.spark.TheoPoint, uint, uint>> _attach_sec_keys(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_map) {
theo_map = theo_map ?? new ExpStrikeKeyedData<mbts.spark.TheoPoint>();
ExchangeSecurityMap sec_map = _threaded_bb.read_state(_in_bb_secmap_tag);
if (sec_map != null)
return sec_map.attach_sec_keys(theo_map, _electronic_equivelant_symbol, _nmsheet_symbol.option_exchange);
else
{
else {
ExpStrikeKeyedData<Triplet<mbts.spark.TheoPoint, uint, uint>> rv = new ExpStrikeKeyedData<Triplet<mbts.spark.TheoPoint, uint, uint>>();
foreach (Triplet<DateTime, double, mbts.spark.TheoPoint> t in theo_map.get_data_linearly())
rv.set_data(t.first, t.second, new Triplet<mbts.spark.TheoPoint, uint, uint>(t.third, 0, 0));
@@ -180,8 +166,7 @@ namespace BCG
}
}
private ExpStrikeKeyedData<mbts.spark.TheoPoint> _adjust_for_freezes(ExpStrikeKeyedData<mbts.spark.TheoPoint> live_theo_map, NMSheetDataRequest data_request)
{
private ExpStrikeKeyedData<mbts.spark.TheoPoint> _adjust_for_freezes(ExpStrikeKeyedData<mbts.spark.TheoPoint> live_theo_map, NMSheetDataRequest data_request) {
return live_theo_map;
// TODO
// ExpStrikeKeyedData<mbts.spark.TheoPoint> rv = new ExpStrikeKeyedData<mbts.spark.TheoPoint>();
@@ -207,11 +192,9 @@ namespace BCG
private Dictionary<DateTime, NMSheetDriverData> _get_option_exp_driver_data_map(NMSheetDataRequest data_request,
ExpStrikeKeyedData<mbts.spark.TheoPoint> output_theo_data_map, ExpStrikeKeyedData<mbts.spark.TheoPoint> shared_live_theo_data_map,
Dictionary<DateTime, Triplet<(string, Venue), DateTime, double>> live_driver_map)
{
Dictionary<DateTime, Triplet<(string, Venue), DateTime, double>> live_driver_map) {
Dictionary<DateTime, NMSheetDriverData> rv = new Dictionary<DateTime, NMSheetDriverData>();
foreach (Triplet<NMSheetExpiration, bool, double> theo_request in data_request.expiration_request_list)
{
foreach (Triplet<NMSheetExpiration, bool, double> theo_request in data_request.expiration_request_list) {
DateTime option_expiration = theo_request.first.option_expiration;
/// live driver
var live_driver_info = live_driver_map.GetValueOrDefault(option_expiration)
@@ -225,11 +208,9 @@ namespace BCG
private Dictionary<DateTime, NMSheetDriverData>
_get_option_exp_driver_data_map(NMSheetDataRequest data_request,
OptionDriverPriceMap drivers)
{
OptionDriverPriceMap drivers) {
var rv = new Dictionary<DateTime, NMSheetDriverData>();
foreach (var tr in data_request.expiration_request_list)
{
foreach (var tr in data_request.expiration_request_list) {
NMSheetExpiration nm_exp = tr.first;
SecurityExpiration exp = tr.first.to_sec_exp();
drivers.exp_price_map.TryGetValue(exp, out DriverMD dr_md);
@@ -241,8 +222,7 @@ namespace BCG
return rv;
}
private static double _get_driverprice_for_expiration(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data_map, DateTime option_expiration)
{
private static double _get_driverprice_for_expiration(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data_map, DateTime option_expiration) {
IDictionary<double, mbts.spark.TheoPoint> exp_data = theo_data_map.get_data_for_single_exp(option_expiration);
if (exp_data != null)
foreach (mbts.spark.TheoPoint tp in exp_data.Values)
@@ -250,8 +230,7 @@ namespace BCG
return Double.NaN;
}
private IDictionary<DateTime, ATMData> _get_atm_data(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data)
{
private IDictionary<DateTime, ATMData> _get_atm_data(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data) {
NMSheetSettings sheet_settings = _threaded_bb.read_state(_in_bb_sheet_settings_tag) ?? new NMSheetSettings();
VolSurfaceNonVolInput vs_nvinp = _threaded_bb.read_state(_in_exp_param_tag);
var option_exp_roll_data = vs_nvinp
@@ -269,43 +248,17 @@ namespace BCG
}
OptionContractKeyedData<PNLRollOverDataUnit> _rollover_data = null;
private OptionContractKeyedData<PNLRollOverDataUnit> _get_rollover_data()
{
private OptionContractKeyedData<PNLRollOverDataUnit> _get_rollover_data() {
if (_rollover_data == null)
_rollover_data = _archived_vol_provider.get_all_data();
return _rollover_data;
}
private ExpStrikeKeyedData<Tuple<double, double>> _get_position_map()
{
var sheet_settings = _threaded_bb.read_state(_in_bb_sheet_settings_tag);
var risk_account_text = sheet_settings?.setting_layout?.risk_account ?? string.Empty;
var risk_accounts = risk_account_text
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(a => a.Trim())
.Where(a => !string.IsNullOrEmpty(a))
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var trades = _threaded_bb.read_state(_in_bb_trade_log_tag) ?? Enumerable.Empty<ITradeLogRow>();
var option_trades = trades.Where(t =>
t.is_active &&
t.security_type == SecurityType.Option &&
t.symbol == _nmsheet_symbol.option_name &&
t.venue == _nmsheet_symbol.option_exchange &&
(risk_accounts.Count == 0 || risk_accounts.Contains(t.account)));
var pos_data = SecurityPositionData.from_trades(option_trades);
var rv = new ExpStrikeKeyedData<Tuple<double, double>>();
foreach (var (sec_exp, strike_map) in pos_data.option_pos_map)
{
foreach (var (strike, strike_pos) in strike_map)
{
rv.set_data(sec_exp.expiration, strike, Tuple.Create(strike_pos.call, strike_pos.put));
}
}
return rv;
private ExpStrikeKeyedData<Tuple<double, double>> _get_position_map() {
return new ExpStrikeKeyedData<Tuple<double, double>>();
}
private double _get_onefive()
{
private double _get_onefive() {
// object dp_obj = _threaded_bb.sec_thread_retrieve_from_cache(_in_bb_onefive_value_tag);
// if (dp_obj != null) {
// DataPacket dp = dp_obj as DataPacket;
@@ -317,29 +270,22 @@ namespace BCG
#region Vol Shift
private void _add_vol_shift(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data)
{
private void _add_vol_shift(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data) {
var vshift_map = _threaded_bb.read_state(_in_bb_vol_shift_tag);
if (vshift_map != null)
add_vol_shift(_nmsheet_symbol, theo_data, vshift_map);
}
internal static void add_vol_shift(NMSheetSymbol apply_to_symbol, ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data, IDictionary<NMSheetExpiration, double> exp_shift_map)
{
foreach (KeyValuePair<NMSheetExpiration, double> p in exp_shift_map)
{
if (p.Key.nmsheet_symbol.Equals(apply_to_symbol))
{
internal static void add_vol_shift(NMSheetSymbol apply_to_symbol, ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data, IDictionary<NMSheetExpiration, double> exp_shift_map) {
foreach (KeyValuePair<NMSheetExpiration, double> p in exp_shift_map) {
if (p.Key.nmsheet_symbol.Equals(apply_to_symbol)) {
DateTime option_expiration = p.Key.option_expiration;
double shift = p.Value;
if (shift != 0)
{
if (shift != 0) {
IDictionary<double, mbts.spark.TheoPoint> exp_theos = theo_data.get_data_for_single_exp(option_expiration);
if (exp_theos != null)
{
if (exp_theos != null) {
Dictionary<double, mbts.spark.TheoPoint> shifted_exp_theos = new Dictionary<double, mbts.spark.TheoPoint>();
foreach (KeyValuePair<double, mbts.spark.TheoPoint> strike_tp in exp_theos)
{
foreach (KeyValuePair<double, mbts.spark.TheoPoint> strike_tp in exp_theos) {
mbts.spark.TheoPoint new_tp = (mbts.spark.TheoPoint)strike_tp.Value.Clone();
add_vol_shift(new_tp, shift);
shifted_exp_theos[strike_tp.Key] = new_tp;
@@ -351,8 +297,7 @@ namespace BCG
}
}
internal static void add_vol_shift(mbts.spark.TheoPoint v, double shift)
{
internal static void add_vol_shift(mbts.spark.TheoPoint v, double shift) {
v.call += v.cvega * shift;
v.put += v.pvega * shift;
}
@@ -360,8 +305,7 @@ namespace BCG
#endregion
#region IDisposable
public void Dispose()
{
public void Dispose() {
_threaded_bb.data_callback -= _react_to_bb;
}
#endregion
@@ -288,7 +288,12 @@ namespace bts.websocket_core {
}
// If the message is the end of the message, handle the message and reset the request string
if (result.EndOfMessage) {
gtwy.HandleMessage(new WebsocketIncomingMessage(id, request));
System.Console.WriteLine($"WebSocketHandler received message from {id}: {request}");
try {
gtwy.HandleMessage(new WebsocketIncomingMessage(id, request));
} catch (Exception ex) {
System.Console.WriteLine($"WebSocketHandler failed to dispatch message from {id}: {ex}");
}
request = "";
}
}
@@ -298,6 +303,9 @@ namespace bts.websocket_core {
var log_txt = $"WebsocketGateway.ProcessWebSocketRequestAsync connection closed Error: {ex_str}";
gtwy.HandleDisconnection(id, log_txt);
} catch (OperationCanceledException) {//not sure about this, read that this should just be ignored
} catch (Exception ex) {
System.Console.WriteLine($"WebSocketHandler.ProcessWebSocketRequestAsync failed for {id}: {ex}");
gtwy.HandleDisconnection(id, ex.Message);
}
}
/// <summary>
@@ -15,6 +15,8 @@ namespace bts.websocket_core {
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using mbts.config;
using System.Collections.Concurrent;
using System.Reactive.Linq;
using System.Text.Json;
@@ -30,6 +32,7 @@ namespace bts.websocket_core {
readonly ISecurityResolver securityResolver;
IReadOnlyDictionary<uint, ExecutionStrategyImmutable> synthStrategies = new Dictionary<uint, ExecutionStrategyImmutable>(0);
IDisposable? synthStrategiesSub;
static readonly TimeSpan SyntheticSpreadCreateTimeout = TimeSpan.FromSeconds(15);
public WebsocketDataSync(Spark.Api.IServiceProvider service_ctx, IMarketDataClient mdClient, ISecurityResolver securityResolver) {
_service_ctx = service_ctx;
@@ -225,17 +228,478 @@ namespace bts.websocket_core {
// broadcast.Start();
}
private uint get_security(Venue v, string sec_name) {
var securites = _securities;
if (securites.Values.TryGetFirst(sec =>
(
sec.Exchange == v &&
sec.ToString() == sec_name),
out var sec
)) {
return sec.Id;
private readonly struct SyntheticSpreadLegRequest {
public readonly Venue Exchange;
public readonly string ExchangeSecurityName;
public readonly double Quantity;
public readonly double PriceMultiplier;
public readonly double LegDelta;
public readonly Side Side;
public readonly bool IsActiveQuoting;
public readonly bool EnableHedging;
public readonly LegSpreaderParams.Types.MarketDataType MarketDataType;
public SyntheticSpreadLegRequest(
Venue exchange,
string exchangeSecurityName,
double quantity,
double priceMultiplier,
double legDelta,
Side side,
bool isActiveQuoting,
bool enableHedging,
LegSpreaderParams.Types.MarketDataType marketDataType) {
Exchange = exchange;
ExchangeSecurityName = exchangeSecurityName;
Quantity = quantity;
PriceMultiplier = priceMultiplier;
LegDelta = legDelta;
Side = side;
IsActiveQuoting = isActiveQuoting;
EnableHedging = enableHedging;
MarketDataType = marketDataType;
}
throw new ArgumentException($"{sec_name} at {v} not found in securites");
}
private static string SanitizeIdentifier(string value) =>
string.Concat(value.Select(ch => char.IsLetterOrDigit(ch) ? ch : '_'));
private static bool TryGetOptionalString(
JsonObject parameters,
string key,
out string? value,
out string message) {
value = null;
if (!WebsocketJsonParsers.TryParse<WebsocketJsonParsers.StringWrapper>(
parameters,
key,
out WebsocketJsonParsers.StringWrapper? wrapper,
out message)) {
return false;
}
value = wrapper?.Val;
return true;
}
private static bool TryGetRequiredString(
JsonObject parameters,
string key,
out string value,
out string message) {
value = string.Empty;
if (!TryGetOptionalString(parameters, key, out string? parsed, out message) ||
string.IsNullOrWhiteSpace(parsed)) {
message = string.IsNullOrWhiteSpace(message) ? $"{key} is required" : message;
return false;
}
value = parsed.Trim();
return true;
}
private static bool TryGetOptionalDouble(
JsonObject parameters,
string key,
double defaultValue,
out double value,
out string message) {
value = defaultValue;
if (!WebsocketJsonParsers.TryParse<double>(
parameters,
key,
out double? parsed,
out message)) {
return false;
}
value = parsed ?? defaultValue;
return true;
}
private static bool TryGetOptionalBool(
JsonObject parameters,
string key,
bool defaultValue,
out bool value,
out string message) {
value = defaultValue;
if (!WebsocketJsonParsers.TryParse<bool>(
parameters,
key,
out bool? parsed,
out message)) {
return false;
}
value = parsed ?? defaultValue;
return true;
}
private static bool TryGetOptionalEnum<TEnum>(
JsonObject parameters,
string key,
TEnum defaultValue,
out TEnum value,
out string message) where TEnum : struct {
value = defaultValue;
if (!WebsocketJsonParsers.TryParse<TEnum>(
parameters,
key,
out TEnum? parsed,
out message)) {
return false;
}
value = parsed ?? defaultValue;
return true;
}
private static bool TryGetRequiredVenue(
JsonObject parameters,
string key,
out Venue value,
out string message) {
value = default;
if (!WebsocketJsonParsers.TryParse<Venue>(
parameters,
key,
out Venue? parsed,
out message) ||
parsed is null) {
message = string.IsNullOrWhiteSpace(message) ? $"{key} is required" : message;
return false;
}
value = parsed.Value;
return true;
}
private static bool IsWildcardSecurityName(string? value) =>
string.IsNullOrWhiteSpace(value) || value.Trim() == "*";
private static bts.config.SyntheticSpreadPreset? ResolvePreset(
bts.config.SyntheticSpreadConfig config,
Venue exchange,
string exchangeSecurityName) {
var normalized = exchangeSecurityName.Trim();
return config.Preset.FirstOrDefault(p =>
p.FirstLegExchange == exchange &&
string.Equals(p.FirstLegExchangeSecurityName, normalized, StringComparison.OrdinalIgnoreCase))
?? config.Preset.FirstOrDefault(p =>
p.FirstLegExchange == exchange &&
IsWildcardSecurityName(p.FirstLegExchangeSecurityName))
?? config.Preset.FirstOrDefault(p =>
p.FirstLegExchange == Venue.Unset &&
IsWildcardSecurityName(p.FirstLegExchangeSecurityName));
}
private static double ResolveDouble(double requested, double configured, double fallback) =>
requested != 0 ? requested : (configured != 0 ? configured : fallback);
private static uint ResolveUInt32(uint requested, uint configured, uint fallback) =>
requested != 0 ? requested : (configured != 0 ? configured : fallback);
private static string BuildSyntheticName(
string? requestedName,
string _configuredName,
ContractLeg leg1,
ContractLeg leg2) {
if (!string.IsNullOrWhiteSpace(requestedName)) {
return requestedName.Trim();
}
var leg1Security = new SecurityImmutable(leg1.Security);
var leg2Security = new SecurityImmutable(leg2.Security);
var leg1Desc = leg1Security.security_desc().ToLowerInvariant();
var leg2Desc = leg2Security.security_desc().ToLowerInvariant();
var leg1Exchange = leg1Security.Exchange.ToString().ToLowerInvariant();
var leg2Exchange = leg2Security.Exchange.ToString().ToLowerInvariant();
return $"{leg1Exchange}:{leg1Desc}_{leg2Exchange}:{leg2Desc}";
}
private static string BuildSyntheticSymbol(
string? requestedSymbol,
string configuredSymbol,
string firstLegSymbol,
string syntheticName) {
if (!string.IsNullOrWhiteSpace(requestedSymbol)) {
return requestedSymbol.Trim();
}
if (!string.IsNullOrWhiteSpace(configuredSymbol)) {
return configuredSymbol.Trim();
}
if (!string.IsNullOrWhiteSpace(firstLegSymbol)) {
return firstLegSymbol.Trim();
}
return SanitizeIdentifier(syntheticName);
}
private static ContractSpecification BuildContractSpec(
double tickSize,
double cashPerPoint,
double priceMultiplier,
uint mainFraction,
uint subFraction,
uint priceDisplayFormat,
bool supportsFractionalQuantity) {
var specification = new ContractSpecification {
LowTick = tickSize,
HighTick = tickSize,
PriceBreak = double.MaxValue,
CashPerPoint = cashPerPoint,
PriceMultiplier = priceMultiplier,
MainFraction = mainFraction,
SubFraction = subFraction,
PriceDisplayFormat = priceDisplayFormat
};
if (supportsFractionalQuantity) {
specification.QuantityInfos.Add(new ContractQuantityInfo {
MinValue = 0.00001,
MaxValue = double.PositiveInfinity,
TickUnit = 1,
TickBase = 100000,
MaxPrice = float.MaxValue
});
}
return specification;
}
private static bool SupportsFractionalQuantity(SecurityImmutable security) =>
security.HasContractSpec && security.ContractSpec.QuantityInfos.Any(info =>
info.TickBase > 0 && info.TickUnit > 0 && info.TickUnit < info.TickBase);
private static ContractLeg BuildLeg(
SecurityImmutable security,
SyntheticSpreadLegRequest leg) =>
new ContractLeg {
Security = security.CloneToMutable(),
Quantity = leg.Quantity,
PriceMultiplier = leg.PriceMultiplier,
LegDelta = leg.LegDelta,
Side = leg.Side
};
private static LegSpreaderParams BuildLegParams(
double leanQuantityRatio,
SyntheticSpreadLegRequest leg) =>
new LegSpreaderParams {
IsActiveQuoting = leg.IsActiveQuoting,
EnableHedging = leg.EnableHedging,
MarketDataType = leg.MarketDataType,
QuoteParams = new QuoteParams {
LeanQuantityRatio = leanQuantityRatio
},
PreHedgeParams = new PreHedgeParams(),
PostHedgeParams = new PostHedgeParams()
};
private static async Task<T> WaitForSyntheticSpreadOperation<T>(Task<T> operation, string operationName) {
try {
return await operation.WaitAsync(SyntheticSpreadCreateTimeout);
} catch (TimeoutException) {
throw new TimeoutException(
$"{operationName} timed out after {SyntheticSpreadCreateTimeout.TotalSeconds:0} seconds");
}
}
private static bool TryGetSyntheticSpreadLeg(
JsonObject parameters,
string legPrefix,
bts.config.SyntheticSpreadLegDefaults defaults,
Side fallbackSide,
out SyntheticSpreadLegRequest leg,
out string message) {
leg = default;
if (!TryGetRequiredVenue(parameters, $"{legPrefix}_exchange", out var exchange, out message) ||
!TryGetRequiredString(parameters, $"{legPrefix}_exchange_security_name", out var secName, out message)) {
return false;
}
double quantity = defaults.Quantity != 0 ? defaults.Quantity : 1.0;
double priceMultiplier = defaults.PriceMultiplier != 0 ? defaults.PriceMultiplier : 1.0;
double legDelta = defaults.LegDelta;
bool isActiveQuoting = defaults.IsActiveQuoting;
bool enableHedging = defaults.EnableHedging;
uint marketDataTypeValue = defaults.MarketDataType;
var side = defaults.Side != Side.Undefined ? defaults.Side : fallbackSide;
if (!TryGetOptionalDouble(parameters, $"{legPrefix}_quantity", quantity, out quantity, out message) ||
!TryGetOptionalDouble(parameters, $"{legPrefix}_price_multiplier", priceMultiplier, out priceMultiplier, out message) ||
!TryGetOptionalDouble(parameters, $"{legPrefix}_leg_delta", legDelta, out legDelta, out message) ||
!TryGetOptionalBool(parameters, $"{legPrefix}_is_active_quoting", isActiveQuoting, out isActiveQuoting, out message) ||
!TryGetOptionalBool(parameters, $"{legPrefix}_enable_hedging", enableHedging, out enableHedging, out message) ||
!TryGetOptionalEnum(parameters, $"{legPrefix}_market_data_type", marketDataTypeValue, out marketDataTypeValue, out message) ||
!TryGetOptionalEnum(parameters, $"{legPrefix}_side", side, out side, out message)) {
return false;
}
leg = new SyntheticSpreadLegRequest(
exchange,
secName,
quantity,
priceMultiplier,
legDelta,
side,
isActiveQuoting,
enableHedging,
(LegSpreaderParams.Types.MarketDataType)marketDataTypeValue);
return true;
}
private bool TryResolveSecurity(
Venue exchange,
string exchangeSecurityName,
out SecurityImmutable security,
out string message) {
if (_securities.Values.TryGetFirst(sec =>
sec.Exchange == exchange &&
sec.ToString() == exchangeSecurityName,
out security)) {
message = string.Empty;
return true;
}
message = $"{exchangeSecurityName} at {exchange} not found in securities";
return false;
}
private async Task<(bool success, uint? id, string status)> CreateSyntheticSpreadAsync(WebsocketRequest req) {
if (req.parameters == null) {
return (false, null, "parameters not present");
}
if (_synth_sec_client is null) {
return (false, null, "synthetic security client is not initialized");
}
var config = Configuration.get<bts.config.SyntheticSpreadConfig>();
if (!TryGetRequiredVenue(req.parameters, "leg1_exchange", out var leg1Exchange, out var message) ||
!TryGetRequiredString(req.parameters, "leg1_exchange_security_name", out var leg1SecurityName, out message) ||
!TryGetRequiredVenue(req.parameters, "leg2_exchange", out var leg2Exchange, out message) ||
!TryGetRequiredString(req.parameters, "leg2_exchange_security_name", out var leg2SecurityName, out message)) {
return (false, null, message);
}
var preset = ResolvePreset(config, leg1Exchange, leg1SecurityName);
if (preset is null) {
return (false, null, $"No synthetic spread preset found for {leg1Exchange}:{leg1SecurityName}");
}
string? requestedName = null;
string? requestedSymbol = null;
string? requestedCurrency = null;
double requestedTickSize = 0;
double requestedCashPerPoint = 0;
double requestedPriceMultiplier = 0;
uint requestedMainFraction = 0;
uint requestedSubFraction = 0;
uint requestedPriceDisplayFormat = 0;
uint requestedPriceCalcValue = preset.PriceCalc;
bool requestedCancelHedgesOnCancel = preset.CancelHedgesOnCancel;
uint requestedHedgePricingValue = preset.HedgePricing;
uint requestedSpreaderTypeValue = preset.SpreaderType;
double requestedLeanQuantityRatio = 0;
if (!TryGetOptionalString(req.parameters, "name", out requestedName, out message) ||
!TryGetOptionalString(req.parameters, "symbol", out requestedSymbol, out message) ||
!TryGetOptionalString(req.parameters, "currency", out requestedCurrency, out message) ||
!TryGetOptionalDouble(req.parameters, "tick_size", preset.TickSize != 0 ? preset.TickSize : config.DefaultTickSize, out requestedTickSize, out message) ||
!TryGetOptionalDouble(req.parameters, "cash_per_point", preset.CashPerPoint != 0 ? preset.CashPerPoint : config.DefaultCashPerPoint, out requestedCashPerPoint, out message) ||
!TryGetOptionalDouble(req.parameters, "price_multiplier", preset.PriceMultiplier != 0 ? preset.PriceMultiplier : config.DefaultPriceMultiplier, out requestedPriceMultiplier, out message) ||
!TryGetOptionalEnum(req.parameters, "main_fraction", 1U, out requestedMainFraction, out message) ||
!TryGetOptionalEnum(req.parameters, "sub_fraction", 100000U, out requestedSubFraction, out message) ||
!TryGetOptionalEnum(req.parameters, "price_display_format", (preset.PriceDisplayFormat != 0 ? preset.PriceDisplayFormat : config.DefaultPriceDisplayFormat), out requestedPriceDisplayFormat, out message) ||
!TryGetOptionalEnum(req.parameters, "price_calc", requestedPriceCalcValue, out requestedPriceCalcValue, out message) ||
!TryGetOptionalBool(req.parameters, "cancel_hedges_on_cancel", requestedCancelHedgesOnCancel, out requestedCancelHedgesOnCancel, out message) ||
!TryGetOptionalEnum(req.parameters, "hedge_pricing", requestedHedgePricingValue, out requestedHedgePricingValue, out message) ||
!TryGetOptionalEnum(req.parameters, "spreader_type", requestedSpreaderTypeValue, out requestedSpreaderTypeValue, out message) ||
!TryGetOptionalDouble(req.parameters, "lean_quantity_ratio", preset.LeanQuantityRatio != 0 ? preset.LeanQuantityRatio : config.DefaultLeanQuantityRatio, out requestedLeanQuantityRatio, out message)) {
return (false, null, message);
}
var leg2Defaults = preset.Leg2 ?? new bts.config.SyntheticSpreadLegDefaults();
leg2Defaults.PriceMultiplier = -1;
if (!TryGetSyntheticSpreadLeg(req.parameters, "leg1", preset.Leg1 ?? new bts.config.SyntheticSpreadLegDefaults(), Side.Buy, out var leg1, out message) ||
!TryGetSyntheticSpreadLeg(req.parameters, "leg2", leg2Defaults, Side.Sell, out var leg2, out message)) {
return (false, null, message);
}
if (!TryResolveSecurity(leg1.Exchange, leg1.ExchangeSecurityName, out var sec1, out message)) {
return (false, null, message);
}
if (!TryResolveSecurity(leg2.Exchange, leg2.ExchangeSecurityName, out var sec2, out message)) {
return (false, null, message);
}
var builtLeg1 = BuildLeg(sec1, leg1);
var builtLeg2 = BuildLeg(sec2, leg2);
string resolvedName = BuildSyntheticName(requestedName, preset.Name, builtLeg1, builtLeg2);
string resolvedSymbol = BuildSyntheticSymbol(requestedSymbol, preset.Symbol, sec1.Symbol, resolvedName);
string resolvedCurrency = !string.IsNullOrWhiteSpace(requestedCurrency)
? requestedCurrency.Trim()
: !string.IsNullOrWhiteSpace(preset.Currency)
? preset.Currency.Trim()
: !string.IsNullOrWhiteSpace(config.DefaultCurrency)
? config.DefaultCurrency.Trim()
: sec1.Currency;
double resolvedTickSize = ResolveDouble(requestedTickSize, preset.TickSize != 0 ? preset.TickSize : config.DefaultTickSize, sec1.ContractSpec.LowTick);
double resolvedCashPerPoint = ResolveDouble(requestedCashPerPoint, preset.CashPerPoint != 0 ? preset.CashPerPoint : config.DefaultCashPerPoint, sec1.ContractSpec.CashPerPoint);
double resolvedPriceMultiplier = ResolveDouble(requestedPriceMultiplier, preset.PriceMultiplier != 0 ? preset.PriceMultiplier : config.DefaultPriceMultiplier, sec1.ContractSpec.PriceMultiplier);
uint resolvedMainFraction = ResolveUInt32(requestedMainFraction, 1, sec1.ContractSpec.MainFraction);
uint resolvedSubFraction = ResolveUInt32(requestedSubFraction, 100000, sec1.ContractSpec.SubFraction);
uint resolvedPriceDisplayFormat = ResolveUInt32(requestedPriceDisplayFormat, preset.PriceDisplayFormat != 0 ? preset.PriceDisplayFormat : config.DefaultPriceDisplayFormat, sec1.ContractSpec.PriceDisplayFormat);
var resolvedPriceCalc = (SyntheticContract.Types.PriceCalc)requestedPriceCalcValue;
var resolvedCancelHedgesOnCancel = requestedCancelHedgesOnCancel;
var resolvedHedgePricing = (ExecutionStrategy.Types.HedgePricing)requestedHedgePricingValue;
var resolvedSpreaderType = (ExecutionStrategy.Types.SpreaderType)requestedSpreaderTypeValue;
var resolvedLeanQuantityRatio = requestedLeanQuantityRatio;
bool supportsFractionalQuantity =
SupportsFractionalQuantity(sec1) || SupportsFractionalQuantity(sec2);
var security = new Security {
Exchange = Venue.Bts,
Symbol = resolvedSymbol,
Currency = resolvedCurrency,
ContractSpec = BuildContractSpec(
resolvedTickSize,
resolvedCashPerPoint,
resolvedPriceMultiplier,
resolvedMainFraction,
resolvedSubFraction,
resolvedPriceDisplayFormat,
supportsFractionalQuantity),
Synthetic = new SyntheticContract {
Name = resolvedName,
PriceCalc = resolvedPriceCalc
}
};
security.Synthetic.Legs.Add(builtLeg1);
security.Synthetic.Legs.Add(builtLeg2);
uint securityKey;
try {
bts.Log.Gen.info($"CREATE_SYNTHETIC_SPREAD creating synthetic '{resolvedName}'");
securityKey = await WaitForSyntheticSpreadOperation(
Synth_Sec_Client.create_synthetic_security(new SecurityImmutable(security)),
"create_synthetic_security");
} catch (Exception ex) {
return (false, null, ex.Message);
}
var strategy = new ExecutionStrategy {
CancelHedgesOnCancel = resolvedCancelHedgesOnCancel,
HedgePricing = resolvedHedgePricing,
SpreaderType = resolvedSpreaderType
};
strategy.LegParamsMap[sec1.Id] = BuildLegParams(resolvedLeanQuantityRatio, leg1);
strategy.LegParamsMap[sec2.Id] = BuildLegParams(resolvedLeanQuantityRatio, leg2);
try {
bts.Log.Gen.info($"CREATE_SYNTHETIC_SPREAD updating strategy for security_key {securityKey}");
var status = await WaitForSyntheticSpreadOperation(
Synth_Sec_Client.update_synthetic_strategy(securityKey, new ExecutionStrategyImmutable(strategy)),
"update_synthetic_strategy");
if (status != null) {
return (false, null, status);
}
} catch (Exception ex) {
return (false, null, ex.Message);
}
return (true, securityKey, "OK");
}
public void handle_connection(WebsocketClientID client_id,
WebsocketIncomingMessage? optional_message) {
@@ -264,10 +728,10 @@ namespace bts.websocket_core {
handle_incorrect_request(id, message.sender_client_id, error);
return;
}
handle_request(req);
var log_txt = $"WebsocketDataSync.handle_client_message message: {req.type}";
var log_txt = $"WebsocketDataSync.handle_client_message message type: {req.type}";
bts.Log.Gen.info(log_txt);
System.Console.WriteLine(log_txt);
handle_request(req);
}
public void handle_request(WebsocketRequest req) {
@@ -296,6 +760,10 @@ namespace bts.websocket_core {
handle_set_theos(req);
break;
}
case WebsocketRequest.REQUEST_TYPES.CREATE_SYNTHETIC_SPREAD: {
handle_create_synthetic_spread(req);
break;
}
case WebsocketRequest.REQUEST_TYPES.GET: {
handle_get(req);
break;
@@ -325,6 +793,19 @@ namespace bts.websocket_core {
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_get(WebsocketRequest req) { await HandleRestRequest(req,WebsocketRestHandler.Get);}
public async void handle_create_synthetic_spread(WebsocketRequest req) {
try {
System.Console.WriteLine($"CREATE_SYNTHETIC_SPREAD request received id: {req.id}");
var (success, id, status) = await CreateSyntheticSpreadAsync(req);
if (!success) {
handle_incorrect_request(req, status);
return;
}
handle_correct_request(req.id, status, new { security_key = id }, req.client_id);
} catch (Exception ex) {
handle_incorrect_request(req, ex.Message);
}
}
public async void handle_enter_order(WebsocketRequest req) {
await HandleRestRequest(req, (req, Orders_Client) => WebsocketRestHandler.EnterOrderAsync(
req, Orders_Client, _securities, adminService, Dynamic_Descriptors, synthStrategies
@@ -375,7 +856,16 @@ namespace bts.websocket_core {
send_message(ms, cid);
}
public void send_message(object value, WebsocketClientID id) {
var requestJson = System.Text.Json.JsonSerializer.Serialize(value);
string requestJson;
try {
requestJson = System.Text.Json.JsonSerializer.Serialize(value);
}
catch (ArgumentException ex) {
// System.Text.Json rejects NaN and +/-Infinity by default. Do not let
// one malformed market-data update terminate the API server.
bts.Log.Gen.error($"Websocket JSON serialization failed for client {id}: {ex.Message}");
return;
}
Gateway.send_to_clients(
new List<WebsocketOutgoingMessage>() {
new WebsocketOutgoingMessage(id, requestJson)
@@ -14,6 +14,7 @@ namespace bts.websocket_core {
DELETE_ORDER,
UPDATE_ORDER,
SET_PRECOMPUTED_THEOS,
CREATE_SYNTHETIC_SPREAD,
DELETE,
GET,
PATCH,
@@ -30,10 +31,12 @@ namespace bts.websocket_core {
type = REQUEST_TYPES.UNKOWN;
JsonObject? request;
_message_id = "missing";
json = json.Replace('\'', '"');
try {
request = JsonSerializer.Deserialize<JsonObject>(json);
} catch (JsonException ex) {
message = $"Incorrect Json: {ex.Path}";
bts.Log.Gen.error($"Message: |{json}| is Inccorect json: {ex.Path}");
message = $"Incorrect Json: Path: {ex.Path}";
return false;
}
if (request is null) {
@@ -192,6 +192,9 @@
<Proto Include="../../lib_spark/spark/config/protos/quickbus_cfg.proto" CSName="QuickbusCfg">
<OutDir>$(ProtoOutputPath)/lib_spark/spark/config/protos</OutDir>
</Proto>
<Proto Include="../../lib_spark/spark/config/protos/synthetic_spread.proto" CSName="SyntheticSpread">
<OutDir>$(ProtoOutputPath)/lib_spark/spark/config/protos</OutDir>
</Proto>
<Proto Include="../../lib_spark/spark/config/protos/symbols.proto" CSName="Symbols">
<OutDir>$(ProtoOutputPath)/lib_spark/spark/config/protos</OutDir>
</Proto>
+1 -4
View File
@@ -40,10 +40,7 @@ namespace Spark.Api.Orders {
) {
_order_service = order_service;
var scheduler = ThreadPoolScheduler.Instance;
if(interval is not null)
logger.Information("originally OrdersClient polling interval {0}", interval);
interval = TimeSpan.FromMilliseconds(1);
logger.Information("OrdersClient polling interval {0}", interval);
interval ??= TimeSpan.FromSeconds(0.1);
var bootstraps = new DisposableBuilder();
positions = position_service.subscribei_position_data().Table().Cache();
@@ -190,10 +190,6 @@ namespace mbts.spark {
result = SingleSecurityCriteria.Empty;
return false;
}
if (id is null && (name is null || venue is null)) {
result = SingleSecurityCriteria.Empty;
return false;
}
result = new(venue, type, asset, name, id);
return true;
}
+3 -3
View File
@@ -126,12 +126,12 @@ namespace bts.api {
tick_sz = s.ContractSpec.LowTick,
leg_1_symbol = leg_1.Security.Symbol,
leg_1_exchange = leg_1.Security.Exchange.ToString(),
leg_1_min_quantity = leg_1.Security.MinOrderQuantity(),
leg_1_min_quantity = Math.Round(leg_1.Security.MinOrderQuantity(),9),
leg_1_tick_sz = leg_1.Security.ContractSpec.LowTick,
leg_1_cpp = leg_1.Security.ContractSpec.CashPerPoint,
leg_2_symbol = leg_2.Security.Symbol,
leg_2_exchange = leg_2.Security.Exchange.ToString(),
leg_2_min_quantity = leg_2.Security.MinOrderQuantity(),
leg_2_min_quantity = Math.Round(leg_2.Security.MinOrderQuantity(),9),
leg_2_tick_sz = leg_2.Security.ContractSpec.LowTick,
leg_2_cpp = leg_2.Security.ContractSpec.CashPerPoint
};
@@ -141,7 +141,7 @@ namespace bts.api {
security = s.security_desc(),
security_type = sec_type(s),
exchange = s.Exchange.ToString(),
min_quantity = s.MinOrderQuantity(),
min_quantity = Math.Round(s.MinOrderQuantity(),9),
tick_sz = s.ContractSpec.LowTick,
cpp = s.ContractSpec.CashPerPoint
};
@@ -182,6 +182,8 @@ namespace bts.api {
Security.ExchangeDataOneofCase.None => security.Synthetic.Name,
Security.ExchangeDataOneofCase.Bybit => security.Bybit.SecurityDesc,
Security.ExchangeDataOneofCase.Gateio => security.Gateio.SecurityDesc,
Security.ExchangeDataOneofCase.Orca => security.Orca.SecurityDesc,
Security.ExchangeDataOneofCase.Raydium => security.Raydium.SecurityDesc,
_ => throw new NotImplementedException("security_desc not implemented for exchange")
};
@@ -235,6 +237,8 @@ namespace bts.api {
domain.Venue.Twse => Venue.Twse,
domain.Venue.Bybit => Venue.Bybit,
domain.Venue.Gateio => Venue.Gateio,
domain.Venue.Orca => Venue.Orca,
domain.Venue.Raydium => Venue.Raydium,
domain.Venue.Unspecified => Venue.Unset,
_ => throw new NotImplementedException(),
};
@@ -257,6 +261,8 @@ namespace bts.api {
Venue.Twse => domain.Venue.Twse,
Venue.Bybit => domain.Venue.Bybit,
Venue.Gateio => domain.Venue.Gateio,
Venue.Orca => domain.Venue.Orca,
Venue.Raydium => domain.Venue.Raydium,
Venue.Unset => domain.Venue.Unspecified,
_ => throw new NotImplementedException(),
};
@@ -30,4 +30,6 @@ enum Venue {
VENUE_TWSE = 18;
VENUE_BYBIT = 19;
VENUE_GATEIO = 20;
VENUE_ORCA = 21;
VENUE_RAYDIUM = 22;
}
@@ -0,0 +1,17 @@
syntax = "proto3";
import "spark/api/domain/SecurityCriteria.proto";
import "spark/api/domain/Venue.proto";
package bts.api.md;
option csharp_namespace = "bts.api.md";
option cc_enable_arenas = true;
message DexMarketDataConfig {
uint32 num_depth_levels = 1;
bts.api.domain.SecurityCriteria security_criteria = 2;
repeated string excluded_securities = 3;
string json_conf_file_name = 4;
bts.api.domain.Venue venue = 5;
}
@@ -18,6 +18,7 @@ import "spark/api/md/BybitMarketDataConfig.proto";
import "spark/api/md/GateioMarketDataConfig.proto";
import "spark/api/md/CboeMarketDataConfig.proto";
import "spark/api/md/PyMarketDataProviderConfig.proto";
import "spark/api/md/DexMarketDataConfig.proto";
option csharp_namespace = "bts.api.md";
@@ -38,6 +39,7 @@ message MarketDataConfig {
repeated TwseMarketDataConfig twse = 12;
repeated BybitMarketDataConfig bybit = 13;
repeated GateioMarketDataConfig gateio = 14;
repeated DexMarketDataConfig dex = 15;
// Add an entry here for all the exchanges that need their market data to be recorded
repeated EmumbaDeribitMarketDataConfig emumba_deribit = 100;
@@ -27,12 +27,27 @@ std::set<bts::api::Venue> MarketDataConfigUtils::get_live_exchanges(
rv.insert(bts::api::VENUE_TWSE);
if (cfg.cboe_size()>0)
rv.insert(bts::api::VENUE_CBOE);
if (cfg.okx_size() > 0) {
rv.insert(bts::api::VENUE_OKX);
}
if (cfg.bybit_size() > 0) {
rv.insert(bts::api::VENUE_BYBIT);
}
if (cfg.gateio_size() > 0) {
rv.insert(bts::api::VENUE_GATEIO);
}
for (const auto &dex_cfg : cfg.dex()) {
switch (dex_cfg.venue()) {
case bts::api::domain::VENUE_ORCA:
rv.insert(bts::api::VENUE_ORCA);
break;
case bts::api::domain::VENUE_RAYDIUM:
rv.insert(bts::api::VENUE_RAYDIUM);
break;
default:
break;
}
}
return rv;
}
@@ -71,6 +71,8 @@ bool AdminServiceUtils::has_exchange(
case api::Venue::VENUE_COINBASE:
case api::Venue::VENUE_BINANCE_USDS_FUTURES:
case api::Venue::VENUE_OKX:
case api::Venue::VENUE_RAYDIUM:
case api::Venue::VENUE_ORCA:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_CBOE:
case api::Venue::VENUE_TWSE:
@@ -188,6 +190,8 @@ AdminServiceUtils::resolve_connection(
case api::Venue::VENUE_DERIBIT:
case api::Venue::VENUE_FTX:
case api::Venue::VENUE_OKX:
case api::Venue::VENUE_ORCA:
case api::Venue::VENUE_RAYDIUM:
case api::Venue::VENUE_BINANCE:
case api::Venue::VENUE_BINANCE_COIN_FUTURES:
case api::Venue::VENUE_BINANCE_US:
@@ -467,6 +471,8 @@ std::string AdminServiceUtils::find_account_exchange_account(
case api::Venue::VENUE_COINBASE:
case api::Venue::VENUE_BINANCE_USDS_FUTURES:
case api::Venue::VENUE_OKX:
case api::Venue::VENUE_RAYDIUM:
case api::Venue::VENUE_ORCA:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_CBOE:
case api::Venue::VENUE_TWSE:
@@ -790,6 +796,20 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(
out_result.set_is_success(true);
return rv;
}
case api::VENUE_RAYDIUM: {
rv.set_session("raydium_1");
rv.set_account(account_ptr->name());
rv.set_operator_id(user_ptr->name());
out_result.set_is_success(true);
return rv;
}
case api::VENUE_ORCA: {
rv.set_session("orca_1");
rv.set_account(account_ptr->name());
rv.set_operator_id(user_ptr->name());
out_result.set_is_success(true);
return rv;
}
case api::VENUE_BYBIT: {
rv.set_session("bybit_1");
rv.set_account(account_ptr->name());
@@ -1204,6 +1224,8 @@ std::string AdminServiceUtils::find_user_exchange_id(
case api::Venue::VENUE_COINBASE:
case api::Venue::VENUE_BINANCE_USDS_FUTURES:
case api::Venue::VENUE_OKX:
case api::Venue::VENUE_RAYDIUM:
case api::Venue::VENUE_ORCA:
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_CBOE:
@@ -1243,6 +1265,8 @@ std::string AdminServiceUtils::find_user_company_id(
case api::Venue::VENUE_COINBASE:
case api::Venue::VENUE_BINANCE_USDS_FUTURES:
case api::Venue::VENUE_OKX:
case api::Venue::VENUE_ORCA:
case api::Venue::VENUE_RAYDIUM:
case api::Venue::VENUE_SIM:
case api::Venue::VENUE_SIMICE:
case api::Venue::VENUE_GLBX:
@@ -4034,6 +4034,16 @@ JetFIX {
max_order_msgs_per_window: 300
venue: VENUE_OKX
}
connection {
name: "orca_1"
max_order_msgs_per_window: 300
venue: VENUE_OKX
}
connection {
name: "raydium_1"
max_order_msgs_per_window: 300
venue: VENUE_OKX
}
connection {
name: "binance_usds_futures_1"
max_order_msgs_per_window: 300
@@ -30,6 +30,5 @@ OrdermanCfg {
poll: false
hosted_actor: "*"
}
fallback_cust_order_handling_inst: "Y"
}
}
@@ -60,7 +60,15 @@ SymbolLists {
list {
key: "OKX"
symbol: "BTC"
symbol: "ETH"
symbol: "SOL"
}
list {
key: "ORCA"
symbol: "SOL"
}
list {
key: "RAYDIUM"
symbol: "SOL"
}
list {
key: "BYBIT"
@@ -199,6 +207,14 @@ Symbols {
future: @SymbolLists.list[OKX].symbol
option: @SymbolLists.list[OKX].symbol
}
venues {
name: "VENUE_ORCA"
spot: @SymbolLists.list[ORCA].symbol
}
venues {
name: "VENUE_RAYDIUM"
spot: @SymbolLists.list[RAYDIUM].symbol
}
venues {
name: "VENUE_BYBIT"
spot: @SymbolLists.list[BYBIT].symbol
@@ -0,0 +1,53 @@
SyntheticSpreadConfig {
value {
default_currency: "USD"
default_tick_size: 0.00001
default_cash_per_point: 1.0
default_price_multiplier: 1.0
default_main_fraction: 1
default_sub_fraction: 100000
default_price_display_format: 0
default_price_calc: 0
default_cancel_hedges_on_cancel: true
default_hedge_pricing: 1
default_spreader_type: 0
default_lean_quantity_ratio: 1.0
preset {
first_leg_exchange: VENUE_UNSET
first_leg_exchange_security_name: ""
name: "Synthetic"
symbol: ""
currency: "USD"
tick_size: 0.00001
cash_per_point: 1.0
price_multiplier: 1.0
main_fraction: 1
sub_fraction: 100000
price_display_format: 0
price_calc: 0
cancel_hedges_on_cancel: true
hedge_pricing: 1
spreader_type: 0
lean_quantity_ratio: 1.0
leg1 {
quantity: 1.0
price_multiplier: 1.0
leg_delta: 0.0
side: SIDE_BUY
is_active_quoting: true
enable_hedging: true
market_data_type: 0
}
leg2 {
quantity: 1.0
price_multiplier: 1.0
leg_delta: 0.0
side: SIDE_SELL
is_active_quoting: true
enable_hedging: true
market_data_type: 0
}
}
}
}
@@ -0,0 +1,58 @@
syntax = "proto3";
import "spark/domain_model/Security.proto";
import "spark/sparkproto/SparkProtos.proto";
package bts.config;
option csharp_namespace = "bts.config";
message SyntheticSpreadConfig {
string default_currency = 1;
double default_tick_size = 2;
double default_cash_per_point = 3;
double default_price_multiplier = 4;
uint32 default_main_fraction = 5;
uint32 default_sub_fraction = 6;
uint32 default_price_display_format = 7;
uint32 default_price_calc = 8;
bool default_cancel_hedges_on_cancel = 9;
uint32 default_hedge_pricing = 10;
uint32 default_spreader_type = 11;
double default_lean_quantity_ratio = 12;
repeated SyntheticSpreadPreset preset = 20;
}
message SyntheticSpreadPreset {
bts.api.Venue first_leg_exchange = 1;
string first_leg_exchange_security_name = 2;
string name = 10;
string symbol = 11;
string currency = 12;
double tick_size = 13;
double cash_per_point = 14;
double price_multiplier = 15;
uint32 main_fraction = 16;
uint32 sub_fraction = 17;
uint32 price_display_format = 18;
uint32 price_calc = 19;
bool cancel_hedges_on_cancel = 20;
uint32 hedge_pricing = 21;
uint32 spreader_type = 22;
double lean_quantity_ratio = 23;
SyntheticSpreadLegDefaults leg1 = 30;
SyntheticSpreadLegDefaults leg2 = 31;
}
message SyntheticSpreadLegDefaults {
double quantity = 1;
double price_multiplier = 2;
double leg_delta = 3;
bts.api.Side side = 4;
bool is_active_quoting = 5;
bool enable_hedging = 6;
uint32 market_data_type = 7;
}
@@ -31,6 +31,10 @@ api::domain::Venue DomainSecurityUtils::convert(bts::api::Venue venue) {
return DOMAIN_VENUE(BTS);
case bts::api::Venue::VENUE_SIM:
return DOMAIN_VENUE(SIM);
case bts::api::Venue::VENUE_RAYDIUM:
return DOMAIN_VENUE(RAYDIUM);
case bts::api::Venue::VENUE_ORCA:
return DOMAIN_VENUE(ORCA);
case bts::api::Venue::VENUE_BINANCE:
return DOMAIN_VENUE(BINANCE);
case bts::api::Venue::VENUE_BINANCE_COIN_FUTURES:
@@ -85,6 +89,10 @@ bts::api::Venue DomainSecurityUtils::convert(api::domain::Venue venue) {
return api::Venue::VENUE_BYBIT;
case DOMAIN_VENUE(GATEIO):
return api::Venue::VENUE_GATEIO;
case DOMAIN_VENUE(ORCA):
return api::Venue::VENUE_ORCA;
case DOMAIN_VENUE(RAYDIUM):
return api::Venue::VENUE_RAYDIUM;
case DOMAIN_VENUE(SIM):
return api::Venue::VENUE_SIM;
case DOMAIN_VENUE(COINBASE):
@@ -245,6 +253,12 @@ bool DomainSecurityUtils::get_security_desc(
case bts::api::Security::ExchangeDataCase::kOkx:
desc = security.okx().security_desc();
break;
case bts::api::Security::ExchangeDataCase::kRaydium:
desc = security.raydium().security_desc();
break;
case bts::api::Security::ExchangeDataCase::kOrca:
desc = security.orca().security_desc();
break;
case bts::api::Security::ExchangeDataCase::kFtx:
desc = security.ftx().security_desc();
break;
@@ -502,6 +502,29 @@ void from_json(const json &j, OkxExchangeData &data) {
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const OrcaExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, OrcaExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const RaydiumExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, RaydiumExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const CoinbaseExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
@@ -535,6 +558,8 @@ void to_json(json &j, const Security &s) {
oneof(j, s, BinanceUs, binance_us) \
oneof(j, s, Coinbase, coinbase) \
oneof(j, s, Okx, okx) \
oneof(j, s, Raydium, raydium) \
oneof(j, s, Orca, orca) \
oneof(j, s, Taifex, taifex)
// clang-format on
@@ -569,6 +594,8 @@ void from_json(const json &j, Security &s) {
oneof(j, s, binance_us, it); \
oneof(j, s, coinbase, it); \
oneof(j, s, okx, it); \
oneof(j, s, orca, it); \
oneof(j, s, raydium, it); \
oneof(j, s, taifex, it);
PB_JSON_READ_VALUE(j, s, id, it);
@@ -88,6 +88,12 @@ void from_json(const nlohmann::json &j, CoinbaseExchangeData &data);
void to_json(nlohmann::json &j, const Security &s);
void from_json(const nlohmann::json &j, Security &s);
void to_json(nlohmann::json &j, const OrcaExchangeData &data);
void from_json(const nlohmann::json &j, OrcaExchangeData &data);
void to_json(nlohmann::json &j, const RaydiumExchangeData &data);
void from_json(const nlohmann::json &j, RaydiumExchangeData &data);
PB_ENUM_FROM_JSON(SyntheticContract_PriceCalc);
PB_ENUM_TO_JSON(SyntheticContract_PriceCalc);
@@ -371,6 +377,10 @@ inline const std::string &descriptor(
return sec.gateio().security_desc();
case api::Security::kOkx:
return sec.okx().security_desc();
case api::Security::kRaydium:
return sec.raydium().security_desc();
case api::Security::kOrca:
return sec.orca().security_desc();
case api::Security::kTwse:
return sec.twse().security_desc();
case api::Security::kTaifex:
@@ -24,6 +24,8 @@ enum Venue {
VENUE_TWSE = 600;
VENUE_BYBIT = 610;
VENUE_GATEIO = 620;
VENUE_ORCA = 630;
VENUE_RAYDIUM = 640;
}
enum SecurityType {
@@ -141,6 +143,16 @@ message OkxExchangeData {
string security_desc = 2;
}
message RaydiumExchangeData {
int64 exchange_id = 1;
string security_desc = 2;
}
message OrcaExchangeData {
int64 exchange_id = 1;
string security_desc = 2;
}
message BybitExchangeData {
int64 exchange_id = 10;
string security_desc = 20;
@@ -282,6 +294,8 @@ message Security {
TWSEExchangeData twse = 500;
BybitExchangeData bybit = 510;
GateioExchangeData gateio = 520;
RaydiumExchangeData raydium = 530;
OrcaExchangeData orca = 540;
}
}
message SecurityList {
@@ -10,7 +10,6 @@
#include "spark/orderman/orderman_utils/orderman_common.h"
#include "spark/tracing/bts_trace.h"
#include "spark/utils/TimeStamp.h"
#include "spark/utils/bts_assert.h"
#include "spark/utils/bts_chrono.h"
#include "spark/utils/datetime.h"
#include "spark/utils/thread_manager.h"
@@ -98,8 +97,7 @@ struct CoreGateway::PImpl {
: cfg(cfg), observer(observer), make_connection(conn_factory),
make_store(store_factory), state(State::OFF), start_of_pause(),
session_info(), event_queue(), timer_thread(), destroy(false),
is_on(false), has_replayed(false), gave_up_permanently(false),
send_batch(), recv_batch(),
is_on(false), has_replayed(false), send_batch(), recv_batch(),
connection_attempts(0), perf_log(l) {
send_batch.reserve(1024);
recv_batch.reserve(1024);
@@ -118,7 +116,6 @@ struct CoreGateway::PImpl {
std::atomic<bool> destroy;
bool is_on;
bool has_replayed;
bool gave_up_permanently;
std::vector<EventData> send_batch;
std::vector<EventData> recv_batch;
uint32_t connection_attempts;
@@ -332,11 +329,6 @@ void CoreGateway::sync_session_ready(EventData const &) {
case State::WSESSION_TERMINATE:
break;
case State::CONNECTING:
LOG(Jetfix, Info,
"Jetfix session {} is now READY after socket connect. attempts={} "
"max_attempts={}",
me->cfg.name(), me->connection_attempts,
me->cfg.max_connection_attempts());
transition(State::READY);
me->observer.ready();
break;
@@ -354,18 +346,10 @@ void CoreGateway::sync_session_terminated(EventData const &) {
break;
case State::WSESSION_TERMINATE:
LOG(Jetfix, Warn,
"Session terminated callback received for {} while waiting for "
"reader disconnect. attempts_so_far={}",
me->cfg.name(), me->connection_attempts);
pause_or_shutdown();
break;
case State::READY:
LOG(Jetfix, Warn,
"Session terminated for {} from READY. Closing socket and waiting "
"for reader disconnect.",
me->cfg.name());
me->session_info->session.reset();
me->session_info->connection->shutdown();
transition(State::WREADER_DISCONNECT);
@@ -373,10 +357,6 @@ void CoreGateway::sync_session_terminated(EventData const &) {
break;
case State::CONNECTING:
LOG(Jetfix, Warn,
"Session terminated for {} during CONNECTING before READY. "
"Closing socket and waiting for reader disconnect.",
me->cfg.name());
me->session_info->session.reset();
me->session_info->connection->shutdown();
transition(State::WREADER_DISCONNECT);
@@ -396,28 +376,17 @@ void CoreGateway::sync_reader_disconnected(EventData const &) {
break;
case State::READY:
LOG(Jetfix, Warn,
"Socket reader disconnected for {} from READY. "
"The exchange or network closed the TCP session.",
me->cfg.name());
me->session_info->session.reset();
me->observer.disconnect();
transition(State::WSESSION_TERMINATE);
break;
case State::CONNECTING:
LOG(Jetfix, Warn,
"Socket reader disconnected for {} during CONNECTING before READY. "
"The TCP socket connected, but the session never completed logon.",
me->cfg.name());
me->session_info->session.reset();
transition(State::WSESSION_TERMINATE);
break;
case State::WREADER_DISCONNECT:
LOG(Jetfix, Info,
"Reader disconnect completed for {} after session shutdown.",
me->cfg.name());
pause_or_shutdown();
break;
@@ -461,46 +430,27 @@ void CoreGateway::sync_in_msg_sequenced(EventData const &ev) {
bts_assert(false);
}
}
void CoreGateway::sync_time(EventData const &ev) {
switch (me->state) {
case State::OFF:
break;
case State::PAUSED:
if (me->gave_up_permanently)
break;
if (datetime::is_at_least_secs_old(
me->start_of_pause, me->cfg.seconds_to_pause())) {
LOG(Jetfix, Info,
"Pause ended for jetfix connection {}. pause_secs={} "
"attempts_so_far={} max_attempts={} host={}:{} segment={} "
"session_window={}-{} {}-{} replayed={}",
me->cfg.name(), me->cfg.seconds_to_pause(),
me->connection_attempts, me->cfg.max_connection_attempts(),
me->cfg.socket_connect_host_ip(), me->cfg.socket_connect_port(),
me->cfg.cme_market_segment_id(), me->cfg.start_time(),
me->cfg.end_time(), me->cfg.start_day(), me->cfg.end_day(),
me->has_replayed);
LOG(Jetfix, Info, "Pause period ended for jetfix connection {}",
me->cfg.name());
me->session_info = std::make_unique<SessionInfo>();
me->session_info->store.reset(me->make_store(me->cfg));
auto const session_active = me->session_info->store->session_active();
LOG(Jetfix, Info,
"Loaded FIX store for {}. session_active={} next_attempt={} "
"max_attempts={}",
me->cfg.name(), session_active, me->connection_attempts + 1,
me->cfg.max_connection_attempts());
if (session_active) {
if (me->session_info->store->session_active()) {
me->connection_attempts++;
LOG(Jetfix, Info,
"Attempting socket connect for jetfix connection {}. "
"attempt={} max_attempts={}",
me->cfg.name(), me->connection_attempts,
me->cfg.max_connection_attempts());
if (!me->has_replayed) {
auto msgs_to_replay = me->session_info->store->replay();
LOG(Jetfix, Warn,
"Replaying {} FIX msgs for {} before reconnect",
msgs_to_replay.size(), me->cfg.name());
LOG(Jetfix, Warn, "Replaying {} FIX msgs",
msgs_to_replay.size());
me->observer.replay(msgs_to_replay);
me->has_replayed = true;
}
@@ -508,20 +458,14 @@ void CoreGateway::sync_time(EventData const &ev) {
if (me->connection_attempts > me->cfg.max_connection_attempts()) {
me->session_info.reset();
me->start_of_pause = datetime::now();
me->gave_up_permanently = true;
LOG(Jetfix, Error,
"Failed to connect to exchange gateway {} too many "
"times. Giving up permanently. attempts={} "
"max_attempts={} pause_secs={} host={}:{} segment={}. "
"You must bounce the server to recover.",
me->cfg.name(), me->connection_attempts,
me->cfg.max_connection_attempts(),
me->cfg.seconds_to_pause(),
me->cfg.socket_connect_host_ip(),
me->cfg.socket_connect_port(),
me->cfg.cme_market_segment_id());
"times. Giving up permanently. You must bounce "
"the server to recover.",
me->cfg.name());
break;
}
me->session_info->connection.reset(me->make_connection(me->cfg));
LOG(Jetfix, Warn, "Trying to connect to exchange for {} ({}/{})",
me->cfg.name(), me->connection_attempts,
@@ -545,18 +489,11 @@ void CoreGateway::sync_time(EventData const &ev) {
me->session_info.reset();
me->start_of_pause = datetime::now();
LOG(Jetfix, Warn,
"Failed to connect to exchange for {}. ({}/{}) "
"Will retry after {} seconds.",
"Failed to connect to exchange for {}. ({}/{}) ",
me->cfg.name(), me->connection_attempts,
me->cfg.max_connection_attempts(),
me->cfg.seconds_to_pause());
me->cfg.max_connection_attempts());
}
} else {
LOG(Jetfix, Warn,
"FIX store for {} is not active yet. Deferring reconnect. "
"session_window={}-{} {}-{}",
me->cfg.name(), me->cfg.start_time(), me->cfg.end_time(),
me->cfg.start_day(), me->cfg.end_day());
me->session_info.reset();
me->start_of_pause = datetime::now();
}
@@ -579,12 +516,6 @@ void CoreGateway::pause_or_shutdown() {
if (me->is_on) {
me->start_of_pause = datetime::now();
LOG(Jetfix, Info,
"Pausing jetfix gateway {}. pause_secs={} attempts_so_far={} "
"max_attempts={} gave_up_permanently={}",
me->cfg.name(), me->cfg.seconds_to_pause(),
me->connection_attempts, me->cfg.max_connection_attempts(),
me->gave_up_permanently);
transition(State::PAUSED);
} else
transition(State::OFF);
@@ -114,15 +114,6 @@ void Session::start() {
me->start_of_logon = me->now();
me->last_seq_num = me->begin_of_this_session;
me->last_seq_num_processed = me->store.last_seq_num_processed();
LOG(Jetfix, Info,
"Starting jetfix session {}. sender_compid={} sender_subid={} "
"target_compid={} target_subid={} secure_logon={} raw_data_len={} "
"user_name_present={} seq_num={} last_processed={}",
me->cfg.name(), me->cfg.sender_compid(), me->cfg.sender_subid(),
me->cfg.target_compid(), me->cfg.target_subid(),
me->cfg.use_cme_secure_logon(), me->cfg.raw_data().size(),
!me->cfg.user_name().empty(), me->last_seq_num + 1,
me->last_seq_num_processed);
transition(State::WLOGON);
internal_send(make_logon(
me->last_seq_num + 1, me->last_seq_num_processed, *me->current_time,
@@ -225,6 +225,14 @@ void convert_security(const FixedSecurity &source, Security *presult) {
dest.mutable_okx()->set_security_desc(source.security_desc());
dest.mutable_okx()->set_exchange_id(source.numeric_id);
break;
case Venue::VENUE_RAYDIUM:
dest.mutable_raydium()->set_security_desc(source.security_desc());
dest.mutable_raydium()->set_exchange_id(source.numeric_id);
break;
case Venue::VENUE_ORCA:
dest.mutable_orca()->set_security_desc(source.security_desc());
dest.mutable_orca()->set_exchange_id(source.numeric_id);
break;
case Venue::VENUE_TWSE:
dest.mutable_twse()->set_security_desc(source.security_desc());
dest.mutable_twse()->set_exchange_id(source.numeric_id);
@@ -349,6 +357,10 @@ uint64_t exchange_id(const Security &security) {
return security.binance_usds_futures().exchange_id();
case Security::ExchangeDataCase::kOkx:
return security.okx().exchange_id();
case Security::ExchangeDataCase::kRaydium:
return security.raydium().exchange_id();
case Security::ExchangeDataCase::kOrca:
return security.orca().exchange_id();
case Security::ExchangeDataCase::kTwse:
return security.twse().exchange_id();
case Security::ExchangeDataCase::kTaifex:
@@ -449,6 +461,14 @@ void convert_security(
dest.numeric_id = src.okx().exchange_id();
dest.security_desc(src.okx().security_desc());
break;
case Security::ExchangeDataCase::kRaydium:
dest.numeric_id = src.raydium().exchange_id();
dest.security_desc(src.raydium().security_desc());
break;
case Security::ExchangeDataCase::kOrca:
dest.numeric_id = src.orca().exchange_id();
dest.security_desc(src.orca().security_desc());
break;
case Security::ExchangeDataCase::kBybit:
dest.numeric_id = src.bybit().exchange_id();
dest.security_desc(src.bybit().security_desc());
@@ -194,6 +194,12 @@ struct MDConsumerGroup {
case api::VENUE_OKX:
this->mic = "OKX";
break;
case api::VENUE_RAYDIUM:
this->mic = "RAYDIUM";
break;
case api::VENUE_ORCA:
this->mic = "ORCA";
break;
case api::VENUE_CBOE:
this->mic = "CBOE";
break;
+7
View File
@@ -24,6 +24,9 @@ class VenueIndex {
std::optional<T> coinbase;
std::optional<T> bybit;
std::optional<T> gateio;
std::optional<T> orca;
std::optional<T> raydium;
public:
std::optional<T> &get(api::Venue venue) { return get_(*this, venue); }
@@ -73,6 +76,10 @@ class VenueIndex {
return self.bybit;
case api::VENUE_GATEIO:
return self.gateio;
case api::VENUE_RAYDIUM:
return self.raydium;
case api::VENUE_ORCA:
return self.orca;
case api::VENUE_BTS:
return self.bts;
case api::VENUE_FTX:
+12 -12
View File
@@ -47,7 +47,7 @@ IL3GW::IL3GW(
: _config(conf), _x_state(State::INIT), _buffer(buffer),
_on_state_change([](auto) {}), _on_exe_report([](auto) {}),
_on_session_change([](auto) {}), _connection(con), _session(session),
_msg_count(0), _time(), _pool("IL3GW_rprts", 1024*2, 100), _exe_parser(cfg, db), _retransmit() {
_msg_count(0), _time(), _pool(), _exe_parser(cfg, db), _retransmit() {
LOG(OrderMan, Info,
"IL3GW::IL3GW cme_session: {} uuid: {} to_cme: {} from_cme: {}",
_config.cme_session, _session.uuid, _session.to_cme, _session.from_cme);
@@ -179,10 +179,15 @@ uint64_t IL3GW::send_party_details(const api::orderman::RoutingRecord &route) {
using namespace iLinkBinary;
auto msg = _buffer.frame<PartyDetailsDefinitionRequest518>(
// Use the generated helper so group sizing stays in sync with the
// active schema when CME updates the template layout.
PartyDetailsDefinitionRequest518::computeLength(3, 0) -
PartyDetailsDefinitionRequest518::sbeBlockLength());
// 2 repeating group header.
// 1 for party details and 1 for trade reg
// This seems unsafe but the api doesn't seem to want you to know how to
// size buffers ahead of time
2 * PartyDetailsDefinitionRequest518::NoPartyDetails::sbeHeaderSize() +
// N * size of each block
3 * PartyDetailsDefinitionRequest518::NoPartyDetails::sbeBlockLength() +
0 * PartyDetailsDefinitionRequest518::NoTrdRegPublications::
sbeBlockLength());
msg.partyDetailsListReqID(0);
msg.sendingTimeEpoch(_time.now());
msg.listUpdateAction(ListUpdAct::Add);
@@ -632,13 +637,8 @@ void IL3GW::handle(
handle_common(msg);
}
void IL3GW::handle(const iLinkBinary::BusinessReject521 &msg) {
LOG(iLink3, Error,
"Business Reject: reason={} '{}' ref_seq={} ref_tag={} ref_msg_type='{}' "
"ref_id={} text='{}'",
msg.businessRejectReason(),
string{translate_business_reject_reason(msg.businessRejectReason())},
msg.refSeqNum(), msg.refTagID(), msg.getRefMsgTypeAsStringView(),
msg.businessRejectRefID(), msg.getTextAsStringView());
LOG(iLink3, Error, "Business Reject: {} '{}'.", msg.businessRejectReason(),
string{translate_business_reject_reason(msg.businessRejectReason())});
handle_common(msg);
// A business reject can de-sync the sequence number because it rejects
// the sequence number of the rejected message. Send a sequence message
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(330);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(521);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -32,7 +32,7 @@ public:
{
GiveUp = static_cast<char>(71),
SGXoffset = static_cast<char>(83),
NULL_VALUE = static_cast<char>(0)
NULL_VALUE = static_cast<char>(0x00)
};
static CmtaGiveUpCD::Value get(const char value)
@@ -41,7 +41,7 @@ public:
{
case static_cast<char>(71): return GiveUp;
case static_cast<char>(83): return SGXoffset;
case static_cast<char>(0): return NULL_VALUE;
case static_cast<char>(0x00): return NULL_VALUE;
}
throw std::runtime_error("unknown value for enum CmtaGiveUpCD [E103]");
@@ -1,75 +0,0 @@
/* Generated SBE (Simple Binary Encoding) message codec */
#ifndef _ILINKBINARY_CROSSTYPEENUM_H_
#define _ILINKBINARY_CROSSTYPEENUM_H_
#if !defined(__STDC_LIMIT_MACROS)
# define __STDC_LIMIT_MACROS 1
#endif
#include <cstdint>
#include <iomanip>
#include <limits>
#include <ostream>
#include <stdexcept>
#include <sstream>
#include <string>
#define SBE_NULLVALUE_INT8 (std::numeric_limits<std::int8_t>::min)()
#define SBE_NULLVALUE_INT16 (std::numeric_limits<std::int16_t>::min)()
#define SBE_NULLVALUE_INT32 (std::numeric_limits<std::int32_t>::min)()
#define SBE_NULLVALUE_INT64 (std::numeric_limits<std::int64_t>::min)()
#define SBE_NULLVALUE_UINT8 (std::numeric_limits<std::uint8_t>::max)()
#define SBE_NULLVALUE_UINT16 (std::numeric_limits<std::uint16_t>::max)()
#define SBE_NULLVALUE_UINT32 (std::numeric_limits<std::uint32_t>::max)()
#define SBE_NULLVALUE_UINT64 (std::numeric_limits<std::uint64_t>::max)()
namespace iLinkBinary {
class CrossTypeEnum
{
public:
enum Value
{
Cross = static_cast<std::uint8_t>(3),
RCross = static_cast<std::uint8_t>(20),
CCross = static_cast<std::uint8_t>(21),
NULL_VALUE = static_cast<std::uint8_t>(255)
};
static CrossTypeEnum::Value get(const std::uint8_t value)
{
switch (value)
{
case static_cast<std::uint8_t>(3): return Cross;
case static_cast<std::uint8_t>(20): return RCross;
case static_cast<std::uint8_t>(21): return CCross;
case static_cast<std::uint8_t>(255): return NULL_VALUE;
}
throw std::runtime_error("unknown value for enum CrossTypeEnum [E103]");
}
static const char *c_str(const CrossTypeEnum::Value value)
{
switch (value)
{
case Cross: return "Cross";
case RCross: return "RCross";
case CCross: return "CCross";
case NULL_VALUE: return "NULL_VALUE";
}
throw std::runtime_error("unknown value for enum CrossTypeEnum [E103]:");
}
template<typename CharT, typename Traits>
friend std::basic_ostream<CharT, Traits> & operator << (
std::basic_ostream<CharT, Traits> &os, CrossTypeEnum::Value m)
{
return os << CrossTypeEnum::c_str(m);
}
};
}
#endif
@@ -36,7 +36,7 @@ public:
AlgoEngine = static_cast<char>(72),
DeskElectronic = static_cast<char>(87),
ClientElectronic = static_cast<char>(89),
NULL_VALUE = static_cast<char>(0)
NULL_VALUE = static_cast<char>(48)
};
static CustOrdHandlInst::Value get(const char value)
@@ -49,7 +49,7 @@ public:
case static_cast<char>(72): return AlgoEngine;
case static_cast<char>(87): return DeskElectronic;
case static_cast<char>(89): return ClientElectronic;
case static_cast<char>(0): return NULL_VALUE;
case static_cast<char>(48): return NULL_VALUE;
}
throw std::runtime_error("unknown value for enum CustOrdHandlInst [E103]");
@@ -194,7 +194,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static const char *lengthMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
@@ -38,7 +38,7 @@ public:
CalculationDifference = static_cast<char>(70),
NoMatchingExecutionReport = static_cast<char>(71),
Other = static_cast<char>(90),
NULL_VALUE = static_cast<char>(0)
NULL_VALUE = static_cast<char>(48)
};
static DKReason::Value get(const char value)
@@ -53,7 +53,7 @@ public:
case static_cast<char>(70): return CalculationDifference;
case static_cast<char>(71): return NoMatchingExecutionReport;
case static_cast<char>(90): return Other;
case static_cast<char>(0): return NULL_VALUE;
case static_cast<char>(48): return NULL_VALUE;
}
throw std::runtime_error("unknown value for enum DKReason [E103]");
@@ -194,7 +194,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static const char *mantissaMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
@@ -194,7 +194,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static const char *mantissaMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
@@ -213,7 +213,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t mantissaSinceVersion() SBE_NOEXCEPT
{
return 9;
return 6;
}
SBE_NODISCARD bool mantissaInActingVersion() SBE_NOEXCEPT
@@ -255,7 +255,7 @@ public:
SBE_NODISCARD std::int64_t mantissa() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
if (m_actingVersion < 6)
{
return INT64_C(9223372036854775807);
}
@@ -288,7 +288,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t exponentSinceVersion() SBE_NOEXCEPT
{
return 9;
return 6;
}
SBE_NODISCARD bool exponentInActingVersion() SBE_NOEXCEPT
@@ -330,7 +330,7 @@ public:
SBE_NODISCARD std::int8_t exponent() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
if (m_actingVersion < 6)
{
return static_cast<std::int8_t>(127);
}
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(132);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(503);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -167,10 +165,10 @@ private:
}
public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(39);
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(38);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(504);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -224,7 +222,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeBlockLength() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(39);
return static_cast<std::uint16_t>(38);
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t sbeBlockAndHeaderLength() SBE_NOEXCEPT
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -970,82 +968,6 @@ public:
return *this;
}
SBE_NODISCARD static const char *EnvironmentIndicatorMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "optional";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t environmentIndicatorId() SBE_NOEXCEPT
{
return 39032;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t environmentIndicatorSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool environmentIndicatorInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= environmentIndicatorSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t environmentIndicatorEncodingOffset() SBE_NOEXCEPT
{
return 38;
}
static SBE_CONSTEXPR std::uint8_t environmentIndicatorNullValue() SBE_NOEXCEPT
{
return static_cast<std::uint8_t>(255);
}
static SBE_CONSTEXPR std::uint8_t environmentIndicatorMinValue() SBE_NOEXCEPT
{
return static_cast<std::uint8_t>(0);
}
static SBE_CONSTEXPR std::uint8_t environmentIndicatorMaxValue() SBE_NOEXCEPT
{
return static_cast<std::uint8_t>(254);
}
static SBE_CONSTEXPR std::size_t environmentIndicatorEncodingLength() SBE_NOEXCEPT
{
return 1;
}
SBE_NODISCARD std::uint8_t environmentIndicator() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return static_cast<std::uint8_t>(255);
}
std::uint8_t val;
std::memcpy(&val, m_buffer + m_offset + 38, sizeof(std::uint8_t));
return (val);
}
EstablishmentAck504 &environmentIndicator(const std::uint8_t value) SBE_NOEXCEPT
{
std::uint8_t val = (value);
std::memcpy(m_buffer + m_offset + 38, &val, sizeof(std::uint8_t));
return *this;
}
template<typename CharT, typename Traits>
friend std::basic_ostream<CharT, Traits> & operator << (
std::basic_ostream<CharT, Traits> &builder, EstablishmentAck504 _writer)
@@ -1098,10 +1020,6 @@ friend std::basic_ostream<CharT, Traits> & operator << (
builder << R"("SplitMsg": )";
builder << '"' << writer.splitMsg() << '"';
builder << ", ";
builder << R"("EnvironmentIndicator": )";
builder << +writer.environmentIndicator();
builder << '}';
return builder;
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -167,10 +165,10 @@ private:
}
public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(73);
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(72);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(505);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -224,7 +222,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeBlockLength() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(73);
return static_cast<std::uint16_t>(72);
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t sbeBlockAndHeaderLength() SBE_NOEXCEPT
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -963,82 +961,6 @@ public:
return *this;
}
SBE_NODISCARD static const char *EnvironmentIndicatorMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "optional";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t environmentIndicatorId() SBE_NOEXCEPT
{
return 39032;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t environmentIndicatorSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool environmentIndicatorInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= environmentIndicatorSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t environmentIndicatorEncodingOffset() SBE_NOEXCEPT
{
return 72;
}
static SBE_CONSTEXPR std::uint8_t environmentIndicatorNullValue() SBE_NOEXCEPT
{
return static_cast<std::uint8_t>(255);
}
static SBE_CONSTEXPR std::uint8_t environmentIndicatorMinValue() SBE_NOEXCEPT
{
return static_cast<std::uint8_t>(0);
}
static SBE_CONSTEXPR std::uint8_t environmentIndicatorMaxValue() SBE_NOEXCEPT
{
return static_cast<std::uint8_t>(254);
}
static SBE_CONSTEXPR std::size_t environmentIndicatorEncodingLength() SBE_NOEXCEPT
{
return 1;
}
SBE_NODISCARD std::uint8_t environmentIndicator() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return static_cast<std::uint8_t>(255);
}
std::uint8_t val;
std::memcpy(&val, m_buffer + m_offset + 72, sizeof(std::uint8_t));
return (val);
}
EstablishmentReject505 &environmentIndicator(const std::uint8_t value) SBE_NOEXCEPT
{
std::uint8_t val = (value);
std::memcpy(m_buffer + m_offset + 72, &val, sizeof(std::uint8_t));
return *this;
}
template<typename CharT, typename Traits>
friend std::basic_ostream<CharT, Traits> & operator << (
std::basic_ostream<CharT, Traits> &builder, EstablishmentReject505 _writer)
@@ -1084,10 +1006,6 @@ friend std::basic_ostream<CharT, Traits> & operator << (
builder << R"("SplitMsg": )";
builder << '"' << writer.splitMsg() << '"';
builder << ", ";
builder << R"("EnvironmentIndicator": )";
builder << +writer.environmentIndicator();
builder << '}';
return builder;
@@ -194,7 +194,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
ExecInst &clear()
@@ -32,7 +32,7 @@ public:
{
Aggressive = static_cast<char>(65),
Passive = static_cast<char>(80),
NULL_VALUE = static_cast<char>(0)
NULL_VALUE = static_cast<char>(0x00)
};
static ExecMode::Value get(const char value)
@@ -41,7 +41,7 @@ public:
{
case static_cast<char>(65): return Aggressive;
case static_cast<char>(80): return Passive;
case static_cast<char>(0): return NULL_VALUE;
case static_cast<char>(0x00): return NULL_VALUE;
}
throw std::runtime_error("unknown value for enum ExecMode [E103]");
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(101);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(539);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(247);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(534);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(219);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(524);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(226);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(531);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(226);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(522);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(219);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(564);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(196);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(565);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(483);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(523);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(513);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(532);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -167,10 +165,10 @@ private:
}
public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(266);
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(222);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(548);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -224,7 +222,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeBlockLength() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(266);
return static_cast<std::uint16_t>(222);
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t sbeBlockAndHeaderLength() SBE_NOEXCEPT
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -3112,7 +3110,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -3159,7 +3157,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -3249,313 +3247,6 @@ public:
return m_benchmarkPrice;
}
SBE_NODISCARD static const char *MDTradeEntryIDMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t mDTradeEntryIDId() SBE_NOEXCEPT
{
return 37711;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t mDTradeEntryIDSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool mDTradeEntryIDInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= mDTradeEntryIDSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t mDTradeEntryIDEncodingOffset() SBE_NOEXCEPT
{
return 222;
}
static SBE_CONSTEXPR std::uint32_t mDTradeEntryIDNullValue() SBE_NOEXCEPT
{
return SBE_NULLVALUE_UINT32;
}
static SBE_CONSTEXPR std::uint32_t mDTradeEntryIDMinValue() SBE_NOEXCEPT
{
return UINT32_C(0x0);
}
static SBE_CONSTEXPR std::uint32_t mDTradeEntryIDMaxValue() SBE_NOEXCEPT
{
return UINT32_C(0xfffffffe);
}
static SBE_CONSTEXPR std::size_t mDTradeEntryIDEncodingLength() SBE_NOEXCEPT
{
return 4;
}
SBE_NODISCARD std::uint32_t mDTradeEntryID() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return UINT32_C(0xffffffff);
}
std::uint32_t val;
std::memcpy(&val, m_buffer + m_offset + 222, sizeof(std::uint32_t));
return SBE_LITTLE_ENDIAN_ENCODE_32(val);
}
ExecutionReportTradeAddendumOutright548 &mDTradeEntryID(const std::uint32_t value) SBE_NOEXCEPT
{
std::uint32_t val = SBE_LITTLE_ENDIAN_ENCODE_32(value);
std::memcpy(m_buffer + m_offset + 222, &val, sizeof(std::uint32_t));
return *this;
}
SBE_NODISCARD static const char *ExecRefIDMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "String";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t execRefIDId() SBE_NOEXCEPT
{
return 19;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t execRefIDSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool execRefIDInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= execRefIDSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t execRefIDEncodingOffset() SBE_NOEXCEPT
{
return 226;
}
static SBE_CONSTEXPR char execRefIDNullValue() SBE_NOEXCEPT
{
return static_cast<char>(0);
}
static SBE_CONSTEXPR char execRefIDMinValue() SBE_NOEXCEPT
{
return static_cast<char>(32);
}
static SBE_CONSTEXPR char execRefIDMaxValue() SBE_NOEXCEPT
{
return static_cast<char>(126);
}
static SBE_CONSTEXPR std::size_t execRefIDEncodingLength() SBE_NOEXCEPT
{
return 40;
}
static SBE_CONSTEXPR std::uint64_t execRefIDLength() SBE_NOEXCEPT
{
return 40;
}
SBE_NODISCARD const char *execRefID() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return nullptr;
}
return m_buffer + m_offset + 226;
}
SBE_NODISCARD char *execRefID() SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return nullptr;
}
return m_buffer + m_offset + 226;
}
SBE_NODISCARD char execRefID(const std::uint64_t index) const
{
if (index >= 40)
{
throw std::runtime_error("index out of range for execRefID [E104]");
}
if (m_actingVersion < 9)
{
return static_cast<char>(0);
}
char val;
std::memcpy(&val, m_buffer + m_offset + 226 + (index * 1), sizeof(char));
return (val);
}
ExecutionReportTradeAddendumOutright548 &execRefID(const std::uint64_t index, const char value)
{
if (index >= 40)
{
throw std::runtime_error("index out of range for execRefID [E105]");
}
char val = (value);
std::memcpy(m_buffer + m_offset + 226 + (index * 1), &val, sizeof(char));
return *this;
}
std::uint64_t getExecRefID(char *const dst, const std::uint64_t length) const
{
if (length > 40)
{
throw std::runtime_error("length too large for getExecRefID [E106]");
}
if (m_actingVersion < 9)
{
return 0;
}
std::memcpy(dst, m_buffer + m_offset + 226, sizeof(char) * static_cast<std::size_t>(length));
return length;
}
ExecutionReportTradeAddendumOutright548 &putExecRefID(const char *const src) SBE_NOEXCEPT
{
std::memcpy(m_buffer + m_offset + 226, src, sizeof(char) * 40);
return *this;
}
SBE_NODISCARD std::string getExecRefIDAsString() const
{
const char *buffer = m_buffer + m_offset + 226;
std::size_t length = 0;
for (; length < 40 && *(buffer + length) != '\0'; ++length);
std::string result(buffer, length);
return result;
}
std::string getExecRefIDAsJsonEscapedString()
{
if (m_actingVersion < 9)
{
return std::string("");
}
std::ostringstream oss;
std::string s = getExecRefIDAsString();
for (const auto c : s)
{
switch (c)
{
case '"': oss << "\\\""; break;
case '\\': oss << "\\\\"; break;
case '\b': oss << "\\b"; break;
case '\f': oss << "\\f"; break;
case '\n': oss << "\\n"; break;
case '\r': oss << "\\r"; break;
case '\t': oss << "\\t"; break;
default:
if ('\x00' <= c && c <= '\x1f')
{
oss << "\\u" << std::hex << std::setw(4)
<< std::setfill('0') << (int)(c);
}
else
{
oss << c;
}
}
}
return oss.str();
}
#if __cplusplus >= 201703L
SBE_NODISCARD std::string_view getExecRefIDAsStringView() const SBE_NOEXCEPT
{
const char *buffer = m_buffer + m_offset + 226;
std::size_t length = 0;
for (; length < 40 && *(buffer + length) != '\0'; ++length);
std::string_view result(buffer, length);
return result;
}
#endif
#if __cplusplus >= 201703L
ExecutionReportTradeAddendumOutright548 &putExecRefID(const std::string_view str)
{
const std::size_t srcLength = str.length();
if (srcLength > 40)
{
throw std::runtime_error("string too large for putExecRefID [E106]");
}
std::memcpy(m_buffer + m_offset + 226, str.data(), srcLength);
for (std::size_t start = srcLength; start < 40; ++start)
{
m_buffer[m_offset + 226 + start] = 0;
}
return *this;
}
#else
ExecutionReportTradeAddendumOutright548 &putExecRefID(const std::string &str)
{
const std::size_t srcLength = str.length();
if (srcLength > 40)
{
throw std::runtime_error("string too large for putExecRefID [E106]");
}
std::memcpy(m_buffer + m_offset + 226, str.c_str(), srcLength);
for (std::size_t start = srcLength; start < 40; ++start)
{
m_buffer[m_offset + 226 + start] = 0;
}
return *this;
}
#endif
class NoFills
{
private:
@@ -4948,7 +4639,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -4995,7 +4686,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -5352,15 +5043,6 @@ friend std::basic_ostream<CharT, Traits> & operator << (
builder << "{}";
}
builder << ", ";
builder << R"("MDTradeEntryID": )";
builder << +writer.mDTradeEntryID();
builder << ", ";
builder << R"("ExecRefID": )";
builder << '"' <<
writer.getExecRefIDAsJsonEscapedString().c_str() << '"';
builder << ", ";
{
bool atLeastOne = false;
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -167,10 +165,10 @@ private:
}
public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(227);
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(187);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(549);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -224,7 +222,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeBlockLength() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(227);
return static_cast<std::uint16_t>(187);
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t sbeBlockAndHeaderLength() SBE_NOEXCEPT
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -2965,237 +2963,6 @@ public:
return *this;
}
SBE_NODISCARD static const char *ExecRefIDMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "String";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t execRefIDId() SBE_NOEXCEPT
{
return 19;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t execRefIDSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool execRefIDInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= execRefIDSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t execRefIDEncodingOffset() SBE_NOEXCEPT
{
return 187;
}
static SBE_CONSTEXPR char execRefIDNullValue() SBE_NOEXCEPT
{
return static_cast<char>(0);
}
static SBE_CONSTEXPR char execRefIDMinValue() SBE_NOEXCEPT
{
return static_cast<char>(32);
}
static SBE_CONSTEXPR char execRefIDMaxValue() SBE_NOEXCEPT
{
return static_cast<char>(126);
}
static SBE_CONSTEXPR std::size_t execRefIDEncodingLength() SBE_NOEXCEPT
{
return 40;
}
static SBE_CONSTEXPR std::uint64_t execRefIDLength() SBE_NOEXCEPT
{
return 40;
}
SBE_NODISCARD const char *execRefID() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return nullptr;
}
return m_buffer + m_offset + 187;
}
SBE_NODISCARD char *execRefID() SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return nullptr;
}
return m_buffer + m_offset + 187;
}
SBE_NODISCARD char execRefID(const std::uint64_t index) const
{
if (index >= 40)
{
throw std::runtime_error("index out of range for execRefID [E104]");
}
if (m_actingVersion < 9)
{
return static_cast<char>(0);
}
char val;
std::memcpy(&val, m_buffer + m_offset + 187 + (index * 1), sizeof(char));
return (val);
}
ExecutionReportTradeAddendumSpread549 &execRefID(const std::uint64_t index, const char value)
{
if (index >= 40)
{
throw std::runtime_error("index out of range for execRefID [E105]");
}
char val = (value);
std::memcpy(m_buffer + m_offset + 187 + (index * 1), &val, sizeof(char));
return *this;
}
std::uint64_t getExecRefID(char *const dst, const std::uint64_t length) const
{
if (length > 40)
{
throw std::runtime_error("length too large for getExecRefID [E106]");
}
if (m_actingVersion < 9)
{
return 0;
}
std::memcpy(dst, m_buffer + m_offset + 187, sizeof(char) * static_cast<std::size_t>(length));
return length;
}
ExecutionReportTradeAddendumSpread549 &putExecRefID(const char *const src) SBE_NOEXCEPT
{
std::memcpy(m_buffer + m_offset + 187, src, sizeof(char) * 40);
return *this;
}
SBE_NODISCARD std::string getExecRefIDAsString() const
{
const char *buffer = m_buffer + m_offset + 187;
std::size_t length = 0;
for (; length < 40 && *(buffer + length) != '\0'; ++length);
std::string result(buffer, length);
return result;
}
std::string getExecRefIDAsJsonEscapedString()
{
if (m_actingVersion < 9)
{
return std::string("");
}
std::ostringstream oss;
std::string s = getExecRefIDAsString();
for (const auto c : s)
{
switch (c)
{
case '"': oss << "\\\""; break;
case '\\': oss << "\\\\"; break;
case '\b': oss << "\\b"; break;
case '\f': oss << "\\f"; break;
case '\n': oss << "\\n"; break;
case '\r': oss << "\\r"; break;
case '\t': oss << "\\t"; break;
default:
if ('\x00' <= c && c <= '\x1f')
{
oss << "\\u" << std::hex << std::setw(4)
<< std::setfill('0') << (int)(c);
}
else
{
oss << c;
}
}
}
return oss.str();
}
#if __cplusplus >= 201703L
SBE_NODISCARD std::string_view getExecRefIDAsStringView() const SBE_NOEXCEPT
{
const char *buffer = m_buffer + m_offset + 187;
std::size_t length = 0;
for (; length < 40 && *(buffer + length) != '\0'; ++length);
std::string_view result(buffer, length);
return result;
}
#endif
#if __cplusplus >= 201703L
ExecutionReportTradeAddendumSpread549 &putExecRefID(const std::string_view str)
{
const std::size_t srcLength = str.length();
if (srcLength > 40)
{
throw std::runtime_error("string too large for putExecRefID [E106]");
}
std::memcpy(m_buffer + m_offset + 187, str.data(), srcLength);
for (std::size_t start = srcLength; start < 40; ++start)
{
m_buffer[m_offset + 187 + start] = 0;
}
return *this;
}
#else
ExecutionReportTradeAddendumSpread549 &putExecRefID(const std::string &str)
{
const std::size_t srcLength = str.length();
if (srcLength > 40)
{
throw std::runtime_error("string too large for putExecRefID [E106]");
}
std::memcpy(m_buffer + m_offset + 187, str.c_str(), srcLength);
for (std::size_t start = srcLength; start < 40; ++start)
{
m_buffer[m_offset + 187 + start] = 0;
}
return *this;
}
#endif
class NoFills
{
private:
@@ -5611,11 +5378,6 @@ friend std::basic_ostream<CharT, Traits> & operator << (
builder << R"("ShortSaleType": )";
builder << '"' << writer.shortSaleType() << '"';
builder << ", ";
builder << R"("ExecRefID": )";
builder << '"' <<
writer.getExecRefIDAsJsonEscapedString().c_str() << '"';
builder << ", ";
{
bool atLeastOne = false;
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -167,10 +165,10 @@ private:
}
public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(236);
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(196);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(550);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -224,7 +222,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeBlockLength() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(236);
return static_cast<std::uint16_t>(196);
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t sbeBlockAndHeaderLength() SBE_NOEXCEPT
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -2546,7 +2544,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -2593,7 +2591,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -2636,237 +2634,6 @@ public:
return m_grossTradeAmt;
}
SBE_NODISCARD static const char *ExecRefIDMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "String";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t execRefIDId() SBE_NOEXCEPT
{
return 19;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t execRefIDSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool execRefIDInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= execRefIDSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t execRefIDEncodingOffset() SBE_NOEXCEPT
{
return 196;
}
static SBE_CONSTEXPR char execRefIDNullValue() SBE_NOEXCEPT
{
return static_cast<char>(0);
}
static SBE_CONSTEXPR char execRefIDMinValue() SBE_NOEXCEPT
{
return static_cast<char>(32);
}
static SBE_CONSTEXPR char execRefIDMaxValue() SBE_NOEXCEPT
{
return static_cast<char>(126);
}
static SBE_CONSTEXPR std::size_t execRefIDEncodingLength() SBE_NOEXCEPT
{
return 40;
}
static SBE_CONSTEXPR std::uint64_t execRefIDLength() SBE_NOEXCEPT
{
return 40;
}
SBE_NODISCARD const char *execRefID() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return nullptr;
}
return m_buffer + m_offset + 196;
}
SBE_NODISCARD char *execRefID() SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return nullptr;
}
return m_buffer + m_offset + 196;
}
SBE_NODISCARD char execRefID(const std::uint64_t index) const
{
if (index >= 40)
{
throw std::runtime_error("index out of range for execRefID [E104]");
}
if (m_actingVersion < 9)
{
return static_cast<char>(0);
}
char val;
std::memcpy(&val, m_buffer + m_offset + 196 + (index * 1), sizeof(char));
return (val);
}
ExecutionReportTradeAddendumSpreadLeg550 &execRefID(const std::uint64_t index, const char value)
{
if (index >= 40)
{
throw std::runtime_error("index out of range for execRefID [E105]");
}
char val = (value);
std::memcpy(m_buffer + m_offset + 196 + (index * 1), &val, sizeof(char));
return *this;
}
std::uint64_t getExecRefID(char *const dst, const std::uint64_t length) const
{
if (length > 40)
{
throw std::runtime_error("length too large for getExecRefID [E106]");
}
if (m_actingVersion < 9)
{
return 0;
}
std::memcpy(dst, m_buffer + m_offset + 196, sizeof(char) * static_cast<std::size_t>(length));
return length;
}
ExecutionReportTradeAddendumSpreadLeg550 &putExecRefID(const char *const src) SBE_NOEXCEPT
{
std::memcpy(m_buffer + m_offset + 196, src, sizeof(char) * 40);
return *this;
}
SBE_NODISCARD std::string getExecRefIDAsString() const
{
const char *buffer = m_buffer + m_offset + 196;
std::size_t length = 0;
for (; length < 40 && *(buffer + length) != '\0'; ++length);
std::string result(buffer, length);
return result;
}
std::string getExecRefIDAsJsonEscapedString()
{
if (m_actingVersion < 9)
{
return std::string("");
}
std::ostringstream oss;
std::string s = getExecRefIDAsString();
for (const auto c : s)
{
switch (c)
{
case '"': oss << "\\\""; break;
case '\\': oss << "\\\\"; break;
case '\b': oss << "\\b"; break;
case '\f': oss << "\\f"; break;
case '\n': oss << "\\n"; break;
case '\r': oss << "\\r"; break;
case '\t': oss << "\\t"; break;
default:
if ('\x00' <= c && c <= '\x1f')
{
oss << "\\u" << std::hex << std::setw(4)
<< std::setfill('0') << (int)(c);
}
else
{
oss << c;
}
}
}
return oss.str();
}
#if __cplusplus >= 201703L
SBE_NODISCARD std::string_view getExecRefIDAsStringView() const SBE_NOEXCEPT
{
const char *buffer = m_buffer + m_offset + 196;
std::size_t length = 0;
for (; length < 40 && *(buffer + length) != '\0'; ++length);
std::string_view result(buffer, length);
return result;
}
#endif
#if __cplusplus >= 201703L
ExecutionReportTradeAddendumSpreadLeg550 &putExecRefID(const std::string_view str)
{
const std::size_t srcLength = str.length();
if (srcLength > 40)
{
throw std::runtime_error("string too large for putExecRefID [E106]");
}
std::memcpy(m_buffer + m_offset + 196, str.data(), srcLength);
for (std::size_t start = srcLength; start < 40; ++start)
{
m_buffer[m_offset + 196 + start] = 0;
}
return *this;
}
#else
ExecutionReportTradeAddendumSpreadLeg550 &putExecRefID(const std::string &str)
{
const std::size_t srcLength = str.length();
if (srcLength > 40)
{
throw std::runtime_error("string too large for putExecRefID [E106]");
}
std::memcpy(m_buffer + m_offset + 196, str.c_str(), srcLength);
for (std::size_t start = srcLength; start < 40; ++start)
{
m_buffer[m_offset + 196 + start] = 0;
}
return *this;
}
#endif
class NoFills
{
private:
@@ -3564,11 +3331,11 @@ public:
m_buffer = buffer;
m_bufferLength = bufferLength;
GroupSize dimensions(buffer, *pos, bufferLength, actingVersion);
dimensions.blockLength(static_cast<std::uint16_t>(45));
dimensions.blockLength(static_cast<std::uint16_t>(27));
dimensions.numInGroup(static_cast<std::uint8_t>(count));
m_index = 0;
m_count = count;
m_blockLength = 45;
m_blockLength = 27;
m_actingVersion = actingVersion;
m_initialPosition = *pos;
m_positionPtr = pos;
@@ -3582,7 +3349,7 @@ public:
static SBE_CONSTEXPR std::uint64_t sbeBlockLength() SBE_NOEXCEPT
{
return 45;
return 27;
}
SBE_NODISCARD std::uint64_t sbePosition() const SBE_NOEXCEPT
@@ -4255,100 +4022,6 @@ public:
return *this;
}
SBE_NODISCARD static const char *ContraGrossTradeAmtMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t contraGrossTradeAmtId() SBE_NOEXCEPT
{
return 5542;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t contraGrossTradeAmtSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool contraGrossTradeAmtInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= contraGrossTradeAmtSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t contraGrossTradeAmtEncodingOffset() SBE_NOEXCEPT
{
return 27;
}
private:
Decimal64NULL m_contraGrossTradeAmt;
public:
SBE_NODISCARD Decimal64NULL &contraGrossTradeAmt()
{
m_contraGrossTradeAmt.wrap(m_buffer, m_offset + 27, m_actingVersion, m_bufferLength);
return m_contraGrossTradeAmt;
}
SBE_NODISCARD static const char *ContraCalculatedCcyLastQtyMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t contraCalculatedCcyLastQtyId() SBE_NOEXCEPT
{
return 5971;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t contraCalculatedCcyLastQtySinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool contraCalculatedCcyLastQtyInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= contraCalculatedCcyLastQtySinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t contraCalculatedCcyLastQtyEncodingOffset() SBE_NOEXCEPT
{
return 36;
}
private:
Decimal64NULL m_contraCalculatedCcyLastQty;
public:
SBE_NODISCARD Decimal64NULL &contraCalculatedCcyLastQty()
{
m_contraCalculatedCcyLastQty.wrap(m_buffer, m_offset + 36, m_actingVersion, m_bufferLength);
return m_contraCalculatedCcyLastQty;
}
template<typename CharT, typename Traits>
friend std::basic_ostream<CharT, Traits> & operator << (
std::basic_ostream<CharT, Traits> &builder, NoOrderEvents writer)
@@ -4382,28 +4055,6 @@ public:
builder << R"("OriginalOrderEventExecID": )";
builder << +writer.originalOrderEventExecID();
builder << ", ";
builder << R"("ContraGrossTradeAmt": )";
if (writer.contraGrossTradeAmtInActingVersion())
{
builder << writer.contraGrossTradeAmt();
}
else
{
builder << "{}";
}
builder << ", ";
builder << R"("ContraCalculatedCcyLastQty": )";
if (writer.contraCalculatedCcyLastQtyInActingVersion())
{
builder << writer.contraCalculatedCcyLastQty();
}
else
{
builder << "{}";
}
builder << '}';
return builder;
@@ -4609,11 +4260,6 @@ friend std::basic_ostream<CharT, Traits> & operator << (
builder << "{}";
}
builder << ", ";
builder << R"("ExecRefID": )";
builder << '"' <<
writer.getExecRefIDAsJsonEscapedString().c_str() << '"';
builder << ", ";
{
bool atLeastOne = false;
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(293);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(525);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -4096,7 +4094,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -4143,7 +4141,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -5719,7 +5717,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -5766,7 +5764,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(230);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(526);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(219);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(527);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -2762,7 +2760,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -2809,7 +2807,7 @@ public:
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
@@ -3549,11 +3547,11 @@ public:
m_buffer = buffer;
m_bufferLength = bufferLength;
GroupSize dimensions(buffer, *pos, bufferLength, actingVersion);
dimensions.blockLength(static_cast<std::uint16_t>(41));
dimensions.blockLength(static_cast<std::uint16_t>(23));
dimensions.numInGroup(static_cast<std::uint8_t>(count));
m_index = 0;
m_count = count;
m_blockLength = 41;
m_blockLength = 23;
m_actingVersion = actingVersion;
m_initialPosition = *pos;
m_positionPtr = pos;
@@ -3567,7 +3565,7 @@ public:
static SBE_CONSTEXPR std::uint64_t sbeBlockLength() SBE_NOEXCEPT
{
return 41;
return 23;
}
SBE_NODISCARD std::uint64_t sbePosition() const SBE_NOEXCEPT
@@ -4164,100 +4162,6 @@ public:
return *this;
}
SBE_NODISCARD static const char *ContraGrossTradeAmtMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t contraGrossTradeAmtId() SBE_NOEXCEPT
{
return 5542;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t contraGrossTradeAmtSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool contraGrossTradeAmtInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= contraGrossTradeAmtSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t contraGrossTradeAmtEncodingOffset() SBE_NOEXCEPT
{
return 23;
}
private:
Decimal64NULL m_contraGrossTradeAmt;
public:
SBE_NODISCARD Decimal64NULL &contraGrossTradeAmt()
{
m_contraGrossTradeAmt.wrap(m_buffer, m_offset + 23, m_actingVersion, m_bufferLength);
return m_contraGrossTradeAmt;
}
SBE_NODISCARD static const char *ContraCalculatedCcyLastQtyMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "Qty";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t contraCalculatedCcyLastQtyId() SBE_NOEXCEPT
{
return 5971;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t contraCalculatedCcyLastQtySinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool contraCalculatedCcyLastQtyInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= contraCalculatedCcyLastQtySinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t contraCalculatedCcyLastQtyEncodingOffset() SBE_NOEXCEPT
{
return 32;
}
private:
Decimal64NULL m_contraCalculatedCcyLastQty;
public:
SBE_NODISCARD Decimal64NULL &contraCalculatedCcyLastQty()
{
m_contraCalculatedCcyLastQty.wrap(m_buffer, m_offset + 32, m_actingVersion, m_bufferLength);
return m_contraCalculatedCcyLastQty;
}
template<typename CharT, typename Traits>
friend std::basic_ostream<CharT, Traits> & operator << (
std::basic_ostream<CharT, Traits> &builder, NoOrderEvents writer)
@@ -4287,28 +4191,6 @@ public:
builder << R"("OrderEventReason": )";
builder << +writer.orderEventReason();
builder << ", ";
builder << R"("ContraGrossTradeAmt": )";
if (writer.contraGrossTradeAmtInActingVersion())
{
builder << writer.contraGrossTradeAmt();
}
else
{
builder << "{}";
}
builder << ", ";
builder << R"("ContraCalculatedCcyLastQty": )";
if (writer.contraCalculatedCcyLastQtyInActingVersion())
{
builder << writer.contraCalculatedCcyLastQty();
}
else
{
builder << "{}";
}
builder << '}';
return builder;
@@ -194,7 +194,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static const char *blockLengthMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
@@ -32,7 +32,7 @@ public:
{
Limit = static_cast<char>(50),
StopLimit = static_cast<char>(52),
NULL_VALUE = static_cast<char>(0)
NULL_VALUE = static_cast<char>(48)
};
static MassActionOrdTyp::Value get(const char value)
@@ -41,7 +41,7 @@ public:
{
case static_cast<char>(50): return Limit;
case static_cast<char>(52): return StopLimit;
case static_cast<char>(0): return NULL_VALUE;
case static_cast<char>(48): return NULL_VALUE;
}
throw std::runtime_error("unknown value for enum MassActionOrdTyp [E103]");
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -170,7 +168,7 @@ public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(123);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(517);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -127,9 +127,7 @@
#include "QuoteAckStatus.h"
#include "ExpCycle.h"
#include "MassCancelTIF.h"
#include "Side.h"
#include "OrderStatus.h"
#include "CrossTypeEnum.h"
#include "ShortSaleType.h"
#include "PartyDetailRole.h"
#include "MassActionScope.h"
@@ -167,10 +165,10 @@ private:
}
public:
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(352);
static const std::uint16_t SBE_BLOCK_LENGTH = static_cast<std::uint16_t>(351);
static const std::uint16_t SBE_TEMPLATE_ID = static_cast<std::uint16_t>(545);
static const std::uint16_t SBE_SCHEMA_ID = static_cast<std::uint16_t>(8);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(9);
static const std::uint16_t SBE_SCHEMA_VERSION = static_cast<std::uint16_t>(8);
enum MetaAttribute
{
@@ -224,7 +222,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeBlockLength() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(352);
return static_cast<std::uint16_t>(351);
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t sbeBlockAndHeaderLength() SBE_NOEXCEPT
@@ -244,7 +242,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT
@@ -2346,79 +2344,6 @@ public:
return *this;
}
SBE_NODISCARD static const char *QuoteEntryControlMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
{
switch (metaAttribute)
{
case MetaAttribute::SEMANTIC_TYPE: return "int";
case MetaAttribute::PRESENCE: return "required";
default: return "";
}
}
static SBE_CONSTEXPR std::uint16_t quoteEntryControlId() SBE_NOEXCEPT
{
return 39034;
}
SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t quoteEntryControlSinceVersion() SBE_NOEXCEPT
{
return 9;
}
SBE_NODISCARD bool quoteEntryControlInActingVersion() SBE_NOEXCEPT
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
#endif
return m_actingVersion >= quoteEntryControlSinceVersion();
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t quoteEntryControlEncodingOffset() SBE_NOEXCEPT
{
return 351;
}
SBE_NODISCARD static SBE_CONSTEXPR std::size_t quoteEntryControlEncodingLength() SBE_NOEXCEPT
{
return 1;
}
SBE_NODISCARD std::uint8_t quoteEntryControlRaw() const SBE_NOEXCEPT
{
if (m_actingVersion < 9)
{
return static_cast<std::uint8_t>(255);
}
std::uint8_t val;
std::memcpy(&val, m_buffer + m_offset + 351, sizeof(std::uint8_t));
return (val);
}
SBE_NODISCARD BooleanNULL::Value quoteEntryControl() const
{
if (m_actingVersion < 9)
{
return BooleanNULL::NULL_VALUE;
}
std::uint8_t val;
std::memcpy(&val, m_buffer + m_offset + 351, sizeof(std::uint8_t));
return BooleanNULL::get((val));
}
MassQuoteAck545 &quoteEntryControl(const BooleanNULL::Value value) SBE_NOEXCEPT
{
std::uint8_t val = (value);
std::memcpy(m_buffer + m_offset + 351, &val, sizeof(std::uint8_t));
return *this;
}
class NoQuoteEntries
{
private:
@@ -3045,10 +2970,6 @@ friend std::basic_ostream<CharT, Traits> & operator << (
builder << R"("QuoteEntryOpen": )";
builder << '"' << writer.quoteEntryOpen() << '"';
builder << ", ";
builder << R"("QuoteEntryControl": )";
builder << '"' << writer.quoteEntryControl() << '"';
builder << ", ";
{
bool atLeastOne = false;
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -194,7 +194,7 @@ public:
SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t sbeSchemaVersion() SBE_NOEXCEPT
{
return static_cast<std::uint16_t>(9);
return static_cast<std::uint16_t>(8);
}
SBE_NODISCARD static const char *yearMetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT
Loaded 100 of 149 files, more files were not shown because too many files have changed in this diff. Show more