Compare commits
27
Commits
push
..
v3.2.3-defi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19fef8492d | ||
|
|
7521b01022 | ||
|
|
d0faa95d7a | ||
|
|
36aa2caa2a | ||
|
|
97e7343110 | ||
|
|
5dccc2e9c3 | ||
|
|
365012102e | ||
|
|
c3d14c8e31 | ||
|
|
5cc80db09d | ||
|
|
12c2deb833 | ||
|
|
2222fb8e3e | ||
|
|
266fdf93e5 | ||
|
|
fd121bd85b | ||
|
|
1ffaf7c05b | ||
|
|
5a87288aaa | ||
|
|
a1d62e552c | ||
|
|
80656a0890 | ||
|
|
188daf2b84 | ||
|
|
5908adcb09 | ||
|
|
36cf021b70 | ||
|
|
d45e0e08a2 | ||
|
|
888e751243 | ||
|
|
bbe3350b4e | ||
|
|
80ea6ad16e | ||
|
|
0bc0e821f3 | ||
|
|
1b82d07a0d | ||
|
|
a83404f20f |
No files matched your search
Vendored
+9
-9
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
+31
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
+8
-326
@@ -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;
|
||||
|
||||
+4
-242
@@ -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;
|
||||
|
||||
+9
-363
@@ -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 "eEntryControl(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
Reference in new issue
Block a user