Merge branch 'master' of bitbucket.org:bluetradesys/spark into simplify_md_sdk

This commit is contained in:
Eric Harding committed 2018-11-16 11:23:51 -05:00
commit e22649c19d
69 files changed
+2026 -560

No files matched your search

+1
View File
@@ -0,0 +1 @@
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;/home/ppinto/Dropbox/bts/spark/doc/user/mkdocs.yml;bts-help-theme/404.html;bts-help-theme/base.html;bts-help-theme/content.html;bts-help-theme/css/base.css;bts-help-theme/css/bts.css;bts-help-theme/css/font-awesome.min.css;bts-help-theme/css/highlight.css;bts-help-theme/css/local.css;bts-help-theme/fonts/fontawesome-webfont.eot;bts-help-theme/fonts/glyphicons-halflings-regular.eot;bts-help-theme/img/favicon.ico;bts-help-theme/js/base.js;bts-help-theme/js/bootstrap-3.0.3.min.js;bts-help-theme/js/highlight.pack.js;bts-help-theme/js/jquery-1.10.2.min.js;bts-help-theme/keyboard-modal.html;bts-help-theme/main.html;bts-help-theme/nav-sub.html;bts-help-theme/nav.html;bts-help-theme/search-modal.html;bts-help-theme/toc.html;docs/Help.md;docs/Launcher.md;docs/Logging-In-to-the-System.md;docs/Market.md;docs/My-Windows.md;docs/Settings.md;docs/Spreader.md;docs/Trading.md;docs/images/image1.png;docs/images/image10.png;docs/images/image11.png;docs/images/image12.png;docs/images/image13.png;docs/images/image14.png;docs/images/image15.png;docs/images/image16.png;docs/images/image17.png;docs/images/image18.png;docs/images/image19.png;docs/images/image2.png;docs/images/image20.png;docs/images/image21.png;docs/images/image22.png;docs/images/image23.png;docs/images/image24.png;docs/images/image25.png;docs/images/image26.png;docs/images/image27.png;docs/images/image28.png;docs/images/image29.png;docs/images/image3.png;docs/images/image30.png;docs/images/image31.png;docs/images/image32.png;docs/images/image33.png;docs/images/image34.png;docs/images/image35.png;docs/images/image36.png;docs/images/image37.png;docs/images/image38.png;docs/images/image39.png;docs/images/image4.png;docs/images/image40.png;docs/images/image41.png;docs/images/image42.png;docs/images/image43.png;docs/images/image44.png;docs/images/image45.png;docs/images/image46.png;docs/images/image47.png;docs/images/image48.png;docs/images/image49.png;docs/images/image5.png;docs/images/image50.png;docs/images/image51.png;docs/images/image52.png;docs/images/image53.png;docs/images/image54.png;docs/images/image55.png;docs/images/image56.png;docs/images/image57.png;docs/images/image58.png;docs/images/image59.png;docs/images/image6.png;docs/images/image60.png;docs/images/image61.png;docs/images/image62.png;docs/images/image63.png;docs/images/image64.png;docs/images/image65.png;docs/images/image66.png;docs/images/image67.png;docs/images/image68.png;docs/images/image69.png;docs/images/image7.png;docs/images/image70.png;docs/images/image71.png;docs/images/image72.png;docs/images/image73.png;docs/images/image74.png;docs/images/image76.png;docs/images/image8.png;docs/images/image9.png;docs/index.md;docs/release_notes.md)
+1
View File
@@ -16,6 +16,7 @@ set(DOC_GLOBS
favicon.ico
)
file (GLOB_RECURSE All_Deps RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${DOC_GLOBS})
set (All_Deps ${All_Deps} ${CMAKE_CURRENT_SOURCE_DIR}/build.sh ${CMAKE_CURRENT_SOURCE_DIR}/mkdocs.yml)
update_deps_file("${All_Deps}")
# END DEPENDENCIES
+10 -1
View File
@@ -1,2 +1,11 @@
#!/bin/bash
python -m mkdocs build -d $1
tmpfile=$(mktemp /tmp/quiet_build_log.XXXX)
python -m mkdocs build -d $1 >$tmpfile 2>&1
if [ $? -eq 0 ]
then
rm $tmpfile
exit 0
else
cat $tmpfile
exit 1
fi
+13 -3
View File
@@ -1,14 +1,14 @@
<html>
<head>
<title>BTS Spark Socket To Socket Requote Times</title>
<title>BTS Spark Socket To Socket Requote and Hedge Times</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/perf_trace/compiled.css">
<script src="https://cdn.polyfill.io/v2/polyfill.js?features=es6"></script>
<style type="text/css">
.summary-block {
margin: 2em 1em;
padding: 1em 1em;
}
.summary-block > ul {
margin: 0em 1em;
@@ -23,6 +23,16 @@
.clear {
clear:both;
}
.session-table tr {
padding-top: 1em;
}
.session-table th {
background-color: #4169AA;
color:#fff;
text-align: center;
padding: 1em;
border-left: 1px solid #f6f6f6;
}
</style>
</head>
@@ -47,7 +57,7 @@
</ul>
</div-->
<div style="margin:2em 0 0 2em;"><!--div class="is-tab-content" is-tab-content is shorter than the screen to accommodate tabs at the top -->
<h1 class="is-size-2">Socket To Socket Requote Times</h1>
<h1 class="is-size-2">Socket To Socket Latencies</h1>
<div id="output"></div>
</div>
</div>
+195 -68
View File
@@ -2,7 +2,9 @@ $( document ).ready(function (){
let urlParams = new URLSearchParams(window.location.search);
num_rows = urlParams.get('r');
let show_rows = urlParams.get('showrows');
let rows_of = urlParams.get('rowsof');
//let trace_data_url = "./perf_trace_data";
let trace_data_url = "/perf_trace_data";
if(num_rows != null) {
trace_data_url += "/" + num_rows;
@@ -15,14 +17,46 @@ $( document ).ready(function (){
error: ajax_err,
crossDomain: true
}).done(function(result) {
if(show_rows != null) {
list_rows(show_rows, result);
if(rows_of === "quot") {
list_rows(show_rows, rows_of, result.requotes);
}
else {
list_rows(show_rows, rows_of, result.hedges);
}
}
else {
if(result.length > 0){
let summary_info = "<h1 class='is-size-3'><b>Host:</b> " + result[0].host + "</h1>";
if((result.requotes.length + result.hedges.length) > 0) {
let useful_result = (result.requotes.length > 0 ? result.requotes : result.hedges);
let less_useful_result = (result.requotes.length === 0 ? result.requotes : result.hedges);
let summary_info = "<h1 class='is-size-3'><b>Host:</b> " + useful_result[0].host + "</h1><br>";
$("#output").html(summary_info);
$(result).each(display_session);
let whole_result = result.requotes.concat(result.hedges);
let unique_sessions = whole_result.filter(function(value, index, self){
return self.find(function(elm){
return elm.session_name === value.session_name;
});
});
$(unique_sessions).each(function (index, sess_val){
let r = result.requotes.find(function(elm){
return elm.session_name === sess_val.session_name;
});
let h = result.hedges.find(function(elm){
return elm.session_name === sess_val.session_name;
});
display_session(r, h);
});
}
else {
}
}
@@ -30,29 +64,30 @@ $( document ).ready(function (){
});
function list_rows(show_rows, data) {
function list_rows(show_rows, rows_of, data) {
$.each(data, function(index, value){
if(value.session_name === show_rows){
let summary_info = "<table class='table bts-data-table'>";
summary_info += "<thead><tr><th>Trace Time</th>" +
"<th>Order Key</th>" +
"<th>MD</th>" +
"<th>Spreader</th>" +
"<th>Fix</th>" +
"<th>Total</th></tr></thead><tbody>";
summary_info += "<thead><tr><th>Trace Time</th>"
$.each(value.rows[0], function(key, unval){
if(key !== "trace_time" && key !== "total")
{
summary_info +="<th>" + format_title(key) + "</th>";
}
});
summary_info += "<th>Total</th></tr></thead><tbody>";
$.each(value.rows, function(key, val) {
summary_info += "<tr>";
console.log(val);
summary_info += "<td>" + val.trace_time + "</td>" +
"<td>" + val.order_key + "</td>" +
"<td>" + val.md + "</td>" +
"<td>" + val.spreader + "</td>" +
"<td>" + val.fix + "</td>" +
"<td>" + val.total + "</td>";
summary_info += "<td>" + val.trace_time + "</td>";
$.each(val, function(key, uval){
if(key !== "trace_time" && key !== "total")
{
summary_info += "<td>" + uval + "</td>";
}
});
summary_info += "<td>" + val.total + "</td>";
summary_info += "</tr>";
});
summary_info += "</tbody></table>";
@@ -77,7 +112,26 @@ function ajax_err (jqXHR, textStatus,errorThrown) {
function first_upper (str)
{
let ret_str = str[0].toUpperCase() + str.substring(1, str.length);
return ret_str
return ret_str;
}
function format_title (str)
{
let ret_str = "";
for(let i=0; i < str.length; i++) {
if(i === 0 || str[i-1] === "_") {
ret_str += str[i].toUpperCase();
}
else if(str[i] === "_") {
ret_str += " ";
}
else {
ret_str += str[i];
}
}
return ret_str;
}
function generate_histogram(id, data)
@@ -95,70 +149,143 @@ function generate_histogram(id, data)
Plotly.newPlot(id, plot_data);
}
function display_session (index, value) {
//console.log(value);
let session_info = "<ul class=''>";
session_info += "<li id='" + value.session_name + "' class='is-size-5'><b>Session:</b> " + value.session_name + "</li>";
session_info += "<li><b>Build Type:</b> " + value.build_type + "</li>";
session_info += "<li><b>Version:</b> " + value.version + "</li>";
function write_summary (histo_id, v)
{
session_info = "<ul>";
session_info += "<li><b>" + format_title(v.value) + "</b></li>";
session_info += "<li>Min: " + v.min + "</li>";
session_info += "<li>Median: " + v.median + "</li>";
session_info += "<li>Max: " + v.max + "</li>";
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
session_info += "</ul>";
session_info += "<div id='" + histo_id + "' class='histogram-div'></div>"
return session_info;
}
function display_session (quot_val, hed_val) {
let safe_quot_val = (quot_val != undefined) ? quot_val : {
session_name: hed_val.session_name,
build_type: hed_val.build_type,
version: hed_val.version,
n_rows: 0,
total: 0,
summaries: []
};
let safe_hed_val = (hed_val != undefined) ? hed_val : {
session_name: quot_val.session_name,
build_type: quot_val.build_type,
version: quot_val.version,
n_rows: 0,
total: 0,
summaries: []
};
let session_info = "<table class='session-table'>";
session_info += "<tr><td colspan='2' id='" + safe_quot_val.session_name + "' class='is-size-5'><b>Session:</b> " + safe_quot_val.session_name + "<br>";
session_info += "<b>Build Type:</b> " + safe_quot_val.build_type + "<br>";
session_info += "<b>Version:</b> " + safe_quot_val.version + "<br><br></td></tr>";
//column headers
session_info += "<tr><th>Requotes</th><th>Hedges</th></tr>"
if(num_rows != null) {
session_info += "<li><a href='?showrows=" + value.session_name + "&r=" + num_rows + "'><b>Rows:</b> " + value.n_rows + "</a></li>";
}
else {
session_info += "<li><a href='?showrows=" + value.session_name + "'><b>Rows:</b> " + value.n_rows + "</a></li>";
}
session_info += "<tr><td ><a href='?showrows=" + safe_quot_val.session_name + "&rowsof=quot" + "&r=" + num_rows + "'><b>Rows:</b> " + safe_quot_val.n_rows + "</a></td>";
session_info += "<td><a href='?showrows=" + safe_hed_val.session_name + "&rowsof=hed" + "&r=" + num_rows + "'><b>Rows:</b> " + safe_hed_val.n_rows + "</a></td></tr>";
}
else {
session_info += "<tr><td><a href='?showrows=" + safe_quot_val.session_name + "&rowsof=quot" + "'><b>Rows:</b> " + safe_quot_val.n_rows + "</a></td>";
session_info += "<td><a href='?showrows=" + safe_hed_val.session_name + "&rowsof=hed" + "'><b>Rows:</b> " + safe_hed_val.n_rows + "</a></td></tr>";
}
$(value.summaries).each(function(i,v){
session_info += "<tr><td class='summary-block'>";
$(safe_quot_val.summaries).each(function(i,v){
if(v.value === "total") {
session_info += "<li class='summary-block'><ul>";
session_info += "<li><b>" + first_upper(v.value) + "</b></li>";
session_info += "<li>Min: " + v.min + "</li>";
session_info += "<li>Median: " + v.median + "</li>";
session_info += "<li>Max: " + v.max + "</li>";
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
session_info += "</ul>";
session_info += "<div id='" + value.session_name + "-" + v.value + "' class='histogram-div'></div>"
session_info += "</li>";
return false
session_info += write_summary(safe_quot_val.session_name + "-quot-" + v.value, v);
return false
}
});
});
$(value.summaries).each(function(i,v){
if(v.value !== "total") {
session_info += "<li class='summary-block'><ul>";
session_info += "<li><b>" + first_upper(v.value) + "</b></li>";
session_info += "<li>Min: " + v.min + "</li>";
session_info += "<li>Median: " + v.median + "</li>";
session_info += "<li>Max: " + v.max + "</li>";
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
session_info += "</ul>";
session_info += "<div id='" + value.session_name + "-" + v.value + "' class='histogram-div'></div>"
session_info += "</li>";
session_info += "</td>";
session_info += "<td class='summary-block'>";
$(safe_hed_val.summaries).each(function(i,v){
if(v.value === "total") {
session_info += write_summary(safe_hed_val.session_name + "-hed-" + v.value, v);
return false
}
});
});
session_info += "</ul><hr class='clear'>";
session_info += "</td></tr>";
let loop_val = 0;
if(safe_quot_val.summaries.length >= safe_hed_val.summaries.length ) {
loop_val = safe_quot_val.summaries.length;
}
else {
loop_val = safe_hed_val.summaries.length;
}
/*
for(let i = 0; i < loop_val; i++) {
session_info += "<tr><td class='summary-block'>";
if (safe_quot_val.summaries[i] !== undefined && safe_quot_val.summaries[i].value !== "total") {
session_info += write_summary(safe_quot_val.session_name + "-quot-" + safe_quot_val.summaries[i].value, safe_quot_val.summaries[i]);
}
session_info += "</td><td class='summary-block'>";
if (safe_hed_val.summaries[i] !== undefined && safe_hed_val.summaries[i].value !== "total") {
session_info += write_summary(safe_hed_val.session_name + "-hed-" + safe_hed_val.summaries[i].value, safe_hed_val.summaries[i]);
}
session_info += "</td></tr>";
}
*/
session_info += "</table><hr class='clear'>";
$("#output").append(session_info);
let all_data = {};
let quot_val_data = {};
$(value.rows).each(function(i, row) {
$(safe_quot_val.rows).each(function(i, row) {
$.each(row, function(name, v) {
if(all_data[name] === undefined){
all_data[name] = [v];
if(quot_val_data[name] === undefined){
quot_val_data[name] = [v];
}
else {
all_data[name].push(v);
quot_val_data[name].push(v);
}
});
});
//console.log(all_data);
let hed_val_data = {};
$(value.summaries).each(function(i,v){
generate_histogram(value.session_name + "-" + v.value, all_data[v.value]);
$(safe_hed_val.rows).each(function(i, row) {
$.each(row, function(name, v) {
if(hed_val_data[name] === undefined){
hed_val_data[name] = [v];
}
else {
hed_val_data[name].push(v);
}
});
});
if(safe_quot_val.summaries.length > 0){
generate_histogram(safe_quot_val.session_name + "-quot-total", quot_val_data["total"]);
}
/*
$(safe_quot_val.summaries).each(function(i,v){
generate_histogram(safe_quot_val.session_name + "-quot-" + v.value, quot_val_data[v.value]);
});
*/
if(safe_hed_val.summaries.length > 0){
generate_histogram(safe_hed_val.session_name + "-hed-total", hed_val_data["total"]);
}
/*
$(safe_hed_val.summaries).each(function(i,v){
generate_histogram(safe_hed_val.session_name + "-hed-" + v.value, hed_val_data[v.value]);
});
*/
}
+1 -1
View File
@@ -1 +1 @@
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/thread_manager.cpp)
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.cpp;orderman/actors/MDSupplementarySource.cpp;orderman/actors/OrderStore.cpp;orderman/actors/SODPosition.cpp;orderman/actors/SecuritySrc.cpp;orderman/actors/ServiceBridge.cpp;orderman/actors/order_manager/OrderBuilder.cpp;orderman/actors/order_manager/OrderManager.cpp;orderman/actors/order_manager/OrderServicesData.cpp;orderman/events/CancelRejectEvent.cpp;orderman/events/NewExecutionReportEvent.cpp;orderman/limiter/LimitStore.cpp;orderman/limiter/Limiter.cpp;orderman/limiter/ProductLimitNode.cpp;orderman/limiter/ProductLimitTree.cpp;orderman/limiter/SecurityLimitNode.cpp;orderman/limiter/SecurityLimitTree.cpp;orderman/limiter/tests/limit_tests.cpp;orderman/order_factory/DynamiLine truncated
@@ -4,7 +4,10 @@
#include "lib/config/protos/cme_reference_data.pb.h"
#include "lib/config/protos/jetfix.pb.h"
#include "lib/config/config.h"
#include "lib/config/protos/system.pb.h"
#include <sstream>
#include <boost/algorithm/string/case_conv.hpp>
namespace bts{
namespace admin{
@@ -83,8 +86,7 @@ AdminServiceUtils::resolve_cme_connection(const bts::api::admin::AdminData& data
const bts::api::admin::ExchangeConnection*
AdminServiceUtils::resolve_sim_connection(const bts::api::admin::AdminData& data,
const bts::api::admin::BTSAccount& account,
const bts::api::Security& sec){
bts_assert(sec.exchange() == api::Venue::VENUE_SIM);
const bts::api::Security& ){
if(account.has_connections())
for (const std::string& conn_name : account.connections().exchange_connection_names()){
const bts::api::admin::ExchangeConnection* conn = find_connection(data, conn_name);
@@ -100,24 +102,30 @@ AdminServiceUtils::resolve_connection(const bts::api::admin::AdminData& data,
const bts::api::Security& sec){
const bts::api::admin::ExchangeConnection* rv = nullptr;
switch(sec.exchange()){
case api::Venue::VENUE_GLBX:
rv = resolve_cme_connection(data, account, sec);
break;
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_SIM:
case api::Venue::VENUE_GLBX: {
auto deployment_type =
boost::to_lower_copy(config::get<config::DeploymentType>().type());
rv = resolve_cme_connection(data, account, sec);
if (rv == nullptr && (deployment_type == "dev" ||
deployment_type == "demo"))
rv = resolve_sim_connection(data, account, sec);
break;
case api::Venue::VENUE_CBOE:
case api::Venue::VENUE_CBOECFE:
case api::Venue::VENUE_ICE:
case api::Venue::Venue_INT_MIN_SENTINEL_DO_NOT_USE_:
break;
}
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_SIM:
rv = resolve_sim_connection(data, account, sec);
break;
case api::Venue::VENUE_CBOE:
case api::Venue::VENUE_CBOECFE:
case api::Venue::VENUE_ICE:
case api::Venue::Venue_INT_MIN_SENTINEL_DO_NOT_USE_:
case api::Venue::Venue_INT_MAX_SENTINEL_DO_NOT_USE_:
rv = nullptr;
break;
rv = nullptr;
break;
}
return rv;
}
}
////////////////////////////////////////////
// connection mutators /////////////////////
////////////////////////////////////////////
+3 -11
View File
@@ -204,7 +204,7 @@ message OrderState {
CoverOrderState cover = 22;
OffsetOrderState offset = 24;
DynamicOrderState dynamic = 1000;
DynamicOrderState dynamic = 255; // do not use anything bigger than 255, or things will break
}
}
@@ -267,7 +267,7 @@ message LimitOrderState {NativeOrderState native_state = 1;}
message StopOrderState {NativeOrderState native_state = 1;}
message StopLimitOrderState {NativeOrderState native_state = 1;}
message IcebergOrderState {}
message SpreaderOrderState {SpreaderChildKind child_kind = 1;}
message SpreaderOrderState {}
message TimeBoxOrderState {}
message TickOrderState {}
message LeanOrderState {}
@@ -276,9 +276,7 @@ message TopOrderState {}
message TriggerOrderState {}
message CoverOrderState {}
message OffsetOrderState {}
message DynamicOrderState {
map<string,string> state = 1;
}
message DynamicOrderState {}
enum OrderStatus {
@@ -403,12 +401,6 @@ enum ExecType
EXEC_TYPE_TRADE_CANCEL = 72 ;
}
enum SpreaderChildKind {
SPREADER_CHILD_KIND_UNDEFINED = 0;
SPREADER_CHILD_KIND_SPREADER_HEDGING = 1;
SPREADER_CHILD_KIND_SPREADER_QUOTING = 2;
}
enum OrderError
{
ORDER_ERROR_UNDEFINED = 0;
+1
View File
@@ -8,4 +8,5 @@ message OrdermanCfg {
repeated string hosted_actor = 3;
}
repeated Executor executor = 1;
optional uint32 default_iceberg_pause_secs = 2 [default = 0];
}
@@ -163,6 +163,7 @@ void AsyncReader::dispatch ()
for (auto &msg : framed_msgs) {
msg.set_trace(trace);
LOG(Latency, Diag, "perf_event AsyncReader.on_fix");
tracepoint(bts_trace, trace_order, (char *)"AsyncReader.on_fix", 0,
msg.get_seq_num(), 0);
me->sink.in_msg_raw (msg);
+14 -11
View File
@@ -6,7 +6,6 @@
#include "lib/utils/TimeStamp.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/helper_macros.h"
#include "lib/orderman/orderman_utils/PerfTraceLog.h"
#include <arpa/inet.h>
#include <fstream>
@@ -84,6 +83,7 @@ int SocketConnection::read(char *to_buffer, size_t read_count) {
bts_assert(_fd >= 0);
auto result = ::read(_fd, to_buffer, read_count);
LOG(Latency, Diag, "perf_event after_socket_read");
if (_log && result > 0) {
_in_log.write(to_buffer, result);
_in_log.flush();
@@ -123,18 +123,20 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
trace.md_sn, s2s.microseconds());
}
LOG(Latency, Diag, "perf_event before_socket_write");
auto result = ::writev(_fd, iv, (int)vec_size);
if (trace.md.to_uint64()) {
LOG(Latency, Info,
"perf_event:tick_to_order s2s_us:{} fix_socket_r:{} md:{} "
"perf_event:tick_to_order s2s_us:{} do_work_begin:{} fix_socket_r:{} "
"md:{} "
"md_src:{} spreader_on_book:{} spreader_modify_order:{} "
"spreader_on_child_order_update:{} "
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{} order_key:{} "
"send_fix_buffer:{}",
s2s.microseconds(), trace_cpy.fix_socket_r.to_uint64(),
trace_cpy.md.to_uint64(), trace_cpy.md_src.to_uint64(),
trace_cpy.spreader_on_book.to_uint64(),
s2s.microseconds(), trace_cpy.do_work_begin.to_uint64(),
trace_cpy.fix_socket_r.to_uint64(), trace_cpy.md.to_uint64(),
trace_cpy.md_src.to_uint64(), trace_cpy.spreader_on_book.to_uint64(),
trace_cpy.spreader_modify_order.to_uint64(),
trace_cpy.spreader_on_child_order_update.to_uint64(),
trace_cpy.order_update.to_uint64(), trace_cpy.exe_gw.to_uint64(),
@@ -145,19 +147,20 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
if (trace.fix_socket_r.to_uint64()) {
LOG(Latency, Info,
"perf_event:fill_to_order s2s_us:{} fix_socket_r:{} md:{} md_src:{} "
"perf_event:fill_to_order s2s_us:{} do_work_begin:{} fix_socket_r:{} "
"md:{} md_src:{} "
"spreader_on_book:{} spreader_modify_order:{} "
"spreader_on_child_order_update:{} "
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{} order_key:{} "
"send_fix_buffer:{} ",
s2s.microseconds(), trace_cpy.fix_socket_r.to_uint64(),
trace_cpy.md.to_uint64(), trace_cpy.md_src.to_uint64(),
trace_cpy.spreader_on_book.to_uint64(),
s2s.microseconds(), trace_cpy.do_work_begin.to_uint64(),
trace_cpy.fix_socket_r.to_uint64(), trace_cpy.md.to_uint64(),
trace_cpy.md_src.to_uint64(), trace_cpy.spreader_on_book.to_uint64(),
trace_cpy.spreader_modify_order.to_uint64(),
trace_cpy.spreader_on_child_order_update.to_uint64(),
trace_cpy.order_update.to_uint64(), trace_cpy.exe_gw.to_uint64(),
trace_cpy.fix_socket_w.to_uint64(), trace_cpy.md_sn, trace.order_key,
trace.send_fix_buffer.to_uint64());
trace_cpy.fix_socket_w.to_uint64(), trace_cpy.md_sn, trace_cpy.order_key,
trace_cpy.send_fix_buffer.to_uint64());
_perf_log.log(trace_cpy);
}
+2
View File
@@ -394,6 +394,7 @@ void CoreGateway::sync_reader_disconnected(EventData const &) {
}
void CoreGateway::sync_in_msg_raw(EventData const &ev) {
LOG(Latency, Diag, "perf_event sync_in_msg_raw");
switch (me->state) {
case State::OFF:
case State::PAUSED:
@@ -412,6 +413,7 @@ void CoreGateway::sync_in_msg_raw(EventData const &ev) {
}
void CoreGateway::sync_in_msg_sequenced(EventData const &ev) {
LOG(Latency, Diag, "perf_event sync_in_msg_sequenced");
switch (me->state) {
case State::OFF:
case State::PAUSED:
+2
View File
@@ -83,8 +83,10 @@ private:
void dispatch (InMsgBuffer const&in_msg) override
{
LOG(Latency, Diag, "perf_event JetFIXGateway::before_parse");
msg.read (in_msg.data (),in_msg.size ());
msg.set_trace(in_msg.trace());
LOG(Latency, Diag, "perf_event JetFIXGateway::after_parse");
handler (msg);
}
+1
View File
@@ -467,6 +467,7 @@ void Session::on_hb(InMsgBuffer const &msg) {
}
void Session::on_app(InMsgBuffer const &msg) {
LOG(Latency, Diag, "perf_event on_app");
switch (me->state) {
case State::WLOGON:
terminate_session(
+9
View File
@@ -54,6 +54,15 @@ struct LoggerHandle {
Logger *logger;
};
struct CharPtrWrapper
{const char* ptr;};
inline std::ostream& operator<<(std::ostream&s,CharPtrWrapper const&ptr)
{
s << ptr.ptr;
return s;
}
extern thread_local LoggerHandle _thread_logger;
void configure_logging(LoggingConfig const&cfg);
void configure_logging(bts::config::Logging const&cfg,bts::config::ThreadGroupCfg const&);
+1 -1
View File
@@ -117,7 +117,7 @@ private:
// Private default constructor allows friend classes to store this class in a concurrent_queue
MDProducerHandle();
friend class tbb::concurrent_queue < MDProducerHandle > ;
friend class SecurityIDRecorderState;
friend struct SecurityIDRecorderState;
friend struct bts::co2::Work;
HotEntry& hdata() const {
+30 -5
View File
@@ -4,6 +4,7 @@
#include "lib/mercury/topics/ITopic.h"
#include "lib/utils/Puid.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/bts_chrono.h"
#include <algorithm>
#include <boost/container/flat_map.hpp>
@@ -30,13 +31,15 @@ struct Broker::LocalSubFilter {
struct Broker::StreamRec {
StreamRec(Broker &owner, IRemoteBroker &src, StreamID sid,
TopicPtr const &topic)
: owner(owner), local(false), src(&src), sid(sid), topic(topic),
local_subs(), external_subs(), sub_id(),
: owner(owner), local(false), src(&src), sid(sid),
topic_str(intern_topic_str(topic)), topic(topic), local_subs(),
external_subs(), sub_id(),
synopsis_builder(topic->make_synopsis_builder()),
topic_tag(topic->to_str()) {}
StreamRec(Broker &owner, StreamID sid, TopicPtr const &topic)
: owner(owner), local(true), src(), sid(sid), topic(topic), local_subs(),
: owner(owner), local(true), src(), sid(sid),
topic_str(intern_topic_str(topic)), topic(topic), local_subs(),
external_subs(), sub_id(),
synopsis_builder(topic->make_synopsis_builder()),
topic_tag(topic->to_str()) {}
@@ -56,10 +59,21 @@ struct Broker::StreamRec {
IRemoteBroker *neighbour;
};
char *intern_topic_str(TopicPtr const &topic) {
// this C string is used to make it possible to log topic names
// assynchronously, without copying strings around. we leak
// topic names, which isn't great, but performance oblige.
auto ts = topic->to_str();
auto result = new char[ts.size() + 1];
memcpy(result, ts.c_str(), ts.size() + 1);
return result;
}
Broker &owner;
const bool local;
IRemoteBroker *const src;
const StreamID sid;
const char *topic_str;
const TopicPtr topic;
std::vector<LocalSubRec> local_subs;
@@ -337,15 +351,26 @@ void Broker::notify(BrokerID sender, StreamID sid, SubID ex_subid,
Notification const &payload) {
NoReenter nr(me->inside);
#else
void Broker::notify(BrokerID , StreamID sid, SubID ex_subid,
void Broker::notify(BrokerID, StreamID sid, SubID ex_subid,
Notification const &payload) {
#endif
auto &srec = me->streams[sid];
bts_assert_d(srec);
bts_assert_d((srec->is_local() && sender == id()) ||
(!srec->is_local() && srec->src->id() == sender));
if (srec->is_local() || srec->sub_id == ex_subid)
if (srec->is_local() || srec->sub_id == ex_subid) {
LOG(Latency, Diag, "Started handling event {} on broker {}",
log::CharPtrWrapper{srec->topic_str}, me->my_id.value());
auto start = Timer::now();
srec->notify(payload);
auto stop = Timer::now();
LOG(Latency, Diag, "Finished handling event {} on broker {}. Time: {}us",
log::CharPtrWrapper{srec->topic_str}, me->my_id.value(),
us_diff(stop, start));
}
}
void Broker::subscribe(BrokerID sender, SubID ex_subid, StreamID sid) {
+1
View File
@@ -7,6 +7,7 @@ namespace mercury {
BrokerProxy::BrokerProxy(ILocalBroker &real_broker)
: _real_broker(real_broker), _id(real_broker.id()),
_cycles(0),
_outgoing(config::get<config::Mercury>().queue_size()),
_incoming(config::get<config::Mercury>().queue_size()),
_action_pool() {}
+13 -2
View File
@@ -59,7 +59,7 @@ class BrokerProxy : public ILocalBroker {
void local_unsubscribe(LocalSubID) override;
// ILocalBroker
void drain() { _outgoing.drain();}
void drain() { _outgoing.drain(); }
void schedule_action(std::function<void()> const &action);
// these are just for performance, we want to allow for
@@ -80,6 +80,7 @@ class BrokerProxy : public ILocalBroker {
}
void do_work() {
_cycles++;
_outgoing.consume_all([this](BrokerMsg *m) {
switch (m->msg_type) {
case BrokerMsgType::ADD_NEIGHBOUR:
@@ -101,6 +102,8 @@ class BrokerProxy : public ILocalBroker {
_real_broker.unsubscribe(m->broker_id, m->sub_id, m->stream_id);
break;
case BrokerMsgType::NOTIFY:
LOG(Latency, Diag, "Notify broker {}. Cycles: {}", _id.value(),
_cycles);
_real_broker.notify(m->broker_id, m->stream_id, m->sub_id,
m->notification);
break;
@@ -111,9 +114,16 @@ class BrokerProxy : public ILocalBroker {
case BrokerMsgType::LOCAL_UNSUBSCRIBE:
_real_broker.local_unsubscribe(m->local_sub_id);
break;
case BrokerMsgType::EXECUTE_ACTION:
case BrokerMsgType::EXECUTE_ACTION: {
auto start = Timer::now();
LOG(Latency, Diag, "Executing lambda on broker {}", _id.value());
m->action();
auto stop = Timer::now();
LOG(Latency, Diag,
"Finished executing lambda on broker {}. Took: {}us",
_id.value(), us_diff(stop, start));
break;
}
default:
bts_assert(false);
break;
@@ -126,6 +136,7 @@ class BrokerProxy : public ILocalBroker {
private:
ILocalBroker &_real_broker;
const BrokerID _id;
uint64_t _cycles;
utils::GrowableSPSCQueue<BrokerMsg *> _outgoing;
utils::GrowableSPSCQueue<BrokerMsg *> _incoming;
boost::object_pool<BrokerMsg> _action_pool;
+1 -1
View File
@@ -14,7 +14,7 @@ using TopicFilter = std::function<bool(const ITopic&)> ;
using StreamID = utils::ValueWrapper<struct StreamIDTag,uint32_t>;
using SubID = utils::ValueWrapper<struct SubIDTag,uint32_t>;
using Notification = FatWrapper<80>;
using Notification = FatWrapper<112>;
using LocalSubID = utils::ValueWrapper<struct LocalSubIDTag,uint32_t>;
using BrokerID = utils::ValueWrapper<struct BrokerIDTag,uint32_t>;
+24 -14
View File
@@ -1,15 +1,16 @@
#include "ExecutionGateway.h"
#include "lib/logging/logging.h"
#include "lib/utils/TimeStamp.h"
#include "lib/jetfix/codec/MessageDefinitions.h"
#include "lib/jetfix/common/OutFIX.h"
#include "lib/jetfix/gateway/JetFIXGateway.h"
#include "lib/logging/logging.h"
#include "lib/orderman/events/CancelRejectEvent.h"
#include "lib/orderman/events/GatewayStatusChangeEvent.h"
#include "lib/orderman/events/NewExecutionReportEvent.h"
#include "lib/orderman/events/NewSecuritiesEvent.h"
#include "lib/orderman/orderman_utils/fix_order_codec.h"
#include "lib/tracing/bts_trace.h"
#include "lib/jetfix/common/OutFIX.h"
#include "lib/utils/TimeStamp.h"
#include "lib/utils/bts_chrono.h"
#include <boost/pool/object_pool.hpp>
@@ -18,17 +19,16 @@ namespace orderman {
using namespace jetfix;
using JFGw =
JetFIXGateway<fix::ExecutionReportMsg, fix::BusinessMessageRejectMsg,
fix::RejectMsg, fix::CancelRejectMsg>;
struct ExecutionGateway::PImpl {
PImpl(config::JetFIX::Connection const &cfg,PerfTraceLog &log)
: gw(cfg,log), exe_report_pool(), state(State::WAITING_FOR_SECURITIES),
PImpl(config::JetFIX::Connection const &cfg, PerfTraceLog &log)
: gw(cfg, log), exe_report_pool(), state(State::WAITING_FOR_SECURITIES),
securities(), securities_by_descriptor(), exe_report_stream(),
gw_status_change_stream(), cancel_reject_stream(), gw_name(cfg.name()),
cfg(cfg) {}
cfg(cfg) {}
JFGw gw;
boost::object_pool<ExecutionReport> exe_report_pool;
@@ -43,8 +43,9 @@ struct ExecutionGateway::PImpl {
config::JetFIX::Connection cfg;
};
ExecutionGateway::ExecutionGateway(config::JetFIX::Connection const &cfg,PerfTraceLog &log)
: me(new PImpl(cfg,log)) {}
ExecutionGateway::ExecutionGateway(config::JetFIX::Connection const &cfg,
PerfTraceLog &log)
: me(new PImpl(cfg, log)) {}
ExecutionGateway::~ExecutionGateway() {}
void ExecutionGateway::init() {
@@ -77,6 +78,7 @@ void ExecutionGateway::on_securities(
// subscribe to gateway events
me->gw.on<fix::ExecutionReportMsg>() = [this](auto const &msg) {
LOG(Latency, Diag, "perf_event ExecutionGateway::after_parse");
this->on_exe_report(msg);
};
me->gw.on<fix::CancelRejectMsg>() = [this](auto const &msg) {
@@ -114,10 +116,13 @@ void ExecutionGateway::on_securities(
void ExecutionGateway::on_exe_report(
const jetfix::fix::ExecutionReportMsg &msg) {
auto start = Timer::now();
OrderKey ok;
SecurityKey sk;
decode_clorid(msg.get<fix::ClOrdID>(), &ok, &sk);
LOG(Latency, Info, "Started handling of execution report for order {}",
ok.value());
auto sec = me->securities[sk.value()];
if (!sec)
@@ -190,7 +195,7 @@ void ExecutionGateway::on_exe_report(
data->exec_type = api::orderman::ExecType(msg.get<fix::ExecType>());
data->msg_seq_num = msg.get<fix::MsgSeqNum>();
tracepoint(bts_trace, trace_order,
(char *)"ExecutionGateway.on_exe_report", 0, 0,0);
(char *)"ExecutionGateway.on_exe_report", 0, 0, 0);
// copy data from fix msg to our structure
if (msg.has<fix::Account>())
@@ -239,14 +244,14 @@ void ExecutionGateway::on_exe_report(
data->last_shares = msg.has<fix::LastShares>()
? Quantity(msg.get<fix::LastShares>())
: Quantity(0);
auto last_px_sec = data->leg_security? data->leg_security:sec;
auto last_px_sec = data->leg_security ? data->leg_security : sec;
data->last_price = msg.has<fix::LastPx>()
? securities::decode_exchange_price(
*last_px_sec, Price(msg.get<fix::LastPx>()))
: Price(0);
data->avg_price = msg.has<fix::AvgPx>()
? securities::decode_exchange_price(
*last_px_sec, Price(msg.get<fix::AvgPx>()))
*last_px_sec, Price(msg.get<fix::AvgPx>()))
: Price(0);
if (msg.has<fix::ExecID>())
str_to(msg.get<fix::ExecID>(), &data->exchange_fill_id);
@@ -255,7 +260,10 @@ void ExecutionGateway::on_exe_report(
data->msg = text_msg;
auto trace = msg.trace();
trace.exe_gw = utils::TimeStamp::now();
publish(me->exe_report_stream, data,trace);
publish(me->exe_report_stream, data, trace);
LOG(Latency, Info,
"Finished handling of execution report for order {}. {}ns",
ok.value(), ns_diff(Timer::now(), start));
}
}
@@ -306,11 +314,13 @@ void ExecutionGateway::on_outgoing_fix(
mercury::Update<OutFIXMsgEvent> const &msg) {
if (msg.events)
for (auto const &ev : *(msg.events)) {
LOG(Latency, Info, "on_outgoing_fix sn: {} ok: {}", ev.trace.md_sn,
ev.trace.order_key);
tracepoint(bts_trace, trace_order, (char *)"on_outgoing_fix", 0,
ev.trace.md_sn, 0);
auto trace = ev.trace;
trace.exe_gw = utils::TimeStamp::now();
me->gw.send(trace,ev.msg);
me->gw.send(trace, ev.msg);
me->gw.dispatch_events();
}
}
+6 -4
View File
@@ -76,9 +76,11 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
private:
void on_poll() override {
Book book;
PerfTrace trace;
auto start = Timer::now();
PerfTrace trace;
auto start = utils::TimeStamp::now();
trace.do_work_begin = start;
for (auto &h : _handles) {
auto &handle = h.handle;
if (handle.get_book(h.last, std::tuple_size<BookSide>(),
@@ -93,8 +95,8 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
publish(_book_update, book, trace);
}
}
auto stop = Timer::now();
_loop_timer->observe(ns_diff(stop, start));
auto stop = utils::TimeStamp::now();
_loop_timer->observe((start-stop).nanoseconds());
}
struct HandleRecord {
+8 -5
View File
@@ -29,7 +29,7 @@ struct OrderStore::PImpl {
State state;
const GatewayName gw_name;
std::unordered_map<OrderKey, google::protobuf::Timestamp> known_orders;
std::unordered_map<OrderKey, api::orderman::OrderState> known_orders;
std::unique_ptr<persistence::IStore> store;
mercury::Stream<StoredOrderEvent> order_stream;
mercury::Actor::SubHandle stored_sec_sub;
@@ -93,7 +93,7 @@ void OrderStore::init() {
} else {
if (!_order_cutoff(rec))
stored_batch.emplace_back(rec);
me->known_orders[ok] = rec.state().created();
me->known_orders[ok] = rec.state();
}
} else
LOG(OrderMan, Error,
@@ -122,19 +122,22 @@ void OrderStore::on_order_state_update(
order.set_key(ev.key.value());
order.set_parent_key(ev.parent_key.value());
*order.mutable_def() = *ev.definition;
*order.mutable_state() = ev.state;
*order.mutable_state() = ev.rebuild_state();
*order.mutable_state()->mutable_last_change() = utils::pb::pb_now();
auto it = me->known_orders.find(ev.key);
if (it == me->known_orders.end()) {
// new order
auto now = utils::pb::pb_now();
me->known_orders[ev.key] = now;
*order.mutable_state()->mutable_created() = now;
me->known_orders[ev.key] = order.state();
me->store->insert(orders_ns, std::to_string(order.key()),
utils::pb::to_string(order));
} else {
*order.mutable_state()->mutable_created() = it->second;
// existing order
auto &existing_state = it->second;
existing_state.MergeFrom(order.state());
*order.mutable_state() = existing_state;
me->store->update(orders_ns, std::to_string(order.key()),
utils::pb::to_string(order));
}
+1 -1
View File
@@ -147,7 +147,7 @@ ServiceBridge::create_order(api::orderman::Create const &c) {
me->order_factory.create(null_order_key(), c.new_order());
me->known_orders.insert(order.key());
publish(me->activate_order_pub, order, Price(c.price()),
Quantity(c.quantity()));
Quantity(c.quantity()), PerfTrace());
r.set_order_key(order.key().value());
r.set_error_code(api::orderman::ORDER_ERROR_OK);
} else
@@ -11,7 +11,9 @@
#include "lib/orderman/order_factory/OrderFactory.h"
#include "lib/orderman/orders/Order.h"
#include "lib/orderman/securities/ISecurityDB.h"
#include "lib/utils/inplace_function.h"
#include <algorithm>
#include <unordered_map>
namespace bts {
@@ -40,18 +42,24 @@ struct OrderManager::OrderInfo {
};
struct OrderManager::PImpl {
PImpl(Actor &owner, DynamicOrderRegistry const &b)
: state(State::WAIT_ADMIN), services_data(owner, b) {}
PImpl(OrderManager &owner, DynamicOrderRegistry const &b)
: state(State::WAIT_ADMIN), services_data(owner, b) {
pending_order_events.reserve(1000);
}
State state;
std::unordered_map<OrderKey, OrderInfo> orders;
std::vector<PendingOrderEvent> pending_order_events;
mercury::Actor::SubHandle stored_order_sub;
mercury::Actor::SubHandle stored_fills_sub;
OrderServicesData services_data;
mercury::Stream<OrderManagerReadyEvent> pub_ready;
};
OrderManager::OrderManager(DynamicOrderRegistry const&b) : me(new PImpl(*this,b)) {}
OrderManager::OrderManager(DynamicOrderRegistry const &b)
: me(new PImpl(*this, b)) {}
OrderManager::~OrderManager() {}
void OrderManager::init() {
@@ -193,66 +201,82 @@ void OrderManager::on_execution_report(
}
}
void OrderManager::queue_order_event(PendingOrderEvent const &event_handler) {
me->pending_order_events.push_back(event_handler);
}
void OrderManager::on_activate_order(
const mercury::Update<ActivateOrderEvent> &m) {
if (m.events)
for (auto const &ev : *m.events) {
auto order = ev.order;
auto k = order->key();
bts_assert(me->orders.find(k) == me->orders.end());
OrderInfo oi{*order, me->services_data};
auto serv_ptr = oi.order_services.get();
me->orders[k] = std::move(oi);
order->on_activate(ev.price, ev.quantity, *serv_ptr, ev.trace);
}
for (auto const &ev : *m.events)
queue_order_event(ev);
}
void OrderManager::handle_order_event(ActivateOrderEvent const &ev) {
auto order = ev.order;
auto k = order->key();
bts_assert(me->orders.find(k) == me->orders.end());
OrderInfo oi{*order, me->services_data};
auto serv_ptr = oi.order_services.get();
me->orders[k] = std::move(oi);
order->on_activate(ev.price, ev.quantity, *serv_ptr, ev.trace);
}
void OrderManager::on_update_order(const mercury::Update<UpdateOrderEvent> &m) {
if (m.events)
for (auto const &ev : *m.events) {
auto k = ev.key();
auto it = me->orders.find(k);
if (it != me->orders.end())
it->second.order->on_update(ev.price, ev.quantity, ev.trace);
else {
LOG(OrderMan, Error,
"Received an update request for an unknown order {}. "
"This should not happen and likely indicates a serious bug. ",
ev.key().value());
}
}
for (auto const &ev : *m.events)
queue_order_event(ev);
}
void OrderManager::handle_order_event(UpdateOrderEvent const &ev) {
auto k = ev.key();
auto it = me->orders.find(k);
if (it != me->orders.end())
it->second.order->on_update(ev.price, ev.quantity, ev.trace);
else {
LOG(OrderMan, Error,
"Received an update request for an unknown order {}. "
"This should not happen and likely indicates a serious bug. ",
ev.key().value());
}
}
void OrderManager::on_cancel_order(const mercury::Update<CancelOrderEvent> &m) {
if (m.events)
for (auto const &ev : *m.events) {
auto k = ev.key();
auto it = me->orders.find(k);
if (it != me->orders.end())
it->second.order->on_cancel(ev.trace);
else {
LOG(OrderMan, Error,
"Received a cancel request for an unknown order {}. "
"This should not happen and likely indicates a serious bug. ",
ev.key().value());
}
}
for (auto const &ev : *m.events)
queue_order_event(ev);
}
void OrderManager::handle_order_event(CancelOrderEvent const &ev) {
auto k = ev.key();
auto it = me->orders.find(k);
if (it != me->orders.end())
it->second.order->on_cancel(ev.trace);
else {
LOG(OrderMan, Error,
"Received a cancel request for an unknown order {}. "
"This should not happen and likely indicates a serious bug. ",
ev.key().value());
}
}
void OrderManager::on_crack_order(const mercury::Update<CrackOrderEvent> &m) {
if (m.events)
for (auto const &ev : *m.events) {
auto k = ev.key();
auto it = me->orders.find(k);
if (it != me->orders.end())
it->second.order->on_crack(ev.trace);
else {
LOG(OrderMan, Error,
"Received a crack request for an unknown order {}. "
"This should not happen and likely indicates a serious bug. ",
ev.key().value());
}
}
for (auto const &ev : *m.events)
queue_order_event(ev);
}
void OrderManager::handle_order_event(CrackOrderEvent const &ev) {
auto k = ev.key();
auto it = me->orders.find(k);
if (it != me->orders.end())
it->second.order->on_crack(ev.trace);
else {
LOG(OrderMan, Error,
"Received a crack request for an unknown order {}. "
"This should not happen and likely indicates a serious bug. ",
ev.key().value());
}
}
void OrderManager::on_gateway_state(
@@ -277,6 +301,13 @@ void OrderManager::on_timer(const mercury::Update<mercury::TimerEvent> &m) {
if (m.events)
for (auto ev : *m.events)
me->services_data.last_timer_ev = ev;
for (auto &osd : me->services_data.sessions) {
if (fix_buffer_pool_size > osd->free_buffers.size()) {
auto need = fix_buffer_pool_size - osd->free_buffers.size();
publish(me->services_data.create_fix_req_pub, need, osd->cfg);
}
}
}
std::vector<jetfix::OutFIXPtr> &
@@ -339,6 +370,7 @@ void OrderManager::init_order_services() {
// FIX output
s->out_fix_pub = make_stream<OutFIXMsgEvent>(gw_name);
reload_fix_buffer(*s);
}
// Order creation and maintenance
@@ -360,7 +392,6 @@ void OrderManager::init_order_services() {
[this](auto const &ev) { this->on_new_fix_buffer(ev); });
// Outputs
me->services_data.order_state_pub = make_stream<OrderStateUpdateEvent>();
me->services_data.activate_order_pub = make_stream<ActivateOrderEvent>();
me->services_data.update_order_pub = make_stream<UpdateOrderEvent>();
me->services_data.cancel_order_pub = make_stream<CancelOrderEvent>();
@@ -371,7 +402,8 @@ void OrderManager::init_order_services() {
me->services_data.create_fix_req_pub =
make_stream<CreateFIXBufferRequestEvent>();
me->services_data.md_request_pub = make_stream<MDRequestEvent>();
me->services_data.settlement_request_pub= make_stream<SettlementRequestEvent>();
me->services_data.settlement_request_pub =
make_stream<SettlementRequestEvent>();
me->services_data.report_order_state_pub =
make_stream<OrderStateUpdateEvent>();
me->pub_ready = make_stream<OrderManagerReadyEvent>();
@@ -390,12 +422,43 @@ void OrderManager::init_order_services() {
me->services_data.md_subs =
std::make_unique<ContentSubscription<BookUpdateEvent>>(*this, on_md_sub);
me->services_data.settlement_subs =
std::make_unique<ContentSubscription<SettlementUpdateEvent>>(*this,on_settlement_sub);
std::make_unique<ContentSubscription<SettlementUpdateEvent>>(
*this, on_settlement_sub);
me->services_data.time_subs =
std::make_unique<ContentSubscription<TimerEvent, NoKey>>(*this);
me->services_data.order_creation_subs =
std::make_unique<ContentSubscription<CreateOrderReplyEvent>>(*this);
}
void OrderManager::on_poll() {
if (me->state == State::READY) {
for (size_t i = 0; i < me->pending_order_events.size(); i++)
me->pending_order_events[i](*this);
me->pending_order_events.clear();
for (auto &osd : me->services_data.sessions) {
auto &fix_stream = osd->current_buffer->first_stream();
if (fix_stream.n_msgs()) {
fix_stream.close();
publish(osd->out_fix_pub, osd->current_buffer, osd->current_traces);
osd->current_traces.clear();
reload_fix_buffer(*osd);
}
}
}
}
void OrderManager::reload_fix_buffer(SessionServicesData &dt) {
if (dt.free_buffers.size()) {
dt.current_buffer = dt.free_buffers.back();
dt.free_buffers.pop_back();
} else {
LOG(OrderMan, Warn,
"Ran out of fix buffers in {}. Will allocate directly. "
"This will have an impact on performance",
dt.cfg.name());
dt.current_buffer = std::make_shared<jetfix::OutFIX>(dt.cfg);
}
}
} // namespace orderman
} // namespace bts
@@ -3,6 +3,13 @@
#include "lib/config/protos/jetfix.pb.h"
#include "lib/jetfix/common/OutFIXPtr.h"
#include "lib/mercury/actors/Actor.h"
#include "lib/mercury/actors/IPollableActor.h"
#include "lib/utils/inplace_function.h"
#include "lib/orderman/events/ActivateOrderEvent.h"
#include "lib/orderman/events/UpdateOrderEvent.h"
#include "lib/orderman/events/CancelOrderEvent.h"
#include "lib/orderman/events/CrackOrderEvent.h"
#include <memory>
// fwd
@@ -22,6 +29,7 @@ class StoredFillEvent;
class CreateFIXBufferReplyEvent;
class GatewayStatusChangeEvent;
class NewAdminDataEvent;
struct SessionServicesData;
class DynamicOrderRegistry;
}
}
@@ -50,13 +58,16 @@ namespace orderman {
// - crack
// - update
class OrderManager : public mercury::Actor {
class OrderManager : public mercury::Actor,
public mercury::IPollableActor
{
public:
OrderManager(DynamicOrderRegistry const&dyn_order_builder);
~OrderManager();
void init() override;
private:
friend class OrderServices;
void on_securities(const mercury::Update<NewSecuritiesEvent> &);
void on_fill_recovery(const mercury::Update<StoredFillEvent> &);
void on_order_recovery(const mercury::Update<StoredOrderEvent> &);
@@ -71,6 +82,26 @@ class OrderManager : public mercury::Actor {
void on_admin_data(const mercury::Update<NewAdminDataEvent> &);
void init_order_services();
void on_poll() override;
using PendingOrderEvent = stdext::inplace_function<
void(OrderManager &),
std::max({128ul, sizeof(ActivateOrderEvent), sizeof(UpdateOrderEvent),
sizeof(CancelOrderEvent), sizeof(CrackOrderEvent)})>;
template <class EV>
void queue_order_event(EV const&ev) {
queue_order_event(PendingOrderEvent( [ev] (auto &om) {
om.handle_order_event(ev);
}));
}
void queue_order_event(PendingOrderEvent const&ev);
void handle_order_event(ActivateOrderEvent const &);
void handle_order_event(UpdateOrderEvent const &);
void handle_order_event(CancelOrderEvent const &);
void handle_order_event(CrackOrderEvent const&);
void reload_fix_buffer(SessionServicesData &dt);
std::vector<jetfix::OutFIXPtr> &free_buffers(std::string const&);
enum class State;
@@ -3,10 +3,12 @@
#include "lib/api/services/OrderService.pb.h"
#include "lib/jetfix/common/OutFIX.h"
#include "lib/logging/logging.h"
#include "lib/orderman/actors/order_manager/OrderManager.h"
#include "lib/orderman/actors/order_manager/OrderServicesData.h"
#include "lib/orderman/governor/Governor.h"
#include "lib/orderman/orderman_utils/EventStream.h"
#include "lib/orderman/orders/Order.h"
#include <memory>
namespace bts {
@@ -15,69 +17,44 @@ namespace orderman {
class SessionServices {
public:
SessionServices(SessionServicesData &session_data, OrderServicesData &data)
SessionServices(SessionServicesData &session_data)
: exe_reports(*session_data.exe_report_subs),
cancel_rejects(*session_data.cancel_reject_subs),
gateway_status(*session_data.gateway_status_subs),
_session_data(&session_data), _data(&data) {}
_session_data(&session_data) {}
std::string const &session_name() const { return _session_data->session; }
config::JetFIX::Connection const &session_cfg() const {
return _session_data->cfg;
}
jetfix::OutFIXPtr get_fix_buffer() {
jetfix::OutFIXPtr result;
if (_session_data->free_buffers.size()) {
result = _session_data->free_buffers.back();
_session_data->free_buffers.pop_back();
} else {
LOG(OrderMan, Warn,
"Ran out of fix buffers in {}. Will allocate directly. "
"This will have an impact on performance",
_session_data->cfg.name());
result = std::make_shared<jetfix::OutFIX>(_session_data->cfg);
}
return result;
}
void refill_fix_buffer_pool()
{
if (fix_buffer_pool_size > _session_data->free_buffers.size()) {
auto need = fix_buffer_pool_size - _session_data->free_buffers.size();
_data->owner.publish(_data->create_fix_req_pub, need,
_session_data->cfg);
}
void queue_fix(jetfix::MessageTypes mt, jetfix::FIXFragment const&f,PerfTrace const&trace) {
auto &stream = _session_data->current_buffer->first_stream();
stream.new_msg(mt).append(f);
_session_data->current_traces.push_back(trace);
}
bool is_gateway_connected() const { return _session_data->gw_connected; }
void send_fix_buffer(jetfix::OutFIXPtr const &fix, PerfTrace &trace) {
trace.send_fix_buffer = utils::TimeStamp::now();
_data->owner.publish(_session_data->out_fix_pub, trace, fix);
}
Governor &governor() { return _session_data->governor; }
EventStream<NewExecutionReportEvent> exe_reports;
EventStream<CancelRejectEvent> cancel_rejects;
EventStream<GatewayStatusChangeEvent, mercury::NoKey> gateway_status;
Governor &governor() { return _session_data->governor; }
private:
SessionServicesData *_session_data;
OrderServicesData *_data;
};
class OrderServices {
public:
OrderServices(Order &owner, OrderServicesData &data)
: book_updates(*data.md_subs),
settlement_updates(*data.settlement_subs),
child_updates(*data.child_order_subs),
time_change(*data.time_subs),
: book_updates(*data.md_subs), settlement_updates(*data.settlement_subs),
child_updates(*data.child_order_subs), time_change(*data.time_subs),
order_creations(*data.order_creation_subs), _owner(owner), _data(data),
_sessions() {
for (auto &sd : data.sessions)
_sessions.push_back(std::make_unique<SessionServices>(*sd, data));
_sessions.push_back(std::make_unique<SessionServices>(*sd));
}
EventStream<BookUpdateEvent> book_updates;
@@ -119,7 +96,7 @@ class OrderServices {
bts_assert(false);
}
void schedule_action(std::function<void()> const&action) {
void schedule_action(std::function<void()> const &action) {
_data.owner.schedule_action(action);
}
@@ -136,29 +113,33 @@ class OrderServices {
}
friend class ChildOrder;
template <class OP> void queue_order_op(const OP &op) {
_data.owner.queue_order_event(op);
}
void send_activate(Order &o, Price p, Quantity q, PerfTrace const &trace) {
_data.owner.publish(_data.activate_order_pub, o, p, q, trace);
queue_order_op(ActivateOrderEvent{o, p, q, trace});
}
void send_activate(Order &o, Price p, Quantity q) {
_data.owner.publish(_data.activate_order_pub, o, p, q, PerfTrace());
queue_order_op(ActivateOrderEvent{o, p, q, PerfTrace()});
}
void send_update(OrderKey k, Price p, Quantity q, PerfTrace const &trace) {
_data.owner.publish(_data.update_order_pub, k, p, q, trace);
queue_order_op(UpdateOrderEvent{k, p, q, trace});
}
void send_update(OrderKey k, Price p, Quantity q) {
_data.owner.publish(_data.update_order_pub, k, p, q, PerfTrace());
queue_order_op(UpdateOrderEvent{k, p, q, PerfTrace()});
}
void send_crack(OrderKey k, PerfTrace const &trace) {
_data.owner.publish(_data.crack_order_pub, k, trace);
queue_order_op(CrackOrderEvent{k, trace});
}
void send_crack(OrderKey k) {
_data.owner.publish(_data.crack_order_pub, k, PerfTrace());
queue_order_op(CrackOrderEvent{k, PerfTrace()});
}
void send_cancel(OrderKey k, PerfTrace const &trace) {
_data.owner.publish(_data.cancel_order_pub, k, trace);
queue_order_op(CancelOrderEvent{k, trace});
}
void send_cancel(OrderKey k) {
_data.owner.publish(_data.cancel_order_pub, k, PerfTrace());
queue_order_op(CancelOrderEvent{k, PerfTrace()});
}
Order &_owner;
@@ -5,7 +5,7 @@
namespace bts {
namespace orderman {
OrderServicesData::OrderServicesData(mercury::Actor &owner,DynamicOrderRegistry const&b)
OrderServicesData::OrderServicesData(OrderManager &owner,DynamicOrderRegistry const&b)
: owner(owner), order_router(), order_factory(sec_db, order_router,b),
last_timer_ev(
Timer::now(),
@@ -35,6 +35,7 @@
namespace bts {
namespace orderman {
class OrderManager;
struct ParentKey {
OrderKey operator()(const OrderStateUpdateEvent &ev) {
@@ -49,7 +50,9 @@ struct SessionServicesData {
cfg(cfg),
governor(cfg.max_order_msgs_per_window(),
cfg.window_duration_msecs())
{}
{
current_traces.reserve(1000);
}
std::string session;
std::vector<jetfix::OutFIXPtr> free_buffers;
@@ -65,13 +68,14 @@ struct SessionServicesData {
gateway_status_subs;
Governor governor;
jetfix::OutFIXPtr current_buffer;
std::vector<PerfTrace> current_traces;
};
class OrderServicesData {
public:
OrderServicesData(mercury::Actor &owner,DynamicOrderRegistry const&b);
mercury::Actor &owner;
public:
OrderServicesData(OrderManager &owner,DynamicOrderRegistry const&b);
OrderManager &owner;
securities::MapSecurityDB sec_db;
OrderRouter order_router;
Limiter limiter;
@@ -89,7 +93,6 @@ class OrderServicesData {
std::unique_ptr<mercury::ContentSubscription<CreateOrderReplyEvent>>
order_creation_subs;
mercury::Stream<OrderStateUpdateEvent> order_state_pub;
mercury::Stream<ActivateOrderEvent> activate_order_pub;
mercury::Stream<UpdateOrderEvent> update_order_pub;
mercury::Stream<CancelOrderEvent> cancel_order_pub;
+138 -4
View File
@@ -16,13 +16,147 @@ class OrderStateUpdateEvent : public mercury::Event<OrderStateUpdateEvent> {
api::orderman::OrderDefinition const &def,
api::orderman::OrderState const &state,
PerfTrace const &trace = PerfTrace())
: key(key), parent_key(parent_key), definition(&def), state(state),
trace(trace) {}
: key(key), parent_key(parent_key), trace(trace), definition(&def) {
status = state.status();
price = state.price();
quantity = state.quantity();
filled_quantity = state.filled_quantity();
last_price = state.last_price();
avg_price = state.avg_price();
last_msg = state.last_msg();
custom = state.custom_case();
auto native_state = get_native_state(state);
if (native_state) {
exchange_order_id = native_state->exchange_order_id();
last_msg_seq_num = native_state->last_msg_seq_num();
sub_error_code = native_state->sub_error_code();
native_status = native_state->native_status();
routing_record = native_state->routing_record();
native_type = native_state->native_type();
orderman::str_to(native_state->last_transact_time(),
&last_transact_time);
}
}
OrderKey key;
OrderKey parent_key;
api::orderman::OrderDefinition const *definition;
api::orderman::OrderState state;
// common state
api::orderman::OrderStatus status;
double price;
uint32_t quantity;
uint32_t filled_quantity;
double last_price;
double avg_price;
std::string last_msg;
PerfTrace trace;
api::orderman::OrderState::CustomCase custom;
// native order state
std::string exchange_order_id;
uint64_t last_msg_seq_num;
api::orderman::NativeOrderState::SubError sub_error_code;
api::orderman::NativeOrderStatus native_status;
api::orderman::RoutingRecord routing_record;
api::orderman::NativeOrderType native_type;
mercury::TimeStampStr last_transact_time;
api::orderman::OrderDefinition const *definition;
api::orderman::OrderState rebuild_state() const {
api::orderman::OrderState result;
result.set_status(status);
result.set_price(price);
result.set_quantity(quantity);
result.set_filled_quantity(filled_quantity);
result.set_last_price(last_price);
result.set_avg_price(avg_price);
result.set_last_msg(last_msg);
switch (custom) {
case api::orderman::OrderState::kMarket:
rebuild_native_state(result.mutable_market()->mutable_native_state());
break;
case api::orderman::OrderState::kLimit:
rebuild_native_state(result.mutable_limit()->mutable_native_state());
break;
case api::orderman::OrderState::kStop:
rebuild_native_state(result.mutable_stop()->mutable_native_state());
break;
case api::orderman::OrderState::kStopLimit:
rebuild_native_state(result.mutable_stop_limit()->mutable_native_state());
break;
case api::orderman::OrderState::kIceberg:
result.mutable_iceberg();
break;
case api::orderman::OrderState::kSpreader:
result.mutable_spreader();
break;
case api::orderman::OrderState::kTimeBox:
result.mutable_time_box();
break;
case api::orderman::OrderState::kTick:
result.mutable_tick();
break;
case api::orderman::OrderState::kLean:
result.mutable_lean();
break;
case api::orderman::OrderState::kJoin:
result.mutable_join();
break;
case api::orderman::OrderState::kTop:
result.mutable_top();
break;
case api::orderman::OrderState::kTrigger:
result.mutable_trigger();
break;
case api::orderman::OrderState::kCover:
result.mutable_cover();
break;
case api::orderman::OrderState::kOffset:
result.mutable_offset();
break;
case api::orderman::OrderState::kDynamic:
result.mutable_dynamic();
break;
case api::orderman::OrderState::CUSTOM_NOT_SET:
break;
}
return result;
}
private:
void rebuild_native_state(api::orderman::NativeOrderState *st) const
{
st->set_exchange_order_id(exchange_order_id);
st->set_last_msg_seq_num(last_msg_seq_num);
st->set_sub_error_code(sub_error_code);
st->set_native_status(native_status);
*st->mutable_routing_record() = routing_record;
st->set_native_type(native_type);
st->set_last_transact_time(to_str(last_transact_time));
}
api::orderman::NativeOrderState const *
get_native_state(api::orderman::OrderState const &st) {
switch (st.custom_case()) {
case api::orderman::OrderState::kMarket:
return &st.market().native_state();
case api::orderman::OrderState::kLimit:
return &st.limit().native_state();
case api::orderman::OrderState::kStop:
return &st.stop().native_state();
case api::orderman::OrderState::kStopLimit:
return &st.stop_limit().native_state();
default:
return nullptr;
};
}
};
} // namespace orderman
+3 -3
View File
@@ -21,12 +21,12 @@ public:
// missed.
using SynopsisPolicy = mercury::NoSynopsis;
OutFIXMsgEvent (PerfTrace trace, jetfix::OutFIXPtr const&fix) :
trace(trace), msg(fix)
OutFIXMsgEvent (jetfix::OutFIXPtr const&fix,std::vector<PerfTrace> const&traces) :
msg(fix),trace(traces[0])
{}
jetfix::OutFIXPtr msg;
PerfTrace trace;
jetfix::OutFIXPtr msg;
};
}}
@@ -7,6 +7,7 @@ namespace bts {
class PerfTrace {
public:
utils::TimeStamp do_work_begin;
utils::TimeStamp fix_socket_r;
utils::TimeStamp md ;
utils::TimeStamp md_src ;
+248 -102
View File
@@ -13,89 +13,115 @@
#include <SQLiteCpp/SQLiteCpp.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <fmt/format.h>
#include <iostream>
#include <mutex>
#include <tbb/concurrent_queue.h>
#include <boost/range/adaptor/reversed.hpp>
namespace bts {
auto insert = "insert into trace ("
"version,"
"host,"
"build_type,"
"session_start,"
"trace_time,"
"fix_socket_r,"
"md,"
"md_src,"
"spreader_on_book,"
"spreader_on_child_order_update,"
"order_update,"
"exe_gw,"
"fix_socket_w,"
"spreader_modify_order,"
"send_fix_buffer,"
"order_key,"
"md_sn"
") values ("
":version,"
":host,"
":build_type,"
":session_start,"
":trace_time,"
":fix_socket_r,"
":md,"
":md_src,"
":spreader_on_book,"
":spreader_on_child_order_update,"
":order_update,"
":exe_gw,"
":fix_socket_w,"
":spreader_modify_order,"
":send_fix_buffer,"
":order_key,"
":md_sn"
")";
auto current_schema_version = 3;
auto create = "create table trace ("
"version TEXT, "
"host TEXT, "
"build_type TEXT, "
"session_start TEXT,"
"trace_time TEXT,"
"fix_socket_r INTEGER,"
"md INTEGER,"
"md_src INTEGER,"
"spreader_on_book INTEGER,"
"spreader_on_child_order_update INTEGER,"
"order_update INTEGER,"
"exe_gw INTEGER,"
"fix_socket_w INTEGER,"
"spreader_modify_order INTEGER,"
"send_fix_buffer INTEGER,"
"order_key INTEGER,"
"md_sn INTEGER );";
auto insert_trace = "insert into trace ("
"version,"
"host,"
"build_type,"
"session_start,"
"trace_time,"
"do_work_begin,"
"fix_socket_r,"
"md,"
"md_src,"
"spreader_on_book,"
"spreader_on_child_order_update,"
"order_update,"
"exe_gw,"
"fix_socket_w,"
"spreader_modify_order,"
"send_fix_buffer,"
"order_key,"
"md_sn"
") values ("
":version,"
":host,"
":build_type,"
":session_start,"
":trace_time,"
":do_work_begin,"
":fix_socket_r,"
":md,"
":md_src,"
":spreader_on_book,"
":spreader_on_child_order_update,"
":order_update,"
":exe_gw,"
":fix_socket_w,"
":spreader_modify_order,"
":send_fix_buffer,"
":order_key,"
":md_sn"
")";
auto view = "create view requote_trace as select "
"version, "
"host, "
"build_type, "
"session_start,"
"trace_time,"
"order_key,"
"(fix_socket_w-md) / 1000.0 total,"
"(spreader_on_book - md) / 1000.0 md,"
"(send_fix_buffer-spreader_on_book)/1000.0 spreader,"
"(fix_socket_w-send_fix_buffer)/1000.0 fix "
"from "
"trace "
"where md<>0 and (md_src-md)<1000000";
auto create_trace_table = "drop table if exists trace;create table trace ("
"version TEXT, "
"host TEXT, "
"build_type TEXT, "
"session_start TEXT,"
"trace_time TEXT,"
"do_work_begin INTEGER,"
"fix_socket_r INTEGER,"
"md INTEGER,"
"md_src INTEGER,"
"spreader_on_book INTEGER,"
"spreader_on_child_order_update INTEGER,"
"order_update INTEGER,"
"exe_gw INTEGER,"
"fix_socket_w INTEGER,"
"spreader_modify_order INTEGER,"
"send_fix_buffer INTEGER,"
"order_key INTEGER,"
"md_sn INTEGER );";
auto create_schema_version_table =
"create table schema_version(version INTEGER); insert into "
"schema_version(version) values(0);";
auto query_schema_version = "select version from schema_version limit 1";
auto delete_version_record = "delete from schema_version;";
auto update_version = "insert into schema_version(version) values(:version);";
auto requote_trace_view = "drop view if exists requote_trace; "
"create view requote_trace as select "
"version, "
"host, "
"build_type, "
"session_start,"
"trace_time,"
"order_key,"
"(fix_socket_w-md) / 1000.0 total,"
"0 md,"
"0 spreader,"
"0 fix "
"from "
"trace "
"where md<>0 and (md_src-md)<1000000";
auto hedge_trace_view =
"drop view if exists hedge_trace; "
"CREATE VIEW hedge_trace as "
"select version, host, build_type, session_start,trace_time,order_key, "
"(fix_socket_w-fix_socket_r) / 1000.0 total, "
"0 fix_in, "
"0 spreader, "
"0 fix_out "
"from trace where fix_socket_r<>0 ";
auto requote_query =
"select * from requote_trace order by trace_time desc limit ?";
auto hedge_query = "select * from hedge_trace order by trace_time desc limit ?";
struct PerfTraceLog::PImpl {
PImpl(std::string const &loc)
: db(loc, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE),
@@ -103,6 +129,7 @@ struct PerfTraceLog::PImpl {
SQLite::Database db;
std::unique_ptr<SQLite::Statement> insert_statement;
std::unique_ptr<SQLite::Statement> requote_query_statement;
std::unique_ptr<SQLite::Statement> hedge_query_statement;
tbb::concurrent_queue<PerfTrace> pending_traces;
std::string session_start;
std::mutex lock;
@@ -112,16 +139,29 @@ PerfTraceLog::PerfTraceLog() : me() {
auto loc = init_storage_location();
try {
me = std::make_unique<PImpl>(loc);
if (!me->db.tableExists("trace")) {
if (!me->db.tableExists("schema_version")) {
LOG(persist, Warn, "{} database does not exist yet. Creating it ",
std::string(me->db.getFilename()));
me->db.exec(create);
me->db.exec(view);
me->db.exec(create_schema_version_table);
}
if (schema_out_of_date()) {
me->db.exec(create_trace_table);
me->db.exec(requote_trace_view);
me->db.exec(hedge_trace_view);
SQLite::Statement(me->db, delete_version_record).exec();
SQLite::Statement update_version_statement(me->db, update_version);
update_version_statement.bind(":version", current_schema_version);
update_version_statement.exec();
}
me->insert_statement =
std::make_unique<SQLite::Statement>(me->db, insert);
std::make_unique<SQLite::Statement>(me->db, insert_trace);
me->requote_query_statement =
std::make_unique<SQLite::Statement>(me->db, requote_query);
me->hedge_query_statement =
std::make_unique<SQLite::Statement>(me->db, hedge_query);
me->session_start = datetime::FIX_timestamp(datetime::local_now());
} catch (SQLite::Exception const &e) {
auto msg = fmt::format("Unable to create {}. Error: {}", loc,
@@ -178,6 +218,8 @@ void PerfTraceLog::store(std::vector<PerfTrace> const &traces) {
datetime::FIX_timestamp(
datetime::timestamp_to_ptime(t.fix_socket_r)));
me->insert_statement->bind(":do_work_begin",
(long long)t.do_work_begin.to_uint64());
me->insert_statement->bind(":fix_socket_r",
(long long)t.fix_socket_r.to_uint64());
me->insert_statement->bind(":md", (long long)t.md.to_uint64());
@@ -210,6 +252,24 @@ void PerfTraceLog::store(std::vector<PerfTrace> const &traces) {
}
}
bool PerfTraceLog::schema_out_of_date() {
try {
SQLite::Statement query_schema_version_statement(me->db,
query_schema_version);
while (query_schema_version_statement.executeStep()) {
int version = query_schema_version_statement.getColumn("version");
return version != current_schema_version;
}
} catch (SQLite::Exception const &e) {
auto msg = fmt::format("Unable to query schema version in {}. Error: {}",
std::string(me->db.getFilename()),
std::string(e.getErrorStr()));
bts_assert_msg(false, msg);
}
return true;
}
std::string encode_string(SQLite::Column const &c) {
return fmt::format("\"{}\"", std::string(c));
}
@@ -219,7 +279,7 @@ std::string encode_number(SQLite::Column const &c) {
}
std::vector<std::map<std::string, std::string>>
PerfTraceLog::get_latest_traces(size_t n) {
PerfTraceLog::get_latest_requote_traces(size_t n) {
std::vector<std::map<std::string, std::string>> result;
std::lock_guard<std::mutex> l(me->lock);
@@ -252,7 +312,7 @@ PerfTraceLog::get_latest_traces(size_t n) {
}
me->requote_query_statement->reset();
} catch (SQLite::Exception const &e) {
auto msg = fmt::format("Unable to query {}. Error: {}",
auto msg = fmt::format("Unable to quote_traces {}. Error: {}",
std::string(me->db.getFilename()),
std::string(e.getErrorStr()));
bts_assert_msg(false, msg);
@@ -260,6 +320,47 @@ PerfTraceLog::get_latest_traces(size_t n) {
return result;
}
std::vector<std::map<std::string, std::string>>
PerfTraceLog::get_latest_hedge_traces(size_t n) {
std::vector<std::map<std::string, std::string>> result;
std::lock_guard<std::mutex> l(me->lock);
try {
me->hedge_query_statement->bind(1, (long long)n);
while (me->hedge_query_statement->executeStep()) {
auto &row = result.emplace_back();
row["version"] =
encode_string(me->hedge_query_statement->getColumn("version"));
row["host"] =
encode_string(me->hedge_query_statement->getColumn("host"));
row["build_type"] =
encode_string(me->hedge_query_statement->getColumn("build_type"));
row["session_start"] = encode_string(
me->hedge_query_statement->getColumn("session_start"));
row["trace_time"] =
encode_string(me->hedge_query_statement->getColumn("trace_time"));
row["order_key"] =
encode_number(me->hedge_query_statement->getColumn("order_key"));
row["total"] =
encode_number(me->hedge_query_statement->getColumn("total"));
row["fix_in"] =
encode_number(me->hedge_query_statement->getColumn("fix_in"));
row["spreader"] =
encode_number(me->hedge_query_statement->getColumn("spreader"));
row["fix_out"] =
encode_number(me->hedge_query_statement->getColumn("fix_out"));
}
me->hedge_query_statement->reset();
} catch (SQLite::Exception const &e) {
auto msg = fmt::format("Unable to query hedge_traces. Error: {}",
std::string(e.getErrorStr()));
bts_assert_msg(false, msg);
}
return result;
}
namespace perf_stat {
using Row = std::map<std::string, std::string>;
@@ -286,8 +387,8 @@ struct Session {
} // namespace perf_stat
std::map<std::string, perf_stat::Session>
structure_traces(std::vector<std::map<std::string, std::string>> &quotes) {
std::map<std::string, perf_stat::Session> structure_quote_traces(
std::vector<std::map<std::string, std::string>> &quotes) {
std::map<std::string, perf_stat::Session> result;
@@ -322,32 +423,63 @@ structure_traces(std::vector<std::map<std::string, std::string>> &quotes) {
return result;
}
void print_summary(std::ostream &ss, perf_stat::Summary const &s,bool last) {
std::map<std::string, perf_stat::Session> structure_hedge_traces(
std::vector<std::map<std::string, std::string>> &quotes) {
std::map<std::string, perf_stat::Session> result;
for (auto &q : quotes) {
auto &session = result[q["session_start"]];
if (session.start.empty()) {
session.start = q["session_start"];
session.host = q["host"];
session.version = q["version"];
session.build_type = q["build_type"];
}
for (auto st : {"total", "fix_in", "spreader", "fix_out"}) {
auto &summary = session.summaries[st];
if (summary.name.empty())
summary.name = st;
summary.values.push_back(std::stod(q[st]));
}
session.rows.push_back(q);
session.n_rows++;
}
for (auto &session_kvp : result)
for (auto &stat_kvp : session_kvp.second.summaries) {
auto &summary = stat_kvp.second;
summary.min = stats::min(summary.values);
summary.max = stats::max(summary.values);
summary.median = stats::percentile(summary.values, 0.5);
summary.ninenty_nine = stats::percentile(summary.values, 0.99);
summary.stddev = stats::stdev(summary.values);
}
return result;
}
void print_summary(std::ostream &ss, perf_stat::Summary const &s, bool last) {
ss << " { "
<< "\"value\":"
<< " \"" << s.name << "\""
<< ", \"min\":" << s.min
<< ", \"max\":" << s.max
<< ", \"median\":" << s.median
<< ", \"ninenty_nine\":" << s.ninenty_nine
<< ", \"min\":" << s.min << ", \"max\":" << s.max
<< ", \"median\":" << s.median << ", \"ninenty_nine\":" << s.ninenty_nine
<< ", \"stdev\":" << s.stddev;
if (!last)
ss << " },\n";
else
ss << " }\n";
}
void print_row(std::ostream &ss, perf_stat::Row const &r,bool last) {
void print_row(std::ostream &ss, perf_stat::Row const &r, bool last) {
ss << " { ";
size_t i = 0;
for (auto const &kvp : r)
if (kvp.first != "version" &&
kvp.first != "host" &&
kvp.first != "build_type" &&
kvp.first != "session_start") {
if (kvp.first != "version" && kvp.first != "host" &&
kvp.first != "build_type" && kvp.first != "session_start") {
ss << "\"" << kvp.first << "\":" << kvp.second;
if (++i != r.size()-4)
if (++i != r.size() - 4)
ss << ",";
}
if (!last)
@@ -356,7 +488,7 @@ void print_row(std::ostream &ss, perf_stat::Row const &r,bool last) {
ss << " }\n";
}
void print_session(std::ostream &ss, perf_stat::Session const &s,bool last) {
void print_session(std::ostream &ss, perf_stat::Session const &s, bool last) {
ss << "{\n"
<< " \"session_name\": " << s.start << ",\n"
<< " \"host\": " << s.host << ",\n"
@@ -365,15 +497,15 @@ void print_session(std::ostream &ss, perf_stat::Session const &s,bool last) {
<< " \"n_rows\": " << s.n_rows << ",\n";
ss << " \"summaries\":\n [\n";
size_t i = 0;
size_t i = 0;
for (auto &summaries_kvp : s.summaries)
print_summary(ss, summaries_kvp.second,++i == s.summaries.size());
print_summary(ss, summaries_kvp.second, ++i == s.summaries.size());
ss << " ],\n";
ss << " \"rows\":\n [\n";
i = 0;
for (auto &r : s.rows)
print_row(ss, r,++i == s.rows.size());
print_row(ss, r, ++i == s.rows.size());
ss << " ]\n";
if (!last)
ss << "},\n";
@@ -382,14 +514,28 @@ void print_session(std::ostream &ss, perf_stat::Session const &s,bool last) {
}
void PerfTraceLog::print_latest_traces(std::ostream &ss, size_t n) {
auto rows = get_latest_traces(n);
auto structured = structure_traces(rows);
ss << "[\n";
size_t i = 0;
for (auto &session_kvp : boost::adaptors::reverse(structured))
print_session(ss, session_kvp.second, ++i == structured.size());
ss << "]\n";
{
auto rows = get_latest_requote_traces(n);
auto structured = structure_quote_traces(rows);
ss << "{\n\"requotes\": [\n";
size_t i = 0;
for (auto &session_kvp : boost::adaptors::reverse(structured))
print_session(ss, session_kvp.second, ++i == structured.size());
ss << "],\n";
}
{
auto rows = get_latest_hedge_traces(n);
auto structured = structure_hedge_traces(rows);
ss << "\"hedges\": [\n";
size_t i = 0;
for (auto &session_kvp : boost::adaptors::reverse(structured))
print_session(ss, session_kvp.second, ++i == structured.size());
ss << "]\n}\n";
}
}
} // namespace bts
@@ -17,7 +17,9 @@ class PerfTraceLog {
int persist_traces();
// return the N last traces
std::vector<std::map<std::string, std::string>> get_latest_traces(size_t n=0);
std::vector<std::map<std::string, std::string>> get_latest_requote_traces(size_t n=0);
std::vector<std::map<std::string, std::string>> get_latest_hedge_traces(size_t n=0);
void print_latest_traces(std::ostream &,size_t n = 0);
private:
@@ -25,6 +27,7 @@ class PerfTraceLog {
std::unique_ptr<PImpl> me;
std::string init_storage_location();
bool schema_out_of_date();
void store(std::vector<PerfTrace> const &);
};
+51 -18
View File
@@ -1,7 +1,12 @@
#include "IcebergOrder.h"
#include "lib/logging/logging.h"
#include "lib/utils/PBUtils.h"
#include "lib/mercury/events/TimerEvent.h"
#include "lib/orderman/actors/order_manager/OrderBuilder.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/bts_chrono.h"
#include "lib/config/config.h"
#include "lib/config/protos/orderman_cfg.pb.h"
#include <algorithm>
namespace bts {
@@ -16,8 +21,13 @@ struct IcebergOrder::ActiveChild {
void clear() { state = api::orderman::OrderState(); }
};
enum class IcebergOrder::WorkingSubStatus { WORKING, PAUSED };
struct IcebergOrder::PImpl {
PImpl() : services(nullptr), child() {}
PImpl()
: services(nullptr), child(),
working_sub_status(WorkingSubStatus::PAUSED),
start_of_pause(Timer::now()) {}
OrderServices *services;
api::orderman::OrderDefinition def;
@@ -25,6 +35,9 @@ struct IcebergOrder::PImpl {
OrderBuilder child_builder;
ActiveChild child;
Quantity show_quantity;
WorkingSubStatus working_sub_status;
TimeStamp start_of_pause;
uint32_t pause_between_orders;
};
IcebergOrder::IcebergOrder() : me(new PImpl()) {}
@@ -42,6 +55,8 @@ void IcebergOrder::on_init_new(api::orderman::OrderDefinition const &def,
auto const &iceberg_def = def.custom_def().iceberg();
me->show_quantity = Quantity(iceberg_def.show_quantity());
me->pause_between_orders =
config::get<config::OrdermanCfg>().default_iceberg_pause_secs();
auto child_def = def;
*child_def.mutable_custom_def() = iceberg_def.child();
@@ -89,7 +104,7 @@ void IcebergOrder::on_init_recovered(api::orderman::OrderRecord const &rec,
}
void IcebergOrder::on_activate(Price p, Quantity q, OrderServices &services,
PerfTrace const&) {
PerfTrace const &) {
bts_assert(!me->services);
me->services = &services;
me->child_builder.activate(services);
@@ -98,9 +113,13 @@ void IcebergOrder::on_activate(Price p, Quantity q, OrderServices &services,
case ORDER_STATUS_INACTIVE:
me->state.set_price(p.value());
me->state.set_quantity(q.value());
if (me->pause_between_orders)
services.time_change.sub(
[this](auto const &time) { this->on_time(time); });
services.child_updates.sub(
key(), [this](auto const &os) { this->on_child_order_update(os); });
transition(ORDER_STATUS_WORKING);
me->working_sub_status = WorkingSubStatus::WORKING;
send_next_child_order();
break;
@@ -126,7 +145,7 @@ void IcebergOrder::on_activate(Price p, Quantity q, OrderServices &services,
report_state();
}
void IcebergOrder::on_update(Price p, Quantity q, PerfTrace const&) {
void IcebergOrder::on_update(Price p, Quantity q, PerfTrace const &) {
switch (me->state.status()) {
case ORDER_STATUS_WORKING:
case ORDER_STATUS_PENDING_WORKING:
@@ -152,9 +171,9 @@ void IcebergOrder::on_update(Price p, Quantity q, PerfTrace const&) {
report_state();
}
void IcebergOrder::on_crack(PerfTrace const&) {}
void IcebergOrder::on_crack(PerfTrace const &) {}
void IcebergOrder::on_cancel(PerfTrace const&) {
void IcebergOrder::on_cancel(PerfTrace const &) {
switch (me->state.status()) {
case ORDER_STATUS_WORKING:
LOG(OrderMan, Info, "Canceling Iceberg Order {}", key().value());
@@ -243,9 +262,9 @@ void IcebergOrder::on_child_order_update(OrderStateUpdateEvent const &ev) {
switch (me->state.status()) {
case ORDER_STATUS_WORKING:
if (ev.key == me->child.order.key()) {
me->child.state = ev.state;
me->child.state = ev.rebuild_state();
switch (ev.state.status()) {
switch (ev.status) {
case ORDER_STATUS_WORKING:
case ORDER_STATUS_PENDING_WORKING:
@@ -253,14 +272,18 @@ void IcebergOrder::on_child_order_update(OrderStateUpdateEvent const &ev) {
break;
case ORDER_STATUS_COMPLETED: {
api::orderman::OrderState merged_state;
merge_states(me->state, me->child.state, &merged_state);
me->state = merged_state;
me->child.clear();
if (me->state.quantity() > me->state.filled_quantity())
api::orderman::OrderState merged_state;
merge_states(me->state, me->child.state, &merged_state);
me->state = merged_state;
me->child.clear();
if (me->state.quantity() > me->state.filled_quantity()) {
if (me->pause_between_orders) {
me->working_sub_status = WorkingSubStatus::PAUSED;
me->start_of_pause = Timer::now();
} else
send_next_child_order();
else
transition(ORDER_STATUS_COMPLETED);
} else
transition(ORDER_STATUS_COMPLETED);
break;
}
@@ -268,8 +291,7 @@ void IcebergOrder::on_child_order_update(OrderStateUpdateEvent const &ev) {
LOG(OrderMan, Info,
"Unexpected cancel of child {} in Iceberg Order {}. Canceling "
"parent",
ev.key.value(),
key().value());
ev.key.value(), key().value());
transition(ORDER_STATUS_CANCELED);
break;
@@ -292,7 +314,7 @@ void IcebergOrder::on_child_order_update(OrderStateUpdateEvent const &ev) {
"Iceberg order {} received update for non active child order {}."
"update state = {}",
key().value(), me->child.order.key().value(),
utils::pb::to_string(ev.state));
utils::pb::to_string(ev.rebuild_state()));
}
break;
@@ -312,5 +334,16 @@ void IcebergOrder::on_child_order_update(OrderStateUpdateEvent const &ev) {
}
report_state();
}
void IcebergOrder::on_time(mercury::TimerEvent const &t) {
bts_assert(me->pause_between_orders);
if (sec_diff(t.ts, me->start_of_pause) >= me->pause_between_orders &&
me->state.status() == ORDER_STATUS_WORKING &&
me->working_sub_status == WorkingSubStatus::PAUSED) {
me->working_sub_status = WorkingSubStatus::WORKING;
send_next_child_order();
}
}
} // namespace orderman
} // namespace bts
+3
View File
@@ -5,6 +5,7 @@
#include "lib/api/services/OrderService.server.pb.h"
namespace bts {
namespace mercury {class TimerEvent;}
namespace orderman {
class OrderStateUpdateEvent;
@@ -35,7 +36,9 @@ private:
void on_child_order_update(OrderStateUpdateEvent const&);
void transition(api::orderman::OrderStatus new_state,std::string const&msg);
void transition(api::orderman::OrderStatus new_state);
void on_time (mercury::TimerEvent const&);
enum class WorkingSubStatus;
struct ActiveChild;
struct PImpl;
std::unique_ptr<PImpl> me;
+44 -49
View File
@@ -12,6 +12,7 @@
#include "lib/orderman/securities/ISecurityDB.h"
#include "lib/tracing/bts_trace.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/bts_chrono.h"
#include "lib/orderman/actors/order_manager/OrderServices.h"
@@ -29,7 +30,7 @@ struct NativeOrder::PImpl {
PImpl()
: services(nullptr), session_services(), sec(nullptr),
previous_position_contribution(0), definition(), state(),
price_encoding_precision(7) {}
price_encoding_precision(7),have_exchange_order_id(false) {}
OrderServices *services;
SessionServices *session_services;
@@ -40,7 +41,8 @@ struct NativeOrder::PImpl {
OrderDefinition definition;
OrderState state;
int price_encoding_precision;
bool have_exchange_order_id;
jetfix::FIXFragment update_encoding;
jetfix::FIXFragment::Bookmark<jetfix::fix::Price> update_price_bm;
jetfix::FIXFragment::Bookmark<jetfix::fix::OrderQty> update_quantity_bm;
@@ -273,10 +275,13 @@ NativeOrder::~NativeOrder() {}
void NativeOrder::on_activate(Price p, Quantity q,
bts::orderman::OrderServices &services,
PerfTrace const &t) {
LOG(Latency,Info, "on_activate start {}", key().value());
auto trace = t;
trace.order_key = key().value();
tracepoint(bts_trace, trace_order, (char *)"on_activate", 0, trace.md_sn, 0);
trace.order_update = utils::TimeStamp::now();
tracepoint(bts_trace, trace_order, (char *)"on_activate", 0, trace.md_sn, key().value());
bts_assert(!me->services);
me->services = &services;
@@ -384,23 +389,18 @@ void NativeOrder::on_activate(Price p, Quantity q,
auto const &ts = services.get_time_str();
me->create_transact_time_bm = encoding.overwrite(
me->create_transact_time_bm, &ts.value()[0], ts.value().size());
auto buffer = me->session_services->get_fix_buffer();
auto &stream = buffer->fix[0];
auto &f = stream.new_msg(jetfix::MessageTypes::NewOrderSingle);
f.append(me->create_encoding);
stream.close();
// Send it
me->session_services->send_fix_buffer(buffer, trace);
// post send book keeping
trace.send_fix_buffer = utils::TimeStamp::now();
me->session_services->queue_fix(jetfix::MessageTypes::NewOrderSingle,
me->create_encoding, trace);
me->session_services->governor().record_sent();
me->session_services->refill_fix_buffer_pool();
me->state.set_status(ORDER_STATUS_PENDING_WORKING);
LOG(Latency,Diag, "on_activate done fix {} t: {}us", key().value(),
(utils::TimeStamp::now() - trace.order_update).microseconds());
LOG(OrderMan, Info, "Sent new order with key {} to gateway",
key().value());
break;
// early return so we skip the expensive report_state() call
return;
}
// all cases below are for orders recovered from disk
@@ -526,32 +526,20 @@ void NativeOrder::on_update(Price p, Quantity q, PerfTrace const &t) {
me->update_quantity_bm =
encoding.overwrite(me->update_quantity_bm, q.value(), quantity_sz());
tracepoint(bts_trace, trace_order, (char *)"on_update.half_way", 0,
trace.md_sn, trace.order_key);
auto const &ts = me->services->get_time_str();
me->update_transact_time_bm = encoding.overwrite(
me->update_transact_time_bm, &ts.value()[0], ts.value().size());
auto buffer = me->session_services->get_fix_buffer();
auto &stream = buffer->fix[0];
auto &f = stream.new_msg(jetfix::MessageTypes::CancelReplace);
f.append(me->update_encoding);
stream.close();
// send it
tracepoint(bts_trace, trace_order, (char *)"on_update.before_send_fix", 0,
trace.md_sn, trace.order_key);
me->session_services->send_fix_buffer(buffer, trace);
me->session_services->queue_fix(jetfix::MessageTypes::CancelReplace,
me->update_encoding, trace);
me->session_services->governor().record_sent();
tracepoint(bts_trace, trace_order, (char *)"on_update.end", 0,
trace.md_sn, trace.order_key);
// post send book keeping
// these are slow and we need to return out of this handler so
// that we can process the fix
me->services->schedule_action([this]() {
me->session_services->refill_fix_buffer_pool();
if (me->state.last_msg().size())
me->state.set_last_msg("");
report_state();
});
update_working_position();
if (me->state.last_msg().size())
me->state.set_last_msg("");
// early return so we skip report_state (for speed)
return;
@@ -585,16 +573,10 @@ void NativeOrder::on_cancel(PerfTrace const &trace) {
auto const &ts = me->services->get_time_str();
me->cancel_transact_time_bm = encoding.overwrite(
me->cancel_transact_time_bm, &ts.value()[0], ts.value().size());
auto buffer = me->session_services->get_fix_buffer();
auto &stream = buffer->fix[0];
auto &f = stream.new_msg(jetfix::MessageTypes::Cancel);
f.append(me->cancel_encoding);
stream.close();
auto trace_cpy = trace;
me->session_services->send_fix_buffer(buffer, trace_cpy);
// post send book keeping
me->session_services->refill_fix_buffer_pool();
me->session_services->queue_fix(jetfix::MessageTypes::Cancel,
me->cancel_encoding, trace);
break;
}
@@ -689,7 +671,8 @@ void NativeOrder::on_exe_report(const NewExecutionReportEvent &er) {
native_state.set_native_status(er.data->order_status);
native_state.set_native_type(er.data->order_type);
if (native_state.exchange_order_id().empty()) {
bool got_first_exchange_order_id = !me->have_exchange_order_id;
if (got_first_exchange_order_id) {
auto ts = to_trim_str(er.data->exchange_order_id.value());
native_state.set_exchange_order_id(std::string(ts.first, ts.second));
add_exchange_order_id_to_fix();
@@ -732,6 +715,15 @@ void NativeOrder::on_exe_report(const NewExecutionReportEvent &er) {
break;
}
report_state(er.trace);
// we only need the exhange order id and the routing record
// reported once, since they are both immutable, so we clear them
// after their first report to make subsquent state updates faster
if (got_first_exchange_order_id) {
me->have_exchange_order_id = true;
native_state.clear_exchange_order_id();
native_state.mutable_routing_record()->Clear();
}
}
void NativeOrder::on_cancel_reject(const orderman::CancelRejectEvent &cr) {
@@ -802,7 +794,7 @@ void NativeOrder::on_gateway_status(const GatewayStatusChangeEvent &ev) {
report_state();
}
void NativeOrder::report_state(PerfTrace const &trace) {
void NativeOrder::update_working_position() {
if (me->sec) {
auto current_position = position_contribution();
auto previous_position = me->previous_position_contribution;
@@ -823,7 +815,10 @@ void NativeOrder::report_state(PerfTrace const &trace) {
"Order {} ignored for purposes of limit enforcing "
"because it references unknown security {} ",
key().value(), me->definition.security_key());
}
void NativeOrder::report_state(PerfTrace const &trace) {
update_working_position();
me->services->report_state(me->state, trace);
}
@@ -1038,7 +1033,7 @@ void NativeOrder::transition(api::orderman::OrderStatus new_status) {
else
LOG(OrderMan, Diag, "Transitioning order {} from {} to {}", key().value(),
from, to);
me->state.set_status(new_status);
}
+1
View File
@@ -46,6 +46,7 @@ private:
void transition_to_error(api::orderman::NativeOrderState::SubError sub_error,
std::string const&msg);
void update_working_position();
bool has_exchange_order_id() ;
Quantity position_contribution();
bool is_old_exe_report( api::orderman::NativeOrderState &, const NewExecutionReportEvent &) const;
+2 -2
View File
@@ -320,11 +320,11 @@ void TimeBoxOrder::on_time_change() {
void TimeBoxOrder::on_child_order_update(OrderStateUpdateEvent const&ev)
{
me->child_state = ev.state;
me->child_state = ev.rebuild_state();
switch (me->state.status()) {
case ORDER_STATUS_WORKING:
switch (ev.state.status()) {
switch (ev.status) {
case ORDER_STATUS_WORKING:
case ORDER_STATUS_PENDING_WORKING:
+5 -4
View File
@@ -232,22 +232,23 @@ void TriggerOrder::_on_book_update(const BookUpdateEvent& b_event){
}
void TriggerOrder::_on_child_order_update(const OrderStateUpdateEvent& o_ev){
if(o_ev.key==me->child_order.key()){
me->child_state=o_ev.state;
auto ev_state = o_ev.rebuild_state();
me->child_state=ev_state;
switch (_state){
case State::START:
case State::WAITING_FOR_TRIGGER:
bts_assert(false);
break;
case State::WAITING_FOR_FIRST_ACK:
if(SpreaderUtils::order_acknowledged(o_ev.state.status())){
if(SpreaderUtils::order_in_terminal_state(o_ev.state.status()))
if(SpreaderUtils::order_acknowledged(o_ev.status)){
if(SpreaderUtils::order_in_terminal_state(o_ev.status))
_switch_state(State::END);
else
_switch_state(State::STEADY);
}
break;
case State::STEADY:
if(SpreaderUtils::order_in_terminal_state(o_ev.state.status()))
if(SpreaderUtils::order_in_terminal_state(ev_state.status()))
_switch_state(State::END);
break;
case State::END:
@@ -343,15 +343,15 @@ void BookAlgo::on_child_order_update(const OrderStateUpdateEvent& ev)
if(me->fake_detached_child)
return;
if(ev.key != me->child.key())
me->hedges_state[ev.key] = ev.state;
me->hedges_state[ev.key] = ev.rebuild_state();
else
{
api::orderman::OrderState orig_state = child_state();
me->child_state = ev.state;
me->child_state = ev.rebuild_state();
switch (this_state().status())
{
case ORDER_STATUS_WORKING:
switch (ev.state.status())
switch (ev.status)
{
case ORDER_STATUS_PENDING_WORKING:
break;
@@ -52,10 +52,10 @@ bool CoverOrder::setup_hedge(const api::orderman::OrderDefinition& this_def, con
void CoverOrder::send_hedge_if_new_fills(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
{
uint32_t qty = new_state.state.filled_quantity() - orig_state.filled_quantity();
uint32_t qty = new_state.filled_quantity - orig_state.filled_quantity();
if(qty > 0)
{
double prx = new_state.state.last_price();
double prx = new_state.last_price;
if(definition().side() == api::SIDE_BUY) //if this is a buy, I'm going to sell my hedge, nticks higher than the fill price
prx = prx + tick_size() * me->hedge_ticks;
else
@@ -121,11 +121,11 @@ void SpreaderOrder::_on_child_order_update(const OrderStateUpdateEvent& ord_ev){
if (!_active) return;
SecurityKey sec_key = SecurityKey(ord_ev.definition->security_key());
OrderKey order_key = ord_ev.key;
bts::api::orderman::OrderStatus status = ord_ev.state.status();
bts::api::orderman::OrderStatus status = ord_ev.status;
Quantity filled_qty = Quantity (ord_ev.state.filled_quantity());
Price last_price = Price(ord_ev.state.last_price()),
order_price=Price(ord_ev.state.price());
Quantity filled_qty = Quantity (ord_ev.filled_quantity);
Price last_price = Price(ord_ev.last_price),
order_price=Price(ord_ev.price);
if (!SpreaderUtils::order_acknowledged(status)){
LOG(OrderMan, Diag, "SpreaderOrder::_on_child_order_update, unack'ed "
"callback for key:{} sec_key:{} order_key:{} "
@@ -146,7 +146,7 @@ void SpreaderOrder::_on_child_order_update(const OrderStateUpdateEvent& ord_ev){
key(), sec_key,
static_cast<int>(_state_worker->state()), order_key, status,
filled_qty, order_price, last_price,
ord_ev.state.last_msg(), static_cast<int>(order_error));
ord_ev.last_msg, static_cast<int>(order_error));
leg_data.order_update_message().on_order_update(ord_ev);
if (order_error == ChildOrderError::NONE){
if (leg_data.order_update_message().last_update_filled_quantity().value() > 0)
@@ -170,7 +170,7 @@ void SpreaderOrder::_on_child_order_update(const OrderStateUpdateEvent& ord_ev){
key(), sec_key,
static_cast<int>(_state_worker->state()), order_key, status,
filled_qty, order_price, last_price,
ord_ev.state.last_msg (), static_cast<int>(order_error));
ord_ev.last_msg, static_cast<int>(order_error));
hedger->order_state().on_order_update(ord_ev);
if (order_error == ChildOrderError::NONE)
_state_worker->on_hedger_order_update (*hedger);
@@ -182,10 +182,12 @@ void SpreaderOrder::_on_child_order_update(const OrderStateUpdateEvent& ord_ev){
"key:{} sec_key:{} order_key:{} status:{} "
"filled_qty:{} last_price:{} msg:{}",
key(), sec_key, order_key, status, filled_qty,
last_price, ord_ev.state.last_msg ());
last_price, ord_ev.last_msg);
}
_send_check_termination(true);
_report_state();
LOG(Latency,Diag, "_on_child_order_update k:{} t:{}us", key().value(),
(utils::TimeStamp::now() - trace.spreader_on_child_order_update).microseconds());
}
// State Transformations ////////////////////
/////////////////////////////////////////////
@@ -209,7 +209,7 @@ private:
static ChildOrderError _get_order_error (const OrderStateUpdateEvent& ord_ev,
bool expecting_cancel){
bts::api::orderman::OrderStatus order_status = ord_ev.state.status();
bts::api::orderman::OrderStatus order_status = ord_ev.status;
if (SpreaderUtils::order_in_error_state(order_status))
return ChildOrderError::ORDER_STATE_ERROR;
if (SpreaderUtils::SpreaderUtils::order_in_cancelled_state(order_status)){
@@ -218,12 +218,12 @@ private:
// i. cancels with messages are always an error state
// ii. when we get a cancel when not expecting thats also
// an error state
if(ord_ev.state.last_msg().length() > 0 || !expecting_cancel)
if(ord_ev.last_msg.length() > 0 || !expecting_cancel)
return ChildOrderError::UNEXPECTED_CANCEL_ERROR;
}
// this is an inelegant way to detect modify errors
if (!SpreaderUtils::order_in_terminal_state(order_status) &&
ord_ev.state.last_msg().length() > 0)
ord_ev.last_msg.length() > 0)
return ChildOrderError::MODIFY_ERROR;
return ChildOrderError::NONE;
}
@@ -113,11 +113,11 @@ public:
// last filled price
void on_order_update(const bts::orderman::OrderStateUpdateEvent& e){
bts_assert(!_is_live || e.key == order_key());
bts_assert(e.state.filled_quantity() >= _filled_quantity.value());
_status = e.state.status();
_last_filled_quantity = Quantity(e.state.filled_quantity() - _filled_quantity.value());
_last_filled_price = Price(e.state.last_price());
_last_reported_order_price=Price(e.state.price());
bts_assert(e.filled_quantity >= _filled_quantity.value());
_status = e.status;
_last_filled_quantity = Quantity(e.filled_quantity - _filled_quantity.value());
_last_filled_price = Price(e.last_price);
_last_reported_order_price=Price(e.price);
SpreaderUtils::compute_average_price(_filled_price,
_filled_quantity,
_last_filled_price,
@@ -72,9 +72,9 @@ public:
// this function checks for the order status (above) or
// for the message size (todo:this is a temporary fix)
static bool order_in_error_state(const bts::orderman::OrderStateUpdateEvent& ord_ev){
bts::api::orderman::OrderStatus order_status = ord_ev.state.status();
bts::api::orderman::OrderStatus order_status = ord_ev.status;
return order_in_error_state(order_status) ||
(!order_in_terminal_state (order_status) && ord_ev.state.last_msg().length() > 0);
(!order_in_terminal_state (order_status) && ord_ev.last_msg.length() > 0);
}
// checks for the 4 states that are designated error states
static bool order_in_terminal_state(bts::api::orderman::OrderStatus order_status){
@@ -103,7 +103,6 @@ void on_order_update (SpreaderChildOrderState& order,
order_state.set_filled_quantity (filled_quantity.value());
bts::api::orderman::OrderDefinition order_def;
OrderStateUpdateEvent e (OrderKey(),OrderKey(),order_def, order_state);
e.state = order_state;
order.on_order_update(e);
}
/**********************************************************************/
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/python3
import pandas as pd
import sqlite3
import dateutil
import matplotlib.pyplot as plt
import numpy as np
import sys
from pathlib import Path
log_path = "%s/sparkdata/data/logging/log" % str(Path.home())
raw_data = None
def load():
global raw_data
global requotes
global hedges
raw_data = pd.read_csv(log_path,sep='|',names=['time','ts','ss','ll','tid','tn','pid','loc','dt','msg'],
usecols=lambda x: x in ['time','ts','tn','msg'],
parse_dates=True)
if len(sys.argv) > 1:
log_path = sys.argv[1]
load()
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/python3
import pandas as pd
import sqlite3
import dateutil
import matplotlib.pyplot as plt
import numpy as np
import sys
from pathlib import Path
# dt = dateutil.parser.parse('2018-10-23 16:35:01.566000')
# requotes = requotes[requotes.session_start == dt]
#samples = requotes.resample('2S',on='trace_time').mean()
#samples.plot()
#plotable = requotes[(requotes.total < 1000) & (requotes.trace_time > '2018-11-06')][-200:].set_index('trace_time')
requotes = None
hedges = None
db_path = "file:%s/sparkdata/data/databases/perf_trace/perf_trace.sqlite?mode=ro" % str(Path.home())
raw_data = None
def load():
global raw_data
global requotes
global hedges
c = sqlite3.connect(db_path,uri = True)
raw_data = pd.read_sql_query("select * from trace;", c)
c.close()
requotes = raw_data[(raw_data.md>0) ]
requotes = requotes.assign (total = (requotes.fix_socket_w - requotes.md) / 1000)
requotes = requotes.assign (md = (requotes.spreader_on_book - requotes.md) / 1000)
requotes = requotes.assign (spreader = (requotes.order_update - requotes.spreader_on_book) / 1000)
requotes = requotes.assign (native_order = (requotes.exe_gw - requotes.spreader_on_book) / 1000)
requotes = requotes.assign (fix = (requotes.fix_socket_w - requotes.exe_gw) / 1000)
requotes = requotes.assign (session_start = requotes.session_start.map(dateutil.parser.parse))
requotes = requotes.assign (trace_time = requotes.trace_time.map(dateutil.parser.parse))
requotes = requotes.drop(['fix_socket_w','spreader_modify_order','send_fix_buffer',
'order_update','exe_gw','fix_socket_r','md_src','host', 'do_work_begin',
'order_key','version',
'build_type', 'spreader_on_book',
'spreader_on_child_order_update','md_sn'],axis=1)
hedges = raw_data[(raw_data.spreader_on_child_order_update>0)]
hedges = hedges.assign (total = (hedges.fix_socket_w - hedges.fix_socket_r) / 1000)
hedges = hedges.assign (fix_in = (hedges.spreader_on_child_order_update - hedges.fix_socket_r) / 1000)
hedges = hedges.assign (spreader = (hedges.order_update - hedges.spreader_on_child_order_update)/ 1000)
hedges = hedges.assign (native_order = (hedges.exe_gw - hedges.order_update)/ 1000)
hedges = hedges.assign (fix_out = (hedges.fix_socket_w - hedges.exe_gw)/ 1000)
hedges = hedges.assign (in_native_order = (hedges.send_fix_buffer - hedges.order_update)/ 1000)
hedges = hedges.drop(['fix_socket_w','spreader_modify_order','send_fix_buffer',
'order_update','exe_gw','fix_socket_r','md_src','host','do_work_begin',
'order_key','version',
'build_type', 'spreader_on_book',
'spreader_on_child_order_update','md_sn'],axis=1)
summary()
requotes
def plot(n):
plt.clf()
p = requotes[-n:][['trace_time','total']]
q = p.set_index('trace_time')
plt.plot(q)
def summary():
grouped = requotes.groupby(requotes['session_start'])
counts = grouped.count()['total']
medians = grouped.median()
medians['count'] = counts
print ("\n\nRequotes")
print (medians)
grouped = hedges.groupby(hedges['session_start'])
counts = grouped.count()['total']
medians = grouped.median()
medians['count'] = counts
print ("\n\nHedges")
print (medians)
if len(sys.argv) > 1:
db_path = sys.argv[1]
load()
@@ -3,7 +3,7 @@
if [ $# -ne 2 ];
then
echo
echo " Syntax: quick_stats.sh tick_to_order|fill_to_order"
echo " Syntax: quick_stats.sh log_file tick_to_order|fill_to_order"
echo
exit 1
fi
@@ -22,6 +22,6 @@ sleep $TRACE_DURATION
lttng stop
lttng destroy
mkdir -p trace_data
babeltrace ~/lttng-traces/* > trace_data/raw_trace.txt
babeltrace --clock-seconds ~/lttng-traces/* > trace_data/raw_trace.txt
@@ -23,7 +23,7 @@
#include "lib/plugin/SparkServices.h"
#include "lib/utils/thread_manager.h"
#include "lib/utils/type_name.h"
#include <boost/algorithm/string/predicate.hpp>
namespace bts {
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "lib/config/protos/jetfix.pb.h"
#include "lib/domain_model/SecurityKey.h"
#include "orderman_utils/orderman_utils.h"
namespace bts {
namespace api {
namespace orderman {
class OrderDefinition;
}
} // namespace api
namespace orderman2 {
class OrderState;
class IOrderManagerObserver {
public:
void subscribe_book(SecurityKey, void *sub);
void request_new_fill_buffers(config::JetFIX::Connection const &,
size_t n_buffers, void *sub);
void request_new_orders(api::orderman::OrderDefinition const &,
OrderKey parent_key, size_t n_orders, void *sub);
void send_fix(size_t gw_idx, jetfix::OutFIXPtr const &);
};
} // namespace orderman2
} // namespace bts
+7
View File
@@ -0,0 +1,7 @@
#include "OrderManager2.h"
#include "IOrderManagerObserver.h"
namespace bts {
namespace orderman2 {
}}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include "orderman_utils/orderman_utils.h"
#include "orderman_utils/Book.h"
#include "lib/jetfix/common/OutFIXPtr.h"
#include <vector>
namespace bts {
namespace api { class Security;}
namespace orderman2 {
class Order;
class IOrderManagerObserver;
class ExecutionReport;
class OrderManager {
OrderManager();
void init(IOrderManagerObserver &observer);
void on_activate(Order &, Price , Quantity);
void on_update(Order&, Price , Quantity);
void on_cancel(Order&);
void on_book_update(Book const&b,void*sub);
void on_execution_report(ExecutionReport &);
void on_securities(std::vector<api::Security const*> const&);
void on_new_fill_buffers(std::vector<jetfix::OutFIXPtr> const&,void*sub);
void on_new_orders(std::vector<Order*> const&,void*sub);
private:
struct PImpl;
std::unique_ptr<PImpl> me;
};
}}
+72
View File
@@ -0,0 +1,72 @@
#pragma once
#include <memory>
#include "lib/domain_model/common.h"
#include "lib/config/protos/MarketData.pb.h"
#include "lib/md/PriceSize.h"
namespace bts {
namespace orderman2 {
using BookSide = std::array<md::PriceSize,5> ;
class Book {
public:
Book () :
_security_key(),
_book()
{}
Book (SecurityKey key) :
_security_key(key),
_book()
{}
Book (SecurityKey key,
BookSide const&bid,
BookSide const&ask) :
_security_key(key),
_book {bid,ask}
{}
Book (Book const&rhs)
: _security_key(rhs._security_key),
_book(rhs._book)
{}
Book &operator=(Book const &rhs)
{
_security_key = rhs._security_key;
_book = rhs._book;
return *this;
}
SecurityKey security_key() const {return _security_key;} ;
BookSide const &bid () const {return _book[0];}
BookSide const &ask () const {return _book[1];}
BookSide &bid () {return _book[0];}
BookSide &ask () {return _book[1];}
size_t n_levels() const {return std::tuple_size<BookSide>();}
BookSide const& side(api::Side bs) const
{
bts_assert_d(bs != api::SIDE_UNDEFINED);
return _book[bs - api::SIDE_BUY];
}
BookSide & side(api::Side bs)
{
bts_assert_d(bs != api::SIDE_UNDEFINED);
return _book[bs - api::SIDE_BUY];
}
private:
friend class MDSource;
SecurityKey _security_key;
std::array<BookSide,2> _book;
};
}}
@@ -0,0 +1,10 @@
#pragma once
#include "lib/utils/ValueWrapper.h"
namespace bts {
namespace orderman2 {
using Price = utils::ValueWrapper<struct PriceTag,double>;
using Quantity = utils::ValueWrapper<struct QuantityTag,uint32_t>;
using OrderKey = utils::ValueWrapper<struct OrderKeyTag, uint32_t>;
}}
+63 -7
View File
@@ -2,12 +2,13 @@
#include "lib/logging/logging.h"
#include "lib/os/os.h"
#include "lib/persistence/persistence_utils.h"
#include "lib/utils/Sqlite_utils.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/bts_chrono.h"
#include "lib/utils/bts_no_copy.h"
#include "lib/utils/defer.h"
#include "lib/utils/file_utils.h"
#include "lib/persistence/persistence_utils.h"
#include <boost/filesystem.hpp>
#include <sqlite/sqlite3.h>
@@ -15,10 +16,9 @@
namespace bts {
namespace persistence {
struct SqliteStore::PImpl {
PImpl(std::string const &store_root, std::string const &db_name)
: db(db_name), db_path(persistence::storage_location(db,store_root)),
: db(db_name), db_path(persistence::storage_location(db, store_root)),
conn(nullptr, sqlite3_close), insert(nullptr, sqlite3_finalize),
remove(nullptr, sqlite3_finalize),
remove_all(nullptr, sqlite3_finalize),
@@ -156,7 +156,22 @@ void SqliteStore::remove(std::string const &ns,
SQLITE_CHK(sqlite3_bind_text(me->remove.get(), 2, k.c_str(), k.size(),
SQLITE_TRANSIENT),
me->conn);
bts_assert(sqlite3_step(me->remove.get()) == SQLITE_DONE);
auto r = sqlite3_step(me->remove.get());
if (r != SQLITE_DONE) {
LOG(persist, Error,
"Could not delete row in store. Will retry once. error_code: {} "
"ns:{}, k: {}",
r, ns, k);
sleep_for(2s);
r = sqlite3_step(me->remove.get());
if (r != SQLITE_DONE) {
LOG(persist, Error,
"Failed a second time to delete row. Bailing out. error_code: "
"{}",
r);
bts_assert(r == SQLITE_DONE);
}
}
SQLITE_CHK(sqlite3_reset(me->remove.get()), me->conn);
}
}
@@ -166,7 +181,22 @@ void SqliteStore::remove_all(std::string const &ns) {
SQLITE_CHK(sqlite3_bind_text(me->remove_all.get(), 1, ns.c_str(), ns.size(),
SQLITE_TRANSIENT),
me->conn);
bts_assert(sqlite3_step(me->remove_all.get()) == SQLITE_DONE);
auto r = sqlite3_step(me->remove_all.get());
if (r != SQLITE_DONE) {
LOG(persist, Error,
"Could not delete all rows in store. Will retry once. error_code: {} "
"ns:{}",
r, ns);
sleep_for(2s);
r = sqlite3_step(me->remove_all.get());
if (r != SQLITE_DONE) {
LOG(persist, Error,
"Failed a second time to delete all rows. Bailing out. error_code: "
"{}",
r);
bts_assert(r == SQLITE_DONE);
}
}
SQLITE_CHK(sqlite3_reset(me->remove_all.get()), me->conn);
}
@@ -182,7 +212,20 @@ void SqliteStore::insert(std::string const &ns, std::string const &k,
SQLITE_TRANSIENT),
me->conn);
auto r = sqlite3_step(me->insert.get());
bts_assert(r == SQLITE_DONE);
if (r != SQLITE_DONE) {
LOG(persist, Error,
"Could not insert row in store. Will retry once. error_code: {} ns: "
"{}, k: {}, v: {}",
r, ns, k, v);
sleep_for(2s);
r = sqlite3_step(me->insert.get());
if (r != SQLITE_DONE) {
LOG(persist, Error,
"Failed a second time to insert row. Bailing out. error_code: {}",
r);
bts_assert(r == SQLITE_DONE);
}
}
SQLITE_CHK(sqlite3_reset(me->insert.get()), me->conn);
}
@@ -198,7 +241,20 @@ void SqliteStore::update(std::string const &ns, std::string const &k,
SQLITE_TRANSIENT),
me->conn);
auto r = sqlite3_step(me->update.get());
bts_assert(r == SQLITE_DONE);
if (r != SQLITE_DONE) {
LOG(persist, Error,
"Could not update row in store. Will retry once. error_code: {} ns: "
"{}, k: {}, v: {}",
r, ns, k, v);
sleep_for(2s);
r = sqlite3_step(me->update.get());
if (r != SQLITE_DONE) {
LOG(persist, Error,
"Failed a second time to update row. Bailing out. error_code: {}",
r);
bts_assert(r == SQLITE_DONE);
}
}
SQLITE_CHK(sqlite3_reset(me->update.get()), me->conn);
}
+3
View File
@@ -73,6 +73,9 @@ inline bool equal(google::protobuf::Timestamp const &t1, google::protobuf::Times
return t1.seconds() == t2.seconds() && t1.nanos() == t2.nanos();
}
inline double to_double_time(google::protobuf::Timestamp const &t1)
{return t1.seconds() + t1.nanos()/1e9;}
#endif
}}}
+361
View File
@@ -0,0 +1,361 @@
/*
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the "Software") to use, reproduce, display, distribute,
* execute, and transmit the Software, and to prepare derivative works of the
* Software, and to permit third-parties to whom the Software is furnished to
* do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer,
* must be included in all copies of the Software, in whole or in part, and
* all derivative works of the Software, unless such copies or derivative
* works are solely in the form of machine-executable object code generated by
* a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <type_traits>
#include <utility>
#include <functional>
namespace stdext {
namespace inplace_function_detail {
static constexpr size_t InplaceFunctionDefaultCapacity = 32;
#if defined(__GLIBCXX__) // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61458
template<size_t Cap>
union aligned_storage_helper {
struct double1 { double a; };
struct double4 { double a[4]; };
template<class T> using maybe = std::conditional_t<(Cap >= sizeof(T)), T, char>;
char real_data[Cap];
maybe<int> a;
maybe<long> b;
maybe<long long> c;
maybe<void*> d;
maybe<void(*)()> e;
maybe<double1> f;
maybe<double4> g;
maybe<long double> h;
};
template<size_t Cap, size_t Align = std::alignment_of<aligned_storage_helper<Cap>>::value>
struct aligned_storage {
using type = std::aligned_storage_t<Cap, Align>;
};
template<size_t Cap, size_t Align = std::alignment_of<aligned_storage_helper<Cap>>::value>
using aligned_storage_t = typename aligned_storage<Cap, Align>::type;
#else
using std::aligned_storage;
using std::aligned_storage_t;
#endif
template<typename T> struct wrapper
{
using type = T;
};
template<typename R, typename... Args> struct vtable
{
using storage_ptr_t = void*;
using invoke_ptr_t = R(*)(storage_ptr_t, Args&&...);
using process_ptr_t = void(*)(storage_ptr_t, storage_ptr_t);
using destructor_ptr_t = void(*)(storage_ptr_t);
const invoke_ptr_t invoke_ptr;
const process_ptr_t copy_ptr;
const process_ptr_t relocate_ptr;
const destructor_ptr_t destructor_ptr;
explicit constexpr vtable() noexcept :
invoke_ptr{ [](storage_ptr_t, Args&&...) -> R
{ throw std::bad_function_call(); }
},
copy_ptr{ [](storage_ptr_t, storage_ptr_t) noexcept -> void {} },
relocate_ptr{ [](storage_ptr_t, storage_ptr_t) noexcept -> void {} },
destructor_ptr{ [](storage_ptr_t) noexcept -> void {} }
{}
template<typename C> explicit constexpr vtable(wrapper<C>) noexcept :
invoke_ptr{ [](storage_ptr_t storage_ptr, Args&&... args)
noexcept(noexcept(std::declval<C>()(args...))) -> R
{ return (*static_cast<C*>(storage_ptr))(
std::forward<Args>(args)...
); }
},
copy_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr)
noexcept(std::is_nothrow_copy_constructible<C>::value) -> void
{ ::new (dst_ptr) C{ (*static_cast<C*>(src_ptr)) }; }
},
relocate_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr)
noexcept(std::is_nothrow_move_constructible<C>::value) -> void
{
::new (dst_ptr) C{ std::move(*static_cast<C*>(src_ptr)) };
static_cast<C*>(src_ptr)->~C();
}
},
destructor_ptr{ [](storage_ptr_t src_ptr)
noexcept -> void
{ static_cast<C*>(src_ptr)->~C(); }
}
{}
vtable(const vtable&) = delete;
vtable(vtable&&) = delete;
vtable& operator= (const vtable&) = delete;
vtable& operator= (vtable&&) = delete;
~vtable() = default;
};
template<typename R, typename... Args>
#if __cplusplus >= 201703L
inline constexpr
#endif
vtable<R, Args...> empty_vtable{};
template<size_t DstCap, size_t DstAlign, size_t SrcCap, size_t SrcAlign>
struct is_valid_inplace_dst : std::true_type
{
static_assert(DstCap >= SrcCap,
"Can't squeeze larger inplace_function into a smaller one"
);
static_assert(DstAlign % SrcAlign == 0,
"Incompatible inplace_function alignments"
);
};
} // namespace inplace_function_detail
template<
typename Signature,
size_t Capacity = inplace_function_detail::InplaceFunctionDefaultCapacity,
size_t Alignment = std::alignment_of<inplace_function_detail::aligned_storage_t<Capacity>>::value
>
class inplace_function; // unspecified
template<
typename R,
typename... Args,
size_t Capacity,
size_t Alignment
>
class inplace_function<R(Args...), Capacity, Alignment>
{
public:
using capacity = std::integral_constant<size_t, Capacity>;
using alignment = std::integral_constant<size_t, Alignment>;
using storage_t = inplace_function_detail::aligned_storage_t<Capacity, Alignment>;
using vtable_t = inplace_function_detail::vtable<R, Args...>;
using vtable_ptr_t = const vtable_t*;
template <typename, size_t, size_t> friend class inplace_function;
inplace_function() noexcept :
vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable<R, Args...>)}
{}
template<
typename T,
typename C = std::decay_t<T>,
typename = std::enable_if_t<
!(std::is_same<C, inplace_function>::value
|| std::is_convertible<C, inplace_function>::value)
>
>
inplace_function(T&& closure)
{
#if __cplusplus >= 201703L
static_assert(std::is_invocable_r<R, C, Args...>::value,
"inplace_function cannot be constructed from non-callable type"
);
#endif
static_assert(std::is_copy_constructible<C>::value,
"inplace_function cannot be constructed from non-copyable type"
);
static_assert(sizeof(C) <= Capacity,
"inplace_function cannot be constructed from object with this (large) size"
);
static_assert(Alignment % std::alignment_of<C>::value == 0,
"inplace_function cannot be constructed from object with this (large) alignment"
);
static const vtable_t vt{inplace_function_detail::wrapper<C>{}};
vtable_ptr_ = std::addressof(vt);
::new (std::addressof(storage_)) C{std::forward<T>(closure)};
}
inplace_function(std::nullptr_t) noexcept :
vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable<R, Args...>)}
{}
inplace_function(const inplace_function& other) :
vtable_ptr_{other.vtable_ptr_}
{
vtable_ptr_->copy_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
}
inplace_function(inplace_function&& other) :
vtable_ptr_{std::exchange(other.vtable_ptr_, std::addressof(inplace_function_detail::empty_vtable<R, Args...>))}
{
vtable_ptr_->relocate_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
}
inplace_function& operator= (std::nullptr_t) noexcept
{
vtable_ptr_->destructor_ptr(std::addressof(storage_));
vtable_ptr_ = std::addressof(inplace_function_detail::empty_vtable<R, Args...>);
return *this;
}
inplace_function& operator= (const inplace_function& other)
{
if(this != std::addressof(other))
{
vtable_ptr_->destructor_ptr(std::addressof(storage_));
vtable_ptr_ = other.vtable_ptr_;
vtable_ptr_->copy_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
}
return *this;
}
inplace_function& operator= (inplace_function&& other)
{
if(this != std::addressof(other))
{
vtable_ptr_->destructor_ptr(std::addressof(storage_));
vtable_ptr_ = std::exchange(other.vtable_ptr_, std::addressof(inplace_function_detail::empty_vtable<R, Args...>));
vtable_ptr_->relocate_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
}
return *this;
}
~inplace_function()
{
vtable_ptr_->destructor_ptr(std::addressof(storage_));
}
R operator() (Args... args) const
{
return vtable_ptr_->invoke_ptr(
std::addressof(storage_),
std::forward<Args>(args)...
);
}
constexpr bool operator== (std::nullptr_t) const noexcept
{
return !operator bool();
}
constexpr bool operator!= (std::nullptr_t) const noexcept
{
return operator bool();
}
explicit constexpr operator bool() const noexcept
{
return vtable_ptr_ != std::addressof(inplace_function_detail::empty_vtable<R, Args...>);
}
template<size_t Cap, size_t Align>
operator inplace_function<R(Args...), Cap, Align>() const&
{
static_assert(inplace_function_detail::is_valid_inplace_dst<
Cap, Align, Capacity, Alignment
>::value, "conversion not allowed");
return {vtable_ptr_, vtable_ptr_->copy_ptr, std::addressof(storage_)};
}
template<size_t Cap, size_t Align>
operator inplace_function<R(Args...), Cap, Align>() &&
{
static_assert(inplace_function_detail::is_valid_inplace_dst<
Cap, Align, Capacity, Alignment
>::value, "conversion not allowed");
auto vtable_ptr = std::exchange(vtable_ptr_, std::addressof(inplace_function_detail::empty_vtable<R, Args...>));
return {vtable_ptr, vtable_ptr->relocate_ptr, std::addressof(storage_)};
}
void swap(inplace_function& other)
{
if (this == std::addressof(other)) return;
storage_t tmp;
vtable_ptr_->relocate_ptr(
std::addressof(tmp),
std::addressof(storage_)
);
other.vtable_ptr_->relocate_ptr(
std::addressof(storage_),
std::addressof(other.storage_)
);
vtable_ptr_->relocate_ptr(
std::addressof(other.storage_),
std::addressof(tmp)
);
std::swap(vtable_ptr_, other.vtable_ptr_);
}
friend void swap(inplace_function& lhs, inplace_function& rhs)
{
lhs.swap(rhs);
}
private:
vtable_ptr_t vtable_ptr_;
mutable storage_t storage_;
inplace_function(
vtable_ptr_t vtable_ptr,
typename vtable_t::process_ptr_t process_ptr,
typename vtable_t::storage_ptr_t storage_ptr
) : vtable_ptr_{vtable_ptr}
{
process_ptr(std::addressof(storage_), storage_ptr);
}
};
} // namespace stdext
+3 -3
View File
@@ -264,9 +264,9 @@ void DynamicIcebergOrder::on_child_order_update(
switch (me->state.status()) {
case ORDER_STATUS_WORKING:
if (ev.key == me->child.order.key()) {
me->child.state = ev.state;
me->child.state = ev.rebuild_state();
switch (ev.state.status()) {
switch (me->child.state.status()) {
case ORDER_STATUS_WORKING:
case ORDER_STATUS_PENDING_WORKING:
@@ -312,7 +312,7 @@ void DynamicIcebergOrder::on_child_order_update(
"DynamicIceberg order {} received update for non active child "
"order{} . update state = {} ",
key().value(), me->child.order.key().value(),
utils::pb::to_string(ev.state));
utils::pb::to_string(ev.rebuild_state()));
}
break;
+1 -1
View File
@@ -1 +1 @@
set(sources bts_trace.cpp;cmds/AutoCert.cpp;cmds/Benchmark.cpp;cmds/CmdRegistry.cpp;cmds/ConfigCheck.cpp;cmds/Help.cpp;cmds/Md.cpp;cmds/QuickTest.cpp;cmds/Run.cpp;cmds/RunSim.cpp;cmds/Span.cpp;cmds/Test.cpp;cmds/TestCeloxica.cpp;cmds/Web.cpp;dspark/mirage/quick_queue/quick_queue.cpp;exchange_sim/ExchangeSim.cpp;exchange_sim/ExchangeSimApplication.cpp;exchange_sim/book/Book.cpp;exchange_sim/book/ExchangeSim.proto;exchange_sim/book/Order.cpp;exchange_sim/book/OrderMatcher.cpp;exchange_sim/book/book_transform.cpp;exchange_sim/tests/test_book_transform.cpp;http/ApiGateway.cpp;http/WebServer.cpp;main.cpp;md/platforms/celoxica/CXListener.cpp;md/platforms/celoxica/CXMain.cpp;md/platforms/celoxica/tests/cx_tests.cpp;md/platforms/mdp3_onixs/MDP3Listener.cpp;md/platforms/mdp3_onixs/MDP3Main.cpp;md/platforms/mdp3_onixs/tests/mdp3_tests.cpp;md/run_md.cpp;position/IMarginObserver.cpp;position/IPositionObserver.cpp;position/MarginServiceCore.cpp;position/PositionServiceCore.cpp;position/PositionServiceServer.cpp;position/tests/PositionTests.cpp;support/ErrorReportService.cpp;support/ShellLatencyServer.cpp;user/AdminServiceServer.cpp;user/UserServiceServer.cpp;user/tests/admin_service_tests.cpp)
set(sources bts_trace.cpp;cmds/AutoCert.cpp;cmds/Benchmark.cpp;cmds/CmdRegistry.cpp;cmds/ConfigCheck.cpp;cmds/Help.cpp;cmds/Md.cpp;cmds/PrintHedgeOrders.cpp;cmds/QuickTest.cpp;cmds/Run.cpp;cmds/RunSim.cpp;cmds/Span.cpp;cmds/Test.cpp;cmds/TestCeloxica.cpp;cmds/Web.cpp;dspark/mirage/quick_queue/quick_queue.cpp;exchange_sim/ExchangeSim.cpp;exchange_sim/ExchangeSimApplication.cpp;exchange_sim/book/Book.cpp;exchange_sim/book/ExchangeSim.proto;exchange_sim/book/Order.cpp;exchange_sim/book/OrderMatcher.cpp;exchange_sim/book/book_transform.cpp;exchange_sim/tests/test_book_transform.cpp;http/ApiGateway.cpp;http/WebServer.cpp;main.cpp;md/ChannelVolume.cpp;md/MarketDataAuxiliaryServiceServer.cpp;md/VolumeAtPriceState.cpp;md/platforms/celoxica/CXListener.cpp;md/platforms/celoxica/CXMain.cpp;md/platforms/celoxica/tests/cx_tests.cpp;md/platforms/mdp3_onixs/MDP3Listener.cpp;md/platforms/mdp3_onixs/MDP3Main.cpp;md/platforms/mdp3_onixs/tests/mdp3_tests.cpp;md/run_md.cpp;position/IMarginObserver.cpp;position/IPositionObserver.cpp;position/MarginServiceCore.cpp;position/PositionServiceCore.cpp;position/PositionServiceServer.cpp;position/tests/PositionTests.cpp;support/ErrorReportService.cpp;support/ShellLatencyServer.cpp;user/AdminServiceServer.cpp;user/UserServiceServer.cpp;user/tests/admin_service_tests.cpp)
+188 -25
View File
@@ -6,6 +6,7 @@
#include "lib/mercury/actors/Actor.h"
#include "lib/mercury/common/FatWrapper.h"
#include "lib/mercury/executor/Executor.h"
#include "lib/orderman/events/OrderStateUpdateEvent.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/Puid.h"
@@ -30,12 +31,87 @@
#include <moodycamel/concurrentqueue.h>
#include <moodycamel/readerwriterqueue.h>
#include <mutex>
#include <sys/time.h>
#include <tbb/concurrent_queue.h>
namespace bts {
namespace cmd {
using namespace orderman;
api::orderman::NativeOrderState const *
get_native_state(api::orderman::OrderState const &st) {
switch (st.custom_case()) {
case api::orderman::OrderState::kMarket:
return &st.market().native_state();
case api::orderman::OrderState::kLimit:
return &st.limit().native_state();
case api::orderman::OrderState::kStop:
return &st.stop().native_state();
case api::orderman::OrderState::kStopLimit:
return &st.stop_limit().native_state();
default:
return nullptr;
};
}
class QuickOrderStateUpdateEvent {
public:
QuickOrderStateUpdateEvent(OrderKey key, OrderKey parent_key,
api::orderman::OrderDefinition const &def,
api::orderman::OrderState const &state,
PerfTrace const &trace = PerfTrace())
: key(key), parent_key(parent_key), trace(trace), definition(&def) {
status = state.status();
price = state.price();
quantity = state.quantity();
filled_quantity = state.quantity();
last_price = state.last_price();
avg_price = state.avg_price();
last_msg = state.last_msg();
custom = state.custom_case();
auto native_state = get_native_state(state);
if (native_state) {
exchange_order_id = native_state->exchange_order_id();
last_msg_seq_num = native_state->last_msg_seq_num();
sub_error_code = native_state->sub_error_code();
native_status = native_state->native_status();
routing_record = native_state->routing_record();
native_type = native_state->native_type();
orderman::str_to(native_state->last_transact_time(),&last_transact_time);
}
}
OrderKey key;
OrderKey parent_key;
// common state
api::orderman::OrderStatus status;
double price;
uint32_t quantity;
uint32_t filled_quantity;
double last_price;
double avg_price;
std::string last_msg;
PerfTrace trace;
api::orderman::OrderState::CustomCase custom;
// native order state
std::string exchange_order_id;
uint64_t last_msg_seq_num;
api::orderman::NativeOrderState::SubError sub_error_code;
api::orderman::NativeOrderStatus native_status;
api::orderman::RoutingRecord routing_record;
api::orderman::NativeOrderType native_type;
mercury::TimeStampStr last_transact_time;
api::orderman::OrderDefinition const *definition;
};
auto sec_str = " id: 3\n"
" symbol: \"ES\"\n"
" exchange: VENUE_GLBX\n"
@@ -77,37 +153,59 @@ auto order_str = " order_key: 189285\n"
" cti_code: CTI_CTI1\n"
" order_status: OS_UNACKNOWLEDGED\n";
auto order_state_str = " status: ORDER_STATUS_COMPLETED\n"
" price: 65.56\n"
" quantity: 5\n"
" filled_quantity: 5\n"
" last_price: 65.56\n"
" avg_price: 65.56\n"
auto order_state_str = " status: ORDER_STATUS_WORKING\n"
" price: 73.42\n"
" quantity: 25\n"
" last_msg: \"\"\n"
" last_change {\n"
" seconds: 1532982945\n"
" nanos: 378466000\n"
" seconds: 1541621138\n"
" nanos: 899901000\n"
" }\n"
" limit {\n"
" native_state {\n"
" exchange_order_id: \"12345678901234567\"\n"
" native_status: NATIVE_ORDER_STATUS_FILLED\n"
" last_msg_seq_num: 868\n"
" native_status: NATIVE_ORDER_STATUS_REPLACED\n"
" last_msg_seq_num: 31869\n"
" routing_record {\n"
" session: \"sim\"\n"
" account: \"ca_ACC A\"\n"
" account: \"ACC A\"\n"
" operator_id: \"op_id_bts\"\n"
" cti_code: CTI_CODE_CTI_CODE1\n"
" customer_or_firm: CUSTOMER_OR_FIRM_FIRM\n"
" location: \"us,nc\"\n"
" }\n"
" native_type: NATIVE_ORDER_TYPE_LIMIT\n"
" last_transact_time: \"20181107-20:00:46.000\"\n"
" }\n"
" }\n"
" created {\n"
" seconds: 1532962902\n"
" nanos: 989007000\n"
" seconds: 1541619812\n"
" nanos: 895210000\n"
" }\n";
auto clean_os =
" status: ORDER_STATUS_WORKING\n"
" price: 73.42\n"
" quantity: 25\n"
" last_msg: \"\"\n"
" limit {\n"
" native_state {\n"
//" exchange_order_id: \"12345678901234567\"\n"
" native_status: NATIVE_ORDER_STATUS_REPLACED\n"
" last_msg_seq_num: 31869\n"
// " routing_record {\n"
// " session: \"sim\"\n"
// " account: \"ACC A\"\n"
// " operator_id: \"op_id_bts\"\n"
// " cti_code: CTI_CODE_CTI_CODE1\n"
// " customer_or_firm: CUSTOMER_OR_FIRM_FIRM\n"
// " location: \"us,nc\"\n"
// " }\n"
" native_type: NATIVE_ORDER_TYPE_LIMIT\n"
" last_transact_time: \"20181107-20:00:46.000\"\n"
" }\n"
" }\n";
static void gen_id(benchmark::State &state) {
std::string scope = "benchmark";
@@ -160,16 +258,29 @@ static void copy_perf_trace(benchmark::State &state) {
}
BENCHMARK(copy_perf_trace);
// static void serialize_order(benchmark::State &state) {
// auto order_state =
// utils::pb::from_string<api::orderman::OrderState>(order_state_str);
// auto
// auto order_state_cpy = order_state;
// for (auto _ : state) {
// order_state_cpy = order_state;
// }
// }
// BENCHMARK(copy_order);
static void init_with_clean_os(benchmark::State &state) {
api::orderman::OrderDefinition def;
auto order_state =
utils::pb::from_string<api::orderman::OrderState>(clean_os);
PerfTrace trace;
OrderKey k(1);
for (auto _ : state)
benchmark::DoNotOptimize(
QuickOrderStateUpdateEvent(k, k, def, order_state, trace));
}
BENCHMARK(init_with_clean_os);
static void init_with_old_order_update(benchmark::State &state) {
api::orderman::OrderDefinition def;
auto order_state =
utils::pb::from_string<api::orderman::OrderState>(order_state_str);
PerfTrace trace;
OrderKey k(1);
for (auto _ : state)
benchmark::DoNotOptimize(
orderman::OrderStateUpdateEvent(k, k, def, order_state, trace));
}
BENCHMARK(init_with_old_order_update);
static void serialize_update(benchmark::State &state) {
api::orderman::Update update;
@@ -446,7 +557,6 @@ static void tbb_concurrent_queue(benchmark::State &state) {
}
BENCHMARK(tbb_concurrent_queue);
static void boost_spsc(benchmark::State &state) {
boost::lockfree::spsc_queue<int> q(10000);
@@ -652,7 +762,7 @@ static void moodycamel_concurrent_queue(benchmark::State &state) {
using Q = moodycamel::ConcurrentQueue<int>;
Q queue(8);
int i = 0;
for (auto _ : state) {
queue.enqueue(++i); // NOLINT
benchmark::DoNotOptimize(queue.try_dequeue(i));
@@ -672,6 +782,59 @@ static void moodycamel_spsc_queue(benchmark::State &state) {
}
BENCHMARK(moodycamel_spsc_queue);
struct MyBase {
virtual int inc(int &x) = 0;
};
struct MyDerived : MyBase {
int inc(int &x) override { return x += 1; };
};
struct MyDirect {
int inc(int &x) { return x += 1; };
};
static void derived_call(benchmark::State &state) {
int x = 0;
MyDerived d;
MyBase &b = d;
for (auto _ : state) {
benchmark::DoNotOptimize(b.inc(x));
}
}
BENCHMARK(derived_call);
static void direct_call(benchmark::State &state) {
int x = 0;
MyDerived b;
for (auto _ : state) {
benchmark::DoNotOptimize(b.inc(x));
}
}
BENCHMARK(direct_call);
static void get_time_of_day(benchmark::State &state) {
std::string result(strlen("20121204-14:44:02.999"), ' ');
struct timeval tv;
for (auto _ : state) {
gettimeofday(&tv, nullptr);
auto tm = localtime(&tv.tv_sec);
sprintf(&result[0], "%d%02d%02d-%02d:%02d:%02d.%03d", tm->tm_year,
tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, 0);
}
}
BENCHMARK(get_time_of_day);
static void fix_time_stamp(benchmark::State &state) {
std::string r(128, 32);
auto now = datetime::now();
for (auto _ : state) {
datetime::FIX_timestamp(now, &r);
}
}
BENCHMARK(fix_time_stamp);
std::string Benchmark::help() {
return "Run micro-benchmarks. Use --benchmark_filter=regex to run only "
"specific benchmarks.";
+42 -20
View File
@@ -1,36 +1,58 @@
#include "server/sparkd/cmds/PrintHedgeOrders.h"
#include "lib/persistence/SqliteStore.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/persistence/SqliteStore.h"
#include "lib/utils/PBUtils.h"
namespace bts {
namespace cmd {
int PrintHedgeOrders::execute(std::vector<std::string> const &) {
persistence::SqliteStore store("orders");
auto all = store.retrieve_all()["orders"];
persistence::SqliteStore order_store("orders");
auto all_orders = order_store.retrieve_all()["orders"];
std::cout << "order_key,time_to_completed" << "\n";
api::orderman::OrderRecord rec;
for (auto const &kvp : all)
if (utils::pb::from_string<api::orderman::OrderRecord>(kvp.second, &rec)) {
auto &def = rec.def();
auto &state = rec.state();
if (rec.parent_key() &&
def.custom_def().has_limit() &&
def.sub_type() == api::orderman::OrderSubType::ORDER_SUB_TYPE_HEDGING_LEG) {
auto created = state.created().seconds()* 1e9 + state.created().nanos();
auto updated = state.last_change().seconds()* 1e9 + state.last_change().nanos();
std::cout << kvp.first << "," << (updated-created) / 1000 / 1000 / 1000 << "\n";
std::map<uint32_t, std::pair<std::string, double>> fill_times;
persistence::SqliteStore fill_store("fills");
auto all_fills = fill_store.retrieve_all()["fills"];
for (auto const &kvp : all_fills) {
api::orderman::Fill fill_rec;
if (utils::pb::from_string<api::orderman::Fill>(kvp.second, &fill_rec)) {
auto ts = utils::pb::to_double_time(fill_rec.timestamp());
auto &p = fill_times[fill_rec.order_key()];
auto &min_time = p.second;
if (min_time < ts) {
p.first = kvp.first;
min_time = ts;
}
}
}
std::cout << "order_key,time_to_first_fill,fill_id"
<< "\n";
api::orderman::OrderRecord rec;
for (auto const &kvp : all_orders)
if (utils::pb::from_string<api::orderman::OrderRecord>(kvp.second,
&rec)) {
auto &def = rec.def();
auto &state = rec.state();
if (rec.parent_key() && def.custom_def().has_limit() &&
def.sub_type() ==
api::orderman::OrderSubType::ORDER_SUB_TYPE_HEDGING_LEG) {
auto create_ts = utils::pb::to_double_time(state.created());
auto &first_fill = fill_times[rec.key()];
auto delta = first_fill.second - create_ts;
if (first_fill.first.size())
std::cout << kvp.first << "," << delta << ","
<< first_fill.first << "\n";
}
}
return 0;
}
std::string PrintHedgeOrders::help(){
return
"Print out order ids for all hedge orders"
"which were completed at least one second being created";
std::string PrintHedgeOrders::help() {
return "Print out order ids for all hedge orders"
"which were completed at least one second being created";
}
}}
} // namespace cmd
} // namespace bts
+15 -40
View File
@@ -1,61 +1,36 @@
#include "QuickTest.h"
#include "ICmd.h"
#include "lib/api/services/OrderService.server.pb.h"
#include "lib/mercury/actors/PingPong.h"
#include "lib/mercury/broker/BrokerProxy.h"
#include "lib/mercury/common/mercury_common.h"
#include "lib/mercury/executor/Executor.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/thread_manager.h"
#include "lib/orderman/events/OrderStateUpdateEvent.h"
#include <iostream>
namespace bts {
namespace cmd {
void test_ping_pong_mercury2()
{
using namespace mercury;
Executor ex1;
Executor ex2;
ex1.init_neighbours(std::vector<Executor*> {&ex2});
ex2.init_neighbours(std::vector<Executor*> {&ex1});
ex1.spawn<Ping>();
//ex1.spawn<Pong>();
ex2.spawn<Pong>();
auto t = std::thread([&ex2]() {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
while (true)
ex2.do_work();
});
bts::sleep_for(2s);
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
while (true) {
ex1.do_work();
bts::sleep_for(100ms);
}
}
using namespace bts::mercury;
using namespace bts::orderman;
int QuickTest::execute(std::vector<std::string> const &) {
test_ping_pong_mercury2();
sleep_for(10s);
for (int i = 0; i < 100; i++) {
auto t = utils::TimeStamp::now();
LOG(OrderMan,Info,"Now: {}", (utils::TimeStamp::now() - t).nanoseconds());
}
sleep_for(10s);
return 0;
}
std::string QuickTest::help() {
return "This is a place holder to make it easy to quickly write discardable "
"test code";
"test code";
}
} // namespace cmd
} // namespace bts
+1 -1
View File
@@ -1 +1 @@
set(sources ../../externals/linux/include/google/protobuf/descriptor.proto;../../externals/linux/include/google/protobuf/timestamp.proto;../lib/api/apicore.proto;../lib/api/apigateway.proto;../lib/api/apioptions.proto;../lib/api/services/AdminService.proto;../lib/api/services/ApiUnitTestService.proto;../lib/api/services/ErrorReportService.proto;../lib/api/services/HelloService.proto;../lib/api/services/MarketDataRelay.proto;../lib/api/services/OrderService.proto;../lib/api/services/OutOfProcessURLListener.proto;../lib/api/services/PersistenceService.proto;../lib/api/services/PositionService.proto;../lib/api/services/ShellDiagnosticService.proto;../lib/api/services/ShellLatencyService.proto;../lib/api/services/SyntheticSecurityService.proto;../lib/api/services/UserService.proto;../lib/domain_model/Security.proto;../lib/md/protos/co2.proto;../lib/sparkproto/SparkProtos.proto;api/build.sh;api/node_modules/cssstyle/scripts/run_tests.sh;api/node_modules/exit/test/fixtures/create-files.sh;api/node_modules/protobufjs/cli/node_modules/acorn-jsx/node_modules/acorn/bin/update_authors.sh;api/node_modules/watch/release.sh;api/quiet_build.sh;api/src/DynamicService.ts;api/src/ServiceContext.ts;api/src/bts_assert.ts;ui/src/Access.fs;ui/src/Accounts/ClipLimits.fs;ui/src/Accounts/ProductLimits.fs;ui/src/Accounts/SecurityLimits.fs;ui/src/Accounts/State.fs;ui/src/Accounts/Types.fs;ui/src/Accounts/View.fs;ui/src/App.fs;ui/src/Cmd.fs;ui/src/Controls/Button.fs;ui/src/Controls/DataGrid.fs;ui/src/Controls/Fields.fs;ui/src/Controls/Modal.fs;ui/src/Controls/ReactGrid.fs;ui/src/Controls/Table.fs;ui/src/Controls/nbsp.fs;ui/src/Global.fs;ui/src/Imports.fs;ui/src/Lens.fs;ui/src/Login/Login.fs;ui/src/Nav.fs;ui/src/State.fs;ui/src/Types.fs;ui/src/Users/State.fs;ui/src/Users/Types.fs;ui/src/Users/View.fs;ui/src/Util/EnumUtils.fs;ui/src/Util/JSMap.fs)
set(sources ../../externals/linux/include/google/protobuf/descriptor.proto;../../externals/linux/include/google/protobuf/timestamp.proto;../lib/api/apicore.proto;../lib/api/apigateway.proto;../lib/api/apioptions.proto;../lib/api/services/AdminService.proto;../lib/api/services/ApiUnitTestService.proto;../lib/api/services/ErrorReportService.proto;../lib/api/services/HelloService.proto;../lib/api/services/MarketDataAuxiliaryService.proto;../lib/api/services/MarketDataRelay.proto;../lib/api/services/OrderService.proto;../lib/api/services/OutOfProcessURLListener.proto;../lib/api/services/PersistenceService.proto;../lib/api/services/PositionService.proto;../lib/api/services/ShellDiagnosticService.proto;../lib/api/services/ShellLatencyService.proto;../lib/api/services/SyntheticSecurityService.proto;../lib/api/services/UserService.proto;../lib/domain_model/Security.proto;../lib/md/protos/co2.proto;../lib/sparkproto/SparkProtos.proto;api/build.sh;api/node_modules/cssstyle/scripts/run_tests.sh;api/node_modules/exit/test/fixtures/create-files.sh;api/node_modules/protobufjs/cli/node_modules/acorn-jsx/node_modules/acorn/bin/update_authors.sh;api/node_modules/watch/release.sh;api/quiet_build.sh;api/src/DynamicService.ts;api/src/ServiceContext.ts;api/src/bts_assert.ts;ui/src/Access.fs;ui/src/Accounts/ClipLimits.fs;ui/src/Accounts/ProductLimits.fs;ui/src/Accounts/SecurityLimits.fs;ui/src/Accounts/State.fs;ui/src/Accounts/Types.fs;ui/src/Accounts/View.fs;ui/src/App.fs;ui/src/Cmd.fs;ui/src/Controls/Button.fs;ui/src/Controls/DataGrid.fs;ui/src/Controls/Fields.fs;ui/src/Controls/Modal.fs;ui/src/Controls/ReactGrid.fs;ui/src/Controls/Table.fs;ui/src/Controls/nbsp.fs;ui/src/Global.fs;ui/src/Imports.fs;ui/src/Lens.fs;ui/src/Login/Login.fs;ui/src/Nav.fs;ui/src/State.fs;ui/src/Types.fs;ui/src/Users/State.fs;ui/src/Users/Types.fs;ui/src/Users/View.fs;ui/src/Util/EnumUtils.fs;ui/src/Util/JSMap.fs)
Regular → Executable
+4 -4
View File
@@ -1,11 +1,11 @@
#!/bin/bash
sed 's/[ \t]*|/|/g' sparkdata/data/logging/log >clean
grep '|' ~/sparkdata/data/logging/log | sed 's/[ \t]*|/|/g' > clean_db
rm log_db
sqlite3 log_db <<EOF
create table log(ts,ss,level,tid,tname,pid,loc,msg);
create table log(dt,ts,ss,level,tid,tname,pid,loc,dtype,msg);
.separator |
.import $1 log
.import clean_db log
EOF
sqlite3 log_db "select ts,tname, msg from log order by ts" > log.csv
Regular → Executable
+1 -1
View File
@@ -1,3 +1,3 @@
#!/bin/bash
tac ~/.bts/data/logging/log | grep "Sparkd launched" -m 1 -B 999999 | tac | sort -n -t '|' -k 2,5 | sed 's/[ \t]*|/|/g' | column -t -s'|'
grep Latency ~/sparkdata/data/logging/log | sort -n -t '|' -k 2,5