From 2d6fb5c15c4d19ef6f2dac8dccc90e5682d1e4bf Mon Sep 17 00:00:00 2001 From: Pedro Pinto Date: Sun, 19 Aug 2018 17:24:40 +0000 Subject: [PATCH 001/203] cleaned up build --- docker/linux | 2 +- src/lib/CMakeDeps.cmake | 2 +- tools/setup_builds.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/linux b/docker/linux index aedfd79e0..ded39afeb 160000 --- a/docker/linux +++ b/docker/linux @@ -1 +1 @@ -Subproject commit aedfd79e025ddfff2af16f9366776f0822b1fc0e +Subproject commit ded39afeb93a95ac6d63f1e9ffe4d934805b4e70 diff --git a/src/lib/CMakeDeps.cmake b/src/lib/CMakeDeps.cmake index 33d2931fa..1bb671e7e 100644 --- a/src/lib/CMakeDeps.cmake +++ b/src/lib/CMakeDeps.cmake @@ -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/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.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/cpp_config.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.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;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/MDPriceLantencyTester.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/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.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/Dispatcher.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) +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/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.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/cpp_config.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.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;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/MDPriceLantencyTester.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/Actor.cpp;mercury/broker/AsyncEventBroker.cpp;mercury/broker/CoreEventBroker.cpp;mercury/broker/IEventBroker.cpp;mercury/events/TimerEvent.cpp;mercury/mercury_utils/Executor.cpp;mercury/mercury_utils/IPollableActor.cpp;mercury/mercury_utils/mercury_common.cpp;mercury/synopsis/SynopsisBuilder.cpp;mercury/topics/ITopic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.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/OrderFactory.cpp;orderman/order_router/OrderRouter.cpp;orderman/orderman_utils/fix_order_codec.cpp;orderman/orders/DynamicOrder.cpp;orderman/orders/IcebergOrder.cpp;orderman/orders/NativeOrder.cpp;orderman/orders/Order.cpp;orderman/orders/TimeBoxOrder.cpp;orderman/orders/TriggerOrder.cpp;orderman/orders/book_algos/BookAlgo.cpp;orderman/orders/book_algos/JoinOrder.cpp;orderman/orders/book_algos/LeanOrder.cpp;orderman/orders/book_algos/TickOrder.cpp;orderman/orders/book_algos/TopOrder.cpp;orderman/orders/spreader_orders/SpreaderOrder.cpp;orderman/orders/spreader_orders/spreader/LegPropertyData.cpp;orderman/orders/spreader_orders/spreader/QuoteLegPriceQuantityState.cpp;orderman/orders/spreader_orders/spreader/SingleFillHedger.cpp;orderman/orders/spreader_orders/spreader/SingleLegQuoter.cpp;orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp;orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.cpp;orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp;orderman/orders/spreader_orders/spreader/SpreaderUtils.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderServices.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderPreActivationOrderServices.cpp;orderman/orders/spreader_orders/spreader/hedger/UnhedgedDelta.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckCancelledStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSet.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSetHistory.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteLegDriver.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckCancelledWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForSizeStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderEndStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWaitingForCancelAllStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWorkingStateWorker.cpp;orderman/orders/spreader_orders/tests/spreader_tests.cpp;orderman/server/OrderServiceServer.cpp;orderman/tests/fat_wrapper_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;persistence/IStore.cpp;persistence/PersistenceServiceServer.cpp;persistence/SqliteStore.cpp;persistence/tests/PersistenceServiceTests.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/Dispatcher.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) \ No newline at end of file diff --git a/tools/setup_builds.sh b/tools/setup_builds.sh index 8f6ef6274..e469a6f6d 100755 --- a/tools/setup_builds.sh +++ b/tools/setup_builds.sh @@ -6,7 +6,7 @@ cd $DIR/../build/ rm -rf clang_debug mkdir clang_debug cd clang_debug -cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_CXX_COMPILER=`realpath ../../tools/linux_control/rtags.sh` ../.. +cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ ../.. cd .. rm -rf ninja_debug From 526805680aaa3d0f6987b10452ad92e5e3229d8d Mon Sep 17 00:00:00 2001 From: Pedro Pinto Date: Fri, 24 Aug 2018 18:14:24 +0000 Subject: [PATCH 002/203] Updated cmake files --- src/lib/CMakeDeps.cmake | 2 +- src/server/sparkd/CMakeDeps.cmake | 2 +- src/web/CMakeDeps.cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/CMakeDeps.cmake b/src/lib/CMakeDeps.cmake index 21329f623..b4c4b1246 100644 --- a/src/lib/CMakeDeps.cmake +++ b/src/lib/CMakeDeps.cmake @@ -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/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/cpp_config.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.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/MDPriceLantencyTester.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/Dispatcher.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) +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/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/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.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/MDPriceLantencyTester.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/Actor.cpp;mercury/broker/AsyncEventBroker.cpp;mercury/broker/CoreEventBroker.cpp;mercury/broker/IEventBroker.cpp;mercury/events/TimerEvent.cpp;mercury/mercury_utils/Executor.cpp;mercury/mercury_utils/IPollableActor.cpp;mercury/mercury_utils/mercury_common.cpp;mercury/synopsis/SynopsisBuilder.cpp;mercury/topics/ITopic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.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/OrderFactory.cpp;orderman/order_router/OrderRouter.cpp;orderman/orderman_utils/fix_order_codec.cpp;orderman/orders/DynamicOrder.cpp;orderman/orders/IcebergOrder.cpp;orderman/orders/NativeOrder.cpp;orderman/orders/Order.cpp;orderman/orders/TimeBoxOrder.cpp;orderman/orders/TriggerOrder.cpp;orderman/orders/book_algos/BookAlgo.cpp;orderman/orders/book_algos/CoverOrder.cpp;orderman/orders/book_algos/JoinOrder.cpp;orderman/orders/book_algos/LeanOrder.cpp;orderman/orders/book_algos/TickOrder.cpp;orderman/orders/book_algos/TopOrder.cpp;orderman/orders/spreader_orders/SpreaderOrder.cpp;orderman/orders/spreader_orders/spreader/LegPropertyData.cpp;orderman/orders/spreader_orders/spreader/QuoteLegPriceQuantityState.cpp;orderman/orders/spreader_orders/spreader/SingleFillHedger.cpp;orderman/orders/spreader_orders/spreader/SingleLegQuoter.cpp;orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp;orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.cpp;orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp;orderman/orders/spreader_orders/spreader/SpreaderUtils.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderServices.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderPreActivationOrderServices.cpp;orderman/orders/spreader_orders/spreader/hedger/UnhedgedDelta.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckCancelledStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSet.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSetHistory.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteLegDriver.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckCancelledWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForIOCWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForSizeStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderEndStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWaitingForCancelAllStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWorkingStateWorker.cpp;orderman/orders/spreader_orders/tests/spreader_tests.cpp;orderman/server/OrderServiceServer.cpp;orderman/tests/fat_wrapper_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;persistence/IStore.cpp;persistence/PersistenceServiceServer.cpp;persistence/SqliteStore.cpp;persistence/tests/PersistenceServiceTests.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/Dispatcher.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) \ No newline at end of file diff --git a/src/server/sparkd/CMakeDeps.cmake b/src/server/sparkd/CMakeDeps.cmake index 90c4bc110..36b86a8a3 100644 --- a/src/server/sparkd/CMakeDeps.cmake +++ b/src/server/sparkd/CMakeDeps.cmake @@ -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/tests/PositionTests.cpp;support/ErrorReportService.cpp;support/ShellLatencyServer.cpp;user/AdminServiceServer.cpp;user/UserServiceServer.cpp;user/tests/admin_service_tests.cpp) \ No newline at end of file +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) \ No newline at end of file diff --git a/src/web/CMakeDeps.cmake b/src/web/CMakeDeps.cmake index 3eaaf1a02..21fe04d3f 100644 --- a/src/web/CMakeDeps.cmake +++ b/src/web/CMakeDeps.cmake @@ -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/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) \ No newline at end of file +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) \ No newline at end of file From c97e7675fde7143c521b52ae09b11e4bff0cb592 Mon Sep 17 00:00:00 2001 From: Pedro Pinto Date: Tue, 28 Aug 2018 13:20:56 +0000 Subject: [PATCH 003/203] Testing SDK --- src/sdk/example/example.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/sdk/example/example.cpp b/src/sdk/example/example.cpp index ad7905e35..7e372bbd8 100644 --- a/src/sdk/example/example.cpp +++ b/src/sdk/example/example.cpp @@ -4,10 +4,26 @@ #include "spark/lib/md/MDProducer.h" #include "spark/lib/utils/PBUtils.h" #include "spark/lib/tracing/bts_trace_c.c" +#include "spark/lib/md/cme/MDP3Utils.h" using namespace bts; using namespace bts::md; - + +void crash_test() +{ + auto configuredChannels = get_configured_cme_channels(); + + std::cout << "crash_test\n"; + for (auto &configuredChannel : configuredChannels) + { + LOG(md, Info, "configuredChannel: {}", configuredChannel.id); + for(auto &symbol : configuredChannel.symbols) + { + LOG(md, Info, " symbol: {}", symbol.to_string()); + } + } +} + void produce() { md::MDProducer producer(co2::PLATFORM_SIM); @@ -76,6 +92,8 @@ int main() // this is needed before using any part of the api utils::init_spark_lib(); + crash_test(); + // this is how we log, the first arg is the subsystem, md in your // case the second is the log level: Diag, Info, Warn, Error, Fatal // the third argument is a format string, followed by arguments From 0695ddc33ece93fda0096a44e333b455fa6d0a16 Mon Sep 17 00:00:00 2001 From: Pedro Pinto Date: Tue, 28 Aug 2018 13:24:08 +0000 Subject: [PATCH 004/203] Moved subrepo ptr --- docker/linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/linux b/docker/linux index ded39afeb..aedfd79e0 160000 --- a/docker/linux +++ b/docker/linux @@ -1 +1 @@ -Subproject commit ded39afeb93a95ac6d63f1e9ffe4d934805b4e70 +Subproject commit aedfd79e025ddfff2af16f9366776f0822b1fc0e From d5716424541c986d8339408ee5157443f66f602a Mon Sep 17 00:00:00 2001 From: Pedro Pinto Date: Tue, 28 Aug 2018 10:21:16 -0400 Subject: [PATCH 005/203] Removed test code from SDK example --- src/sdk/example/example.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/sdk/example/example.cpp b/src/sdk/example/example.cpp index 7e372bbd8..817869e1b 100644 --- a/src/sdk/example/example.cpp +++ b/src/sdk/example/example.cpp @@ -9,21 +9,6 @@ using namespace bts; using namespace bts::md; -void crash_test() -{ - auto configuredChannels = get_configured_cme_channels(); - - std::cout << "crash_test\n"; - for (auto &configuredChannel : configuredChannels) - { - LOG(md, Info, "configuredChannel: {}", configuredChannel.id); - for(auto &symbol : configuredChannel.symbols) - { - LOG(md, Info, " symbol: {}", symbol.to_string()); - } - } -} - void produce() { md::MDProducer producer(co2::PLATFORM_SIM); @@ -92,8 +77,6 @@ int main() // this is needed before using any part of the api utils::init_spark_lib(); - crash_test(); - // this is how we log, the first arg is the subsystem, md in your // case the second is the log level: Diag, Info, Warn, Error, Fatal // the third argument is a format string, followed by arguments From 5df90f27c8e3b3dc1eb3d7d105ed5f3859dd1e97 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 29 Aug 2018 10:43:53 -0400 Subject: [PATCH 006/203] Update onixs license --- src/data_files/files/Onixs.BTS.lic | 8 +++---- .../mdp3_onixs/licenses/Onixs.BTS.lic | 21 ------------------- 2 files changed, 4 insertions(+), 25 deletions(-) delete mode 100644 src/server/sparkd/md/platforms/mdp3_onixs/licenses/Onixs.BTS.lic diff --git a/src/data_files/files/Onixs.BTS.lic b/src/data_files/files/Onixs.BTS.lic index b91537e9d..be997e282 100644 --- a/src/data_files/files/Onixs.BTS.lic +++ b/src/data_files/files/Onixs.BTS.lic @@ -4,18 +4,18 @@ Blue Trading Systems - 2017-04-24 - 2018-08-29 + 2018-08-29 + 2019-08-28 - jqN4XONnMeiDSJIAa6CK9m9SitquhMq3MhJW97fG26Fp+GPog9ObMLS2elPvippdZsRWmJGsjTmFxAg8d41Vr+tpNgObi3VT3DtfYjGESTmi+u4vvJ9PNi8tQWQ7HPYBJe1X6j84ijhe5e1jHEcaXZ08ExDbESebn2ndHb4Phv4= + eidnzkv57m24dLHIYmL11TfzfAGSd4kzF/t0ZmTYpB660EAZwjvQnPkmY+UaCdOHbSqaLbsJDH+cnv1AtU4XRO867NpDgE3n8VjVsbPnkr6PFnbASZaue2u9tjDJzLvPV79mIKCSyBCXZNNy2hm0rAfV/D3yfEpGQkQ+ELKp9Qc= - F5502F3BD9CE954022ABCBF94A7E4052 + 4D85FA4C77A7A4D28A38F22BED3E4244 \ No newline at end of file diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/licenses/Onixs.BTS.lic b/src/server/sparkd/md/platforms/mdp3_onixs/licenses/Onixs.BTS.lic deleted file mode 100644 index b91537e9d..000000000 --- a/src/server/sparkd/md/platforms/mdp3_onixs/licenses/Onixs.BTS.lic +++ /dev/null @@ -1,21 +0,0 @@ - - production - - Blue Trading Systems - - - 2017-04-24 - 2018-08-29 - - - - - jqN4XONnMeiDSJIAa6CK9m9SitquhMq3MhJW97fG26Fp+GPog9ObMLS2elPvippdZsRWmJGsjTmFxAg8d41Vr+tpNgObi3VT3DtfYjGESTmi+u4vvJ9PNi8tQWQ7HPYBJe1X6j84ijhe5e1jHEcaXZ08ExDbESebn2ndHb4Phv4= - - - - - - - F5502F3BD9CE954022ABCBF94A7E4052 - \ No newline at end of file From bc6e7928556e6054c025a411ce7f4a09d1f303e0 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Sat, 1 Sep 2018 21:25:20 -0400 Subject: [PATCH 007/203] SPARK-1684 ladder/ticket - infrastructure for order definition templates --- .../spark/Orders/OrderPropertiesEditorVM.cs | 63 +++++++++++++++++-- .../spark/Orders/OrderTypeVMs/CoverOrderVM.cs | 42 ++++++------- .../Orders/OrderTypeVMs/DynamicOrderVM.cs | 26 ++++---- .../spark/Orders/OrderTypeVMs/IOrderTypeVM.cs | 1 + .../Orders/OrderTypeVMs/IcebergOrderVM.cs | 28 ++++----- .../spark/Orders/OrderTypeVMs/JoinOrderVM.cs | 44 ++++++------- .../spark/Orders/OrderTypeVMs/LeanOrderVM.cs | 26 ++++---- .../spark/Orders/OrderTypeVMs/LimitOrderVM.cs | 2 +- .../Orders/OrderTypeVMs/MarketOrderVM.cs | 2 +- .../Orders/OrderTypeVMs/OrderTypeVMFactory.cs | 61 +++++++++--------- .../Orders/OrderTypeVMs/SpreaderOrderVM.cs | 8 +-- .../Orders/OrderTypeVMs/StopLimitOrderVM.cs | 7 ++- .../spark/Orders/OrderTypeVMs/StopOrderVM.cs | 3 +- .../spark/Orders/OrderTypeVMs/TickOrderVM.cs | 26 ++++---- .../Orders/OrderTypeVMs/TimeBoxOrderVM.cs | 28 ++++----- .../spark/Orders/OrderTypeVMs/TopOrderVM.cs | 44 ++++++------- .../Orders/OrderTypeVMs/TriggerOrderVM.cs | 27 ++++---- .../Order/OrderDetailsPopupControl.xaml | 2 +- .../view/Controls/Order/OrderTypeItem.xaml | 2 +- .../view/Controls/Order/OrderTypeItem.xaml.cs | 40 +++++++++--- .../view/Spark/Utilities/OrderDataHelper.cs | 29 +++++++++ 21 files changed, 312 insertions(+), 199 deletions(-) diff --git a/src/client/spark/Orders/OrderPropertiesEditorVM.cs b/src/client/spark/Orders/OrderPropertiesEditorVM.cs index 3b225cdd4..a0826b756 100644 --- a/src/client/spark/Orders/OrderPropertiesEditorVM.cs +++ b/src/client/spark/Orders/OrderPropertiesEditorVM.cs @@ -1,6 +1,7 @@ using bts; using bts.api; using bts.api.orderman; +using bts.utils; using mbts.ui; using mbts.ui.shell; using System; @@ -11,6 +12,53 @@ using System.Linq; using System.Reactive.Linq; namespace mbts.spark { + struct OrderTemplateKey : IEquatable { + public readonly CustomOrderDefinition.CustomOneofCase type; + public readonly string name; + + public OrderTemplateKey(CustomOrderDefinition.CustomOneofCase type) + : this(type, OrderUtils.order_type_display_name(type)) { } + + public OrderTemplateKey(CustomOrderDefinition.CustomOneofCase type, string name) { + this.type = type; + this.name = name; + } + + public bool Equals(OrderTemplateKey other) => + type == other.type && + name == other.name; + + public override bool Equals(object obj) => + obj is OrderTemplateKey other && Equals(other); + + public override int GetHashCode() => + EqualityComparer.aggregate_hash( + type.GetHashCode(), + name == null ? 0 : name.GetHashCode()); + } + + struct OrderTemplateHeader : IEquatable { + public readonly OrderTemplateKey key; + // Note: display_name is NOT part of EQUALITY + public readonly string display_name; + + public OrderTemplateHeader(CustomOrderDefinition.CustomOneofCase type) + : this(new OrderTemplateKey(type), OrderUtils.order_type_display_name(type)) { } + + public OrderTemplateHeader(OrderTemplateKey key, string display_name) { + this.key = key; + this.display_name = display_name; + } + + public bool Equals(OrderTemplateHeader other) => + key.Equals(other.key); + + public override bool Equals(object obj) => + obj is OrderTemplateHeader other && Equals(other); + + public override int GetHashCode() => key.GetHashCode(); + } + class InlineOrderProperties { public readonly InlineUserProperty account; public readonly InlineUserProperty time_in_force; @@ -62,10 +110,10 @@ namespace mbts.spark { val => (!string.IsNullOrWhiteSpace(val.name) && accounts.Contains(val), "Please select an account")); _order_type = backing( nameof(selected_order_type), - security_source.Select(s => model.factory.default_order_type_defs(s).Select(create_order_type_vm)), + security_source.Select(s => model.factory.default_order_type_templates(s).Select(kv => create_order_type_vm(kv.Value, kv.Key.key.name, kv.Key.display_name))), order_properties.Select(get_order_type_vm_prop), get_default_order_type_vm, - (a, b) => a.order_type == b.order_type); + (a, b) => a.id == b.id); _order_type.Changed += on_selected_order_type_changed; _time_in_force = backing( nameof(selected_time_in_force), @@ -77,19 +125,22 @@ namespace mbts.spark { tif => time_in_forces.Contains(tif)); _dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed); // local methods - IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) { + IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd, string id, string display_name) { IOrderTypeVM vm = null; if(cd.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic) - vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source, cd.Dynamic.DynamicOrderType); + vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source, cd.Dynamic.DynamicOrderType); else - vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source); + vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source); vm.load_from(cd); return vm; } InlineUserProperty get_order_type_vm_prop(InlineOrderProperties p) { if (!p.order_type_def.has_value) return new InlineUserProperty(); - return new InlineUserProperty(create_order_type_vm(p.order_type_def.value), p.order_type_def.force_override); + return new InlineUserProperty( + create_order_type_vm(p.order_type_def.value, p.order_type_def.value.CustomCase.ToString(), + OrderUtils.order_type_display_name(p.order_type_def.value.CustomCase)), + p.order_type_def.force_override); } IOrderTypeVM get_default_order_type_vm() { var ot = model.factory.default_order_type(security); diff --git a/src/client/spark/Orders/OrderTypeVMs/CoverOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/CoverOrderVM.cs index 89ee3f5e8..0192876f5 100644 --- a/src/client/spark/Orders/OrderTypeVMs/CoverOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/CoverOrderVM.cs @@ -17,22 +17,22 @@ namespace mbts.spark { class InlineCoverOrderProperties { - public InlineCoverOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) + public InlineCoverOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); var hedgetype = CustomOneofCase.Limit; - this.selected_hedge_order_type = new InlineUserProperty(hedgetype, true); - this.hedge_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_hedge_order_type = new InlineUserProperty(new OrderTemplateHeader(hedgetype), true); + this.hedge_order_types = new InlineUserProperty>(child_order_types, true); hedge_ticks = new InlineUserProperty(1, false); } - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; - public readonly InlineUserProperty selected_hedge_order_type; - public readonly InlineUserProperty> hedge_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_hedge_order_type; + public readonly InlineUserProperty> hedge_order_types; public readonly InlineUserProperty hedge_ticks; @@ -42,25 +42,25 @@ namespace mbts.spark { readonly IObservable _security_md_source; SecurityImmutable? _security; - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineCoverOrderProperties _inline_props; IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; - ObservableAutoWrapper _hedge_order_choices; + ObservableAutoWrapper _hedge_order_choices; IOrderTypeVM _hedge_order; IDisposable _hedge_order_errors_sub; public int hedge_ticks { get; set; } = 1; - public CoverOrderVM(OrdersModel model, IObservable security_md_source) + public CoverOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) : this(model, security_md_source, security_md_source.Select(smd => - new InlineCoverOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) + new InlineCoverOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } public CoverOrderVM(OrdersModel model, IObservable security_md_source, - IObservable inline_properties) : base(model) + IObservable inline_properties, string id, string display_name) : base(model, id, display_name) { _security_md_source = security_md_source; _dispose = Disposable.Create( @@ -75,11 +75,11 @@ namespace mbts.spark } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override)) { - _select_hedge_order(p.selected_hedge_order_type.value); + _select_hedge_order(p.selected_hedge_order_type.value.key.type); } if (p.hedge_ticks.force_override) { @@ -116,7 +116,7 @@ namespace mbts.spark get { if (_child_order_choices == null) - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _child_order_choices; } } @@ -126,7 +126,7 @@ namespace mbts.spark get { if (_hedge_order_choices == null) - _hedge_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _hedge_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _hedge_order_choices; } } @@ -200,7 +200,7 @@ namespace mbts.spark void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } @@ -216,7 +216,7 @@ namespace mbts.spark void _select_hedge_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ? - _inline_props.selected_hedge_order_type.value : + _inline_props.selected_hedge_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_hedge_order(ot); } diff --git a/src/client/spark/Orders/OrderTypeVMs/DynamicOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/DynamicOrderVM.cs index eaa302c86..0ef39f658 100644 --- a/src/client/spark/Orders/OrderTypeVMs/DynamicOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/DynamicOrderVM.cs @@ -53,14 +53,14 @@ namespace mbts.spark { class InlineDynamicOrderProperties { - public InlineDynamicOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) + public InlineDynamicOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); } - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; } class DynamicOrderVM : OrderTypeVMBase, IHierarchicalOrderTypeVM, IDisposable @@ -68,8 +68,8 @@ namespace mbts.spark readonly IObservable _security_md_source; SecurityImmutable? _security; - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineDynamicOrderProperties _inline_props; IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; @@ -77,13 +77,13 @@ namespace mbts.spark string _dyn_type; List _parameters; - public DynamicOrderVM(OrdersModel model, IObservable security_md_source, string dyn_type) + public DynamicOrderVM(OrdersModel model, IObservable security_md_source, string dyn_type, string id, string display_name) : this(model, security_md_source, dyn_type, security_md_source.Select(smd => - new InlineDynamicOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) + new InlineDynamicOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } public DynamicOrderVM(OrdersModel model, IObservable security_md_source, string dyn_type, - IObservable inline_properties) : base(model) + IObservable inline_properties, string id, string display_name) : base(model, id, display_name) { _security_md_source = security_md_source; _dyn_type = dyn_type; @@ -100,7 +100,7 @@ namespace mbts.spark } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } _inline_props = p; _validate(); @@ -139,7 +139,7 @@ namespace mbts.spark get { if (_child_order_choices == null) - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _child_order_choices; } } @@ -185,7 +185,7 @@ namespace mbts.spark void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } diff --git a/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs b/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs index 3f7fc9c33..3209d2172 100644 --- a/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs @@ -6,6 +6,7 @@ using static bts.api.orderman.CustomOrderDefinition; namespace mbts.spark { interface IOrderTypeVM : IPropertyWrapperVM { + string id { get; } CustomOneofCase order_type { get; } string type_name { get; } bool is_bts_type { get; } diff --git a/src/client/spark/Orders/OrderTypeVMs/IcebergOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/IcebergOrderVM.cs index cd844c9c6..31c9a3d11 100644 --- a/src/client/spark/Orders/OrderTypeVMs/IcebergOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/IcebergOrderVM.cs @@ -14,34 +14,34 @@ using static bts.api.orderman.CustomOrderDefinition; namespace mbts.spark { static partial class OrderTypeVMFactory { class InlineIcebergOrderProperties { - public InlineIcebergOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) { + public InlineIcebergOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); show_quantity = new InlineUserProperty(1, true); } - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; public readonly InlineUserProperty show_quantity; } class IcebergOrderVM : OrderTypeVMBase, IHierarchicalOrderTypeVM, IDisposable { readonly IObservable _security_md_source; SecurityImmutable? _security; - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineIcebergOrderProperties _inline_props; IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; - public IcebergOrderVM(OrdersModel model, IObservable security_md_source) + public IcebergOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) : this(model, security_md_source, security_md_source.Select(smd => - new InlineIcebergOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { } + new InlineIcebergOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } public IcebergOrderVM(OrdersModel model, IObservable security_md_source, - IObservable inline_properties) - : base(model) { + IObservable inline_properties, string id, string display_name) + : base(model, id, display_name) { _security_md_source = security_md_source; _dispose = Disposable.Create( security_md_source.OnDispatcher() @@ -52,7 +52,7 @@ namespace mbts.spark { _order_types.SetItems(p.child_order_types.value); } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } if (p.show_quantity.force_override) { show_quantity = p.show_quantity.value; @@ -68,7 +68,7 @@ namespace mbts.spark { public ICollection child_order_types { get { if (_child_order_choices == null) { - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); } return _child_order_choices; } @@ -122,7 +122,7 @@ namespace mbts.spark { void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } diff --git a/src/client/spark/Orders/OrderTypeVMs/JoinOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/JoinOrderVM.cs index 26ac1583c..ae8ce4cd7 100644 --- a/src/client/spark/Orders/OrderTypeVMs/JoinOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/JoinOrderVM.cs @@ -17,19 +17,19 @@ namespace mbts.spark { class InlineJoinOrderProperties { - public InlineJoinOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) + public InlineJoinOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); var hedgetype = CustomOneofCase.Lean; - if(!child_order_types.Contains(hedgetype)) + if(!child_order_types.Any(t => t.key.type == hedgetype)) hedgetype = CustomOneofCase.Limit; //ExceptionAssert.InvalidOperation.That(child_order_types.Contains(hedgetype), $"Hedge order type {hedgetype} is not in hedge_order_types"); - this.selected_hedge_order_type = new InlineUserProperty(hedgetype, true); - this.hedge_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_hedge_order_type = new InlineUserProperty(new OrderTemplateHeader(hedgetype), true); + this.hedge_order_types = new InlineUserProperty>(child_order_types, true); tick_offset_on_deploy = new InlineUserProperty(0, false); join_threshold = new InlineUserProperty( @@ -37,10 +37,10 @@ namespace mbts.spark enable_hedge = new InlineUserProperty(false, false); hedge_ticks = new InlineUserProperty(1, false); } - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; - public readonly InlineUserProperty selected_hedge_order_type; - public readonly InlineUserProperty> hedge_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_hedge_order_type; + public readonly InlineUserProperty> hedge_order_types; public readonly InlineUserProperty tick_offset_on_deploy; @@ -55,13 +55,13 @@ namespace mbts.spark { readonly IObservable _security_md_source; SecurityImmutable? _security; - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineJoinOrderProperties _inline_props; IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; - ObservableAutoWrapper _hedge_order_choices; + ObservableAutoWrapper _hedge_order_choices; IOrderTypeVM _hedge_order; IDisposable _hedge_order_errors_sub; @@ -79,12 +79,12 @@ namespace mbts.spark } public int hedge_ticks { get; set; } = 1; - public JoinOrderVM(OrdersModel model, IObservable security_md_source) + public JoinOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) : this(model, security_md_source, security_md_source.Select(smd => - new InlineJoinOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { } + new InlineJoinOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } public JoinOrderVM(OrdersModel model, IObservable security_md_source, - IObservable inline_properties) : base(model) + IObservable inline_properties, string id, string display_name) : base(model, id, display_name) { _security_md_source = security_md_source; _dispose = Disposable.Create( @@ -98,11 +98,11 @@ namespace mbts.spark } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override)) { - _select_hedge_order(p.selected_hedge_order_type.value); + _select_hedge_order(p.selected_hedge_order_type.value.key.type); } if (p.tick_offset_on_deploy.force_override) { @@ -157,7 +157,7 @@ namespace mbts.spark get { if (_child_order_choices == null) - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _child_order_choices; } } @@ -167,7 +167,7 @@ namespace mbts.spark get { if (_hedge_order_choices == null) - _hedge_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _hedge_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _hedge_order_choices; } } @@ -241,7 +241,7 @@ namespace mbts.spark void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } @@ -257,7 +257,7 @@ namespace mbts.spark void _select_hedge_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ? - _inline_props.selected_hedge_order_type.value : + _inline_props.selected_hedge_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_hedge_order(ot); } diff --git a/src/client/spark/Orders/OrderTypeVMs/LeanOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/LeanOrderVM.cs index daa526f83..9b42c6e4f 100644 --- a/src/client/spark/Orders/OrderTypeVMs/LeanOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/LeanOrderVM.cs @@ -17,17 +17,17 @@ namespace mbts.spark { class InlineLeanOrderProperties { - public InlineLeanOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) + public InlineLeanOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); cross_threshold = new InlineUserProperty( new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false); max_chase_ticks = new InlineUserProperty(10, false); } - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; public readonly InlineUserProperty cross_threshold; public readonly InlineUserProperty max_chase_ticks; @@ -37,8 +37,8 @@ namespace mbts.spark { readonly IObservable _security_md_source; SecurityImmutable? _security; - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineLeanOrderProperties _inline_props; IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; @@ -46,12 +46,12 @@ namespace mbts.spark public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 }; public uint max_chase_ticks { get; set; } = 10; - public LeanOrderVM(OrdersModel model, IObservable security_md_source) + public LeanOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) : this(model, security_md_source, security_md_source.Select(smd => - new InlineLeanOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { } + new InlineLeanOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } public LeanOrderVM(OrdersModel model, IObservable security_md_source, - IObservable inline_properties) : base(model) + IObservable inline_properties, string id, string display_name) : base(model, id, display_name) { _security_md_source = security_md_source; _dispose = Disposable.Create( @@ -65,7 +65,7 @@ namespace mbts.spark } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } if (p.cross_threshold.force_override) { @@ -104,7 +104,7 @@ namespace mbts.spark get { if (_child_order_choices == null) - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _child_order_choices; } } @@ -150,7 +150,7 @@ namespace mbts.spark void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } diff --git a/src/client/spark/Orders/OrderTypeVMs/LimitOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/LimitOrderVM.cs index 80bf27d75..c9ce62236 100644 --- a/src/client/spark/Orders/OrderTypeVMs/LimitOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/LimitOrderVM.cs @@ -3,7 +3,7 @@ namespace mbts.spark { static partial class OrderTypeVMFactory { private class LimitOrderVM : OrderTypeVMBase { - internal LimitOrderVM(OrdersModel model) : base(model) { } + internal LimitOrderVM(OrdersModel model) : base(model, "Limit", OrderUtils.order_type_display_name(CustomOrderDefinition.CustomOneofCase.Limit)) { } public override void assign_to(CustomOrderDefinition value) => value.Limit = factory.limit_order(); diff --git a/src/client/spark/Orders/OrderTypeVMs/MarketOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/MarketOrderVM.cs index ee0fb8422..dbefb3206 100644 --- a/src/client/spark/Orders/OrderTypeVMs/MarketOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/MarketOrderVM.cs @@ -3,7 +3,7 @@ namespace mbts.spark { static partial class OrderTypeVMFactory { private class MarketOrderVM : OrderTypeVMBase { - internal MarketOrderVM(OrdersModel model) : base(model) { } + internal MarketOrderVM(OrdersModel model) : base(model, "Market", OrderUtils.order_type_display_name(CustomOrderDefinition.CustomOneofCase.Market)) { } public override void assign_to(CustomOrderDefinition parent) => parent.Market = factory.market_order(); diff --git a/src/client/spark/Orders/OrderTypeVMs/OrderTypeVMFactory.cs b/src/client/spark/Orders/OrderTypeVMs/OrderTypeVMFactory.cs index e9efc6e27..8eb6a0af5 100644 --- a/src/client/spark/Orders/OrderTypeVMs/OrderTypeVMFactory.cs +++ b/src/client/spark/Orders/OrderTypeVMs/OrderTypeVMFactory.cs @@ -12,36 +12,36 @@ namespace mbts.spark { static partial class OrderTypeVMFactory { public static IOrderTypeVM market(OrdersModel model) => new MarketOrderVM(model); public static IOrderTypeVM limit(OrdersModel model) => new LimitOrderVM(model); - public static IOrderTypeVM stop(OrdersModel model) => new StopOrderVM(model); - public static IOrderTypeVM stop_limit(OrdersModel model, IObservable security_md_source) => new StopLimitOrderVM(model, security_md_source); - public static IOrderTypeVM time_box(OrdersModel model, IObservable security_md_source) => new TimeBoxOrderVM(model, security_md_source); - public static IOrderTypeVM tick(OrdersModel model, IObservable security_md_source) => new TickOrderVM(model, security_md_source); - public static IOrderTypeVM lean(OrdersModel model, IObservable security_md_source) => new LeanOrderVM(model, security_md_source); - public static IOrderTypeVM join(OrdersModel model, IObservable security_md_source) => new JoinOrderVM(model, security_md_source); - public static IOrderTypeVM top(OrdersModel model, IObservable security_md_source) => new TopOrderVM(model, security_md_source); - public static IOrderTypeVM cover(OrdersModel model, IObservable security_md_source) => new CoverOrderVM(model, security_md_source); - public static IOrderTypeVM trigger(OrdersModel model, IObservable security_md_source) => new TriggerOrderVM(model, security_md_source); - public static IOrderTypeVM iceberg(OrdersModel model, IObservable security_md_source) => new IcebergOrderVM(model, security_md_source); - public static IOrderTypeVM spreader(OrdersModel model, IObservable security_source) => new SpreaderOrderVM(model, security_source); - public static IOrderTypeVM dynamic(OrdersModel model, IObservable security_md_source, string type) => new DynamicOrderVM(model, security_md_source, type); + public static IOrderTypeVM stop(OrdersModel model, string id, string display_name) => new StopOrderVM(model, id, display_name); + public static IOrderTypeVM stop_limit(OrdersModel model, IObservable security_md_source, string id, string display_name) => new StopLimitOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM time_box(OrdersModel model, IObservable security_md_source, string id, string display_name) => new TimeBoxOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM tick(OrdersModel model, IObservable security_md_source, string id, string display_name) => new TickOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM lean(OrdersModel model, IObservable security_md_source, string id, string display_name) => new LeanOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM join(OrdersModel model, IObservable security_md_source, string id, string display_name) => new JoinOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM top(OrdersModel model, IObservable security_md_source, string id, string display_name) => new TopOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM cover(OrdersModel model, IObservable security_md_source, string id, string display_name) => new CoverOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM trigger(OrdersModel model, IObservable security_md_source, string id, string display_name) => new TriggerOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM iceberg(OrdersModel model, IObservable security_md_source, string id, string display_name) => new IcebergOrderVM(model, security_md_source, id, display_name); + public static IOrderTypeVM spreader(OrdersModel model, IObservable security_source, string id, string display_name) => new SpreaderOrderVM(model, security_source, id, display_name); + public static IOrderTypeVM dynamic(OrdersModel model, IObservable security_md_source, string type, string id, string display_name) => new DynamicOrderVM(model, security_md_source, type, null, id, display_name); - public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model, + public static IOrderTypeVM create(CustomOneofCase order_type, string id, string display_name, OrdersModel model, IObservable security_md_source, string dynamic_type = null ) { switch (order_type) { case CustomOneofCase.Market: return market(model); case CustomOneofCase.Limit: return limit(model); - case CustomOneofCase.Stop: return stop(model); - case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source); - case CustomOneofCase.TimeBox: return time_box(model, security_md_source); - case CustomOneofCase.Tick: return tick(model, security_md_source); - case CustomOneofCase.Lean: return lean(model, security_md_source); - case CustomOneofCase.Join: return join(model, security_md_source); - case CustomOneofCase.Top: return top(model, security_md_source); - case CustomOneofCase.Cover: return cover(model, security_md_source); - case CustomOneofCase.Trigger: return trigger(model, security_md_source); - case CustomOneofCase.Iceberg: return iceberg(model, security_md_source); - case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security)); - case CustomOneofCase.Dynamic: return dynamic(model, security_md_source, dynamic_type); + case CustomOneofCase.Stop: return stop(model, id, display_name); + case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source, id, display_name); + case CustomOneofCase.TimeBox: return time_box(model, security_md_source, id, display_name); + case CustomOneofCase.Tick: return tick(model, security_md_source, id, display_name); + case CustomOneofCase.Lean: return lean(model, security_md_source, id, display_name); + case CustomOneofCase.Join: return join(model, security_md_source, id, display_name); + case CustomOneofCase.Top: return top(model, security_md_source, id, display_name); + case CustomOneofCase.Cover: return cover(model, security_md_source, id, display_name); + case CustomOneofCase.Trigger: return trigger(model, security_md_source, id, display_name); + case CustomOneofCase.Iceberg: return iceberg(model, security_md_source, id, display_name); + case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security), id, display_name); + case CustomOneofCase.Dynamic: return dynamic(model, security_md_source, dynamic_type, id, display_name); default: throw new NotImplementedException(); } } @@ -51,12 +51,17 @@ namespace mbts.spark { protected readonly OrdersModel model; protected OrderFactory factory => model.factory; - protected OrderTypeVMBase(OrdersModel model) => this.model = model; + protected OrderTypeVMBase(OrdersModel model, string id, string display_name) { + this.model = model; + this.id = id; + this.display_name = display_name; + } public abstract CustomOneofCase order_type { get; } public abstract string type_name { get; } public bool is_bts_type => display_name.StartsWith("BTS"); - public string display_name => OrderUtils.order_type_display_name(order_type); + public string id { get; } + public string display_name { get; } public OrderMetadata meta => factory.get_metadata(order_type); public virtual bool has_editable_properties => !meta.NoProps; public virtual bool editable_properties_open { get; set; } @@ -76,7 +81,7 @@ namespace mbts.spark { } private abstract class OrderTypeVMBase : OrderTypeVMBase where T : Google.Protobuf.IMessage, new() { - protected OrderTypeVMBase(OrdersModel model) : base(model) { } + protected OrderTypeVMBase(OrdersModel model, string id, string display_name) : base(model, id, display_name) { } public override CustomOneofCase order_type => OrderDataHelper.get_order_type(new T()); diff --git a/src/client/spark/Orders/OrderTypeVMs/SpreaderOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/SpreaderOrderVM.cs index 40f43a9bd..7ac53dcc6 100644 --- a/src/client/spark/Orders/OrderTypeVMs/SpreaderOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/SpreaderOrderVM.cs @@ -13,20 +13,20 @@ namespace mbts.spark { readonly StrategyEditorWrapper _strategy_editor_wrapper; IDisposable _dispose; - public SpreaderOrderVM(OrdersModel model, IObservable security_source) + public SpreaderOrderVM(OrdersModel model, IObservable security_source, string id, string display_name) : this(model, security_source, security_source.SelectMany(s => model.spreader.security_strategy(s.Id) .Select(strategy => { var so = model.factory.spreader_order(); so.Security = s.CloneToMutable(); so.ExecutionStrategy = strategy.CloneToMutable(); return new InlineUserProperty(so, true); - }))) { } + })), id, display_name) { } public SpreaderOrderVM( OrdersModel model, IObservable security_source, - IObservable> order_template_source) - : base(model) { + IObservable> order_template_source, string id, string display_name) + : base(model, id, display_name) { _template = backing(nameof(template), new SpreaderOrderDefinitionImmutable?()); _strategy_editor_wrapper = new StrategyEditorWrapper(this); _dispose = Disposable.Create( diff --git a/src/client/spark/Orders/OrderTypeVMs/StopLimitOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/StopLimitOrderVM.cs index f6620cfa3..0af2b19ec 100644 --- a/src/client/spark/Orders/OrderTypeVMs/StopLimitOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/StopLimitOrderVM.cs @@ -29,10 +29,11 @@ namespace mbts.spark { InlineStopLimitOrderProperties _inline_props; IDisposable _dispose; - internal StopLimitOrderVM(OrdersModel model, IObservable security_md_source) - : this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source)) { } + internal StopLimitOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) + : this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source), id, display_name) { } - internal StopLimitOrderVM(OrdersModel model, IObservable spec_source, IObservable inline_properties) : base(model) { + internal StopLimitOrderVM(OrdersModel model, IObservable spec_source, IObservable inline_properties, string id, string display_name) + : base(model, id, display_name) { _stop_price = new UserProperty(backing(nameof(stop_price), double.NaN)); _dispose = Disposable.Create( spec_source.OnDispatcher().Subscribe(s => { diff --git a/src/client/spark/Orders/OrderTypeVMs/StopOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/StopOrderVM.cs index 47b333abc..3fc291428 100644 --- a/src/client/spark/Orders/OrderTypeVMs/StopOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/StopOrderVM.cs @@ -6,8 +6,7 @@ namespace mbts.spark { private class StopOrderVM : OrderTypeVMBase { double _stop_price = 0; - internal StopOrderVM(OrdersModel model) : base(model) { } - + internal StopOrderVM(OrdersModel model, string id, string display_name) : base(model, id, display_name) { } public double stop_price { get => _stop_price; diff --git a/src/client/spark/Orders/OrderTypeVMs/TickOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/TickOrderVM.cs index 3f9f62663..ee868ef2b 100644 --- a/src/client/spark/Orders/OrderTypeVMs/TickOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/TickOrderVM.cs @@ -17,11 +17,11 @@ namespace mbts.spark { class InlineTickOrderProperties { - public InlineTickOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) + public InlineTickOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); tick_offset_on_deploy = new InlineUserProperty(0, false); cross_threshold = new InlineUserProperty( new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false); @@ -29,8 +29,8 @@ namespace mbts.spark max_chase_ticks = new InlineUserProperty(10, false); detach_on_toward = new InlineUserProperty(true, false); } - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; public readonly InlineUserProperty tick_offset_on_deploy; public readonly InlineUserProperty cross_threshold; @@ -43,8 +43,8 @@ namespace mbts.spark { readonly IObservable _security_md_source; SecurityImmutable? _security; - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineTickOrderProperties _inline_props; IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; @@ -55,12 +55,12 @@ namespace mbts.spark public uint max_chase_ticks { get; set; } = 10; public bool detach_on_toward { get; set; } = true; - public TickOrderVM(OrdersModel model, IObservable security_md_source) + public TickOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) : this(model, security_md_source, security_md_source.Select(smd => - new InlineTickOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { } + new InlineTickOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } public TickOrderVM(OrdersModel model, IObservable security_md_source, - IObservable inline_properties) : base(model) + IObservable inline_properties, string id, string display_name) : base(model, id, display_name) { _security_md_source = security_md_source; _dispose = Disposable.Create( @@ -74,7 +74,7 @@ namespace mbts.spark } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } if (p.tick_offset_on_deploy.force_override) { @@ -131,7 +131,7 @@ namespace mbts.spark get { if (_child_order_choices == null) - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _child_order_choices; } } @@ -177,7 +177,7 @@ namespace mbts.spark void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } diff --git a/src/client/spark/Orders/OrderTypeVMs/TimeBoxOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/TimeBoxOrderVM.cs index cd3f058cc..d8410c1a5 100644 --- a/src/client/spark/Orders/OrderTypeVMs/TimeBoxOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/TimeBoxOrderVM.cs @@ -14,15 +14,15 @@ using static bts.api.orderman.CustomOrderDefinition; namespace mbts.spark { static partial class OrderTypeVMFactory { class InlineTimeBoxOrderProperties { - public InlineTimeBoxOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) { + public InlineTimeBoxOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); start = new InlineUserProperty(DateTime.Now, true); stop = new InlineUserProperty(DateTime.Now.AddMinutes(5), true); } - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; public readonly InlineUserProperty start, stop; } @@ -30,8 +30,8 @@ namespace mbts.spark { const double _start_immediately_delay_ms = 500; readonly IObservable _security_md_source; SecurityImmutable? _security; - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineTimeBoxOrderProperties _inline_props; TimeTextTuple _absolute_start = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(5)), _absolute_stop = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(10)); TimeSpan? _relative_start = TimeSpan.FromMinutes(5), _relative_stop = TimeSpan.FromMinutes(5); @@ -40,14 +40,14 @@ namespace mbts.spark { IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; - public TimeBoxOrderVM(OrdersModel model, IObservable security_md_source) + public TimeBoxOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) : this(model, security_md_source, security_md_source.Select(smd => - new InlineTimeBoxOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { } + new InlineTimeBoxOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } public TimeBoxOrderVM(OrdersModel model, IObservable security_md_source, - IObservable inline_properties) - : base(model) { + IObservable inline_properties, string id, string display_name) + : base(model, id, display_name) { _security_md_source = security_md_source; _dispose = Disposable.Create( security_md_source.OnDispatcher() @@ -58,7 +58,7 @@ namespace mbts.spark { _order_types.SetItems(p.child_order_types.value); } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } if (p.start.force_override) { absolute_start = TimeTextTuple.FromDateTime(p.start.value); @@ -94,7 +94,7 @@ namespace mbts.spark { public ICollection child_order_types { get { if (_child_order_choices == null) { - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); } return _child_order_choices; } @@ -173,7 +173,7 @@ namespace mbts.spark { void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } diff --git a/src/client/spark/Orders/OrderTypeVMs/TopOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/TopOrderVM.cs index 1a315a130..afc2279a0 100644 --- a/src/client/spark/Orders/OrderTypeVMs/TopOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/TopOrderVM.cs @@ -17,17 +17,17 @@ namespace mbts.spark { class InlineTopOrderProperties { - public InlineTopOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) + public InlineTopOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); var hedgetype = CustomOneofCase.Lean; - if(!child_order_types.Contains(hedgetype)) + if(!child_order_types.Any(ct => ct.key.type == hedgetype)) hedgetype = CustomOneofCase.Limit; - this.selected_hedge_order_type = new InlineUserProperty(hedgetype, true); - this.hedge_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_hedge_order_type = new InlineUserProperty(new OrderTemplateHeader(hedgetype), true); + this.hedge_order_types = new InlineUserProperty>(child_order_types, true); watch_level_tick_offset = new InlineUserProperty(0, false); top_threshold = new InlineUserProperty( @@ -35,10 +35,10 @@ namespace mbts.spark enable_hedge = new InlineUserProperty(false, false); hedge_ticks = new InlineUserProperty(1, false); } - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; - public readonly InlineUserProperty selected_hedge_order_type; - public readonly InlineUserProperty> hedge_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_hedge_order_type; + public readonly InlineUserProperty> hedge_order_types; public readonly InlineUserProperty watch_level_tick_offset; @@ -53,13 +53,13 @@ namespace mbts.spark { readonly IObservable _security_md_source; SecurityImmutable? _security; - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineTopOrderProperties _inline_props; IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; - ObservableAutoWrapper _hedge_order_choices; + ObservableAutoWrapper _hedge_order_choices; IOrderTypeVM _hedge_order; IDisposable _hedge_order_errors_sub; @@ -77,13 +77,13 @@ namespace mbts.spark } public int hedge_ticks { get; set; } = 1; - public TopOrderVM(OrdersModel model, IObservable security_md_source) + public TopOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) : this(model, security_md_source, security_md_source.Select(smd => - new InlineTopOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) + new InlineTopOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } public TopOrderVM(OrdersModel model, IObservable security_md_source, - IObservable inline_properties) : base(model) + IObservable inline_properties, string id, string display_name) : base(model, id, display_name) { _security_md_source = security_md_source; _dispose = Disposable.Create( @@ -98,11 +98,11 @@ namespace mbts.spark } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override)) { - _select_hedge_order(p.selected_hedge_order_type.value); + _select_hedge_order(p.selected_hedge_order_type.value.key.type); } if (p.watch_level_tick_offset.force_override) { @@ -157,7 +157,7 @@ namespace mbts.spark get { if (_child_order_choices == null) - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _child_order_choices; } } @@ -167,7 +167,7 @@ namespace mbts.spark get { if (_hedge_order_choices == null) - _hedge_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _hedge_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _hedge_order_choices; } } @@ -241,7 +241,7 @@ namespace mbts.spark void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } @@ -257,7 +257,7 @@ namespace mbts.spark void _select_hedge_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ? - _inline_props.selected_hedge_order_type.value : + _inline_props.selected_hedge_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_hedge_order(ot); } diff --git a/src/client/spark/Orders/OrderTypeVMs/TriggerOrderVM.cs b/src/client/spark/Orders/OrderTypeVMs/TriggerOrderVM.cs index 9bd4a24a2..23b12fddc 100644 --- a/src/client/spark/Orders/OrderTypeVMs/TriggerOrderVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/TriggerOrderVM.cs @@ -14,8 +14,8 @@ using static bts.api.orderman.CustomOrderDefinition; namespace mbts.spark { static partial class OrderTypeVMFactory { class InlineTriggerOrderProperties { - public readonly InlineUserProperty selected_child_order_type; - public readonly InlineUserProperty> child_order_types; + public readonly InlineUserProperty selected_child_order_type; + public readonly InlineUserProperty> child_order_types; public readonly InlineUserProperty trigger_price_type; public readonly InlineUserProperty payup_price_type; public readonly InlineUserProperty fixed_price; @@ -23,10 +23,10 @@ namespace mbts.spark { public readonly InlineUserProperty use_fixed_price, is_priceout_checked, is_timeout_checked; public readonly InlineUserProperty timeout_secs; - public InlineTriggerOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection child_order_types) { + public InlineTriggerOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection child_order_types) { ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types"); - this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); - this.child_order_types = new InlineUserProperty>(child_order_types, true); + this.selected_child_order_type = new InlineUserProperty(selected_child_order_type, true); + this.child_order_types = new InlineUserProperty>(child_order_types, true); trigger_price_type = new InlineUserProperty(TriggerPriceType.Bid, false); payup_price_type = new InlineUserProperty(); use_fixed_price = new InlineUserProperty(false); @@ -47,19 +47,20 @@ namespace mbts.spark { readonly UserProperty _use_fixed_price, _is_timeout_checked, _is_priceout_checked; SecurityMD? _security_md; SecurityImmutable? _security => _security_md.security(); - ObservableCollection _order_types = new ObservableCollection(); - ObservableAutoWrapper _child_order_choices; + ObservableCollection _order_types = new ObservableCollection(); + ObservableAutoWrapper _child_order_choices; InlineTriggerOrderProperties _inline_props; IOrderTypeVM _child_order; IDisposable _dispose, _child_order_errors_sub; ulong _bbo_ref = 0, _last_trade_ref = 0; double _best_bid = double.NaN, _best_ask = double.NaN, _last_trade_price = double.NaN; - public TriggerOrderVM(OrdersModel model, IObservable security_md_source) + public TriggerOrderVM(OrdersModel model, IObservable security_md_source, string id, string display_name) : this(model, security_md_source, security_md_source.Select(smd => - new InlineTriggerOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { } + new InlineTriggerOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { } - public TriggerOrderVM(OrdersModel model, IObservable security_md_source, IObservable inline_properties) : base(model) { + public TriggerOrderVM(OrdersModel model, IObservable security_md_source, IObservable inline_properties, string id, string display_name) + : base(model, id, display_name) { _trigger_price_type = create_user_prop(nameof(trigger_price_type), TriggerPriceType.Bid); _use_fixed_price = create_user_prop(nameof(use_fixed_price), true); _fixed_price = create_user_prop(nameof(fixed_price), double.NaN); @@ -88,7 +89,7 @@ namespace mbts.spark { _order_types.SetItems(p.child_order_types.value); } if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) { - _select_child_order(p.selected_child_order_type.value); + _select_child_order(p.selected_child_order_type.value.key.type); } // trigger price _trigger_price_type.on_inline_prop(p.trigger_price_type); @@ -135,7 +136,7 @@ namespace mbts.spark { public ICollection child_order_types { get { if (_child_order_choices == null) - _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot, model, _security_md_source)); + _child_order_choices = new ObservableAutoWrapper(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source)); return _child_order_choices; } } @@ -216,7 +217,7 @@ namespace mbts.spark { void _select_child_order() { CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ? - _inline_props.selected_child_order_type.value : + _inline_props.selected_child_order_type.value.key.type : model.factory.default_order_type(_security.Value, true); _select_child_order(ot); } diff --git a/src/client/spark/view/Controls/Order/OrderDetailsPopupControl.xaml b/src/client/spark/view/Controls/Order/OrderDetailsPopupControl.xaml index 5eb3d4062..70ba8ce5c 100644 --- a/src/client/spark/view/Controls/Order/OrderDetailsPopupControl.xaml +++ b/src/client/spark/view/Controls/Order/OrderDetailsPopupControl.xaml @@ -47,7 +47,7 @@ - + diff --git a/src/client/spark/view/Controls/Order/OrderTypeItem.xaml b/src/client/spark/view/Controls/Order/OrderTypeItem.xaml index 2eafa6a61..c66a0b261 100644 --- a/src/client/spark/view/Controls/Order/OrderTypeItem.xaml +++ b/src/client/spark/view/Controls/Order/OrderTypeItem.xaml @@ -16,7 +16,7 @@ - + diff --git a/src/client/spark/view/Controls/Order/OrderTypeItem.xaml.cs b/src/client/spark/view/Controls/Order/OrderTypeItem.xaml.cs index 27ae3d858..92b62ad5f 100644 --- a/src/client/spark/view/Controls/Order/OrderTypeItem.xaml.cs +++ b/src/client/spark/view/Controls/Order/OrderTypeItem.xaml.cs @@ -3,23 +3,49 @@ using bts.spark; using System.Windows; using System.Windows.Controls; -namespace mbts.ui.spark -{ +namespace mbts.ui.spark { /// /// Interaction logic for OrderTypeItem.xaml /// - public partial class OrderTypeItem : Control - { - static OrderTypeItem() { + partial class OrderTypeItem : Control { + static OrderTypeItem() => DefaultStyleKeyProperty.OverrideMetadata(typeof(OrderTypeItem), new FrameworkPropertyMetadata(typeof(OrderTypeItem))); - } public static readonly DependencyProperty OrderTypeProperty = DependencyProperty.Register(nameof(OrderType), typeof(CustomOrderDefinition.CustomOneofCase), typeof(OrderTypeItem), new PropertyMetadata(CustomOrderDefinition.CustomOneofCase.None)); public CustomOrderDefinition.CustomOneofCase OrderType { get => (CustomOrderDefinition.CustomOneofCase)GetValue(OrderTypeProperty); set => SetValue(OrderTypeProperty, value); } + public static readonly DependencyProperty DisplayNameProperty = + WPFHelper.create_dp(nameof(DisplayName), (o, v) => { + o._display_name = v; + o._update_text(); + }); + + public string DisplayName { + get => GetValue(DisplayNameProperty) as string; + set => SetValue(DisplayNameProperty, value); + } + public static readonly DependencyProperty MetadataProperty = DependencyProperty.Register(nameof(Metadata), typeof(OrderMetadata), typeof(OrderTypeItem), new PropertyMetadata(new OrderMetadata { Color = 0, KeyBinding = string.Empty })); public OrderMetadata Metadata { get => GetValue(MetadataProperty) as OrderMetadata; set => SetValue(MetadataProperty, value); } + + string _display_name; + TextBlock _text; + + public override void OnApplyTemplate() { + this.try_get_template_child("text", out _text); + _update_text(); + } + + void _update_text() { + if (_text != null) { + if (string.IsNullOrEmpty(_display_name)) { + _text.ClearValue(TextBlock.TextProperty); + } else { + _text.SetCurrentValue(TextBlock.TextProperty, _display_name); + } + } + } } -} +} \ No newline at end of file diff --git a/src/client/spark/view/Spark/Utilities/OrderDataHelper.cs b/src/client/spark/view/Spark/Utilities/OrderDataHelper.cs index ec93f5205..735a9409e 100644 --- a/src/client/spark/view/Spark/Utilities/OrderDataHelper.cs +++ b/src/client/spark/view/Spark/Utilities/OrderDataHelper.cs @@ -110,6 +110,35 @@ namespace mbts.spark.utilities { return defs; } + public Dictionary default_order_type_templates(SecurityImmutable security, bool is_child = false) { + const int name_ref_seed = 1; + // standard types + var defs = default_order_types(security, is_child) + .Except(new[] { CustomOrderDefinition.CustomOneofCase.Dynamic }) + .ToDictionary(ot => new OrderTemplateHeader(new OrderTemplateKey(ot), OrderUtils.order_type_display_name(ot)), ot => new CustomOrderDefinitionImmutable(custom_order(ot))); + // TODO: replace with better naming scheme (right now, the order type ref count 'n' is appended to the order-type name) + var order_type_ref_count = defs.ToDictionary(kv => kv.Key, kv => name_ref_seed); + var dynamic_defs = new List(); + DynamicOrderHelper.add_dynamic_order_defs(dynamic_defs, ot => custom_order(ot)); + foreach (var ddef in dynamic_defs) { + var header = new OrderTemplateHeader(new OrderTemplateKey(ddef.CustomCase), OrderUtils.order_type_display_name(ddef.CustomCase)); + if (order_type_ref_count.TryGetValue(header, out int count)) { + order_type_ref_count[header]++; + header = new OrderTemplateHeader(new OrderTemplateKey(header.key.type, $"{header.key.name}{count}"), $"{header.display_name}{count}"); + } else { + order_type_ref_count[header] = name_ref_seed; + } + defs.Add(header, ddef); + } +#if DEBUG + var test_ot = CustomOrderDefinition.CustomOneofCase.Trigger; + for (int i = 1; i < 4; i++) { + defs.Add(new OrderTemplateHeader(new OrderTemplateKey(test_ot, $"{test_ot}{i}"), $"{test_ot}{i}"), new CustomOrderDefinitionImmutable(custom_order(test_ot))); + } +#endif + return defs; + } + public IEnumerable default_order_types(SecurityImmutable? security, bool is_child=false) => security.HasValue ? default_order_types(security.Value, is_child) : default_order_types(); From 979506d5be656a2fa2ebf512f2d9b7760a15bc43 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Sun, 2 Sep 2018 12:38:13 -0400 Subject: [PATCH 008/203] OrderTemplateKey/Header - override ToString --- src/client/spark/Orders/OrderPropertiesEditorVM.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client/spark/Orders/OrderPropertiesEditorVM.cs b/src/client/spark/Orders/OrderPropertiesEditorVM.cs index a0826b756..2a0767673 100644 --- a/src/client/spark/Orders/OrderPropertiesEditorVM.cs +++ b/src/client/spark/Orders/OrderPropertiesEditorVM.cs @@ -35,6 +35,8 @@ namespace mbts.spark { EqualityComparer.aggregate_hash( type.GetHashCode(), name == null ? 0 : name.GetHashCode()); + + public override string ToString() => $"{name}:{type}"; } struct OrderTemplateHeader : IEquatable { @@ -57,6 +59,8 @@ namespace mbts.spark { obj is OrderTemplateHeader other && Equals(other); public override int GetHashCode() => key.GetHashCode(); + + public override string ToString() => $"{display_name} ({key})"; } class InlineOrderProperties { From ad2869ea6fe744e07752c2a063f46b4fa3dbd2ae Mon Sep 17 00:00:00 2001 From: John Colvin Date: Sun, 2 Sep 2018 13:06:29 -0400 Subject: [PATCH 009/203] OrderPropertiesEditor - use id and order-type to match selected orderdef in inlineprops --- src/client/spark/Orders/OrderPropertiesEditorVM.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/client/spark/Orders/OrderPropertiesEditorVM.cs b/src/client/spark/Orders/OrderPropertiesEditorVM.cs index 2a0767673..04ce0b641 100644 --- a/src/client/spark/Orders/OrderPropertiesEditorVM.cs +++ b/src/client/spark/Orders/OrderPropertiesEditorVM.cs @@ -37,6 +37,9 @@ namespace mbts.spark { name == null ? 0 : name.GetHashCode()); public override string ToString() => $"{name}:{type}"; + + public static bool operator ==(OrderTemplateKey a, OrderTemplateKey b) => a.Equals(b); + public static bool operator !=(OrderTemplateKey a, OrderTemplateKey b) => !a.Equals(b); } struct OrderTemplateHeader : IEquatable { @@ -117,7 +120,7 @@ namespace mbts.spark { security_source.Select(s => model.factory.default_order_type_templates(s).Select(kv => create_order_type_vm(kv.Value, kv.Key.key.name, kv.Key.display_name))), order_properties.Select(get_order_type_vm_prop), get_default_order_type_vm, - (a, b) => a.id == b.id); + (a, b) => a.key() == b.key()); _order_type.Changed += on_selected_order_type_changed; _time_in_force = backing( nameof(selected_time_in_force), From 92f8f74d8f8678dab4bf11e63e40ee31fd5ef8b1 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Tue, 4 Sep 2018 08:51:50 -0400 Subject: [PATCH 010/203] SPARK-1679 --- src/lib/api/services/AdminService.proto | 23 +++++++++++++++++++ src/server/sparkd/user/AdminServiceServer.cpp | 5 +++- src/server/sparkd/user/AdminServiceServer.h | 3 +++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index 0f699a548..06604d312 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -331,6 +331,28 @@ message ICEUserData{ message ICEUserMarketDataEntitlementData{ } +/////////////////////////////////////////////////////////// +// Order Auxiliary Service //////////////////////////////// + +message OrdersSummaryUnit{ + string user = 10; + string account = 20; + uint32 total_buy_orders = 30; + uint32 total_sell_orders = 40; + uint32 total_buy_contracts = 50; + uint32 total_sell_contracts = 60; +} +message OrdersSummary{ + repeated OrdersSummaryUnit units = 10; +} +message OrderSummaryRequest{ + string account = 10; + string user = 20; +} +message OrderCancelRequest{ + string account = 10; + string user = 20; +} /************************************************************************/ /************************************************************************/ /************************************************************************/ @@ -385,4 +407,5 @@ service AdminService { // subscription rpc admin_data(bts.api.VoidMessage) returns (stream AdminData); rpc get_admin_data(bts.api.VoidMessage) returns (AdminData); + rpc get_order_summary(OrderSummaryRequest) returns (OrdersSummary); } \ No newline at end of file diff --git a/src/server/sparkd/user/AdminServiceServer.cpp b/src/server/sparkd/user/AdminServiceServer.cpp index 056314272..1cbd8dcbf 100644 --- a/src/server/sparkd/user/AdminServiceServer.cpp +++ b/src/server/sparkd/user/AdminServiceServer.cpp @@ -82,7 +82,10 @@ void AdminServiceServer::update_user(const api::admin::UserUpdateAPIArgument& re } void AdminServiceServer::get_admin_data(const bts::api::VoidMessage&, const api::Response<::bts::api::admin::AdminData>& reply){ - reply(_state); + reply(_state); +} +void AdminServiceServer::get_order_summary(const api::admin::OrderSummaryRequest&, + const api::Response<::bts::api::admin::OrdersSummary>&){ } void AdminServiceServer::delete_user(const bts::api::admin::IDAPIArgument& id, const api::Response& reply){ diff --git a/src/server/sparkd/user/AdminServiceServer.h b/src/server/sparkd/user/AdminServiceServer.h index 1fb0adf12..85f15ce48 100644 --- a/src/server/sparkd/user/AdminServiceServer.h +++ b/src/server/sparkd/user/AdminServiceServer.h @@ -37,6 +37,9 @@ public: void get_admin_data(const bts::api::VoidMessage& request, const api::Response<::bts::api::admin::AdminData>& reply) override; + + void get_order_summary(const api::admin::OrderSummaryRequest& request, + const api::Response<::bts::api::admin::OrdersSummary>& reply) override; void delete_user(const bts::api::admin::IDAPIArgument& request, const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply) override; From 1f4c91a344aefbf2e44ef638db06cd3a860f46e3 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Tue, 4 Sep 2018 10:37:45 -0400 Subject: [PATCH 011/203] SPARK-1679 --- src/lib/api/services/AdminService.proto | 6 ++++-- src/server/sparkd/user/AdminServiceServer.cpp | 9 ++++++--- src/server/sparkd/user/AdminServiceServer.h | 10 +++++++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index 06604d312..ebed88546 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -349,9 +349,9 @@ message OrderSummaryRequest{ string account = 10; string user = 20; } -message OrderCancelRequest{ +message OrderUsersCancelRequest{ string account = 10; - string user = 20; + repeated string user = 20; } /************************************************************************/ /************************************************************************/ @@ -407,5 +407,7 @@ service AdminService { // subscription rpc admin_data(bts.api.VoidMessage) returns (stream AdminData); rpc get_admin_data(bts.api.VoidMessage) returns (AdminData); + // orders rpc get_order_summary(OrderSummaryRequest) returns (OrdersSummary); + rpc cancel_orders(OrderUsersCancelRequest) returns (bts.api.VoidMessage); } \ No newline at end of file diff --git a/src/server/sparkd/user/AdminServiceServer.cpp b/src/server/sparkd/user/AdminServiceServer.cpp index 1cbd8dcbf..e6774ce3a 100644 --- a/src/server/sparkd/user/AdminServiceServer.cpp +++ b/src/server/sparkd/user/AdminServiceServer.cpp @@ -84,9 +84,6 @@ void AdminServiceServer::get_admin_data(const bts::api::VoidMessage&, const api::Response<::bts::api::admin::AdminData>& reply){ reply(_state); } -void AdminServiceServer::get_order_summary(const api::admin::OrderSummaryRequest&, - const api::Response<::bts::api::admin::OrdersSummary>&){ -} void AdminServiceServer::delete_user(const bts::api::admin::IDAPIArgument& id, const api::Response& reply){ bts::api::admin::AdminServiceOperationResult result; result.set_is_success(true); @@ -116,4 +113,10 @@ void AdminServiceServer::_update_and_publish(bool is_insert){ else LOG(Gen, Error, "Could not serialize admin data, saving failed"); } +void AdminServiceServer::get_order_summary(const api::admin::OrderSummaryRequest&, + const api::Response<::bts::api::admin::OrdersSummary>&){ +} +void AdminServiceServer::cancel_orders(const api::admin::OrderUsersCancelRequest&, + const api::Response&){ +} }} diff --git a/src/server/sparkd/user/AdminServiceServer.h b/src/server/sparkd/user/AdminServiceServer.h index 85f15ce48..f8a7503e8 100644 --- a/src/server/sparkd/user/AdminServiceServer.h +++ b/src/server/sparkd/user/AdminServiceServer.h @@ -37,15 +37,19 @@ public: void get_admin_data(const bts::api::VoidMessage& request, const api::Response<::bts::api::admin::AdminData>& reply) override; - - void get_order_summary(const api::admin::OrderSummaryRequest& request, - const api::Response<::bts::api::admin::OrdersSummary>& reply) override; void delete_user(const bts::api::admin::IDAPIArgument& request, const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply) override; void delete_account(const bts::api::admin::IDAPIArgument& request, const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply) override; + + void get_order_summary(const api::admin::OrderSummaryRequest& request, + const api::Response<::bts::api::admin::OrdersSummary>& reply) override; + + void cancel_orders(const api::admin::OrderUsersCancelRequest& request, + const api::Response& reply) override; + private: void _update_and_publish (bool is_insert); }; From 2b2fe023156d91c2cc9db3418a1d34cac68391b3 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Tue, 4 Sep 2018 22:13:25 -0400 Subject: [PATCH 012/203] SPARK-1679 get_order_summary done --- src/lib/CMakeDeps.cmake | 2 +- src/lib/api/services/AdminService.proto | 16 ++--- .../orderman/orderman_utils/orderman_utils.h | 30 +++++++++ src/server/sparkd/user/AdminServiceServer.cpp | 67 +++++++++++++++++-- src/server/sparkd/user/AdminServiceServer.h | 11 ++- 5 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 src/lib/orderman/orderman_utils/orderman_utils.h diff --git a/src/lib/CMakeDeps.cmake b/src/lib/CMakeDeps.cmake index b4c4b1246..739e47c0c 100644 --- a/src/lib/CMakeDeps.cmake +++ b/src/lib/CMakeDeps.cmake @@ -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/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/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.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/MDPriceLantencyTester.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/Actor.cpp;mercury/broker/AsyncEventBroker.cpp;mercury/broker/CoreEventBroker.cpp;mercury/broker/IEventBroker.cpp;mercury/events/TimerEvent.cpp;mercury/mercury_utils/Executor.cpp;mercury/mercury_utils/IPollableActor.cpp;mercury/mercury_utils/mercury_common.cpp;mercury/synopsis/SynopsisBuilder.cpp;mercury/topics/ITopic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.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/OrderFactory.cpp;orderman/order_router/OrderRouter.cpp;orderman/orderman_utils/fix_order_codec.cpp;orderman/orders/DynamicOrder.cpp;orderman/orders/IcebergOrder.cpp;orderman/orders/NativeOrder.cpp;orderman/orders/Order.cpp;orderman/orders/TimeBoxOrder.cpp;orderman/orders/TriggerOrder.cpp;orderman/orders/book_algos/BookAlgo.cpp;orderman/orders/book_algos/CoverOrder.cpp;orderman/orders/book_algos/JoinOrder.cpp;orderman/orders/book_algos/LeanOrder.cpp;orderman/orders/book_algos/TickOrder.cpp;orderman/orders/book_algos/TopOrder.cpp;orderman/orders/spreader_orders/SpreaderOrder.cpp;orderman/orders/spreader_orders/spreader/LegPropertyData.cpp;orderman/orders/spreader_orders/spreader/QuoteLegPriceQuantityState.cpp;orderman/orders/spreader_orders/spreader/SingleFillHedger.cpp;orderman/orders/spreader_orders/spreader/SingleLegQuoter.cpp;orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp;orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.cpp;orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp;orderman/orders/spreader_orders/spreader/SpreaderUtils.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderServices.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderPreActivationOrderServices.cpp;orderman/orders/spreader_orders/spreader/hedger/UnhedgedDelta.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckCancelledStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSet.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSetHistory.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteLegDriver.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckCancelledWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForIOCWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForSizeStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderEndStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWaitingForCancelAllStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWorkingStateWorker.cpp;orderman/orders/spreader_orders/tests/spreader_tests.cpp;orderman/server/OrderServiceServer.cpp;orderman/tests/fat_wrapper_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;persistence/IStore.cpp;persistence/PersistenceServiceServer.cpp;persistence/SqliteStore.cpp;persistence/tests/PersistenceServiceTests.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/Dispatcher.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) \ No newline at end of file +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/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/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.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/MDPriceLantencyTester.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/Dispatcher.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) \ No newline at end of file diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index ebed88546..f68f22aea 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -334,20 +334,16 @@ message ICEUserMarketDataEntitlementData{ /////////////////////////////////////////////////////////// // Order Auxiliary Service //////////////////////////////// -message OrdersSummaryUnit{ - string user = 10; - string account = 20; - uint32 total_buy_orders = 30; - uint32 total_sell_orders = 40; - uint32 total_buy_contracts = 50; - uint32 total_sell_contracts = 60; +message OrdersSummaryUnit{ + string account = 10; + string user = 20; + uint32 total_orders = 30; + uint32 total_contracts = 40; } message OrdersSummary{ repeated OrdersSummaryUnit units = 10; } message OrderSummaryRequest{ - string account = 10; - string user = 20; } message OrderUsersCancelRequest{ string account = 10; @@ -408,6 +404,6 @@ service AdminService { rpc admin_data(bts.api.VoidMessage) returns (stream AdminData); rpc get_admin_data(bts.api.VoidMessage) returns (AdminData); // orders - rpc get_order_summary(OrderSummaryRequest) returns (OrdersSummary); + rpc get_order_summary(bts.api.VoidMessage) returns (OrdersSummary); rpc cancel_orders(OrderUsersCancelRequest) returns (bts.api.VoidMessage); } \ No newline at end of file diff --git a/src/lib/orderman/orderman_utils/orderman_utils.h b/src/lib/orderman/orderman_utils/orderman_utils.h new file mode 100644 index 000000000..32194c4c3 --- /dev/null +++ b/src/lib/orderman/orderman_utils/orderman_utils.h @@ -0,0 +1,30 @@ +#include "lib/api/services/OrderService.pb.h" + +namespace bts{ +namespace orderman{ + +using namespace bts::api::orderman; + +inline bool order_in_error_state(OrderStatus order_status){ + return order_status == OrderStatus::ORDER_STATUS_ERROR || + order_status == OrderStatus::ORDER_STATUS_DISCONNECTED || + order_status == OrderStatus::ORDER_STATUS_REJECTED_BY_SPARK; +} + +inline bool order_in_working_state(OrderStatus order_status){ + return order_status == OrderStatus::ORDER_STATUS_WORKING || + order_status == OrderStatus::ORDER_STATUS_PENDING_WORKING; +} + +inline bool is_exchange_order(const OrderState& state){ + return state.has_market() || + state.has_limit() || + state.has_stop() || + state.has_stop_limit(); +} + +inline bool is_parent_order(const OrderRecord& ord){ + return ord.parent_key()==0; +} + +}} \ No newline at end of file diff --git a/src/server/sparkd/user/AdminServiceServer.cpp b/src/server/sparkd/user/AdminServiceServer.cpp index e6774ce3a..18e2c2e67 100644 --- a/src/server/sparkd/user/AdminServiceServer.cpp +++ b/src/server/sparkd/user/AdminServiceServer.cpp @@ -2,8 +2,10 @@ #include "lib/persistence/SqliteStore.h" #include "lib/logging/logging.h" +#include "lib/utils/bts_assert.h" #include "lib/utils/PBUtils.h" #include "lib/api/service_helpers/AdminServiceUtils.h" +#include "lib/orderman/orderman_utils/orderman_utils.h" #include #include @@ -22,7 +24,9 @@ AdminServiceServer::AdminServiceServer(bts::api::ServiceContext& context) _unique_key("0"), _state(), _store(new bts::persistence::SqliteStore(_db_name)), - _publisher(make_admin_data_stateful_publisher()) + _publisher(make_admin_data_stateful_publisher()), + _order_service(context.locate()), + _order_map() { LOG(Gen, Info, "AdminServiceServer: service started"); std::map> all_data = _store->retrieve_all(); @@ -48,7 +52,19 @@ AdminServiceServer::AdminServiceServer(bts::api::ServiceContext& context) AdminServiceUtils::fill_connections_from_config(_state); AdminServiceUtils::bootstrap_data(_state); _update_and_publish(true); - } + } + _order_service.subscribe_orders() | + rxcpp::operators::subscribe( + [this](auto const& update) { + for (const auto& o : update.removed()){ + const auto& it = _order_map.find(o->key()); + if(it != _order_map.end()) + _order_map.erase(it); + } + for (const auto& o : update.added()){ + _order_map[o->key()]=o; + } + }); } AdminServiceServer::~AdminServiceServer() {} void AdminServiceServer::create_account(const api::admin::AccountCreateAPIArgument& request, @@ -113,10 +129,51 @@ void AdminServiceServer::_update_and_publish(bool is_insert){ else LOG(Gen, Error, "Could not serialize admin data, saving failed"); } -void AdminServiceServer::get_order_summary(const api::admin::OrderSummaryRequest&, - const api::Response<::bts::api::admin::OrdersSummary>&){ +void AdminServiceServer::get_order_summary(const bts::api::VoidMessage&, + const api::Response<::bts::api::admin::OrdersSummary>& reply){ + std::map> agg; + for(auto it=_order_map.begin();it!=_order_map.end();it++){ + const OrderRecord& ord=*(it->second); + if(bts::orderman::order_in_working_state(ord.state().status()) && + bts::orderman::is_parent_order(ord)) + _add_live_order(agg,ord); + } + OrdersSummary rv; + for (auto acct_it=agg.begin();acct_it!=agg.end();acct_it++){ + const std::map& user_map=acct_it->second; + for(auto user_it=user_map.begin();user_it!=user_map.end();user_it++){ + const OrdersSummaryUnit& u=user_it->second; + OrdersSummaryUnit* nv=rv.add_units(); + nv->CopyFrom(u); + } + } + reply(rv); } void AdminServiceServer::cancel_orders(const api::admin::OrderUsersCancelRequest&, - const api::Response&){ + const api::Response&){ +} + +void AdminServiceServer::_add_live_order(std::map>& agg, + const OrderRecord& ord){ + const std::string& acct=ord.def().account(); + const std::string& user=ord.def().user(); + auto acct_it=agg.find(acct); + if(acct_it==agg.end()) + agg[acct]=std::map(); + acct_it=agg.find(acct); + bts_assert(acct_it!=agg.end()); + std::map& user_map=acct_it->second; + auto user_it=user_map.find(user); + if(user_it==user_map.end()){ + OrdersSummaryUnit nu; + nu.set_account(acct); + nu.set_user(user); + user_map[user]=nu; + } + user_it=user_map.find(user); + bts_assert(user_it!=user_map.end()); + OrdersSummaryUnit& u=user_it->second; + u.set_total_orders(u.total_orders()+1); + u.set_total_contracts(u.total_contracts()+ord.state().quantity()); } }} diff --git a/src/server/sparkd/user/AdminServiceServer.h b/src/server/sparkd/user/AdminServiceServer.h index f8a7503e8..5a74b9c65 100644 --- a/src/server/sparkd/user/AdminServiceServer.h +++ b/src/server/sparkd/user/AdminServiceServer.h @@ -5,10 +5,15 @@ #include "lib/utils/bts_no_copy.h" #include "lib/persistence/IStore.h" #include "lib/api/public/ServiceContext.h" +#include "lib/api/services/OrderService.pb.h" +#include "lib/api/services/OrderService.client.pb.h" namespace bts { namespace admin{ +using namespace bts::api::orderman; +using namespace bts::api::admin; + class AdminServiceServer : public api::admin::AdminServiceBase { private: @@ -18,6 +23,8 @@ private: bts::api::admin::AdminData _state; std::unique_ptr _store; bts::api::admin::AdminServiceBase::Admin_dataStatefulPublisher _publisher; + bts::api::orderman::OrderService _order_service; + std::map> _order_map; public: //BTS_NO_COPY(UserModelServiceServer); AdminServiceServer(bts::api::ServiceContext& context); @@ -44,7 +51,7 @@ public: void delete_account(const bts::api::admin::IDAPIArgument& request, const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply) override; - void get_order_summary(const api::admin::OrderSummaryRequest& request, + void get_order_summary(const bts::api::VoidMessage& request, const api::Response<::bts::api::admin::OrdersSummary>& reply) override; void cancel_orders(const api::admin::OrderUsersCancelRequest& request, @@ -52,5 +59,7 @@ public: private: void _update_and_publish (bool is_insert); + void _add_live_order(std::map>& agg, + const OrderRecord& order); }; }} From 15c8a8bf2884782041103b52b9e6546de0c5f1cd Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Wed, 5 Sep 2018 09:55:05 -0400 Subject: [PATCH 013/203] SPARK-1679 done ! --- src/lib/api/services/AdminService.proto | 8 ++--- src/server/sparkd/user/AdminServiceServer.cpp | 32 +++++++++++++++++-- src/server/sparkd/user/AdminServiceServer.h | 5 ++- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index f68f22aea..bc5e7873f 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -345,9 +345,9 @@ message OrdersSummary{ } message OrderSummaryRequest{ } -message OrderUsersCancelRequest{ - string account = 10; - repeated string user = 20; +message OrderCancelRequest{ + repeated string accounts = 10; + repeated string users = 20; } /************************************************************************/ /************************************************************************/ @@ -405,5 +405,5 @@ service AdminService { rpc get_admin_data(bts.api.VoidMessage) returns (AdminData); // orders rpc get_order_summary(bts.api.VoidMessage) returns (OrdersSummary); - rpc cancel_orders(OrderUsersCancelRequest) returns (bts.api.VoidMessage); + rpc cancel_orders(OrderCancelRequest) returns (bts.api.VoidMessage); } \ No newline at end of file diff --git a/src/server/sparkd/user/AdminServiceServer.cpp b/src/server/sparkd/user/AdminServiceServer.cpp index 18e2c2e67..62afb668a 100644 --- a/src/server/sparkd/user/AdminServiceServer.cpp +++ b/src/server/sparkd/user/AdminServiceServer.cpp @@ -149,8 +149,14 @@ void AdminServiceServer::get_order_summary(const bts::api::VoidMessage&, } reply(rv); } -void AdminServiceServer::cancel_orders(const api::admin::OrderUsersCancelRequest&, - const api::Response&){ +void AdminServiceServer::cancel_orders(const api::admin::OrderCancelRequest& req, + const api::Response&){ + vector accts, users; + for(auto it=req.accounts().begin();it!=req.accounts().end();it++) + accts.push_back(*it); + for(auto it=req.users().begin();it!=req.users().end();it++) + users.push_back(*it); + _cancel_orders(accts,users); } void AdminServiceServer::_add_live_order(std::map>& agg, @@ -176,4 +182,26 @@ void AdminServiceServer::_add_live_order(std::map& accounts, + const vector& users){ + map acct_map, user_map; + vector keys; + for(const std::string& a : accounts) + acct_map[a]=true; + for(const std::string& u : users) + user_map[u]=true; + for(auto it=_order_map.begin();it!=_order_map.end();it++){ + const OrderRecord& ord=*(it->second); + if(bts::orderman::order_in_working_state(ord.state().status()) && + bts::orderman::is_parent_order(ord) && + acct_map.find(ord.def().account()) != acct_map.end() && + user_map.find(ord.def().user()) != user_map.end()) + keys.push_back(ord.key()); + } + for(uint32_t k : keys){ + Cancel c; + c.set_order_key(k); + _order_service.cancel_order(c); + } +} }} diff --git a/src/server/sparkd/user/AdminServiceServer.h b/src/server/sparkd/user/AdminServiceServer.h index 5a74b9c65..e984bc8b5 100644 --- a/src/server/sparkd/user/AdminServiceServer.h +++ b/src/server/sparkd/user/AdminServiceServer.h @@ -13,6 +13,7 @@ namespace admin{ using namespace bts::api::orderman; using namespace bts::api::admin; +using namespace std; class AdminServiceServer : public api::admin::AdminServiceBase { @@ -54,12 +55,14 @@ public: void get_order_summary(const bts::api::VoidMessage& request, const api::Response<::bts::api::admin::OrdersSummary>& reply) override; - void cancel_orders(const api::admin::OrderUsersCancelRequest& request, + void cancel_orders(const api::admin::OrderCancelRequest& request, const api::Response& reply) override; private: void _update_and_publish (bool is_insert); void _add_live_order(std::map>& agg, const OrderRecord& order); + void _cancel_orders(const std::vector& accounts, + const std::vector& users); }; }} From 6b68029b6d60907c602b55337f1c6666d5fccd18 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Wed, 5 Sep 2018 10:03:38 -0400 Subject: [PATCH 014/203] adding comments to SPARK-1679 protos --- src/lib/api/services/AdminService.proto | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index bc5e7873f..a1099b265 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -345,7 +345,11 @@ message OrdersSummary{ } message OrderSummaryRequest{ } -message OrderCancelRequest{ +// top level orders that have account +// and user that are present here, will +// be cancelled. wild cards are not +// supported. +message OrderCancelRequest{ repeated string accounts = 10; repeated string users = 20; } From 19b3ba6615092544670438ff7dd06de71eb3f4d3 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Wed, 5 Sep 2018 12:41:03 -0400 Subject: [PATCH 015/203] SPARK-1668 MIC enums added --- src/lib/api/services/AdminService.proto | 2 ++ src/lib/domain_model/Security.proto | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index a1099b265..13771f048 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -185,6 +185,8 @@ message AccountExchangeConnections{ message ExchangeUserMarketDataEntitlementData{ CMEUserMarketDataEntitlementData cme_data = 10; ICEUserMarketDataEntitlementData ice_data = 20; + //repeated CMEMics allowed_cme_mics = 30; + //repeated ICEMics allowed_ice_mics = 40; } message ExchangeUserData{ diff --git a/src/lib/domain_model/Security.proto b/src/lib/domain_model/Security.proto index 9e6424230..bc07b6c60 100644 --- a/src/lib/domain_model/Security.proto +++ b/src/lib/domain_model/Security.proto @@ -40,6 +40,21 @@ enum Currency { CURRENCY_RMB = 14; } +enum CMEMics { + CME=0; + NYMEX=1; + CBT=2; + COMEX=3; + MGE=4; + DME=5; + NYMSW=6; + FXS=7; +} + +enum ICEMics{ + ICE_FUTURES_CANADA=0; +} + enum Side { SIDE_UNDEFINED = 0; SIDE_BUY = 49; From 91345cf9d2a559d752846d5ae8d74076dc9915cc Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Wed, 5 Sep 2018 12:37:25 -0500 Subject: [PATCH 016/203] renaming cpp_config proto and conf to cme_reference_data --- .../defaults/{cpp_config.conf => cme_reference_data.conf} | 0 .../protos/{cpp_config.proto => cme_reference_data.proto} | 4 ++++ 2 files changed, 4 insertions(+) rename src/lib/config/defaults/{cpp_config.conf => cme_reference_data.conf} (100%) rename src/lib/config/protos/{cpp_config.proto => cme_reference_data.proto} (57%) diff --git a/src/lib/config/defaults/cpp_config.conf b/src/lib/config/defaults/cme_reference_data.conf similarity index 100% rename from src/lib/config/defaults/cpp_config.conf rename to src/lib/config/defaults/cme_reference_data.conf diff --git a/src/lib/config/protos/cpp_config.proto b/src/lib/config/protos/cme_reference_data.proto similarity index 57% rename from src/lib/config/protos/cpp_config.proto rename to src/lib/config/protos/cme_reference_data.proto index 7070adbe9..84c0f184f 100644 --- a/src/lib/config/protos/cpp_config.proto +++ b/src/lib/config/protos/cme_reference_data.proto @@ -3,4 +3,8 @@ package bts.config; message CppConfig { map cpp_entry = 1; +} + +message CmeSymbolMics { + map cme_mic = 1; } \ No newline at end of file From b50381d386b0397e882d4f35b55cb888397e5f7b Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Wed, 5 Sep 2018 12:38:12 -0500 Subject: [PATCH 017/203] adding an unset enum state to CMEMics --- src/lib/domain_model/Security.proto | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lib/domain_model/Security.proto b/src/lib/domain_model/Security.proto index bc07b6c60..3d2b5f738 100644 --- a/src/lib/domain_model/Security.proto +++ b/src/lib/domain_model/Security.proto @@ -41,14 +41,15 @@ enum Currency { } enum CMEMics { - CME=0; - NYMEX=1; - CBT=2; - COMEX=3; - MGE=4; - DME=5; - NYMSW=6; - FXS=7; + UNSET = 0; + CME = 1; + NYMEX = 2; + CBT = 3; + COMEX = 4; + MGE = 5; + DME = 6; + NYMSW = 7; + FXS = 8; } enum ICEMics{ From 87712819546470f734f2d9f51e464f1f07ceb453 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Wed, 5 Sep 2018 13:26:50 -0500 Subject: [PATCH 018/203] Updating script to parse and dump mics --- .../config/defaults/cme_reference_data.conf | 1456 ++++++++++++++++- tools/cpp_helper/Generate-Cpps-reqs.ps1 | 12 + tools/cpp_helper/Generate-Cpps.ps1 | 24 +- 3 files changed, 1470 insertions(+), 22 deletions(-) diff --git a/src/lib/config/defaults/cme_reference_data.conf b/src/lib/config/defaults/cme_reference_data.conf index 0f4fb61f8..45372ff86 100644 --- a/src/lib/config/defaults/cme_reference_data.conf +++ b/src/lib/config/defaults/cme_reference_data.conf @@ -1,5 +1,8 @@ -# Generated by automated script. DO NOT EDIT! +# +# Entire file generated by automated script. DO NOT EDIT! # source: tools/cpp_helper/generate-cpps.ps1 +# + CppConfig { value @@ -179,7 +182,7 @@ CppConfig cpp_entry { key: "AE5" value: 1000 } cpp_entry { key: "AE8" value: 5 } cpp_entry { key: "AE9" value: 80 } - cpp_entry { key: "AEJ" value: 1020 } + cpp_entry { key: "AEJ" value: 940 } cpp_entry { key: "AEP" value: 25 } cpp_entry { key: "AET" value: 1000 } cpp_entry { key: "AEZ" value: 42000 } @@ -218,7 +221,7 @@ CppConfig cpp_entry { key: "AJB" value: 1000 } cpp_entry { key: "AJJ" value: 5 } cpp_entry { key: "AJL" value: 42000 } - cpp_entry { key: "AJP" value: 1020 } + cpp_entry { key: "AJP" value: 940 } cpp_entry { key: "AJR" value: 1000 } cpp_entry { key: "AJS" value: 42000 } cpp_entry { key: "AJU" value: 42000 } @@ -723,7 +726,7 @@ CppConfig cpp_entry { key: "JFB" value: 1000 } cpp_entry { key: "JFC" value: 1000 } cpp_entry { key: "JKM" value: 10000 } - cpp_entry { key: "JML" value: 440 } + cpp_entry { key: "JML" value: 40 } cpp_entry { key: "JNC" value: 1000 } cpp_entry { key: "JNG" value: 10000 } cpp_entry { key: "JNL" value: 80 } @@ -813,7 +816,7 @@ CppConfig cpp_entry { key: "MEB" value: 100 } cpp_entry { key: "MEE" value: 100 } cpp_entry { key: "MEF" value: 10 } - cpp_entry { key: "MEL" value: 840 } + cpp_entry { key: "MEL" value: 920 } cpp_entry { key: "MEO" value: 100 } cpp_entry { key: "MEW" value: 100 } cpp_entry { key: "MFB" value: 1000 } @@ -1124,20 +1127,19 @@ CppConfig cpp_entry { key: "STY" value: 1000 } cpp_entry { key: "SU" value: 250 } cpp_entry { key: "SUT" value: 250 } - cpp_entry { key: "T08" value: 800 } - cpp_entry { key: "T09" value: 800 } + cpp_entry { key: "T04" value: 800 } + cpp_entry { key: "T05" value: 800 } + cpp_entry { key: "T06" value: 800 } + cpp_entry { key: "T07" value: 800 } cpp_entry { key: "T10" value: 800 } + cpp_entry { key: "T11" value: 800 } + cpp_entry { key: "T12" value: 800 } cpp_entry { key: "T13" value: 800 } cpp_entry { key: "T14" value: 800 } - cpp_entry { key: "T15" value: 800 } - cpp_entry { key: "T16" value: 800 } cpp_entry { key: "T17" value: 800 } + cpp_entry { key: "T18" value: 800 } + cpp_entry { key: "T19" value: 800 } cpp_entry { key: "T1U" value: 1000 } - cpp_entry { key: "T20" value: 800 } - cpp_entry { key: "T21" value: 800 } - cpp_entry { key: "T22" value: 800 } - cpp_entry { key: "T23" value: 800 } - cpp_entry { key: "T24" value: 800 } cpp_entry { key: "T27" value: 800 } cpp_entry { key: "T28" value: 800 } cpp_entry { key: "T29" value: 800 } @@ -1145,6 +1147,7 @@ CppConfig cpp_entry { key: "T2D" value: 1000 } cpp_entry { key: "T2M" value: 100 } cpp_entry { key: "T30" value: 800 } + cpp_entry { key: "T31" value: 800 } cpp_entry { key: "T3B" value: 1000 } cpp_entry { key: "T3L" value: 80 } cpp_entry { key: "T4B" value: 1000 } @@ -1180,8 +1183,8 @@ CppConfig cpp_entry { key: "TPB" value: 5000 } cpp_entry { key: "TPY" value: 5000 } cpp_entry { key: "TRB" value: 25 } - cpp_entry { key: "TRE" value: 125000 } cpp_entry { key: "TRI" value: 25 } + cpp_entry { key: "TRL" value: 1000000 } cpp_entry { key: "TRM" value: 25 } cpp_entry { key: "TRY" value: 200000 } cpp_entry { key: "TRZ" value: 2500 } @@ -1189,7 +1192,7 @@ CppConfig cpp_entry { key: "TT" value: 50000 } cpp_entry { key: "TTD" value: 24 } cpp_entry { key: "TTE" value: 10000 } - cpp_entry { key: "TTF" value: 744 } + cpp_entry { key: "TTF" value: 720 } cpp_entry { key: "TW" value: 1000 } cpp_entry { key: "TXN" value: 2500 } cpp_entry { key: "TZ6" value: 2500 } @@ -1204,7 +1207,7 @@ CppConfig cpp_entry { key: "UFU" value: 100 } cpp_entry { key: "UFZ" value: 100 } cpp_entry { key: "UGG" value: 42000 } - cpp_entry { key: "UKG" value: 310 } + cpp_entry { key: "UKG" value: 300 } cpp_entry { key: "ULB" value: 42000 } cpp_entry { key: "UMB" value: 1000 } cpp_entry { key: "UMD" value: 1000 } @@ -1291,6 +1294,7 @@ CppConfig cpp_entry { key: "XAU" value: 100 } cpp_entry { key: "XAV" value: 100 } cpp_entry { key: "XAY" value: 100 } + cpp_entry { key: "XAZ" value: 250 } cpp_entry { key: "XBT" value: 100 } cpp_entry { key: "XC" value: 10 } cpp_entry { key: "XER" value: 1000 } @@ -1310,6 +1314,8 @@ CppConfig cpp_entry { key: "XW" value: 10 } cpp_entry { key: "XX" value: 2500 } cpp_entry { key: "XYT" value: 100 } + cpp_entry { key: "XZI" value: 250 } + cpp_entry { key: "XZT" value: 250 } cpp_entry { key: "YF" value: 42000 } cpp_entry { key: "YHE" value: 80 } cpp_entry { key: "YHF" value: 80 } @@ -1391,7 +1397,7 @@ CppConfig cpp_entry { key: "ZD9909" value: 1000 } cpp_entry { key: "ZD9912" value: 1000 } cpp_entry { key: "ZF" value: 1000 } - cpp_entry { key: "ZGD" value: 100 } + cpp_entry { key: "ZGD" value: 1000 } cpp_entry { key: "ZGL" value: 5 } cpp_entry { key: "ZJL" value: 5 } cpp_entry { key: "ZKU" value: 1000 } @@ -1409,4 +1415,1418 @@ CppConfig cpp_entry { key: "ZW" value: 50 } cpp_entry { key: "ZWT" value: 50 } } +} + +CmeSymbolMics +{ + value + { + cme_mic { key: "0A" value: 2 } + cme_mic { key: "0B" value: 2 } + cme_mic { key: "0C" value: 2 } + cme_mic { key: "0E" value: 2 } + cme_mic { key: "16E" value: 1 } + cme_mic { key: "1BT" value: 1 } + cme_mic { key: "1E" value: 2 } + cme_mic { key: "1ET" value: 1 } + cme_mic { key: "1FT" value: 1 } + cme_mic { key: "1H" value: 2 } + cme_mic { key: "1IT" value: 1 } + cme_mic { key: "1KT" value: 1 } + cme_mic { key: "1NA" value: 2 } + cme_mic { key: "1PT" value: 1 } + cme_mic { key: "1S" value: 2 } + cme_mic { key: "1T" value: 2 } + cme_mic { key: "1UT" value: 1 } + cme_mic { key: "1VT" value: 1 } + cme_mic { key: "1Y" value: 2 } + cme_mic { key: "1YT" value: 1 } + cme_mic { key: "1ZA" value: 2 } + cme_mic { key: "22" value: 2 } + cme_mic { key: "23" value: 2 } + cme_mic { key: "26" value: 2 } + cme_mic { key: "27" value: 2 } + cme_mic { key: "29" value: 2 } + cme_mic { key: "2C" value: 2 } + cme_mic { key: "2D" value: 2 } + cme_mic { key: "2FW" value: 2 } + cme_mic { key: "2GT" value: 1 } + cme_mic { key: "2JW" value: 2 } + cme_mic { key: "2RB" value: 2 } + cme_mic { key: "2VT" value: 1 } + cme_mic { key: "2ZW" value: 2 } + cme_mic { key: "36" value: 2 } + cme_mic { key: "36E" value: 1 } + cme_mic { key: "37" value: 2 } + cme_mic { key: "38" value: 2 } + cme_mic { key: "39" value: 2 } + cme_mic { key: "3L" value: 2 } + cme_mic { key: "3NA" value: 2 } + cme_mic { key: "3NB" value: 2 } + cme_mic { key: "3P" value: 2 } + cme_mic { key: "3V" value: 2 } + cme_mic { key: "3XW" value: 2 } + cme_mic { key: "3ZW" value: 2 } + cme_mic { key: "4C" value: 2 } + cme_mic { key: "4G" value: 2 } + cme_mic { key: "4RB" value: 2 } + cme_mic { key: "4U" value: 2 } + cme_mic { key: "4V" value: 2 } + cme_mic { key: "4XW" value: 2 } + cme_mic { key: "4Y" value: 2 } + cme_mic { key: "51" value: 2 } + cme_mic { key: "5L" value: 2 } + cme_mic { key: "5Y" value: 2 } + cme_mic { key: "5ZN" value: 2 } + cme_mic { key: "63" value: 2 } + cme_mic { key: "6A" value: 1 } + cme_mic { key: "6B" value: 1 } + cme_mic { key: "6C" value: 1 } + cme_mic { key: "6E" value: 1 } + cme_mic { key: "6J" value: 1 } + cme_mic { key: "6L" value: 1 } + cme_mic { key: "6M" value: 1 } + cme_mic { key: "6N" value: 1 } + cme_mic { key: "6R" value: 1 } + cme_mic { key: "6S" value: 1 } + cme_mic { key: "6Z" value: 1 } + cme_mic { key: "7D" value: 2 } + cme_mic { key: "7FT" value: 2 } + cme_mic { key: "7N" value: 2 } + cme_mic { key: "7P" value: 2 } + cme_mic { key: "7S" value: 2 } + cme_mic { key: "7U" value: 2 } + cme_mic { key: "7V" value: 2 } + cme_mic { key: "7X" value: 2 } + cme_mic { key: "88" value: 2 } + cme_mic { key: "8D" value: 2 } + cme_mic { key: "8W" value: 2 } + cme_mic { key: "8XN" value: 2 } + cme_mic { key: "8ZN" value: 2 } + cme_mic { key: "9FN" value: 2 } + cme_mic { key: "9Q" value: 2 } + cme_mic { key: "A0D" value: 2 } + cme_mic { key: "A0F" value: 2 } + cme_mic { key: "A1D" value: 2 } + cme_mic { key: "A1G" value: 2 } + cme_mic { key: "A1L" value: 2 } + cme_mic { key: "A1M" value: 2 } + cme_mic { key: "A1P" value: 2 } + cme_mic { key: "A1Q" value: 2 } + cme_mic { key: "A1R" value: 2 } + cme_mic { key: "A1U" value: 2 } + cme_mic { key: "A1V" value: 2 } + cme_mic { key: "A1W" value: 2 } + cme_mic { key: "A1X" value: 2 } + cme_mic { key: "A32" value: 2 } + cme_mic { key: "A33" value: 2 } + cme_mic { key: "A38" value: 4 } + cme_mic { key: "A3C" value: 2 } + cme_mic { key: "A3G" value: 2 } + cme_mic { key: "A3M" value: 2 } + cme_mic { key: "A3N" value: 2 } + cme_mic { key: "A3Q" value: 2 } + cme_mic { key: "A3R" value: 2 } + cme_mic { key: "A42" value: 2 } + cme_mic { key: "A43" value: 2 } + cme_mic { key: "A46" value: 2 } + cme_mic { key: "A47" value: 2 } + cme_mic { key: "A49" value: 2 } + cme_mic { key: "A4L" value: 2 } + cme_mic { key: "A4M" value: 2 } + cme_mic { key: "A4N" value: 2 } + cme_mic { key: "A4P" value: 2 } + cme_mic { key: "A4Q" value: 2 } + cme_mic { key: "A4R" value: 2 } + cme_mic { key: "A50" value: 2 } + cme_mic { key: "A55" value: 2 } + cme_mic { key: "A58" value: 2 } + cme_mic { key: "A59" value: 2 } + cme_mic { key: "A5C" value: 2 } + cme_mic { key: "A5U" value: 2 } + cme_mic { key: "A6L" value: 2 } + cme_mic { key: "A6V" value: 2 } + cme_mic { key: "A6W" value: 2 } + cme_mic { key: "A6X" value: 2 } + cme_mic { key: "A7E" value: 2 } + cme_mic { key: "A7G" value: 2 } + cme_mic { key: "A7I" value: 2 } + cme_mic { key: "A7L" value: 2 } + cme_mic { key: "A7Q" value: 2 } + cme_mic { key: "A7Y" value: 2 } + cme_mic { key: "A81" value: 2 } + cme_mic { key: "A8C" value: 2 } + cme_mic { key: "A8G" value: 2 } + cme_mic { key: "A8I" value: 2 } + cme_mic { key: "A8J" value: 2 } + cme_mic { key: "A8K" value: 2 } + cme_mic { key: "A8L" value: 2 } + cme_mic { key: "A8M" value: 2 } + cme_mic { key: "A8N" value: 2 } + cme_mic { key: "A8O" value: 2 } + cme_mic { key: "A91" value: 2 } + cme_mic { key: "A9N" value: 2 } + cme_mic { key: "AA3" value: 2 } + cme_mic { key: "AA4" value: 2 } + cme_mic { key: "AA5" value: 2 } + cme_mic { key: "AA6" value: 2 } + cme_mic { key: "AA7" value: 2 } + cme_mic { key: "AA8" value: 2 } + cme_mic { key: "AA9" value: 2 } + cme_mic { key: "AAW" value: 2 } + cme_mic { key: "AB3" value: 2 } + cme_mic { key: "AB7" value: 2 } + cme_mic { key: "ABH" value: 2 } + cme_mic { key: "ABI" value: 2 } + cme_mic { key: "ABS" value: 2 } + cme_mic { key: "ABT" value: 2 } + cme_mic { key: "ABX" value: 2 } + cme_mic { key: "ABY" value: 2 } + cme_mic { key: "AC0" value: 2 } + cme_mic { key: "ACD" value: 1 } + cme_mic { key: "ACM" value: 2 } + cme_mic { key: "ACT" value: 2 } + cme_mic { key: "AD0" value: 2 } + cme_mic { key: "AD5" value: 2 } + cme_mic { key: "AD6" value: 2 } + cme_mic { key: "AD8" value: 2 } + cme_mic { key: "AD9" value: 2 } + cme_mic { key: "ADB" value: 2 } + cme_mic { key: "AE3" value: 2 } + cme_mic { key: "AE5" value: 2 } + cme_mic { key: "AE8" value: 2 } + cme_mic { key: "AE9" value: 2 } + cme_mic { key: "AEJ" value: 2 } + cme_mic { key: "AEP" value: 4 } + cme_mic { key: "AET" value: 2 } + cme_mic { key: "AEZ" value: 2 } + cme_mic { key: "AF" value: 2 } + cme_mic { key: "AF2" value: 2 } + cme_mic { key: "AF4" value: 2 } + cme_mic { key: "AF5" value: 2 } + cme_mic { key: "AFE" value: 2 } + cme_mic { key: "AFF" value: 2 } + cme_mic { key: "AFH" value: 2 } + cme_mic { key: "AFI" value: 2 } + cme_mic { key: "AFK" value: 2 } + cme_mic { key: "AFY" value: 2 } + cme_mic { key: "AGA" value: 2 } + cme_mic { key: "AGE" value: 2 } + cme_mic { key: "AGP" value: 2 } + cme_mic { key: "AGT" value: 2 } + cme_mic { key: "AGX" value: 2 } + cme_mic { key: "AH1" value: 2 } + cme_mic { key: "AH3" value: 2 } + cme_mic { key: "AHJ" value: 2 } + cme_mic { key: "AHL" value: 2 } + cme_mic { key: "AHM" value: 2 } + cme_mic { key: "AI1" value: 2 } + cme_mic { key: "AI2" value: 2 } + cme_mic { key: "AI3" value: 2 } + cme_mic { key: "AI4" value: 2 } + cme_mic { key: "AI5" value: 2 } + cme_mic { key: "AI6" value: 2 } + cme_mic { key: "AI7" value: 2 } + cme_mic { key: "AI9" value: 2 } + cme_mic { key: "AJ" value: 2 } + cme_mic { key: "AJ1" value: 2 } + cme_mic { key: "AJ2" value: 2 } + cme_mic { key: "AJ9" value: 2 } + cme_mic { key: "AJB" value: 2 } + cme_mic { key: "AJJ" value: 2 } + cme_mic { key: "AJL" value: 2 } + cme_mic { key: "AJP" value: 2 } + cme_mic { key: "AJR" value: 2 } + cme_mic { key: "AJS" value: 2 } + cme_mic { key: "AJU" value: 2 } + cme_mic { key: "AJY" value: 1 } + cme_mic { key: "AK1" value: 2 } + cme_mic { key: "AKL" value: 2 } + cme_mic { key: "AKR" value: 2 } + cme_mic { key: "AKS" value: 2 } + cme_mic { key: "AKX" value: 2 } + cme_mic { key: "AKZ" value: 2 } + cme_mic { key: "AL1" value: 2 } + cme_mic { key: "AL5" value: 2 } + cme_mic { key: "AL6" value: 2 } + cme_mic { key: "AL8" value: 2 } + cme_mic { key: "AL9" value: 2 } + cme_mic { key: "ALA" value: 4 } + cme_mic { key: "ALB" value: 4 } + cme_mic { key: "ALI" value: 4 } + cme_mic { key: "ALM" value: 2 } + cme_mic { key: "ALW" value: 2 } + cme_mic { key: "ALX" value: 2 } + cme_mic { key: "ALY" value: 2 } + cme_mic { key: "AM1" value: 2 } + cme_mic { key: "AML" value: 2 } + cme_mic { key: "AN1" value: 2 } + cme_mic { key: "ANC" value: 2 } + cme_mic { key: "ANE" value: 1 } + cme_mic { key: "ANL" value: 2 } + cme_mic { key: "ANT" value: 2 } + cme_mic { key: "AO1" value: 2 } + cme_mic { key: "AOB" value: 2 } + cme_mic { key: "AOH" value: 2 } + cme_mic { key: "AOJ" value: 2 } + cme_mic { key: "AOL" value: 2 } + cme_mic { key: "AP1" value: 2 } + cme_mic { key: "AP2" value: 2 } + cme_mic { key: "AP3" value: 2 } + cme_mic { key: "AP4" value: 2 } + cme_mic { key: "AP5" value: 2 } + cme_mic { key: "AP7" value: 2 } + cme_mic { key: "AP8" value: 2 } + cme_mic { key: "AP9" value: 2 } + cme_mic { key: "APA" value: 2 } + cme_mic { key: "APS" value: 2 } + cme_mic { key: "AQ5" value: 2 } + cme_mic { key: "AQ8" value: 2 } + cme_mic { key: "AQA" value: 2 } + cme_mic { key: "AQK" value: 2 } + cme_mic { key: "AR0" value: 2 } + cme_mic { key: "AR1" value: 2 } + cme_mic { key: "AR3" value: 2 } + cme_mic { key: "AR4" value: 2 } + cme_mic { key: "AR6" value: 2 } + cme_mic { key: "AR8" value: 2 } + cme_mic { key: "ARE" value: 2 } + cme_mic { key: "ARI" value: 2 } + cme_mic { key: "ARY" value: 2 } + cme_mic { key: "AS4" value: 2 } + cme_mic { key: "ASD" value: 2 } + cme_mic { key: "ASP" value: 2 } + cme_mic { key: "AT0" value: 2 } + cme_mic { key: "ATP" value: 2 } + cme_mic { key: "ATU" value: 2 } + cme_mic { key: "ATY" value: 2 } + cme_mic { key: "AU2" value: 2 } + cme_mic { key: "AU3" value: 2 } + cme_mic { key: "AU4" value: 2 } + cme_mic { key: "AU5" value: 2 } + cme_mic { key: "AU6" value: 2 } + cme_mic { key: "AUB" value: 2 } + cme_mic { key: "AUDJYC" value: 1 } + cme_mic { key: "AUDUSC" value: 1 } + cme_mic { key: "AUDUSD" value: 8 } + cme_mic { key: "AUDUSN" value: 1 } + cme_mic { key: "AUF" value: 2 } + cme_mic { key: "AUH" value: 2 } + cme_mic { key: "AUI" value: 2 } + cme_mic { key: "AUJ" value: 2 } + cme_mic { key: "AUP" value: 4 } + cme_mic { key: "AUS" value: 2 } + cme_mic { key: "AUT" value: 2 } + cme_mic { key: "AUW" value: 3 } + cme_mic { key: "AUY" value: 2 } + cme_mic { key: "AUZ" value: 2 } + cme_mic { key: "AV0" value: 2 } + cme_mic { key: "AVK" value: 2 } + cme_mic { key: "AVL" value: 2 } + cme_mic { key: "AVU" value: 2 } + cme_mic { key: "AVZ" value: 2 } + cme_mic { key: "AW" value: 3 } + cme_mic { key: "AW2" value: 2 } + cme_mic { key: "AW4" value: 2 } + cme_mic { key: "AW6" value: 2 } + cme_mic { key: "AWI" value: 3 } + cme_mic { key: "AWJ" value: 2 } + cme_mic { key: "AWQ" value: 2 } + cme_mic { key: "AWT" value: 3 } + cme_mic { key: "AX1" value: 2 } + cme_mic { key: "AXA" value: 2 } + cme_mic { key: "AXB" value: 2 } + cme_mic { key: "AY" value: 2 } + cme_mic { key: "AY1" value: 2 } + cme_mic { key: "AY3" value: 2 } + cme_mic { key: "AY4" value: 2 } + cme_mic { key: "AYH" value: 2 } + cme_mic { key: "AYV" value: 2 } + cme_mic { key: "AYX" value: 2 } + cme_mic { key: "AZ0" value: 2 } + cme_mic { key: "AZ1" value: 2 } + cme_mic { key: "AZ5" value: 2 } + cme_mic { key: "AZ7" value: 2 } + cme_mic { key: "AZ9" value: 2 } + cme_mic { key: "B0" value: 2 } + cme_mic { key: "B1" value: 2 } + cme_mic { key: "B1U" value: 3 } + cme_mic { key: "B2K" value: 2 } + cme_mic { key: "B6L" value: 2 } + cme_mic { key: "B7H" value: 2 } + cme_mic { key: "B8" value: 2 } + cme_mic { key: "BB" value: 2 } + cme_mic { key: "BBT" value: 2 } + cme_mic { key: "BBV" value: 1 } + cme_mic { key: "BCF" value: 3 } + cme_mic { key: "BCH" value: 3 } + cme_mic { key: "BDB" value: 2 } + cme_mic { key: "BDI" value: 1 } + cme_mic { key: "BDS" value: 6 } + cme_mic { key: "BFR" value: 2 } + cme_mic { key: "BG" value: 2 } + cme_mic { key: "BIO" value: 1 } + cme_mic { key: "BIT" value: 1 } + cme_mic { key: "BK" value: 2 } + cme_mic { key: "BOO" value: 2 } + cme_mic { key: "BOS" value: 1 } + cme_mic { key: "BR7" value: 2 } + cme_mic { key: "BSW" value: 3 } + cme_mic { key: "BTC" value: 1 } + cme_mic { key: "BTT" value: 1 } + cme_mic { key: "BUC" value: 2 } + cme_mic { key: "BUS" value: 2 } + cme_mic { key: "BUW" value: 7 } + cme_mic { key: "BWF" value: 3 } + cme_mic { key: "BWH" value: 3 } + cme_mic { key: "BZ" value: 2 } + cme_mic { key: "BZ1" value: 2 } + cme_mic { key: "BZ2" value: 2 } + cme_mic { key: "BZL" value: 2 } + cme_mic { key: "BZS" value: 2 } + cme_mic { key: "BZT" value: 2 } + cme_mic { key: "C2E" value: 2 } + cme_mic { key: "C3D" value: 2 } + cme_mic { key: "C4D" value: 2 } + cme_mic { key: "C4Z" value: 2 } + cme_mic { key: "C5D" value: 2 } + cme_mic { key: "C6C" value: 2 } + cme_mic { key: "C7C" value: 2 } + cme_mic { key: "C8C" value: 2 } + cme_mic { key: "C9C" value: 2 } + cme_mic { key: "CADJYC" value: 1 } + cme_mic { key: "CB" value: 1 } + cme_mic { key: "CBA" value: 2 } + cme_mic { key: "CBB" value: 2 } + cme_mic { key: "CBC" value: 2 } + cme_mic { key: "CBO" value: 2 } + cme_mic { key: "CC0" value: 2 } + cme_mic { key: "CC5" value: 2 } + cme_mic { key: "CCM" value: 2 } + cme_mic { key: "CF" value: 2 } + cme_mic { key: "CFB" value: 2 } + cme_mic { key: "CFC" value: 2 } + cme_mic { key: "CGB" value: 2 } + cme_mic { key: "CHB" value: 2 } + cme_mic { key: "CHI" value: 1 } + cme_mic { key: "CHL" value: 1 } + cme_mic { key: "CHP" value: 1 } + cme_mic { key: "CIL" value: 2 } + cme_mic { key: "CIN" value: 2 } + cme_mic { key: "CJ" value: 2 } + cme_mic { key: "CJY" value: 1 } + cme_mic { key: "CK" value: 2 } + cme_mic { key: "CKW" value: 3 } + cme_mic { key: "CL" value: 2 } + cme_mic { key: "CL1" value: 2 } + cme_mic { key: "CL2" value: 2 } + cme_mic { key: "CLL" value: 2 } + cme_mic { key: "CLS" value: 2 } + cme_mic { key: "CLT" value: 2 } + cme_mic { key: "CMF" value: 2 } + cme_mic { key: "CNH" value: 1 } + cme_mic { key: "COL" value: 2 } + cme_mic { key: "CPB" value: 2 } + cme_mic { key: "CPC" value: 1 } + cme_mic { key: "CPD" value: 2 } + cme_mic { key: "CPO" value: 1 } + cme_mic { key: "CPP" value: 2 } + cme_mic { key: "CRB" value: 2 } + cme_mic { key: "CRG" value: 2 } + cme_mic { key: "CSC" value: 1 } + cme_mic { key: "CSN" value: 2 } + cme_mic { key: "CSW" value: 2 } + cme_mic { key: "CSX" value: 2 } + cme_mic { key: "CTB" value: 1 } + cme_mic { key: "CTM" value: 1 } + cme_mic { key: "CTR" value: 1 } + cme_mic { key: "CU" value: 2 } + cme_mic { key: "CUP" value: 4 } + cme_mic { key: "CUS" value: 1 } + cme_mic { key: "CY" value: 2 } + cme_mic { key: "CZK" value: 1 } + cme_mic { key: "CZN" value: 2 } + cme_mic { key: "D0" value: 1 } + cme_mic { key: "D0X" value: 1 } + cme_mic { key: "D0Z" value: 1 } + cme_mic { key: "D1N" value: 2 } + cme_mic { key: "D2" value: 1 } + cme_mic { key: "D2L" value: 2 } + cme_mic { key: "D2X" value: 1 } + cme_mic { key: "D2Z" value: 1 } + cme_mic { key: "D3L" value: 2 } + cme_mic { key: "D4L" value: 2 } + cme_mic { key: "D7L" value: 2 } + cme_mic { key: "DAX" value: 2 } + cme_mic { key: "DAZ" value: 2 } + cme_mic { key: "DBB" value: 2 } + cme_mic { key: "DBI" value: 6 } + cme_mic { key: "DBL" value: 2 } + cme_mic { key: "DBR" value: 6 } + cme_mic { key: "DBT" value: 2 } + cme_mic { key: "DC" value: 1 } + cme_mic { key: "DCB" value: 2 } + cme_mic { key: "DCD" value: 6 } + cme_mic { key: "DCL" value: 2 } + cme_mic { key: "DCW" value: 2 } + cme_mic { key: "DDA" value: 6 } + cme_mic { key: "DDB" value: 6 } + cme_mic { key: "DDE" value: 6 } + cme_mic { key: "DDI" value: 6 } + cme_mic { key: "DDO" value: 6 } + cme_mic { key: "DEB" value: 2 } + cme_mic { key: "DEN" value: 1 } + cme_mic { key: "DEP" value: 2 } + cme_mic { key: "DES" value: 6 } + cme_mic { key: "DFL" value: 3 } + cme_mic { key: "DFT" value: 3 } + cme_mic { key: "DG2" value: 3 } + cme_mic { key: "DG3" value: 3 } + cme_mic { key: "DGH" value: 6 } + cme_mic { key: "DGI" value: 3 } + cme_mic { key: "DGS" value: 3 } + cme_mic { key: "DGT" value: 3 } + cme_mic { key: "DHA" value: 2 } + cme_mic { key: "DHE" value: 6 } + cme_mic { key: "DHI" value: 6 } + cme_mic { key: "DIH" value: 2 } + cme_mic { key: "DJR" value: 3 } + cme_mic { key: "DKR" value: 6 } + cme_mic { key: "DMB" value: 6 } + cme_mic { key: "DOB" value: 6 } + cme_mic { key: "DOP" value: 6 } + cme_mic { key: "DRB" value: 6 } + cme_mic { key: "DRS" value: 3 } + cme_mic { key: "DSD" value: 6 } + cme_mic { key: "DSE" value: 6 } + cme_mic { key: "DSG" value: 6 } + cme_mic { key: "DSI" value: 6 } + cme_mic { key: "DSM" value: 6 } + cme_mic { key: "DUA" value: 6 } + cme_mic { key: "DUP" value: 6 } + cme_mic { key: "DVE" value: 1 } + cme_mic { key: "DVT" value: 1 } + cme_mic { key: "DY" value: 1 } + cme_mic { key: "E1U" value: 3 } + cme_mic { key: "E4L" value: 2 } + cme_mic { key: "E6" value: 2 } + cme_mic { key: "E6M" value: 2 } + cme_mic { key: "E7" value: 1 } + cme_mic { key: "E9X" value: 2 } + cme_mic { key: "EAA" value: 2 } + cme_mic { key: "EAB" value: 2 } + cme_mic { key: "EAC" value: 2 } + cme_mic { key: "EAD" value: 1 } + cme_mic { key: "EAE" value: 2 } + cme_mic { key: "EAF" value: 2 } + cme_mic { key: "EAW" value: 2 } + cme_mic { key: "EBE" value: 2 } + cme_mic { key: "ECD" value: 1 } + cme_mic { key: "ECK" value: 1 } + cme_mic { key: "EDP" value: 4 } + cme_mic { key: "EFF" value: 2 } + cme_mic { key: "EFM" value: 2 } + cme_mic { key: "EGB" value: 2 } + cme_mic { key: "EGN" value: 2 } + cme_mic { key: "EH" value: 3 } + cme_mic { key: "EHF" value: 1 } + cme_mic { key: "EI" value: 1 } + cme_mic { key: "EIT" value: 1 } + cme_mic { key: "EJL" value: 2 } + cme_mic { key: "EL1" value: 2 } + cme_mic { key: "EMC" value: 2 } + cme_mic { key: "EMD" value: 1 } + cme_mic { key: "EMT" value: 1 } + cme_mic { key: "EMW" value: 2 } + cme_mic { key: "EN" value: 2 } + cme_mic { key: "ENK" value: 1 } + cme_mic { key: "ENP" value: 2 } + cme_mic { key: "ENS" value: 2 } + cme_mic { key: "ENY" value: 1 } + cme_mic { key: "EO1" value: 2 } + cme_mic { key: "EOB" value: 2 } + cme_mic { key: "EP1" value: 2 } + cme_mic { key: "EPN" value: 2 } + cme_mic { key: "EPZ" value: 1 } + cme_mic { key: "ERL" value: 2 } + cme_mic { key: "ES" value: 1 } + cme_mic { key: "ESB" value: 2 } + cme_mic { key: "ESK" value: 1 } + cme_mic { key: "ESQ" value: 1 } + cme_mic { key: "ESS" value: 2 } + cme_mic { key: "EST" value: 1 } + cme_mic { key: "ETT" value: 1 } + cme_mic { key: "EUB" value: 2 } + cme_mic { key: "EUL" value: 2 } + cme_mic { key: "EURADC" value: 1 } + cme_mic { key: "EURBPC" value: 1 } + cme_mic { key: "EURBPN" value: 1 } + cme_mic { key: "EURJYC" value: 1 } + cme_mic { key: "EURSFC" value: 1 } + cme_mic { key: "EURUSC" value: 1 } + cme_mic { key: "EURUSD" value: 8 } + cme_mic { key: "EURUSN" value: 1 } + cme_mic { key: "EVC" value: 2 } + cme_mic { key: "EWB" value: 2 } + cme_mic { key: "EWG" value: 2 } + cme_mic { key: "EWN" value: 2 } + cme_mic { key: "EXR" value: 2 } + cme_mic { key: "F1U" value: 3 } + cme_mic { key: "F3" value: 2 } + cme_mic { key: "FAL" value: 2 } + cme_mic { key: "FBD" value: 2 } + cme_mic { key: "FBT" value: 2 } + cme_mic { key: "FCB" value: 2 } + cme_mic { key: "FCN" value: 2 } + cme_mic { key: "FEF" value: 2 } + cme_mic { key: "FEW" value: 2 } + cme_mic { key: "FL" value: 2 } + cme_mic { key: "FLB" value: 2 } + cme_mic { key: "FLP" value: 2 } + cme_mic { key: "FO" value: 2 } + cme_mic { key: "FOA" value: 2 } + cme_mic { key: "FOC" value: 2 } + cme_mic { key: "FOM" value: 2 } + cme_mic { key: "FOR" value: 2 } + cme_mic { key: "FRC" value: 2 } + cme_mic { key: "FRS" value: 2 } + cme_mic { key: "FSF" value: 2 } + cme_mic { key: "FSS" value: 2 } + cme_mic { key: "FT1" value: 1 } + cme_mic { key: "FT5" value: 1 } + cme_mic { key: "FTB" value: 1 } + cme_mic { key: "FTC" value: 1 } + cme_mic { key: "FTL" value: 2 } + cme_mic { key: "FTT" value: 1 } + cme_mic { key: "FTU" value: 1 } + cme_mic { key: "FVB" value: 2 } + cme_mic { key: "FXBP" value: 8 } + cme_mic { key: "FZE" value: 3 } + cme_mic { key: "G0" value: 1 } + cme_mic { key: "G0K" value: 1 } + cme_mic { key: "G0N" value: 1 } + cme_mic { key: "G2" value: 1 } + cme_mic { key: "G2K" value: 1 } + cme_mic { key: "G2N" value: 1 } + cme_mic { key: "GB" value: 4 } + cme_mic { key: "GBA" value: 2 } + cme_mic { key: "GBB" value: 2 } + cme_mic { key: "GBC" value: 4 } + cme_mic { key: "GBPUSC" value: 1 } + cme_mic { key: "GBPUSD" value: 8 } + cme_mic { key: "GBPUSN" value: 1 } + cme_mic { key: "GBR" value: 2 } + cme_mic { key: "GC" value: 4 } + cme_mic { key: "GC7" value: 4 } + cme_mic { key: "GCB" value: 2 } + cme_mic { key: "GCC" value: 2 } + cme_mic { key: "GCD" value: 4 } + cme_mic { key: "GCG" value: 2 } + cme_mic { key: "GCI" value: 2 } + cme_mic { key: "GCK" value: 4 } + cme_mic { key: "GCM" value: 2 } + cme_mic { key: "GCT" value: 4 } + cme_mic { key: "GCU" value: 2 } + cme_mic { key: "GD" value: 1 } + cme_mic { key: "GDH" value: 2 } + cme_mic { key: "GDK" value: 1 } + cme_mic { key: "GDL" value: 2 } + cme_mic { key: "GE" value: 1 } + cme_mic { key: "GES" value: 2 } + cme_mic { key: "GF" value: 1 } + cme_mic { key: "GFC" value: 2 } + cme_mic { key: "GFT" value: 1 } + cme_mic { key: "GHB" value: 2 } + cme_mic { key: "GHS" value: 2 } + cme_mic { key: "GIE" value: 1 } + cme_mic { key: "GKS" value: 2 } + cme_mic { key: "GLB" value: 1 } + cme_mic { key: "GLI" value: 2 } + cme_mic { key: "GNB" value: 2 } + cme_mic { key: "GNF" value: 1 } + cme_mic { key: "GNL" value: 2 } + cme_mic { key: "GNO" value: 2 } + cme_mic { key: "GNS" value: 2 } + cme_mic { key: "GOC" value: 2 } + cme_mic { key: "GPS" value: 4 } + cme_mic { key: "GSI" value: 2 } + cme_mic { key: "GSP" value: 4 } + cme_mic { key: "GSR" value: 4 } + cme_mic { key: "GSW" value: 2 } + cme_mic { key: "GUD" value: 2 } + cme_mic { key: "GY" value: 2 } + cme_mic { key: "GZ" value: 2 } + cme_mic { key: "H0" value: 1 } + cme_mic { key: "H0X" value: 1 } + cme_mic { key: "H0Z" value: 1 } + cme_mic { key: "H1" value: 1 } + cme_mic { key: "H1X" value: 1 } + cme_mic { key: "H1Z" value: 1 } + cme_mic { key: "H2" value: 1 } + cme_mic { key: "H2L" value: 2 } + cme_mic { key: "H2X" value: 1 } + cme_mic { key: "H2Z" value: 1 } + cme_mic { key: "H3" value: 1 } + cme_mic { key: "H3X" value: 1 } + cme_mic { key: "H3Z" value: 1 } + cme_mic { key: "H4" value: 1 } + cme_mic { key: "H4X" value: 1 } + cme_mic { key: "H4Z" value: 1 } + cme_mic { key: "H5" value: 1 } + cme_mic { key: "H5L" value: 2 } + cme_mic { key: "H5X" value: 1 } + cme_mic { key: "H5Z" value: 1 } + cme_mic { key: "HB" value: 2 } + cme_mic { key: "HE" value: 1 } + cme_mic { key: "HET" value: 1 } + cme_mic { key: "HG" value: 4 } + cme_mic { key: "HG0" value: 4 } + cme_mic { key: "HGS" value: 4 } + cme_mic { key: "HGT" value: 4 } + cme_mic { key: "HH" value: 2 } + cme_mic { key: "HHE" value: 2 } + cme_mic { key: "HHT" value: 2 } + cme_mic { key: "HIA" value: 2 } + cme_mic { key: "HIB" value: 2 } + cme_mic { key: "HIL" value: 2 } + cme_mic { key: "HJC" value: 2 } + cme_mic { key: "HO" value: 2 } + cme_mic { key: "HO2" value: 2 } + cme_mic { key: "HO4" value: 2 } + cme_mic { key: "HOA" value: 2 } + cme_mic { key: "HOB" value: 2 } + cme_mic { key: "HOL" value: 2 } + cme_mic { key: "HOS" value: 2 } + cme_mic { key: "HOT" value: 2 } + cme_mic { key: "HP" value: 2 } + cme_mic { key: "HPD" value: 2 } + cme_mic { key: "HPE" value: 2 } + cme_mic { key: "HQ" value: 1 } + cme_mic { key: "HQX" value: 1 } + cme_mic { key: "HQZ" value: 1 } + cme_mic { key: "HRC" value: 2 } + cme_mic { key: "HS" value: 1 } + cme_mic { key: "HSX" value: 1 } + cme_mic { key: "HSZ" value: 1 } + cme_mic { key: "HTA" value: 2 } + cme_mic { key: "HTB" value: 2 } + cme_mic { key: "HTT" value: 2 } + cme_mic { key: "HUF" value: 1 } + cme_mic { key: "HWA" value: 2 } + cme_mic { key: "IBB" value: 1 } + cme_mic { key: "IBE" value: 2 } + cme_mic { key: "IBS" value: 2 } + cme_mic { key: "IBV" value: 1 } + cme_mic { key: "IC" value: 5 } + cme_mic { key: "ICI" value: 2 } + cme_mic { key: "IDL" value: 2 } + cme_mic { key: "IH" value: 5 } + cme_mic { key: "ILB" value: 2 } + cme_mic { key: "ILS" value: 1 } + cme_mic { key: "IN" value: 2 } + cme_mic { key: "IP" value: 5 } + cme_mic { key: "IPO" value: 1 } + cme_mic { key: "IPT" value: 1 } + cme_mic { key: "IS" value: 5 } + cme_mic { key: "ITB" value: 2 } + cme_mic { key: "ITP" value: 2 } + cme_mic { key: "IW" value: 5 } + cme_mic { key: "J4L" value: 2 } + cme_mic { key: "J7" value: 1 } + cme_mic { key: "JA" value: 2 } + cme_mic { key: "JDL" value: 2 } + cme_mic { key: "JE" value: 2 } + cme_mic { key: "JET" value: 2 } + cme_mic { key: "JFB" value: 2 } + cme_mic { key: "JFC" value: 2 } + cme_mic { key: "JKM" value: 2 } + cme_mic { key: "JML" value: 2 } + cme_mic { key: "JNC" value: 2 } + cme_mic { key: "JNG" value: 2 } + cme_mic { key: "JNL" value: 2 } + cme_mic { key: "JPP" value: 2 } + cme_mic { key: "JQ" value: 1 } + cme_mic { key: "JTB" value: 2 } + cme_mic { key: "K0" value: 1 } + cme_mic { key: "K0K" value: 1 } + cme_mic { key: "K0N" value: 1 } + cme_mic { key: "K1" value: 1 } + cme_mic { key: "K1K" value: 1 } + cme_mic { key: "K1N" value: 1 } + cme_mic { key: "K2" value: 1 } + cme_mic { key: "K2K" value: 1 } + cme_mic { key: "K2L" value: 2 } + cme_mic { key: "K2N" value: 1 } + cme_mic { key: "K3" value: 1 } + cme_mic { key: "K3K" value: 1 } + cme_mic { key: "K3L" value: 2 } + cme_mic { key: "K3N" value: 1 } + cme_mic { key: "K4" value: 1 } + cme_mic { key: "K4K" value: 1 } + cme_mic { key: "K4L" value: 2 } + cme_mic { key: "K4N" value: 1 } + cme_mic { key: "K5" value: 1 } + cme_mic { key: "K5K" value: 1 } + cme_mic { key: "K5N" value: 1 } + cme_mic { key: "KE" value: 3 } + cme_mic { key: "KET" value: 3 } + cme_mic { key: "KQ" value: 1 } + cme_mic { key: "KQK" value: 1 } + cme_mic { key: "KQN" value: 1 } + cme_mic { key: "KRW" value: 1 } + cme_mic { key: "KS" value: 1 } + cme_mic { key: "KSK" value: 1 } + cme_mic { key: "KSN" value: 1 } + cme_mic { key: "KT" value: 2 } + cme_mic { key: "L3L" value: 2 } + cme_mic { key: "LAF" value: 2 } + cme_mic { key: "LAP" value: 2 } + cme_mic { key: "LAV" value: 1 } + cme_mic { key: "LAX" value: 1 } + cme_mic { key: "LBB" value: 2 } + cme_mic { key: "LBS" value: 1 } + cme_mic { key: "LCF" value: 2 } + cme_mic { key: "LE" value: 1 } + cme_mic { key: "LED" value: 4 } + cme_mic { key: "LEL" value: 2 } + cme_mic { key: "LET" value: 1 } + cme_mic { key: "LHV" value: 2 } + cme_mic { key: "LL" value: 2 } + cme_mic { key: "LLR" value: 2 } + cme_mic { key: "LPE" value: 2 } + cme_mic { key: "LPS" value: 2 } + cme_mic { key: "LSF" value: 4 } + cme_mic { key: "LSW" value: 2 } + cme_mic { key: "LT" value: 2 } + cme_mic { key: "LWB" value: 2 } + cme_mic { key: "M5F" value: 2 } + cme_mic { key: "M6A" value: 1 } + cme_mic { key: "M6B" value: 1 } + cme_mic { key: "M6E" value: 1 } + cme_mic { key: "MAA" value: 2 } + cme_mic { key: "MAB" value: 2 } + cme_mic { key: "MAC" value: 2 } + cme_mic { key: "MAE" value: 2 } + cme_mic { key: "MAF" value: 2 } + cme_mic { key: "MAS" value: 2 } + cme_mic { key: "MB" value: 2 } + cme_mic { key: "MBB" value: 2 } + cme_mic { key: "MBC" value: 2 } + cme_mic { key: "MBE" value: 2 } + cme_mic { key: "MBL" value: 2 } + cme_mic { key: "MBM" value: 2 } + cme_mic { key: "MBR" value: 2 } + cme_mic { key: "MBS" value: 2 } + cme_mic { key: "MCC" value: 2 } + cme_mic { key: "MCD" value: 1 } + cme_mic { key: "MCT" value: 2 } + cme_mic { key: "MCX" value: 3 } + cme_mic { key: "MDB" value: 2 } + cme_mic { key: "MDD" value: 2 } + cme_mic { key: "MDL" value: 2 } + cme_mic { key: "MDM" value: 2 } + cme_mic { key: "MDR" value: 2 } + cme_mic { key: "ME" value: 2 } + cme_mic { key: "MEB" value: 2 } + cme_mic { key: "MEE" value: 2 } + cme_mic { key: "MEF" value: 2 } + cme_mic { key: "MEL" value: 2 } + cme_mic { key: "MEO" value: 2 } + cme_mic { key: "MEW" value: 2 } + cme_mic { key: "MFB" value: 2 } + cme_mic { key: "MFD" value: 2 } + cme_mic { key: "MFF" value: 2 } + cme_mic { key: "MFP" value: 2 } + cme_mic { key: "MFR" value: 2 } + cme_mic { key: "MGB" value: 2 } + cme_mic { key: "MGC" value: 4 } + cme_mic { key: "MGF" value: 2 } + cme_mic { key: "MGH" value: 2 } + cme_mic { key: "MGN" value: 2 } + cme_mic { key: "MGO" value: 2 } + cme_mic { key: "MGS" value: 2 } + cme_mic { key: "MH" value: 2 } + cme_mic { key: "MHE" value: 2 } + cme_mic { key: "MHS" value: 2 } + cme_mic { key: "MIA" value: 1 } + cme_mic { key: "MIP" value: 2 } + cme_mic { key: "MIR" value: 1 } + cme_mic { key: "MJB" value: 2 } + cme_mic { key: "MJC" value: 2 } + cme_mic { key: "MJN" value: 2 } + cme_mic { key: "MJP" value: 4 } + cme_mic { key: "MJY" value: 1 } + cme_mic { key: "MKC" value: 3 } + cme_mic { key: "MLD" value: 2 } + cme_mic { key: "MM" value: 2 } + cme_mic { key: "MME" value: 2 } + cme_mic { key: "MMF" value: 2 } + cme_mic { key: "MMO" value: 2 } + cme_mic { key: "MMP" value: 2 } + cme_mic { key: "MMR" value: 2 } + cme_mic { key: "MNB" value: 2 } + cme_mic { key: "MNC" value: 2 } + cme_mic { key: "MNE" value: 2 } + cme_mic { key: "MNG" value: 2 } + cme_mic { key: "MNH" value: 1 } + cme_mic { key: "MNS" value: 2 } + cme_mic { key: "MNT" value: 2 } + cme_mic { key: "MO" value: 2 } + cme_mic { key: "MOA" value: 2 } + cme_mic { key: "MOI" value: 2 } + cme_mic { key: "MPA" value: 2 } + cme_mic { key: "MPE" value: 2 } + cme_mic { key: "MPP" value: 2 } + cme_mic { key: "MPS" value: 2 } + cme_mic { key: "MPX" value: 2 } + cme_mic { key: "MQ" value: 2 } + cme_mic { key: "MRI" value: 2 } + cme_mic { key: "MRT" value: 2 } + cme_mic { key: "MSB" value: 2 } + cme_mic { key: "MSD" value: 2 } + cme_mic { key: "MSE" value: 2 } + cme_mic { key: "MSF" value: 1 } + cme_mic { key: "MSG" value: 2 } + cme_mic { key: "MT2" value: 2 } + cme_mic { key: "MTB" value: 2 } + cme_mic { key: "MTF" value: 2 } + cme_mic { key: "MTI" value: 2 } + cme_mic { key: "MTS" value: 2 } + cme_mic { key: "MUD" value: 2 } + cme_mic { key: "MWE" value: 5 } + cme_mic { key: "MX" value: 2 } + cme_mic { key: "MXB" value: 2 } + cme_mic { key: "MXL" value: 2 } + cme_mic { key: "MXR" value: 2 } + cme_mic { key: "N1B" value: 2 } + cme_mic { key: "N1U" value: 3 } + cme_mic { key: "N3L" value: 2 } + cme_mic { key: "N3P" value: 2 } + cme_mic { key: "N9L" value: 2 } + cme_mic { key: "NBB" value: 2 } + cme_mic { key: "NBD" value: 2 } + cme_mic { key: "NBP" value: 2 } + cme_mic { key: "NCC" value: 2 } + cme_mic { key: "NCD" value: 2 } + cme_mic { key: "NCF" value: 2 } + cme_mic { key: "NCO" value: 2 } + cme_mic { key: "NCP" value: 2 } + cme_mic { key: "NEN" value: 2 } + cme_mic { key: "NEO" value: 2 } + cme_mic { key: "NEP" value: 2 } + cme_mic { key: "NFC" value: 2 } + cme_mic { key: "NFD" value: 2 } + cme_mic { key: "NFG" value: 2 } + cme_mic { key: "NFN" value: 2 } + cme_mic { key: "NFO" value: 2 } + cme_mic { key: "NG" value: 2 } + cme_mic { key: "NGT" value: 2 } + cme_mic { key: "NHH" value: 2 } + cme_mic { key: "NHN" value: 2 } + cme_mic { key: "NHO" value: 2 } + cme_mic { key: "NHP" value: 2 } + cme_mic { key: "NIY" value: 1 } + cme_mic { key: "NJ" value: 2 } + cme_mic { key: "NKD" value: 1 } + cme_mic { key: "NKN" value: 2 } + cme_mic { key: "NL" value: 2 } + cme_mic { key: "NLS" value: 2 } + cme_mic { key: "NMO" value: 2 } + cme_mic { key: "NMP" value: 2 } + cme_mic { key: "NN" value: 2 } + cme_mic { key: "NNE" value: 2 } + cme_mic { key: "NNP" value: 2 } + cme_mic { key: "NNT" value: 2 } + cme_mic { key: "NOD" value: 2 } + cme_mic { key: "NOI" value: 2 } + cme_mic { key: "NOK" value: 1 } + cme_mic { key: "NOO" value: 2 } + cme_mic { key: "NPG" value: 2 } + cme_mic { key: "NQ" value: 1 } + cme_mic { key: "NQT" value: 1 } + cme_mic { key: "NR" value: 2 } + cme_mic { key: "NRO" value: 2 } + cme_mic { key: "NRP" value: 2 } + cme_mic { key: "NRR" value: 2 } + cme_mic { key: "NSO" value: 2 } + cme_mic { key: "NSP" value: 2 } + cme_mic { key: "NTP" value: 2 } + cme_mic { key: "NW" value: 2 } + cme_mic { key: "NWO" value: 2 } + cme_mic { key: "NWP" value: 2 } + cme_mic { key: "NX" value: 2 } + cme_mic { key: "NY" value: 2 } + cme_mic { key: "NYA" value: 2 } + cme_mic { key: "NYB" value: 2 } + cme_mic { key: "NYF" value: 2 } + cme_mic { key: "NYG" value: 2 } + cme_mic { key: "NYH" value: 2 } + cme_mic { key: "NYM" value: 1 } + cme_mic { key: "NYP" value: 2 } + cme_mic { key: "NYR" value: 2 } + cme_mic { key: "NYS" value: 2 } + cme_mic { key: "NYT" value: 2 } + cme_mic { key: "NZDUSC" value: 1 } + cme_mic { key: "NZN" value: 2 } + cme_mic { key: "OAD" value: 2 } + cme_mic { key: "ODO" value: 6 } + cme_mic { key: "OFF" value: 2 } + cme_mic { key: "OML" value: 2 } + cme_mic { key: "OMM" value: 2 } + cme_mic { key: "OOD" value: 2 } + cme_mic { key: "OPF" value: 1 } + cme_mic { key: "OPO" value: 2 } + cme_mic { key: "OPS" value: 1 } + cme_mic { key: "OQ1" value: 6 } + cme_mic { key: "OQB" value: 6 } + cme_mic { key: "OQD" value: 6 } + cme_mic { key: "OVL" value: 2 } + cme_mic { key: "PA" value: 2 } + cme_mic { key: "PAL" value: 2 } + cme_mic { key: "PBO" value: 2 } + cme_mic { key: "PBP" value: 2 } + cme_mic { key: "PCO" value: 2 } + cme_mic { key: "PCP" value: 2 } + cme_mic { key: "PD" value: 2 } + cme_mic { key: "PDJ" value: 2 } + cme_mic { key: "PDL" value: 2 } + cme_mic { key: "PE" value: 2 } + cme_mic { key: "PEL" value: 2 } + cme_mic { key: "PEX" value: 2 } + cme_mic { key: "PF" value: 2 } + cme_mic { key: "PFO" value: 2 } + cme_mic { key: "PFP" value: 2 } + cme_mic { key: "PGG" value: 2 } + cme_mic { key: "PGN" value: 2 } + cme_mic { key: "PH" value: 2 } + cme_mic { key: "PHF" value: 2 } + cme_mic { key: "PHO" value: 2 } + cme_mic { key: "PHP" value: 2 } + cme_mic { key: "PIO" value: 2 } + cme_mic { key: "PJY" value: 1 } + cme_mic { key: "PKP" value: 2 } + cme_mic { key: "PL" value: 2 } + cme_mic { key: "PLN" value: 1 } + cme_mic { key: "PLO" value: 2 } + cme_mic { key: "PLP" value: 2 } + cme_mic { key: "PLT" value: 2 } + cme_mic { key: "PM" value: 2 } + cme_mic { key: "PMF" value: 2 } + cme_mic { key: "PMO" value: 2 } + cme_mic { key: "PMP" value: 2 } + cme_mic { key: "PNF" value: 2 } + cme_mic { key: "PNL" value: 2 } + cme_mic { key: "POC" value: 2 } + cme_mic { key: "POG" value: 1 } + cme_mic { key: "POL" value: 2 } + cme_mic { key: "PPL" value: 2 } + cme_mic { key: "PPP" value: 2 } + cme_mic { key: "PPS" value: 2 } + cme_mic { key: "PPW" value: 2 } + cme_mic { key: "PQO" value: 2 } + cme_mic { key: "PQP" value: 2 } + cme_mic { key: "PRO" value: 2 } + cme_mic { key: "PRP" value: 2 } + cme_mic { key: "PSF" value: 1 } + cme_mic { key: "PSO" value: 2 } + cme_mic { key: "PSP" value: 2 } + cme_mic { key: "PTL" value: 2 } + cme_mic { key: "PTO" value: 2 } + cme_mic { key: "PUO" value: 2 } + cme_mic { key: "PUP" value: 2 } + cme_mic { key: "PVL" value: 2 } + cme_mic { key: "PVO" value: 2 } + cme_mic { key: "PVP" value: 2 } + cme_mic { key: "PW" value: 2 } + cme_mic { key: "PWL" value: 2 } + cme_mic { key: "PXO" value: 2 } + cme_mic { key: "PXP" value: 2 } + cme_mic { key: "PYO" value: 2 } + cme_mic { key: "PYP" value: 2 } + cme_mic { key: "PZO" value: 2 } + cme_mic { key: "PZP" value: 2 } + cme_mic { key: "QC" value: 4 } + cme_mic { key: "QCN" value: 1 } + cme_mic { key: "QG" value: 2 } + cme_mic { key: "QH" value: 2 } + cme_mic { key: "QI" value: 4 } + cme_mic { key: "QM" value: 2 } + cme_mic { key: "QO" value: 4 } + cme_mic { key: "QP" value: 2 } + cme_mic { key: "QU" value: 2 } + cme_mic { key: "QVL" value: 2 } + cme_mic { key: "QXB" value: 2 } + cme_mic { key: "R1T" value: 1 } + cme_mic { key: "R2G" value: 1 } + cme_mic { key: "R2V" value: 1 } + cme_mic { key: "R7L" value: 2 } + cme_mic { key: "RB" value: 2 } + cme_mic { key: "RBB" value: 2 } + cme_mic { key: "RBF" value: 2 } + cme_mic { key: "RBG" value: 2 } + cme_mic { key: "RBL" value: 2 } + cme_mic { key: "RBS" value: 2 } + cme_mic { key: "RBT" value: 2 } + cme_mic { key: "RE" value: 2 } + cme_mic { key: "REB" value: 2 } + cme_mic { key: "REX" value: 3 } + cme_mic { key: "RF" value: 1 } + cme_mic { key: "RGF" value: 2 } + cme_mic { key: "RGI" value: 2 } + cme_mic { key: "RGT" value: 1 } + cme_mic { key: "RHB" value: 2 } + cme_mic { key: "RHS" value: 2 } + cme_mic { key: "RKA" value: 2 } + cme_mic { key: "RLT" value: 1 } + cme_mic { key: "RLX" value: 2 } + cme_mic { key: "RMB" value: 1 } + cme_mic { key: "RME" value: 1 } + cme_mic { key: "RP" value: 1 } + cme_mic { key: "RRE" value: 1 } + cme_mic { key: "RS1" value: 1 } + cme_mic { key: "RSG" value: 1 } + cme_mic { key: "RSV" value: 1 } + cme_mic { key: "RT" value: 2 } + cme_mic { key: "RTY" value: 1 } + cme_mic { key: "RVR" value: 2 } + cme_mic { key: "RVT" value: 1 } + cme_mic { key: "RX" value: 3 } + cme_mic { key: "RY" value: 1 } + cme_mic { key: "RYA" value: 2 } + cme_mic { key: "S1U" value: 3 } + cme_mic { key: "SBT" value: 3 } + cme_mic { key: "SD" value: 2 } + cme_mic { key: "SDA" value: 1 } + cme_mic { key: "SDD" value: 2 } + cme_mic { key: "SDG" value: 1 } + cme_mic { key: "SDI" value: 1 } + cme_mic { key: "SDM" value: 2 } + cme_mic { key: "SE" value: 2 } + cme_mic { key: "SE2" value: 1 } + cme_mic { key: "SE3" value: 1 } + cme_mic { key: "SEK" value: 1 } + cme_mic { key: "SES" value: 1 } + cme_mic { key: "SF1" value: 2 } + cme_mic { key: "SF3" value: 2 } + cme_mic { key: "SFB" value: 2 } + cme_mic { key: "SFR" value: 1 } + cme_mic { key: "SG" value: 1 } + cme_mic { key: "SGB" value: 2 } + cme_mic { key: "SGE" value: 2 } + cme_mic { key: "SGF" value: 2 } + cme_mic { key: "SGG" value: 2 } + cme_mic { key: "SGT" value: 1 } + cme_mic { key: "SI" value: 4 } + cme_mic { key: "SIL" value: 4 } + cme_mic { key: "SIR" value: 1 } + cme_mic { key: "SIT" value: 4 } + cme_mic { key: "SJY" value: 1 } + cme_mic { key: "SLP" value: 1 } + cme_mic { key: "SLT" value: 1 } + cme_mic { key: "SMC" value: 1 } + cme_mic { key: "SMT" value: 1 } + cme_mic { key: "SMU" value: 2 } + cme_mic { key: "SMW" value: 2 } + cme_mic { key: "SP" value: 1 } + cme_mic { key: "SR1" value: 1 } + cme_mic { key: "SR3" value: 1 } + cme_mic { key: "SRT" value: 2 } + cme_mic { key: "SSI" value: 2 } + cme_mic { key: "SSP" value: 4 } + cme_mic { key: "SSW" value: 2 } + cme_mic { key: "STI" value: 2 } + cme_mic { key: "STR" value: 2 } + cme_mic { key: "STS" value: 2 } + cme_mic { key: "STT" value: 2 } + cme_mic { key: "STY" value: 2 } + cme_mic { key: "SU" value: 1 } + cme_mic { key: "SUT" value: 1 } + cme_mic { key: "T04" value: 2 } + cme_mic { key: "T05" value: 2 } + cme_mic { key: "T06" value: 2 } + cme_mic { key: "T07" value: 2 } + cme_mic { key: "T10" value: 2 } + cme_mic { key: "T11" value: 2 } + cme_mic { key: "T12" value: 2 } + cme_mic { key: "T13" value: 2 } + cme_mic { key: "T14" value: 2 } + cme_mic { key: "T17" value: 2 } + cme_mic { key: "T18" value: 2 } + cme_mic { key: "T19" value: 2 } + cme_mic { key: "T1U" value: 3 } + cme_mic { key: "T27" value: 2 } + cme_mic { key: "T28" value: 2 } + cme_mic { key: "T29" value: 2 } + cme_mic { key: "T2B" value: 2 } + cme_mic { key: "T2D" value: 2 } + cme_mic { key: "T2M" value: 2 } + cme_mic { key: "T30" value: 2 } + cme_mic { key: "T31" value: 2 } + cme_mic { key: "T3B" value: 2 } + cme_mic { key: "T3L" value: 2 } + cme_mic { key: "T4B" value: 2 } + cme_mic { key: "T4D" value: 2 } + cme_mic { key: "T5B" value: 2 } + cme_mic { key: "T5C" value: 2 } + cme_mic { key: "T6B" value: 2 } + cme_mic { key: "T7K" value: 2 } + cme_mic { key: "T8B" value: 2 } + cme_mic { key: "T9B" value: 2 } + cme_mic { key: "TB2" value: 2 } + cme_mic { key: "TC" value: 2 } + cme_mic { key: "TC6" value: 2 } + cme_mic { key: "TC9" value: 2 } + cme_mic { key: "TD3" value: 2 } + cme_mic { key: "TD8" value: 2 } + cme_mic { key: "TGT" value: 1 } + cme_mic { key: "TH" value: 2 } + cme_mic { key: "THB" value: 2 } + cme_mic { key: "THD" value: 2 } + cme_mic { key: "TIC" value: 2 } + cme_mic { key: "TIO" value: 2 } + cme_mic { key: "TK" value: 2 } + cme_mic { key: "TKB" value: 2 } + cme_mic { key: "TL" value: 2 } + cme_mic { key: "TLB" value: 2 } + cme_mic { key: "TLD" value: 2 } + cme_mic { key: "TM" value: 2 } + cme_mic { key: "TMB" value: 2 } + cme_mic { key: "TMD" value: 2 } + cme_mic { key: "TMT" value: 1 } + cme_mic { key: "TN" value: 3 } + cme_mic { key: "TPB" value: 1 } + cme_mic { key: "TPY" value: 1 } + cme_mic { key: "TRB" value: 1 } + cme_mic { key: "TRI" value: 1 } + cme_mic { key: "TRL" value: 1 } + cme_mic { key: "TRM" value: 1 } + cme_mic { key: "TRY" value: 1 } + cme_mic { key: "TRZ" value: 2 } + cme_mic { key: "TST" value: 1 } + cme_mic { key: "TT" value: 2 } + cme_mic { key: "TTD" value: 2 } + cme_mic { key: "TTE" value: 2 } + cme_mic { key: "TTF" value: 2 } + cme_mic { key: "TW" value: 2 } + cme_mic { key: "TXN" value: 2 } + cme_mic { key: "TZ6" value: 2 } + cme_mic { key: "U7" value: 2 } + cme_mic { key: "U9" value: 2 } + cme_mic { key: "UA" value: 2 } + cme_mic { key: "UB" value: 3 } + cme_mic { key: "UCM" value: 2 } + cme_mic { key: "UDL" value: 2 } + cme_mic { key: "UFG" value: 3 } + cme_mic { key: "UFN" value: 3 } + cme_mic { key: "UFU" value: 3 } + cme_mic { key: "UFZ" value: 3 } + cme_mic { key: "UGG" value: 2 } + cme_mic { key: "UKG" value: 2 } + cme_mic { key: "ULB" value: 2 } + cme_mic { key: "UMB" value: 2 } + cme_mic { key: "UMD" value: 2 } + cme_mic { key: "UME" value: 3 } + cme_mic { key: "UN" value: 2 } + cme_mic { key: "UNB" value: 2 } + cme_mic { key: "UNS" value: 2 } + cme_mic { key: "UR" value: 2 } + cme_mic { key: "USDBRL" value: 1 } + cme_mic { key: "USDCAC" value: 1 } + cme_mic { key: "USDCAD" value: 8 } + cme_mic { key: "USDCAN" value: 1 } + cme_mic { key: "USDCKC" value: 1 } + cme_mic { key: "USDCLP" value: 1 } + cme_mic { key: "USDCNY" value: 1 } + cme_mic { key: "USDCOP" value: 1 } + cme_mic { key: "USDDKC" value: 1 } + cme_mic { key: "USDHFC" value: 1 } + cme_mic { key: "USDHKC" value: 1 } + cme_mic { key: "USDIDR" value: 1 } + cme_mic { key: "USDINR" value: 1 } + cme_mic { key: "USDISC" value: 1 } + cme_mic { key: "USDJPY" value: 8 } + cme_mic { key: "USDJYC" value: 1 } + cme_mic { key: "USDJYN" value: 1 } + cme_mic { key: "USDKRW" value: 1 } + cme_mic { key: "USDMPC" value: 1 } + cme_mic { key: "USDMXN" value: 8 } + cme_mic { key: "USDNKC" value: 1 } + cme_mic { key: "USDPEN" value: 1 } + cme_mic { key: "USDPHP" value: 1 } + cme_mic { key: "USDPZC" value: 1 } + cme_mic { key: "USDRUB" value: 1 } + cme_mic { key: "USDSDC" value: 1 } + cme_mic { key: "USDSFC" value: 1 } + cme_mic { key: "USDSFN" value: 1 } + cme_mic { key: "USDSKC" value: 1 } + cme_mic { key: "USDTBC" value: 1 } + cme_mic { key: "USDTLC" value: 1 } + cme_mic { key: "USDTWD" value: 1 } + cme_mic { key: "USDZRC" value: 1 } + cme_mic { key: "UV" value: 2 } + cme_mic { key: "UX" value: 2 } + cme_mic { key: "V3L" value: 2 } + cme_mic { key: "V7" value: 2 } + cme_mic { key: "VDL" value: 2 } + cme_mic { key: "VLD" value: 2 } + cme_mic { key: "VR" value: 2 } + cme_mic { key: "VV" value: 2 } + cme_mic { key: "W0" value: 2 } + cme_mic { key: "WBR" value: 2 } + cme_mic { key: "WCC" value: 2 } + cme_mic { key: "WCE" value: 2 } + cme_mic { key: "WCW" value: 2 } + cme_mic { key: "WDB" value: 2 } + cme_mic { key: "WDC" value: 1 } + cme_mic { key: "WHB" value: 2 } + cme_mic { key: "WHD" value: 2 } + cme_mic { key: "WMB" value: 2 } + cme_mic { key: "WMD" value: 2 } + cme_mic { key: "WMK" value: 3 } + cme_mic { key: "WMR" value: 2 } + cme_mic { key: "WNC" value: 2 } + cme_mic { key: "WNT" value: 2 } + cme_mic { key: "WOL" value: 2 } + cme_mic { key: "WPL" value: 2 } + cme_mic { key: "WS" value: 2 } + cme_mic { key: "WTA" value: 2 } + cme_mic { key: "WTD" value: 2 } + cme_mic { key: "WTI" value: 2 } + cme_mic { key: "WTS" value: 2 } + cme_mic { key: "WTT" value: 2 } + cme_mic { key: "X0" value: 2 } + cme_mic { key: "X6" value: 2 } + cme_mic { key: "X7" value: 2 } + cme_mic { key: "X9" value: 2 } + cme_mic { key: "XAB" value: 1 } + cme_mic { key: "XAE" value: 1 } + cme_mic { key: "XAF" value: 1 } + cme_mic { key: "XAI" value: 1 } + cme_mic { key: "XAK" value: 1 } + cme_mic { key: "XAP" value: 1 } + cme_mic { key: "XAR" value: 1 } + cme_mic { key: "XAU" value: 1 } + cme_mic { key: "XAV" value: 1 } + cme_mic { key: "XAY" value: 1 } + cme_mic { key: "XAZ" value: 1 } + cme_mic { key: "XBT" value: 1 } + cme_mic { key: "XC" value: 3 } + cme_mic { key: "XER" value: 2 } + cme_mic { key: "XET" value: 1 } + cme_mic { key: "XFT" value: 1 } + cme_mic { key: "XH" value: 2 } + cme_mic { key: "XIT" value: 1 } + cme_mic { key: "XK" value: 3 } + cme_mic { key: "XKT" value: 1 } + cme_mic { key: "XPP" value: 2 } + cme_mic { key: "XPT" value: 1 } + cme_mic { key: "XR" value: 2 } + cme_mic { key: "XRI" value: 1 } + cme_mic { key: "XRT" value: 1 } + cme_mic { key: "XUT" value: 1 } + cme_mic { key: "XVT" value: 1 } + cme_mic { key: "XW" value: 3 } + cme_mic { key: "XX" value: 2 } + cme_mic { key: "XYT" value: 1 } + cme_mic { key: "XZI" value: 1 } + cme_mic { key: "XZT" value: 1 } + cme_mic { key: "YF" value: 2 } + cme_mic { key: "YHE" value: 2 } + cme_mic { key: "YHF" value: 2 } + cme_mic { key: "YM" value: 3 } + cme_mic { key: "YMT" value: 3 } + cme_mic { key: "YNO" value: 2 } + cme_mic { key: "YO" value: 2 } + cme_mic { key: "YRP" value: 2 } + cme_mic { key: "YRW" value: 2 } + cme_mic { key: "YS" value: 2 } + cme_mic { key: "YUE" value: 2 } + cme_mic { key: "YWE" value: 2 } + cme_mic { key: "YWF" value: 2 } + cme_mic { key: "YWK" value: 2 } + cme_mic { key: "Z00000" value: 1 } + cme_mic { key: "Z00001" value: 1 } + cme_mic { key: "Z00002" value: 1 } + cme_mic { key: "Z1B" value: 2 } + cme_mic { key: "Z3N" value: 3 } + cme_mic { key: "Z4" value: 2 } + cme_mic { key: "Z6" value: 2 } + cme_mic { key: "ZA9102" value: 1 } + cme_mic { key: "ZA9202" value: 1 } + cme_mic { key: "ZA9903" value: 1 } + cme_mic { key: "ZA9906" value: 1 } + cme_mic { key: "ZA9909" value: 1 } + cme_mic { key: "ZA9912" value: 1 } + cme_mic { key: "ZAL" value: 2 } + cme_mic { key: "ZAR" value: 1 } + cme_mic { key: "ZB" value: 3 } + cme_mic { key: "ZB0001" value: 1 } + cme_mic { key: "ZB0002" value: 1 } + cme_mic { key: "ZB0003" value: 1 } + cme_mic { key: "ZB0038" value: 1 } + cme_mic { key: "ZB0039" value: 1 } + cme_mic { key: "ZB0040" value: 1 } + cme_mic { key: "ZB9103" value: 1 } + cme_mic { key: "ZB9104" value: 1 } + cme_mic { key: "ZB9105" value: 1 } + cme_mic { key: "ZB9203" value: 1 } + cme_mic { key: "ZB9204" value: 1 } + cme_mic { key: "ZB9205" value: 1 } + cme_mic { key: "ZB9903" value: 1 } + cme_mic { key: "ZB9906" value: 1 } + cme_mic { key: "ZB9909" value: 1 } + cme_mic { key: "ZB9912" value: 1 } + cme_mic { key: "ZC" value: 3 } + cme_mic { key: "ZC0001" value: 1 } + cme_mic { key: "ZC0002" value: 1 } + cme_mic { key: "ZC0003" value: 1 } + cme_mic { key: "ZC0004" value: 1 } + cme_mic { key: "ZC0005" value: 1 } + cme_mic { key: "ZC0029" value: 1 } + cme_mic { key: "ZC0030" value: 1 } + cme_mic { key: "ZC9107" value: 1 } + cme_mic { key: "ZC9110" value: 1 } + cme_mic { key: "ZC9207" value: 1 } + cme_mic { key: "ZC9210" value: 1 } + cme_mic { key: "ZC9903" value: 1 } + cme_mic { key: "ZC9906" value: 1 } + cme_mic { key: "ZC9909" value: 1 } + cme_mic { key: "ZC9912" value: 1 } + cme_mic { key: "ZCT" value: 3 } + cme_mic { key: "ZD0030" value: 1 } + cme_mic { key: "ZD9112" value: 1 } + cme_mic { key: "ZD9115" value: 1 } + cme_mic { key: "ZD9120" value: 1 } + cme_mic { key: "ZD9130" value: 1 } + cme_mic { key: "ZD9212" value: 1 } + cme_mic { key: "ZD9215" value: 1 } + cme_mic { key: "ZD9220" value: 1 } + cme_mic { key: "ZD9230" value: 1 } + cme_mic { key: "ZD9803" value: 1 } + cme_mic { key: "ZD9806" value: 1 } + cme_mic { key: "ZD9809" value: 1 } + cme_mic { key: "ZD9812" value: 1 } + cme_mic { key: "ZD9903" value: 1 } + cme_mic { key: "ZD9906" value: 1 } + cme_mic { key: "ZD9909" value: 1 } + cme_mic { key: "ZD9912" value: 1 } + cme_mic { key: "ZF" value: 3 } + cme_mic { key: "ZGD" value: 6 } + cme_mic { key: "ZGL" value: 2 } + cme_mic { key: "ZJL" value: 2 } + cme_mic { key: "ZKU" value: 2 } + cme_mic { key: "ZL" value: 3 } + cme_mic { key: "ZLT" value: 3 } + cme_mic { key: "ZM" value: 3 } + cme_mic { key: "ZMT" value: 3 } + cme_mic { key: "ZN" value: 3 } + cme_mic { key: "ZNC" value: 4 } + cme_mic { key: "ZO" value: 3 } + cme_mic { key: "ZQ" value: 3 } + cme_mic { key: "ZR" value: 3 } + cme_mic { key: "ZS" value: 3 } + cme_mic { key: "ZT" value: 3 } + cme_mic { key: "ZW" value: 3 } + cme_mic { key: "ZWT" value: 3 } + } } diff --git a/tools/cpp_helper/Generate-Cpps-reqs.ps1 b/tools/cpp_helper/Generate-Cpps-reqs.ps1 index 07d324706..d0f3c03ec 100644 --- a/tools/cpp_helper/Generate-Cpps-reqs.ps1 +++ b/tools/cpp_helper/Generate-Cpps-reqs.ps1 @@ -1,3 +1,15 @@ +# This should mirror the one found in Security.proto +$cme_mics = @{ + "CME" = 1 + "NYMEX" = 2 + "CBT" = 3 + "COMEX" = 4 + "MGE" = 5 + "DME" = 6 + "NYMSW" = 7 + "FXS" = 8 +} + $known_good = @{ "IC" = 50 "IH" = 50 diff --git a/tools/cpp_helper/Generate-Cpps.ps1 b/tools/cpp_helper/Generate-Cpps.ps1 index a7a71a1fe..1d0726610 100644 --- a/tools/cpp_helper/Generate-Cpps.ps1 +++ b/tools/cpp_helper/Generate-Cpps.ps1 @@ -3,7 +3,7 @@ $cme_file_url = "ftp://ftp.cmegroup.com/pub/fprf/csv/cmeg.fut.prf.csv" $csv_save_path = ".\cmeg.fut.prf.csv" -$output_file = ".\cpp_config.conf" +$output_file = ".\cme_reference_data.conf" # turn off console update progress while downloading $CurrentProgressPref = $ProgressPreference; @@ -13,11 +13,18 @@ $ProgressPreference = $CurrentProgressPref $a = Import-Csv $csv_save_path $b = $a | select Sym, Mult | Group Sym | Sort Name +$mic_grouping = $a | select Sym, Exch | Group Sym | Sort Name +# Automated header disclaimer @" -# Generated by automated script. DO NOT EDIT! +# +# Entire file generated by automated script. DO NOT EDIT! # source: tools/cpp_helper/generate-cpps.ps1 +# + "@ | out-file -Encoding ascii $output_file + +# cpps "CppConfig`n{`n`tvalue`n`t{" | out-file -Encoding ascii $output_file -Append $b | % { if ($known_good.ContainsKey($_.Group[0].Sym)) { @@ -27,9 +34,18 @@ $b | % { } "`t`tcpp_entry {{ key: `"{0}`" value: {1} }}" -f $_.Group[0].Sym, $cpp } ` | out-file -Encoding ascii $output_file -Append +"`t}`n}`n" | out-file -Encoding ascii $output_file -Append + +# mics +"CmeSymbolMics`n{`n`tvalue`n`t{" | out-file -Encoding ascii $output_file -Append +$mic_grouping | % { + "`t`tcme_mic {{ key: `"{0}`" value: {1} }}" -f $_.Group[0].Sym, $cme_mics[$_.Group[0].Exch] } ` + | out-file -Encoding ascii $output_file -Append "`t}`n}" | out-file -Encoding ascii $output_file -Append + + Remove-Item $csv_save_path -Force # You can now copy the output file to defaults assuming all looks good -# Copy-Item .\cpp_config.conf ..\..\src\lib\config\defaults\ -Force -# Remove-Item cpp_config.conf \ No newline at end of file +# Copy-Item .\cme_reference_data.conf ..\..\src\lib\config\defaults\ -Force +# Remove-Item cme_reference_data.conf \ No newline at end of file From 0a81521e6e81d16d6f52b443c39369f849d22307 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Wed, 5 Sep 2018 13:56:27 -0500 Subject: [PATCH 019/203] changing to Taha's proto, not int32, silly man --- src/lib/config/protos/cme_reference_data.proto | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/config/protos/cme_reference_data.proto b/src/lib/config/protos/cme_reference_data.proto index 84c0f184f..4aba79684 100644 --- a/src/lib/config/protos/cme_reference_data.proto +++ b/src/lib/config/protos/cme_reference_data.proto @@ -1,10 +1,11 @@ syntax='proto2'; package bts.config; +import "lib/domain_model/Security.proto"; message CppConfig { map cpp_entry = 1; } message CmeSymbolMics { - map cme_mic = 1; + map cme_mic = 1; } \ No newline at end of file From efac4a6077d4ad6ae90f989d55b2c2283c327edb Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Wed, 5 Sep 2018 18:51:53 -0400 Subject: [PATCH 020/203] SPARK-1668 foundation --- src/lib/api/services/AdminService.proto | 27 +++++++++++++++++-- src/server/sparkd/user/AdminServiceServer.cpp | 3 +++ src/server/sparkd/user/AdminServiceServer.h | 2 ++ src/web/ui/src/Users/State.fs | 1 + 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index 13771f048..6a0dd2e10 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -182,11 +182,20 @@ message AccountExchangeConnections{ /***************************Exchange Data********************************/ /************************************************************************/ +message CMEMicData{ + api.CMEMics mic=10; + bool enabled=20; + // there are other things here +} + +message ICEMicData{ + api.CMEMics mic=10; + bool enabled=20; +} + message ExchangeUserMarketDataEntitlementData{ CMEUserMarketDataEntitlementData cme_data = 10; ICEUserMarketDataEntitlementData ice_data = 20; - //repeated CMEMics allowed_cme_mics = 30; - //repeated ICEMics allowed_ice_mics = 40; } message ExchangeUserData{ @@ -278,6 +287,8 @@ message CMEUserData{ string smp_instruction = 70; string max_order_rate_per_connection = 80; string location = 90; + // add suff here + repeated CMEMicData md_permissions = 1000; } message SIMUserData { @@ -355,6 +366,16 @@ message OrderCancelRequest{ repeated string accounts = 10; repeated string users = 20; } +/////////////////////////////////////////////////////////// +// Reporting ////////////////////////////////////////////// +message MDEnitlement{ + string record_id = 1 [(bts.api.key)=true]; + string user = 10; + string exchange = 20; + string mic = 30; + bool enabled = 40; +} + /************************************************************************/ /************************************************************************/ /************************************************************************/ @@ -412,4 +433,6 @@ service AdminService { // orders rpc get_order_summary(bts.api.VoidMessage) returns (OrdersSummary); rpc cancel_orders(OrderCancelRequest) returns (bts.api.VoidMessage); + // reports + rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEnitlement); } \ No newline at end of file diff --git a/src/server/sparkd/user/AdminServiceServer.cpp b/src/server/sparkd/user/AdminServiceServer.cpp index 62afb668a..7fb6cc96c 100644 --- a/src/server/sparkd/user/AdminServiceServer.cpp +++ b/src/server/sparkd/user/AdminServiceServer.cpp @@ -25,6 +25,7 @@ AdminServiceServer::AdminServiceServer(bts::api::ServiceContext& context) _state(), _store(new bts::persistence::SqliteStore(_db_name)), _publisher(make_admin_data_stateful_publisher()), + _md_entit_publisher(make_md_entitlement_data_stateful_publisher()), _order_service(context.locate()), _order_map() { @@ -204,4 +205,6 @@ void AdminServiceServer::_cancel_orders(const vector& accounts, _order_service.cancel_order(c); } } +void AdminServiceServer::_publish_md_entitlements(){ +} }} diff --git a/src/server/sparkd/user/AdminServiceServer.h b/src/server/sparkd/user/AdminServiceServer.h index e984bc8b5..3702eff7d 100644 --- a/src/server/sparkd/user/AdminServiceServer.h +++ b/src/server/sparkd/user/AdminServiceServer.h @@ -24,6 +24,7 @@ private: bts::api::admin::AdminData _state; std::unique_ptr _store; bts::api::admin::AdminServiceBase::Admin_dataStatefulPublisher _publisher; + bts::api::admin::AdminServiceBase::Md_entitlement_dataStatefulPublisher _md_entit_publisher; bts::api::orderman::OrderService _order_service; std::map> _order_map; public: @@ -60,6 +61,7 @@ public: private: void _update_and_publish (bool is_insert); + void _publish_md_entitlements(); void _add_live_order(std::map>& agg, const OrderRecord& order); void _cancel_orders(const std::vector& accounts, diff --git a/src/web/ui/src/Users/State.fs b/src/web/ui/src/Users/State.fs index 18f708009..40db505a7 100644 --- a/src/web/ui/src/Users/State.fs +++ b/src/web/ui/src/Users/State.fs @@ -62,6 +62,7 @@ let defaultScratchUser = { smpInstruction = "" maxOrderRatePerConnection = "" location = "" + mdPermissions = [||] } simData = { operatorId = "" From b8883fc8ae3ca4ad1d23de0a4692afd951251c44 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Thu, 6 Sep 2018 10:31:23 -0400 Subject: [PATCH 021/203] SPARK-1668 reporting stream is done --- .../api/service_helpers/AdminServiceUtils.cpp | 58 +++++++++++++++++++ .../api/service_helpers/AdminServiceUtils.h | 8 +++ src/lib/api/services/AdminService.proto | 4 +- src/server/sparkd/user/AdminServiceServer.cpp | 7 +++ 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/lib/api/service_helpers/AdminServiceUtils.cpp b/src/lib/api/service_helpers/AdminServiceUtils.cpp index f96811367..37318b956 100644 --- a/src/lib/api/service_helpers/AdminServiceUtils.cpp +++ b/src/lib/api/service_helpers/AdminServiceUtils.cpp @@ -1,6 +1,7 @@ #include "lib/api/services/AdminService.server.pb.h" #include "lib/api/service_helpers/AdminServiceUtils.h" #include "lib/md/get_symbols.h" +#include namespace bts{ namespace admin{ @@ -870,6 +871,63 @@ void AdminServiceUtils::validate_user_exchange_data (const bts::api::admin::BTSU } } } +void AdminServiceUtils::get_md_entitlement_report(const AdminData& admin_data, + std::vector& entitlements){ + for (const auto& user : admin_data.users()){ + const CMEUserData* cme_data=AdminServiceUtils::get_user_cme_data(user, + admin_data); + if(cme_data!=nullptr){ + for (const auto& cme_mic_data : cme_data->md_permissions()){ + MDEntitlementReportUnit rv=get_cme_md_entitlement_unit(user.name(), + cme_mic_data); + entitlements.push_back(rv); + } + } + } +} +MDEntitlementReportUnit AdminServiceUtils::get_cme_md_entitlement_unit( + const std::string& user, + const CMEMicData& cme_mic_data){ + MDEntitlementReportUnit rv; + rv.set_user(user); + rv.set_exchange("GLBX"); + switch(cme_mic_data.mic()) + { + case api::CMEMics::CME: + rv.set_mic("CME"); + break; + case api::CMEMics::NYMEX: + rv.set_mic("NYMEX"); + break; + case api::CMEMics::CBT: + rv.set_mic("CBT"); + break; + case api::CMEMics::COMEX: + rv.set_mic("COMEX"); + break; + case api::CMEMics::MGE: + rv.set_mic("MGE"); + break; + case api::CMEMics::DME: + rv.set_mic("DME"); + break; + case api::CMEMics::NYMSW: + rv.set_mic("NYMSW"); + break; + case api::CMEMics::FXS: + rv.set_mic("FXS"); + break; + case api::CMEMics::CMEMics_INT_MIN_SENTINEL_DO_NOT_USE_: + case api::CMEMics::CMEMics_INT_MAX_SENTINEL_DO_NOT_USE_: + rv.set_mic(""); + break; + } + rv.set_enabled(cme_mic_data.enabled()); + std::ostringstream key; + key << rv.user() << rv.exchange() << rv.mic(); + rv.set_record_id(key.str()); + return rv; +} //////////////////////////////////////////// // user mutators /////////////////////////// //////////////////////////////////////////// diff --git a/src/lib/api/service_helpers/AdminServiceUtils.h b/src/lib/api/service_helpers/AdminServiceUtils.h index de9ea6ad0..e43d43231 100644 --- a/src/lib/api/service_helpers/AdminServiceUtils.h +++ b/src/lib/api/service_helpers/AdminServiceUtils.h @@ -10,6 +10,8 @@ namespace bts{ namespace admin{ +using namespace bts::api::admin; + class AdminServiceUtils{ public: //////////////////////////////////////////// @@ -190,6 +192,12 @@ public: // vaidates the exchange part of user data updates static void validate_user_exchange_data (const bts::api::admin::BTSUser& updated_user, bts::api::admin::AdminServiceOperationResult& out_result); + static void get_md_entitlement_report(const AdminData& admin_data, + std::vector& entitlements); + // Gets a reporting unit out of CMEMicData + static MDEntitlementReportUnit get_cme_md_entitlement_unit( + const std::string& user, + const CMEMicData& cme_mic_data); //////////////////////////////////////////// // user mutators /////////////////////////// //////////////////////////////////////////// diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index 6a0dd2e10..e1d9a0a17 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -368,7 +368,7 @@ message OrderCancelRequest{ } /////////////////////////////////////////////////////////// // Reporting ////////////////////////////////////////////// -message MDEnitlement{ +message MDEntitlementReportUnit{ string record_id = 1 [(bts.api.key)=true]; string user = 10; string exchange = 20; @@ -434,5 +434,5 @@ service AdminService { rpc get_order_summary(bts.api.VoidMessage) returns (OrdersSummary); rpc cancel_orders(OrderCancelRequest) returns (bts.api.VoidMessage); // reports - rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEnitlement); + rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEntitlementReportUnit); } \ No newline at end of file diff --git a/src/server/sparkd/user/AdminServiceServer.cpp b/src/server/sparkd/user/AdminServiceServer.cpp index 7fb6cc96c..9c992c8fd 100644 --- a/src/server/sparkd/user/AdminServiceServer.cpp +++ b/src/server/sparkd/user/AdminServiceServer.cpp @@ -126,9 +126,11 @@ void AdminServiceServer::_update_and_publish(bool is_insert){ _store->update(_table_name, _unique_key, ser_state); _publisher.add_or_update(_state); _publisher.flush(); + _publish_md_entitlements(); } else LOG(Gen, Error, "Could not serialize admin data, saving failed"); + } void AdminServiceServer::get_order_summary(const bts::api::VoidMessage&, const api::Response<::bts::api::admin::OrdersSummary>& reply){ @@ -206,5 +208,10 @@ void AdminServiceServer::_cancel_orders(const vector& accounts, } } void AdminServiceServer::_publish_md_entitlements(){ + std::vector entitlements; + AdminServiceUtils::get_md_entitlement_report(_state,entitlements); + for (const auto& e : entitlements) + _md_entit_publisher.add_or_update(e); + _md_entit_publisher.flush(); } }} From 593944266d87920aadae3a1df670cbb16dd27d1f Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Thu, 6 Sep 2018 10:47:29 -0400 Subject: [PATCH 022/203] merging --- src/lib/api/service_helpers/AdminServiceUtils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/api/service_helpers/AdminServiceUtils.cpp b/src/lib/api/service_helpers/AdminServiceUtils.cpp index 37318b956..6bd572002 100644 --- a/src/lib/api/service_helpers/AdminServiceUtils.cpp +++ b/src/lib/api/service_helpers/AdminServiceUtils.cpp @@ -917,6 +917,7 @@ MDEntitlementReportUnit AdminServiceUtils::get_cme_md_entitlement_unit( case api::CMEMics::FXS: rv.set_mic("FXS"); break; + case api::CMEMics::UNSET: case api::CMEMics::CMEMics_INT_MIN_SENTINEL_DO_NOT_USE_: case api::CMEMics::CMEMics_INT_MAX_SENTINEL_DO_NOT_USE_: rv.set_mic(""); From db8063190170e3b936ec252fca9c2eb5dde90c6c Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Thu, 6 Sep 2018 12:21:05 -0400 Subject: [PATCH 023/203] Fix "some projects had trouble loading" in vscode --- spark_client.sln | 114 ++++++++++++++-------------- src/client/sparkcmd/sparkcmd.csproj | 3 +- 2 files changed, 61 insertions(+), 56 deletions(-) diff --git a/spark_client.sln b/spark_client.sln index b09517f46..f8aa20aaf 100644 --- a/spark_client.sln +++ b/spark_client.sln @@ -4,7 +4,7 @@ VisualStudioVersion = 15.0.27130.2024 MinimumVisualStudioVersion = 15.0.26124.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}" EndProject @@ -12,11 +12,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sparkcmd", "src\client\spar EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{9AD1A867-93A9-301D-BF77-0113E18E9E39}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}" ProjectSection(ProjectDependencies) = postProject - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3} = {8530E0E8-3768-37EC-B1E1-8248C5A92FE3} + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C} = {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{ECD78CC5-138F-4809-A764-2358C2AC2E82}" @@ -57,22 +57,22 @@ Global {DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64 {DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.ActiveCfg = Release|x64 {DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.Build.0 = Release|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|Any CPU.ActiveCfg = Debug|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x64.ActiveCfg = Debug|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x64.Build.0 = Debug|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x86.ActiveCfg = Debug|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|Any CPU.ActiveCfg = Release|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x64.ActiveCfg = Release|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x64.Build.0 = Release|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x86.ActiveCfg = Release|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 - {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|Any CPU.ActiveCfg = Debug|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x64.ActiveCfg = Debug|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x64.Build.0 = Debug|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x86.ActiveCfg = Debug|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|Any CPU.ActiveCfg = Release|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x64.ActiveCfg = Release|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x64.Build.0 = Release|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x86.ActiveCfg = Release|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 + {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64 {9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|Any CPU.ActiveCfg = Debug|x64 {9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.ActiveCfg = Debug|x64 {9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.Build.0 = Debug|x64 @@ -93,26 +93,30 @@ Global {9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x64.Build.0 = Release|x64 {9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x86.ActiveCfg = Release|x64 {9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x86.Build.0 = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.ActiveCfg = Debug|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.ActiveCfg = Debug|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.Build.0 = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.Build.0 = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.ActiveCfg = Release|x64 - {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.Build.0 = Release|x64 + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.Build.0 = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.ActiveCfg = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.Build.0 = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.Build.0 = Debug|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.ActiveCfg = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.Build.0 = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.ActiveCfg = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.Build.0 = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU + {387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|Any CPU.ActiveCfg = Debug|x64 {6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.ActiveCfg = Debug|x64 {6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.Build.0 = Debug|x64 @@ -133,22 +137,22 @@ Global {6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64 {6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.ActiveCfg = Release|x64 {6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.Build.0 = Release|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|Any CPU.ActiveCfg = Debug|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x64.ActiveCfg = Debug|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x64.Build.0 = Debug|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x86.ActiveCfg = Debug|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|Any CPU.ActiveCfg = Release|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x64.ActiveCfg = Release|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x64.Build.0 = Release|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x86.ActiveCfg = Release|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 - {9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|Any CPU.ActiveCfg = Debug|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x64.ActiveCfg = Debug|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x64.Build.0 = Debug|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x86.ActiveCfg = Debug|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|Any CPU.ActiveCfg = Release|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x64.ActiveCfg = Release|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x64.Build.0 = Release|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x86.ActiveCfg = Release|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 + {33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64 {6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|Any CPU.ActiveCfg = Debug|x64 {6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.ActiveCfg = Debug|x64 {6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.Build.0 = Debug|x64 diff --git a/src/client/sparkcmd/sparkcmd.csproj b/src/client/sparkcmd/sparkcmd.csproj index 2c2241ba3..31c2efcd9 100644 --- a/src/client/sparkcmd/sparkcmd.csproj +++ b/src/client/sparkcmd/sparkcmd.csproj @@ -8,7 +8,8 @@ Blue Trading Systems Blue Trading Systems Spark - x64 + x64 + x64 1.0.0 7.2 From 58e88812b193967a0a4629434956d8b474a76297 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Thu, 6 Sep 2018 13:54:02 -0400 Subject: [PATCH 024/203] SPARK-1693 - Code quota reached. Have to delete some. --- .../spark/core/Utility/ExpirationFormatter.cs | 156 -------------- .../SecurityTypeStringConverter.cs | 18 -- .../SecurityTypeForegroundConverter.cs | 32 --- .../spark/view/Resources/Converters.xaml | 2 - .../spark/view/Utility/SecurityUtils.cs | 90 -------- src/client/sparkcmd/Program.cs | 3 - src/lib/CMakeDeps.cmake | 2 +- .../defaults/expiration_display_offset.conf | 194 ------------------ .../protos/expiration_display_offset.proto | 24 --- 9 files changed, 1 insertion(+), 520 deletions(-) delete mode 100644 src/client/spark/core/Utility/ExpirationFormatter.cs delete mode 100644 src/client/spark/view/Converters/EnumStringConverters/SecurityTypeStringConverter.cs delete mode 100644 src/client/spark/view/Converters/SecurityConverters/SecurityTypeForegroundConverter.cs delete mode 100644 src/client/spark/view/Utility/SecurityUtils.cs delete mode 100644 src/lib/config/defaults/expiration_display_offset.conf delete mode 100644 src/lib/config/protos/expiration_display_offset.proto diff --git a/src/client/spark/core/Utility/ExpirationFormatter.cs b/src/client/spark/core/Utility/ExpirationFormatter.cs deleted file mode 100644 index 1bea1ae9e..000000000 --- a/src/client/spark/core/Utility/ExpirationFormatter.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using bts; -using bts.api; - -namespace mbts.ui.utility -{ - class ExpirationFormatter - { - private static class DateFormatter - { - private static string[] month_codes = new string[] { "F", "G", "H", "J", "K", "M", "N", "Q", "U", "V", "X", "Z" }; - private static string[] month_three_letters = new string[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - - public static string format_condensed(DateTime date, bool use_month_code, bool show_year) - { - return String.Format("{0}{1}{2}", date.Day, month_codes[date.Month - 1], show_year ? (date.Year - 2000).ToString() : String.Empty); - } - - public static string format(DateTime date, bool use_month_code, bool show_day) { - string month_text = use_month_code ? "{0}{1:00}".fmt(month_codes[date.Month - 1], date.Year - 2000) : "{0} {1:00}".fmt(month_three_letters[date.Month - 1], date.Year - 2000); - return show_day ? "{0} {1}".fmt(date.Day, month_text) : month_text; - } - - public static string format_standard(DateTime date) { return "{0} {1} {2:00}".fmt(date.Day, month_three_letters[date.Month - 1], date.Year - 2000); } - public static string format_coded(DateTime date) { return "{0} {1}{2:00}".fmt(date.Day, month_codes[date.Month - 1], date.Year - 2000); } - public static string format_coded_hide_day(DateTime date) { return "{0}{1}".fmt(month_codes[date.Month - 1], date.Year - 2000); } - - public static DateTime parse_standard(string date_text) { - string[] split = date_text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - int year = 0; - int month = Array.IndexOf(month_three_letters, split[1]) + 1; - int day = 0; - int.TryParse(split[2], out year); - int.TryParse(split[0], out day); - return new DateTime(year + 2000, month, day); - } - - public static DateTime parse_coded(string date_text) { - string[] split = date_text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - string year_text = split[1].Substring(1); - string month_text = split[1].Substring(0, 1); - string day_text = split[0]; - int month = Array.IndexOf(month_codes, month_text) + 1; - int day, year; - int.TryParse(year_text, out year); - int.TryParse(day_text, out day); - return new DateTime(year + 2000, month, day); - } - - public static DateTime parse_coded_hide_day(string date_text) { - string year_text = date_text.Substring(1); - string month_text = date_text.Substring(0, 1); - int month = Array.IndexOf(month_codes, month_text) + 1; - int year; - int.TryParse(year_text, out year); - return new DateTime(year + 2000, month, 0); - } - } - - private static IDictionary> _offset_map; - - static ExpirationFormatter() - { - try - { - _offset_map = new Dictionary>(); - _offset_map[SecurityType.Option] = new Dictionary(); - _offset_map[SecurityType.Future] = new Dictionary(); - - bts.config.ExpirationDisplayOffset offset_list = mbts.config.Configuration.get(); - foreach (bts.config.ExpirationDisplayOffsetEntry e in offset_list.entries) - { - string security_name = e.security_name; - string security_type_text = e.security_type; - SecurityType security_type = SecurityType.Unset; - Enum.TryParse(security_type_text, true, out security_type); - if (security_type == SecurityType.Option || security_type == SecurityType.Future) - _offset_map[security_type][security_name] = e; - } - } - catch (Exception ex) - { - Log.Ui.error(String.Format("Exception message = {0}", ex.Message)); - } - } - - private static bts.config.ExpirationDisplayOffsetEntry _get_offset_entry(SecurityType security_type, string security_name) - { - bts.config.ExpirationDisplayOffsetEntry rv = null; - IDictionary this_security_type_map = null; - if (_offset_map.TryGetValue(security_type, out this_security_type_map)) - if (this_security_type_map.TryGetValue(security_name, out rv)) - return rv; - return rv; - } - - public static DateTime convert_real_to_mapped(SecurityType security_type, string security_name, DateTime security_expiration) - { - bts.config.ExpirationDisplayOffsetEntry offset_entry = _get_offset_entry(security_type, security_name); - DateTime rv = security_expiration; - if (offset_entry != null) - { - if (offset_entry.addition_type == bts.config.ExpirationOffsetType.type.Month) - rv = security_expiration.AddMonths(offset_entry.addition_count); - else if (offset_entry.addition_type == bts.config.ExpirationOffsetType.type.MonthMoveToStart) - { - rv = security_expiration.AddMonths(offset_entry.addition_count); - rv = new DateTime(rv.Year, rv.Month, 1); - } - } - return rv; - } - - public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration) { - return format_expiration_standard(security_type, security_name, security_expiration, map_expiration, false); - } - - public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool use_month_code) { - return format_expiration_standard(security_type, security_name, security_expiration, map_expiration, use_month_code, true); - } - - public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool use_month_code, bool show_day) { - DateTime mapped_expiration = map_expiration ? convert_real_to_mapped(security_type, security_name, security_expiration) : security_expiration; - return DateFormatter.format(mapped_expiration, use_month_code, show_day); - } - - public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration) { - return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, false, false, true); - } - - public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type) { - return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, show_security_type, false, true); - } - - public static string format_symbol_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type, bool use_month_code) { - return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, show_security_type, use_month_code, true); - } - - public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type, bool use_month_code, bool show_day) { - string exp_text = format_expiration_standard(security_type, security_name, security_expiration, map_expiration, use_month_code, show_day); - return !show_security_type ? String.Format("{0} {1}", security_name, exp_text) : String.Format("{0} {1} {2}", security_type.ToString().ToUpper(), security_name, exp_text); - } - - public static string format_option_expiration_condensed(string option_name, DateTime option_expiration, bool map_expiration, bool show_symbol_name, bool show_year) - { - DateTime mapped_expiration = map_expiration ? convert_real_to_mapped(SecurityType.Option, option_name, option_expiration) : option_expiration; - return String.Format("{0}{1}", show_symbol_name ? option_name : String.Empty, DateFormatter.format_condensed(mapped_expiration, true, show_year)); - } - - public static DateTime parse_standard_formatted_expiration(SecurityType security_type, string security_name, string security_expiration) { return DateFormatter.parse_standard(security_expiration); } - } -} diff --git a/src/client/spark/view/Converters/EnumStringConverters/SecurityTypeStringConverter.cs b/src/client/spark/view/Converters/EnumStringConverters/SecurityTypeStringConverter.cs deleted file mode 100644 index 4ba079d4f..000000000 --- a/src/client/spark/view/Converters/EnumStringConverters/SecurityTypeStringConverter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using bts.api; -using mbts.ui.utility; -using System; -using System.Windows.Data; - -namespace mbts.ui.converters -{ - public class SecurityTypeStringConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - return SecurityDisplay.format_security_type((SecurityType)value); - } - - public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - throw new NotImplementedException(); - } - } -} diff --git a/src/client/spark/view/Converters/SecurityConverters/SecurityTypeForegroundConverter.cs b/src/client/spark/view/Converters/SecurityConverters/SecurityTypeForegroundConverter.cs deleted file mode 100644 index b800fe108..000000000 --- a/src/client/spark/view/Converters/SecurityConverters/SecurityTypeForegroundConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -using bts.api; -using System; -using System.Windows; -using System.Windows.Data; -using System.Windows.Media; - -namespace mbts.ui.converters -{ - [ValueConversion(typeof(SecurityType), typeof(Brush))] - public class SecurityTypeForegroundConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (value is SecurityType) return _convert((SecurityType)value); - //else if (value is SecurityTuple) return _convert(((SecurityTuple)value).security_type); - //else if (value is SecurityExpiration) return _convert(((SecurityExpiration)value).security_type); - else return SystemColors.ControlTextBrush; - } - - public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - throw new NotImplementedException(); - } - - private Brush _convert(SecurityType type) { - switch (type) { - case SecurityType.Option: return (Brush)BTSColors.header_text; - case SecurityType.Future: return new SolidColorBrush(Colors.Gold); - case SecurityType.Spread: return new SolidColorBrush(Colors.SlateBlue); - default: return new SolidColorBrush(Colors.PaleGreen); - } - } - } -} diff --git a/src/client/spark/view/Resources/Converters.xaml b/src/client/spark/view/Resources/Converters.xaml index a0e130254..59b3e9707 100644 --- a/src/client/spark/view/Resources/Converters.xaml +++ b/src/client/spark/view/Resources/Converters.xaml @@ -26,8 +26,6 @@ - - diff --git a/src/client/spark/view/Utility/SecurityUtils.cs b/src/client/spark/view/Utility/SecurityUtils.cs deleted file mode 100644 index 0e732c297..000000000 --- a/src/client/spark/view/Utility/SecurityUtils.cs +++ /dev/null @@ -1,90 +0,0 @@ -using bts.api; -using mbts.utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace mbts.ui.utility -{ - public enum DateFormat { Standard, DayMonthAbvDiff2000, DayMonthCodeDiff2000 } - static class SecurityDisplay - { - /// - /// Returns a shorthand string representation of a security type appropriate for UIs. - /// - public static string format_security_type(SecurityType type) { - switch (type) { - case SecurityType.Option: return "Opt"; - case SecurityType.Future: return "Fut"; - case SecurityType.Stock: return "Stk"; - case SecurityType.Spread: return "Spd"; - default: return type.ToString(); - } - } - - /// - /// Returns a concise string representation of a security expiration appropriate for UIs (e.g. es March 22, 2019: es 22 Mar 19). - /// - public static string format_expiration(SecurityType sec_type, string symbol, DateTime exp_date) { return format_expiration(sec_type, symbol, exp_date, false, true, true, DateFormat.DayMonthAbvDiff2000); } - - /// - /// Returns a concise string representation of a security expiration (using symbol-mapped expiration dates, and single-letter month codes) appropriate for UIs (e.g. es March 22, 2019: [Opt] [es] 22 Mar 19). - /// - public static string format_expiration(SecurityType sec_type, string symbol, DateTime exp_date, bool show_type, bool show_symbol, bool use_mapped_exp) { return format_expiration(sec_type, symbol, exp_date, show_type, show_symbol, use_mapped_exp, DateFormat.Standard); } - - /// - /// Returns a concise string representation of a security expiration (using symbol-mapped expiration dates, and single-letter month codes) appropriate for UIs (e.g. es March 22, 2019: [Opt] [es] 22 Mar 19, or [Opt] [es] 22 H19). - /// - public static string format_expiration(SecurityType sec_type, string symbol, DateTime exp_date, bool show_type, bool show_symbol, bool use_mapped_exp, DateFormat date_format) { - if (use_mapped_exp) exp_date = ExpirationFormatter.convert_real_to_mapped(sec_type, symbol, exp_date); - string result = format_date(exp_date); - if (show_symbol) result = (symbol + " " + result); - if (show_type) result = (format_security_type(sec_type) + " " + result); - return result; - } - - - /// - /// Returns a concise string representation of a future trade appropriate for UIs. - /// - public static string format_future_trade(string symbol, DateTime exp_date, int size, double marginal_price) { return "{0} {1} lots @ {2:C}".fmt(format_expiration(SecurityType.Future, symbol, exp_date, false, true, true), size, marginal_price); } - - /// - /// Returns a concise string representation of an option trade appropriate for UIs. - /// - public static string format_option_trade(string symbol, DateTime exp_date, double strike, bool is_call, int size, double marginal_price) { - return "{0} {1} {2} {3} lots @ {4:C}".fmt(format_expiration(SecurityType.Option, symbol, exp_date, false, true, true), strike, is_call ? "C" : "P", size, marginal_price); - } - - /// - /// Returns a concise string representation of a date (using standard month abbreviations) appropriate for UIs (e.g. March 22, 2019: 22 Mar 19). - /// - public static string format_date(DateTime date) { return format_date(date.Year, date.Month, date.Day, DateFormat.DayMonthAbvDiff2000); } - - /// - /// Returns a concise string representation of a date (using single-letter month codes) appropriate for UIs (e.g. March 22, 2019: 22 Mar 19, or 22 H19). - /// - public static string format_date(int year, int month, int day, DateFormat format) { - switch(format) { - case DateFormat.DayMonthAbvDiff2000: return "{0} {1} {2}".fmt(day, get_month_abv(month), year - 2000); - case DateFormat.DayMonthCodeDiff2000: return "{0} {1}{2}".fmt(day, get_month_code(month), year - 2000); - case DateFormat.Standard: goto default; - default: return "{0}/{1}/{2}".fmt(day, month, year); - } - } - /// - /// Returns the option industry standard month code. - /// - public static string get_month_code(int month) { _check_month(month); return _month_codes[month - 1]; } - /// - /// Returns the standard month abbreviation (Jan, Feb, Mar, etc.). - /// - public static string get_month_abv(int month) { _check_month(month); return _month_abvs[month - 1]; } - #region Private Members - private static void _check_month(int month) { if (month < 1 || month > 12) throw new ArgumentException("There are only twelve months in a year", "month"); } - private static string[] _month_codes = new string[] { "F", "G", "H", "J", "K", "M", "N", "Q", "U", "V", "X", "Z" }; - private static string[] _month_abvs = new string[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - #endregion - } -} \ No newline at end of file diff --git a/src/client/sparkcmd/Program.cs b/src/client/sparkcmd/Program.cs index a4eeaaa20..1feb68ecb 100644 --- a/src/client/sparkcmd/Program.cs +++ b/src/client/sparkcmd/Program.cs @@ -13,8 +13,5 @@ namespace sparkcmd { ConsoleCommandDispatcher.DispatchCommand(commands, args, Console.Out); return; } - - - } } \ No newline at end of file diff --git a/src/lib/CMakeDeps.cmake b/src/lib/CMakeDeps.cmake index 8921353f3..e5efb96cd 100644 --- a/src/lib/CMakeDeps.cmake +++ b/src/lib/CMakeDeps.cmake @@ -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/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/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Actor.cpp;mercury/broker/AsyncEventBroker.cpp;mercury/broker/CoreEventBroker.cpp;mercury/broker/IEventBroker.cpp;mercury/events/TimerEvent.cpp;mercury/mercury_utils/Executor.cpp;mercury/mercury_utils/IPollableActor.cpp;mercury/mercury_utils/mercury_common.cpp;mercury/synopsis/SynopsisBuilder.cpp;mercury/topics/ITopic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.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/OrderFactory.cpp;orderman/order_router/OrderRouter.cpp;orderman/orderman_utils/fix_order_codec.cpp;orderman/orders/DynamicOrder.cpp;orderman/orders/IcebergOrder.cpp;orderman/orders/NativeOrder.cpp;orderman/orders/Order.cpp;orderman/orders/TimeBoxOrder.cpp;orderman/orders/TriggerOrder.cpp;orderman/orders/book_algos/BookAlgo.cpp;orderman/orders/book_algos/CoverOrder.cpp;orderman/orders/book_algos/JoinOrder.cpp;orderman/orders/book_algos/LeanOrder.cpp;orderman/orders/book_algos/TickOrder.cpp;orderman/orders/book_algos/TopOrder.cpp;orderman/orders/spreader_orders/SpreaderOrder.cpp;orderman/orders/spreader_orders/spreader/LegPropertyData.cpp;orderman/orders/spreader_orders/spreader/QuoteLegPriceQuantityState.cpp;orderman/orders/spreader_orders/spreader/SingleFillHedger.cpp;orderman/orders/spreader_orders/spreader/SingleLegQuoter.cpp;orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp;orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.cpp;orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp;orderman/orders/spreader_orders/spreader/SpreaderUtils.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderServices.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderPreActivationOrderServices.cpp;orderman/orders/spreader_orders/spreader/hedger/UnhedgedDelta.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckCancelledStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSet.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSetHistory.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteLegDriver.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckCancelledWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForIOCWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForSizeStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderEndStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWaitingForCancelAllStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWorkingStateWorker.cpp;orderman/orders/spreader_orders/tests/spreader_tests.cpp;orderman/server/OrderServiceServer.cpp;orderman/tests/fat_wrapper_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;persistence/IStore.cpp;persistence/PersistenceServiceServer.cpp;persistence/SqliteStore.cpp;persistence/tests/PersistenceServiceTests.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/Dispatcher.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) \ No newline at end of file +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/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/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/dynamic_orders.proto;config/protos/environment.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Dispatcher.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) \ No newline at end of file diff --git a/src/lib/config/defaults/expiration_display_offset.conf b/src/lib/config/defaults/expiration_display_offset.conf deleted file mode 100644 index 959d3f291..000000000 --- a/src/lib/config/defaults/expiration_display_offset.conf +++ /dev/null @@ -1,194 +0,0 @@ -ExpirationDisplayOffset -{ - value - { - entries - { - security_type: "OPTION" - security_name: "lo" - addition_count: 1 - addition_type: MonthMoveToStart - } - - entries - { - security_type: "FUTURE" - security_name: "CL" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "ob" - addition_count: 1 - addition_type: MonthMoveToStart - } - - entries - { - security_type: "FUTURE" - security_name: "RB" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "oh" - addition_count: 1 - addition_type: MonthMoveToStart - } - - entries - { - security_type: "FUTURE" - security_name: "HO" - addition_count: 1 - addition_type: MonthMoveToStart - } - - entries - { - security_type: "OPTION" - security_name: "wbs" - addition_count: 1 - addition_type: MonthMoveToStart - } - - entries - { - security_type: "FUTURE" - security_name: "WBS" - addition_count: 1 - addition_type: MonthMoveToStart - } - - entries - { - security_type: "OPTION" - security_name: "so" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "FUTURE" - security_name: "SI" - addition_count: 0 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "og" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "FUTURE" - security_name: "GC" - addition_count: 0 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "hxe" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "FUTURE" - security_name: "HG" - addition_count: 0 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "ozs" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "ozw" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "osd" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "FUTURE" - security_name: "ZS" - addition_count: 0 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "ozm" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "FUTURE" - security_name: "ZM" - addition_count: 0 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "ozl" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "FUTURE" - security_name: "ZL" - addition_count: 0 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "ozc" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "FUTURE" - security_name: "ZC" - addition_count: 0 - addition_type: MonthMoveToStart - } - entries - { - security_type: "OPTION" - security_name: "ozn" - addition_count: 1 - addition_type: MonthMoveToStart - } - entries - { - security_type: "FUTURE" - security_name: "ZN" - addition_count: 0 - addition_type: MonthMoveToStart - } - } -} \ No newline at end of file diff --git a/src/lib/config/protos/expiration_display_offset.proto b/src/lib/config/protos/expiration_display_offset.proto deleted file mode 100644 index 3c8ded3c7..000000000 --- a/src/lib/config/protos/expiration_display_offset.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax='proto2'; -package bts.config; - -message ExpirationOffsetType -{ - enum type - { - Month=0; - MonthMoveToStart=1; - } -} - -message ExpirationDisplayOffsetEntry -{ - required string security_type = 1; - required string security_name = 2; - required int32 addition_count = 3; - required ExpirationOffsetType.type addition_type = 4; -} - -message ExpirationDisplayOffset -{ - repeated ExpirationDisplayOffsetEntry entries = 1; -} From 7bda1cc1cc77f2506b84e664df7f11360face26e Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Thu, 6 Sep 2018 21:50:58 -0400 Subject: [PATCH 025/203] Permissions proto and service member added --- src/lib/api/services/AdminService.proto | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index e1d9a0a17..4646343c9 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -376,6 +376,16 @@ message MDEntitlementReportUnit{ bool enabled = 40; } +//////////////////////////////////////////////////////////// +// Misc /////////////////////////////////////////////////// +message UserSymbolMDPermissionUnit{ + string record_id = 1 [(bts.api.key)=true]; + string user=10; + api.Venue exchange=20; + string symbol=30; + bool enabled=40; +} + /************************************************************************/ /************************************************************************/ /************************************************************************/ @@ -435,4 +445,6 @@ service AdminService { rpc cancel_orders(OrderCancelRequest) returns (bts.api.VoidMessage); // reports rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEntitlementReportUnit); + // misc + rpc md_permissions(bts.api.VoidMessage) returns (stream UserSymbolMDPermissionUnit); } \ No newline at end of file From dd5b489e8f940b404e92d7d78c720c5d2ae83cf3 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Fri, 7 Sep 2018 09:29:41 -0400 Subject: [PATCH 026/203] SPARK-1659 Update producer api to handle implied markets (push early for emumba) --- src/lib/md/MDProducerHandle.cpp | 7 ++++++- src/lib/md/MDProducerHandle.h | 6 +++--- src/lib/md/Shm/MDStructure.h | 4 ++-- src/lib/md/relay/RelayClient.cpp | 2 +- src/lib/md/test/synthetic_book_merge_tests.cpp | 2 +- src/sdk/example/example.cpp | 2 +- src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp | 2 +- src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp | 2 +- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/lib/md/MDProducerHandle.cpp b/src/lib/md/MDProducerHandle.cpp index 3af9741da..a63d1b826 100644 --- a/src/lib/md/MDProducerHandle.cpp +++ b/src/lib/md/MDProducerHandle.cpp @@ -209,10 +209,15 @@ void MDProducerHandle::mark_deleted() { TimeStamp ts = TimeStamp::now(); cdata().deleted = true; std::array blank; + std::array iblank; for (size_t x = 0; x < blank.size(); ++x) { blank[x] = PriceSize::NO_MARKET; + if (x < COCONUT_MAX_IMPLIED_DEPTH) { + iblank[x] = PriceSize::NO_MARKET; + } } - update_book(ts, blank, blank, 0); + update_direct(ts, blank, blank, 0); + update_implied(ts, iblank, iblank, 0); } }}//namespace diff --git a/src/lib/md/MDProducerHandle.h b/src/lib/md/MDProducerHandle.h index 4203c1fe4..e98ce4efb 100644 --- a/src/lib/md/MDProducerHandle.h +++ b/src/lib/md/MDProducerHandle.h @@ -34,8 +34,8 @@ public: } void set_legs(const std::vector& legs); - void update_bbo(TimeStamp time, const PriceSize bid_price, const PriceSize ask_price, uint16_t seq=0); // hot - void update_book(TimeStamp time, std::array& bids, std::array& asks, uint32_t seq=0) { + void update_bbo(TimeStamp time, const PriceSize bid_price, const PriceSize ask_price, uint16_t seq=0); + void update_direct(TimeStamp time, std::array& bids, std::array& asks, uint32_t seq=0) { if (!has_depth()) { update_bbo(time, bids[0], asks[0], seq); } @@ -51,7 +51,7 @@ public: hheader().direct_count += 1; } } - void update_implied(TimeStamp time, std::array& bids, std::array& asks, uint32_t seq=0) { + void update_implied(TimeStamp time, std::array& bids, std::array& asks, uint32_t seq=0) { if (has_depth()) { auto& book = hdepth(); book.set_time(TimeStamp::invalid()); diff --git a/src/lib/md/Shm/MDStructure.h b/src/lib/md/Shm/MDStructure.h index c9fa769ac..e443184a4 100644 --- a/src/lib/md/Shm/MDStructure.h +++ b/src/lib/md/Shm/MDStructure.h @@ -52,7 +52,7 @@ class HotEntry { static_assert(sizeof(HotEntry) <= CACHE_LINE, "HotEntry should be small"); const uint32_t COCONUT_MAX_DEPTH = 10; -const uint32_t COCONUT_IMPLIED_DEPTH = 2; +const uint32_t COCONUT_MAX_IMPLIED_DEPTH = 2; class HotDepthEntry { std::atomic timestamp; // 8 public: @@ -68,7 +68,7 @@ class HotDepthEntry { security_key key; // 4 NOTE: key is at a different offset than it is in HotEntry uint32_t sequence; // 4 std::array direct; // 240 - std::array implied; // 48 + std::array implied; // 48 uint8_t status; // 1 const PriceLevel* at(uint32_t depth) const { diff --git a/src/lib/md/relay/RelayClient.cpp b/src/lib/md/relay/RelayClient.cpp index f3aac8fb3..7345f7172 100644 --- a/src/lib/md/relay/RelayClient.cpp +++ b/src/lib/md/relay/RelayClient.cpp @@ -60,7 +60,7 @@ class RelayProducer : public IRelayWriter { bids[x] = book_at(update.book().bids(), x); asks[x] = book_at(update.book().asks(), x); } - handle.update_book(updated, bids, asks); + handle.update_direct(updated, bids, asks); } if (update.has_misc()) { auto& misc = update.misc(); diff --git a/src/lib/md/test/synthetic_book_merge_tests.cpp b/src/lib/md/test/synthetic_book_merge_tests.cpp index 8616446a9..215db620f 100644 --- a/src/lib/md/test/synthetic_book_merge_tests.cpp +++ b/src/lib/md/test/synthetic_book_merge_tests.cpp @@ -38,7 +38,7 @@ void setup_market(MDProducerHandle producer, const vector& bids, const vecto std::transform(bids.begin(), bids.end(), bids_a.begin(), [](const PS& ps) { return PriceSize::from_double<7>(ps.price, ps.size); }); std::transform(asks.begin(), asks.end(), asks_a.begin(), [](const PS& ps) { return PriceSize::from_double<7>(ps.price, ps.size); }); - producer.update_book(ts, bids_a, asks_a); + producer.update_direct(ts, bids_a, asks_a); } void expect_market(const MDConsumerHandle& handle, const vector bids, const vector& asks) { diff --git a/src/sdk/example/example.cpp b/src/sdk/example/example.cpp index 817869e1b..d38cb424c 100644 --- a/src/sdk/example/example.cpp +++ b/src/sdk/example/example.cpp @@ -69,7 +69,7 @@ void produce() }; // Update book - this call does not use locks so should not be called concurrently - producer_handles[0].update_book(utils::TimeStamp::now(), bids, asks); + producer_handles[0].update_direct(utils::TimeStamp::now(), bids, asks); } int main() diff --git a/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp b/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp index 1f2f4c8ce..8d38f9301 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp +++ b/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp @@ -473,7 +473,7 @@ void ExchangeSimApplication::update_md(std::string const &sec_desc) { return md::PriceSize::from_double<7>( bs.first.value() * price_factor, bs.second.value()); }); - handle.update_book(utils::TimeStamp::now(), bids_temp, asks_temp); + handle.update_direct(utils::TimeStamp::now(), bids_temp, asks_temp); auto new_trades = matcher.get_new_trades(); for (auto &t : new_trades) diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp index e8468f865..fa2b252cf 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp @@ -246,7 +246,7 @@ void MDP3Listener::flush_books() { // Write all book updates to shared memory and reset for the next packet for (auto& entry : _book_updates) { uint16_t sequence = (_latest_sequence & 0xFFFF); - entry.handle.update_book(_packet_arrived, entry.bids, entry.asks, sequence); + entry.handle.update_direct(_packet_arrived, entry.bids, entry.asks, sequence); } // Reset pending updates _book_updates.clear(); From 92d7503528a83dc58f4edba9971311b8fa038fd9 Mon Sep 17 00:00:00 2001 From: Kevin Darby Date: Fri, 7 Sep 2018 16:18:46 -0400 Subject: [PATCH 027/203] cmake? --- src/lib/CMakeDeps.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/CMakeDeps.cmake b/src/lib/CMakeDeps.cmake index ff879e749..e5efb96cd 100644 --- a/src/lib/CMakeDeps.cmake +++ b/src/lib/CMakeDeps.cmake @@ -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/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/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Dispatcher.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) \ No newline at end of file +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/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/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/dynamic_orders.proto;config/protos/environment.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Dispatcher.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) \ No newline at end of file From 4a5f71e7f27a1cd5ca053ce690a621887d618d84 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Fri, 7 Sep 2018 16:37:23 -0400 Subject: [PATCH 028/203] fix build --- src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs b/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs index 3209d2172..3fbcd1e97 100644 --- a/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs @@ -22,4 +22,9 @@ namespace mbts.spark { ICollection child_order_types { get; } IOrderTypeVM child_order { get; } } + + static class OrderTypeVMExtensions { + public static OrderTemplateKey key(this IOrderTypeVM vm) => + new OrderTemplateKey(vm.order_type, vm.id); + } } \ No newline at end of file From f89f2b8ad066b154bcc3e8e24f69349bf0ed920b Mon Sep 17 00:00:00 2001 From: Michael Harding Date: Tue, 4 Sep 2018 13:15:06 -0400 Subject: [PATCH 029/203] Remove Beta from SparkAdmin --- src/web/ui/src/Login/Login.fs | 37 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/web/ui/src/Login/Login.fs b/src/web/ui/src/Login/Login.fs index b8a2adeb2..20d9d49e9 100644 --- a/src/web/ui/src/Login/Login.fs +++ b/src/web/ui/src/Login/Login.fs @@ -34,14 +34,14 @@ let loginMsg user pass = (function | U2.Case1 sc -> LoginSuccess sc | U2.Case2 str -> LoginFailure str) (sprintf "Login exception: %A" >> LoginFailure) -let update msg model = +let update msg model = match msg with | Username u -> { model with user = u.ToLower(); error = "" }, Cmd.Empty | Password p -> { model with password = p; error = "" }, Cmd.Empty - | Help -> + | Help -> Global.alert Global.Info "Call support" model, Cmd.Empty - | Submit -> + | Submit -> printfn "Login with %s %s" model.user model.password let isAdmin = ((model.user.ToLower().Contains "bts") || (model.user.ToLower().Contains "admin")) match isAdmin with @@ -58,37 +58,36 @@ let root model dispatch = let sparkImg = "img/BTS-300.png" let handler msg (ev:Fable.Import.React.FormEvent) = !!ev.target?value |> msg |> dispatch - section [ ClassName "hero is-fullheight is-primary" ] [ - div [ ClassName "hero-body" ] [ - div [ ClassName "container has-text-centered" ] [ - div [ ClassName "column is-4 is-offset-4" ] [ - div [ ClassName "box" ] [ + section [ ClassName "hero is-fullheight is-primary" ] [ + div [ ClassName "hero-body" ] [ + div [ ClassName "container has-text-centered" ] [ + div [ ClassName "column is-4 is-offset-4" ] [ + div [ ClassName "box" ] [ p [ Style [Margin "2em 0 3em 0"] ][ - figure [ ClassName "avatar" ] [ - img [ Src sparkImg ] + figure [ ClassName "avatar" ] [ + img [ Src sparkImg ] ] - h1 [ Style [ Float "right"; FontSize "1.2rem"; Margin "1.6rem .5rem 0 -1rem"; Transform "rotate(90deg)"] ] [ str "beta" ] h1 [ ClassName "is-size-1" ] [ span [] [str "Spark Admin"]] ] - form [ OnSubmit (fun e -> - e.preventDefault() - dispatch Submit) ] [ - div [ ClassName "field" ] [ - div [ ClassName "control" ] [ + form [ OnSubmit (fun e -> + e.preventDefault() + dispatch Submit) ] [ + div [ ClassName "field" ] [ + div [ ClassName "control" ] [ input [ ClassName "input login-input" Type "text" OnChange (handler Username) AutoFocus true Placeholder "Username" ] ] ] - div [ ClassName "field" ] [ - div [ ClassName "control" ] [ + div [ ClassName "field" ] [ + div [ ClassName "control" ] [ input [ ClassName "input login-input" Type "password" OnChange (handler Password) Placeholder "Password" ] ] ] div [ Style [Color "red"] ] [str model.error] br [] - button [ClassName "button is-block is-primary is-fullwidth" ] [ + button [ClassName "button is-block is-primary is-fullwidth" ] [ str "Login" ] ] ] ] ] ] ] From c58b5fb0e61e63f369417a5ae9523a5496ae88b8 Mon Sep 17 00:00:00 2001 From: Michael Harding Date: Fri, 7 Sep 2018 17:09:57 -0400 Subject: [PATCH 030/203] SPARK-1678: Cancel orders for account --- src/web/ui/src/Access.fs | 8 +++++ src/web/ui/src/Accounts/State.fs | 39 +++++++++++++++++++++++-- src/web/ui/src/Accounts/Types.fs | 7 +++++ src/web/ui/src/Accounts/View.fs | 50 ++++++++++++++++++++++++++++++-- 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/web/ui/src/Access.fs b/src/web/ui/src/Access.fs index 840de8fd8..9d3ba6411 100644 --- a/src/web/ui/src/Access.fs +++ b/src/web/ui/src/Access.fs @@ -12,6 +12,14 @@ let getAdminData (admin:IAdminService) (fsuc) (ffail) = fsuc (fun e -> ffail e.Message) +let getAccountOrdersData (admin:IAdminService) (success) (fail) = + Cmd.ofPromise + admin.get_order_summary + () + success + (fun e -> fail e.Message) + + module Lens = // type Setter<'a,'b> = { type Setter<'a,'b> = 'b -> 'a -> 'a diff --git a/src/web/ui/src/Accounts/State.fs b/src/web/ui/src/Accounts/State.fs index f297d27cd..ba536ea97 100644 --- a/src/web/ui/src/Accounts/State.fs +++ b/src/web/ui/src/Accounts/State.fs @@ -19,6 +19,8 @@ let init (context:ServiceContext.ServiceContext) = { tab = General admin = admin adminData = None + ordersData = [] + SelectedOrders = [] mdRelay = mdr mdRelayProductData = [||] mdRelayExpNameData = Array.empty @@ -94,9 +96,19 @@ let deleteAccount (admin:IAdminService) (account:BTSAccount) = promise { return ErrorOccured (sprintf "Error deleting account: %s" result.errorText) } +let cancelOrders (admin:IAdminService) (accountName:string) (orders:OrdersSummaryUnit list) = promise { + let users = (Array.map (fun ord -> ord.user) (Array.ofList orders)) + let accounts = [| accountName |] + let! result = admin.cancel_orders {accounts = accounts; users = users } + + let! data = admin.get_admin_data() + return UpdateAdminData data +} + + let requestProducts (mdRelay:IMarketDataRelay) = promise { - let! result = mdRelay.request_products() - return UpdateProducts result.products + let! result = mdRelay.request_products() + return UpdateProducts result.products } let requestExpNames (mdRelay:IMarketDataRelay) = promise { @@ -115,6 +127,10 @@ let clearSelection model = SelectedPositionLimits = [] } +let orderIsEqual (order1:OrdersSummaryUnit) (order2:OrdersSummaryUnit) = + (order1.account = order2.account) + && (order1.user = order2.user) + let ClipLimitIsEqual (limit1:AccountSecurityClipLimitUnit) (limit2:AccountSecurityClipLimitUnit) = (limit1.security.exchange = limit2.security.exchange) && (limit1.security.securityType = limit2.security.securityType) @@ -151,6 +167,19 @@ let update msg (model:Model) : Model * Cmd = | NoAdminData msg -> Global.alert Global.Info msg model, Cmd.Empty + | GetOrdersData -> + model, (Access.getAccountOrdersData model.admin UpdateOrdersData (box>>ErrorOccured)) + | UpdateOrdersData summary -> + {model with ordersData = List.ofArray summary.units}, Cmd.Empty + | CancelOrders orders -> + model, Cmd.ofExistingPromise (cancelOrders model.admin model.scratch.name orders) id (box>>ErrorOccured) + | SelectOrder (chckd, orders) -> + let so = match chckd, orders.Length > 1 with + | true, false -> (orders.[0]::model.SelectedOrders) + | false, false -> List.filter (fun elm -> (orderIsEqual elm orders.[0]) |> not) model.SelectedOrders + | true, true -> orders + | false, true -> [] + {model with SelectedOrders = so}, Cmd.Empty | SelectAccount account -> { (clearSelection model) with previousScratch = model.scratch; scratch = account }, Cmd.Empty | DeleteAccount account -> @@ -170,7 +199,11 @@ let update msg (model:Model) : Model * Cmd = Browser.console.log msg model,Cmd.Empty | SelectTab tab -> - { (clearSelection model) with tab = tab }, Cmd.Empty + match tab with + | OrdersTab -> + { (clearSelection model) with tab = tab }, Cmd.ofMsg GetOrdersData + | _ -> + { (clearSelection model) with tab = tab }, Cmd.Empty | Update field -> { model with scratch = applyUpdate model.scratch field }, Cmd.Empty | ClipLimits (oldLimit, newLimit) -> diff --git a/src/web/ui/src/Accounts/Types.fs b/src/web/ui/src/Accounts/Types.fs index 63f87d71a..402f009e8 100644 --- a/src/web/ui/src/Accounts/Types.fs +++ b/src/web/ui/src/Accounts/Types.fs @@ -12,6 +12,7 @@ type Tab = | CreditLimitsTab | ExchangeSettingsTab | ConnectionsTab + | OrdersTab let venues = Venue.values |> Array.skip 1 let securityTypes = SecurityType.values |> Array.skip 1 @@ -42,6 +43,10 @@ type SIMExchangeField = type Msg = | UpdateAdminData of AdminData + | GetOrdersData + | UpdateOrdersData of OrdersSummary + | SelectOrder of bool * OrdersSummaryUnit list + | CancelOrders of OrdersSummaryUnit list | NoAdminData of string | SelectAccount of BTSAccount | DeleteAccount of BTSAccount @@ -84,6 +89,8 @@ type Model = { tab : Tab admin : IAdminService adminData : AdminData option + ordersData : OrdersSummaryUnit list + SelectedOrders: OrdersSummaryUnit list mdRelay : IMarketDataRelay mdRelayProductData: Product[] mdRelayExpNameData: ExpirationNameList[] diff --git a/src/web/ui/src/Accounts/View.fs b/src/web/ui/src/Accounts/View.fs index 2fb76559c..b75251e5f 100644 --- a/src/web/ui/src/Accounts/View.fs +++ b/src/web/ui/src/Accounts/View.fs @@ -74,7 +74,50 @@ let GetFilteredSymbolArray (model:Model) (remove:string []) = |> Array.distinct) [ [|model.AllValueDisplay|]; a ] |> Array.concat -let limitFilter (positionLimit:AccountSecurityPositionLimitUnit option) (clipLimit:AccountSecurityClipLimitUnit option) allValueDisplay (array:'a []) = + +let ordersView (model:Model) dispatch = + let ordersList = List.filter (fun (elm:OrdersSummaryUnit) -> elm.account = model.scratch.name) model.ordersData + + let headerRow:TableControl.row = + { + cells = + [ + TableControl.controlCell (Fields.checkbox "" "" + (model.SelectedOrders.Length = ordersList.Length) + [] + (fun chckd -> ((chckd, ordersList) |> SelectOrder |> dispatch))) + TableControl.Cell (str "User") + TableControl.Cell (str "Total Contracts") + TableControl.Cell (str "Total Orders") + ] + className = "" + header = None + } + + let rws:TableControl.row list = + [ for od in ordersList do yield { + cells = + [ + TableControl.controlCell (Fields.checkbox "" "" + (List.exists (fun (elm:OrdersSummaryUnit) -> State.orderIsEqual elm od) model.SelectedOrders) + [] + (fun chckd -> ((chckd, [od]) |> SelectOrder |> dispatch))) + TableControl.Cell (str od.user) + TableControl.Cell (str (sprintf "%i" od.totalContracts)) + TableControl.Cell (str (sprintf "%i" od.totalOrders)) + ] + className = "" + header = None + } + ] + + let cancelButton = Button.button "Cancel Selected Orders" "is-danger" "times" Button.Enabled (fun _ -> model.SelectedOrders |> CancelOrders |> dispatch) + + div [][ + TableControl.createDataTable (Some headerRow) rws cancelButton + ] + +let limitFilter (positionLimit:AccountSecurityPositionLimitUnit option) (clipLimit:AccountSecurityClipLimitUnit option) allValueDisplay (array:ExpirationNameList []) = match positionLimit, clipLimit with | Some limit, None -> Array.filter (fun (x:bts.co2.ExpirationNameList) -> ( @@ -683,7 +726,8 @@ let tabContent model dispatch = | LimitsTab -> LimitsView model dispatch | ConnectionsTab -> ConnectionsView model dispatch | CreditLimitsTab -> creditLimitsView model dispatch - | ExchangeSettingsTab -> exchangeSettings model dispatch) + | ExchangeSettingsTab -> exchangeSettings model dispatch + | OrdersTab -> ordersView model dispatch) accountControls model dispatch ] @@ -834,6 +878,7 @@ let tabName = function | CreditLimitsTab -> "Credit Limits" | ConnectionsTab -> "Connections" | ExchangeSettingsTab -> "Exchange Settings" + | OrdersTab -> "Orders" | other -> string other let tabHeader tab currentTab dispatch = @@ -866,6 +911,7 @@ let root (model:Model) dispatch = tabHeader ConnectionsTab model.tab dispatch // tabHeader CreditLimits model.tab dispatch tabHeader ExchangeSettingsTab model.tab dispatch + tabHeader OrdersTab model.tab dispatch ] ] tabContent model dispatch From 501dc37e947b640a86a69da18bdd170622beb185 Mon Sep 17 00:00:00 2001 From: Michael Harding Date: Fri, 7 Sep 2018 17:17:42 -0400 Subject: [PATCH 031/203] F# type system is less resilient than it thinks it is --- src/web/ui/src/Accounts/State.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/ui/src/Accounts/State.fs b/src/web/ui/src/Accounts/State.fs index ba536ea97..e1d11ae06 100644 --- a/src/web/ui/src/Accounts/State.fs +++ b/src/web/ui/src/Accounts/State.fs @@ -97,7 +97,7 @@ let deleteAccount (admin:IAdminService) (account:BTSAccount) = promise { } let cancelOrders (admin:IAdminService) (accountName:string) (orders:OrdersSummaryUnit list) = promise { - let users = (Array.map (fun ord -> ord.user) (Array.ofList orders)) + let users = (Array.map (fun (ord:OrdersSummaryUnit) -> ord.user) (Array.ofList orders)) let accounts = [| accountName |] let! result = admin.cancel_orders {accounts = accounts; users = users } From bc8513778b39f537a2a65a488d6d9f017f420852 Mon Sep 17 00:00:00 2001 From: Michael Harding Date: Fri, 7 Sep 2018 17:33:20 -0400 Subject: [PATCH 032/203] Move Account controls into tab contents --- src/web/ui/src/Accounts/View.fs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/web/ui/src/Accounts/View.fs b/src/web/ui/src/Accounts/View.fs index b75251e5f..363fb42be 100644 --- a/src/web/ui/src/Accounts/View.fs +++ b/src/web/ui/src/Accounts/View.fs @@ -32,6 +32,18 @@ let saveButton model dispatch = let enabled = if dirty then Button.Enabled else Button.Disabled let flavor = if dirty then "is-success" else "" Button.button buttonText flavor "" enabled (fun _ -> dispatch SaveScratch) + +let accountControls model (dispatch:Msg->unit) = + let revertFun = + match (selectedAccount model) with + | Some acc -> fun _ -> (dispatch <| SelectAccount acc) + | None -> ignore + + div [ClassName "is-main-control-bar"] [ + saveButton model dispatch + Button.button "Revert" "" "" (if isDirty model then Button.Enabled else Button.Disabled) revertFun + ] + // // General // @@ -61,6 +73,8 @@ let generalView model dispatch = Fields.text "Description" account.description [] (Description >> Update >> dispatch) Fields.checkbox "Can Trade Into Account" "Unchecking this stops all users from entering orders into the account" account.isActive [] (IsActive >> Update >> dispatch) (if State.isNewAccount account then modal else span[][]) + + accountControls model dispatch ] let GetSymbolArray (model:Model) = @@ -624,6 +638,8 @@ let LimitsView model dispatch = clipLimitsView model dispatch productLimits model dispatch //securityLimitsView model dispatch + + accountControls model dispatch ] let ConnectionsView (model:Accounts.Types.Model) dispatch = @@ -682,6 +698,8 @@ let ConnectionsView (model:Accounts.Types.Model) dispatch = div [ ] [ TableControl.createSelectorTable (Some headerRow) rows + + accountControls model dispatch ] let creditLimitsView model dispatch = div [] [str "lorem ipsum"] @@ -706,19 +724,9 @@ let exchangeSettings model dispatch = Fields.enumSelect "Origin" (toHuman false CustomerOrFirm.values sim.origin) (allToHuman false CustomerOrFirm.values) [] (fromHuman CustomerOrFirm.values>>SIMExchangeField.Origin>>sendSim) Fields.text "Clearing Account" sim.clearingAccount [] (SIMExchangeField.ClearingAccount>>sendSim) + accountControls model dispatch ] -let accountControls model (dispatch:Msg->unit) = - let revertFun = - match (selectedAccount model) with - | Some acc -> fun _ -> (dispatch <| SelectAccount acc) - | None -> ignore - - div [ClassName "is-main-control-bar"] [ - saveButton model dispatch - Button.button "Revert" "" "" (if isDirty model then Button.Enabled else Button.Disabled) revertFun - ] - let tabContent model dispatch = div [Key (string model.scratch.id); ClassName "is-tab-content"] [ (match model.tab with @@ -728,8 +736,6 @@ let tabContent model dispatch = | CreditLimitsTab -> creditLimitsView model dispatch | ExchangeSettingsTab -> exchangeSettings model dispatch | OrdersTab -> ordersView model dispatch) - - accountControls model dispatch ] let isActive account = function From dd7ebc54a29a8cfb977c50e115f744f53c4f1b64 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Fri, 7 Sep 2018 19:22:21 -0400 Subject: [PATCH 033/203] Permissions functions in AminServiceUtils --- .../api/service_helpers/AdminServiceUtils.cpp | 11 ++++++ .../api/service_helpers/AdminServiceUtils.h | 35 +++++++++++++------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/lib/api/service_helpers/AdminServiceUtils.cpp b/src/lib/api/service_helpers/AdminServiceUtils.cpp index 6bd572002..c0ce16065 100644 --- a/src/lib/api/service_helpers/AdminServiceUtils.cpp +++ b/src/lib/api/service_helpers/AdminServiceUtils.cpp @@ -929,6 +929,17 @@ MDEntitlementReportUnit AdminServiceUtils::get_cme_md_entitlement_unit( rv.set_record_id(key.str()); return rv; } +void AdminServiceUtils::get_user_symbol_permissions(const AdminData& data, + const BTSUser& user, + std::vector&){ + std::map mic_map; + const CMEUserData* cme_data=get_user_cme_data(user,data); + if(cme_data!=nullptr) + for(const auto& mic_data : cme_data->md_permissions()) + if(mic_data.enabled()) + mic_map[mic_data.mic()]=true; + +} //////////////////////////////////////////// // user mutators /////////////////////////// //////////////////////////////////////////// diff --git a/src/lib/api/service_helpers/AdminServiceUtils.h b/src/lib/api/service_helpers/AdminServiceUtils.h index e43d43231..874b5d2b3 100644 --- a/src/lib/api/service_helpers/AdminServiceUtils.h +++ b/src/lib/api/service_helpers/AdminServiceUtils.h @@ -153,17 +153,27 @@ public: //////////////////////////////////////////// // user accessors ////////////////////////// //////////////////////////////////////////// - static bts::api::admin::BTSUser* find_user (bts::api::admin::AdminData& data, const std::string& user_name, bool include_deleted=false); - static const bts::api::admin::BTSUser* find_user (const bts::api::admin::AdminData& data, const std::string& user_name, bool include_deleted=false); - static bts::api::admin::BTSUser* find_user (bts::api::admin::AdminData& data, uint32_t id, bool include_deleted=false); - static const bts::api::admin::BTSUser* const find_user (const bts::api::admin::AdminData& data, uint32_t id, bool include_deleted=false); - static std::vector users (const bts::api::admin::AdminData& data, bool include_deleted); - static std::vector users_mutable (bts::api::admin::AdminData& data, bool include_deleted); - static uint32_t find_max_user_id (const bts::api::admin::AdminData& data); - static const bts::api::admin::CMEUserData* get_user_cme_data(const bts::api::admin::BTSUser& user, - const bts::api::admin::AdminData& data); - static const bts::api::admin::SIMUserData* get_user_sim_data(const bts::api::admin::BTSUser& user, - const bts::api::admin::AdminData& data); + static BTSUser* find_user (AdminData& data, + const std::string& user_name, + bool include_deleted=false); + static const BTSUser* find_user (const AdminData& data, + const std::string& user_name, + bool include_deleted=false); + static BTSUser* find_user (AdminData& data, + uint32_t id, + bool include_deleted=false); + static const BTSUser* const find_user (const AdminData& data, + uint32_t id, + bool include_deleted=false); + static std::vector users (const AdminData& data, + bool include_deleted); + static std::vector users_mutable (AdminData& data, + bool include_deleted); + static uint32_t find_max_user_id (const AdminData& data); + static const CMEUserData* get_user_cme_data(const BTSUser& user, + const AdminData& data); + static const SIMUserData* get_user_sim_data(const BTSUser& user, + const AdminData& data); // finds the "exchange user" (ex: "operator_id" at CME or "authorized_trader" // at ICE) for a user static std::string find_user_exchange_id (const bts::api::admin::AdminData& data, @@ -198,6 +208,9 @@ public: static MDEntitlementReportUnit get_cme_md_entitlement_unit( const std::string& user, const CMEMicData& cme_mic_data); + static void get_user_symbol_permissions(const AdminData& data, + const BTSUser& user, + std::vector& res); //////////////////////////////////////////// // user mutators /////////////////////////// //////////////////////////////////////////// From dfb33efd455297567d6edcc985a6b0819de26f15 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Sun, 9 Sep 2018 11:00:51 -0400 Subject: [PATCH 034/203] SPARK-1696 util functions added --- .../api/service_helpers/AdminServiceUtils.cpp | 39 ++- .../api/service_helpers/AdminServiceUtils.h | 305 ++++++++++-------- 2 files changed, 198 insertions(+), 146 deletions(-) diff --git a/src/lib/api/service_helpers/AdminServiceUtils.cpp b/src/lib/api/service_helpers/AdminServiceUtils.cpp index c0ce16065..40b5bfdec 100644 --- a/src/lib/api/service_helpers/AdminServiceUtils.cpp +++ b/src/lib/api/service_helpers/AdminServiceUtils.cpp @@ -1,6 +1,9 @@ #include "lib/api/services/AdminService.server.pb.h" #include "lib/api/service_helpers/AdminServiceUtils.h" #include "lib/md/get_symbols.h" +#include "lib/config/protos/cme_reference_data.pb.h" +#include "lib/config/protos/jetfix.pb.h" +#include "lib/config/config.h" #include namespace bts{ @@ -8,28 +11,30 @@ namespace admin{ //////////////////////////////////////////// // connection accessors //////////////////// //////////////////////////////////////////// -uint32_t AdminServiceUtils::find_max_connection_id (const bts::api::admin::AdminData& data){ +uint32_t AdminServiceUtils::find_max_connection_id (const AdminData& data){ uint32_t rv = 0; for(const auto &conn : data.connections()) if (conn.id() > rv) rv = conn.id(); return rv; } -const bts::api::admin::ExchangeConnection* AdminServiceUtils::find_connection(const bts::api::admin::AdminData& data, - const std::string& connection_name){ +const ExchangeConnection* AdminServiceUtils::find_connection( + const AdminData& data, + const std::string& connection_name){ for(const auto& connection : data.connections()) if(connection.name()==connection_name) return &connection; return nullptr; } -bool AdminServiceUtils::has_exchange(const bts::api::admin::ExchangeConnection& conn, - bts::api::Venue exchange){ +bool AdminServiceUtils::has_exchange(const ExchangeConnection& conn, + api::Venue exchange){ bool rv = false; switch(exchange) { case api::Venue::VENUE_GLBX: rv = conn.has_cme_connection() && - (conn.cme_connection().has_cgw_connection() || conn.cme_connection().has_msgw_connection()); + (conn.cme_connection().has_cgw_connection() || + conn.cme_connection().has_msgw_connection()); break; case api::Venue::VENUE_UNSET: case api::Venue::VENUE_SIM: @@ -931,14 +936,28 @@ MDEntitlementReportUnit AdminServiceUtils::get_cme_md_entitlement_unit( } void AdminServiceUtils::get_user_symbol_permissions(const AdminData& data, const BTSUser& user, - std::vector&){ - std::map mic_map; + std::vector& rv){ + std::map mic_perm_map; + auto exchange = api::Venue::VENUE_GLBX; const CMEUserData* cme_data=get_user_cme_data(user,data); if(cme_data!=nullptr) for(const auto& mic_data : cme_data->md_permissions()) if(mic_data.enabled()) - mic_map[mic_data.mic()]=true; - + mic_perm_map[mic_data.mic()]=true; + const auto& cme_symb_mics = config::get().cme_mic(); + for(auto it=cme_symb_mics.begin();it!=cme_symb_mics.end();it++){ + const std::string& symbol=it->first; + api::CMEMics mic=it->second; + UserSymbolMDPermissionUnit u; + std::ostringstream key; + key << exchange << user.name() << symbol; + u.set_record_id(key.str()); + u.set_user(user.name()); + u.set_exchange(exchange); + u.set_symbol(symbol); + u.set_enabled(mic_perm_map.find(mic)!=mic_perm_map.end()); + rv.push_back(u); + } } //////////////////////////////////////////// // user mutators /////////////////////////// diff --git a/src/lib/api/service_helpers/AdminServiceUtils.h b/src/lib/api/service_helpers/AdminServiceUtils.h index 874b5d2b3..9836c65ee 100644 --- a/src/lib/api/service_helpers/AdminServiceUtils.h +++ b/src/lib/api/service_helpers/AdminServiceUtils.h @@ -4,13 +4,12 @@ #include "lib/api/services/AdminService.server.pb.h" #include "lib/api/services/OrderService.server.pb.h" #include "lib/domain_model/Security.h" -#include "lib/config/protos/jetfix.pb.h" -#include "lib/config/config.h" namespace bts{ namespace admin{ using namespace bts::api::admin; +using namespace bts::api::orderman; class AdminServiceUtils{ public: @@ -19,137 +18,156 @@ public: //////////////////////////////////////////// // finds the max id out of all the existing connections // (todo: deleted connections) - static uint32_t find_max_connection_id (const bts::api::admin::AdminData& data); + static uint32_t find_max_connection_id (const AdminData& data); // finds a connection by name and returns a mutable pointer to it - static const bts::api::admin::ExchangeConnection* find_connection(const bts::api::admin::AdminData& data, - const std::string& connection_name); + static const ExchangeConnection* find_connection(const AdminData& data, + const std::string& connection_name); // finds the presence of an exchange in a connection - static bool has_exchange(const bts::api::admin::ExchangeConnection& conn, + static bool has_exchange(const ExchangeConnection& conn, bts::api::Venue exchange); // finds the default connections for an exchange // in a given account - static const bts::api::admin::ExchangeConnection* find_default_account_connection(const bts::api::admin::AdminData& data, - const bts::api::admin::BTSAccount& account, - bts::api::Venue exchange); - static const bts::api::admin::ExchangeConnection* resolve_cme_connection(const bts::api::admin::AdminData& data, - const bts::api::admin::BTSAccount& account, - const bts::api::Security& sec); - static const bts::api::admin::ExchangeConnection* resolve_sim_connection(const bts::api::admin::AdminData& data, - const bts::api::admin::BTSAccount& account, - const bts::api::Security& sec); - static const bts::api::admin::ExchangeConnection* resolve_connection(const bts::api::admin::AdminData& data, - const bts::api::admin::BTSAccount& account, - const bts::api::Security& sec); + static const ExchangeConnection* find_default_account_connection( + const AdminData& data, + const BTSAccount& account, + bts::api::Venue exchange); + static const ExchangeConnection* resolve_cme_connection( + const AdminData& data, + const BTSAccount& account, + const api::Security& sec); + static const ExchangeConnection* resolve_sim_connection( + const AdminData& data, + const BTSAccount& account, + const api::Security& sec); + static const ExchangeConnection* resolve_connection( + const AdminData& data, + const BTSAccount& account, + const bts::api::Security& sec); //////////////////////////////////////////// // connection mutators ///////////////////// //////////////////////////////////////////// // adds a new connection (does not check for name uniqueness) - static bts::api::admin::ExchangeConnection* add_connection(bts::api::admin::AdminData& data, - const bts::api::admin::ExchangeConnection& new_connection); + static ExchangeConnection* add_connection(AdminData& data, + const ExchangeConnection& new_connection); // reads the connections from config and fills in the // admin data (with names only) - static void fill_connections_from_config(bts::api::admin::AdminData& admin_data); + static void fill_connections_from_config(AdminData& admin_data); //////////////////////////////////////////// // account accessors /////////////////////// //////////////////////////////////////////// // finds the account from the name and returns // a mutable pointer to the account - static bts::api::admin::BTSAccount* find_account (bts::api::admin::AdminData& data, const std::string& acct_name, bool include_deleted=false); + static BTSAccount* find_account (AdminData& data, + const std::string& acct_name, + bool include_deleted=false); // same as above for const pointer - static const bts::api::admin::BTSAccount* find_account (const bts::api::admin::AdminData& data, const std::string& acct_name, bool include_deleted=false); + static const BTSAccount* find_account (const AdminData& data, + const std::string& acct_name, + bool include_deleted=false); // finds the account from the id and returns // a mutable pointer to the account - static bts::api::admin::BTSAccount* find_account (bts::api::admin::AdminData& data, uint32_t id, bool include_deleted=false); + static BTSAccount* find_account (AdminData& data, + uint32_t id, + bool include_deleted=false); // like above but returns a const pointer - static const bts::api::admin::BTSAccount* find_account (const bts::api::admin::AdminData& data, uint32_t id, bool include_deleted=false); + static const BTSAccount* find_account (const AdminData& data, + uint32_t id, + bool include_deleted=false); // this function is an indirection layer for account access // so deletions are dealt in a more deliberate manner - static std::vector accounts (const bts::api::admin::AdminData& data, bool include_deleted); - static std::vector accounts_mutable (bts::api::admin::AdminData& data, bool include_deleted); + static std::vector accounts (const AdminData& data, + bool include_deleted); + static std::vector accounts_mutable (AdminData& data, + bool include_deleted); // finds the max id amongts all the existing accounts - static uint32_t find_max_acct_id (const bts::api::admin::AdminData& data); + static uint32_t find_max_acct_id (const AdminData& data); // gets the account data for cme (and goes up the hierarchy // if needed) - static const bts::api::admin::CMEAccountData* get_account_cme_data(const bts::api::admin::BTSAccount& account, - const bts::api::admin::AdminData& data); - static const bts::api::admin::SIMAccountData* get_account_sim_data(const bts::api::admin::BTSAccount& account, - const bts::api::admin::AdminData& data); + static const CMEAccountData* get_account_cme_data(const BTSAccount& account, + const AdminData& data); + static const SIMAccountData* get_account_sim_data(const BTSAccount& account, + const AdminData& data); // returns true if an account has a connection - bool static account_has_connection(const bts::api::admin::BTSAccount& account, const std::string& connection_name); + bool static account_has_connection(const BTSAccount& account, + const std::string& connection_name); // finds the external/clearing account associated // with an account - static std::string find_account_exchange_account (const bts::api::admin::AdminData& data, - const bts::api::admin::BTSAccount& account, - api::Venue exchange, - bts::api::admin::AdminServiceOperationResult& out_result); + static std::string find_account_exchange_account (const AdminData& data, + const BTSAccount& account, + api::Venue exchange, + AdminServiceOperationResult& out_result); // returns a map of the accounts vs their 'is_active' - static std::map accounts_map(const bts::api::admin::AdminData& admin_data); + static std::map accounts_map(const AdminData& admin_data); // validates that the update to the account is valid - static void validate_account_update (const bts::api::admin::AdminData& admin_data, - const bts::api::admin::BTSAccount& updated_account, - bts::api::admin::AdminServiceOperationResult& result); + static void validate_account_update (const AdminData& admin_data, + const BTSAccount& updated_account, + AdminServiceOperationResult& result); // validates that the account exchange changes are valid // (ex > 12 characters for accounts at CME) - static void validate_account_exchange_data (const bts::api::admin::BTSAccount& account, - bts::api::admin::AdminServiceOperationResult& out_result); - static bts::api::orderman::RoutingRecord get_routing_record(const bts::api::admin::AdminData& data, - const std::string& account_name, - const std::string& user_name, - const bts::api::Security& sec, - bts::api::admin::AdminServiceOperationResult& out_result); - static bts::api::orderman::RoutingRecord get_routing_record(const bts::api::admin::AdminData& data, - uint32_t account_id, - uint32_t user_id, - const bts::api::Security& sec, - bts::api::admin::AdminServiceOperationResult& out_result); + static void validate_account_exchange_data (const BTSAccount& account, + AdminServiceOperationResult& out_result); + static RoutingRecord get_routing_record( + const AdminData& data, + const std::string& account_name, + const std::string& user_name, + const bts::api::Security& sec, + AdminServiceOperationResult& out_result); + static RoutingRecord get_routing_record(const AdminData& data, + uint32_t account_id, + uint32_t user_id, + const bts::api::Security& sec, + AdminServiceOperationResult& out_result); //////////////////////////////////////////// // account mutators //////////////////////// //////////////////////////////////////////// - static bts::api::admin::BTSAccount* create_account(bts::api::admin::AdminData& data, - uint32_t parent_account_id, - const std::string& account_name, - const std::string& description, - bool is_active, - bts::api::admin::AdminServiceCreateAccountResult& result); + static BTSAccount* create_account(AdminData& data, + uint32_t parent_account_id, + const std::string& account_name, + const std::string& description, + bool is_active, + AdminServiceCreateAccountResult& result); // writes over the account data completely - static void update_account(bts::api::admin::AdminData& data, - const bts::api::admin::BTSAccount& updated_account, - bts::api::admin::AdminServiceOperationResult& result); - static void delete_account (bts::api::admin::AdminData& admin_data, - const bts::api::admin::IDAPIArgument& id, - bts::api::admin::AdminServiceOperationResult& result); - static void set_account_exchange_data(bts::api::admin::BTSAccount* account, - const bts::api::admin::ExchangeAccountData& exchange_data); + static void update_account(AdminData& data, + const BTSAccount& updated_account, + AdminServiceOperationResult& result); + static void delete_account (AdminData& admin_data, + const IDAPIArgument& id, + AdminServiceOperationResult& result); + static void set_account_exchange_data(BTSAccount* account, + const ExchangeAccountData& exchange_data); // this is not a generally useful function but since // connection are in config, this cleans up the account's // connections and syncs it with the list in AdminData - static void sync_account_connections(const bts::api::admin::AdminData& data, - bts::api::admin::BTSAccount& account); + static void sync_account_connections(const AdminData& data, + BTSAccount& account); //////////////////////////////////////////// // group accessors ///////////////////////// //////////////////////////////////////////// // given an id, finds the mutable pointer to a group - static bts::api::admin::BTSUserGroup* find_group (bts::api::admin::AdminData& data, uint32_t id); + static BTSUserGroup* find_group (AdminData& data, uint32_t id); // given an id, finds the const pointer to a group - static const bts::api::admin::BTSUserGroup* const find_group (const bts::api::admin::AdminData& data, uint32_t id); + static const BTSUserGroup* const find_group (const AdminData& data, + uint32_t id); // given a name, finds the mutable pointer to a group - static bts::api::admin::BTSUserGroup* find_group (bts::api::admin::AdminData& data, const std::string& group_name); + static BTSUserGroup* find_group (AdminData& data, + const std::string& group_name); // finds the max id out of all the groups - static uint32_t find_max_group_id (const bts::api::admin::AdminData& data); + static uint32_t find_max_group_id (const AdminData& data); // travels up the group tree until it finds a populated cme data - static const bts::api::admin::CMEUserData* get_group_cme_data(const bts::api::admin::BTSUserGroup& group, - const bts::api::admin::AdminData& data); - static const bts::api::admin::SIMUserData* get_group_sim_data(const bts::api::admin::BTSUserGroup& group, - const bts::api::admin::AdminData& data); - static const bts::api::admin::UserAccountPermissions* const get_group_user_permissions (const bts::api::admin::AdminData& admin_data, - uint32_t group_id); + static const CMEUserData* get_group_cme_data(const BTSUserGroup& group, + const AdminData& data); + static const SIMUserData* get_group_sim_data(const BTSUserGroup& group, + const AdminData& data); + static const UserAccountPermissions* const get_group_user_permissions ( + const bts::api::admin::AdminData& admin_data, + uint32_t group_id); //////////////////////////////////////////// // group mutators/ ///////////////////////// //////////////////////////////////////////// - static bts::api::admin::BTSUserGroup* create_group(bts::api::admin::AdminData& data, - uint32_t parent_group_id, - const std::string& group_name, - bts::api::admin::AdminServiceOperationResult& result); + static BTSUserGroup* create_group(AdminData& data, + uint32_t parent_group_id, + const std::string& group_name, + AdminServiceOperationResult& result); //////////////////////////////////////////// // user accessors ////////////////////////// //////////////////////////////////////////// @@ -176,95 +194,110 @@ public: const AdminData& data); // finds the "exchange user" (ex: "operator_id" at CME or "authorized_trader" // at ICE) for a user - static std::string find_user_exchange_id (const bts::api::admin::AdminData& data, - const bts::api::admin::BTSUser& user, - api::Venue exchange, - bts::api::admin::AdminServiceOperationResult& out_result); - static std::pair - find_user_account_matches (const bts::api::admin::AdminData& admin_data, - const std::string& connection, const std::string& exchange_account, + static std::string find_user_exchange_id ( + const AdminData& data, + const BTSUser& user, + api::Venue exchange, + AdminServiceOperationResult& out_result); + static std::pair + find_user_account_matches (const AdminData& admin_data, + const std::string& connection, + const std::string& exchange_account, const std::string& exchange_user, api::Venue exchange); // for a given user finds the account permissions (if permissions are // derived from the group, it traverses the group hierarcy to find the // correct permissions. - static const bts::api::admin::UserAccountPermissions* get_user_permissions (const bts::api::admin::AdminData& admin_data, uint32_t user_id); + static const UserAccountPermissions* get_user_permissions ( + const AdminData& admin_data, + uint32_t user_id); // for a given user, fills in the associated_accounts ref arg // with the accounts the user has permission to trade in - static void find_user_trading_accounts (const bts::api::admin::AdminData& admin_data, - uint32_t user_id, - std::vector& associated_accounts); + static void find_user_trading_accounts ( + const AdminData& admin_data, + uint32_t user_id, + std::vector& associated_accounts); // validates that changes in user data satisfy all the // constraints - static void validate_user_update (const bts::api::admin::AdminData& admin_data, - const bts::api::admin::BTSUser& updated_user, - bts::api::admin::AdminServiceOperationResult& result); + static void validate_user_update (const AdminData& admin_data, + const BTSUser& updated_user, + AdminServiceOperationResult& result); // vaidates the exchange part of user data updates - static void validate_user_exchange_data (const bts::api::admin::BTSUser& updated_user, - bts::api::admin::AdminServiceOperationResult& out_result); + static void validate_user_exchange_data ( + const BTSUser& updated_user, + AdminServiceOperationResult& out_result); static void get_md_entitlement_report(const AdminData& admin_data, std::vector& entitlements); // Gets a reporting unit out of CMEMicData static MDEntitlementReportUnit get_cme_md_entitlement_unit( const std::string& user, const CMEMicData& cme_mic_data); - static void get_user_symbol_permissions(const AdminData& data, - const BTSUser& user, + static void get_user_symbol_permissions( + const AdminData& data, + const BTSUser& user, std::vector& res); //////////////////////////////////////////// // user mutators /////////////////////////// //////////////////////////////////////////// - static bts::api::admin::BTSUser* create_user (bts::api::admin::AdminData& data, - uint32_t parent_group_id, - const std::string& name, - const std::string& password, - bool is_active, - const bts::api::admin::LicenseData& license_data, - const bts::api::admin::LicenseEntitlements& license_entitlements, - bts::api::admin::AdminServiceCreateEntityResult& result); - static bts::api::admin::BTSUser* update_user(bts::api::admin::AdminData& admin_data, - const bts::api::admin::BTSUser& updated_user, - bts::api::admin::AdminServiceOperationResult& result); - static void delete_user (bts::api::admin::AdminData& admin_data, - const bts::api::admin::IDAPIArgument& id, - bts::api::admin::AdminServiceOperationResult& result); - static void set_user_exchange_data(bts::api::admin::BTSUser* user, - const bts::api::admin::ExchangeUserData& exchange_user_data); - static void set_user_md_entitlement_data(bts::api::admin::BTSUser* user, - const bts::api::admin::ExchangeUserMarketDataEntitlementData& exchange_user_md_entitelemnt_data); - static void clear_user_accounts (bts::api::admin::BTSUser* user); - static void set_user_accounts (bts::api::admin::BTSUser* user, - const bts::api::admin::UserAccountPermissions& user_account_permissions); + static BTSUser* create_user (AdminData& data, + uint32_t parent_group_id, + const std::string& name, + const std::string& password, + bool is_active, + const LicenseData& license_data, + const LicenseEntitlements& license_entitlements, + AdminServiceCreateEntityResult& result); + static BTSUser* update_user(AdminData& admin_data, + const BTSUser& updated_user, + AdminServiceOperationResult& result); + static void delete_user (AdminData& admin_data, + const IDAPIArgument& id, + AdminServiceOperationResult& result); + static void set_user_exchange_data( + BTSUser* user, + const ExchangeUserData& exchange_user_data); + static void set_user_md_entitlement_data(BTSUser* user, + const ExchangeUserMarketDataEntitlementData&); + static void clear_user_accounts (BTSUser* user); + static void set_user_accounts (BTSUser* user, + const UserAccountPermissions& user_account_permissions); //////////////////////////////////////////// //risk limits ////////////////////////////// //////////////////////////////////////////// // sorting function used by sort_limits to // sort SecurityScope's - static bool _sort_security_scopes (const bts::api::admin::SecurityScope& a, const bts::api::admin::SecurityScope& b); + static bool _sort_security_scopes (const SecurityScope& a, + const SecurityScope& b); // sorting function used by sort_limits to // sort ProductScope's - static bool _sort_product_scopes (const bts::api::admin::ProductScope& a, const bts::api::admin::ProductScope&); + static bool _sort_product_scopes (const ProductScope& a, + const ProductScope&); // this sorts the rules such that the most // specific ones are at the top. this makes it // easier to match rules with securities/products - static void sort_limits (bts::api::admin::BTSAccount& account); + static void sort_limits (BTSAccount& account); // creates a copy of AdminData with sorted rules - static bts::api::admin::AdminData sort_limits (const bts::api::admin::AdminData& admin_data); + static AdminData sort_limits (const AdminData& admin_data); // find the clip size for a security, account - static const uint32_t find_account_security_clip_size(const bts::api::admin::BTSAccount& account, - const api::Security& security); + static const uint32_t find_account_security_clip_size( + const BTSAccount& account, + const api::Security& security); // find the position limit for an account, security - static const bts::api::admin::AccountSecurityPositionLimitUnit* find_account_security_position_limit(const bts::api::admin::BTSAccount& account, + static const AccountSecurityPositionLimitUnit* + find_account_security_position_limit(const BTSAccount& account, const api::Security& security); // find the position limit for an account, product - static const bts::api::admin::AccountProductPositionLimitUnit* find_account_product_position_limit(const bts::api::admin::BTSAccount& account, + static const AccountProductPositionLimitUnit* + find_account_product_position_limit(const BTSAccount& account, const api::Product& product); - static bool match_security_scope (const bts::api::admin::SecurityScope& scope, - const bts::api::Security& security); - static bool match_product_scope (const bts::api::admin::ProductScope& scope, - const bts::api::Product& product); - static bool match_product(const api::Security& security, const api::Product& product); - static bool _match_limit_rule_symbol (const std::string& symbol, const std::string& limit_symbol); + static bool match_security_scope (const SecurityScope& scope, + const api::Security& security); + static bool match_product_scope (const ProductScope& scope, + const api::Product& product); + static bool match_product(const api::Security& security, + const api::Product& product); + static bool _match_limit_rule_symbol (const std::string& symbol, + const std::string& limit_symbol); static api::SecurityType security_type(const api::Security& security); //////////////////////////////////////////// // misc//////// //////////////////////////// From ee825f76c7dd94a64d527b1225af6bbf1ec6b5b1 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Sun, 9 Sep 2018 21:27:54 -0400 Subject: [PATCH 035/203] SPARK-1696 completed --- src/lib/api/services/AdminService.proto | 2 +- src/server/sparkd/user/AdminServiceServer.cpp | 87 +++++++++++++------ src/server/sparkd/user/AdminServiceServer.h | 51 ++++++----- 3 files changed, 90 insertions(+), 50 deletions(-) diff --git a/src/lib/api/services/AdminService.proto b/src/lib/api/services/AdminService.proto index 4646343c9..3e13152f1 100644 --- a/src/lib/api/services/AdminService.proto +++ b/src/lib/api/services/AdminService.proto @@ -446,5 +446,5 @@ service AdminService { // reports rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEntitlementReportUnit); // misc - rpc md_permissions(bts.api.VoidMessage) returns (stream UserSymbolMDPermissionUnit); + rpc md_permissions_data(bts.api.VoidMessage) returns (stream UserSymbolMDPermissionUnit); } \ No newline at end of file diff --git a/src/server/sparkd/user/AdminServiceServer.cpp b/src/server/sparkd/user/AdminServiceServer.cpp index 9c992c8fd..28b3031a7 100644 --- a/src/server/sparkd/user/AdminServiceServer.cpp +++ b/src/server/sparkd/user/AdminServiceServer.cpp @@ -26,11 +26,12 @@ AdminServiceServer::AdminServiceServer(bts::api::ServiceContext& context) _store(new bts::persistence::SqliteStore(_db_name)), _publisher(make_admin_data_stateful_publisher()), _md_entit_publisher(make_md_entitlement_data_stateful_publisher()), + _md_perm_publisher(make_md_permissions_data_stateful_publisher()), _order_service(context.locate()), _order_map() { LOG(Gen, Info, "AdminServiceServer: service started"); - std::map> all_data = _store->retrieve_all(); + map> all_data = _store->retrieve_all(); auto table = all_data.find(_table_name); bool data_found = false; if (table != all_data.end()) @@ -68,49 +69,66 @@ AdminServiceServer::AdminServiceServer(bts::api::ServiceContext& context) }); } AdminServiceServer::~AdminServiceServer() {} -void AdminServiceServer::create_account(const api::admin::AccountCreateAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceCreateAccountResult>& reply){ +void AdminServiceServer::create_account( + const AccountCreateAPIArgument& request, + const api::Response& reply){ bts::api::admin::AdminServiceCreateAccountResult result; - AdminServiceUtils::create_account(_state, request.parent_account_id(), request.account_name(), request.description(), false, result); + AdminServiceUtils::create_account(_state, + request.parent_account_id(), + request.account_name(), + request.description(), + false, + result); _update_and_publish(false); reply(result); } -void AdminServiceServer::update_account(const api::admin::AccountUpdateAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply){ +void AdminServiceServer::update_account( + const AccountUpdateAPIArgument& request, + const api::Response& reply){ bts::api::admin::AdminServiceOperationResult result; AdminServiceUtils::update_account(_state, request.complete_data(), result); _update_and_publish(false); reply(result); } -void AdminServiceServer::create_user(const api::admin::UserCreateAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceCreateEntityResult>& reply){ - bts::api::admin::AdminServiceCreateEntityResult result; - AdminServiceUtils::create_user(_state, request.parent_group_id(), request.name(), request.password(), false, - request.license_data(), request.license_entitlements(), result); +void AdminServiceServer::create_user( + const UserCreateAPIArgument& request, + const api::Response& reply){ + AdminServiceCreateEntityResult result; + AdminServiceUtils::create_user(_state, + request.parent_group_id(), + request.name(), + request.password(), + false, + request.license_data(), + request.license_entitlements(), + result); _update_and_publish(false); reply(result); } -void AdminServiceServer::update_user(const api::admin::UserUpdateAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply){ - bts::api::admin::AdminServiceOperationResult result; +void AdminServiceServer::update_user( + const UserUpdateAPIArgument& request, + const api::Response& reply){ + AdminServiceOperationResult result; AdminServiceUtils::update_user(_state, request.complete_data(), result); _update_and_publish(false); reply(result); } void AdminServiceServer::get_admin_data(const bts::api::VoidMessage&, - const api::Response<::bts::api::admin::AdminData>& reply){ + const api::Response& reply){ reply(_state); } -void AdminServiceServer::delete_user(const bts::api::admin::IDAPIArgument& id, - const api::Response& reply){ - bts::api::admin::AdminServiceOperationResult result; result.set_is_success(true); - bts::admin::AdminServiceUtils::delete_user(_state, id, result); +void AdminServiceServer::delete_user( + const IDAPIArgument& id, + const api::Response& reply){ + AdminServiceOperationResult result; result.set_is_success(true); + AdminServiceUtils::delete_user(_state, id, result); _update_and_publish(false); reply(result); } -void AdminServiceServer::delete_account(const bts::api::admin::IDAPIArgument& id, - const api::Response& reply){ - bts::api::admin::AdminServiceOperationResult result; result.set_is_success(true); +void AdminServiceServer::delete_account( + const IDAPIArgument& id, + const api::Response& reply){ + AdminServiceOperationResult result; result.set_is_success(true); bts::admin::AdminServiceUtils::delete_account(_state, id, result); _update_and_publish(false); reply(result); @@ -127,13 +145,15 @@ void AdminServiceServer::_update_and_publish(bool is_insert){ _publisher.add_or_update(_state); _publisher.flush(); _publish_md_entitlements(); + _publish_md_permissions(); } else LOG(Gen, Error, "Could not serialize admin data, saving failed"); } -void AdminServiceServer::get_order_summary(const bts::api::VoidMessage&, - const api::Response<::bts::api::admin::OrdersSummary>& reply){ +void AdminServiceServer::get_order_summary( + const bts::api::VoidMessage&, + const api::Response& reply){ std::map> agg; for(auto it=_order_map.begin();it!=_order_map.end();it++){ const OrderRecord& ord=*(it->second); @@ -152,8 +172,9 @@ void AdminServiceServer::get_order_summary(const bts::api::VoidMessage&, } reply(rv); } -void AdminServiceServer::cancel_orders(const api::admin::OrderCancelRequest& req, - const api::Response&){ +void AdminServiceServer::cancel_orders( + const OrderCancelRequest& req, + const api::Response&){ vector accts, users; for(auto it=req.accounts().begin();it!=req.accounts().end();it++) accts.push_back(*it); @@ -162,8 +183,9 @@ void AdminServiceServer::cancel_orders(const api::admin::OrderCancelRequest& req _cancel_orders(accts,users); } -void AdminServiceServer::_add_live_order(std::map>& agg, - const OrderRecord& ord){ +void AdminServiceServer::_add_live_order( + map>& agg, + const OrderRecord& ord){ const std::string& acct=ord.def().account(); const std::string& user=ord.def().user(); auto acct_it=agg.find(acct); @@ -208,10 +230,19 @@ void AdminServiceServer::_cancel_orders(const vector& accounts, } } void AdminServiceServer::_publish_md_entitlements(){ + // TODO, need to delete all state std::vector entitlements; AdminServiceUtils::get_md_entitlement_report(_state,entitlements); for (const auto& e : entitlements) _md_entit_publisher.add_or_update(e); _md_entit_publisher.flush(); } +void AdminServiceServer::_publish_md_permissions(){ + std::vector perms; + for (const BTSUser* user : AdminServiceUtils::users(_state,false)) + AdminServiceUtils::get_user_symbol_permissions(_state,*user,perms); + for (const auto& p : perms) + _md_perm_publisher.add_or_update(p); + _md_perm_publisher.flush(); +} }} diff --git a/src/server/sparkd/user/AdminServiceServer.h b/src/server/sparkd/user/AdminServiceServer.h index 3702eff7d..32bbe126c 100644 --- a/src/server/sparkd/user/AdminServiceServer.h +++ b/src/server/sparkd/user/AdminServiceServer.h @@ -23,46 +23,55 @@ private: const std::string _unique_key; bts::api::admin::AdminData _state; std::unique_ptr _store; - bts::api::admin::AdminServiceBase::Admin_dataStatefulPublisher _publisher; - bts::api::admin::AdminServiceBase::Md_entitlement_dataStatefulPublisher _md_entit_publisher; - bts::api::orderman::OrderService _order_service; - std::map> _order_map; + AdminServiceBase::Admin_dataStatefulPublisher _publisher; + AdminServiceBase::Md_entitlement_dataStatefulPublisher _md_entit_publisher; + AdminServiceBase::Md_permissions_dataStatefulPublisher _md_perm_publisher; + OrderService _order_service; + map> _order_map; public: //BTS_NO_COPY(UserModelServiceServer); AdminServiceServer(bts::api::ServiceContext& context); ~AdminServiceServer(); - void create_account(const api::admin::AccountCreateAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceCreateAccountResult>& reply) override; + void create_account( + const AccountCreateAPIArgument& request, + const api::Response& reply) override; - void update_account(const api::admin::AccountUpdateAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply) override; + void update_account( + const AccountUpdateAPIArgument& request, + const api::Response& reply) override; - void create_user(const api::admin::UserCreateAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceCreateEntityResult>& reply) override; + void create_user( + const UserCreateAPIArgument& request, + const api::Response& reply) override; - void update_user(const api::admin::UserUpdateAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply) override; + void update_user( + const UserUpdateAPIArgument& request, + const api::Response& reply) override; void get_admin_data(const bts::api::VoidMessage& request, - const api::Response<::bts::api::admin::AdminData>& reply) override; + const api::Response& reply) override; - void delete_user(const bts::api::admin::IDAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply) override; + void delete_user( + const IDAPIArgument& request, + const api::Response& reply) override; - void delete_account(const bts::api::admin::IDAPIArgument& request, - const api::Response<::bts::api::admin::AdminServiceOperationResult>& reply) override; + void delete_account( + const IDAPIArgument& request, + const api::Response& reply) override; void get_order_summary(const bts::api::VoidMessage& request, - const api::Response<::bts::api::admin::OrdersSummary>& reply) override; + const api::Response& reply) override; - void cancel_orders(const api::admin::OrderCancelRequest& request, - const api::Response& reply) override; + void cancel_orders( + const OrderCancelRequest& request, + const api::Response& reply) override; private: void _update_and_publish (bool is_insert); void _publish_md_entitlements(); - void _add_live_order(std::map>& agg, + void _publish_md_permissions(); + void _add_live_order(map>& agg, const OrderRecord& order); void _cancel_orders(const std::vector& accounts, const std::vector& users); From 58c298fed8fb31e12dac6df87e20d46505e4515f Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Mon, 10 Sep 2018 10:18:05 -0400 Subject: [PATCH 036/203] Changing the AdminService -> OrderService call to async --- src/server/sparkd/user/AdminServiceServer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server/sparkd/user/AdminServiceServer.cpp b/src/server/sparkd/user/AdminServiceServer.cpp index 28b3031a7..5ae3c2319 100644 --- a/src/server/sparkd/user/AdminServiceServer.cpp +++ b/src/server/sparkd/user/AdminServiceServer.cpp @@ -223,10 +223,11 @@ void AdminServiceServer::_cancel_orders(const vector& accounts, user_map.find(ord.def().user()) != user_map.end()) keys.push_back(ord.key()); } + auto cb = [&](bts::api::Result<::bts::api::orderman::Response>) -> void {}; for(uint32_t k : keys){ Cancel c; - c.set_order_key(k); - _order_service.cancel_order(c); + c.set_order_key(k); + _order_service.cancel_order(c, cb); } } void AdminServiceServer::_publish_md_entitlements(){ From f3d2c9e5b990d8a8279fc57e2efc7b099949a793 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Mon, 10 Sep 2018 12:39:22 -0400 Subject: [PATCH 037/203] SPARK-1672 VAP initial check in --- .../api/services/MarketDataAuxiliaryService.proto | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/lib/api/services/MarketDataAuxiliaryService.proto diff --git a/src/lib/api/services/MarketDataAuxiliaryService.proto b/src/lib/api/services/MarketDataAuxiliaryService.proto new file mode 100644 index 000000000..107336b45 --- /dev/null +++ b/src/lib/api/services/MarketDataAuxiliaryService.proto @@ -0,0 +1,14 @@ +syntax="proto3"; +package bts.co2.auxiliary; +option csharp_namespace="bts.co2.auxiliary"; +import "lib/api/apioptions.proto"; +import "lib/api/apicore.proto"; +import "lib/domain_model/Security.proto"; +import "lib/md/protos/co2.proto"; + +message VolumeAtPriceLevel{ + int32 record_id = 1 [(bts.api.key)=true]; + uint32 security_id = 20; + double price = 30; + uint32 volume = 40; +} \ No newline at end of file From d29ec26beb002589eb1445c53ef20551798e39c2 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Mon, 10 Sep 2018 15:09:16 -0400 Subject: [PATCH 038/203] SPARK-1672 scaffolding --- .../services/MarketDataAuxiliaryService.proto | 9 ++++- .../md/MarketDataAuxiliaryServiceServer.cpp | 39 +++++++++++++++++++ .../md/MarketDataAuxiliaryServiceServer.h | 22 +++++++++++ .../sparkd/position/PositionServiceServer.cpp | 3 ++ .../sparkd/position/PositionServiceServer.h | 15 ++++--- 5 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp create mode 100644 src/server/sparkd/md/MarketDataAuxiliaryServiceServer.h diff --git a/src/lib/api/services/MarketDataAuxiliaryService.proto b/src/lib/api/services/MarketDataAuxiliaryService.proto index 107336b45..c207ce4ab 100644 --- a/src/lib/api/services/MarketDataAuxiliaryService.proto +++ b/src/lib/api/services/MarketDataAuxiliaryService.proto @@ -1,6 +1,6 @@ syntax="proto3"; -package bts.co2.auxiliary; -option csharp_namespace="bts.co2.auxiliary"; +package bts.api.md.auxiliary; +option csharp_namespace="bts.api.md.auxiliary"; import "lib/api/apioptions.proto"; import "lib/api/apicore.proto"; import "lib/domain_model/Security.proto"; @@ -11,4 +11,9 @@ message VolumeAtPriceLevel{ uint32 security_id = 20; double price = 30; uint32 volume = 40; +} + +service MarketDataAuxiliaryService{ + option (bts.api.service_description)="Provides non-core supplementary market data"; + rpc volume_at_price(bts.api.VoidMessage) returns (stream VolumeAtPriceLevel); } \ No newline at end of file diff --git a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp new file mode 100644 index 000000000..6000acc45 --- /dev/null +++ b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp @@ -0,0 +1,39 @@ +#include "MarketDataAuxiliaryServiceServer.h" +#include "lib/api/services/MarketDataRelay.pb.h" +#include "lib/api/services/MarketDataRelay.client.pb.h" +#include "lib/api/public/ServiceContext.h" +#include "lib/logging/logging.h" +#include "lib/utils/PBUtils.h" + +namespace bts{ +namespace md{ +namespace auxiliary{ + +using namespace bts::co2; +using namespace bts::api::md::auxiliary; + +struct MarketDataAuxiliaryServiceServer::PImpl{ + MarketDataAuxiliaryServiceBase::Volume_at_priceStatefulPublisher publisher; + MarketDataRelay relay_service; + int id; + PImpl(bts::api::ServiceContext& context, + const MarketDataAuxiliaryServiceBase::Volume_at_priceStatefulPublisher& publisher) + : + publisher(publisher), + relay_service(context.locate()), + id(0) + { + } +}; + +MarketDataAuxiliaryServiceServer::MarketDataAuxiliaryServiceServer(bts::api::ServiceContext& context) +: +MarketDataAuxiliaryServiceBase(context), +me(new PImpl(context, + make_volume_at_price_stateful_publisher())){ + +} + +MarketDataAuxiliaryServiceServer::~MarketDataAuxiliaryServiceServer(){ +} +}}} \ No newline at end of file diff --git a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.h b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.h new file mode 100644 index 000000000..96426ae24 --- /dev/null +++ b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "lib/api/services/MarketDataAuxiliaryService.server.pb.h" +#include "lib/utils/bts_no_copy.h" + + +namespace bts{ +namespace api {class ServiceContext;} +namespace md{ +namespace auxiliary{ + +class MarketDataAuxiliaryServiceServer : public bts::api::md::auxiliary::MarketDataAuxiliaryServiceBase +{ +private: + struct PImpl; + std::unique_ptr me; +public: + MarketDataAuxiliaryServiceServer(bts::api::ServiceContext& context); + ~MarketDataAuxiliaryServiceServer(); +}; +}}} diff --git a/src/server/sparkd/position/PositionServiceServer.cpp b/src/server/sparkd/position/PositionServiceServer.cpp index ef1eac047..274ae22b9 100644 --- a/src/server/sparkd/position/PositionServiceServer.cpp +++ b/src/server/sparkd/position/PositionServiceServer.cpp @@ -15,6 +15,9 @@ namespace bts { namespace position{ +using namespace bts::api::position; +using namespace bts::api::orderman; + struct PositionServiceServer::PImpl { PositionServiceBase::Position_dataStatefulPublisher publisher; bts::api::orderman::OrderService order_service; diff --git a/src/server/sparkd/position/PositionServiceServer.h b/src/server/sparkd/position/PositionServiceServer.h index e5b6c441a..67f7b1384 100644 --- a/src/server/sparkd/position/PositionServiceServer.h +++ b/src/server/sparkd/position/PositionServiceServer.h @@ -10,10 +10,7 @@ namespace api {class ServiceContext;} namespace api {namespace orderman {class Fill;}} namespace position{ -using namespace bts::api::position; -using namespace bts::api::orderman; - -class PositionServiceServer : public PositionServiceBase +class PositionServiceServer : public bts::api::position::PositionServiceBase { private: struct PImpl; @@ -22,11 +19,13 @@ private: // this is the function that aggrgates the // position. is_add is to indicate if a row // is added or removed - const PositionUnit& _on_fill (const Fill& fill, bool is_add); + const bts::api::position::PositionUnit& _on_fill ( + const bts::api::orderman::Fill& fill, + bool is_add); // given an account and key, finds (or adds if absent) // the PositionUnit entry - PositionUnit& _get_or_add (const std::string& acct, - uint32_t sec_id); + bts::api::position::PositionUnit& _get_or_add (const std::string& acct, + uint32_t sec_id); static void _avg_price_qty(double base_p, double base_q, double fill_p, @@ -35,7 +34,7 @@ private: const std::string& fill_id, double& avg_price, double& tot_qty); - static std::string _fill_account(const Fill& fill); + static std::string _fill_account(const bts::api::orderman::Fill& fill); public: PositionServiceServer(bts::api::ServiceContext& context); ~PositionServiceServer(); From 77854a3cf0a0283b45a2cd658a49d7e6cd3a7cc0 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 10 Sep 2018 18:56:26 -0400 Subject: [PATCH 039/203] SPARK-1714 Ladder account - unable to pick a selection on server restart --- .../spark/Orders/OrderPropertiesEditorVM.cs | 19 ++- .../spark/Orders/OrderTypeVMs/IOrderTypeVM.cs | 4 + .../spark/core/Utility/ViewModelBase.cs | 122 +++++++++++------- .../spark/view/Spark/Ladder/LadderVM.cs | 2 +- .../spark_core/utils/EqualityComparer.cs | 3 + 5 files changed, 93 insertions(+), 57 deletions(-) diff --git a/src/client/spark/Orders/OrderPropertiesEditorVM.cs b/src/client/spark/Orders/OrderPropertiesEditorVM.cs index 04ce0b641..509c67f35 100644 --- a/src/client/spark/Orders/OrderPropertiesEditorVM.cs +++ b/src/client/spark/Orders/OrderPropertiesEditorVM.cs @@ -94,7 +94,7 @@ namespace mbts.spark { class OrderPropertiesEditorVM : DisposableViewModelBase { readonly OrdersModel _model; readonly Property _security; - readonly SelectorProperty _accounts; + readonly SelectorProperty _accounts; readonly SelectorProperty _time_in_force; readonly SelectorProperty _order_type; IDisposable _order_type_param_errors_sub; @@ -113,14 +113,21 @@ namespace mbts.spark { nameof(selected_account), model.factory.subscribe_account_tuples(), order_properties.Select(ops => ops.account), - () => accounts?.FirstOrDefault() ?? default, - val => (!string.IsNullOrWhiteSpace(val.name) && accounts.Contains(val), "Please select an account")); + a => new AccountTuple?(a), + () => { + foreach (var a in accounts) { + return a; + } + return new AccountTuple?(); + }, + EqualityComparer.Default, + new[] { new PropertyValidation(a => (a.HasValue && !string.IsNullOrWhiteSpace(a.Value.name) && accounts.Contains(a.Value), "Please select an account")) }.ToReadOnlyList()); _order_type = backing( nameof(selected_order_type), security_source.Select(s => model.factory.default_order_type_templates(s).Select(kv => create_order_type_vm(kv.Value, kv.Key.key.name, kv.Key.display_name))), order_properties.Select(get_order_type_vm_prop), get_default_order_type_vm, - (a, b) => a.key() == b.key()); + EqualityComparer.null_check_wrapper(OrderTypeVMExtensions.key_comparer)); _order_type.Changed += on_selected_order_type_changed; _time_in_force = backing( nameof(selected_time_in_force), @@ -172,13 +179,13 @@ namespace mbts.spark { public SecurityImmutable security => _security.Value; public ReadOnlyObservableCollection accounts => _accounts.items; - public AccountTuple selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; } + public AccountTuple? selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; } public ReadOnlyObservableCollection time_in_forces => _time_in_force.items; public TimeInForce selected_time_in_force { get => _time_in_force.selected_item; set => _time_in_force.selected_item = value; } public ReadOnlyObservableCollection order_types => _order_type.items; public IOrderTypeVM selected_order_type { get => _order_type.selected_item; set => _order_type.selected_item = value; } - public IObservable observe_account(bool start_immediately = false) => + public IObservable observe_account(bool start_immediately = false) => _accounts.observe_current(start_immediately); public IObservable observe_time_in_force(bool start_immediately = false) => diff --git a/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs b/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs index 3fbcd1e97..355592e80 100644 --- a/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs +++ b/src/client/spark/Orders/OrderTypeVMs/IOrderTypeVM.cs @@ -1,5 +1,6 @@ using bts.api.orderman; using bts.spark; +using bts.utils; using System.Collections.Generic; using System.Windows.Input; using static bts.api.orderman.CustomOrderDefinition; @@ -24,6 +25,9 @@ namespace mbts.spark { } static class OrderTypeVMExtensions { + public static IEqualityComparer key_comparer => + EqualityComparer.Create(a => a.key()); + public static OrderTemplateKey key(this IOrderTypeVM vm) => new OrderTemplateKey(vm.order_type, vm.id); } diff --git a/src/client/spark/core/Utility/ViewModelBase.cs b/src/client/spark/core/Utility/ViewModelBase.cs index 50d8efe5b..b6bb7405a 100644 --- a/src/client/spark/core/Utility/ViewModelBase.cs +++ b/src/client/spark/core/Utility/ViewModelBase.cs @@ -529,7 +529,7 @@ namespace mbts.ui.shell /// If the user has not selected an item, but the current items do not contain the desired item, it will re-evaluate whether or not to select the item when new items arrive. /// protected SelectorProperty backing(string name, IObservable> items, IObservable> desired_item, Func select_default, IReadOnlyList> validation_steps) => - backing(name, items, desired_item, select_default, null, validation_steps); + backing(name, items, desired_item, select_default, EqualityComparer.Default, validation_steps); /// /// Returns a SelectorProperty<T> that observes an items source, and a desired item. When either observable ticks, it determines whether or not to assign the selected_item property. @@ -541,8 +541,8 @@ namespace mbts.ui.shell /// The observable sequence of a desired item. /// The fallback function to select an item by default. /// The item-matching function used to preserve a selected item when new items arrive. - protected SelectorProperty backing(string name, IObservable> items, IObservable> desired_item, Func select_default, Func items_match) => - backing(name, items, desired_item, select_default, items_match, new List>(0)); + protected SelectorProperty backing(string name, IObservable> items, IObservable> desired_item, Func select_default, IEqualityComparer comparer) => + backing(name, items, desired_item, select_default, comparer, new List>(0)); /// /// Returns a SelectorProperty<T> that observes an items source, and a desired item. When either observable ticks, it determines whether or not to assign the selected_item property. @@ -555,14 +555,24 @@ namespace mbts.ui.shell /// The fallback function to select an item by default. /// The item-matching function used to preserve a selected item when new items arrive. /// The validation steps to take when selected_item changes. - protected SelectorProperty backing(string name, IObservable> items, IObservable> desired_item, Func select_default, Func items_match, IReadOnlyList> validation_steps) { - if (_observable_properties == null) _observable_properties = new List(); - var prop = new SelectorProperty(this, name, select_default, items_match, validation_steps); - _observable_properties.Add(items.OnDispatcher().Subscribe(prop)); - _observable_properties.Add(desired_item.OnDispatcher().Subscribe(prop)); + protected SelectorProperty backing(string name, IObservable> items, IObservable> desired_item, Func select_default, IEqualityComparer comparer, IReadOnlyList> validation_steps) { + var prop = new SelectorProperty(this, name, select_default, comparer, validation_steps); + attach(prop, items, desired_item); return prop; } + protected SelectorProperty backing(string name, IObservable> items, IObservable> desired_item, Func item_to_value, Func select_default, IEqualityComparer comparer, IReadOnlyList> validation_steps) { + var prop = new SelectorProperty(this, name, item_to_value, select_default, comparer, validation_steps); + attach(prop, items, desired_item); + return prop; + } + + protected void attach(SelectorProperty selector_property, IObservable> items, IObservable> desired_item) { + if (_observable_properties == null) _observable_properties = new List(); + _observable_properties.Add(items.OnDispatcher().Subscribe(selector_property)); + _observable_properties.Add(desired_item.OnDispatcher().Subscribe(selector_property)); + } + protected void set_dispose(IDisposable disposable) { if (_dispose != null) throw new InvalidOperationException("Cannot set disposable twice"); _dispose = disposable; @@ -579,37 +589,44 @@ namespace mbts.ui.shell OnDisposed?.Invoke(true); } + protected class SelectorProperty : SelectorProperty { + public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func select_default, IEqualityComparer comparer, IReadOnlyList> validation_steps) + : base(owner, prop_name, i => i, select_default, comparer, validation_steps) { } + } + /// /// Designed to observe an items source, and a [settings-driven] desired item. When a new desired item is observed, it will evaluate it's current state and determine if it should be assigned to the selected_item. /// Notes: If the user has selected an item (ie the selected_item has been assigned through the public setter), the desired item will be ignored unless force_override is true. /// If the user has not selected an item, but the current items do not contain the desired item, it will re-evaluate whether or not to select the item when new items arrive. /// - protected class SelectorProperty : IObserver>, IObserver>, IObservable<(T oldval, T newval)> { + protected class SelectorProperty : IObserver>, IObserver>, IObservable<(TValue oldval, TValue newval)> { readonly DisposableViewModelBase _owner; readonly string _prop_name; - readonly IReadOnlyList> _validation_steps; - readonly ObservableCollection _items = new ObservableCollection(); - readonly Func _select_default; - readonly Func _items_match; // used to maintain selected_item through an items-change - T _selected_item; - InlineUserProperty _desired_item; + readonly IReadOnlyList> _validation_steps; + readonly ObservableCollection _items = new ObservableCollection(); + readonly Func _select_default; + readonly IEqualityComparer _value_comparer; + readonly Func _item_to_value; + TValue _selected_item; + InlineUserProperty _desired_item = new InlineUserProperty(); bool _is_user_selected_item; - public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func select_default, Func items_match, IReadOnlyList> validation_steps) { + public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func item_to_value, Func select_default, IEqualityComparer value_comparer, IReadOnlyList> validation_steps) { _owner = owner; _prop_name = prop_name; + _item_to_value = item_to_value; _select_default = select_default; - _items_match = items_match; + _value_comparer = value_comparer ?? EqualityComparer.Default; _validation_steps = validation_steps; - items = new ReadOnlyObservableCollection(_items); + items = new ReadOnlyObservableCollection(_items); } - public event PropertyChange Changing; - public event PropertyChange Changed; + public event PropertyChange Changing; + public event PropertyChange Changed; - public ReadOnlyObservableCollection items { get; } + public ReadOnlyObservableCollection items { get; } - public T selected_item { + public TValue selected_item { get => _selected_item; set { _is_user_selected_item = true; @@ -617,31 +634,31 @@ namespace mbts.ui.shell } } - public IDisposable Subscribe(IObserver<(T oldval, T newval)> observer) { + public IDisposable Subscribe(IObserver<(TValue oldval, TValue newval)> observer) { Changed += pch; return Disposable.Create(() => Changed -= pch); - void pch(T o, T n) => observer.OnNext((o, n)); + void pch(TValue o, TValue n) => observer.OnNext((o, n)); } - public IObservable observe_current(bool start_now = false) { + public IObservable observe_current(bool start_now = false) { var source = this.Select(vals => vals.newval); if (start_now) - source = source.StartWith(_selected_item); + source = source.StartWith(selected_item); return source; } - void IObserver>.OnNext(InlineUserProperty iup) { + void IObserver>.OnNext(InlineUserProperty iup) { _desired_item = iup; _try_auto_select(); } - void IObserver>.OnError(Exception error) { } - void IObserver>.OnCompleted() { } + void IObserver>.OnError(Exception error) { } + void IObserver>.OnCompleted() { } - void IObserver>.OnNext(IEnumerable value) { - if (_items_match != null && _is_user_selected_item) { + void IObserver>.OnNext(IEnumerable value) { + if (_is_user_selected_item) { var curr_selected_item = selected_item; _items.sync_with(value); - if (_try_match_item(curr_selected_item, out T match)) + if (_try_match_item(curr_selected_item, out TValue match)) selected_item = match; else { _is_user_selected_item = false; @@ -649,18 +666,13 @@ namespace mbts.ui.shell } } else { _items.sync_with(value); - if (_is_user_selected_item) { - _is_user_selected_item = _items.Contains(_selected_item); - } - if (!_is_user_selected_item) { - _try_auto_select(); - } + _try_auto_select(); } } - void IObserver>.OnError(Exception error) { } - void IObserver>.OnCompleted() { } + void IObserver>.OnError(Exception error) { } + void IObserver>.OnCompleted() { } - void _run_validation(T val) { + void _run_validation(TValue val) { var invalid_steps = _validation_steps.Select(v => v.validate(val)).Where(r => !r.valid); if (invalid_steps.Count() > 0) { var error_msg = string.Join("\n", invalid_steps.Where(r => !string.IsNullOrEmpty(r.msg)).Select(r => r.msg)); @@ -670,15 +682,15 @@ namespace mbts.ui.shell void _try_auto_select() { if (_is_user_selected_item) { - if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out T match)) { + if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out TValue match)) { _set_selected_item(match); return; } - } else if (desired_item_valid() && _try_match_item(_desired_item.value, out T match)) { + } else if (desired_item_valid() && _try_match_item(_desired_item.value, out TValue match)) { _set_selected_item(match); return; } - if (_try_match_item(_select_default(), out T default_match)) + if (_try_match_item(_select_default(), out TValue default_match)) _set_selected_item(default_match); else { // the _set_selected_item has never been entered, so validation has never ran, force it here @@ -688,8 +700,8 @@ namespace mbts.ui.shell bool desired_item_valid() => (_desired_item?.has_value).GetValueOrDefault(); } - void _set_selected_item(T value) { - if (!Equals(value, _selected_item)) { + void _set_selected_item(TValue value) { + if (!_value_comparer.Equals(value, _selected_item)) { Changing?.Invoke(_selected_item, value); var old = _selected_item; _selected_item = value; @@ -699,10 +711,20 @@ namespace mbts.ui.shell } } - bool _try_match_item(T item, out T match) => - _items_match != null ? - _items.TryGetFirst(i => _items_match(i, item), out match) : - _items.TryGetFirst(i => i.Equals(item), out match); + bool _try_match_item(TValue value, out TValue match) { + foreach (TItem item in _items) { + TValue v = _item_to_value(item); + if (_value_comparer.Equals(v, value)) { + match = v; + return true; + } + } + match = default; + return false; + } + + bool _try_match_item(TItem item, out TValue match) => + _try_match_item(_item_to_value(item), out match); } public event Action OnDisposed; diff --git a/src/client/spark/view/Spark/Ladder/LadderVM.cs b/src/client/spark/view/Spark/Ladder/LadderVM.cs index 3bc639b4a..f4a6969c9 100644 --- a/src/client/spark/view/Spark/Ladder/LadderVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderVM.cs @@ -78,7 +78,7 @@ namespace mbts.spark { _dispose = Disposable.Create( settings_command, order_properties_vm, - order_properties_vm.observe_account(true).Select(a => a.name).Subscribe(account_positions), + order_properties_vm.observe_account(true).Select(a => a?.name ?? string.Empty).Subscribe(account_positions), account_positions, size_calculator = new CalculatorLogic(0, _settings_manager.subscribe_current().Select(s => s.calculator_buttons)), bus.subscribe(SparkTags.LogActivity).OnDispatcher().Subscribe(status_update), diff --git a/src/client/spark_core/utils/EqualityComparer.cs b/src/client/spark_core/utils/EqualityComparer.cs index 21710ba6e..2fc8bd237 100644 --- a/src/client/spark_core/utils/EqualityComparer.cs +++ b/src/client/spark_core/utils/EqualityComparer.cs @@ -27,6 +27,9 @@ namespace bts.utils { public static IEqualityComparer CreateNullable(Func equals) where T : struct => new AnonymousEqualityComparer(nullable_equals); + public static IEqualityComparer null_check_wrapper(IEqualityComparer comparer) where T : class => + new AnonymousEqualityComparer((a, b) => NullCheckEquals(a, b, comparer.Equals), comparer.GetHashCode); + public static bool safe_sequence_equals(IEnumerable source, IEnumerable other) => NullCheckEquals(source, other, (a, b) => a.SequenceEqual(b)); From 7944338516c36e86fd8ba6b37cad2179e67fecfb Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 11 Sep 2018 16:15:39 +0000 Subject: [PATCH 040/203] add test watching script (linux) --- tools/watch_tests.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 tools/watch_tests.sh diff --git a/tools/watch_tests.sh b/tools/watch_tests.sh new file mode 100755 index 000000000..29a41ffd6 --- /dev/null +++ b/tools/watch_tests.sh @@ -0,0 +1,13 @@ +#!/bin/bash +DIR=$(dirname ${BASH_SOURCE[0]}) +if [ "$1" = "-b" ] + then + cd $DIR/../build/ninja_debug/ + ninja + cd server + ./sparkd test $2 +else + cd $DIR/../src + find $DIR/../src/ -name \*.h -o -name \*.cpp | entr /home/bts/spark/tools/watch_tests.sh -b $1 +fi + From 00d91353a4d804891ef79c71c9243a322de42a4c Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 11 Sep 2018 16:03:19 -0400 Subject: [PATCH 041/203] SPARK-1710 add read_direct/read_implied/read_merged accessors for market data --- .gitignore | 2 + src/lib/CMakeDeps.cmake | 2 +- src/lib/md/Diag/MDPriceLantencyTester.cpp | 2 +- src/lib/md/MDConsumerHandle.cpp | 3 +- src/lib/md/MDConsumerHandle.h | 154 ++++++++++++++---- src/lib/md/MemorySegmentUtils.cpp | 5 +- src/lib/md/Shm/MDStructure.h | 12 +- src/lib/md/relay/QueryServer.cpp | 2 +- src/lib/md/test/md2_tests.cpp | 111 +++++++++++-- .../md/test/synthetic_book_merge_tests.cpp | 2 +- src/lib/orderman/actors/MDSource.h | 2 +- tools/tool.fsx | 45 +++++ tools/watch_tests.sh | 1 + 13 files changed, 275 insertions(+), 68 deletions(-) create mode 100644 tools/tool.fsx diff --git a/.gitignore b/.gitignore index 787f0980b..3104595a9 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ project.lock.json src/server/sparkd/orderman/perf_analysis/data src/server/sparkd/orderman/perf_analysis/logs/ src/server/sparkd/orderman/perf_analysis/trace_data/ +.fake +*.fsx.lock diff --git a/src/lib/CMakeDeps.cmake b/src/lib/CMakeDeps.cmake index 8921353f3..e5efb96cd 100644 --- a/src/lib/CMakeDeps.cmake +++ b/src/lib/CMakeDeps.cmake @@ -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/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/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Actor.cpp;mercury/broker/AsyncEventBroker.cpp;mercury/broker/CoreEventBroker.cpp;mercury/broker/IEventBroker.cpp;mercury/events/TimerEvent.cpp;mercury/mercury_utils/Executor.cpp;mercury/mercury_utils/IPollableActor.cpp;mercury/mercury_utils/mercury_common.cpp;mercury/synopsis/SynopsisBuilder.cpp;mercury/topics/ITopic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.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/OrderFactory.cpp;orderman/order_router/OrderRouter.cpp;orderman/orderman_utils/fix_order_codec.cpp;orderman/orders/DynamicOrder.cpp;orderman/orders/IcebergOrder.cpp;orderman/orders/NativeOrder.cpp;orderman/orders/Order.cpp;orderman/orders/TimeBoxOrder.cpp;orderman/orders/TriggerOrder.cpp;orderman/orders/book_algos/BookAlgo.cpp;orderman/orders/book_algos/CoverOrder.cpp;orderman/orders/book_algos/JoinOrder.cpp;orderman/orders/book_algos/LeanOrder.cpp;orderman/orders/book_algos/TickOrder.cpp;orderman/orders/book_algos/TopOrder.cpp;orderman/orders/spreader_orders/SpreaderOrder.cpp;orderman/orders/spreader_orders/spreader/LegPropertyData.cpp;orderman/orders/spreader_orders/spreader/QuoteLegPriceQuantityState.cpp;orderman/orders/spreader_orders/spreader/SingleFillHedger.cpp;orderman/orders/spreader_orders/spreader/SingleLegQuoter.cpp;orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp;orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.cpp;orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp;orderman/orders/spreader_orders/spreader/SpreaderUtils.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderServices.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderPreActivationOrderServices.cpp;orderman/orders/spreader_orders/spreader/hedger/UnhedgedDelta.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckCancelledStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSet.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSetHistory.cpp;orderman/orders/spreader_orders/spreader/quoter/QuoteLegDriver.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckCancelledWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForIOCWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForSizeStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderEndStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWaitingForCancelAllStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWorkingStateWorker.cpp;orderman/orders/spreader_orders/tests/spreader_tests.cpp;orderman/server/OrderServiceServer.cpp;orderman/tests/fat_wrapper_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;persistence/IStore.cpp;persistence/PersistenceServiceServer.cpp;persistence/SqliteStore.cpp;persistence/tests/PersistenceServiceTests.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/Dispatcher.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) \ No newline at end of file +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/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/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/dynamic_orders.proto;config/protos/environment.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Dispatcher.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) \ No newline at end of file diff --git a/src/lib/md/Diag/MDPriceLantencyTester.cpp b/src/lib/md/Diag/MDPriceLantencyTester.cpp index 0067cfebe..c15319110 100644 --- a/src/lib/md/Diag/MDPriceLantencyTester.cpp +++ b/src/lib/md/Diag/MDPriceLantencyTester.cpp @@ -39,7 +39,7 @@ void run_md_price_latency_test(UserSymbol symbol) { while (true) { for(auto& entry : handles) { PriceSize bid, ask; - if (entry.handle.get_bbo(entry.time, bid, ask) == HandleReadResult::SUCCESS) { + if (entry.handle.get_direct_book(entry.time, 1, &bid, &ask) == HandleReadResult::SUCCESS) { auto bid_price = bid.price(); auto ask_price = ask.price(); if (bid_price != entry.bid || ask_price != entry.ask) { diff --git a/src/lib/md/MDConsumerHandle.cpp b/src/lib/md/MDConsumerHandle.cpp index 0b82746bf..87397243a 100644 --- a/src/lib/md/MDConsumerHandle.cpp +++ b/src/lib/md/MDConsumerHandle.cpp @@ -173,7 +173,8 @@ TimeStamp copy_depth(const SyntheticStrategy& synth, size_t count, PriceSize* bi for (size_t x = 0; x < synth.legs.size(); ++x) { std::array bids, asks; TimeStamp t; - HandleReadResult r = synth.legs[x].get_depth(t, count, &bids[0], &asks[0]); + // todo: read type of md from synthetic + HandleReadResult r = synth.legs[x].get_direct_book(t, count, &bids[0], &asks[0]); if (r == HandleReadResult::BUSY || t == TimeStamp::invalid()) return TimeStamp::invalid(); rv = std::max(rv, t); const auto& leg = synth.security.synthetic().legs(x); diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 1f8904f22..824ec1f11 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -28,6 +28,110 @@ enum class HandleReadResult { SUCCESS }; +inline void read_direct( + const HotDepthEntry& hot, + size_t count, + PriceSize* bid, PriceSize* ask) { + bts_assert_d(count <= COCONUT_MAX_DEPTH); + count = std::min(count, (size_t)COCONUT_MAX_DEPTH); + for (size_t x = 0; x < count; ++x) { + hot.read_direct(x, &bid[x], &ask[x]); + } +} +inline void read_implied( + const HotDepthEntry& hot, + size_t count, + PriceSize* bid, PriceSize* ask) { + bts_assert_d(count <= COCONUT_MAX_IMPLIED_DEPTH); + count = std::min(count, (size_t)COCONUT_MAX_IMPLIED_DEPTH); + for (size_t x = 0; x < count; ++x) { + hot.read_implied(x, &bid[x], &ask[x]); + } +} +inline void read_combined( + const HotDepthEntry& hot, + size_t count, + PriceSize* bid, PriceSize* ask) { + bts_assert_d(count < COCONUT_MAX_DEPTH); + size_t xda=0, xdb=0, xia=0, xib=0; + for (size_t x = 0; x < count; ++x) { + if (x < COCONUT_MAX_IMPLIED_DEPTH) { + bts_assert_d(hot.direct[xdb].bid_exponent() == hot.implied[xib].bid_exponent()); + bts_assert_d(hot.direct[xda].ask_exponent() == hot.implied[xia].ask_exponent()); + + auto direct_bid_mantissa = hot.direct[xdb].bid_mantissa(); + auto implied_bid_mantissa = hot.implied[xib].bid_mantissa(); + auto direct_ask_mantissa = hot.direct[xda].ask_mantissa(); + auto implied_ask_mantissa = hot.implied[xia].ask_mantissa(); + + // Compare bids + if (direct_bid_mantissa == implied_bid_mantissa) { + bid[x] = PriceSize( + hot.direct[xdb].bid_mantissa(), + hot.direct[xdb].bid_exponent(), + hot.direct[xdb].bid_size() + hot.implied[xib].bid_size()); + xdb += 1; + xib += 1; + } + // Bids grow down, take the higher price + else if (direct_bid_mantissa > implied_bid_mantissa) { + bid[x] = hot.direct[xdb].bid(); + xdb += 1; + } + else if (direct_bid_mantissa < implied_bid_mantissa) { + bid[x] = hot.implied[xib].bid(); + xib += 1; + } + + // Compare asks + if (direct_ask_mantissa == implied_ask_mantissa) { + ask[x] = PriceSize( + hot.direct[xda].ask_mantissa(), + hot.direct[xda].ask_exponent(), + hot.direct[xda].ask_size() + hot.implied[xia].ask_size()); + xda += 1; + xia += 1; + } + // asks grow up, take the lower price + else if (direct_ask_mantissa < implied_ask_mantissa) { + ask[x] = hot.implied[xia].ask(); + xia += 1; + } + else if (direct_ask_mantissa > implied_ask_mantissa) { + ask[x] = hot.implied[xia].ask(); + xia += 1; + } + } + else { + bid[x] = hot.direct[xdb].bid(); + ask[x] = hot.direct[xda].ask(); + xdb += 1; + xda += 1; + } + } +} + +template +TimeStamp optimistic_copy( + Read read, + const HotDepthEntry& hot, + size_t count, + PriceSize* bid, PriceSize* ask) +{ + TimeStamp t; + int attempts = 0; + const int max_attempts = 1619200; + do { + t = hot.time(); + read(hot, count, bid, ask); + ++attempts; + } while((t != hot.time() || t == TimeStamp::invalid()) && attempts < max_attempts); + if (attempts >= max_attempts) { + return TimeStamp::invalid(); + } + return t; +} + // Can be an invalid handle if you request a security which has not been discovered yet. class BTS_EXPORT MDConsumerHandle @@ -77,52 +181,23 @@ public: TimeStamp last_market_update() const; - HandleReadResult get_bbo(TimeStamp& time, PriceSize& bid, PriceSize& ask) const - { - if (!is_initialized()) return HandleReadResult::NO_CHANGE; - - switch(_type) { - case HANDLETYPE_DEPTH: - if (book.hot->time() > time || book.hot->time() == TimeStamp::invalid()) { - TimeStamp temp = copy_depth(*book.hot, 1, &bid, &ask); - if (temp == TimeStamp::invalid()) { return HandleReadResult::BUSY; } - time = temp; - return HandleReadResult::SUCCESS; - } - return HandleReadResult::NO_CHANGE; - case HANDLETYPE_BBO: - if (bbo.hot->time() > time || bbo.hot->time() == TimeStamp::invalid()) { - TimeStamp temp = copy_bbo(*bbo.hot, bid, ask); - if (temp == TimeStamp::invalid()) { return HandleReadResult::BUSY; } - time = temp; - return HandleReadResult::SUCCESS; - } - return HandleReadResult::NO_CHANGE; - case HANDLETYPE_SYNTHETIC: - return get_depth(time, 1, &bid, &ask); - case HANDLETYPE_VOID: - return HandleReadResult::NO_CHANGE; - } - bts_assert_d(false); - return HandleReadResult::NO_CHANGE; - } - - HandleReadResult get_depth(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { + template + HandleReadResult get_book(Read read, TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { bts_assert_d(copydepth <= COCONUT_MAX_DEPTH); if (!is_initialized() || copydepth == 0) return HandleReadResult::NO_CHANGE; - switch(_type) { case HANDLETYPE_VOID: return HandleReadResult::NO_CHANGE; case HANDLETYPE_DEPTH: if (book.hot->time() > time || book.hot->time() == TimeStamp::invalid()) { - TimeStamp temp = copy_depth(*book.hot, std::min(copydepth, COCONUT_MAX_DEPTH), bids, asks); + TimeStamp temp = optimistic_copy(read, *book.hot, copydepth, bids, asks); if (temp == TimeStamp::invalid()) { return HandleReadResult::BUSY; } time = temp; return HandleReadResult::SUCCESS; } return HandleReadResult::NO_CHANGE; case HANDLETYPE_BBO: - return get_bbo(time, *bids, *asks); + bts_assert_msg(false,"Not impelmented"); + return HandleReadResult::NO_CHANGE; case HANDLETYPE_SYNTHETIC: auto t = last_market_update(); if (t > time || t == TimeStamp::invalid()) { @@ -135,6 +210,17 @@ public: } bts_assert(false); } + + HandleReadResult get_direct_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { + return get_book(read_direct, time, copydepth, bids, asks); + } + HandleReadResult get_implied_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { + return get_book(read_implied, time, copydepth, bids, asks); + } + HandleReadResult get_combined_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { + return get_book(read_combined, time, copydepth, bids, asks); + } + bool get_last_trade(TimeStamp& time, PriceSize& out_trade, datetime::LLDateTime& out_trade_timestamp, size_t* total_volume=nullptr) const; diff --git a/src/lib/md/MemorySegmentUtils.cpp b/src/lib/md/MemorySegmentUtils.cpp index 1f216ce33..60b92b71e 100644 --- a/src/lib/md/MemorySegmentUtils.cpp +++ b/src/lib/md/MemorySegmentUtils.cpp @@ -8,8 +8,9 @@ using namespace std; namespace bts { namespace md { -bool enable_depth(const string&, SecurityType type) { - return type == api::SECURITYTYPE_FUTURE || type == api::SECURITYTYPE_SPREAD || type == api::SECURITYTYPE_SYNTHETIC; +bool enable_depth(const string&, SecurityType) { + return true; + // return type == api::SECURITYTYPE_FUTURE || type == api::SECURITYTYPE_SPREAD || type == api::SECURITYTYPE_SYNTHETIC; } string segment_name(const string& symbol, SecurityType type, Venue exchange, Platform platform) { diff --git a/src/lib/md/Shm/MDStructure.h b/src/lib/md/Shm/MDStructure.h index e443184a4..7ddc3d8b0 100644 --- a/src/lib/md/Shm/MDStructure.h +++ b/src/lib/md/Shm/MDStructure.h @@ -78,16 +78,12 @@ class HotDepthEntry { return nullptr; } void read_direct(uint32_t depth, PriceSize* bid, PriceSize* ask) const { - if (depth < direct.size()) { - *bid = direct[depth].bid(); - *ask = direct[depth].ask(); - } + *bid = direct[depth].bid(); + *ask = direct[depth].ask(); } void read_implied(uint32_t depth, PriceSize* bid, PriceSize* ask) const { - if (depth < implied.size()) { - *bid = implied[depth].bid(); - *ask = implied[depth].ask(); - } + *bid = implied[depth].bid(); + *ask = implied[depth].ask(); } bool write_direct(uint32_t depth, const PriceSize& bid, const PriceSize& ask) { if (depth < direct.size()) { diff --git a/src/lib/md/relay/QueryServer.cpp b/src/lib/md/relay/QueryServer.cpp index d6252b220..cf169baa9 100644 --- a/src/lib/md/relay/QueryServer.cpp +++ b/src/lib/md/relay/QueryServer.cpp @@ -245,7 +245,7 @@ public: std::array bids, asks; TimeStamp time(snapshot ? 0 : chan.time()); size_t copy_depth = HASFLAG(flags, MDRequest::DEPTH) ? COCONUT_MAX_DEPTH : 1; - auto result = handle.get_depth(time, copy_depth, bids.data(), asks.data()); + auto result = handle.get_direct_book(time, copy_depth, bids.data(), asks.data()); if (result == HandleReadResult::SUCCESS) { auto* p = pupd->mutable_book(); for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index f98387ad6..d7ac06002 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -349,7 +349,7 @@ public: PriceSize bid, ask; TimeStamp read_time; // get_boo returns true becuase this time is after the change - REQUIRE(c.get_bbo(read_time, bid, ask) == HandleReadResult::SUCCESS); + REQUIRE(c.get_direct_book(read_time, 1, &bid, &ask) == HandleReadResult::SUCCESS); REQUIRE((read_time - TimeStamp()).seconds() > 0); REQUIRE(bid.price() == 5.0); REQUIRE(ask.price() == 5.0); @@ -361,7 +361,7 @@ public: auto read_time = TimeStamp::now(); for (const auto& c : consumers) { PriceSize bid, ask; - REQUIRE(c.get_bbo(read_time, bid, ask) != HandleReadResult::SUCCESS); + REQUIRE(c.get_direct_book(read_time, 1, &bid, &ask) != HandleReadResult::SUCCESS); } // update everything once more @@ -374,8 +374,8 @@ public: for (const auto& c : consumers) { TimeStamp modified_time = read_time; PriceSize bid, ask; - // get_bbo returns true becuase this time is after the change - REQUIRE(c.get_bbo(modified_time, bid, ask) == HandleReadResult::SUCCESS); + // get_direct_book returns true becuase this time is after the change + REQUIRE(c.get_direct_book(modified_time, 1, &bid, &ask) == HandleReadResult::SUCCESS); REQUIRE( (modified_time - read_time).seconds() > 0); REQUIRE(bid.price() == 6.55); REQUIRE(ask.price() == 6.55); @@ -454,11 +454,11 @@ public: PriceSize bid, ask; for(auto& c : consumers) { TimeStamp zerotime; - REQUIRE(c.get_bbo(zerotime, bid, ask) != HandleReadResult::SUCCESS); + REQUIRE(c.get_direct_book(zerotime, 1, &bid, &ask) != HandleReadResult::SUCCESS); } for(auto& mc : managed_consumers) { TimeStamp zerotime; - REQUIRE(mc->get_bbo(zerotime, bid, ask) != HandleReadResult::SUCCESS); + REQUIRE(mc->get_direct_book(zerotime, 1, &bid, &ask) != HandleReadResult::SUCCESS); } assign_ids(producers.begin(), producers.end()); @@ -467,7 +467,7 @@ public: // Existing old handles remain invalid for(auto& c : consumers) { TimeStamp zerotime; - if (c.get_bbo(zerotime, bid, ask) == HandleReadResult::SUCCESS) { + if (c.get_direct_book(zerotime, 1, &bid, &ask) == HandleReadResult::SUCCESS) { bbo_count++; } } @@ -475,7 +475,7 @@ public: // Now, magically, the existing (managed) handles are valid for(auto& mc : managed_consumers) { TimeStamp zerotime; - if (mc->get_bbo(zerotime, bid, ask) == HandleReadResult::SUCCESS) { + if (mc->get_direct_book(zerotime, 1, &bid, &ask) == HandleReadResult::SUCCESS) { bbo_count++; } } @@ -506,11 +506,11 @@ public: PriceSize bid, ask; for(auto& c : consumers) { TimeStamp zerotime; - REQUIRE(c.get_bbo(zerotime, bid, ask) != HandleReadResult::SUCCESS); + REQUIRE(c.get_direct_book(zerotime, 1, &bid, &ask) != HandleReadResult::SUCCESS); } for(auto& mc : managed_consumers) { TimeStamp zerotime; - REQUIRE(mc->get_bbo(zerotime, bid, ask) != HandleReadResult::SUCCESS); + REQUIRE(mc->get_direct_book(zerotime, 1, &bid, &ask) != HandleReadResult::SUCCESS); } // Only NOW create the producers (and the shared memory) @@ -528,7 +528,7 @@ public: // Existing old handles remain invalid for(auto& c : consumers) { TimeStamp zerotime; - if (c.get_bbo(zerotime, bid, ask) == HandleReadResult::SUCCESS) { + if (c.get_direct_book(zerotime, 1, &bid, &ask) == HandleReadResult::SUCCESS) { bbo_count++; } } @@ -536,7 +536,7 @@ public: // Now, magically, the existing (managed) handles are valid for(auto& mc : managed_consumers) { TimeStamp zerotime; - if (mc->get_bbo(zerotime, bid, ask) == HandleReadResult::SUCCESS) { + if (mc->get_direct_book(zerotime, 1, &bid, &ask) == HandleReadResult::SUCCESS) { bbo_count++; } } @@ -693,7 +693,7 @@ public: REQUIRE(handle->is_heart_beating()); TimeStamp tzero; PriceSize bid, ask; - REQUIRE(handle->get_bbo(tzero, bid, ask) == HandleReadResult::SUCCESS); + REQUIRE(handle->get_direct_book(tzero, 1, &bid, &ask) == HandleReadResult::SUCCESS); REQUIRE(bid.price() == 4.0); REQUIRE(bid.size() == 5); REQUIRE(ask.price() == 6.0); @@ -715,7 +715,7 @@ public: src->producer().update_bbo(TimeStamp::invalid(), PriceSize::from_double<3>(100.0, 100), PriceSize::from_double<3>(101.0, 101)); PriceSize bid, ask; TimeStamp start = TimeStamp::now(); - auto result = src->consumer().get_bbo(read, bid, ask); + auto result = src->consumer().get_direct_book(read, 1, &bid, &ask); TimeStamp end = TimeStamp::now(); REQUIRE(result != HandleReadResult::SUCCESS); REQUIRE((end - start).seconds() < 5); @@ -1411,10 +1411,85 @@ TEST_CASE ("Co2RelayTests","[md]") } -TEST_CASE("Co2SyntheticsTests", "[md]") { - // test synthetics show up with other securities - // test synehtetics with legs of synthetics - // test adding a synthetic with synthetic legs later in the day +TEST_CASE("MergeImpliedTests", "[md]") { + config::internal::initialize_for_test( + "PlatformMap {" + " value {" + " glbx_platform: 'PLATFORM_SIM'" + " }" + "}"); + // create a handle with implied and direct + MDProducer producer(TEST_PLATFORM); + vector producers = create_test_securities(producer); + auto write_time = TimeStamp::now(); + std::array direct; + std::array implied; + SECTION("equal_impiled") { + for (size_t x = 0; x < direct.size(); ++x) { + direct[x] = PriceSize::from_double<7>(x * 100, 2); + if (x < implied.size()) { + implied[x] = PriceSize::from_double<7>(x * 100, 1); + } + } + // update both direct and implied + for (auto& p : producers) { + p.update_direct(write_time, direct, direct, 0); + p.update_implied(write_time, implied, implied, 1); + } + // read from both + set symbols; + UserSymbol future_symbol( "FOO", VENUE_BTS, SECURITYTYPE_FUTURE ); + symbols.insert(future_symbol); + UserSymbol option_symbol( "FOOX", VENUE_BTS, SECURITYTYPE_OPTION ); + symbols.insert(option_symbol); + MDConsumer consumer(symbols, nullptr); + vector consumer_securities; + // Poll for security list - none visible until they have keys + size_t count = consumer.walk_security_list([&](const Security& security, MDConsumerHandle) {consumer_securities.push_back(security);}); + // Since no security has an id none are "public" + REQUIRE(consumer_securities.size() == count); + REQUIRE(count == 0); + assign_ids(producers.begin(), producers.end()); + // Now securities can be enumerated by the client + count = consumer.walk_security_list([&](const Security& security, MDConsumerHandle) {consumer_securities.push_back(security);}); + REQUIRE(consumer_securities.size() == count); + REQUIRE(count == 24); + vector consumers; + for(const auto& security : consumer_securities) { + consumers.push_back(consumer.get_handle(security.id())); + } + + // Read all varieties of books + for (const auto& c : consumers) { + std::array bids, asks; + TimeStamp read_time; + // get_boo returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, COCONUT_MAX_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + read_time = TimeStamp(); + REQUIRE(c.get_combined_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { + if (x < COCONUT_MAX_IMPLIED_DEPTH) { + REQUIRE(bids[x].size() == 3); + REQUIRE(asks[x].size() == 3); + } + else { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + } + } + } + } + } }}} diff --git a/src/lib/md/test/synthetic_book_merge_tests.cpp b/src/lib/md/test/synthetic_book_merge_tests.cpp index 215db620f..d06e45d73 100644 --- a/src/lib/md/test/synthetic_book_merge_tests.cpp +++ b/src/lib/md/test/synthetic_book_merge_tests.cpp @@ -44,7 +44,7 @@ void setup_market(MDProducerHandle producer, const vector& bids, const vecto void expect_market(const MDConsumerHandle& handle, const vector bids, const vector& asks) { array raw_bids,raw_asks; TimeStamp t; - handle.get_depth(t, COCONUT_MAX_DEPTH, raw_bids.data(), raw_asks.data()); + handle.get_direct_book(t, COCONUT_MAX_DEPTH, raw_bids.data(), raw_asks.data()); vector src_bids, src_asks; sane::map(raw_bids, src_bids, [](const PriceSize& ps) -> PS {return {ps.price(), ps.size()};}); sane::map(raw_asks, src_asks, [](const PriceSize& ps) -> PS {return {ps.price(), ps.size()}; }); diff --git a/src/lib/orderman/actors/MDSource.h b/src/lib/orderman/actors/MDSource.h index 1c18c0b6d..28b832254 100644 --- a/src/lib/orderman/actors/MDSource.h +++ b/src/lib/orderman/actors/MDSource.h @@ -82,7 +82,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor { auto start = Timer::now(); for (auto &h : _handles) { auto &handle = h.handle; - if (handle.get_depth(h.last, std::tuple_size(), + if (handle.get_direct_book(h.last, std::tuple_size(), &book.bid()[0], &book.ask()[0]) == md::HandleReadResult::SUCCESS) { book._security_key = SecurityKey(handle.key()); diff --git a/tools/tool.fsx b/tools/tool.fsx new file mode 100644 index 000000000..781fc03c2 --- /dev/null +++ b/tools/tool.fsx @@ -0,0 +1,45 @@ +#r "paket: +nuget Fake.IO.FileSystem +nuget Fake.Core.Target" +#load "./.fake/tool.fsx/intellisense.fsx" + +open Fake.Core +open Fake.IO +open Fake.IO.Globbing.Operators +open Fake + +open System.Diagnostics + +let replicate _ = + // unison.exe doesn't work without very specific parameters + printfn "Detected changes..." + let psi = ProcessStartInfo() + psi.FileName <- "python" + psi.Arguments <- __SOURCE_DIRECTORY__ + "/replicator/replicate_to_linux_vm.py" + psi.UseShellExecute <- false + psi.CreateNoWindow <- true + let p = Process.Start(psi) + p.WaitForExit() + printfn "Ok" + +let build _ = + replicate () + Process.directExec (fun config -> + { config with + FileName = "ssh" + Arguments = "bts@sparkvm -o StrictHostKeyChecking=no -t \"cd /home/bts/spark/build/ninja_debug/ && touch ../../CMakeLists.txt && time ninja\" " + }) |> ignore + + +Target.create "AutoBuild" (fun _ -> + use watcher = !!"src/**/*.cpp" ++ "src/**/*.h" ++ "src/**/*.proto" ++ "tools/*.sh" |> ChangeWatcher.run build + System.Console.ReadLine() |> ignore +) + +Target.create "Replicate" (fun _ -> + use watcher = !!"src/**/*.cpp" ++ "src/**/*.h" ++ "src/**/*.proto" |> ChangeWatcher.run replicate + System.Console.ReadLine() |> ignore +) + +Target.runOrDefault "Replicate" + diff --git a/tools/watch_tests.sh b/tools/watch_tests.sh index 29a41ffd6..6a5b9a871 100755 --- a/tools/watch_tests.sh +++ b/tools/watch_tests.sh @@ -5,6 +5,7 @@ if [ "$1" = "-b" ] cd $DIR/../build/ninja_debug/ ninja cd server + echo "Starting tests..." ./sparkd test $2 else cd $DIR/../src From 45283593ce00eb043264890be8815e4121739d1c Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 12 Sep 2018 13:03:43 -0400 Subject: [PATCH 042/203] SPARK-1710 Add good test coverage for read_combined() --- src/lib/md/MDConsumerHandle.h | 75 +++--- src/lib/md/MDProducerHandle.h | 4 +- src/lib/md/PriceSize.h | 44 +++- src/lib/md/test/md2_tests.cpp | 435 ++++++++++++++++++++++++++++------ 4 files changed, 437 insertions(+), 121 deletions(-) diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 824ec1f11..4e2e4b656 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -55,57 +55,60 @@ inline void read_combined( bts_assert_d(count < COCONUT_MAX_DEPTH); size_t xda=0, xdb=0, xia=0, xib=0; for (size_t x = 0; x < count; ++x) { - if (x < COCONUT_MAX_IMPLIED_DEPTH) { - bts_assert_d(hot.direct[xdb].bid_exponent() == hot.implied[xib].bid_exponent()); - bts_assert_d(hot.direct[xda].ask_exponent() == hot.implied[xia].ask_exponent()); - - auto direct_bid_mantissa = hot.direct[xdb].bid_mantissa(); - auto implied_bid_mantissa = hot.implied[xib].bid_mantissa(); - auto direct_ask_mantissa = hot.direct[xda].ask_mantissa(); - auto implied_ask_mantissa = hot.implied[xia].ask_mantissa(); + if (xib < COCONUT_MAX_IMPLIED_DEPTH) { + auto direct = hot.direct[xdb].bid(); + auto implied = hot.implied[xib].bid(); // Compare bids - if (direct_bid_mantissa == implied_bid_mantissa) { + if (direct.price() == implied.price()) { bid[x] = PriceSize( - hot.direct[xdb].bid_mantissa(), - hot.direct[xdb].bid_exponent(), - hot.direct[xdb].bid_size() + hot.implied[xib].bid_size()); + direct.mantissa(), + direct.exponent(), + direct.size() + implied.size()); xdb += 1; xib += 1; } // Bids grow down, take the higher price - else if (direct_bid_mantissa > implied_bid_mantissa) { - bid[x] = hot.direct[xdb].bid(); + else if (implied.price() < direct.price()) { + bid[x] = direct; xdb += 1; } - else if (direct_bid_mantissa < implied_bid_mantissa) { - bid[x] = hot.implied[xib].bid(); + else { + bid[x] = implied; xib += 1; } - - // Compare asks - if (direct_ask_mantissa == implied_ask_mantissa) { - ask[x] = PriceSize( - hot.direct[xda].ask_mantissa(), - hot.direct[xda].ask_exponent(), - hot.direct[xda].ask_size() + hot.implied[xia].ask_size()); - xda += 1; - xia += 1; - } - // asks grow up, take the lower price - else if (direct_ask_mantissa < implied_ask_mantissa) { - ask[x] = hot.implied[xia].ask(); - xia += 1; - } - else if (direct_ask_mantissa > implied_ask_mantissa) { - ask[x] = hot.implied[xia].ask(); - xia += 1; - } } else { bid[x] = hot.direct[xdb].bid(); - ask[x] = hot.direct[xda].ask(); xdb += 1; + } + + if (xia < COCONUT_MAX_IMPLIED_DEPTH) { + // We only need to compare the mantissa because all levels are guarunteed to have the same exponent + auto direct = hot.direct[xda].ask(); + auto implied = hot.implied[xia].ask(); + + // Compare asks + if (direct.price() == implied.price()) { + ask[x] = PriceSize( + direct.mantissa(), + direct.exponent(), + direct.size() + implied.size()); + xda += 1; + xia += 1; + } + // asks grow up, take the lower price + else if (direct.price() < implied.price()) { + ask[x] = direct; + xda += 1; + } + else { + ask[x] = implied; + xia += 1; + } + } + else { + ask[x] = hot.direct[xda].ask(); xda += 1; } } diff --git a/src/lib/md/MDProducerHandle.h b/src/lib/md/MDProducerHandle.h index e98ce4efb..84c966668 100644 --- a/src/lib/md/MDProducerHandle.h +++ b/src/lib/md/MDProducerHandle.h @@ -35,7 +35,7 @@ public: void set_legs(const std::vector& legs); void update_bbo(TimeStamp time, const PriceSize bid_price, const PriceSize ask_price, uint16_t seq=0); - void update_direct(TimeStamp time, std::array& bids, std::array& asks, uint32_t seq=0) { + void update_direct(TimeStamp time, const std::array& bids, const std::array& asks, uint32_t seq=0) { if (!has_depth()) { update_bbo(time, bids[0], asks[0], seq); } @@ -51,7 +51,7 @@ public: hheader().direct_count += 1; } } - void update_implied(TimeStamp time, std::array& bids, std::array& asks, uint32_t seq=0) { + void update_implied(TimeStamp time, const std::array& bids, const std::array& asks, uint32_t seq=0) { if (has_depth()) { auto& book = hdepth(); book.set_time(TimeStamp::invalid()); diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 2b33551e3..828f73b3f 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -49,6 +49,29 @@ inline bool compact_mantissa(Mantissa& mantissa, Exponent& exponent) { return result; } +class IntPrice { + int64_t _mantissa; + int8_t _exponent; + public: + + IntPrice(int64_t mantissa, int8_t exponent) : _mantissa(mantissa), _exponent(exponent) {} + IntPrice(const IntPrice& other) : _mantissa(other._mantissa), _exponent(other._exponent) {} + + bool operator==(const IntPrice& other) const { + return _mantissa == other._mantissa && _exponent == other._exponent; + } + bool operator!=(const IntPrice& other) const { + return !(*this == other); + } + bool operator<(const IntPrice& other) const { + if (_exponent < other._exponent) return true; + return _mantissa < other._mantissa; + } + + int64_t mantissa() const { return _mantissa; } + int8_t exponent() const { return _exponent; } +}; + class PriceSize { int64_t _mantissa; int8_t _exponent; @@ -59,16 +82,19 @@ public: _mantissa(mantissa), _exponent(exponent), _size(size) - { - // bts_assert_d(exponent != 0); - } - PriceSize() : _mantissa(0), _exponent(), _size(0) { } + { } + PriceSize(IntPrice& price, Size size) : + _mantissa(price.mantissa()), + _exponent(price.exponent()), + _size(size) + { } + PriceSize() : _mantissa(0), _exponent(0), _size(0) {} PriceSize(const PriceSize& other) = default; PriceSize& operator=(const PriceSize& other) = default; bool operator==(const PriceSize& other) const { - return (_size == other._size && _mantissa == other._mantissa && _exponent == other._exponent); + return (_size == other._size && int_price() == other.int_price()); } bool operator!=(const PriceSize& other) const { return !(*this == other); @@ -109,13 +135,15 @@ public: int8_t exponent() const { return _exponent; } + Size size() const { return _size; } // This call is relatively slow so don't call it often double price() const { - if (*this == NO_MARKET) return NO_PRICE; + if (is_nan()) return NO_PRICE; return mantissa() * pow10(exponent()); } - Size size() const { - return _size; + IntPrice int_price() const { return IntPrice(_mantissa,_exponent); } + bool is_nan() const { + return (*this == NO_MARKET); } }; static_assert(sizeof(PriceSize) == 16, "PriceSize should be small"); diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index d7ac06002..fc9fc7904 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -1214,34 +1214,6 @@ struct Co2IndexTests { } } ; - - -// TEST_CASE ("Co2IndexTests","[md]") { -// Co2IndexTests co2_index_tests; -// co2_index_tests.setup(); - -// SECTION("test_simple_index_search") { -// co2_index_tests.test_simple_index_search(); -// } - -// SECTION("test_simple_index_parallel_search") { -// co2_index_tests.test_simple_index_parallel_search(); -// } - -// SECTION("test_simple_index_iterate") { -// co2_index_tests.test_simple_index_iterate(); -// } -// SECTION("test_concurrent_index_search") { -// co2_index_tests.test_concurrent_index_search(); -// } -// SECTION("test_concurrent_index_iterate") { -// co2_index_tests.test_concurrent_index_iterate(); -// } -// SECTION("test_concurrent_index_parallel_search") { -// co2_index_tests.test_concurrent_index_parallel_search(); -// } -// } - struct Co2RelayTests { void setup() { config::internal::initialize_for_test( @@ -1410,31 +1382,20 @@ TEST_CASE ("Co2RelayTests","[md]") } } - -TEST_CASE("MergeImpliedTests", "[md]") { - config::internal::initialize_for_test( - "PlatformMap {" - " value {" - " glbx_platform: 'PLATFORM_SIM'" - " }" - "}"); - // create a handle with implied and direct - MDProducer producer(TEST_PLATFORM); - vector producers = create_test_securities(producer); - auto write_time = TimeStamp::now(); - std::array direct; - std::array implied; - SECTION("equal_impiled") { - for (size_t x = 0; x < direct.size(); ++x) { - direct[x] = PriceSize::from_double<7>(x * 100, 2); - if (x < implied.size()) { - implied[x] = PriceSize::from_double<7>(x * 100, 1); - } - } +namespace ImpliedTests { + void test_merge(const std::array& direct_bid, + const std::array& implied_bid, + const std::array& direct_ask, + const std::array& implied_ask, + const std::function&)>& validate) { + MDProducer producer(TEST_PLATFORM); + vector producers = create_test_securities(producer); + auto write_time = TimeStamp::now(); + // update both direct and implied for (auto& p : producers) { - p.update_direct(write_time, direct, direct, 0); - p.update_implied(write_time, implied, implied, 1); + p.update_direct(write_time, direct_bid, direct_ask, 0); + p.update_implied(write_time, implied_bid, implied_ask, 1); } // read from both set symbols; @@ -1459,35 +1420,359 @@ TEST_CASE("MergeImpliedTests", "[md]") { consumers.push_back(consumer.get_handle(security.id())); } - // Read all varieties of books - for (const auto& c : consumers) { - std::array bids, asks; - TimeStamp read_time; - // get_boo returns true becuase this time is after the change - REQUIRE(c.get_direct_book(read_time, COCONUT_MAX_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); - for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { - REQUIRE(bids[x].size() == 2); - REQUIRE(asks[x].size() == 2); + validate(consumers); + } +} + +TEST_CASE("MergeImpliedTests", "[md]") { + config::internal::initialize_for_test( + "PlatformMap {" + " value {" + " glbx_platform: 'PLATFORM_SIM'" + " }" + "}"); + // create a handle with implied and direct + std::array direct_bid, direct_ask; + std::array implied_bid, implied_ask; + + SECTION("equal_impiled_deep") { + for (size_t x = 0; x < direct_bid.size(); ++x) { + direct_bid[x] = PriceSize::from_double<7>(x * 100, 2); + if (x < implied_bid.size()) { + implied_bid[x] = PriceSize::from_double<7>(x * 100, 1); } - read_time = TimeStamp(); - REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); - for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { - REQUIRE(bids[x].size() == 1); - REQUIRE(asks[x].size() == 1); - } - read_time = TimeStamp(); - REQUIRE(c.get_combined_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); - for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { - if (x < COCONUT_MAX_IMPLIED_DEPTH) { - REQUIRE(bids[x].size() == 3); - REQUIRE(asks[x].size() == 3); - } - else { + } + direct_ask = direct_bid; + implied_ask = implied_bid; + ImpliedTests::test_merge(direct_bid,implied_bid, direct_ask, implied_ask, [](const vector& consumers) { + for (const auto& c : consumers) { + std::array bids, asks; + TimeStamp read_time; + // get_book returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, COCONUT_MAX_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { REQUIRE(bids[x].size() == 2); REQUIRE(asks[x].size() == 2); } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + read_time = TimeStamp(); + REQUIRE(c.get_combined_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { + if (x < COCONUT_MAX_IMPLIED_DEPTH) { + REQUIRE(bids[x].size() == 3); + REQUIRE(asks[x].size() == 3); + } + else { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + } + } } - } + }); + } + + SECTION("partial_Read") { + direct_bid[0] = { 700, 1, 1 }; + direct_bid[1] = { 600, 1, 1 }; + direct_bid[2] = { 500, 1, 1 }; + direct_bid[3] = { 400, 1, 1 }; + + direct_ask[0] = { 800, 1, 1 }; + direct_ask[1] = { 900, 1, 1 }; + direct_ask[2] = { 1000, 1, 1 }; + direct_ask[3] = { 1100, 1, 1 }; + + implied_bid[0] = { 700, 1, 2 }; + implied_bid[1] = { 600, 1, 2 }; + + implied_ask[0] = { 800, 1, 2 }; + implied_ask[1] = { 900, 1, 2 }; + + + ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [](const vector& consumers) { + const size_t TEST_DEPTH = 4; + for (const auto& c : consumers) { + std::array bids, asks; + int canary = 0; + TimeStamp read_time; + // get_boo returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < TEST_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + } + read_time = TimeStamp(); + REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < TEST_DEPTH; ++x) { + if (x < COCONUT_MAX_IMPLIED_DEPTH) { + REQUIRE(bids[x].size() == 3); + REQUIRE(asks[x].size() == 3); + } + else { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + } + REQUIRE(canary == 0); + } + }); + } + + SECTION("better_implied") { + direct_bid[0] = { 500, 1, 1 }; + direct_bid[1] = { 400, 1, 1 }; + direct_bid[2] = { 300, 1, 1 }; + direct_bid[3] = { 200, 1, 1 }; + + direct_ask[0] = { 1000, 1, 1 }; + direct_ask[1] = { 1100, 1, 1 }; + direct_ask[2] = { 1200, 1, 1 }; + direct_ask[3] = { 1300, 1, 1 }; + + implied_bid[0] = { 600, 1, 2 }; + implied_bid[1] = { 500, 1, 2 }; + + implied_ask[0] = { 900, 1, 2 }; + implied_ask[1] = { 1000, 1, 2 }; + + + ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { + const size_t TEST_DEPTH = 4; + for (const auto& c : consumers) { + std::array bids, asks; + int canary = 0; + TimeStamp read_time; + // get_boo returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < TEST_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + } + read_time = TimeStamp(); + REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(bids[0].size() == 2); + REQUIRE(bids[0].mantissa() == 600); + REQUIRE(asks[0].size() == 2); + REQUIRE(asks[0].mantissa() == 900); + REQUIRE(bids[1].size() == 3); + REQUIRE(asks[1].size() == 3); + for (size_t x = 2; x < TEST_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + REQUIRE(canary == 0); + } + }); + } + + SECTION("worse_implied") { + direct_bid[0] = { 500, 1, 1 }; + direct_bid[1] = { 400, 1, 1 }; + direct_bid[2] = { 300, 1, 1 }; + direct_bid[3] = { 200, 1, 1 }; + + direct_ask[0] = { 1000, 1, 1 }; + direct_ask[1] = { 1100, 1, 1 }; + direct_ask[2] = { 1200, 1, 1 }; + direct_ask[3] = { 1300, 1, 1 }; + + implied_bid[0] = { 400, 1, 2 }; + implied_bid[1] = { 300, 1, 2 }; + + implied_ask[0] = { 1100, 1, 2 }; + implied_ask[1] = { 1200, 1, 2 }; + + + ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { + const size_t TEST_DEPTH = 4; + for (const auto& c : consumers) { + std::array bids, asks; + int canary = 0; + TimeStamp read_time; + // get_boo returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < TEST_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + } + read_time = TimeStamp(); + REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(bids[0].mantissa() == 500); + REQUIRE(bids[0].size() == 1); + REQUIRE(asks[0].mantissa() == 1000); + REQUIRE(asks[0].size() == 1); + + REQUIRE(bids[1].mantissa() == 400); + REQUIRE(bids[1].size() == 3); + REQUIRE(asks[1].mantissa() == 1100); + REQUIRE(asks[1].size() == 3); + + REQUIRE(bids[2].mantissa() == 300); + REQUIRE(bids[2].size() == 3); + REQUIRE(asks[2].mantissa() == 1200); + REQUIRE(asks[2].size() == 3); + + REQUIRE(bids[3].mantissa() == 200); + REQUIRE(bids[3].size() == 1); + REQUIRE(asks[3].mantissa() == 1300); + REQUIRE(asks[3].size() == 1); + + REQUIRE(canary == 0); + } + }); + } + + SECTION("worse_implied_bid") { + direct_bid[0] = { 500, 1, 1 }; + direct_bid[1] = { 400, 1, 1 }; + direct_bid[2] = { 300, 1, 1 }; + direct_bid[3] = { 200, 1, 1 }; + + direct_ask[0] = { 1000, 1, 1 }; + direct_ask[1] = { 1100, 1, 1 }; + direct_ask[2] = { 1200, 1, 1 }; + direct_ask[3] = { 1300, 1, 1 }; + + implied_bid[0] = { 400, 1, 2 }; + implied_bid[1] = { 300, 1, 2 }; + + implied_ask[0] = { 1000, 1, 2 }; + implied_ask[1] = { 1100, 1, 2 }; + + + ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { + const size_t TEST_DEPTH = 4; + for (const auto& c : consumers) { + std::array bids, asks; + int canary = 0; + TimeStamp read_time; + // get_boo returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < TEST_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + } + read_time = TimeStamp(); + REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(bids[0].mantissa() == 500); + REQUIRE(bids[0].size() == 1); + REQUIRE(asks[0].mantissa() == 1000); + REQUIRE(asks[0].size() == 3); + + REQUIRE(bids[1].mantissa() == 400); + REQUIRE(bids[1].size() == 3); + REQUIRE(asks[1].mantissa() == 1100); + REQUIRE(asks[1].size() == 3); + + REQUIRE(bids[2].mantissa() == 300); + REQUIRE(bids[2].size() == 3); + REQUIRE(asks[2].mantissa() == 1200); + REQUIRE(asks[2].size() == 1); + + REQUIRE(bids[3].mantissa() == 200); + REQUIRE(bids[3].size() == 1); + REQUIRE(asks[3].mantissa() == 1300); + REQUIRE(asks[3].size() == 1); + + REQUIRE(canary == 0); + } + }); + } + SECTION("worse_implied_ask") { + direct_bid[0] = { 500, 1, 1 }; + direct_bid[1] = { 400, 1, 1 }; + direct_bid[2] = { 300, 1, 1 }; + direct_bid[3] = { 200, 1, 1 }; + + direct_ask[0] = { 1000, 1, 1 }; + direct_ask[1] = { 1100, 1, 1 }; + direct_ask[2] = { 1200, 1, 1 }; + direct_ask[3] = { 1300, 1, 1 }; + + implied_bid[0] = { 500, 1, 2 }; + implied_bid[1] = { 400, 1, 2 }; + + implied_ask[0] = { 1300, 1, 2 }; + implied_ask[1] = { 1400, 1, 2 }; + + + ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { + const size_t TEST_DEPTH = 4; + for (const auto& c : consumers) { + std::array bids, asks; + int canary = 0; + TimeStamp read_time; + // get_boo returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < TEST_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + } + read_time = TimeStamp(); + REQUIRE(c.get_combined_book(read_time, TEST_DEPTH+1, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(bids[0].mantissa() == 500); + REQUIRE(bids[0].size() == 3); + REQUIRE(asks[0].mantissa() == 1000); + REQUIRE(asks[0].size() == 1); + + REQUIRE(bids[1].mantissa() == 400); + REQUIRE(bids[1].size() == 3); + REQUIRE(asks[1].mantissa() == 1100); + REQUIRE(asks[1].size() == 1); + + REQUIRE(bids[2].mantissa() == 300); + REQUIRE(bids[2].size() == 1); + REQUIRE(asks[2].mantissa() == 1200); + REQUIRE(asks[2].size() == 1); + + REQUIRE(bids[3].mantissa() == 200); + REQUIRE(bids[3].size() == 1); + REQUIRE(asks[3].mantissa() == 1300); + REQUIRE(asks[3].size() == 3); + + REQUIRE(bids[4].mantissa() == 0); + REQUIRE(bids[4].size() == 0); + REQUIRE(asks[4].mantissa() == 1400); + REQUIRE(asks[4].size() == 2); + + REQUIRE(canary == 0); + } + }); } } From 29afa7f3b1dcfb7c91dea1560489c50fcac085f6 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 13 Sep 2018 15:42:57 -0400 Subject: [PATCH 043/203] create SimpleGridSettingsManager/VM (no sound-triggers) --- .../Market/TimeAndSales/TimeAndSalesModel.cs | 3 + .../Market/TimeAndSales/TimeAndSalesVM.cs | 28 +++-- .../Market/TimeAndSales/TimeAndSalesWindow.cs | 10 +- .../Market/TimeAndSalesWindowFactory.cs | 55 +++++++-- .../TimeAndSalesColumn.cs | 17 ++- .../Controls/DIYGrid/Columns/RenderColumn.cs | 2 +- .../DIYGrid/Settings/GridColumnSettingsVM.cs | 20 ++-- .../DIYGrid/Settings/GridSettingsVM.cs | 1 + .../DIYGrid/Settings/GridSettingsView.xaml | 3 + .../DIYGrid/Settings/GridSettingsView.xaml.cs | 2 + .../Settings/SimpleGridColumnSettingsVM.cs | 91 +++++++++++++++ .../Settings/SimpleGridSettingsManager.cs | 106 ++++++++++++++++++ .../DIYGrid/Settings/SimpleGridSettingsVM.cs | 91 +++++++++++++++ 13 files changed, 391 insertions(+), 38 deletions(-) create mode 100644 src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridColumnSettingsVM.cs create mode 100644 src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs create mode 100644 src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesModel.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesModel.cs index 7f99b82f9..fb5b92f62 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesModel.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesModel.cs @@ -19,11 +19,14 @@ namespace mbts.spark { } class TimeAndSalesModel : IDisposable { + internal readonly SecurityCriteria security_criteria; + static readonly TimeSpan _poll_interval = TimeSpan.FromSeconds(0.4); Lazy>> _trades; IDisposable _dispose; public TimeAndSalesModel(IBus bus, MDConsumer consumer, SecurityCriteria security_criteria) { + this.security_criteria = security_criteria; var curr_channels = new Dictionary(); _trades = new Lazy>>(() => Observable.Interval(_poll_interval).Select(_ => { var copy = curr_channels; diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs index 728a8edde..2e2d5606f 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs @@ -3,7 +3,6 @@ using bts.co2; using mbts.spark.filters; using mbts.ui; using mbts.ui.controls; -using mbts.ui.settings; using mbts.ui.spark; using mbts.ui.utility; using System; @@ -19,13 +18,15 @@ using static mbts.ui.controls.DIYGrid; namespace mbts.spark { class TimeAndSalesVM : SecurityGrid { readonly IBus _bus; + readonly TimeAndSalesModel _model; - public TimeAndSalesVM(IBus bus, TimeAndSalesModel model, IReadOnlyCollection sorting_criteria, FilterStates filters) - : base(RXCollection.CreateBuffer(model.trades, 1000), sorting_criteria) { + public TimeAndSalesVM(IBus bus, IObservable owner_title, TimeAndSalesModel model, StartupData startup_data) + : base(RXCollection.CreateBuffer(model.trades, 1000), startup_data.grid?.sorting_criterion ?? Enumerable.Empty()) { _bus = bus; + _model = model; show_ladder = DelegateCommandParamObserver.Create(current_row_observable, r => _show_ladder_async(r).ignore(), r => r != null); security_filter = new RegexFilterVM(r => r.security.ToString(), nameof(Row.security)); - if (filters != null) { + if (startup_data.filters is FilterStates filters) { time_filter.load_state(filters.time); security_filter.load_state(filters.security); price_filter.load_state(filters.price); @@ -39,12 +40,12 @@ namespace mbts.spark { add_column(TimeAndSalesView.Columns.Time, DefaultColumnHeaders.Time, DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter); add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right); add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right); - add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DIYGrid.DefaultColumnWidths.Aggressor, aggressor_filter, DataGridFilterKnownTemplates.EnumValues); - add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex); - add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex); + add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DefaultColumnWidths.Aggressor, aggressor_filter, DataGridFilterKnownTemplates.EnumValues); + add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex); + add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex); void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) => columns.Add(new TimeAndSalesColumn( - r => r[col_key] + col_key , (r) => _get_aggressor_brush(r) , filter , filter_template @@ -52,16 +53,25 @@ namespace mbts.spark { { Header = header, Width = width, horizontal_alignment = h_alignment } ); + // be sure to load GridSettings AFTER the columns collection has been populated, the grid settings manager looks columns by id to apply the appropriate settings, + // if the collection is not populated, the column settings will not be applied + grid_settings_manager = new SimpleGridSettingsManager(owner_title, rows_view, columns); + grid_settings_manager.load_from(startup_data.grid); _resources.AddRange( show_ladder, DataGridFilterHelper.hook_filters( rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, trade_key_filter, security_key_filter ) ); + grid_settings_manager.load_from(startup_data.grid); } public ObservableCollection columns { get; } = new ObservableCollection(); public DelegateCommandParamObserver show_ladder { get; } + public SimpleGridSettingsManager grid_settings_manager { get; } + + public StartupData create() => + new StartupData(_model.security_criteria, _get_filter_states(), grid_settings_manager.create()); private Brush _get_aggressor_brush(Row r) { BTSColor c; @@ -82,7 +92,7 @@ namespace mbts.spark { public IDataGridColumnPersistentFilter security_key_filter { get; } = new RegexFilterVM(r => r.security_key.ToString(), nameof(Row.security_key)); public IDataGridColumnPersistentFilter trade_key_filter { get; } = new RegexFilterVM(r => r.trade_key.ToString(), nameof(Row.trade_key)); - public FilterStates get_filter_states() => + FilterStates _get_filter_states() => new FilterStates { time = time_filter.get_state(), security = security_filter.get_state(), diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs index 639c127c7..879ac6cab 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs @@ -49,7 +49,7 @@ namespace mbts.spark { + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; - public override StartupData SaveState => _state = new StartupData(_state.security_criteria, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states()); + public override StartupData SaveState => _state = _vm.value.create(); public override void Dispose() => _dispose_resources(); @@ -66,7 +66,9 @@ namespace mbts.spark { SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault), SparkCommands.ShowFills.complete(selected_secs_source, always_true, get_fills_startup_data), SparkCommands.ShowMarketView.complete(selected_secs_source, always_true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria), - SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)) + SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)), + // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes + SparkCommands.window_settings_command(new DialogCommand(() => _vm.value.grid_settings_manager.view_factory())) }; _subscriptions = Disposable.Create( _bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(update_criteria), @@ -79,7 +81,7 @@ namespace mbts.spark { security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria }); void update_criteria(SecurityCriteria c) { - _state = new StartupData(c, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states()); + _state = _vm.value.create().with(c); _initialize(); } } @@ -98,7 +100,7 @@ namespace mbts.spark { Disposable.dispose(ref _model); _model = new TimeAndSalesModel(_bus, _consumer, _state.security_criteria); // the previous _vm.value is automatically disposed when _vm.value is set - no need to dispose it here - _vm.value = new TimeAndSalesVM(_bus, _model, _state.sorting_criteria, _state.filters); + _vm.value = new TimeAndSalesVM(_bus, this.observe_title(), _model, _state); if (_view == null) _view = new TimeAndSalesView(); _view.DataContext = _vm.value; diff --git a/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs b/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs index b3dfad8bf..17cbe946a 100644 --- a/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs +++ b/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs @@ -5,11 +5,14 @@ using mbts.co2; using mbts.spark; using mbts.spark.filters; using mbts.ui; +using mbts.ui.settings; using ProtoBuf; using System; using System.Collections.Generic; using System.Reactive.Linq; using System.Threading.Tasks; +using static mbts.ui.controls.DIYGrid; +using static mbts.ui.spark.TimeAndSalesView; namespace bts.shell { class TimeAndSalesWindowFactory : ShellWindowFactory { @@ -27,6 +30,8 @@ namespace bts.shell { ); } + public static string col_id(Columns col) => col.ToString(); + public override Task load(StartupData state) => Task.FromResult(new TimeAndSalesWindow(this, _bus, _consumer, state, SparkCommands.PickTimeAndSales.icon)); @@ -39,20 +44,56 @@ namespace bts.shell { public class StartupData { [ProtoMember(1)] public readonly SecurityCriteria security_criteria; - [ProtoMember(2)] - public readonly List sorting_criteria; + //[ProtoMember(2)] (THIS HAS BEEN USED in previous versions, please don't remove comment) + //public readonly List sorting_criteria; [ProtoMember(3)] public FilterStates filters; + [ProtoMember(4)] + public GridSettings grid; - StartupData() { }//for ptorobuf + public StartupData() { } - public StartupData(SecurityCriteria security_criteria) - : this (security_criteria, new List(), null) { } + public StartupData(SecurityCriteria security_criteria) : + this(security_criteria, new FilterStates()) { } - public StartupData(SecurityCriteria security_criteria, List sorting_criteria, FilterStates filters) { + public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default) { this.security_criteria = security_criteria; - this.sorting_criteria = sorting_criteria; this.filters = filters; + this.grid = grid; + } + + public static StartupData Default(SecurityCriteria security_criteria) => + new StartupData(security_criteria, new FilterStates(), GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion)); + + public StartupData with(SecurityCriteria security_criteria) => + new StartupData(security_criteria, filters, grid); + + static class DefaultColumns { + public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security); + public static GridColumnSettings time => new GridColumnSettings(col_id(Columns.Time), DefaultColumnHeaders.Time, width: DefaultColumnWidths.Time); + public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price); + public static GridColumnSettings size => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size); + public static GridColumnSettings aggressor => new GridColumnSettings(col_id(Columns.Aggressor), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor); + public static GridColumnSettings trade_key => new GridColumnSettings(col_id(Columns.TradeKey), DefaultColumnHeaders.TradeKey, width: DefaultColumnWidths.TradeKey); + public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey); + + public static IEnumerable sorting_criterion { + get { + yield return new SortingCriteria(time.id, false); + } + } + + public static IEnumerable all { + get { + yield return security; + yield return time; + yield return price; + yield return size; + yield return aggressor; + yield return trade_key; + yield return security_key; + } + } } } diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TimeAndSalesColumn.cs b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TimeAndSalesColumn.cs index 162cd05f4..853681c16 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TimeAndSalesColumn.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TimeAndSalesColumn.cs @@ -1,21 +1,26 @@ -using mbts.spark; +using bts.shell; +using mbts.spark; using mbts.spark.filters; +using mbts.ui.spark; using mbts.ui.utility.text; using System; using System.Windows.Media; namespace mbts.ui.controls { partial class DIYGrid { - internal class TimeAndSalesColumn : RenderTextColumn { - - readonly Func _get_text; + internal class TimeAndSalesColumn : RenderTextColumn, IKeyedColumn { readonly Func _get_brush; - public TimeAndSalesColumn(Func get_text, Func get_brush, IDataGridColumnFilter filter = null, string filter_template = null, GlyphContext glyph_context = null) : base(get_text, filter, filter_template, glyph_context) { - _get_text = get_text; + public TimeAndSalesColumn(TimeAndSalesView.Columns key, Func get_brush, IDataGridColumnFilter filter = null, string filter_template = null, GlyphContext glyph_context = null) + : base(r => r[key], filter, filter_template, glyph_context) { + this.key = key; + // id is used for settings (another case of ProtoBufs vs Generics - strings seemed like the easiest way to provide an identifier for the common GridColumnSettings proto) + id = TimeAndSalesWindowFactory.col_id(key); _get_brush = get_brush; } + public TimeAndSalesView.Columns key { get; } + public override void draw(TimeAndSalesVM.Row row, DrawingContext dc, ColumnGeometry position, double row_height) { CellRender.draw_text(dc , glyph_context diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/RenderColumn.cs b/src/client/spark/view/Controls/DIYGrid/Columns/RenderColumn.cs index c3a57b55b..c825a17ea 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/RenderColumn.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/RenderColumn.cs @@ -20,7 +20,7 @@ namespace mbts.ui.controls { } public class RenderTextColumn : RenderColumnBase, IRenderColumn, IFilterableColumn { - readonly Func _get_text; + protected readonly Func _get_text; public RenderTextColumn( Func get_text, diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs index 3d9db0ad0..57917bf1c 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs @@ -4,7 +4,6 @@ using mbts.ui; using mbts.ui.controls; using mbts.ui.settings; using mbts.ui.shell; -using mbts.ui.utilities; using mbts.ui.utility; using System; using System.Collections.Generic; @@ -29,17 +28,10 @@ namespace mbts.spark { } partial class GridSettingsVM { - public interface IColumnVM : IValueFactory<(GridColumnSettings settings, ILookup animations)>, IDisposable { + public interface IColumnVM : ISimpleColumnVM, IValueFactory<(GridColumnSettings settings, ILookup animations)>, IDisposable { ObservableEditableCollection predicate_animations { get; } - string id { get; } - string display_name { get; set; } - double width { get; set; } - bool is_visible { get; set; } - bool is_font_color_overridden { get; set; } - ColorPickerPopupVM font_color_override { get; } - bool is_font_weight_overridden { get; set; } - FontWeight font_weight_override { get; set; } - void apply_flags(GridColumnSettingsFeatureFlags flags); + new (GridColumnSettings settings, ILookup animations) create(); + new void load_from((GridColumnSettings settings, ILookup animations) args); } public class ColumnVM : DisposableViewModelBase, IColumnVM { @@ -133,6 +125,12 @@ namespace mbts.spark { } public override string ToString() => $"{display_name} ({id})"; + + GridColumnSettings IValueFactory.create() => + create().settings; + + void IValueFactory.load_from(GridColumnSettings value) => + load_from((value, new Dictionary().ToLookup(kv => kv.Key, kv => kv.Value))); } public class PredicateCellAnimationVM : DisposableViewModelBase, IValueFactory<(TPredicateBuilder predicate, IEnumerable animations)> { diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs index aa4264d68..e48955331 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs @@ -68,6 +68,7 @@ namespace mbts.spark { public ColorPickerPopupVM font_color { get; } = new ColorPickerPopupVM(); public ColorPickerPopupVM selection_highlight { get; } = new ColorPickerPopupVM(); public double font_size { get => _font_size.Value; set => _font_size.set(value); } + public bool enable_sound_triggers => true; public bool show_predicate_animations { get => _show_predicate_animations.Value; set => _show_predicate_animations.set(value); } public bool show_cell_predicate_animations { get => _show_cell_predicate_animations.Value; set => _show_cell_predicate_animations.set(value); } public ObservableEditableCollection predicate_animations { get; } diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsView.xaml b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsView.xaml index a6adf52c3..58bd1cc51 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsView.xaml +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsView.xaml @@ -359,6 +359,9 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use Default + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml.cs new file mode 100644 index 000000000..19904f46f --- /dev/null +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml.cs @@ -0,0 +1,13 @@ +using System.Windows; + +namespace mbts.ui.spark { + /// + /// Interaction logic for SimpleGridSettingsDialog.xaml + /// + public partial class SimpleGridSettingsDialog : Window { + public SimpleGridSettingsDialog() => + InitializeComponent(); + + public const string ResetToFactoryKey = "Reset", CopyFromOtherKey = "Copy"; + } +} \ No newline at end of file diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs index fd269052c..4dd73a1c0 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs @@ -1,11 +1,8 @@ -using bts.api; -using bts.proto; -using bts.shell; +using bts.proto; +using mbts.persist; using mbts.spark; using mbts.ui.settings; using mbts.ui.shell; -using mbts.ui.spark; -using mbts.ui.utility; using mbts.utils; using System; using System.Collections.Generic; @@ -18,8 +15,7 @@ using System.Windows.Media; using static mbts.ui.controls.DIYGrid; namespace mbts.ui { - class SimpleGridSettingsManager : DisposableViewModelBase { - readonly ICollectionView _rows_view; + class SimpleGridSettingsManager : ViewModelBase, IObserver { readonly ObservableCollection _columns; readonly Property _grid_settings; readonly DelegateProperty _font_family; @@ -27,17 +23,11 @@ namespace mbts.ui { readonly DelegateProperty _font_size; readonly DelegateProperty _font_color, _selection_highlight; readonly IReadOnlyDictionary _column_flags; - readonly IObservable _owner_title; - public SimpleGridSettingsManager( - IObservable owner_title, IObservable settings_source, ICollectionView rows_view, ObservableCollection columns) : - this(owner_title, settings_source, rows_view, columns, new Dictionary()) { } + public SimpleGridSettingsManager(ObservableCollection columns) + : this(columns, new Dictionary()) { } - public SimpleGridSettingsManager( - IObservable owner_title, IObservable settings_source, ICollectionView rows_view, ObservableCollection columns, IReadOnlyDictionary column_flags) { - // - _owner_title = owner_title; - _rows_view = rows_view; + public SimpleGridSettingsManager(ObservableCollection columns, IReadOnlyDictionary column_flags) { _columns = columns; _column_flags = column_flags.ToDictionary(kv => kv.Key, kv => ImmutableColumnSettingStore.create(kv.Value)); _grid_settings = backing(nameof(grid_settings), GridSettings.Default); @@ -46,25 +36,6 @@ namespace mbts.ui { _font_size = backing(nameof(font_size), () => grid_settings.font_size, nameof(font_size)); _font_color = backing(nameof(font_color), () => grid_settings.font_color.rgb_solid_brush().frozen(), nameof(grid_settings)); _selection_highlight = backing(nameof(selection_highlight), () => grid_settings.selection_highlight.to_brush().frozen(), nameof(grid_settings)); - - if (settings_source != null) { - _dispose = settings_source.OnDispatcher().Subscribe(on_settings_changed); - } - - void on_settings_changed(GridSettings grid) { - if (grid.columns != null) { - var cols_with_applied_flags = grid.columns.ToList(); - for (int i = 0; i < cols_with_applied_flags.Count; i++) { - if (_column_flags.TryGetValue(cols_with_applied_flags[i].id, out ImmutableColumnSettingStore store)) { - cols_with_applied_flags[i] = store.apply(cols_with_applied_flags[i]); - } - } - grid = grid.with(cols_with_applied_flags); - } - // IMPORTANT: don't overwrite current sorting - grid = grid.with(sorting_criterion: _rows_view.get_sorting_criteria(_columns)); - _load_from_core(grid, false); - } } public GridSettings grid_settings => _grid_settings.Value; @@ -74,37 +45,6 @@ namespace mbts.ui { public SolidColorBrush font_color => _font_color.Value; public SolidColorBrush selection_highlight => _selection_highlight.Value; - public Window view_factory() { - var vm = new SimpleGridSettingsVM(_owner_title); - // need to refresh before launching the settings-view bc users can chanve some grid-column-settings properties directly from the grid (e.g. width, is_collapsed, etc.) - _grid_settings.set(grid_settings.with(_columns.Select(c => c.create()))); - vm.load_from(grid_settings); - if (_column_flags != null) { - foreach (var col in vm.columns) { - if (_column_flags.TryGetValue(col.id, out ImmutableColumnSettingStore s)) - col.apply_flags(s.flags); - } - } - var window = new GridSettingsDialog { DataContext = vm }; - vm.committed += () => { - var grid = vm.create(); - if (grid.columns != null) { - var cols_with_applied_flags = grid.columns.ToList(); - for (int i = 0; i < cols_with_applied_flags.Count; i++) { - if (_column_flags.TryGetValue(cols_with_applied_flags[i].id, out ImmutableColumnSettingStore store)) { - cols_with_applied_flags[i] = store.apply(cols_with_applied_flags[i]); - } - } - grid = grid.with(cols_with_applied_flags); - } - // IMPORTANT: don't overwrite current sorting - grid = grid.with(sorting_criterion: _rows_view.get_sorting_criteria(_columns)); - _load_from_core(grid, false); - }; - vm.RequestClose += (s, e) => WindowUtilities.force_close(window); - return window; - } - public GridSettings create() { // IMPORTANT: don't overwrite current sorting _grid_settings.set(grid_settings.with(_columns.Select(c => { @@ -112,19 +52,33 @@ namespace mbts.ui { if (_column_flags.TryGetValue(c.id, out ImmutableColumnSettingStore store)) col_settings = store.apply(col_settings); return col_settings; - }), _rows_view.get_sorting_criteria(_columns))); + }))); return grid_settings; } public void load_from(GridSettings grid) => _load_from_core(grid, true); + void IObserver.OnCompleted() { } + void IObserver.OnError(Exception error) { } + void IObserver.OnNext(GridSettings grid) { + if (grid.columns != null) { + var cols_with_applied_flags = grid.columns.ToList(); + for (int i = 0; i < cols_with_applied_flags.Count; i++) { + if (_column_flags.TryGetValue(cols_with_applied_flags[i].id, out ImmutableColumnSettingStore store)) { + cols_with_applied_flags[i] = store.apply(cols_with_applied_flags[i]); + } + } + grid = grid.with(cols_with_applied_flags); + } + _load_from_core(grid, false); + } + void _load_from_core(GridSettings grid, bool reset_immutable_flag_stores) { grid = grid ?? GridSettings.Default; _grid_settings.set(grid); foreach (ColumnBase col in _columns) col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight); - _rows_view.set_sort_descriptions(grid.sorting_criterion, _columns); // ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings // don't add insult to injury by crashing ** if (grid_settings.columns != null) { @@ -218,4 +172,4 @@ namespace mbts.ui { } } } -} \ No newline at end of file +} diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs index b46a2653f..aff6a61b4 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs @@ -24,7 +24,7 @@ namespace mbts.spark { void apply_flags(GridColumnSettingsFeatureFlags flags); } - partial class SimpleGridSettingsVM : DisposableViewModelBase, IValueFactory { + partial class SimpleGridSettingsVM : DisposableViewModelBase, IValueFactory, IWindowSettingsVM { readonly Property _current_column; readonly Property _font_family; readonly Property _font_weight; @@ -33,7 +33,6 @@ namespace mbts.spark { readonly Property _show_predicate_animations, _show_cell_predicate_animations; public SimpleGridSettingsVM(IObservable owner_title) { - // _owner_title = backing(nameof(owner_title), owner_title); _font_family = backing(nameof(font_family), TextUtils.StandardFontFamily); _font_size = backing(nameof(font_size), FontSizes.Small); @@ -87,5 +86,8 @@ namespace mbts.spark { curr_column = columns.FirstOrDefault(); _current_column.set(curr_column); } + + void IWindowSettingsVM.load(GridSettings value) => load_from(value); + GridSettings IWindowSettingsVM.get_result() => create(); } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderVM.cs b/src/client/spark/view/Spark/Ladder/LadderVM.cs index 3bc639b4a..998491700 100644 --- a/src/client/spark/view/Spark/Ladder/LadderVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderVM.cs @@ -64,7 +64,7 @@ namespace mbts.spark { show_book_command = new DelegateCommand(_show_book); show_ticket_command = new DelegateCommand(_show_ticket); settings_command = new DialogCommand(() => { - var vm = new WindowSettingsDialogVM(settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER); + var vm = new WindowSettingsDialogVM(settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER, () => new LadderSettingsVM()); var dialog = new ui.spark.LadderSettingsDialog { DataContext = vm, Title = $"Ladder Settings - '{security}'" diff --git a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs index ccc6a5bf1..4e8d388ea 100644 --- a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs +++ b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs @@ -11,18 +11,18 @@ namespace mbts.spark { T get_result(); } - class WindowSettingsDialogVM : ViewModelBase, IRequestViewClose where T : class, IEquatable where TVM : IWindowSettingsVM, new() { + class WindowSettingsDialogVM : ViewModelBase, IRequestViewClose where T : class, IEquatable where TVM : IWindowSettingsVM { readonly Property _default_tab_selected, _use_default; readonly DialogSkirtItem _sync_to_factory_item, _sync_to_default_item, _sync_to_instance_item, _ok_item, _apply_item, _cancel_item; readonly DelegateProperty> _left_skirt_items; - public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext) { + public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext, Func vm_factory) { var settings = settings_manager.current_settings(); _default_tab_selected = backing(nameof(default_tab_selected), settings_manager.instance_settings == null); _use_default = backing(nameof(use_default), default_tab_selected); - default_vm = new TVM(); + default_vm = vm_factory(); default_vm.load(settings_manager.default_settings); - instance_vm = new TVM(); + instance_vm = vm_factory(); instance_vm.load(settings_manager.current_settings()); // items const string import_group = "i", export_group = "e"; From e8b210913c3384e4bca3657a0421298c94691475 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Fri, 21 Sep 2018 14:00:03 -0400 Subject: [PATCH 066/203] SPARK-1659 Refactor PriceSize to PriceSizeSize --- src/lib/md/MDConsumerHandle.cpp | 4 +- src/lib/md/MDConsumerHandle.h | 6 +- src/lib/md/MDProducerHandle.cpp | 4 +- src/lib/md/PriceSize.cpp | 3 +- src/lib/md/PriceSize.h | 113 ++++++----- src/lib/md/Shm/MDStructure.h | 7 +- src/lib/md/relay/QueryServer.cpp | 8 +- src/lib/md/relay/RelayClient.cpp | 6 +- src/lib/md/test/Co2ManagedTestHelper.cpp | 16 +- src/lib/md/test/md2_tests.cpp | 191 +++++++++++++++--- .../md/test/synthetic_book_merge_tests.cpp | 4 +- .../spreader_orders/tests/tests_common.h | 10 +- src/sdk/example/example.cpp | 40 ++-- .../exchange_sim/ExchangeSimApplication.cpp | 6 +- .../md/platforms/mdp3_onixs/MDP3Listener.h | 4 +- 15 files changed, 277 insertions(+), 145 deletions(-) diff --git a/src/lib/md/MDConsumerHandle.cpp b/src/lib/md/MDConsumerHandle.cpp index 87397243a..6526acfa6 100644 --- a/src/lib/md/MDConsumerHandle.cpp +++ b/src/lib/md/MDConsumerHandle.cpp @@ -187,12 +187,12 @@ TimeStamp copy_depth(const SyntheticStrategy& synth, size_t count, PriceSize* bi const auto& spec = synth.security.contract_spec(); copy_depth(count, api::SIDE_BUY, spec, bid_leg_travs, scratch.data()); for (size_t x = 0; x < count; ++x) { - bid[x] = PriceSize::from_double<7>(scratch[x].price, scratch[x].size); + bid[x] = PriceSize_from_double<7>(scratch[x].price, scratch[x].size); } memset(scratch.data(), 0, sizeof(scratch)); copy_depth(count, api::SIDE_SELL, spec, ask_leg_travs, scratch.data()); for (size_t x = 0; x < count; ++x) { - ask[x] = PriceSize::from_double<7>(scratch[x].price, scratch[x].size); + ask[x] = PriceSize_from_double<7>(scratch[x].price, scratch[x].size); } return rv; } diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 4e2e4b656..96aae64b6 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -64,7 +64,8 @@ inline void read_combined( bid[x] = PriceSize( direct.mantissa(), direct.exponent(), - direct.size() + implied.size()); + direct.size() + implied.size(), + direct.size()); xdb += 1; xib += 1; } @@ -93,7 +94,8 @@ inline void read_combined( ask[x] = PriceSize( direct.mantissa(), direct.exponent(), - direct.size() + implied.size()); + direct.size() + implied.size(), + direct.size()); xda += 1; xia += 1; } diff --git a/src/lib/md/MDProducerHandle.cpp b/src/lib/md/MDProducerHandle.cpp index a63d1b826..2e1b20590 100644 --- a/src/lib/md/MDProducerHandle.cpp +++ b/src/lib/md/MDProducerHandle.cpp @@ -211,9 +211,9 @@ void MDProducerHandle::mark_deleted() { std::array blank; std::array iblank; for (size_t x = 0; x < blank.size(); ++x) { - blank[x] = PriceSize::NO_MARKET; + blank[x] = NO_MARKET; if (x < COCONUT_MAX_IMPLIED_DEPTH) { - iblank[x] = PriceSize::NO_MARKET; + iblank[x] = NO_MARKET; } } update_direct(ts, blank, blank, 0); diff --git a/src/lib/md/PriceSize.cpp b/src/lib/md/PriceSize.cpp index 490e132b6..46ea81030 100644 --- a/src/lib/md/PriceSize.cpp +++ b/src/lib/md/PriceSize.cpp @@ -4,7 +4,8 @@ namespace bts { namespace md { -BTS_EXPORT PriceSize PriceSize::NO_MARKET = PriceSize(); +IntPrice INT_NAN = IntPrice(); +PriceSize NO_MARKET = PriceSize(); int to_exponent(double value) { if (value == 0 && os::is_nan(value)) return 0; diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index dad526c48..40a651d9a 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -18,9 +18,6 @@ namespace md { typedef double Price; typedef uint32_t Size; -// #define LOAD(x) x.load(std::memory_order_acquire) -// #define STORE(x,value) x.store(value, std::memory_order_release) - static const double powers_10[] = { 1E-20, 1E-19, 1E-18, 1E-17, 1E-16, 1E-15, 1E-14, 1E-13, 1E-12, 1E-11, 1E-10, 1E-9, 1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, 1E0, @@ -58,6 +55,7 @@ class IntPrice { int8_t _exponent : 8; #endif public: + IntPrice() : _mantissa(0), _exponent(127) {/*nan*/} IntPrice(int64_t mantissa, int8_t exponent) : _mantissa(mantissa), _exponent(exponent) {} IntPrice(const IntPrice& other) : _mantissa(other._mantissa), _exponent(other._exponent) {} @@ -74,24 +72,33 @@ class IntPrice { int64_t mantissa() const { return _mantissa; } int8_t exponent() const { return _exponent; } - double price() const { return mantissa() * pow10(exponent()); } + double price() const { + if (is_nan()) return NO_PRICE; + return mantissa() * pow10(exponent()); + } + bool is_nan() const { return _exponent == 127; } }; - + +static_assert(sizeof(IntPrice) == 8, "IntPrice should be this big |--------|"); +extern IntPrice INT_NAN; class PriceSize { IntPrice _price; uint32_t _size; + uint32_t _direct_size; public: PriceSize(int64_t mantissa, int8_t exponent, Size size) : _price(mantissa, exponent), - _size(size) + _size(size), + _direct_size(size) { } - PriceSize(IntPrice& price, Size size) : - _price(price), - _size(size) + PriceSize(int64_t mantissa, int8_t exponent, Size size, Size direct_size) : + _price(mantissa, exponent), + _size(size), + _direct_size(direct_size) { } - PriceSize() : _price(0,0), _size(0) {} + PriceSize() : _price(INT_NAN), _size(0), _direct_size(0) {} PriceSize(const PriceSize& other) = default; PriceSize& operator=(const PriceSize& other) = default; @@ -103,49 +110,34 @@ public: return !(*this == other); } - BTS_EXPORT static PriceSize NO_MARKET; - - template - static PriceSize from_double(double price, Size size) { - if (size == 0 || os::is_nan(price)) return NO_MARKET; - double temp; - if (fabs(modf(price, &temp)) != 0.0) { - const double rounding = 0.5 * pow10(-PRECISION); - price += (price > 0) ? rounding : -rounding; - price *= pow10(PRECISION); - return PriceSize((int64_t)price, -PRECISION, size); - } - return PriceSize((int64_t)price, 0, size); - } - static PriceSize from_double(double price, int precision, Size size) { - if (size == 0 || os::is_nan(price)) return NO_MARKET; - double temp; - if (fabs(modf(price, &temp)) != 0.0) { - const double rounding = 0.5 * pow10(-precision); - price += (price > 0) ? rounding : -rounding; - price *= pow10(precision); - return PriceSize((int64_t)price, -precision, size); - } - return PriceSize((int64_t)price, 0, size); - } - // // Accessors int64_t mantissa() const { return _price.mantissa(); } int8_t exponent() const { return _price.exponent(); } Size size() const { return _size; } - // This call is relatively slow so don't call it often - double price() const { - if (is_nan()) return NO_PRICE; - return _price.price(); - } + Size direct_size() const { return _direct_size; } + Size implied_size() const { return _size - _direct_size; } + double price() const { return _price.price(); } IntPrice int_price() const { return _price; } - bool is_nan() const { - return (*this == NO_MARKET); - } + bool is_nan() const { return _price.is_nan(); } }; +extern PriceSize NO_MARKET; + static_assert(sizeof(PriceSize) == 16, "PriceSize should be small"); + +template +PriceSize PriceSize_from_double(double price, Size size) { + if (size == 0 || os::is_nan(price)) return NO_MARKET; + double temp; + if (fabs(modf(price, &temp)) != 0.0) { + const double rounding = 0.5 * pow10(-PRECISION); + price += (price > 0) ? rounding : -rounding; + price *= pow10(PRECISION); + return PriceSize((int64_t)price, -PRECISION, size); + } + return PriceSize((int64_t)price, 0, size); +} typedef utils::Optimistic AtomicPriceSize; @@ -158,18 +150,6 @@ struct PriceLevel { _mantissa_b(0), _exponent_b(0), _mantissa_a(0), _exponent_a(0), _size_b(0), _size_a(0) {} - PriceLevel( - int64_t bid_mantissa, - int8_t bid_exponent, - uint32_t bid_size, - int64_t ask_mantissa, - int8_t ask_exponent, - uint32_t ask_size) : - _mantissa_b(bid_mantissa), _exponent_b(bid_exponent), - _mantissa_a(ask_mantissa), _exponent_a(ask_exponent), - _size_b(bid_size), - _size_a(ask_size) - {} PriceLevel(const PriceSize& bid, const PriceSize& ask): _mantissa_b(bid.mantissa()), _exponent_b(bid.exponent()), _mantissa_a(ask.mantissa()), _exponent_a(ask.exponent()), @@ -231,6 +211,27 @@ struct PriceLevel { }; static_assert(sizeof(PriceLevel) == 24, "PriceLevel should be small"); +struct ImpliedPriceLevel : private PriceLevel { + ImpliedPriceLevel() : PriceLevel() {} + ImpliedPriceLevel(const PriceSize& bid, const PriceSize& ask): + PriceLevel(bid,ask) {} + using PriceLevel::bid_mantissa; + using PriceLevel::ask_mantissa; + using PriceLevel::bid_exponent; + using PriceLevel::ask_exponent; + using PriceLevel::bid_size; + using PriceLevel::ask_size; + using PriceLevel::operator==; + using PriceLevel::operator!=; + bool operator=(const PriceLevel& other) { return update(other); } + PriceSize bid() const { + return PriceSize(_mantissa_b, _exponent_b, _size_b, 0); + } + PriceSize ask() const { + return PriceSize(_mantissa_a, _exponent_a, _size_a, 0); + } +}; +static_assert(sizeof(ImpliedPriceLevel) == 24, "PriceLevel should be small"); }}//namespace diff --git a/src/lib/md/Shm/MDStructure.h b/src/lib/md/Shm/MDStructure.h index e1a128ae7..3b276a0cf 100644 --- a/src/lib/md/Shm/MDStructure.h +++ b/src/lib/md/Shm/MDStructure.h @@ -68,7 +68,7 @@ class HotDepthEntry { security_key key; // 4 NOTE: key is at a different offset than it is in HotEntry uint32_t sequence; // 4 std::array direct; // 240 - std::array implied; // 48 + std::array implied; // 48 uint8_t status; // 1 void read_direct(uint32_t depth, PriceSize* bid, PriceSize* ask) const { @@ -76,8 +76,9 @@ class HotDepthEntry { *ask = direct[depth].ask(); } void read_implied(uint32_t depth, PriceSize* bid, PriceSize* ask) const { - *bid = implied[depth].bid(); - *ask = implied[depth].ask(); + const auto& d = implied[depth]; + *bid = PriceSize(d.bid_mantissa(), d.bid_exponent(), d.bid_size(), 0); + *ask = PriceSize(d.ask_mantissa(), d.ask_exponent(), d.ask_size(), 0); } bool write_direct(uint32_t depth, const PriceSize& bid, const PriceSize& ask) { bts_assert_d(depth < direct.size()); diff --git a/src/lib/md/relay/QueryServer.cpp b/src/lib/md/relay/QueryServer.cpp index fa1c6d103..24419e0b6 100644 --- a/src/lib/md/relay/QueryServer.cpp +++ b/src/lib/md/relay/QueryServer.cpp @@ -250,11 +250,11 @@ public: auto* p = pupd->mutable_direct(); for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { PriceSize b = bids[x]; - if (b != PriceSize::NO_MARKET) { + if (b != NO_MARKET) { convert(b, p->add_bids()); } PriceSize a = asks[x]; - if (a != PriceSize::NO_MARKET) { + if (a != NO_MARKET) { convert(a, p->add_asks()); } } @@ -279,11 +279,11 @@ public: auto* p = pupd->mutable_implied(); for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { PriceSize b = bids[x]; - if (b != PriceSize::NO_MARKET) { + if (b != NO_MARKET) { convert(b, p->add_bids()); } PriceSize a = asks[x]; - if (a != PriceSize::NO_MARKET) { + if (a != NO_MARKET) { convert(a, p->add_asks()); } } diff --git a/src/lib/md/relay/RelayClient.cpp b/src/lib/md/relay/RelayClient.cpp index 696ba4747..629d82348 100644 --- a/src/lib/md/relay/RelayClient.cpp +++ b/src/lib/md/relay/RelayClient.cpp @@ -32,17 +32,17 @@ namespace md { using namespace co2; PriceSize convert(const PriceSizeMessage& psm) { - return psm.size() > 0 ? PriceSize(psm.mantissa(), psm.exponent(), psm.size()) : PriceSize::NO_MARKET; + return psm.size() > 0 ? PriceSize(psm.mantissa(), psm.exponent(), psm.size()) : NO_MARKET; } PriceSize trade_pricesize(const co2::TradeMessage& trade) { return trade.size() > 0 ? - PriceSize(trade.mantissa(), trade.exponent(), trade.size()) : PriceSize::NO_MARKET; + PriceSize(trade.mantissa(), trade.exponent(), trade.size()) : NO_MARKET; } PriceSize book_at(const google::protobuf::RepeatedPtrField& side, int index) { if (side.size() > index) { return convert(side.Get(index)); } - return PriceSize::NO_MARKET; + return NO_MARKET; } class RelayProducer : public IRelayWriter { diff --git a/src/lib/md/test/Co2ManagedTestHelper.cpp b/src/lib/md/test/Co2ManagedTestHelper.cpp index 7e11ecd18..30eb8c243 100644 --- a/src/lib/md/test/Co2ManagedTestHelper.cpp +++ b/src/lib/md/test/Co2ManagedTestHelper.cpp @@ -37,8 +37,8 @@ extern "C" BTS_EXPORT void co2_get_sample_hot(size_t& size, HotEntry& entry) { size = sizeof(HotEntry); entry.key = 1234; entry.book = PriceLevel( - PriceSize::from_double<2>(7.2, 101), - PriceSize::from_double<2>(-9.7, 245)); + PriceSize_from_double<2>(7.2, 101), + PriceSize_from_double<2>(-9.7, 245)); entry.sequence = 777; entry.status = COCONUTSECURITYSTATUS_PACKET_LOSS; entry.set_time(utils::TimeStamp::now()); @@ -49,8 +49,8 @@ extern "C" BTS_EXPORT void co2_get_sample_depth(size_t& size, HotDepthEntry& ent entry.key = 1234; for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { entry.write_direct(x, - PriceSize::from_double<2>(x + 0.5, x + 1), - PriceSize::from_double<2>(x + 0.6, x + 2)); + PriceSize_from_double<2>(x + 0.5, x + 1), + PriceSize_from_double<2>(x + 0.6, x + 2)); } entry.set_time(utils::TimeStamp::now()); entry.status = COCONUTSECURITYSTATUS_PACKET_LOSS; @@ -62,7 +62,7 @@ extern "C" BTS_EXPORT void co2_get_sample_cold(size_t& size, ColdEntry& entry) { entry.exchange_status = TRADINGSTATUS_NORMAL; // Last trade - entry.last_trade.store(PriceSize::from_double<2>(4.2, 101)); + entry.last_trade.store(PriceSize_from_double<2>(4.2, 101)); entry.total_volume = 12345; entry.last_trade_time = datetime::ll_now(); // Settlement @@ -113,8 +113,8 @@ extern "C" BTS_EXPORT MDProducer* co2_produce_test_md() { auto producers = create_test_securities(*p); assign_ids(producers.begin(), producers.end()); for (auto& p : producers) { - p.update_bbo(utils::TimeStamp::now(), PriceSize::from_double<3>(1.1, 101), PriceSize::from_double<3>(2.2,202)); - p.add_trade(utils::TimeStamp::now(), PriceSize::from_double<3>(5.5, 505), datetime::ll_from_date(datetime::Date(2015, 9, 22)), AGGRESSOR_SIDE_NONE, 4200); + p.update_bbo(utils::TimeStamp::now(), PriceSize_from_double<3>(1.1, 101), PriceSize_from_double<3>(2.2,202)); + p.add_trade(utils::TimeStamp::now(), PriceSize_from_double<3>(5.5, 505), datetime::ll_from_date(datetime::Date(2015, 9, 22)), AGGRESSOR_SIDE_NONE, 4200); p.update_settlement(utils::TimeStamp::now(), 6.6, datetime::Date(2000, 8, 8)); p.update_settlement(utils::TimeStamp::now(), 6.6, datetime::Date(2000, 8, 9)); @@ -138,7 +138,7 @@ extern "C" BTS_EXPORT void co2_destroy_test_md(MDProducer* producer) { } extern "C" BTS_EXPORT void co2_get_zero_and_nan_book(PriceLevel& price) { - price = { PriceSize::NO_MARKET, PriceSize::from_double<7>(0,1) }; + price = { NO_MARKET, PriceSize_from_double<7>(0,1) }; } diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index 13387eb16..ea1bd99c3 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -196,8 +196,8 @@ public: // PriceSize violates some type punning rules // So it is important to have this test work correctly on the target platform PriceSize p2; - auto bid = PriceSize::from_double<2>(4.5, 14); - auto ask = PriceSize::from_double<9>(4.5, 14); + auto bid = PriceSize_from_double<2>(4.5, 14); + auto ask = PriceSize_from_double<9>(4.5, 14); PriceLevel memory; memory = PriceLevel(bid,ask); REQUIRE(bid.price() == memory.bid().price()); @@ -209,14 +209,14 @@ public: REQUIRE(p.size() == 14); } - auto p = PriceSize::from_double<2>(4.0, 5); + auto p = PriceSize_from_double<2>(4.0, 5); memory = PriceLevel(p,p); for (auto p2 : { memory.bid(), memory.ask() }) { REQUIRE(p2.price() == 4.0); REQUIRE(p2.size() == 5); } - p = PriceSize::from_double<2>(-4.2, 6); + p = PriceSize_from_double<2>(-4.2, 6); memory = PriceLevel(p,p); for (auto p2 : { memory.bid(), memory.ask() }) { REQUIRE(p2.price() == -4.2); @@ -232,7 +232,7 @@ public: // Test requirement for more than 32 bit mantissa double bigval = -9999.9921875; - p = PriceSize::from_double<7>(bigval, 42); + p = PriceSize_from_double<7>(bigval, 42); memory = PriceLevel(p,p); for (auto p2 : { memory.bid(), memory.ask() }) { REQUIRE(p2.price() == bigval); @@ -242,14 +242,14 @@ public: void test_price_size_required_precision() { auto min_tick_2017 = 9999.0078125; - auto p17 = PriceSize::from_double<7>(min_tick_2017, 2017); + auto p17 = PriceSize_from_double<7>(min_tick_2017, 2017); PriceLevel m17(p17, p17); for (auto p : { m17.bid(), m17.ask() }) { REQUIRE(min_tick_2017 == p.price()); REQUIRE(p.size() == 2017); } auto min_tick_2018 = 1999.00390625; - auto p18 = PriceSize::from_double<8>(min_tick_2018, 2018); + auto p18 = PriceSize_from_double<8>(min_tick_2018, 2018); PriceLevel m18(p18, p18); for (auto p : { m18.bid(), m18.ask() }) { REQUIRE(min_tick_2018 == p.price()); @@ -314,7 +314,7 @@ public: // Add bbo data for those expirations auto write_time = TimeStamp::now(); for (auto& p : producers) { - p.update_bbo(write_time, PriceSize::from_double<2>(5.0, 5), PriceSize::from_double<2>(5.0, 5)); + p.update_bbo(write_time, PriceSize_from_double<2>(5.0, 5), PriceSize_from_double<2>(5.0, 5)); } // Symbol list for the consumer @@ -366,7 +366,7 @@ public: write_time = TimeStamp::now(); for (auto& p : producers) { // Update bid/ask with one call - p.update_bbo(write_time, PriceSize::from_double<2>(6.55, 6), PriceSize::from_double<2>(6.55, 6)); + p.update_bbo(write_time, PriceSize_from_double<2>(6.55, 6), PriceSize_from_double<2>(6.55, 6)); } // Now read_time is < write_time for (const auto& c : consumers) { @@ -446,7 +446,7 @@ public: // Set bbo on the producers - still not public TimeStamp write_time = TimeStamp::now(); for (auto& p : producers) { - p.update_bbo(write_time, PriceSize::from_double<3>(4.5, 5), PriceSize::from_double<3>(6.75, 7)); + p.update_bbo(write_time, PriceSize_from_double<3>(4.5, 5), PriceSize_from_double<3>(6.75, 7)); } // Even though bbo is available it is not given out because the producing security does not have an id PriceSize bid, ask; @@ -517,7 +517,7 @@ public: // Set bbo on the producers - still not public TimeStamp write_time = TimeStamp::now(); for (auto& p : producers) { - p.update_bbo(write_time, PriceSize::from_double<3>( 4.5, 5 ), PriceSize::from_double<3>(6.75, 7)); + p.update_bbo(write_time, PriceSize_from_double<3>( 4.5, 5 ), PriceSize_from_double<3>(6.75, 7)); } assign_ids(producers.begin(), producers.end()); @@ -710,7 +710,7 @@ public: #pragma warning(pop) #endif TimeStamp read = TimeStamp::now(); - src->producer().update_bbo(TimeStamp::invalid(), PriceSize::from_double<3>(100.0, 100), PriceSize::from_double<3>(101.0, 101)); + src->producer().update_bbo(TimeStamp::invalid(), PriceSize_from_double<3>(100.0, 100), PriceSize_from_double<3>(101.0, 101)); PriceSize bid, ask; TimeStamp start = TimeStamp::now(); auto result = src->consumer().get_direct_book(read, 1, &bid, &ask); @@ -726,7 +726,7 @@ public: vector p2_no_id = create_test_securities(producer, "2"); vector p3 = create_test_securities(producer, "3"); - for (auto& p : producers) { p.update_bbo(TimeStamp::now(), PriceSize::from_double<3>(5.0, 5), PriceSize::from_double<3>(5.0, 5)); } + for (auto& p : producers) { p.update_bbo(TimeStamp::now(), PriceSize_from_double<3>(5.0, 5), PriceSize_from_double<3>(5.0, 5)); } assign_ids(producers.begin(), producers.end()); assign_ids(p3.begin(), p3.end()); @@ -1279,8 +1279,8 @@ struct Co2RelayTests { auto server = setup_query_server(producer, &producers); // assign prices - PriceSize price35 = PriceSize::from_double<2>(3.5, 5); - PriceSize price5 = PriceSize::from_double<2>(5, 5); + PriceSize price35 = PriceSize_from_double<2>(3.5, 5); + PriceSize price5 = PriceSize_from_double<2>(5, 5); for(auto p : producers) { p.update_bbo(TimeStamp::now(), price35, price35); } @@ -1338,7 +1338,7 @@ struct Co2RelayTests { // NaN out all the prices - expect to get a full set of updates for(auto p : producers) { - p.update_bbo(TimeStamp::now(), PriceSize::NO_MARKET, PriceSize::NO_MARKET, 2); + p.update_bbo(TimeStamp::now(), NO_MARKET, NO_MARKET, 2); } request.mutable_channels(0)->set_time(reply.channels(0).channel().time()); reply.Clear(); @@ -1351,8 +1351,8 @@ struct Co2RelayTests { for (auto& update : channel.updates()) { REQUIRE(update.has_direct()); // We need to have a book to signal that it was updated but it can have no rows. This signifies an empty book and will be written as such by the relay client. - REQUIRE(book_at(update.direct().bids(), 0) == PriceSize::NO_MARKET); - REQUIRE(book_at(update.direct().asks(), 0) == PriceSize::NO_MARKET); + REQUIRE(book_at(update.direct().bids(), 0) == NO_MARKET); + REQUIRE(book_at(update.direct().asks(), 0) == NO_MARKET); } } @@ -1380,7 +1380,7 @@ struct Co2RelayTests { // NaN out the bids for(auto p : producers) { - p.update_bbo(TimeStamp::now(), PriceSize::NO_MARKET, price35, 4); + p.update_bbo(TimeStamp::now(), NO_MARKET, price35, 4); } request.mutable_channels(0)->set_time(reply.channels(0).channel().time()); request.mutable_channels(1)->set_time(reply.channels(1).channel().time()); @@ -1393,7 +1393,7 @@ struct Co2RelayTests { for (auto& channel : reply.channels()) { for (auto& update : channel.updates()) { REQUIRE(update.has_direct()); - REQUIRE(book_at(update.direct().bids(), 0) == PriceSize::NO_MARKET); + REQUIRE(book_at(update.direct().bids(), 0) == NO_MARKET); REQUIRE(book_at(update.direct().asks(), 0) == price35); } } @@ -1423,8 +1423,8 @@ struct Co2RelayTests { p.update_implied(TimeStamp::now(), implied_bids, implied_asks); } }; - PriceSize price35 = PriceSize::from_double<7>(3.5, 5); - PriceSize price5 = PriceSize::from_double<7>(5, 5); + PriceSize price35 = PriceSize_from_double<7>(3.5, 5); + PriceSize price5 = PriceSize_from_double<7>(5, 5); set_direct(price35, price5); set_implied(price35, price5); @@ -1465,7 +1465,7 @@ struct Co2RelayTests { // // Nan out implied request.mutable_channels(0)->set_time(reply.channels(0).channel().time()); - set_implied(PriceSize::NO_MARKET, PriceSize::NO_MARKET); + set_implied(NO_MARKET, NO_MARKET); reply.Clear(); server->query(request, &reply); reply = simulate_wire_transfer(reply); @@ -1483,8 +1483,8 @@ struct Co2RelayTests { REQUIRE(book_at(update.direct().asks(), x) == price5); } for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { - REQUIRE(book_at(update.implied().bids(), x) == PriceSize::NO_MARKET); - REQUIRE(book_at(update.implied().asks(), x) == PriceSize::NO_MARKET); + REQUIRE(book_at(update.implied().bids(), x) == NO_MARKET); + REQUIRE(book_at(update.implied().asks(), x) == NO_MARKET); } } } @@ -1492,7 +1492,7 @@ struct Co2RelayTests { // // Nan out direct request.mutable_channels(0)->set_time(reply.channels(0).channel().time()); - set_direct(PriceSize::NO_MARKET, PriceSize::NO_MARKET); + set_direct(NO_MARKET, NO_MARKET); reply.Clear(); server->query(request, &reply); reply = simulate_wire_transfer(reply); @@ -1506,12 +1506,12 @@ struct Co2RelayTests { REQUIRE(update.implied().bids_size() == 0); REQUIRE(update.implied().asks_size() == 0); for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { - REQUIRE(book_at(update.direct().bids(), x) == PriceSize::NO_MARKET); - REQUIRE(book_at(update.direct().asks(), x) == PriceSize::NO_MARKET); + REQUIRE(book_at(update.direct().bids(), x) == NO_MARKET); + REQUIRE(book_at(update.direct().asks(), x) == NO_MARKET); } for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { - REQUIRE(book_at(update.implied().bids(), x) == PriceSize::NO_MARKET); - REQUIRE(book_at(update.implied().asks(), x) == PriceSize::NO_MARKET); + REQUIRE(book_at(update.implied().bids(), x) == NO_MARKET); + REQUIRE(book_at(update.implied().asks(), x) == NO_MARKET); } } } @@ -1614,9 +1614,9 @@ TEST_CASE("MergeImpliedTests", "[md]") { SECTION("equal_impiled_deep") { for (size_t x = 0; x < direct_bid.size(); ++x) { - direct_bid[x] = PriceSize::from_double<7>(x * 100, 2); + direct_bid[x] = PriceSize_from_double<7>(x * 100, 2); if (x < implied_bid.size()) { - implied_bid[x] = PriceSize::from_double<7>(x * 100, 1); + implied_bid[x] = PriceSize_from_double<7>(x * 100, 1); } } direct_ask = direct_bid; @@ -1629,13 +1629,22 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(c.get_direct_book(read_time, COCONUT_MAX_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { REQUIRE(bids[x].size() == 2); + REQUIRE(bids[x].direct_size() == 2); + REQUIRE(bids[x].implied_size() == 0); REQUIRE(asks[x].size() == 2); + REQUIRE(asks[x].direct_size() == 2); + REQUIRE(asks[x].implied_size() == 0); } read_time = TimeStamp(); REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { REQUIRE(bids[x].size() == 1); REQUIRE(asks[x].size() == 1); + + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 1); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 1); } read_time = TimeStamp(); REQUIRE(c.get_combined_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); @@ -1643,10 +1652,20 @@ TEST_CASE("MergeImpliedTests", "[md]") { if (x < COCONUT_MAX_IMPLIED_DEPTH) { REQUIRE(bids[x].size() == 3); REQUIRE(asks[x].size() == 3); + + REQUIRE(bids[x].direct_size() == 2); + REQUIRE(bids[x].implied_size() == 1); + REQUIRE(asks[x].direct_size() == 2); + REQUIRE(asks[x].implied_size() == 1); } else { REQUIRE(bids[x].size() == 2); REQUIRE(asks[x].size() == 2); + + REQUIRE(bids[x].direct_size() == 2); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 2); + REQUIRE(asks[x].implied_size() == 0); } } } @@ -1682,12 +1701,20 @@ TEST_CASE("MergeImpliedTests", "[md]") { for (size_t x = 0; x < TEST_DEPTH; ++x) { REQUIRE(bids[x].size() == 1); REQUIRE(asks[x].size() == 1); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 0); } read_time = TimeStamp(); REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { REQUIRE(bids[x].size() == 2); REQUIRE(asks[x].size() == 2); + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 2); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); @@ -1695,10 +1722,18 @@ TEST_CASE("MergeImpliedTests", "[md]") { if (x < COCONUT_MAX_IMPLIED_DEPTH) { REQUIRE(bids[x].size() == 3); REQUIRE(asks[x].size() == 3); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 2); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 2); } else { REQUIRE(bids[x].size() == 1); REQUIRE(asks[x].size() == 1); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 0); } } REQUIRE(canary == 0); @@ -1735,12 +1770,20 @@ TEST_CASE("MergeImpliedTests", "[md]") { for (size_t x = 0; x < TEST_DEPTH; ++x) { REQUIRE(bids[x].size() == 1); REQUIRE(asks[x].size() == 1); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 0); } read_time = TimeStamp(); REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { REQUIRE(bids[x].size() == 2); REQUIRE(asks[x].size() == 2); + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 2); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); @@ -1750,9 +1793,17 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(asks[0].mantissa() == 900); REQUIRE(bids[1].size() == 3); REQUIRE(asks[1].size() == 3); + REQUIRE(bids[1].direct_size() == 1); + REQUIRE(bids[1].implied_size() == 2); + REQUIRE(asks[1].direct_size() == 1); + REQUIRE(asks[1].implied_size() == 2); for (size_t x = 2; x < TEST_DEPTH; ++x) { REQUIRE(bids[x].size() == 1); REQUIRE(asks[x].size() == 1); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 0); } REQUIRE(canary == 0); } @@ -1788,12 +1839,20 @@ TEST_CASE("MergeImpliedTests", "[md]") { for (size_t x = 0; x < TEST_DEPTH; ++x) { REQUIRE(bids[x].size() == 1); REQUIRE(asks[x].size() == 1); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 0); } read_time = TimeStamp(); REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { REQUIRE(bids[x].size() == 2); REQUIRE(asks[x].size() == 2); + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 2); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); @@ -1801,21 +1860,37 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(bids[0].size() == 1); REQUIRE(asks[0].mantissa() == 1000); REQUIRE(asks[0].size() == 1); + REQUIRE(bids[0].direct_size() == 1); + REQUIRE(bids[0].implied_size() == 0); + REQUIRE(asks[0].direct_size() == 1); + REQUIRE(asks[0].implied_size() == 0); REQUIRE(bids[1].mantissa() == 400); REQUIRE(bids[1].size() == 3); REQUIRE(asks[1].mantissa() == 1100); REQUIRE(asks[1].size() == 3); + REQUIRE(bids[1].direct_size() == 1); + REQUIRE(bids[1].implied_size() == 2); + REQUIRE(asks[1].direct_size() == 1); + REQUIRE(asks[1].implied_size() == 2); REQUIRE(bids[2].mantissa() == 300); REQUIRE(bids[2].size() == 3); REQUIRE(asks[2].mantissa() == 1200); REQUIRE(asks[2].size() == 3); + REQUIRE(bids[2].direct_size() == 1); + REQUIRE(bids[2].implied_size() == 2); + REQUIRE(asks[2].direct_size() == 1); + REQUIRE(asks[2].implied_size() == 2); REQUIRE(bids[3].mantissa() == 200); REQUIRE(bids[3].size() == 1); REQUIRE(asks[3].mantissa() == 1300); REQUIRE(asks[3].size() == 1); + REQUIRE(bids[3].direct_size() == 1); + REQUIRE(bids[3].implied_size() == 0); + REQUIRE(asks[3].direct_size() == 1); + REQUIRE(asks[3].implied_size() == 0); REQUIRE(canary == 0); } @@ -1851,12 +1926,20 @@ TEST_CASE("MergeImpliedTests", "[md]") { for (size_t x = 0; x < TEST_DEPTH; ++x) { REQUIRE(bids[x].size() == 1); REQUIRE(asks[x].size() == 1); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 0); } read_time = TimeStamp(); REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { REQUIRE(bids[x].size() == 2); REQUIRE(asks[x].size() == 2); + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 2); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); @@ -1864,21 +1947,37 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(bids[0].size() == 1); REQUIRE(asks[0].mantissa() == 1000); REQUIRE(asks[0].size() == 3); + REQUIRE(bids[0].direct_size() == 1); + REQUIRE(bids[0].implied_size() == 0); + REQUIRE(asks[0].direct_size() == 1); + REQUIRE(asks[0].implied_size() == 2); REQUIRE(bids[1].mantissa() == 400); REQUIRE(bids[1].size() == 3); REQUIRE(asks[1].mantissa() == 1100); REQUIRE(asks[1].size() == 3); + REQUIRE(bids[1].direct_size() == 1); + REQUIRE(bids[1].implied_size() == 2); + REQUIRE(asks[1].direct_size() == 1); + REQUIRE(asks[1].implied_size() == 2); REQUIRE(bids[2].mantissa() == 300); REQUIRE(bids[2].size() == 3); REQUIRE(asks[2].mantissa() == 1200); REQUIRE(asks[2].size() == 1); + REQUIRE(bids[2].direct_size() == 1); + REQUIRE(bids[2].implied_size() == 2); + REQUIRE(asks[2].direct_size() == 1); + REQUIRE(asks[2].implied_size() == 0); REQUIRE(bids[3].mantissa() == 200); REQUIRE(bids[3].size() == 1); REQUIRE(asks[3].mantissa() == 1300); REQUIRE(asks[3].size() == 1); + REQUIRE(bids[3].direct_size() == 1); + REQUIRE(bids[3].implied_size() == 0); + REQUIRE(asks[3].direct_size() == 1); + REQUIRE(asks[3].implied_size() == 0); REQUIRE(canary == 0); } @@ -1913,12 +2012,20 @@ TEST_CASE("MergeImpliedTests", "[md]") { for (size_t x = 0; x < TEST_DEPTH; ++x) { REQUIRE(bids[x].size() == 1); REQUIRE(asks[x].size() == 1); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 0); } read_time = TimeStamp(); REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { REQUIRE(bids[x].size() == 2); REQUIRE(asks[x].size() == 2); + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 2); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); REQUIRE(c.get_combined_book(read_time, TEST_DEPTH+1, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); @@ -1926,26 +2033,46 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(bids[0].size() == 3); REQUIRE(asks[0].mantissa() == 1000); REQUIRE(asks[0].size() == 1); + REQUIRE(bids[0].direct_size() == 1); + REQUIRE(bids[0].implied_size() == 2); + REQUIRE(asks[0].direct_size() == 1); + REQUIRE(asks[0].implied_size() == 0); REQUIRE(bids[1].mantissa() == 400); REQUIRE(bids[1].size() == 3); REQUIRE(asks[1].mantissa() == 1100); REQUIRE(asks[1].size() == 1); + REQUIRE(bids[1].direct_size() == 1); + REQUIRE(bids[1].implied_size() == 2); + REQUIRE(asks[1].direct_size() == 1); + REQUIRE(asks[1].implied_size() == 0); REQUIRE(bids[2].mantissa() == 300); REQUIRE(bids[2].size() == 1); REQUIRE(asks[2].mantissa() == 1200); REQUIRE(asks[2].size() == 1); + REQUIRE(bids[2].direct_size() == 1); + REQUIRE(bids[2].implied_size() == 0); + REQUIRE(asks[2].direct_size() == 1); + REQUIRE(asks[2].implied_size() == 0); REQUIRE(bids[3].mantissa() == 200); REQUIRE(bids[3].size() == 1); REQUIRE(asks[3].mantissa() == 1300); REQUIRE(asks[3].size() == 3); + REQUIRE(bids[3].direct_size() == 1); + REQUIRE(bids[3].implied_size() == 0); + REQUIRE(asks[3].direct_size() == 1); + REQUIRE(asks[3].implied_size() == 2); REQUIRE(bids[4].mantissa() == 0); REQUIRE(bids[4].size() == 0); REQUIRE(asks[4].mantissa() == 1400); REQUIRE(asks[4].size() == 2); + REQUIRE(bids[4].direct_size() == 0); + REQUIRE(bids[4].implied_size() == 0); + REQUIRE(asks[4].direct_size() == 0); + REQUIRE(asks[4].implied_size() == 2); REQUIRE(canary == 0); } diff --git a/src/lib/md/test/synthetic_book_merge_tests.cpp b/src/lib/md/test/synthetic_book_merge_tests.cpp index d06e45d73..759b7a19a 100644 --- a/src/lib/md/test/synthetic_book_merge_tests.cpp +++ b/src/lib/md/test/synthetic_book_merge_tests.cpp @@ -35,8 +35,8 @@ void setup_market(MDProducerHandle producer, const vector& bids, const vecto auto ts = TimeStamp::now(); std::array bids_a = { }, asks_a = { }; - std::transform(bids.begin(), bids.end(), bids_a.begin(), [](const PS& ps) { return PriceSize::from_double<7>(ps.price, ps.size); }); - std::transform(asks.begin(), asks.end(), asks_a.begin(), [](const PS& ps) { return PriceSize::from_double<7>(ps.price, ps.size); }); + std::transform(bids.begin(), bids.end(), bids_a.begin(), [](const PS& ps) { return PriceSize_from_double<7>(ps.price, ps.size); }); + std::transform(asks.begin(), asks.end(), asks_a.begin(), [](const PS& ps) { return PriceSize_from_double<7>(ps.price, ps.size); }); producer.update_direct(ts, bids_a, asks_a); } diff --git a/src/lib/orderman/orders/spreader_orders/tests/tests_common.h b/src/lib/orderman/orders/spreader_orders/tests/tests_common.h index a927df1b4..6139c6f99 100644 --- a/src/lib/orderman/orders/spreader_orders/tests/tests_common.h +++ b/src/lib/orderman/orders/spreader_orders/tests/tests_common.h @@ -256,7 +256,7 @@ void fill_book(bts::orderman::Book& book, bts::api::Side side){ bts::orderman::BookSide& bookside = book.side(side); for (size_t i = 0; i < book.n_levels(); i++) - bookside[i] = md::PriceSize::from_double<6>(prices[i], sizes[i]); + bookside[i] = md::PriceSize_from_double<6>(prices[i], sizes[i]); } void fill_book(bts::orderman::Book& book, double* bid_prices, @@ -266,8 +266,8 @@ void fill_book(bts::orderman::Book& book, bts::orderman::BookSide& bidside = book.side(api::Side::SIDE_BUY); bts::orderman::BookSide& askside = book.side(api::Side::SIDE_SELL); for (size_t i = 0; i < book.n_levels(); i++){ - bidside[i] = md::PriceSize::from_double<6>(bid_prices[i], bid_sizes[i]); - askside[i] = md::PriceSize::from_double<6>(ask_prices[i], ask_sizes[i]); + bidside[i] = md::PriceSize_from_double<6>(bid_prices[i], bid_sizes[i]); + askside[i] = md::PriceSize_from_double<6>(ask_prices[i], ask_sizes[i]); } } bts::orderman::Book get_book(SecurityKey key, @@ -280,11 +280,11 @@ bts::orderman::Book get_book(SecurityKey key, bool delete_arrays){ bts::orderman::BookSide bidside, askside; for (size_t i = 0; i < n_bid_levels; i++){ - md::PriceSize bid = md::PriceSize::from_double<6>(bid_prices[i], bid_sizes[i]); + md::PriceSize bid = md::PriceSize_from_double<6>(bid_prices[i], bid_sizes[i]); bidside[i] = bid; } for (size_t i = 0; i < n_ask_levels; i++){ - md::PriceSize ask = md::PriceSize::from_double<6>(ask_prices[i], ask_sizes[i]); + md::PriceSize ask = md::PriceSize_from_double<6>(ask_prices[i], ask_sizes[i]); askside[i] = ask; } if (delete_arrays){ diff --git a/src/sdk/example/example.cpp b/src/sdk/example/example.cpp index d38cb424c..28b2c5d61 100644 --- a/src/sdk/example/example.cpp +++ b/src/sdk/example/example.cpp @@ -42,30 +42,30 @@ void produce() // initialized bid side book data std::array bids = { - md::PriceSize::from_double<7> (1.0,10), - md::PriceSize::from_double<7> (2.0,20), - md::PriceSize::from_double<7> (3.0,30), - md::PriceSize::from_double<7> (4.0,40), - md::PriceSize::from_double<7> (5.0,50), - md::PriceSize::from_double<7> (6.0,60), - md::PriceSize::from_double<7> (7.0,70), - md::PriceSize::from_double<7> (8.0,80), - md::PriceSize::from_double<7> (9.0,90), - md::PriceSize::from_double<7> (10.0,100) + md::PriceSize_from_double<7> (1.0,10), + md::PriceSize_from_double<7> (2.0,20), + md::PriceSize_from_double<7> (3.0,30), + md::PriceSize_from_double<7> (4.0,40), + md::PriceSize_from_double<7> (5.0,50), + md::PriceSize_from_double<7> (6.0,60), + md::PriceSize_from_double<7> (7.0,70), + md::PriceSize_from_double<7> (8.0,80), + md::PriceSize_from_double<7> (9.0,90), + md::PriceSize_from_double<7> (10.0,100) }; // initialized ask side book data std::array asks = { - md::PriceSize::from_double<7> (1.0,10), - md::PriceSize::from_double<7> (2.0,20), - md::PriceSize::from_double<7> (3.0,30), - md::PriceSize::from_double<7> (4.0,40), - md::PriceSize::from_double<7> (5.0,50), - md::PriceSize::from_double<7> (6.0,60), - md::PriceSize::from_double<7> (7.0,70), - md::PriceSize::from_double<7> (8.0,80), - md::PriceSize::from_double<7> (9.0,90), - md::PriceSize::from_double<7> (10.0,100) + md::PriceSize_from_double<7> (1.0,10), + md::PriceSize_from_double<7> (2.0,20), + md::PriceSize_from_double<7> (3.0,30), + md::PriceSize_from_double<7> (4.0,40), + md::PriceSize_from_double<7> (5.0,50), + md::PriceSize_from_double<7> (6.0,60), + md::PriceSize_from_double<7> (7.0,70), + md::PriceSize_from_double<7> (8.0,80), + md::PriceSize_from_double<7> (9.0,90), + md::PriceSize_from_double<7> (10.0,100) }; // Update book - this call does not use locks so should not be called concurrently diff --git a/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp b/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp index 8d38f9301..9ea11d065 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp +++ b/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp @@ -462,7 +462,7 @@ void ExchangeSimApplication::update_md(std::string const &sec_desc) { : book.bids.end(); std::transform(book.bids.begin(), bids_end, bids_temp.begin(), [&](const auto &bs) { - return md::PriceSize::from_double<7>( + return md::PriceSize_from_double<7>( bs.first.value() * price_factor, bs.second.value()); }); auto asks_end = book.asks.size() > cmax @@ -470,7 +470,7 @@ void ExchangeSimApplication::update_md(std::string const &sec_desc) { : book.asks.end(); std::transform(book.asks.begin(), asks_end, asks_temp.begin(), [&](const auto &bs) { - return md::PriceSize::from_double<7>( + return md::PriceSize_from_double<7>( bs.first.value() * price_factor, bs.second.value()); }); handle.update_direct(utils::TimeStamp::now(), bids_temp, asks_temp); @@ -479,7 +479,7 @@ void ExchangeSimApplication::update_md(std::string const &sec_desc) { for (auto &t : new_trades) handle.add_trade( utils::TimeStamp::now(), - md::PriceSize::from_double<7>(t.price.value() * price_factor, + md::PriceSize_from_double<7>(t.price.value() * price_factor, t.quantity.value()), datetime::ll_now(), co2::AGGRESSOR_SIDE_NONE); diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h index b19aeddd9..30fde8349 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h @@ -378,10 +378,10 @@ void MDP3Listener::onBookUpdate(const onixs::Security& sec, const TBook& onixs_b } // NaN out the remaining depth for (size_t x = bids_size; x < MAX_DEPTH; ++x) { - cache_bids[x] = PriceSize::NO_MARKET; + cache_bids[x] = NO_MARKET; } for (size_t x = asks_size; x < MAX_DEPTH; ++x) { - cache_asks[x] = PriceSize::NO_MARKET; + cache_asks[x] = NO_MARKET; } } From 505b01cdcbea672dd24010a83bff9b154f6b522f Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Sun, 23 Sep 2018 20:14:40 -0400 Subject: [PATCH 067/203] Settlements hooked to the order infrastructure --- .../orderman/actors/MDSupplementarySource.cpp | 72 +++++++++++++++++++ .../orderman/actors/MDSupplementarySource.h | 30 ++++++++ .../actors/order_manager/OrderServices.h | 5 +- .../actors/order_manager/OrderServicesData.h | 2 + .../orderman/events/SettlementUpdateEvent.h | 26 +++++++ .../orderman/server/OrderServiceServer.cpp | 2 + 6 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/lib/orderman/actors/MDSupplementarySource.cpp create mode 100644 src/lib/orderman/actors/MDSupplementarySource.h create mode 100644 src/lib/orderman/events/SettlementUpdateEvent.h diff --git a/src/lib/orderman/actors/MDSupplementarySource.cpp b/src/lib/orderman/actors/MDSupplementarySource.cpp new file mode 100644 index 000000000..907f4babd --- /dev/null +++ b/src/lib/orderman/actors/MDSupplementarySource.cpp @@ -0,0 +1,72 @@ +#include "MDSupplementarySource.h" +#include "lib/orderman/events/SettlementUpdateEvent.h" +#include "lib/logging/logging.h" +#include "lib/md/MDConsumerHandle.h" +#include "lib/md/MDConsumer.h" +#include "lib/orderman/events/MDRequestEvent.h" +#include "lib/md/MDConsumer.h" +#include "lib/os/os.h" + +namespace bts { +namespace orderman { + +struct HandleRecord { + md::MDConsumerHandle handle; + md::Price settlement_price; +}; + +struct MDSupplementarySource::PImpl +{ + PImpl(md::MDConsumer& md_consumer) + : + md_consumer(md_consumer), + handle_map(), + settlement_update(){ + } + md::MDConsumer md_consumer; + // list of sec subscriptions vs their handles + std::map handle_map; + mercury::Stream settlement_update; +}; + +MDSupplementarySource::MDSupplementarySource(md::MDConsumer& md_consumer) +: +me(new PImpl(md_consumer)){ +} + +MDSupplementarySource::~MDSupplementarySource(){ +} + +void MDSupplementarySource::init(){ + me->settlement_update=make_stream(); + subscribe([this](auto const &update) { + if (update.events) { + for (auto const &ev : *update.events) { + auto k = ev.security_key.value(); + if(me->handle_map.find(k)==me->handle_map.end()){ + LOG(OrderMan, Info, "MDSupplementarySource::init key:{}", k); + me->handle_map[k]={me->md_consumer.get_handle(k),os::nan()}; + } + } + } + }); +} + +void MDSupplementarySource::on_poll(){ + for(auto& kvp : me->handle_map){ + SecurityKey key(kvp.first); + HandleRecord& hr=kvp.second; + const md::MiscData& misc_data=hr.handle.misc(); + if(!os::is_nan(misc_data.settlement_price) && + misc_data.settlement_price!=hr.settlement_price){ + LOG(OrderMan, Info, + "MDSupplementarySource::on_poll new settle key:{} price:{}", + key, hr.settlement_price); + hr.settlement_price=misc_data.settlement_price; + SettlementUpdateEvent ev(misc_data.settlement_price,key); + publish(me->settlement_update,ev); + } + } +} + +}} \ No newline at end of file diff --git a/src/lib/orderman/actors/MDSupplementarySource.h b/src/lib/orderman/actors/MDSupplementarySource.h new file mode 100644 index 000000000..bbc5b17bf --- /dev/null +++ b/src/lib/orderman/actors/MDSupplementarySource.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include "lib/mercury/actors/Actor.h" +#include "lib/mercury/mercury_utils/IPollableActor.h" + +namespace bts { +namespace md {class MDConsumer;} +namespace orderman { + +// provides non hot-path market data such as settles, daily high-low etc +// (only settles for now) +// like MDSource, watches for MDRequestEvent's and hence constructs handles +// for requested contracts only. these handles are then polled and settles are +// published if changed. +class MDSupplementarySource : public mercury::Actor, public mercury::IPollableActor +{ +public: + MDSupplementarySource(md::MDConsumer& md_consumer); + ~MDSupplementarySource(); + + void init() override; + void on_poll() override; +private: + struct PImpl; + std::unique_ptr me; +}; + +}} + diff --git a/src/lib/orderman/actors/order_manager/OrderServices.h b/src/lib/orderman/actors/order_manager/OrderServices.h index 8d04272a1..01c0adf7e 100644 --- a/src/lib/orderman/actors/order_manager/OrderServices.h +++ b/src/lib/orderman/actors/order_manager/OrderServices.h @@ -64,7 +64,9 @@ public: class OrderServices { public: OrderServices(Order &owner, OrderServicesData &data) - : book_updates(*data.md_subs), child_updates(*data.child_order_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), @@ -75,6 +77,7 @@ class OrderServices { } EventStream book_updates; + EventStream settlement_updates; EventStream child_updates; EventStream time_change; diff --git a/src/lib/orderman/actors/order_manager/OrderServicesData.h b/src/lib/orderman/actors/order_manager/OrderServicesData.h index b16511ba0..e3cc99d11 100644 --- a/src/lib/orderman/actors/order_manager/OrderServicesData.h +++ b/src/lib/orderman/actors/order_manager/OrderServicesData.h @@ -11,6 +11,7 @@ #include "lib/mercury/events/TimerEvent.h" #include "lib/orderman/events/ActivateOrderEvent.h" #include "lib/orderman/events/BookUpdateEvent.h" +#include "lib/orderman/events/SettlementUpdateEvent.h" #include "lib/orderman/events/CancelOrderEvent.h" #include "lib/orderman/events/CancelRejectEvent.h" #include "lib/orderman/events/CrackOrderEvent.h" @@ -75,6 +76,7 @@ class OrderServicesData { OrderFactory order_factory; std::unique_ptr> md_subs; + std::unique_ptr> settlement_subs; std::unique_ptr< mercury::ContentSubscription> child_order_subs; diff --git a/src/lib/orderman/events/SettlementUpdateEvent.h b/src/lib/orderman/events/SettlementUpdateEvent.h new file mode 100644 index 000000000..59421d56f --- /dev/null +++ b/src/lib/orderman/events/SettlementUpdateEvent.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "lib/mercury/events/Event.h" +#include "lib/orderman/orderman_utils/Book.h" +#include "lib/orderman/orderman_utils/orderman_common.h" +#include "lib/mercury/synopsis/MergeByKey.h" + +namespace bts { +namespace orderman { + +class SettlementUpdateEvent : public mercury::Event { +public: + using SynopsisPolicy = mercury::MergeByKey; + md::Price settlement_price; + SecurityKey security_key; + SecurityKey key() const { return security_key; } + + SettlementUpdateEvent(const md::Price& price, + const SecurityKey& key) + : + settlement_price(price), + security_key(key) {} +}; +}} diff --git a/src/lib/orderman/server/OrderServiceServer.cpp b/src/lib/orderman/server/OrderServiceServer.cpp index 346250023..c9db06f03 100644 --- a/src/lib/orderman/server/OrderServiceServer.cpp +++ b/src/lib/orderman/server/OrderServiceServer.cpp @@ -9,6 +9,7 @@ #include "lib/mercury/mercury_utils/Executor.h" #include "lib/metrics/metrics.h" #include "lib/orderman/actors/AsyncFactory.h" +#include "lib/orderman/actors/MDSupplementarySource.h" #include "lib/orderman/actors/ExecutionGateway.h" #include "lib/orderman/actors/FillStore.h" #include "lib/orderman/actors/MDSource.h" @@ -106,6 +107,7 @@ OrderServiceServer::OrderServiceServer( me->slow_exe.activate(me->service_bridge); me->slow_exe.spawn(); me->slow_exe.spawn(); + me->slow_exe.spawn(me->md_consumer); me->slow_exe.spawn(); me->slow_exe.spawn(); me->slow_exe.spawn(me->md_consumer); From 4d00be1ab51ff55deeeb952392838bd3483284a6 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 24 Sep 2018 11:22:48 -0500 Subject: [PATCH 068/203] fix build error; fix Columns VerticalAlgnment so that the expected behavior is followed when collapsed --- .../Modules/Market/MarketView/MarketViewWindowFactory.cs | 4 ++-- .../Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs index 19d3f0b6a..5c6a194e8 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs @@ -64,12 +64,12 @@ namespace bts.shell { this.security_criteria = security_criteria; this.filters = filters; this.grid = grid; - _sorting_criterion = sorting_criterion; + _sorting_criteria = sorting_criterion; } public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion); - public IReadOnlyList sorting_criterion => _sorting_criterion ?? (_sorting_criterion = new List()); + public IReadOnlyList sorting_criterion => _sorting_criteria ?? (_sorting_criteria = new List()); public static StartupData Default(SecurityCriteria security_criteria) => new StartupData(security_criteria, new FilterStates(), DefaultGridSettings); diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml index b1ff52313..90467ae8c 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml @@ -101,12 +101,14 @@ Margin="8" DataContext="{Binding default_vm}" Visibility="{Binding DataContext.default_tab_selected, RelativeSource={RelativeSource AncestorType=usp:SimpleGridSettingsDialog}, Converter={StaticResource DefaultTabVisibilityConverter}}" + VerticalAlignment="Top" /> Date: Mon, 24 Sep 2018 12:54:52 -0400 Subject: [PATCH 069/203] SPARK-1659 Refactor to use IntPrice everywhere to reduce logic duplication --- src/lib/md/MDConsumerHandle.h | 6 +- src/lib/md/PriceSize.h | 83 +++++------- src/lib/md/Shm/MDStructure.h | 5 +- src/lib/md/relay/RelayClient.cpp | 4 +- src/lib/md/test/md2_tests.cpp | 126 +++++++++--------- .../sparkd/exchange_sim/ExchangeSim.cpp | 4 +- .../mdp3_onixs/MDP3ListenerHelpers.h | 2 +- 7 files changed, 104 insertions(+), 126 deletions(-) diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 96aae64b6..54aa61e55 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -62,8 +62,7 @@ inline void read_combined( // Compare bids if (direct.price() == implied.price()) { bid[x] = PriceSize( - direct.mantissa(), - direct.exponent(), + direct.int_price(), direct.size() + implied.size(), direct.size()); xdb += 1; @@ -92,8 +91,7 @@ inline void read_combined( // Compare asks if (direct.price() == implied.price()) { ask[x] = PriceSize( - direct.mantissa(), - direct.exponent(), + direct.int_price(), direct.size() + implied.size(), direct.size()); xda += 1; diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 40a651d9a..3b39ad17b 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -88,13 +88,13 @@ class PriceSize { uint32_t _direct_size; public: - PriceSize(int64_t mantissa, int8_t exponent, Size size) : - _price(mantissa, exponent), + PriceSize(IntPrice price, Size size) : + _price(price), _size(size), _direct_size(size) { } - PriceSize(int64_t mantissa, int8_t exponent, Size size, Size direct_size) : - _price(mantissa, exponent), + PriceSize(IntPrice price, Size size, Size direct_size) : + _price(price), _size(size), _direct_size(direct_size) { } @@ -134,9 +134,9 @@ PriceSize PriceSize_from_double(double price, Size size) { const double rounding = 0.5 * pow10(-PRECISION); price += (price > 0) ? rounding : -rounding; price *= pow10(PRECISION); - return PriceSize((int64_t)price, -PRECISION, size); + return PriceSize({(int64_t)price, -PRECISION}, size); } - return PriceSize((int64_t)price, 0, size); + return PriceSize({(int64_t)price, 0}, size); } @@ -147,51 +147,34 @@ typedef utils::Optimistic AtomicPriceSize; // This structure can store the same data in 24 bytes so it's more efficient for shared memory struct PriceLevel { PriceLevel() : - _mantissa_b(0), _exponent_b(0), - _mantissa_a(0), _exponent_a(0), - _size_b(0), _size_a(0) {} + _bid(), _ask(), + _bid_size(0), _ask_size(0) {} PriceLevel(const PriceSize& bid, const PriceSize& ask): - _mantissa_b(bid.mantissa()), _exponent_b(bid.exponent()), - _mantissa_a(ask.mantissa()), _exponent_a(ask.exponent()), - _size_b(bid.size()), _size_a(ask.size()) + _bid(bid.int_price()), _ask(ask.int_price()), + _bid_size(bid.size()), _ask_size(ask.size()) {} -#ifdef _WIN32 - int64_t _mantissa_b : 56; - int64_t _exponent_b : 8; - int64_t _mantissa_a : 56; - int64_t _exponent_a : 8; -#else - int64_t _mantissa_b : 56; - int8_t _exponent_b : 8; - int64_t _mantissa_a : 56; - int8_t _exponent_a : 8; -#endif - uint32_t _size_b; - uint32_t _size_a; + IntPrice _bid; + IntPrice _ask; + uint32_t _bid_size; + uint32_t _ask_size; - uint64_t bid_mantissa() const { return _mantissa_b; } - uint64_t ask_mantissa() const { return _mantissa_a; } - int8_t bid_exponent() const { return _exponent_b; } - int8_t ask_exponent() const { return _exponent_a; } - uint32_t bid_size() const { return _size_b; } - uint32_t ask_size() const { return _size_a; } + const IntPrice& bid_price() const { return _bid; } + const IntPrice& ask_price() const { return _ask; } + uint32_t bid_size() const { return _bid_size; } + uint32_t ask_size() const { return _ask_size; } bool operator==(const PriceLevel& other) const { - return _size_a == other._size_a && - _size_b == other._size_b && - _mantissa_a == other._mantissa_a && - _mantissa_b == other._mantissa_b && - _exponent_a == other._exponent_a && - _exponent_b == other._exponent_b; + return _ask_size == other._ask_size && + _bid_size == other._bid_size && + _bid == other._bid && + _ask == other._ask; } bool operator!=(const PriceLevel& other) const { - return _size_a != other._size_a || - _size_b != other._size_b || - _mantissa_a != other._mantissa_a || - _mantissa_b != other._mantissa_b || - _exponent_a != other._exponent_a || - _exponent_b != other._exponent_b; + return _ask_size != other._ask_size || + _bid_size != other._bid_size || + _bid != other._bid || + _ask != other._ask; } bool update(const PriceLevel& temp) { @@ -203,10 +186,10 @@ struct PriceLevel { } PriceSize bid() const { - return PriceSize(_mantissa_b, _exponent_b, _size_b); + return PriceSize(_bid, _bid_size); } PriceSize ask() const { - return PriceSize(_mantissa_a, _exponent_a, _size_a); + return PriceSize(_ask, _ask_size); } }; static_assert(sizeof(PriceLevel) == 24, "PriceLevel should be small"); @@ -215,20 +198,18 @@ struct ImpliedPriceLevel : private PriceLevel { ImpliedPriceLevel() : PriceLevel() {} ImpliedPriceLevel(const PriceSize& bid, const PriceSize& ask): PriceLevel(bid,ask) {} - using PriceLevel::bid_mantissa; - using PriceLevel::ask_mantissa; - using PriceLevel::bid_exponent; - using PriceLevel::ask_exponent; + using PriceLevel::bid_price; + using PriceLevel::ask_price; using PriceLevel::bid_size; using PriceLevel::ask_size; using PriceLevel::operator==; using PriceLevel::operator!=; bool operator=(const PriceLevel& other) { return update(other); } PriceSize bid() const { - return PriceSize(_mantissa_b, _exponent_b, _size_b, 0); + return PriceSize(_bid, _bid_size, 0); } PriceSize ask() const { - return PriceSize(_mantissa_a, _exponent_a, _size_a, 0); + return PriceSize(_ask, _ask_size, 0); } }; static_assert(sizeof(ImpliedPriceLevel) == 24, "PriceLevel should be small"); diff --git a/src/lib/md/Shm/MDStructure.h b/src/lib/md/Shm/MDStructure.h index 3b276a0cf..847e4dbf2 100644 --- a/src/lib/md/Shm/MDStructure.h +++ b/src/lib/md/Shm/MDStructure.h @@ -76,9 +76,8 @@ class HotDepthEntry { *ask = direct[depth].ask(); } void read_implied(uint32_t depth, PriceSize* bid, PriceSize* ask) const { - const auto& d = implied[depth]; - *bid = PriceSize(d.bid_mantissa(), d.bid_exponent(), d.bid_size(), 0); - *ask = PriceSize(d.ask_mantissa(), d.ask_exponent(), d.ask_size(), 0); + *bid = implied[depth].bid(); + *ask = implied[depth].ask(); } bool write_direct(uint32_t depth, const PriceSize& bid, const PriceSize& ask) { bts_assert_d(depth < direct.size()); diff --git a/src/lib/md/relay/RelayClient.cpp b/src/lib/md/relay/RelayClient.cpp index 629d82348..63a63541e 100644 --- a/src/lib/md/relay/RelayClient.cpp +++ b/src/lib/md/relay/RelayClient.cpp @@ -32,11 +32,11 @@ namespace md { using namespace co2; PriceSize convert(const PriceSizeMessage& psm) { - return psm.size() > 0 ? PriceSize(psm.mantissa(), psm.exponent(), psm.size()) : NO_MARKET; + return psm.size() > 0 ? PriceSize({psm.mantissa(), (int8_t)psm.exponent()}, psm.size()) : NO_MARKET; } PriceSize trade_pricesize(const co2::TradeMessage& trade) { return trade.size() > 0 ? - PriceSize(trade.mantissa(), trade.exponent(), trade.size()) : NO_MARKET; + PriceSize({trade.mantissa(), (int8_t)trade.exponent()}, trade.size()) : NO_MARKET; } PriceSize book_at(const google::protobuf::RepeatedPtrField& side, int index) { if (side.size() > index) { diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index ea1bd99c3..01642403c 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -223,7 +223,7 @@ public: REQUIRE(p2.size() == 6); } - p = PriceSize(4, -1, 2); + p = PriceSize({4, -1}, 2); memory = PriceLevel(p,p); for (auto p2 : { memory.bid(), memory.ask() }) { REQUIRE(p2.price() == 0.4); @@ -616,7 +616,7 @@ public: assign_ids(producers.begin(), producers.end()); producer1->get_channel("FOO", SECURITYTYPE_FUTURE, VENUE_BTS).heartbeat(); producer1->get_channel("FOOX", SECURITYTYPE_OPTION, VENUE_BTS).heartbeat(); - for (auto& pro : producers) { pro.update_bbo(TimeStamp::now(), PriceSize(1, 0, 1), PriceSize(2, 0, 2)); } + for (auto& pro : producers) { pro.update_bbo(TimeStamp::now(), PriceSize({1, 0}, 1), PriceSize({2, 0}, 2)); } // Symbol list for the consumer MDRecoverableConsumer consumer(get_test_symbols(), nullptr); @@ -678,7 +678,7 @@ public: // set bbo to a different value for (auto& pro : producers) { - pro.update_bbo(TimeStamp::now(), PriceSize(4, 0, 5), PriceSize(6, 0, 7)); + pro.update_bbo(TimeStamp::now(), PriceSize({4, 0}, 5), PriceSize({6, 0}, 7)); } // Attempt to recover the managed handles @@ -1673,21 +1673,21 @@ TEST_CASE("MergeImpliedTests", "[md]") { } SECTION("partial_Read") { - direct_bid[0] = { 700, 1, 1 }; - direct_bid[1] = { 600, 1, 1 }; - direct_bid[2] = { 500, 1, 1 }; - direct_bid[3] = { 400, 1, 1 }; + direct_bid[0] = { {700, 1}, 1 }; + direct_bid[1] = { {600, 1}, 1 }; + direct_bid[2] = { {500, 1}, 1 }; + direct_bid[3] = { {400, 1}, 1 }; - direct_ask[0] = { 800, 1, 1 }; - direct_ask[1] = { 900, 1, 1 }; - direct_ask[2] = { 1000, 1, 1 }; - direct_ask[3] = { 1100, 1, 1 }; + direct_ask[0] = { {800, 1}, 1 }; + direct_ask[1] = { {900, 1}, 1 }; + direct_ask[2] = { {1000, 1}, 1 }; + direct_ask[3] = { {1100, 1}, 1 }; - implied_bid[0] = { 700, 1, 2 }; - implied_bid[1] = { 600, 1, 2 }; + implied_bid[0] = { {700, 1}, 2 }; + implied_bid[1] = { {600, 1}, 2 }; - implied_ask[0] = { 800, 1, 2 }; - implied_ask[1] = { 900, 1, 2 }; + implied_ask[0] = { {800, 1}, 2 }; + implied_ask[1] = { {900, 1}, 2 }; ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [](const vector& consumers) { @@ -1742,21 +1742,21 @@ TEST_CASE("MergeImpliedTests", "[md]") { } SECTION("better_implied") { - direct_bid[0] = { 500, 1, 1 }; - direct_bid[1] = { 400, 1, 1 }; - direct_bid[2] = { 300, 1, 1 }; - direct_bid[3] = { 200, 1, 1 }; + direct_bid[0] = { {500, 1}, 1 }; + direct_bid[1] = { {400, 1}, 1 }; + direct_bid[2] = { {300, 1}, 1 }; + direct_bid[3] = { {200, 1}, 1 }; - direct_ask[0] = { 1000, 1, 1 }; - direct_ask[1] = { 1100, 1, 1 }; - direct_ask[2] = { 1200, 1, 1 }; - direct_ask[3] = { 1300, 1, 1 }; + direct_ask[0] = { {1000, 1}, 1 }; + direct_ask[1] = { {1100, 1}, 1 }; + direct_ask[2] = { {1200, 1}, 1 }; + direct_ask[3] = { {1300, 1}, 1 }; - implied_bid[0] = { 600, 1, 2 }; - implied_bid[1] = { 500, 1, 2 }; + implied_bid[0] = { {600, 1}, 2 }; + implied_bid[1] = { {500, 1}, 2 }; - implied_ask[0] = { 900, 1, 2 }; - implied_ask[1] = { 1000, 1, 2 }; + implied_ask[0] = { {900, 1}, 2 }; + implied_ask[1] = { {1000, 1}, 2 }; ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { @@ -1811,21 +1811,21 @@ TEST_CASE("MergeImpliedTests", "[md]") { } SECTION("worse_implied") { - direct_bid[0] = { 500, 1, 1 }; - direct_bid[1] = { 400, 1, 1 }; - direct_bid[2] = { 300, 1, 1 }; - direct_bid[3] = { 200, 1, 1 }; + direct_bid[0] = { {500, 1}, 1 }; + direct_bid[1] = { {400, 1}, 1 }; + direct_bid[2] = { {300, 1}, 1 }; + direct_bid[3] = { {200, 1}, 1 }; - direct_ask[0] = { 1000, 1, 1 }; - direct_ask[1] = { 1100, 1, 1 }; - direct_ask[2] = { 1200, 1, 1 }; - direct_ask[3] = { 1300, 1, 1 }; + direct_ask[0] = { {1000, 1}, 1 }; + direct_ask[1] = { {1100, 1}, 1 }; + direct_ask[2] = { {1200, 1}, 1 }; + direct_ask[3] = { {1300, 1}, 1 }; - implied_bid[0] = { 400, 1, 2 }; - implied_bid[1] = { 300, 1, 2 }; + implied_bid[0] = { {400, 1}, 2 }; + implied_bid[1] = { {300, 1}, 2 }; - implied_ask[0] = { 1100, 1, 2 }; - implied_ask[1] = { 1200, 1, 2 }; + implied_ask[0] = { {1100, 1}, 2 }; + implied_ask[1] = { {1200, 1}, 2 }; ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { @@ -1898,21 +1898,21 @@ TEST_CASE("MergeImpliedTests", "[md]") { } SECTION("worse_implied_bid") { - direct_bid[0] = { 500, 1, 1 }; - direct_bid[1] = { 400, 1, 1 }; - direct_bid[2] = { 300, 1, 1 }; - direct_bid[3] = { 200, 1, 1 }; + direct_bid[0] = { {500, 1}, 1 }; + direct_bid[1] = { {400, 1}, 1 }; + direct_bid[2] = { {300, 1}, 1 }; + direct_bid[3] = { {200, 1}, 1 }; - direct_ask[0] = { 1000, 1, 1 }; - direct_ask[1] = { 1100, 1, 1 }; - direct_ask[2] = { 1200, 1, 1 }; - direct_ask[3] = { 1300, 1, 1 }; + direct_ask[0] = { {1000, 1}, 1 }; + direct_ask[1] = { {1100, 1}, 1 }; + direct_ask[2] = { {1200, 1}, 1 }; + direct_ask[3] = { {1300, 1}, 1 }; - implied_bid[0] = { 400, 1, 2 }; - implied_bid[1] = { 300, 1, 2 }; + implied_bid[0] = { {400, 1}, 2 }; + implied_bid[1] = { {300, 1}, 2 }; - implied_ask[0] = { 1000, 1, 2 }; - implied_ask[1] = { 1100, 1, 2 }; + implied_ask[0] = { {1000, 1}, 2 }; + implied_ask[1] = { {1100, 1}, 2 }; ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { @@ -1984,21 +1984,21 @@ TEST_CASE("MergeImpliedTests", "[md]") { }); } SECTION("worse_implied_ask") { - direct_bid[0] = { 500, 1, 1 }; - direct_bid[1] = { 400, 1, 1 }; - direct_bid[2] = { 300, 1, 1 }; - direct_bid[3] = { 200, 1, 1 }; + direct_bid[0] = { {500, 1}, 1 }; + direct_bid[1] = { {400, 1}, 1 }; + direct_bid[2] = { {300, 1}, 1 }; + direct_bid[3] = { {200, 1}, 1 }; - direct_ask[0] = { 1000, 1, 1 }; - direct_ask[1] = { 1100, 1, 1 }; - direct_ask[2] = { 1200, 1, 1 }; - direct_ask[3] = { 1300, 1, 1 }; + direct_ask[0] = { {1000, 1}, 1 }; + direct_ask[1] = { {1100, 1}, 1 }; + direct_ask[2] = { {1200, 1}, 1 }; + direct_ask[3] = { {1300, 1}, 1 }; - implied_bid[0] = { 500, 1, 2 }; - implied_bid[1] = { 400, 1, 2 }; + implied_bid[0] = { {500, 1}, 2 }; + implied_bid[1] = { {400, 1}, 2 }; - implied_ask[0] = { 1300, 1, 2 }; - implied_ask[1] = { 1400, 1, 2 }; + implied_ask[0] = { {1300, 1}, 2 }; + implied_ask[1] = { {1400, 1}, 2 }; ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { diff --git a/src/server/sparkd/exchange_sim/ExchangeSim.cpp b/src/server/sparkd/exchange_sim/ExchangeSim.cpp index c7b69910d..7eb6662fd 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSim.cpp +++ b/src/server/sparkd/exchange_sim/ExchangeSim.cpp @@ -70,11 +70,11 @@ struct MDPlayer : md::IRelayWriter size_t i = 0; for (auto &b : book.bids()) - bids[i++] = md::PriceSize(b.mantissa(),b.exponent(),b.size()); + bids[i++] = md::PriceSize({b.mantissa(),(int8_t)b.exponent()},b.size()); size_t n = 0; for (auto &a : book.asks()) - asks[n++] = md::PriceSize(a.mantissa(),a.exponent(),a.size()); + asks[n++] = md::PriceSize({a.mantissa(),(int8_t)a.exponent()},a.size()); obs.on_depth(*sec_ptr, bids, asks); } } diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h index 4c3972592..2d36ee542 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h @@ -12,7 +12,7 @@ namespace bts { namespace md { inline PriceSize pricesize_of_price_size(const onixs::Decimal price, int8_t display_factor, Size size) { - PriceSize result(price.mantissa(), price.exponent() + display_factor, size); + PriceSize result({price.mantissa(), (int8_t)(price.exponent() + display_factor)}, size); return result; } From 69d75292ee4f0a6d4cd060c297995c816beb81cd Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Mon, 24 Sep 2018 12:55:39 -0400 Subject: [PATCH 070/203] SPARK-1712 add synthetic leg parameter for implied/direct book --- src/lib/sparkproto/SparkProtos.proto | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/sparkproto/SparkProtos.proto b/src/lib/sparkproto/SparkProtos.proto index e30e01b2e..282ce3de9 100644 --- a/src/lib/sparkproto/SparkProtos.proto +++ b/src/lib/sparkproto/SparkProtos.proto @@ -28,8 +28,13 @@ message RulePriority int32 priority = 20; } + message LegSpreaderParams { + enum MarketDataType { + DIRECT = 0; + MERGED = 1; + } /// Legs can be quoted actively, or sent in reponse /// to fills bool is_active_quoting = 20; @@ -37,6 +42,7 @@ message LegSpreaderParams QuoteParams quote_params = 40; PreHedgeParams pre_hedge_params = 50; PostHedgeParams post_hedge_params = 60; + MarketDataType market_data_type = 70; } // /************************************************************************************************* From 3ffc130889c7f25a9a7df3e664c044d091b823c7 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Mon, 24 Sep 2018 13:38:42 -0400 Subject: [PATCH 071/203] SPARK-1711 Refactor PriceSize.cs --- .../Spark/Utilities/MDHandleExtensions.cs | 4 +- src/client/spark_core/Co2/AtomicPriceSize.cs | 42 ++++ src/client/spark_core/Co2/Co2NativeMethods.cs | 3 + src/client/spark_core/Co2/IntPrice.cs | 41 ++++ src/client/spark_core/Co2/MDConsumerHandle.cs | 7 +- .../spark_core/Co2/NativePriceHelpers.cs | 32 +++ src/client/spark_core/Co2/PriceSize.cs | 193 ++++-------------- .../spark_core/TestRunner/TestAssert.cs | 5 + src/client/spark_test/Co2/Co2Tests.cs | 16 +- src/lib/md/test/Co2ManagedTestHelper.cpp | 3 + 10 files changed, 179 insertions(+), 167 deletions(-) create mode 100644 src/client/spark_core/Co2/AtomicPriceSize.cs create mode 100644 src/client/spark_core/Co2/IntPrice.cs create mode 100644 src/client/spark_core/Co2/NativePriceHelpers.cs diff --git a/src/client/spark/view/Spark/Utilities/MDHandleExtensions.cs b/src/client/spark/view/Spark/Utilities/MDHandleExtensions.cs index 7b4c3a68d..379235c88 100644 --- a/src/client/spark/view/Spark/Utilities/MDHandleExtensions.cs +++ b/src/client/spark/view/Spark/Utilities/MDHandleExtensions.cs @@ -9,8 +9,8 @@ namespace mbts.co2 { public static double mid(this MDConsumerHandle handle) { ulong unused = 0; handle.get_bbo(ref unused, out NativePriceSize bid, out NativePriceSize ask); - if (bid == NativePriceSize.no_market) return ask.price; - if (ask == NativePriceSize.no_market) return bid.price; + if (bid == NativePriceSize.NO_MARKET) return ask.price; + if (ask == NativePriceSize.NO_MARKET) return bid.price; return (ask.price + bid.price) / 2; } diff --git a/src/client/spark_core/Co2/AtomicPriceSize.cs b/src/client/spark_core/Co2/AtomicPriceSize.cs new file mode 100644 index 000000000..2c6922602 --- /dev/null +++ b/src/client/spark_core/Co2/AtomicPriceSize.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading; + +namespace mbts.co2 { + struct TimeStamp { + Int64 _value; + public UInt64 time => (UInt64)Interlocked.Read(ref _value); + public static UInt64 invalid_value => UInt64.MaxValue; + } + + // This type cannot be generic because we will get error CS0208 + //internal struct Optimistic where T : struct {... + internal struct AtomicPriceSize { + TimeStamp _time; + NativePriceSize _value; + void store(NativePriceSize nps) { + _time = new TimeStamp(); + _value = new NativePriceSize(); + throw new NotImplementedException(); + } + public (UInt64 time, NativePriceSize value) load() { + UInt64 temp; + NativePriceSize result; + int attempts = 0; + do { + ++attempts; + temp = _time.time; + result = _value; + } while ((temp != _time.time || temp == TimeStamp.invalid_value) && attempts < 1619200); + return (temp, result); + } + public bool load_if_new(ref UInt64 time, out NativePriceSize t) { + if (_time.time > time || _time.time == TimeStamp.invalid_value) { + (time, t) = load(); + return (time != TimeStamp.invalid_value); + } + t = NativePriceSize.NO_MARKET; + return false; + } + } + +} diff --git a/src/client/spark_core/Co2/Co2NativeMethods.cs b/src/client/spark_core/Co2/Co2NativeMethods.cs index e32741403..294510034 100644 --- a/src/client/spark_core/Co2/Co2NativeMethods.cs +++ b/src/client/spark_core/Co2/Co2NativeMethods.cs @@ -113,5 +113,8 @@ namespace mbts.co2 [DllImport("libspark")] internal static extern void co2_get_zero_and_nan_book(ref NativePriceLevel nps); + [DllImport("libspark")] + internal static extern void co2_get_nan_price_size(out NativePriceSize ps); + } } diff --git a/src/client/spark_core/Co2/IntPrice.cs b/src/client/spark_core/Co2/IntPrice.cs new file mode 100644 index 000000000..c238df309 --- /dev/null +++ b/src/client/spark_core/Co2/IntPrice.cs @@ -0,0 +1,41 @@ +using System; +using System.Runtime.InteropServices; + +namespace mbts.co2 { + + [StructLayout(LayoutKind.Explicit)] + public readonly struct IntPrice : IEquatable { + [FieldOffset(0)] + readonly long _mantissa; + [FieldOffset(7)] + readonly sbyte _exponent; + + public IntPrice(long mantissa, sbyte exponent) { + _mantissa = mantissa; + _exponent = exponent; + } + public long mantissa => NativePriceHelpers.decode(_mantissa); + public sbyte exponent => _exponent; + public bool is_nan => _exponent == 127; + public double price() { + if (is_nan) return double.NaN; + return mantissa * NativePriceHelpers.pow10(exponent); + } + + public bool Equals(IntPrice other) { + return _exponent == other._exponent && _mantissa == other._mantissa; + } + public override bool Equals(object obj) { + return base.Equals(obj); + } + public override string ToString() { + return $"{price():0.00}"; + } + public override int GetHashCode() { + return _mantissa.GetHashCode(); + } + + public static IntPrice NaN = new IntPrice(0,127); + } + +} diff --git a/src/client/spark_core/Co2/MDConsumerHandle.cs b/src/client/spark_core/Co2/MDConsumerHandle.cs index 1b2f77b64..0a9cce2c4 100644 --- a/src/client/spark_core/Co2/MDConsumerHandle.cs +++ b/src/client/spark_core/Co2/MDConsumerHandle.cs @@ -47,15 +47,10 @@ namespace mbts.co2 time = updated; return true; } - bid = ask = NativePriceSize.no_market; + bid = ask = NativePriceSize.NO_MARKET; return false; } - public void get_depth(ref NativePriceSize[] bids, ref NativePriceSize[] asks) { - ulong unused = 0; - get_depth(ref unused, ref bids, ref asks); - } - public bool get_depth(ref ulong time, ref NativePriceSize[] bids, ref NativePriceSize[] asks) { if (!has_depth) { throw new InvalidOperationException("Cannot get depth data from a handle that does not offer depth!"); } var updated = market_timestamp; diff --git a/src/client/spark_core/Co2/NativePriceHelpers.cs b/src/client/spark_core/Co2/NativePriceHelpers.cs new file mode 100644 index 000000000..2c44a4d15 --- /dev/null +++ b/src/client/spark_core/Co2/NativePriceHelpers.cs @@ -0,0 +1,32 @@ +using bts; + +namespace mbts.co2 { + public static class NativePriceHelpers { + static double[] s_powers10 = { + 1E-20, 1E-19, 1E-18, 1E-17, 1E-16, 1E-15, 1E-14, 1E-13, 1E-12, 1E-11, 1E-10, 1E-9, 1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, + 1E0, + 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15, 1E16, 1E17, 1E18, 1E19, 1E20 + }; + static int s_max_abs_pow10 = s_powers10.Length/2; + + public static double pow10(int power) { + int offset = power + s_max_abs_pow10; + Log.assert_d(offset >= 0 && offset < s_powers10.Length); + if (offset >= 0 && offset < s_powers10.Length) { + return s_powers10[offset]; + } + return double.NaN; + } + internal static long decode(long m) { + long mantissa = m & 0xFFFFFFFFFFFFFF; + // If the 56th bit is set this is negative + const long bit56 = 0x80000000000000; + if ((mantissa & bit56) != 0) { + // Compute 2's compliment manually + mantissa = -1 * (((~mantissa) & (bit56 - 1)) + 1); + } + return mantissa; + } + } + +} diff --git a/src/client/spark_core/Co2/PriceSize.cs b/src/client/spark_core/Co2/PriceSize.cs index 8f1e14e9f..f32e7360f 100644 --- a/src/client/spark_core/Co2/PriceSize.cs +++ b/src/client/spark_core/Co2/PriceSize.cs @@ -1,106 +1,72 @@ using bts; using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -namespace mbts.co2 -{ +namespace mbts.co2 { - [StructLayout(LayoutKind.Explicit)] + [StructLayout(LayoutKind.Sequential)] public readonly struct NativePriceLevel { - [FieldOffset(0)] - readonly Int64 _mantissa_b; // : 56; - [FieldOffset(7)] - readonly sbyte _exponent_b; // : 8; - [FieldOffset(8)] - readonly Int64 _mantissa_a; // : 56; - [FieldOffset(15)] - readonly sbyte _exponent_a; //: 8; - [FieldOffset(16)] - readonly UInt32 _size_b; - [FieldOffset(20)] - readonly UInt32 _size_a; - - long decode(long m) { - long mantissa = m & 0xFFFFFFFFFFFFFF; - // If the 56th bit is set this is negative - const long bit56 = 0x80000000000000; - if ((mantissa & bit56) != 0) { - // Compute 2's compliment manually - mantissa = -1 * (((~mantissa) & (bit56 - 1)) + 1); - } - return mantissa; + readonly IntPrice _bid; + readonly IntPrice _ask; + readonly uint _bid_size; + readonly uint _ask_size; + public NativePriceLevel(IntPrice bid, uint bid_size, IntPrice ask, uint ask_size) { + _bid = bid; + _bid_size = bid_size; + _ask = ask; + _ask_size = ask_size; } - public Int64 bid_mantissa => decode(_mantissa_b); - public sbyte bid_exponent => _exponent_b; - public Int64 ask_mantissa => decode(_mantissa_a); - public sbyte ask_exponent => _exponent_a; - public UInt32 bid_size => _size_b; - public UInt32 ask_size => _size_a; + public NativePriceSize bid => new NativePriceSize(_bid, _bid_size); + public NativePriceSize ask => new NativePriceSize(_ask, _ask_size); - public NativePriceSize bid => new NativePriceSize(bid_mantissa, bid_exponent, bid_size); - public NativePriceSize ask => new NativePriceSize(ask_mantissa, ask_exponent, ask_size); + public static NativePriceLevel empty = new NativePriceLevel(IntPrice.NaN, 0, IntPrice.NaN, 0); } - public static class NativePriceSizeHelpers { - static double[] s_powers10 = { - 1E-20, 1E-19, 1E-18, 1E-17, 1E-16, 1E-15, 1E-14, 1E-13, 1E-12, 1E-11, 1E-10, 1E-9, 1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, - 1E0, - 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15, 1E16, 1E17, 1E18, 1E19, 1E20 - }; - static int s_max_abs_pow10 = s_powers10.Length/2; - - public static double pow10(int power) { - int offset = power + s_max_abs_pow10; - Log.assert_d(offset >= 0 && offset < s_powers10.Length); - if (offset >= 0 && offset < s_powers10.Length) { - return s_powers10[offset]; - } - return double.NaN; - } - } - - public readonly struct NativePriceSize : IEquatable { - public NativePriceSize(Int64 mantissa, sbyte exponent, uint size) { - this.mantissa = mantissa; - this.exponent = exponent; - this.size = size; + readonly IntPrice _price; + readonly uint _size; + + static NativePriceSize() { + Log.assert(Marshal.SizeOf() == 16); } - public static NativePriceSize no_market = new NativePriceSize(0, 0, 0); + public NativePriceSize(IntPrice price, uint size) { + _price = price; + _size = size; + } + public NativePriceSize(long mantissa, sbyte exponent, uint size) { + _price = new IntPrice(mantissa, exponent); + _size = size; + } - public Int64 mantissa { get; } - public sbyte exponent {get;} + public static NativePriceSize NO_MARKET = new NativePriceSize(IntPrice.NaN, 0); - public double price => (is_nan) ? double.NaN : mantissa * NativePriceSizeHelpers.pow10(exponent); + public IntPrice int_price => _price; + public long mantissa => _price.mantissa; + public sbyte exponent => _price.exponent; + public uint size => _size; + public double price => _price.price(); + public bool is_nan => _price.is_nan; - public bool is_nan => this == no_market; - - public uint size { get; } public override string ToString() { - return "{0:0.00} {1}".fmt(price, size); + return "{0} {1}".fmt(price, size); } public override bool Equals(object obj) { if (obj is NativePriceSize p) return Equals(p); return base.Equals(obj); } + public override int GetHashCode() { - return bts.utils.EqualityComparer.aggregate_hash(mantissa.GetHashCode(), exponent.GetHashCode(), size.GetHashCode()); + return bts.utils.EqualityComparer.aggregate_hash(_price.GetHashCode(), size.GetHashCode()); } public bool Equals(NativePriceSize other) { return size == other.size && - mantissa == other.mantissa && - exponent == other.exponent; + _price.Equals(other._price); } public static bool operator ==(NativePriceSize a, NativePriceSize b) { return a.Equals(b); @@ -109,86 +75,5 @@ namespace mbts.co2 return !a.Equals(b); } } - - struct TimeStamp { - Int64 _value; - public UInt64 time => (UInt64)Interlocked.Read(ref _value); - public static UInt64 invalid_value => UInt64.MaxValue; - } - - // This type cannot be generic because we will get error CS0208 - //internal struct Optimistic where T : struct {... - internal struct AtomicPriceSize { - TimeStamp _time; - NativePriceSize _value; - void store(NativePriceSize nps) { - _time = new TimeStamp(); - _value = new NativePriceSize(); - throw new NotImplementedException(); - } - public (UInt64 time, NativePriceSize value) load() { - UInt64 temp; - NativePriceSize result; - int attempts = 0; - do { - ++attempts; - temp = _time.time; - result = _value; - } while ((temp != _time.time || temp == TimeStamp.invalid_value) && attempts < 1619200); - return (temp, result); - } - public bool load_if_new(ref UInt64 time, out NativePriceSize t) { - if (_time.time > time || _time.time == TimeStamp.invalid_value) { - (time, t) = load(); - return (time != TimeStamp.invalid_value); - } - t = NativePriceSize.no_market; - return false; - } - } - - public struct PriceSize : IEquatable - { - public PriceSize(double price, uint size) - { - this.price = price; - this.size = size; - } - - public PriceSize(NativePriceSize nps) { - price = nps.price; - size = nps.size; - } - - public static implicit operator PriceSize(NativePriceSize nps) { - return new PriceSize(nps); - } - - public static PriceSize no_market = NativePriceSize.no_market; - - public double price { get; } - public uint size { get; } - - public override string ToString() { - return "{0:0.00} {1}".fmt(price, size); - } - - public override bool Equals(object obj) { - if (obj is PriceSize p) return Equals(p); - return base.Equals(obj); - } - public override int GetHashCode() { - return bts.utils.EqualityComparer.aggregate_hash(price.GetHashCode(), size.GetHashCode()); - } - - public bool Equals(PriceSize other) { - return price == other.price && size == other.size; - } - public static bool operator ==(PriceSize a, PriceSize b) { - return a.Equals(b); - } - public static bool operator !=(PriceSize a, PriceSize b) { - return !a.Equals(b); - } - } + } diff --git a/src/client/spark_core/TestRunner/TestAssert.cs b/src/client/spark_core/TestRunner/TestAssert.cs index 9351f1d79..5bc09ae02 100644 --- a/src/client/spark_core/TestRunner/TestAssert.cs +++ b/src/client/spark_core/TestRunner/TestAssert.cs @@ -29,6 +29,11 @@ namespace bts.test { throw new AssertFailture($"Expected: 'false' got 'true'. {msg}", file, line); } } + public static void IsNaN(double value, string msg = "", [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) { + if (!double.IsNaN(value)) { + throw new AssertFailture($"Expected: NaN got {value}", file, line); + } + } public static void AreEqual(T a, T b, string msg="", [CallerFilePath] string file="", [CallerLineNumber] int line=0) { if (!a.Equals(b)) { throw new AssertFailture( diff --git a/src/client/spark_test/Co2/Co2Tests.cs b/src/client/spark_test/Co2/Co2Tests.cs index 4d852861f..e9dce8cf3 100644 --- a/src/client/spark_test/Co2/Co2Tests.cs +++ b/src/client/spark_test/Co2/Co2Tests.cs @@ -43,9 +43,15 @@ namespace mbts.co2.tests Assert.AreEqual(ps.price, 2030); Assert.IsTrue(ps.size == 1); - ps = NativePriceSize.no_market; + ps = NativePriceSize.NO_MARKET; Assert.IsTrue(double.IsNaN(ps.price)); Assert.IsTrue(ps.size == 0); + + ps = new NativePriceSize(100, 100, 100); + Co2NativeTestMethods.co2_get_nan_price_size(out ps); + Assert.IsTrue(double.IsNaN(ps.price)); + Assert.AreEqual(ps.size, 0u); + Assert.AreEqual(ps.exponent, NativePriceSize.NO_MARKET.exponent); } [Test] @@ -237,18 +243,18 @@ namespace mbts.co2.tests [Test] public void price_size_nan() { - NativePriceLevel npl = new NativePriceLevel(); + NativePriceLevel npl = NativePriceLevel.empty; Assert.IsTrue(npl.bid.is_nan); - Assert.IsTrue(double.IsNaN(npl.bid.price)); + Assert.IsNaN(npl.bid.price); Assert.IsTrue(npl.ask.is_nan); - Assert.IsTrue(double.IsNaN(npl.ask.price)); + Assert.IsNaN(npl.ask.price); Co2NativeTestMethods.co2_get_zero_and_nan_book(ref npl); Assert.AreEqual(npl.ask.price, 0); Assert.AreEqual(npl.ask.size, 1u); Assert.AreEqual(npl.bid.size, 0u); Assert.IsTrue(npl.bid.is_nan); - Assert.IsTrue(double.IsNaN(npl.bid.price)); + Assert.IsNaN(npl.bid.price); } diff --git a/src/lib/md/test/Co2ManagedTestHelper.cpp b/src/lib/md/test/Co2ManagedTestHelper.cpp index 30eb8c243..10d58ea23 100644 --- a/src/lib/md/test/Co2ManagedTestHelper.cpp +++ b/src/lib/md/test/Co2ManagedTestHelper.cpp @@ -140,6 +140,9 @@ extern "C" BTS_EXPORT void co2_destroy_test_md(MDProducer* producer) { extern "C" BTS_EXPORT void co2_get_zero_and_nan_book(PriceLevel& price) { price = { NO_MARKET, PriceSize_from_double<7>(0,1) }; } +extern "C" BTS_EXPORT void co2_get_nan_price_size(PriceSize& ps) { + ps = NO_MARKET; +} }}}//namespace From 0ff7adf73b2b4563356d54eb867936095ee7febc Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 24 Sep 2018 15:13:41 -0500 Subject: [PATCH 072/203] SimpleGridSettingsManager now reflects settings changes immediately after they are made --- .../Modules/Market/MarketView/MarketViewVM.cs | 4 ++-- .../Modules/Market/TimeAndSales/TimeAndSalesVM.cs | 4 ++-- .../DIYGrid/Settings/SimpleGridSettingsManager.cs | 15 ++++++++++----- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs index 0c2359009..bc7e580b2 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs @@ -104,7 +104,7 @@ namespace mbts.spark { { Columns.WorkingAsks.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }, { Columns.WorkingBids.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor } }; - grid_settings_manager = new SimpleGridSettingsManager(columns, col_settings_flags); + grid_settings_manager = new SimpleGridSettingsManager(columns, col_settings_flags, row_vms); // this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.) grid_settings_manager.load_from(UserSettings.factory_defaults.market_view_grid_defaults); // @@ -148,7 +148,7 @@ namespace mbts.spark { public ObservableCollection columns { get; } = new ObservableCollection(); - public SimpleGridSettingsManager grid_settings_manager { get; } + public SimpleGridSettingsManager grid_settings_manager { get; } #region Filters public IDataGridColumnPersistentFilter security_filter { get; } diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs index 61d6f3f80..8c2bfd1c0 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs @@ -58,7 +58,7 @@ namespace mbts.spark { // be sure to load GridSettings AFTER the columns collection has been populated, the grid settings manager looks columns by id to apply the appropriate settings, // if the collection is not populated, the column settings will not be applied - grid_settings_manager = new SimpleGridSettingsManager(columns); + grid_settings_manager = new SimpleGridSettingsManager(columns); grid_settings_manager.load_from(startup_data.grid); _resources.AddRange( show_ladder, @@ -71,7 +71,7 @@ namespace mbts.spark { public ObservableCollection columns { get; } = new ObservableCollection(); public DelegateCommandParamObserver show_ladder { get; } - public SimpleGridSettingsManager grid_settings_manager { get; } + public SimpleGridSettingsManager grid_settings_manager { get; } public StartupData create() => new StartupData(_model.security_criteria, _get_filter_states(), grid_settings_manager.create(), rows_view.get_sorting_criteria(columns)); diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs index 4dd73a1c0..3896b258f 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs @@ -15,8 +15,9 @@ using System.Windows.Media; using static mbts.ui.controls.DIYGrid; namespace mbts.ui { - class SimpleGridSettingsManager : ViewModelBase, IObserver { + class SimpleGridSettingsManager : ViewModelBase, IObserver { readonly ObservableCollection _columns; + readonly IReadOnlyCollection _row_vms; readonly Property _grid_settings; readonly DelegateProperty _font_family; readonly DelegateProperty _font_weight; @@ -25,9 +26,9 @@ namespace mbts.ui { readonly IReadOnlyDictionary _column_flags; public SimpleGridSettingsManager(ObservableCollection columns) - : this(columns, new Dictionary()) { } + : this(columns, new Dictionary(), null) { } - public SimpleGridSettingsManager(ObservableCollection columns, IReadOnlyDictionary column_flags) { + public SimpleGridSettingsManager(ObservableCollection columns, IReadOnlyDictionary column_flags, IReadOnlyCollection row_vms) { _columns = columns; _column_flags = column_flags.ToDictionary(kv => kv.Key, kv => ImmutableColumnSettingStore.create(kv.Value)); _grid_settings = backing(nameof(grid_settings), GridSettings.Default); @@ -36,6 +37,7 @@ namespace mbts.ui { _font_size = backing(nameof(font_size), () => grid_settings.font_size, nameof(font_size)); _font_color = backing(nameof(font_color), () => grid_settings.font_color.rgb_solid_brush().frozen(), nameof(grid_settings)); _selection_highlight = backing(nameof(selection_highlight), () => grid_settings.selection_highlight.to_brush().frozen(), nameof(grid_settings)); + _row_vms = row_vms; } public GridSettings grid_settings => _grid_settings.Value; @@ -56,8 +58,7 @@ namespace mbts.ui { return grid_settings; } - public void load_from(GridSettings grid) => - _load_from_core(grid, true); + public void load_from(GridSettings grid) => _load_from_core(grid, true); void IObserver.OnCompleted() { } void IObserver.OnError(Exception error) { } @@ -102,6 +103,10 @@ namespace mbts.ui { col.load_from(cs); } } + // invalidate all the rows to draw them again to reflect the latest settings + if (_row_vms != null) + foreach (var r in _row_vms) r.raise_invalidated(); + // string id_key(string id) => id.ToLower(); } From 37c6b5d46d7ee83df23d5bc35664c31191b76609 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Mon, 24 Sep 2018 16:32:04 -0400 Subject: [PATCH 073/203] SPARK-1711 Expose direct/implied markets in managed --- .../spark/view/Spark/Ladder/LadderGridHost.cs | 2 +- src/client/spark_core/Co2/Co2NativeMethods.cs | 2 + src/client/spark_core/Co2/IntPrice.cs | 23 ++- src/client/spark_core/Co2/MDConsumerHandle.cs | 176 ++++++++++++------ .../spark_core/Co2/NativePriceHelpers.cs | 11 ++ src/client/spark_core/Co2/PriceSize.cs | 35 +++- .../TestRunner/TestResultReceiver.cs | 7 + .../spark_core/TestRunner/TestRunner.cs | 10 + src/client/spark_test/Co2/Co2Tests.cs | 21 ++- src/lib/api/services/ApiUnitTestService.proto | 3 +- src/lib/md/test/Co2ManagedTestHelper.cpp | 10 + 11 files changed, 228 insertions(+), 72 deletions(-) diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index c6c249fd3..b137ab86a 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -387,7 +387,7 @@ namespace mbts.spark { if (_settings != null) { ulong unused = 0; bool last_trade_changed = _vm.md.get_last_trade(ref unused, out _last_trade, out DateTime _, out ulong __); - bool depth_changed = _vm.md.get_depth(ref _prices_updated, ref _bids, ref _asks); + bool depth_changed = _vm.md.get_depth(ref _prices_updated, ref _bids, ref _asks, NativePriceHelpers.COCONUT_MAX_DEPTH); _mkt_break_lines.refresh(); bool prices_changed = _price_dirty || diff --git a/src/client/spark_core/Co2/Co2NativeMethods.cs b/src/client/spark_core/Co2/Co2NativeMethods.cs index 294510034..9bae6ed87 100644 --- a/src/client/spark_core/Co2/Co2NativeMethods.cs +++ b/src/client/spark_core/Co2/Co2NativeMethods.cs @@ -115,6 +115,8 @@ namespace mbts.co2 [DllImport("libspark")] internal static extern void co2_get_nan_price_size(out NativePriceSize ps); + [DllImport("libspark")] + internal static extern void co2_test_get_constants(out ulong max_depth, out ulong max_implied_depth, out sbyte nan_exponent); } } diff --git a/src/client/spark_core/Co2/IntPrice.cs b/src/client/spark_core/Co2/IntPrice.cs index c238df309..989fa4063 100644 --- a/src/client/spark_core/Co2/IntPrice.cs +++ b/src/client/spark_core/Co2/IntPrice.cs @@ -16,10 +16,9 @@ namespace mbts.co2 { } public long mantissa => NativePriceHelpers.decode(_mantissa); public sbyte exponent => _exponent; - public bool is_nan => _exponent == 127; + public bool is_nan => _exponent == NativePriceHelpers.COCONUT_NAN_EXPONENT; public double price() { - if (is_nan) return double.NaN; - return mantissa * NativePriceHelpers.pow10(exponent); + return NativePriceHelpers.to_double(mantissa, exponent); } public bool Equals(IntPrice other) { @@ -28,6 +27,24 @@ namespace mbts.co2 { public override bool Equals(object obj) { return base.Equals(obj); } + public static bool operator ==(IntPrice a, IntPrice b) { + return a.Equals(b); + } + public static bool operator !=(IntPrice a, IntPrice b) { + return !a.Equals(b); + } + public static bool operator < (IntPrice a, IntPrice b) { + if (a.exponent == b.exponent) { + return a.mantissa < b.mantissa; + } + return a.price() < b.price(); + } + public static bool operator > (IntPrice a, IntPrice b) { + if (a.exponent == b.exponent) { + return a.mantissa > b.mantissa; + } + return a.price() > b.price(); + } public override string ToString() { return $"{price():0.00}"; } diff --git a/src/client/spark_core/Co2/MDConsumerHandle.cs b/src/client/spark_core/Co2/MDConsumerHandle.cs index 0a9cce2c4..eabedc9de 100644 --- a/src/client/spark_core/Co2/MDConsumerHandle.cs +++ b/src/client/spark_core/Co2/MDConsumerHandle.cs @@ -1,4 +1,6 @@ -using bts.co2; +using bts; +using bts.api; +using bts.co2; using System; using System.Collections.Generic; using System.IO; @@ -8,11 +10,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using security_key = System.UInt32; -using bts.api; -using bts; -namespace mbts.co2 -{ +namespace mbts.co2 { unsafe class MDConsumerHandle { // Once is_initialized goes true it never goes false @@ -37,10 +36,14 @@ namespace mbts.co2 var updated = market_timestamp; if (updated > time && updated != TimeStamp.invalid_value) { if (has_depth) { - bid = _hot_depth->bid(0); - ask = _hot_depth->ask(0); + var bids = new NativePriceSize[1]; + var asks = new NativePriceSize[1]; + var result = get_depth(ref time, ref bids, ref asks, 1); + bid = bids[0]; + ask = asks[0]; } else { + Log.assert_d(false); bid = _hot->book.bid; ask = _hot->book.ask; } @@ -51,16 +54,77 @@ namespace mbts.co2 return false; } - public bool get_depth(ref ulong time, ref NativePriceSize[] bids, ref NativePriceSize[] asks) { + private unsafe static void copy_merged(in HotDepthEntry hot, uint count, NativePriceSize[] bid, NativePriceSize[] ask) { + Log.assert_d(bid.Length >= count); + Log.assert_d(ask.Length >= count); + uint xda = 0, xdb = 0, xia = 0, xib = 0; + for (uint x = 0; x < count; ++x) { + if (xib < NativePriceHelpers.COCONUT_MAX_IMPLIED_DEPTH) { + var direct = hot.direct(xdb).bid; + var implied = hot.implied(xib).bid; + + // Compare bids + if (direct.int_price == implied.int_price) { + bid[x] = new NativePriceSize(direct.int_price, direct.size, implied.size); + xdb += 1; + xib += 1; + } + // Bids grow down, take the higher price + else if (implied.price < direct.price) { + bid[x] = direct; + xdb += 1; + } + else { + bid[x] = implied; + xib += 1; + } + } + else { + bid[x] = hot.direct(xdb).bid; + xdb += 1; + } + + if (xia < NativePriceHelpers.COCONUT_MAX_IMPLIED_DEPTH) { + var direct = hot.direct(xda).ask; + var implied = hot.implied(xia).ask; + + // Compare asks + if (direct.int_price == implied.int_price) { + ask[x] = new NativePriceSize( direct.int_price, direct.size, implied.size); + xda += 1; + xia += 1; + } + // asks grow up, take the lower price + else if (direct.int_price < implied.int_price) { + ask[x] = direct; + xda += 1; + } + else { + ask[x] = implied; + xia += 1; + } + } + else { + ask[x] = hot.direct(xda).ask; + xda += 1; + } + } + } + + + public bool get_depth(ref ulong time, ref NativePriceSize[] bids, ref NativePriceSize[] asks, uint depth) { if (!has_depth) { throw new InvalidOperationException("Cannot get depth data from a handle that does not offer depth!"); } var updated = market_timestamp; - if (updated > time && updated != UInt64.MaxValue) { - if (bids == null || bids.Length < 10) bids = new NativePriceSize[10]; - if (asks == null || asks.Length < 10) asks = new NativePriceSize[10]; - for (int depth = 0; depth < 10; ++depth) { - bids[depth] = _hot_depth->bid(depth); - asks[depth] = _hot_depth->ask(depth); + Log.assert(depth <= NativePriceHelpers.COCONUT_MAX_DEPTH); + if (updated > time && updated != TimeStamp.invalid_value) { + if (bids == null || bids.Length < depth) { + bids = new NativePriceSize[depth]; } + + if (asks == null || asks.Length < depth) { + asks = new NativePriceSize[depth]; + } + copy_merged(*_hot_depth, depth, bids, asks); time = updated; return true; } @@ -72,7 +136,7 @@ namespace mbts.co2 total_volume = _cold->total_volume; return (_cold->last_trade.load_if_new(ref time, out ps)); } - public ulong misc_timestamp { get { return _cold->misc_timestamp; } } + public ulong misc_timestamp { get { return _cold->misc_timestamp; } } public MiscMessage get_misc() { return new MiscMessage() { @@ -80,24 +144,24 @@ namespace mbts.co2 }; } - public ulong total_volume { get { return _cold->total_volume; } } + public ulong total_volume { get { return _cold->total_volume; } } - public double settlement_price { get { return _cold->settlement_price;} } - public DateTime settlement_date { get { return _cold->settlement_date;} } - public double previous_settlement_price { get { return _cold->previous_settlement_price;} } - public DateTime previous_settlement_date { get { return _cold->previous_settlement_date;} } + public double settlement_price { get { return _cold->settlement_price; } } + public DateTime settlement_date { get { return _cold->settlement_date; } } + public double previous_settlement_price { get { return _cold->previous_settlement_price; } } + public DateTime previous_settlement_date { get { return _cold->previous_settlement_date; } } - public double open_price { get { return _cold->open_price;} } - public DateTime open_date { get { return _cold->open_date; } } + public double open_price { get { return _cold->open_price; } } + public DateTime open_date { get { return _cold->open_date; } } - public double close_price { get { return _cold->close_price;} } - public DateTime close_date { get { return _cold->close_date; } } - public double session_high { get { return _cold->session_high;} } - public double session_low { get { return _cold->session_low; } } - public double open_interest { get { return _cold->open_interest; } } + public double close_price { get { return _cold->close_price; } } + public DateTime close_date { get { return _cold->close_date; } } + public double session_high { get { return _cold->session_high; } } + public double session_low { get { return _cold->session_low; } } + public double open_interest { get { return _cold->open_interest; } } // public double session_high_bid { get { return _cold->session_high_bid; } } // public double session_low_offer { get { return _cold->session_low_offer; } } - public double fixing_price { get { return _cold->fixing_price; } } + public double fixing_price { get { return _cold->fixing_price; } } // // Internal @@ -126,8 +190,7 @@ namespace mbts.co2 #region structures [StructLayout(LayoutKind.Sequential)] - internal struct HotEntry - { + internal struct HotEntry { // we don't actually need to decode timestamp. If it goes up data has changed public readonly TimeStamp timestamp; // 8 public readonly NativePriceLevel book; // 24 @@ -139,12 +202,11 @@ namespace mbts.co2 } [StructLayout(LayoutKind.Sequential)] - internal struct HotDepthEntry - { + internal struct HotDepthEntry { public readonly TimeStamp timestamp; // 8 public readonly security_key key; // 4 public readonly UInt32 sequence; // 4 - + // The ByValArray type would be more elegant but it does not allocate inline. // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] @@ -160,48 +222,39 @@ namespace mbts.co2 private readonly NativePriceLevel direct8; private readonly NativePriceLevel direct9; // 2 implied levels - private readonly NativePriceLevel implied0; - private readonly NativePriceLevel implied1; + private readonly ImpliedPriceLevel implied0; + private readonly ImpliedPriceLevel implied1; private readonly byte _status; public CoconutSecurityStatus status { get { return (CoconutSecurityStatus)_status; } } - public NativePriceSize bid(int depth) { + public NativePriceLevel direct(uint depth) { switch (depth) { - case 0: return direct0.bid; - case 1: return direct1.bid; - case 2: return direct2.bid; - case 3: return direct3.bid; - case 4: return direct4.bid; - case 5: return direct5.bid; - case 6: return direct6.bid; - case 7: return direct7.bid; - case 8: return direct8.bid; - case 9: return direct9.bid; + case 0: return direct0; + case 1: return direct1; + case 2: return direct2; + case 3: return direct3; + case 4: return direct4; + case 5: return direct5; + case 6: return direct6; + case 7: return direct7; + case 8: return direct8; + case 9: return direct9; default: throw new ArgumentException("depth"); } } - public NativePriceSize ask(int depth) { + public ImpliedPriceLevel implied(uint depth) { switch (depth) { - case 0: return direct0.ask; - case 1: return direct1.ask; - case 2: return direct2.ask; - case 3: return direct3.ask; - case 4: return direct4.ask; - case 5: return direct5.ask; - case 6: return direct6.ask; - case 7: return direct7.ask; - case 8: return direct8.ask; - case 9: return direct9.ask; + case 0: return implied0; + case 1: return implied1; default: throw new ArgumentException("depth"); } } - + } [StructLayout(LayoutKind.Sequential, Size = 392)] - internal struct ColdEntry - { + internal struct ColdEntry { public int exchange_status; // Last trade public AtomicPriceSize last_trade; @@ -237,8 +290,7 @@ namespace mbts.co2 } [StructLayout(LayoutKind.Sequential)] - internal struct ColdHeader - { + internal struct ColdHeader { internal uint version; internal ulong count; internal ulong segment_count; diff --git a/src/client/spark_core/Co2/NativePriceHelpers.cs b/src/client/spark_core/Co2/NativePriceHelpers.cs index 2c44a4d15..40fc131ed 100644 --- a/src/client/spark_core/Co2/NativePriceHelpers.cs +++ b/src/client/spark_core/Co2/NativePriceHelpers.cs @@ -27,6 +27,17 @@ namespace mbts.co2 { } return mantissa; } + + public static double to_double(long mantissa, sbyte exponent) { + if (exponent == COCONUT_NAN_EXPONENT) { + return double.NaN; + } + return mantissa * pow10(exponent); + } + + public const uint COCONUT_MAX_DEPTH = 10; + public const uint COCONUT_MAX_IMPLIED_DEPTH = 2; + internal const sbyte COCONUT_NAN_EXPONENT = 127; } } diff --git a/src/client/spark_core/Co2/PriceSize.cs b/src/client/spark_core/Co2/PriceSize.cs index f32e7360f..0f1876aa8 100644 --- a/src/client/spark_core/Co2/PriceSize.cs +++ b/src/client/spark_core/Co2/PriceSize.cs @@ -20,13 +20,37 @@ namespace mbts.co2 { public NativePriceSize bid => new NativePriceSize(_bid, _bid_size); public NativePriceSize ask => new NativePriceSize(_ask, _ask_size); - public static NativePriceLevel empty = new NativePriceLevel(IntPrice.NaN, 0, IntPrice.NaN, 0); + public void Deconstruct(out NativePriceSize bid, out NativePriceSize ask) { + bid = this.bid; + ask = this.ask; + } + + internal static NativePriceLevel empty => new NativePriceLevel(IntPrice.NaN, 0, IntPrice.NaN, 0); + } + public readonly struct ImpliedPriceLevel { + readonly IntPrice _bid; + readonly IntPrice _ask; + readonly uint _bid_size; + readonly uint _ask_size; + public ImpliedPriceLevel(IntPrice bid, uint bid_size, IntPrice ask, uint ask_size) { + _bid = bid; + _bid_size = bid_size; + _ask = ask; + _ask_size = ask_size; + } + public NativePriceSize bid => new NativePriceSize(_bid, _bid_size, 0); + public NativePriceSize ask => new NativePriceSize(_ask, _ask_size, 0); + public void Deconstruct(out NativePriceSize bid, out NativePriceSize ask) { + bid = this.bid; + ask = this.ask; + } } public readonly struct NativePriceSize : IEquatable { readonly IntPrice _price; readonly uint _size; + readonly uint _direct_size; static NativePriceSize() { Log.assert(Marshal.SizeOf() == 16); @@ -35,10 +59,17 @@ namespace mbts.co2 { public NativePriceSize(IntPrice price, uint size) { _price = price; _size = size; + _direct_size = size; } public NativePriceSize(long mantissa, sbyte exponent, uint size) { _price = new IntPrice(mantissa, exponent); _size = size; + _direct_size = size; + } + public NativePriceSize(IntPrice price, uint direct_size, uint implied_size) { + _price = price; + _size = direct_size + implied_size; + _direct_size = direct_size; } public static NativePriceSize NO_MARKET = new NativePriceSize(IntPrice.NaN, 0); @@ -47,6 +78,8 @@ namespace mbts.co2 { public long mantissa => _price.mantissa; public sbyte exponent => _price.exponent; public uint size => _size; + public uint direct_size => _direct_size; + public uint implied_size => _size - _direct_size; public double price => _price.price(); public bool is_nan => _price.is_nan; diff --git a/src/client/spark_core/TestRunner/TestResultReceiver.cs b/src/client/spark_core/TestRunner/TestResultReceiver.cs index 72a052c84..9063a0c59 100644 --- a/src/client/spark_core/TestRunner/TestResultReceiver.cs +++ b/src/client/spark_core/TestRunner/TestResultReceiver.cs @@ -50,7 +50,9 @@ namespace bts.test { var unused = wr.GetResponseAsync(); } + public void print_result(TestResult result) { + ResultCount += 1; if (_defer) { _deferred.Add(result); return; @@ -93,6 +95,10 @@ namespace bts.test { _fail++; ConsoleEx.Write("TIMEOUT", ConsoleColor.DarkYellow); break; + case TestResult.Types.Step.Crash: + _fail++; + ConsoleEx.Write("CRASH", ConsoleColor.Red); + break; } Console.WriteLine(); } @@ -112,6 +118,7 @@ namespace bts.test { } public int ErrorCount => _fail; + public int ResultCount { get; set; } public void Dispose() { _running = false; diff --git a/src/client/spark_core/TestRunner/TestRunner.cs b/src/client/spark_core/TestRunner/TestRunner.cs index be3f09bb4..9a1e6f436 100644 --- a/src/client/spark_core/TestRunner/TestRunner.cs +++ b/src/client/spark_core/TestRunner/TestRunner.cs @@ -21,6 +21,7 @@ namespace bts.test { var listening = trr.listen(); trr.print_begin(); foreach (var t in tests) { + int count = trr.ResultCount; var res = run_one(t, TimeSpan.FromSeconds(timeout)); if (res == TIMEOUT) { trr.print_result(new TestResult() { @@ -28,6 +29,15 @@ namespace bts.test { Status = TestResult.Types.Step.Timeout }); } + else { + // If we didn't time out and no results were reported we crashed or something + if (count == trr.ResultCount) { + trr.print_result(new TestResult() { + TestName = t, + Status = TestResult.Types.Step.Crash + }); + } + } } trr.print_summary(); return trr.ErrorCount; diff --git a/src/client/spark_test/Co2/Co2Tests.cs b/src/client/spark_test/Co2/Co2Tests.cs index e9dce8cf3..9ff7a242d 100644 --- a/src/client/spark_test/Co2/Co2Tests.cs +++ b/src/client/spark_test/Co2/Co2Tests.cs @@ -86,13 +86,18 @@ namespace mbts.co2.tests Assert.IsTrue(entry_size == (ulong)Marshal.SizeOf(entry)); Assert.IsTrue(entry.status == CoconutSecurityStatus.PacketLoss); Assert.IsTrue(entry.sequence == 888); - for (int x = 0; x < 10; ++x) { - var bid = entry.bid(x); + for (uint x = 0; x < NativePriceHelpers.COCONUT_MAX_DEPTH; ++x) { + (var bid, var ask) = entry.direct(x); Assert.IsTrue(bid.price == x + 0.5); Assert.IsTrue(bid.size == x + 1); - var ask = entry.ask(x); Assert.AreEqual(fmt(ask.price), fmt(x+0.6)); - //Assert.IsTrue(ask.price == x + 0.6); + Assert.IsTrue(ask.size == x + 2); + } + for (uint x = 0; x < NativePriceHelpers.COCONUT_MAX_IMPLIED_DEPTH; ++x) { + (var bid, var ask) = entry.implied(x); + Assert.AreEqual(77, (bid.price)); + Assert.IsTrue(bid.size == x + 1); + Assert.AreEqual(88, ask.price); Assert.IsTrue(ask.size == x + 2); } } @@ -257,6 +262,14 @@ namespace mbts.co2.tests Assert.IsNaN(npl.bid.price); } + [Test] + public void co2_constants_test() { + Co2NativeTestMethods.co2_test_get_constants(out ulong max_depth, out ulong max_implied_depth, out sbyte nan); + Assert.AreEqual(max_depth, NativePriceHelpers.COCONUT_MAX_DEPTH); + Assert.AreEqual(max_implied_depth, NativePriceHelpers.COCONUT_MAX_IMPLIED_DEPTH); + Assert.AreEqual(nan, NativePriceHelpers.COCONUT_NAN_EXPONENT); + } + } } diff --git a/src/lib/api/services/ApiUnitTestService.proto b/src/lib/api/services/ApiUnitTestService.proto index a711ba570..a5e56e044 100644 --- a/src/lib/api/services/ApiUnitTestService.proto +++ b/src/lib/api/services/ApiUnitTestService.proto @@ -48,7 +48,8 @@ message TestResult { TEARDOWN = 2; PASS = 3; TIMEOUT = 4; - IGNORED = 5; + CRASH = 5; + IGNORED = 6; } string test_name = 1; string message = 3; diff --git a/src/lib/md/test/Co2ManagedTestHelper.cpp b/src/lib/md/test/Co2ManagedTestHelper.cpp index 10d58ea23..680e41841 100644 --- a/src/lib/md/test/Co2ManagedTestHelper.cpp +++ b/src/lib/md/test/Co2ManagedTestHelper.cpp @@ -52,6 +52,11 @@ extern "C" BTS_EXPORT void co2_get_sample_depth(size_t& size, HotDepthEntry& ent PriceSize_from_double<2>(x + 0.5, x + 1), PriceSize_from_double<2>(x + 0.6, x + 2)); } + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + entry.write_implied(x, + PriceSize_from_double<3>(77, x + 1), + PriceSize_from_double<3>(88, x + 2)); + } entry.set_time(utils::TimeStamp::now()); entry.status = COCONUTSECURITYSTATUS_PACKET_LOSS; entry.sequence = 888; @@ -143,6 +148,11 @@ extern "C" BTS_EXPORT void co2_get_zero_and_nan_book(PriceLevel& price) { extern "C" BTS_EXPORT void co2_get_nan_price_size(PriceSize& ps) { ps = NO_MARKET; } +extern "C" BTS_EXPORT void co2_test_get_constants(size_t& max_depth, size_t& max_implied_depth, uint8_t& nan_exponent) { + max_depth = COCONUT_MAX_DEPTH; + max_implied_depth = COCONUT_MAX_IMPLIED_DEPTH; + nan_exponent = INT_NAN.exponent(); +} }}}//namespace From add6f99189822ae4ff36c4c1207e4a62e0c14854 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Mon, 24 Sep 2018 16:32:18 -0400 Subject: [PATCH 074/203] SPARK-1659 Fix operator < (cpp) --- src/lib/md/MDConsumerHandle.h | 9 ++++----- src/lib/md/PriceSize.h | 8 ++++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 54aa61e55..0804b3a21 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -60,7 +60,7 @@ inline void read_combined( auto implied = hot.implied[xib].bid(); // Compare bids - if (direct.price() == implied.price()) { + if (direct.int_price() == implied.int_price()) { bid[x] = PriceSize( direct.int_price(), direct.size() + implied.size(), @@ -69,7 +69,7 @@ inline void read_combined( xib += 1; } // Bids grow down, take the higher price - else if (implied.price() < direct.price()) { + else if (implied.int_price() < direct.int_price()) { bid[x] = direct; xdb += 1; } @@ -84,12 +84,11 @@ inline void read_combined( } if (xia < COCONUT_MAX_IMPLIED_DEPTH) { - // We only need to compare the mantissa because all levels are guarunteed to have the same exponent auto direct = hot.direct[xda].ask(); auto implied = hot.implied[xia].ask(); // Compare asks - if (direct.price() == implied.price()) { + if (direct.int_price() == implied.int_price()) { ask[x] = PriceSize( direct.int_price(), direct.size() + implied.size(), @@ -98,7 +97,7 @@ inline void read_combined( xia += 1; } // asks grow up, take the lower price - else if (direct.price() < implied.price()) { + else if (direct.int_price() < implied.int_price()) { ask[x] = direct; xda += 1; } diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 3b39ad17b..89d8161a4 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -66,8 +66,12 @@ class IntPrice { return !(*this == other); } bool operator<(const IntPrice& other) const { - if (_exponent < other._exponent) return true; - return _mantissa < other._mantissa; + if (_exponent == other._exponent) { + return _mantissa < other._mantissa; + } + else { + return price() < other.price(); + } } int64_t mantissa() const { return _mantissa; } From dc9df4af090b9350a8d2b4490492e2dc1e95c7d1 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Tue, 25 Sep 2018 09:46:07 -0400 Subject: [PATCH 075/203] net change pricing hooked up, need some optimizations in PriceSize for speed --- .../orders/spreader_orders/SpreaderOrder.cpp | 18 ++++++- .../orders/spreader_orders/SpreaderOrder.h | 3 ++ .../spreader/LegPropertyData.h | 3 ++ .../spreader/SpreaderLegData.cpp | 3 +- .../spreader/SpreaderLegData.h | 50 ++++++++++++++++++- .../SpreaderStartStateWorker.cpp | 5 +- src/lib/sparkproto/SparkProtos.proto | 5 ++ 7 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp b/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp index cbf1cb1bb..991b630f3 100644 --- a/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp +++ b/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp @@ -6,6 +6,7 @@ #include "lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h" #include "lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h" #include "lib/orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.h" +#include "lib/orderman/events/SettlementUpdateEvent.h" #include "lib/tracing/bts_trace.h" #include "lib/utils/helper_macros.h" @@ -100,6 +101,21 @@ void SpreaderOrder::_on_book_update(const BookUpdateEvent& book_event){ trace.spreader_on_book = utils::TimeStamp::now(); _state_worker->on_book (book_event.book,trace); } +void SpreaderOrder::_on_settlement_update(const SettlementUpdateEvent& + settlement_event){ + if (!_active) return; + LOG(OrderMan, Info, "SpreaderOrder::_on_settlement_update " + "key:{} sec_key:{} state:{} settlement:{}", + key(), settlement_event.key(), + static_cast(_state_worker->state()), + settlement_event.settlement_price); + // this is a key->index lookup + SpreaderLegData& md_leg = + _bookkeeping_data().get_leg_data(settlement_event.key()); + md_leg.update_settlement_price(settlement_event.settlement_price); + bts::PerfTrace trace; + _state_worker->on_book (md_leg.book(),trace); +} void SpreaderOrder::_on_child_order_update(const OrderStateUpdateEvent& ord_ev){ auto trace = ord_ev.trace; trace.spreader_on_child_order_update = utils::TimeStamp::now(); @@ -114,7 +130,7 @@ void SpreaderOrder::_on_child_order_update(const OrderStateUpdateEvent& ord_ev){ if (!SpreaderUtils::order_acknowledged(status)){ LOG(OrderMan, Diag, "SpreaderOrder::_on_child_order_update, unack'ed " "callback for key:{} sec_key:{} order_key:{} " - "status:{} ignoring", + "status:{} ignoring", key(), sec_key, order_key, status); bts_assert(filled_qty.value() == 0); return; diff --git a/src/lib/orderman/orders/spreader_orders/SpreaderOrder.h b/src/lib/orderman/orders/spreader_orders/SpreaderOrder.h index 55a819528..8be458b9d 100644 --- a/src/lib/orderman/orders/spreader_orders/SpreaderOrder.h +++ b/src/lib/orderman/orders/spreader_orders/SpreaderOrder.h @@ -24,6 +24,8 @@ namespace bts { namespace api {namespace orderman {class OrderState; class CreateSpreaderOrder;}} namespace orderman { +class SettlementUpdateEvent; + class SpreaderOrder : public Order, public ISpreaderManager { friend class SpreaderStartStateWorker; @@ -124,6 +126,7 @@ private: ///////////////////////////////////////////////// void _on_time (const bts::mercury::TimerEvent&); void _on_book_update(const BookUpdateEvent&); + void _on_settlement_update(const SettlementUpdateEvent&); void _on_child_order_update(const OrderStateUpdateEvent&); // State Transformations //////////////////// ///////////////////////////////////////////// diff --git a/src/lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h b/src/lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h index 64e3fb66b..b6eba2d11 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h +++ b/src/lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h @@ -40,6 +40,9 @@ public: bool is_active_quoting() const {return _leg_params.is_active_quoting();} const bts::api::orderman::OrderDefinition& quoter_order_definition() const {return _quoter_order_definition;} const bts::api::orderman::OrderDefinition& hedger_order_definition() const {return _hedger_order_definition;} + bts::spark::LegSpreaderParams::LegMarketDataCalculation leg_md_calc() const { + return _leg_params.leg_md_calc(); + } }; }} diff --git a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp index 57c4e776a..580b65921 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp +++ b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp @@ -19,7 +19,8 @@ _hedger_order_builder (hedger_order_builder), _quoter_child_order_state (quoter_child_order_state), _hedger_data_vector (hedger_data_vector), _book(book), -_unhedged_delta(unhedged_delta) +_unhedged_delta(unhedged_delta), +_settlement_price(os::nan()) {} SpreaderLegData::SpreaderLegData(SpreaderLegData&& other) diff --git a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h index b3ad546f6..46f065221 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h +++ b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h @@ -6,6 +6,8 @@ #include "lib/orderman/actors/order_manager/OrderBuilder.h" #include "lib/orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.h" #include "lib/orderman/orders/spreader_orders/spreader/hedger/UnhedgedDelta.h" +#include "lib/os/os.h" +#include "lib/logging/logging.h" namespace bts{ namespace orderman{ @@ -30,6 +32,7 @@ private: std::vector& _hedger_data_vector; Book& _book; UnhedgedDelta& _unhedged_delta; + md::Price _settlement_price; public: SpreaderLegData (const LegPropertyData& leg_property_data, SingleLegQuoter& quoter, @@ -56,6 +59,7 @@ public: SingleLegQuoter& quoter() {return _quoter;} const SingleLegQuoter& quoter() const {return _quoter;} const bts::orderman::Book& book () const {return _book;} + md::Price settlement_price() const {return _settlement_price;} SpreaderChildOrderState& order_update_message () {return _quoter_child_order_state;} bts::orderman::OrderKey key() {return _quoter_child_order_state.order_key();} bts::orderman::OrderKey order_key() {return _quoter_child_order_state.order_key();} @@ -99,6 +103,50 @@ public: // mutators //////////////////// //////////////////////////////// // called for every book update - void update_book(const bts::orderman::Book& book_arg) {_book = book_arg;} + void update_book(const Book& book) { + if(_leg_property_data.leg_md_calc()== + bts::spark::LegSpreaderParams::BOOK) + _book=book; + else + // this has a problem. if we get events + // in this order + // 1. book + // 2. settlement + // we will continue with an empty + // book until 3 + // 3. book + _offset_book(book, _settlement_price); + } + void update_settlement_price(md::Price price){ + if(_leg_property_data.leg_md_calc()== + bts::spark::LegSpreaderParams::NET_CHANGE){ + if(os::is_nan(_settlement_price)){ + _settlement_price=price; + _offset_book(_book,price); + } + else + LOG(OrderMan,Info,"SpreaderLegData::update_settlement_price " + "ignoring update key:{} curr:{} new:{}", + _leg_property_data.security_key(), + _settlement_price, price); + } + } +private: + void _offset_book(const Book& book, + md::Price settlement_price){ + if(os::is_nan(settlement_price)) + _book=Book(); + else{ + const BookSide& bid_source=book.bid(); + BookSide& bid_target=_book.bid(); + for(size_t i=0;i( + source.price()-settlement_price, + source.size()); + } + } + } }; }} diff --git a/src/lib/orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp b/src/lib/orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp index 13b204c58..b1522cc42 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp +++ b/src/lib/orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp @@ -94,8 +94,11 @@ void SpreaderStartStateWorker::on_activate(Price price, Quantity quantity, Sprea const LegPropertyData& leg_prop = _spreader->_bookkeeping_data().get_leg_data(l_idx).leg_property_data(); LOG(OrderMan, Info, "SpreaderStartStateWorker::on_activate subbing to book sec_key: {}", leg_prop.security_key()); order_services.order_services().book_updates.sub(leg_prop.leg_security_key(), - [this](const bts::orderman::BookUpdateEvent& bk_event) + [this](const BookUpdateEvent& bk_event) {_spreader->_on_book_update (bk_event);}); + order_services.order_services().settlement_updates.sub(leg_prop.leg_security_key(), + [this](const SettlementUpdateEvent& s_event) + {_spreader->_on_settlement_update (s_event);}); } // 6. sub to time order_services.order_services().time_change.sub([this](const bts::mercury::TimerEvent& t_event){_spreader->_on_time(t_event);}); diff --git a/src/lib/sparkproto/SparkProtos.proto b/src/lib/sparkproto/SparkProtos.proto index e30e01b2e..e47c85dc6 100644 --- a/src/lib/sparkproto/SparkProtos.proto +++ b/src/lib/sparkproto/SparkProtos.proto @@ -30,6 +30,10 @@ message RulePriority message LegSpreaderParams { + enum LegMarketDataCalculation{ + BOOK=0; + NET_CHANGE=1; + } /// Legs can be quoted actively, or sent in reponse /// to fills bool is_active_quoting = 20; @@ -37,6 +41,7 @@ message LegSpreaderParams QuoteParams quote_params = 40; PreHedgeParams pre_hedge_params = 50; PostHedgeParams post_hedge_params = 60; + LegMarketDataCalculation leg_md_calc = 80; } // /************************************************************************************************* From 7958ce236f865987b6f3f8616fb8e4d9d39f59a5 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 25 Sep 2018 10:22:28 -0400 Subject: [PATCH 076/203] SPARK-1659 Make PriceSize 3rd argument type safe --- src/lib/md/IntPrice.h | 63 +++++++++++++++++++++++++++ src/lib/md/MDConsumerHandle.h | 8 ++-- src/lib/md/PriceSize.h | 81 ++++------------------------------- src/lib/md/SizeTypes.h | 6 +++ src/lib/md/test/md2_tests.cpp | 26 ----------- 5 files changed, 82 insertions(+), 102 deletions(-) create mode 100644 src/lib/md/IntPrice.h create mode 100644 src/lib/md/SizeTypes.h diff --git a/src/lib/md/IntPrice.h b/src/lib/md/IntPrice.h new file mode 100644 index 000000000..a615ae115 --- /dev/null +++ b/src/lib/md/IntPrice.h @@ -0,0 +1,63 @@ +#include "lib/utils/bts_assert.h" +#include "lib/utils/bts_array_size.h" + +namespace bts { +namespace md { + +static const double powers_10[] = { + 1E-20, 1E-19, 1E-18, 1E-17, 1E-16, 1E-15, 1E-14, 1E-13, 1E-12, 1E-11, 1E-10, 1E-9, 1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, + 1E0, + 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15, 1E16, 1E17, 1E18, 1E19, 1E20 +}; +static const int max_abs_power_10 = BTS_ARRAYSIZE(powers_10) / 2; +inline double pow10(int power) { + const int offset = power + max_abs_power_10; + const int powers_10_sz = BTS_ARRAYSIZE(powers_10); + bts_assert_d(offset >= 0 && offset < powers_10_sz); + if (offset >= 0 && offset < powers_10_sz) { + return powers_10[offset]; + } + return std::numeric_limits().quiet_NaN(); +} + +class IntPrice { +#ifdef _WIN32 + int64_t _mantissa : 56; + int64_t _exponent : 8; +#else + int64_t _mantissa : 56; + int8_t _exponent : 8; +#endif + public: + IntPrice() : _mantissa(0), _exponent(127) {/*nan*/} + IntPrice(int64_t mantissa, int8_t exponent) : _mantissa(mantissa), _exponent(exponent) {} + IntPrice(const IntPrice& other) : _mantissa(other._mantissa), _exponent(other._exponent) {} + + bool operator==(const IntPrice& other) const { + return _mantissa == other._mantissa && _exponent == other._exponent; + } + bool operator!=(const IntPrice& other) const { + return !(*this == other); + } + bool operator<(const IntPrice& other) const { + if (_exponent == other._exponent) { + return _mantissa < other._mantissa; + } + else { + return price() < other.price(); + } + } + + int64_t mantissa() const { return _mantissa; } + int8_t exponent() const { return _exponent; } + double price() const { + if (is_nan()) return NO_PRICE; + return mantissa() * pow10(exponent()); + } + bool is_nan() const { return _exponent == 127; } +}; +static_assert(sizeof(IntPrice) == 8, "IntPrice should be this big |--------|"); +extern IntPrice INT_NAN; + + +}} \ No newline at end of file diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 0804b3a21..591e81f82 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -63,8 +63,8 @@ inline void read_combined( if (direct.int_price() == implied.int_price()) { bid[x] = PriceSize( direct.int_price(), - direct.size() + implied.size(), - direct.size()); + direct.size(), + ImpliedSize(implied.size())); xdb += 1; xib += 1; } @@ -91,8 +91,8 @@ inline void read_combined( if (direct.int_price() == implied.int_price()) { ask[x] = PriceSize( direct.int_price(), - direct.size() + implied.size(), - direct.size()); + direct.size(), + ImpliedSize(implied.size())); xda += 1; xia += 1; } diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 89d8161a4..93d525db3 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -1,6 +1,9 @@ #pragma once #include "common.h" +#include "IntPrice.h" +#include "SizeTypes.h" + #include "lib/os/os.h" #include "lib/utils/warn_ctrl.h" #include "lib/utils/bts_array_size.h" @@ -8,6 +11,7 @@ #include "lib/utils/bts_no_copy.h" #include "lib/utils/Optimistic.h" + #include #include #include @@ -18,73 +22,7 @@ namespace md { typedef double Price; typedef uint32_t Size; -static const double powers_10[] = { - 1E-20, 1E-19, 1E-18, 1E-17, 1E-16, 1E-15, 1E-14, 1E-13, 1E-12, 1E-11, 1E-10, 1E-9, 1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, - 1E0, - 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15, 1E16, 1E17, 1E18, 1E19, 1E20 -}; -static const int max_abs_power_10 = BTS_ARRAYSIZE(powers_10) / 2; -inline double pow10(int power) { - const int offset = power + max_abs_power_10; - const int powers_10_sz = BTS_ARRAYSIZE(powers_10); - bts_assert_d(offset >= 0 && offset < powers_10_sz); - if (offset >= 0 && offset < powers_10_sz) { - return powers_10[offset]; - } - return std::numeric_limits().quiet_NaN(); -} int to_exponent(double value); -template -inline bool compact_mantissa(Mantissa& mantissa, Exponent& exponent) { - bool result = false; - // If the mantissa is larger than 39 bits will allow attempt to shrink it - while (mantissa > 0x8000000000 || mantissa < -0x8000000000) { - mantissa /= 10; - exponent += 1; - result = true; - } - return result; -} - -class IntPrice { -#ifdef _WIN32 - int64_t _mantissa : 56; - int64_t _exponent : 8; -#else - int64_t _mantissa : 56; - int8_t _exponent : 8; -#endif - public: - IntPrice() : _mantissa(0), _exponent(127) {/*nan*/} - IntPrice(int64_t mantissa, int8_t exponent) : _mantissa(mantissa), _exponent(exponent) {} - IntPrice(const IntPrice& other) : _mantissa(other._mantissa), _exponent(other._exponent) {} - - bool operator==(const IntPrice& other) const { - return _mantissa == other._mantissa && _exponent == other._exponent; - } - bool operator!=(const IntPrice& other) const { - return !(*this == other); - } - bool operator<(const IntPrice& other) const { - if (_exponent == other._exponent) { - return _mantissa < other._mantissa; - } - else { - return price() < other.price(); - } - } - - int64_t mantissa() const { return _mantissa; } - int8_t exponent() const { return _exponent; } - double price() const { - if (is_nan()) return NO_PRICE; - return mantissa() * pow10(exponent()); - } - bool is_nan() const { return _exponent == 127; } -}; - -static_assert(sizeof(IntPrice) == 8, "IntPrice should be this big |--------|"); -extern IntPrice INT_NAN; class PriceSize { IntPrice _price; @@ -97,10 +35,10 @@ public: _size(size), _direct_size(size) { } - PriceSize(IntPrice price, Size size, Size direct_size) : + PriceSize(IntPrice price, Size size, ImpliedSize implied_size) : _price(price), - _size(size), - _direct_size(direct_size) + _size(size + implied_size.value()), + _direct_size(size) { } PriceSize() : _price(INT_NAN), _size(0), _direct_size(0) {} @@ -127,7 +65,6 @@ public: bool is_nan() const { return _price.is_nan(); } }; extern PriceSize NO_MARKET; - static_assert(sizeof(PriceSize) == 16, "PriceSize should be small"); template @@ -210,10 +147,10 @@ struct ImpliedPriceLevel : private PriceLevel { using PriceLevel::operator!=; bool operator=(const PriceLevel& other) { return update(other); } PriceSize bid() const { - return PriceSize(_bid, _bid_size, 0); + return PriceSize(_bid, 0, ImpliedSize(_bid_size)); } PriceSize ask() const { - return PriceSize(_ask, _ask_size, 0); + return PriceSize(_ask, 0, ImpliedSize(_ask_size)); } }; static_assert(sizeof(ImpliedPriceLevel) == 24, "PriceLevel should be small"); diff --git a/src/lib/md/SizeTypes.h b/src/lib/md/SizeTypes.h new file mode 100644 index 000000000..16484c8dd --- /dev/null +++ b/src/lib/md/SizeTypes.h @@ -0,0 +1,6 @@ +#pragma once +#include "lib/utils/ValueWrapper.h" +namespace bts { +namespace md { + using ImpliedSize = utils::ValueWrapper; +}} \ No newline at end of file diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index 01642403c..375a5e2f1 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -283,29 +283,6 @@ public: // Equal if the delta is less than error return delta < acceptable_error; } - void test_compact_exponent() { - ExchangeDecimal test_values[] = { - { 2030000000000, -9 }, // mantissa > pow(2,40) - { -2030000000000, -9 }, // mantissa > pow(2,40) - { 2022750000000, -9 },// mantissa > pow(2,40) - { -2022750000000, -9 },// mantissa > pow(2,40) - { 20227500000000, -10 },// mantissa > pow(2,40) - { 202275000000000, -11 },// mantissa > pow(2,40) - { 2022750000000000, -12 },// mantissa > pow(2,40) - { 20227500000000000, -13 },// mantissa > pow(2,40) - { -20227500000000000, -13 },// mantissa > pow(2,40) - { 203, 1}, // control - { -14000000000, -8 }, // control - { -14, 1 }, // control - { 20227500, -4 }, // control - }; - for (auto p : test_values) { - double expected = p.mantissa * pow10(p.exponent); - compact_mantissa(p.mantissa, p.exponent); - double compacted = p.mantissa * pow10(p.exponent); - REQUIRE(close_enough(expected, compacted)); - } - } void test_producer_consumer() { MDProducer producer(TEST_PLATFORM); @@ -1037,9 +1014,6 @@ TEST_CASE("CO2Tests","[md]") { SECTION("test_slow_double_conversion") { co2_tests->test_slow_double_conversion(); } - SECTION("test_compact_exponent") { - co2_tests->test_compact_exponent(); - } SECTION("test_producer_consumer") { co2_tests->test_producer_consumer(); } From 49e24804f757db588cf81035b056f20afe73d280 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 25 Sep 2018 13:35:28 -0400 Subject: [PATCH 077/203] SPARK-1659 move settlement into hot data section so we can use it to compute books --- src/client/spark_core/Co2/MDConsumerHandle.cs | 17 ++-- src/client/spark_test/Co2/Co2Tests.cs | 7 +- src/lib/CMakeDeps.cmake | 6 +- src/lib/md/Diag/ShmDumper.cpp | 9 +- src/lib/md/IntPrice.h | 12 +++ src/lib/md/MDConsumerHandle.h | 84 +++++++++++-------- src/lib/md/MDProducerHandle.cpp | 13 ++- src/lib/md/MDProducerHandle.h | 4 +- src/lib/md/PriceSize.h | 9 +- src/lib/md/Shm/MDStructure.h | 9 +- src/lib/md/protos/co2.proto | 10 ++- src/lib/md/relay/QueryServer.cpp | 13 ++- src/lib/md/relay/RelayClient.cpp | 8 +- src/lib/md/test/Co2ManagedTestHelper.cpp | 9 +- src/lib/md/test/md2_tests.cpp | 12 +-- .../md/platforms/mdp3_onixs/MDP3Listener.h | 2 +- .../mdp3_onixs/MDP3ListenerHelpers.h | 10 ++- 17 files changed, 136 insertions(+), 98 deletions(-) diff --git a/src/client/spark_core/Co2/MDConsumerHandle.cs b/src/client/spark_core/Co2/MDConsumerHandle.cs index eabedc9de..1d7bfdcc7 100644 --- a/src/client/spark_core/Co2/MDConsumerHandle.cs +++ b/src/client/spark_core/Co2/MDConsumerHandle.cs @@ -138,17 +138,11 @@ namespace mbts.co2 { } public ulong misc_timestamp { get { return _cold->misc_timestamp; } } - public MiscMessage get_misc() { - return new MiscMessage() { - SettlementPrice = _cold->settlement_price, - }; - } - public ulong total_volume { get { return _cold->total_volume; } } - public double settlement_price { get { return _cold->settlement_price; } } + public double settlement_price { get { return has_depth ? _hot_depth->settlement.price() : _hot->settlement.price(); } } public DateTime settlement_date { get { return _cold->settlement_date; } } - public double previous_settlement_price { get { return _cold->previous_settlement_price; } } + public double previous_settlement_price { get { return _cold->previous_settlement_price.price(); } } public DateTime previous_settlement_date { get { return _cold->previous_settlement_date; } } public double open_price { get { return _cold->open_price; } } @@ -196,6 +190,7 @@ namespace mbts.co2 { public readonly NativePriceLevel book; // 24 public readonly security_key key; // 4 public readonly UInt32 sequence; // 4 + public readonly IntPrice settlement; // 8 public readonly byte _status; // 1 public CoconutSecurityStatus status { get { return (CoconutSecurityStatus)_status; } } @@ -206,6 +201,7 @@ namespace mbts.co2 { public readonly TimeStamp timestamp; // 8 public readonly security_key key; // 4 public readonly UInt32 sequence; // 4 + public readonly IntPrice settlement; // 8 // The ByValArray type would be more elegant but it does not allocate inline. // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] @@ -253,7 +249,7 @@ namespace mbts.co2 { } - [StructLayout(LayoutKind.Sequential, Size = 392)] + [StructLayout(LayoutKind.Sequential, Size = 384)] internal struct ColdEntry { public int exchange_status; // Last trade @@ -263,9 +259,8 @@ namespace mbts.co2 { //// Settlement public UInt64 misc_timestamp; - public double settlement_price; public utils.LLDate settlement_date; - public double previous_settlement_price; + public IntPrice previous_settlement_price; public utils.LLDate previous_settlement_date; //// Opening/closing diff --git a/src/client/spark_test/Co2/Co2Tests.cs b/src/client/spark_test/Co2/Co2Tests.cs index 9ff7a242d..61fc880c8 100644 --- a/src/client/spark_test/Co2/Co2Tests.cs +++ b/src/client/spark_test/Co2/Co2Tests.cs @@ -72,6 +72,7 @@ namespace mbts.co2.tests Assert.IsTrue(entry.status == CoconutSecurityStatus.PacketLoss); Assert.IsTrue(entry.key == 1234); Assert.IsTrue(entry.sequence == 777); + Assert.AreEqual(entry.settlement.price(), 98.6); } string fmt(double a) { @@ -86,6 +87,7 @@ namespace mbts.co2.tests Assert.IsTrue(entry_size == (ulong)Marshal.SizeOf(entry)); Assert.IsTrue(entry.status == CoconutSecurityStatus.PacketLoss); Assert.IsTrue(entry.sequence == 888); + Assert.AreEqual(entry.settlement.price(), 98.6); for (uint x = 0; x < NativePriceHelpers.COCONUT_MAX_DEPTH; ++x) { (var bid, var ask) = entry.direct(x); Assert.IsTrue(bid.price == x + 0.5); @@ -125,9 +127,8 @@ namespace mbts.co2.tests Assert.IsTrue(entry.total_volume == 12345); Assert.IsTrue(compare_date(entry.last_trade_time, DateTime.Now)); //// Settlement - Assert.AreEqual(entry.settlement_price, 3.3); Assert.IsTrue(entry.settlement_date == new DateTime(2015, 9, 3)); - Assert.AreEqual(entry.previous_settlement_price, 3.3); + Assert.AreEqual(entry.previous_settlement_price.price(), 3.3); Assert.IsTrue(entry.previous_settlement_date == new DateTime(2015, 9, 2)); //// Open/close Assert.IsTrue(entry.open_price == 4.4); @@ -229,7 +230,7 @@ namespace mbts.co2.tests Assert.IsTrue(trade_date.Year == 2015 && trade_date.Month == 9 && trade_date.Day == 22); Assert.IsTrue(total_volume == 4200); Assert.IsTrue(handle.settlement_date.Year == 2000 && handle.settlement_date.Day == 9 && handle.settlement_date.Month == 8); - Assert.IsTrue(handle.settlement_price == 6.6); + Assert.AreEqual(handle.settlement_price, 6.6); Assert.IsTrue(handle.open_price == 7.7); Assert.IsTrue(handle.open_date.Year == 2001 && handle.open_date.Month == 1 && handle.open_date.Day == 2); Assert.IsTrue(handle.close_price == 8.8); diff --git a/src/lib/CMakeDeps.cmake b/src/lib/CMakeDeps.cmake index e846df538..cb3a82d45 100644 --- a/src/lib/CMakeDeps.cmake +++ b/src/lib/CMakeDeps.cmake @@ -1,5 +1 @@ -<<<<<<< HEAD -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/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/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/dynamic_orders.proto;config/protos/environment.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Dispatcher.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) -======= -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/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/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Dispatcher.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) ->>>>>>> net_change +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/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.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/dynamic_orders.proto;config/protos/jetfix.proto;config/protos/logging.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/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/MDPriceLantencyTester.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/Dispatcher.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) \ No newline at end of file diff --git a/src/lib/md/Diag/ShmDumper.cpp b/src/lib/md/Diag/ShmDumper.cpp index 809704d86..b60bbb98d 100644 --- a/src/lib/md/Diag/ShmDumper.cpp +++ b/src/lib/md/Diag/ShmDumper.cpp @@ -25,6 +25,11 @@ double dump(TimeStamp time) { return (TimeStamp::now() - time).seconds(); } +string dump(IntPrice price) { + stringstream res; + res << std::setprecision(11) << price.price(); + return res.str(); +} string dump(PriceSize ps) { stringstream res; res << "{"; @@ -144,9 +149,8 @@ string dump_entry(const ColdEntry& entry) { if (has_value(entry.last_trade)) res << "'last_trade': " << dump(entry.last_trade) << ", "; if (entry.total_volume > 0) res << "'total_volume': " << entry.total_volume << ", "; if (entry.last_trade_time > 0) res << "'last_trade_time_ll': " << entry.last_trade_time << ", "; - if (!os::is_nan(entry.misc.settlement_price)) res << "'settlement_price': " << entry.misc.settlement_price << ", "; if (entry.misc.settlement_date.year > 0) res << "'settlement_date': " << dump(entry.misc.settlement_date) << ", "; - if (!os::is_nan(entry.misc.previous_settlement_price)) res << "'previous_settlement_price': " << entry.misc.previous_settlement_price << ", "; + if (!(entry.misc.previous_settlement_price.is_nan())) res << "'previous_settlement_price': " << entry.misc.previous_settlement_price.price() << ", "; if (entry.misc.previous_settlement_date.year > 0) res << "'previous_settlement_date': " << dump(entry.misc.previous_settlement_date) << ", "; res << "'deleted': " << std::boolalpha << entry.deleted << " "; res << "}"; @@ -183,6 +187,7 @@ string dump_entry(const HotDepthEntry& entry) { } res << "], "; + res << "'settlement': '" << dump(entry.settlement) << "', "; res << "'status': '" << CoconutSecurityStatus_Name((CoconutSecurityStatus)entry.status) << "', "; res << "'sequence': '" << entry.sequence << "', "; res << " }"; diff --git a/src/lib/md/IntPrice.h b/src/lib/md/IntPrice.h index a615ae115..ee2d1fc7b 100644 --- a/src/lib/md/IntPrice.h +++ b/src/lib/md/IntPrice.h @@ -59,5 +59,17 @@ class IntPrice { static_assert(sizeof(IntPrice) == 8, "IntPrice should be this big |--------|"); extern IntPrice INT_NAN; +template +IntPrice IntPrice_from_double(double price) { + if (os::is_nan(price)) return INT_NAN; + double temp; + if (fabs(modf(price, &temp)) != 0.0) { + const double rounding = 0.5 * pow10(-PRECISION); + price += (price > 0) ? rounding : -rounding; + price *= pow10(PRECISION); + return {(int64_t)price, -PRECISION}; + } + return {(int64_t)price, 0}; +} }} \ No newline at end of file diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 591e81f82..9c77d393b 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -183,6 +183,53 @@ public: TimeStamp last_market_update() const; + HandleReadResult get_direct_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { + return get_book(read_direct, time, copydepth, bids, asks); + } + HandleReadResult get_implied_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { + return get_book(read_implied, time, copydepth, bids, asks); + } + HandleReadResult get_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { + return get_book(read_combined, time, copydepth, bids, asks); + } + + bool get_last_trade(TimeStamp& time, PriceSize& out_trade, datetime::LLDateTime& out_trade_timestamp, size_t* total_volume=nullptr) const; + + IntPrice settlement_price() const { + switch(_type) { + case HANDLETYPE_DEPTH: return book.hot->settlement; + case HANDLETYPE_BBO: return bbo.hot->settlement; + default: + return INT_NAN; + } + } + datetime::Date settlement_date() const { return this->misc().settlement_date; } + + TimeStamp misc_timestamp() const; + MiscData misc() const; + + MDConsumerHandle& operator=(const MDConsumerHandle& other) { + _type = HANDLETYPE_VOID; + switch(other._type) { + case HANDLETYPE_VOID: break; + case HANDLETYPE_DEPTH: + book = other.book; + break; + case HANDLETYPE_BBO: + bbo = other.bbo; + break; + case HANDLETYPE_SYNTHETIC: + synthetic.def = other.synthetic.def; + break; + } + std::atomic_thread_fence(std::memory_order_release); + _type = other._type; + return *this; + } + +private: + friend class MDConsumerInst; + template HandleReadResult get_book(Read read, TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { bts_assert_d(copydepth <= COCONUT_MAX_DEPTH); @@ -212,44 +259,7 @@ public: } bts_assert(false); } - - HandleReadResult get_direct_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { - return get_book(read_direct, time, copydepth, bids, asks); - } - HandleReadResult get_implied_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { - return get_book(read_implied, time, copydepth, bids, asks); - } - HandleReadResult get_combined_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { - return get_book(read_combined, time, copydepth, bids, asks); - } - - bool get_last_trade(TimeStamp& time, PriceSize& out_trade, datetime::LLDateTime& out_trade_timestamp, size_t* total_volume=nullptr) const; - - TimeStamp misc_timestamp() const; - MiscData misc() const; - - MDConsumerHandle& operator=(const MDConsumerHandle& other) { - _type = HANDLETYPE_VOID; - switch(other._type) { - case HANDLETYPE_VOID: break; - case HANDLETYPE_DEPTH: - book = other.book; - break; - case HANDLETYPE_BBO: - bbo = other.bbo; - break; - case HANDLETYPE_SYNTHETIC: - synthetic.def = other.synthetic.def; - break; - } - std::atomic_thread_fence(std::memory_order_release); - _type = other._type; - return *this; - } - -private: - friend class MDConsumerInst; void set_hot(const void* hot) { switch(_type) { case HANDLETYPE_DEPTH: diff --git a/src/lib/md/MDProducerHandle.cpp b/src/lib/md/MDProducerHandle.cpp index 2e1b20590..de5819197 100644 --- a/src/lib/md/MDProducerHandle.cpp +++ b/src/lib/md/MDProducerHandle.cpp @@ -148,16 +148,21 @@ void MDProducerHandle::update_exchange_status(TimeStamp, TradingStatus status) { cheader().exchange_status_count++; } -void MDProducerHandle::update_settlement(TimeStamp time, Price price, Date date) { +void MDProducerHandle::update_settlement(TimeStamp time, IntPrice price, Date date) { if (date > cdata().misc.settlement_date) { - cdata().misc.previous_settlement_price = cdata().misc.settlement_price; + cdata().misc.previous_settlement_price = has_depth() ? hdepth().settlement : hdata().settlement; cdata().misc.previous_settlement_date = cdata().misc.settlement_date; } - cdata().misc.settlement_price = price; + if (has_depth()) { + hdepth().settlement = price; + } + else { + hdata().settlement = price; + } cdata().misc.settlement_date = date; cdata().misc.misc_timestamp = time; } -void MDProducerHandle::update_previous_settlement(TimeStamp time, Price price, Date date) { +void MDProducerHandle::update_previous_settlement(TimeStamp time, IntPrice price, Date date) { cdata().misc.previous_settlement_price = price; cdata().misc.previous_settlement_date = date; cdata().misc.misc_timestamp = time; diff --git a/src/lib/md/MDProducerHandle.h b/src/lib/md/MDProducerHandle.h index d912802fe..37600a69e 100644 --- a/src/lib/md/MDProducerHandle.h +++ b/src/lib/md/MDProducerHandle.h @@ -101,8 +101,8 @@ public: void update_exchange_status(TimeStamp time, TradingStatus status); // cold // Misc fields - void update_settlement(TimeStamp time, Price price, datetime::Date date); // cold - void update_previous_settlement(TimeStamp time, Price price, datetime::Date date); // cold + void update_settlement(TimeStamp time, IntPrice price, datetime::Date date); // cold + void update_previous_settlement(TimeStamp time, IntPrice price, datetime::Date date); // cold void update_opening(TimeStamp time, Price price, datetime::Date date); // cold void update_closing(TimeStamp time, Price price, datetime::Date date); // cold void update_session_high(TimeStamp time, Price price); // cold diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 93d525db3..00cecba66 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -70,14 +70,7 @@ static_assert(sizeof(PriceSize) == 16, "PriceSize should be small"); template PriceSize PriceSize_from_double(double price, Size size) { if (size == 0 || os::is_nan(price)) return NO_MARKET; - double temp; - if (fabs(modf(price, &temp)) != 0.0) { - const double rounding = 0.5 * pow10(-PRECISION); - price += (price > 0) ? rounding : -rounding; - price *= pow10(PRECISION); - return PriceSize({(int64_t)price, -PRECISION}, size); - } - return PriceSize({(int64_t)price, 0}, size); + return { IntPrice_from_double(price), size }; } diff --git a/src/lib/md/Shm/MDStructure.h b/src/lib/md/Shm/MDStructure.h index 847e4dbf2..4017dc66d 100644 --- a/src/lib/md/Shm/MDStructure.h +++ b/src/lib/md/Shm/MDStructure.h @@ -45,6 +45,7 @@ class HotEntry { PriceLevel book; // 24 security_key key; // 4 uint32_t sequence; // 4 + IntPrice settlement; // 8 uint8_t status; // 1 void set_time(const TimeStamp& t) { timestamp.store(t, std::memory_order_release); } TimeStamp time() const { return timestamp.load(std::memory_order_acquire); } @@ -67,6 +68,7 @@ class HotDepthEntry { uint32_t size() const { return sizeof(HotDepthEntry); } security_key key; // 4 NOTE: key is at a different offset than it is in HotEntry uint32_t sequence; // 4 + IntPrice settlement; // 8 std::array direct; // 240 std::array implied; // 48 uint8_t status; // 1 @@ -156,9 +158,8 @@ struct ALIGNED_(CACHE_LINE) ColdHeader { struct MiscData { MiscData(): misc_timestamp(), - settlement_price(NO_PRICE), settlement_date(), - previous_settlement_price(NO_PRICE), + previous_settlement_price(INT_NAN), previous_settlement_date(), open_price(NO_PRICE), open_date(), @@ -173,9 +174,8 @@ struct MiscData { { } TimeStamp misc_timestamp; // Settlement - Price settlement_price; datetime::Date settlement_date; - Price previous_settlement_price; + IntPrice previous_settlement_price; datetime::Date previous_settlement_date; // Opening/Closing @@ -194,7 +194,6 @@ struct MiscData { bool operator==(const MiscData& other) const { return - settlement_price == other.settlement_price && settlement_date == other.settlement_date && previous_settlement_price == other.previous_settlement_price && previous_settlement_date == other.previous_settlement_date && diff --git a/src/lib/md/protos/co2.proto b/src/lib/md/protos/co2.proto index b83c15150..369d33069 100644 --- a/src/lib/md/protos/co2.proto +++ b/src/lib/md/protos/co2.proto @@ -82,6 +82,11 @@ message MDReply { repeated ChannelReply channels = 1; } +message PriceMessage { + int64 mantissa = 1; + sint32 exponent = 2; +} + message PriceSizeMessage { int64 mantissa = 1; sint32 exponent = 2; @@ -105,9 +110,10 @@ message BookMessage { message MiscMessage { // int32 key = 1; reserved 1; - double settlement_price = 2; + reserved 2; + PriceMessage settlement = 22; uint32 settlement_date = 3; - double previous_settlement_price = 20; + PriceMessage previous_settlement_price = 23; uint32 previous_settlement_date = 21; double open_price = 4; uint32 open_date = 5; diff --git a/src/lib/md/relay/QueryServer.cpp b/src/lib/md/relay/QueryServer.cpp index 24419e0b6..47ea8c4d3 100644 --- a/src/lib/md/relay/QueryServer.cpp +++ b/src/lib/md/relay/QueryServer.cpp @@ -133,10 +133,15 @@ void set_price(PriceSize ps, co2::TradeMessage* ptrade) { ptrade->set_size(ps.size()); } -void copy_misc(MiscData misc, co2::MiscMessage* pmisc) { - pmisc->set_settlement_price(misc.settlement_price); +void set_price(const IntPrice& price, co2::PriceMessage* dest) { + dest->set_mantissa(price.mantissa()); + dest->set_exponent(price.exponent()); +} + +void copy_misc(IntPrice settlement, MiscData misc, co2::MiscMessage* pmisc) { + set_price(settlement, pmisc->mutable_settlement()); pmisc->set_settlement_date(misc.settlement_date.to_short()); - pmisc->set_previous_settlement_price(misc.previous_settlement_price); + set_price(misc.previous_settlement_price, pmisc->mutable_previous_settlement_price()); pmisc->set_previous_settlement_date(misc.previous_settlement_date.to_short()); pmisc->set_open_price(misc.open_price); pmisc->set_open_date(misc.open_date.to_short()); @@ -309,7 +314,7 @@ public: if (HASFLAG(flags, MDRequest::MISC)) { TimeStamp time(snapshot ? 0 : chan.time()); if (handle.misc_timestamp() > time) { - copy_misc(handle.misc(), pupd->mutable_misc()); + copy_misc(handle.settlement_price(), handle.misc(), pupd->mutable_misc()); } } if (pupd->has_direct() || diff --git a/src/lib/md/relay/RelayClient.cpp b/src/lib/md/relay/RelayClient.cpp index 63a63541e..64aa81bfb 100644 --- a/src/lib/md/relay/RelayClient.cpp +++ b/src/lib/md/relay/RelayClient.cpp @@ -45,6 +45,10 @@ PriceSize book_at(const google::protobuf::RepeatedPtrField& si return NO_MARKET; } +IntPrice convert(const PriceMessage& price) { + return { price.mantissa(), (int8_t)price.exponent() }; +} + class RelayProducer : public IRelayWriter { bool _shared; std::map _producers; @@ -69,8 +73,8 @@ class RelayProducer : public IRelayWriter { } if (update.has_misc()) { auto& misc = update.misc(); - handle.update_settlement(updated, misc.settlement_price(), datetime::Date(misc.settlement_date())); - handle.update_previous_settlement(updated, misc.previous_settlement_price(), datetime::Date(misc.previous_settlement_date())); + handle.update_settlement(updated, convert(misc.settlement()), datetime::Date(misc.settlement_date())); + handle.update_previous_settlement(updated, convert(misc.previous_settlement_price()), datetime::Date(misc.previous_settlement_date())); handle.update_opening(updated, misc.open_price(), datetime::Date(misc.open_date())); handle.update_closing(updated, misc.close_price(), datetime::Date(misc.close_date())); handle.update_session_high(updated, misc.session_high()); diff --git a/src/lib/md/test/Co2ManagedTestHelper.cpp b/src/lib/md/test/Co2ManagedTestHelper.cpp index 680e41841..e0bc44448 100644 --- a/src/lib/md/test/Co2ManagedTestHelper.cpp +++ b/src/lib/md/test/Co2ManagedTestHelper.cpp @@ -41,6 +41,7 @@ extern "C" BTS_EXPORT void co2_get_sample_hot(size_t& size, HotEntry& entry) { PriceSize_from_double<2>(-9.7, 245)); entry.sequence = 777; entry.status = COCONUTSECURITYSTATUS_PACKET_LOSS; + entry.settlement = IntPrice_from_double<7>(98.6); entry.set_time(utils::TimeStamp::now()); } @@ -60,6 +61,7 @@ extern "C" BTS_EXPORT void co2_get_sample_depth(size_t& size, HotDepthEntry& ent entry.set_time(utils::TimeStamp::now()); entry.status = COCONUTSECURITYSTATUS_PACKET_LOSS; entry.sequence = 888; + entry.settlement = IntPrice_from_double<7>(98.6); } extern "C" BTS_EXPORT void co2_get_sample_cold(size_t& size, ColdEntry& entry) { @@ -71,9 +73,8 @@ extern "C" BTS_EXPORT void co2_get_sample_cold(size_t& size, ColdEntry& entry) { entry.total_volume = 12345; entry.last_trade_time = datetime::ll_now(); // Settlement - entry.misc.settlement_price = 3.3; entry.misc.settlement_date = datetime::Date(2015, 9, 3); - entry.misc.previous_settlement_price = 3.3; + entry.misc.previous_settlement_price = IntPrice_from_double<7>(3.3); entry.misc.previous_settlement_date = datetime::Date(2015, 9, 2); // Open/close entry.misc.open_price = 4.4; @@ -120,8 +121,8 @@ extern "C" BTS_EXPORT MDProducer* co2_produce_test_md() { for (auto& p : producers) { p.update_bbo(utils::TimeStamp::now(), PriceSize_from_double<3>(1.1, 101), PriceSize_from_double<3>(2.2,202)); p.add_trade(utils::TimeStamp::now(), PriceSize_from_double<3>(5.5, 505), datetime::ll_from_date(datetime::Date(2015, 9, 22)), AGGRESSOR_SIDE_NONE, 4200); - p.update_settlement(utils::TimeStamp::now(), 6.6, datetime::Date(2000, 8, 8)); - p.update_settlement(utils::TimeStamp::now(), 6.6, datetime::Date(2000, 8, 9)); + p.update_settlement(utils::TimeStamp::now(), IntPrice_from_double<7>(6.6), datetime::Date(2000, 8, 8)); + p.update_settlement(utils::TimeStamp::now(), IntPrice_from_double<7>(6.6), datetime::Date(2000, 8, 9)); p.update_opening(utils::TimeStamp::now(), 7.7, datetime::Date(2001, 1, 2)); auto t = utils::TimeStamp::now(); diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index 375a5e2f1..68fbf9de3 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -1621,7 +1621,7 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(asks[x].implied_size() == 1); } read_time = TimeStamp(); - REQUIRE(c.get_combined_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(c.get_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) { if (x < COCONUT_MAX_IMPLIED_DEPTH) { REQUIRE(bids[x].size() == 3); @@ -1691,7 +1691,7 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); - REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(c.get_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); for (size_t x = 0; x < TEST_DEPTH; ++x) { if (x < COCONUT_MAX_IMPLIED_DEPTH) { REQUIRE(bids[x].size() == 3); @@ -1760,7 +1760,7 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); - REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(c.get_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); REQUIRE(bids[0].size() == 2); REQUIRE(bids[0].mantissa() == 600); REQUIRE(asks[0].size() == 2); @@ -1829,7 +1829,7 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); - REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(c.get_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); REQUIRE(bids[0].mantissa() == 500); REQUIRE(bids[0].size() == 1); REQUIRE(asks[0].mantissa() == 1000); @@ -1916,7 +1916,7 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); - REQUIRE(c.get_combined_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(c.get_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); REQUIRE(bids[0].mantissa() == 500); REQUIRE(bids[0].size() == 1); REQUIRE(asks[0].mantissa() == 1000); @@ -2002,7 +2002,7 @@ TEST_CASE("MergeImpliedTests", "[md]") { REQUIRE(asks[x].implied_size() == 2); } read_time = TimeStamp(); - REQUIRE(c.get_combined_book(read_time, TEST_DEPTH+1, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(c.get_book(read_time, TEST_DEPTH+1, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); REQUIRE(bids[0].mantissa() == 500); REQUIRE(bids[0].size() == 3); REQUIRE(asks[0].mantissa() == 1000); diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h index 30fde8349..995a260d0 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h @@ -438,7 +438,7 @@ void MDP3Listener::onSettlementPrice(const onixs::Security& sec, const Statistic auto display_factor = data->handle.security().display_factor; data->handle.update_settlement( utils::TimeStamp::now(), - double_of_price(price, display_factor), + convert_price(price, display_factor), date_of_timestamp(time)); } } diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h index 2d36ee542..67316bd4f 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h @@ -16,12 +16,18 @@ inline PriceSize pricesize_of_price_size(const onixs::Decimal price, int8_t disp return result; } -template -double double_of_price(const Price& price, int8_t display_factor) { +template +double double_of_price(const OnixsPrice& price, int8_t display_factor) { // order of multiplication is important because of floating point error return (price.mantissa() * pow10(price.exponent())) * pow10(display_factor); } +template +IntPrice convert_price(const OnixsPrice& price, int8_t display_factor) { + return IntPrice(price.mantissa(), price.exponent() + display_factor); +} + + inline int floorlog10(int64_t m) { bts_assert(m >= 0); if (m < 10) return 0; From cc0dd649eacdd70e556167b3c9679208ecb586c6 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 25 Sep 2018 14:57:01 -0400 Subject: [PATCH 078/203] SPARK-1712 Change settlement price to IntPrice --- src/lib/md/SettlementRecorder.cpp | 30 +++++++------ src/lib/md/SettlementRecorder.h | 2 +- src/lib/md/test/md2_tests.cpp | 44 +++++++++---------- .../orderman/actors/MDSupplementarySource.cpp | 16 +++---- .../orderman/events/SettlementUpdateEvent.h | 4 +- .../orders/spreader_orders/SpreaderOrder.cpp | 4 +- .../md/platforms/mdp3_onixs/MDP3Listener.cpp | 2 +- .../md/platforms/mdp3_onixs/MDP3Listener.h | 2 +- .../mdp3_onixs/MDP3ListenerHelpers.h | 2 +- .../md/platforms/mdp3_onixs/MDP3Main.cpp | 12 ++--- .../sparkd/md/platforms/mdp3_onixs/MDP3Main.h | 2 +- 11 files changed, 62 insertions(+), 58 deletions(-) diff --git a/src/lib/md/SettlementRecorder.cpp b/src/lib/md/SettlementRecorder.cpp index cef22e6f7..e2b80b2fa 100644 --- a/src/lib/md/SettlementRecorder.cpp +++ b/src/lib/md/SettlementRecorder.cpp @@ -26,7 +26,7 @@ enum WorkType { struct Work { WorkType type; MDProducerHandle handle; - Price price; + IntPrice price; Date date; }; @@ -43,10 +43,11 @@ struct SettlementRecorderImpl { sqlite3_stmt_ptr insert_stmt; }; -bool record_settle(sqlite3_stmt* insert, security_key key, Price price, Date date) { +bool record_settle(sqlite3_stmt* insert, security_key key, IntPrice price, Date date) { sqlite3_bind_int64(insert, 1, key); sqlite3_bind_int(insert, 2, date.to_short()); - sqlite3_bind_double(insert, 3, price); + sqlite3_bind_int64(insert, 3, price.mantissa()); + sqlite3_bind_int(insert, 4, price.exponent()); auto result = sqlite3_step(insert); sqlite3_reset(insert); return (result == SQLITE_DONE || result == SQLITE_ROW); @@ -56,14 +57,16 @@ void publish_settles(sqlite3_stmt* select, MDProducerHandle handle) { sqlite3_bind_int(select, 1, handle.key()); auto result = sqlite3_step(select); if (result == SQLITE_ROW) { - Price price = sqlite3_column_double(select, 0); - Date date = Date(sqlite3_column_int(select, 1)); - handle.update_settlement(TimeStamp::now(), price, date); + int64_t mantissa = sqlite3_column_int(select, 0); + int exponent = sqlite3_column_int(select, 1); + Date date = Date(sqlite3_column_int(select, 2)); + handle.update_settlement(TimeStamp::now(), {mantissa,(int8_t)exponent}, date); result = sqlite3_step(select); if (result == SQLITE_ROW) { - price = sqlite3_column_double(select, 0); + int64_t mantissa = sqlite3_column_int(select, 0); + int exponent = sqlite3_column_int(select, 1); date = Date(sqlite3_column_int(select, 1)); - handle.update_previous_settlement(TimeStamp::now(), price, date); + handle.update_previous_settlement(TimeStamp::now(), {mantissa,(int8_t)exponent}, date); } } sqlite3_reset(select); @@ -97,7 +100,8 @@ SettlementRecorder::SettlementRecorder(Platform platform) : sqlite3_exec_nofail(db, "PRAGMA journal_mode=OFF"); sqlite3_exec_nofail(db, "PRAGMA syncronous=0"); // key=(security_key+date) - sqlite3_exec_nofail(db, "CREATE TABLE IF NOT EXISTS settlements (key INTEGER NOT NULL, date INTEGER NOT NULL, price REAL NOT NULL, PRIMARY KEY (key,date));"); + sqlite3_exec_nofail(db, + "CREATE TABLE IF NOT EXISTS settlements (key INTEGER NOT NULL, date INTEGER NOT NULL, mantissa INTEGER NOT NULL, exponent INTEGER NOT NULL, PRIMARY KEY (key,date));"); // Delete any records > 10 days old auto cutoff = datetime::local_now() - boost::gregorian::days(10); @@ -107,18 +111,18 @@ SettlementRecorder::SettlementRecorder(Platform platform) : sqlite3_exec_nofail(db, query); // statements - _self->select_stmt = prepare_query(db, "select price,date from settlements where key=? order by date desc limit 2"); - _self->insert_stmt = prepare_query(db, "insert or replace into settlements(key, date, price) values(?,?,?)"); + _self->select_stmt = prepare_query(db, "select mantissa,exponent,date from settlements where key=? order by date desc limit 2"); + _self->insert_stmt = prepare_query(db, "insert or replace into settlements(key, date, mantissa,exponent) values(?,?,?,?)"); } SettlementRecorder::~SettlementRecorder() { } void SettlementRecorder::restore_settlement(MDProducerHandle handle) { - Work work = { SNAPSHOT, handle, 0, Date() }; + Work work = { SNAPSHOT, handle, INT_NAN, Date() }; _self->work_queue.push(work); } -void SettlementRecorder::add_settlement(MDProducerHandle handle, Price price, datetime::Date date) { +void SettlementRecorder::add_settlement(MDProducerHandle handle, IntPrice price, datetime::Date date) { Work work = { UPDATE, handle, price, date }; _self->work_queue.push(work); } diff --git a/src/lib/md/SettlementRecorder.h b/src/lib/md/SettlementRecorder.h index 536ca374f..2dfc9844b 100644 --- a/src/lib/md/SettlementRecorder.h +++ b/src/lib/md/SettlementRecorder.h @@ -19,7 +19,7 @@ public: // Put settlement update in a queue // in do_work() drain queue and update settlement 'depth' void restore_settlement(MDProducerHandle handle); - void add_settlement(MDProducerHandle handle, Price price, datetime::Date date); + void add_settlement(MDProducerHandle handle, IntPrice price, datetime::Date date); void do_work(); private: diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index 68fbf9de3..d0eb3ac01 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -934,41 +934,41 @@ public: { co2::SettlementRecorder recorder(PLATFORM_UNSET); - recorder.add_settlement(ph1, 101, yesterday); - recorder.add_settlement(ph1, 102, today); - recorder.add_settlement(ph2, 199, yesterday); - recorder.add_settlement(ph2, 188, today); + recorder.add_settlement(ph1, {101,0}, yesterday); + recorder.add_settlement(ph1, {102,0}, today); + recorder.add_settlement(ph2, {199,0}, yesterday); + recorder.add_settlement(ph2, {188,0}, today); recorder.do_work(); recorder.do_work(); } - REQUIRE(h1.misc().settlement_price == 102); + REQUIRE(h1.settlement_price().price() == 102); REQUIRE(h1.misc().settlement_date == today); - REQUIRE(h1.misc().previous_settlement_price == 101); + REQUIRE(h1.misc().previous_settlement_price.price() == 101); REQUIRE(h1.misc().previous_settlement_date == yesterday); - REQUIRE(h2.misc().settlement_price == 188); + REQUIRE(h2.settlement_price().price() == 188); REQUIRE(h2.misc().settlement_date == today); - REQUIRE(h2.misc().previous_settlement_price == 199); + REQUIRE(h2.misc().previous_settlement_price.price() == 199); REQUIRE(h2.misc().previous_settlement_date == yesterday); // Update but skip persist auto tomorrow = datetime::add_days(today, 1); - ph1.update_settlement(TimeStamp::now(), 103, tomorrow); - REQUIRE(h1.misc().settlement_price == 103); + ph1.update_settlement(TimeStamp::now(), {103,0}, tomorrow); + REQUIRE(h1.settlement_price().price() == 103); REQUIRE(h1.misc().settlement_date == tomorrow); - REQUIRE(h1.misc().previous_settlement_price == 102); + REQUIRE(h1.misc().previous_settlement_price.price() == 102); REQUIRE(h1.misc().previous_settlement_date == today); - ph1.update_previous_settlement(TimeStamp::now(), 0, Date()); - REQUIRE(h1.misc().settlement_price == 103); + ph1.update_previous_settlement(TimeStamp::now(), {0,0}, Date()); + REQUIRE(h1.settlement_price().price() == 103); REQUIRE(h1.misc().settlement_date == tomorrow); - ph1.update_settlement(TimeStamp::now(), 0, Date()); - REQUIRE(h1.misc().settlement_price == 0); + ph1.update_settlement(TimeStamp::now(),{0,0}, Date()); + REQUIRE(h1.settlement_price().price() == 0); REQUIRE(h1.misc().settlement_date.day == 0); // pushing a new settlement for today doesn't invalidate yesterday - ph2.update_settlement(TimeStamp::now(), 201, today); - REQUIRE(h2.misc().settlement_price == 201); + ph2.update_settlement(TimeStamp::now(), {201,0}, today); + REQUIRE(h2.settlement_price().price() == 201); REQUIRE(h2.misc().settlement_date == today); - REQUIRE(h2.misc().previous_settlement_price == 199); + REQUIRE(h2.misc().previous_settlement_price.price() == 199); REQUIRE(h2.misc().previous_settlement_date == yesterday); // Restore persisted state @@ -978,13 +978,13 @@ public: recorder.restore_settlement(ph2); recorder.do_work(); } - REQUIRE(h1.misc().settlement_price == 102); + REQUIRE(h1.settlement_price().price() == 102); REQUIRE(h1.misc().settlement_date == today); - REQUIRE(h1.misc().previous_settlement_price == 101); + REQUIRE(h1.misc().previous_settlement_price.price() == 101); REQUIRE(h1.misc().previous_settlement_date == yesterday); - REQUIRE(h2.misc().settlement_price == 188); + REQUIRE(h2.settlement_price().price() == 188); REQUIRE(h2.misc().settlement_date == today); - REQUIRE(h2.misc().previous_settlement_price == 199); + REQUIRE(h2.misc().previous_settlement_price.price() == 199); REQUIRE(h2.misc().previous_settlement_date == yesterday); } #endif diff --git a/src/lib/orderman/actors/MDSupplementarySource.cpp b/src/lib/orderman/actors/MDSupplementarySource.cpp index 907f4babd..789921a80 100644 --- a/src/lib/orderman/actors/MDSupplementarySource.cpp +++ b/src/lib/orderman/actors/MDSupplementarySource.cpp @@ -12,7 +12,7 @@ namespace orderman { struct HandleRecord { md::MDConsumerHandle handle; - md::Price settlement_price; + md::IntPrice settlement_price; }; struct MDSupplementarySource::PImpl @@ -45,7 +45,7 @@ void MDSupplementarySource::init(){ auto k = ev.security_key.value(); if(me->handle_map.find(k)==me->handle_map.end()){ LOG(OrderMan, Info, "MDSupplementarySource::init key:{}", k); - me->handle_map[k]={me->md_consumer.get_handle(k),os::nan()}; + me->handle_map[k]={me->md_consumer.get_handle(k),md::INT_NAN}; } } } @@ -56,14 +56,14 @@ void MDSupplementarySource::on_poll(){ for(auto& kvp : me->handle_map){ SecurityKey key(kvp.first); HandleRecord& hr=kvp.second; - const md::MiscData& misc_data=hr.handle.misc(); - if(!os::is_nan(misc_data.settlement_price) && - misc_data.settlement_price!=hr.settlement_price){ + auto settlement_price = hr.handle.settlement_price(); + if(!settlement_price.is_nan() && + settlement_price!=hr.settlement_price){ LOG(OrderMan, Info, "MDSupplementarySource::on_poll new settle key:{} price:{}", - key, hr.settlement_price); - hr.settlement_price=misc_data.settlement_price; - SettlementUpdateEvent ev(misc_data.settlement_price,key); + key, hr.settlement_price.price()); + hr.settlement_price=settlement_price; + SettlementUpdateEvent ev(settlement_price,key); publish(me->settlement_update,ev); } } diff --git a/src/lib/orderman/events/SettlementUpdateEvent.h b/src/lib/orderman/events/SettlementUpdateEvent.h index 59421d56f..47bc8f24a 100644 --- a/src/lib/orderman/events/SettlementUpdateEvent.h +++ b/src/lib/orderman/events/SettlementUpdateEvent.h @@ -13,11 +13,11 @@ namespace orderman { class SettlementUpdateEvent : public mercury::Event { public: using SynopsisPolicy = mercury::MergeByKey; - md::Price settlement_price; + md::IntPrice settlement_price; SecurityKey security_key; SecurityKey key() const { return security_key; } - SettlementUpdateEvent(const md::Price& price, + SettlementUpdateEvent(const md::IntPrice& price, const SecurityKey& key) : settlement_price(price), diff --git a/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp b/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp index 991b630f3..c3d762124 100644 --- a/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp +++ b/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp @@ -108,11 +108,11 @@ void SpreaderOrder::_on_settlement_update(const SettlementUpdateEvent& "key:{} sec_key:{} state:{} settlement:{}", key(), settlement_event.key(), static_cast(_state_worker->state()), - settlement_event.settlement_price); + settlement_event.settlement_price.price()); // this is a key->index lookup SpreaderLegData& md_leg = _bookkeeping_data().get_leg_data(settlement_event.key()); - md_leg.update_settlement_price(settlement_event.settlement_price); + md_leg.update_settlement_price(settlement_event.settlement_price.price()); bts::PerfTrace trace; _state_worker->on_book (md_leg.book(),trace); } diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp index d23a7816a..0acd2b757 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp @@ -16,7 +16,7 @@ MDP3Listener::MDP3Listener( CMESecurityIndex& index, MDProducer& producer, SecurityIDRecorder& recorder, - co2::SettlementRecorder& , + // co2::SettlementRecorder& , const std::vector channels) : _index(index), _producer(producer), diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h index 995a260d0..1c9c599d6 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h @@ -27,7 +27,7 @@ public: MDP3Listener(CMESecurityIndex& index, MDProducer&, SecurityIDRecorder&, - co2::SettlementRecorder&, + // co2::SettlementRecorder&, const std::vector); virtual ~MDP3Listener(); diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h index 67316bd4f..5d323c5db 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3ListenerHelpers.h @@ -17,7 +17,7 @@ inline PriceSize pricesize_of_price_size(const onixs::Decimal price, int8_t disp } template -double double_of_price(const OnixsPrice& price, int8_t display_factor) { +double double_of_price(const OnixPrice& price, int8_t display_factor) { // order of multiplication is important because of floating point error return (price.mantissa() * pow10(price.exponent())) * pow10(display_factor); } diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Main.cpp b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Main.cpp index 0aa5ad045..b52adae86 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Main.cpp +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Main.cpp @@ -26,7 +26,7 @@ namespace md { MDP3Main::MDP3Main() : _producer(PLATFORM_MDP3ONIXS), _security_recorder(), - _settlement_recorder(PLATFORM_MDP3ONIXS), + // _settlement_recorder(PLATFORM_MDP3ONIXS), _engines(), _handlers(), _listeners(), @@ -169,10 +169,10 @@ void MDP3Main::run(std::vector& symbols) { }); spread_resolve_timer.start(); - utils::DispatcherTimer settlement_timer(disp, 100, [&]() { - _settlement_recorder.do_work(); - }); - settlement_timer.start(); + // utils::DispatcherTimer settlement_timer(disp, 100, [&]() { + // _settlement_recorder.do_work(); + // }); + // settlement_timer.start(); utils::DispatcherTimer heartbeat_timer(disp, 250, [&]() { for (const auto& listener: _listeners) { @@ -352,7 +352,7 @@ unique_ptr MDP3Main::prepare_listener(onixs::Handler& handler, con const auto& options = channel_settings_for(channel.id); - unique_ptr listener(new MDP3Listener(_index, _producer, _security_recorder, _settlement_recorder, producer_channels)); + unique_ptr listener(new MDP3Listener(_index, _producer, _security_recorder, /*_settlement_recorder,*/ producer_channels)); for (auto& symbol : channel.symbols) { listener->add_filter_symbol(symbol.symbol()); } diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h index e6059c7fd..1ebfdb99b 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Main.h @@ -40,7 +40,7 @@ private: CMESecurityIndex _index; MDProducer _producer; SecurityIDRecorder _security_recorder; - co2::SettlementRecorder _settlement_recorder; + // co2::SettlementRecorder _settlement_recorder; std::map> _engines; std::map> _handlers; std::vector> _listeners; From 2cb5cddb91fa24c73bf346141ebed1f32f0efb93 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 25 Sep 2018 14:58:22 -0400 Subject: [PATCH 079/203] Increment market data shm version --- src/lib/md/Shm/MDStructure.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/md/Shm/MDStructure.h b/src/lib/md/Shm/MDStructure.h index 4017dc66d..6a069fb60 100644 --- a/src/lib/md/Shm/MDStructure.h +++ b/src/lib/md/Shm/MDStructure.h @@ -26,7 +26,7 @@ namespace md { using utils::TimeStamp; -const uint32_t shm_version = 100; +const uint32_t shm_version = 101; struct ALIGNED_(CACHE_LINE) HotHeader { size_t count; From 4ea9c6c28dc2c3669d63cd5a030e4bf5cd818cbc Mon Sep 17 00:00:00 2001 From: John Colvin Date: Tue, 25 Sep 2018 15:23:04 -0400 Subject: [PATCH 080/203] Export MarketDataAuxiliaryService in ShellServices --- src/client/spark/Daemons/ShellServices.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client/spark/Daemons/ShellServices.cs b/src/client/spark/Daemons/ShellServices.cs index 25a8e9f8e..a57ac2547 100644 --- a/src/client/spark/Daemons/ShellServices.cs +++ b/src/client/spark/Daemons/ShellServices.cs @@ -1,5 +1,6 @@ using bts.api.admin; using bts.api.core; +using bts.api.md.auxiliary; using bts.api.orderman; using bts.api.persistence; using bts.api.position; @@ -40,5 +41,8 @@ namespace Spark { [Export] public PositionService position_service => context.locate(); + + [Export] + public MarketDataAuxiliaryService md_auxiliary_service => context.locate(); } } \ No newline at end of file From 22b1bfa777410674181e77067ec4f6c935730b16 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Tue, 25 Sep 2018 15:37:55 -0400 Subject: [PATCH 081/203] SPARK-1671 Volume at price level (UI) --- .../MarketDataAuxiliaryModel.cs | 33 + .../Modules/Trading/LadderWindowFactory.cs | 8 +- .../Modules/Trading/TradingShellModule.cs | 6 +- .../spark/core/FontsAndColors/BTSColors.cs | 6 + .../GenericCollectionExtensions.cs | 3 - .../spark/core/Utility/RefValueContainer.cs | 36 ++ .../spark/core/Utility/ViewModelBase.cs | 2 +- .../spark/core/data/Proto/ColorShowProto.cs | 47 ++ .../Converters/AnonymousValueConverter.cs | 16 + .../spark/view/Spark/Ladder/LadderGridHost.cs | 66 +- .../spark/view/Spark/Ladder/LadderModel.cs | 5 +- .../spark/view/Spark/Ladder/LadderSettings.cs | 82 ++- .../view/Spark/Ladder/LadderSettingsVM.cs | 117 ++++ .../view/Spark/Ladder/LadderSettingsView.xaml | 587 ++++++++++-------- .../Spark/Ladder/LadderSettingsView.xaml.cs | 21 + .../spark/view/Spark/Ladder/LadderVM.cs | 10 +- .../spark/view/Spark/Ladder/LadderView.xaml | 1 + .../spark/view/Spark/Ladder/LaderRender.cs | 97 +++ src/client/spark/view/Utility/WPFHelper.cs | 3 + 19 files changed, 876 insertions(+), 270 deletions(-) create mode 100644 src/client/spark/MarketDataAuxiliary/MarketDataAuxiliaryModel.cs create mode 100644 src/client/spark/core/Utility/RefValueContainer.cs create mode 100644 src/client/spark/core/data/Proto/ColorShowProto.cs diff --git a/src/client/spark/MarketDataAuxiliary/MarketDataAuxiliaryModel.cs b/src/client/spark/MarketDataAuxiliary/MarketDataAuxiliaryModel.cs new file mode 100644 index 000000000..020a51430 --- /dev/null +++ b/src/client/spark/MarketDataAuxiliary/MarketDataAuxiliaryModel.cs @@ -0,0 +1,33 @@ +using bts.api.core; +using bts.api.md.auxiliary; +using mbts.utils; +using System; +using System.ComponentModel.Composition; +using System.Reactive.Concurrency; + +namespace mbts.spark { + [Export] + class MarketDataAuxiliaryModel : IDisposable { + readonly MarketDataAuxiliaryService _service; + readonly TableUpdateDemuxer _security_orders_demuxer; + IDisposable _dispose; + + [ImportingConstructor] + MarketDataAuxiliaryModel(MarketDataAuxiliaryService service) { + _service = service; + var scheduler = ThreadPoolScheduler.Instance; + var interval = TimeSpan.FromMilliseconds(100); + volume_at_price = service.subscribei_volume_at_price().Naggle(interval, scheduler).Table().Cache(); + _security_orders_demuxer = new TableUpdateDemuxer(o => o.RecordId, v => v.SecurityId); + _dispose = volume_at_price.AddRemoveOnly().Subscribe(_security_orders_demuxer); + } + + public IObservable> volume_at_price { get; } + + public IObservable> security_volume_at_price(uint security) => + _security_orders_demuxer.subscribe(security); + + public void Dispose() => + Disposable.dispose(ref _dispose); + } +} \ No newline at end of file diff --git a/src/client/spark/Modules/Trading/LadderWindowFactory.cs b/src/client/spark/Modules/Trading/LadderWindowFactory.cs index 76a1c32e2..531b50f59 100644 --- a/src/client/spark/Modules/Trading/LadderWindowFactory.cs +++ b/src/client/spark/Modules/Trading/LadderWindowFactory.cs @@ -22,15 +22,17 @@ namespace bts.shell { readonly IWindowManager _wm; readonly UserSettingsHub _settings_hub; readonly OrdersModel _orders_model; + readonly MarketDataAuxiliaryModel _md_auxiliary_model; readonly int _max_ladders_open; IReadOnlyDictionary _securities = new Dictionary(); - public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model) + public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model, MarketDataAuxiliaryModel md_auxiliary_model) : base(owner, "SparkLadder") { _bus = bus; _wm = wm; _settings_hub = settings_hub; _orders_model = orders_model; + _md_auxiliary_model = md_auxiliary_model; var knobs = Configuration.get(); _max_ladders_open = (int)(knobs.max_open_ladders); _security_timeout = TimeSpan.FromSeconds(Math.Max(15, knobs.security_timeout_secs)); @@ -56,7 +58,7 @@ namespace bts.shell { IShellWindow create_ladder_window(SecurityMD md, LadderSettings state_settings) { var id = Interlocked.Increment(ref _ladder_count) % Environment.ProcessorCount; var view = new Lazy(() => { - var model = new LadderModel(md, _orders_model); + var model = new LadderModel(md, _orders_model, _md_auxiliary_model); var settings_manager = new WindowSettingsManager( _settings_hub, state_settings, us => sanitize_ladder_settings(us.ladder), @@ -85,6 +87,8 @@ namespace bts.shell { s.session_high_line = LadderSettings.Default.session_high_line; if (s.session_range_fill == null) s.session_range_fill = LadderSettings.Default.session_range_fill; + if (s.volume_column == null) + s.volume_column = LadderSettings.Default.volume_column; s.row_height = LadderRender.measure_row_height(s); return s; } diff --git a/src/client/spark/Modules/Trading/TradingShellModule.cs b/src/client/spark/Modules/Trading/TradingShellModule.cs index 9924f00ff..7ae0abd6e 100644 --- a/src/client/spark/Modules/Trading/TradingShellModule.cs +++ b/src/client/spark/Modules/Trading/TradingShellModule.cs @@ -1,4 +1,5 @@ using bts.api; +using bts.api.md.auxiliary; using bts.spark; using mbts.persist; using mbts.spark; @@ -19,8 +20,9 @@ namespace bts.shell { PAndLWindowFactory _plwf; [ImportingConstructor] - public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model) { - _lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model); + public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, + UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model, MarketDataAuxiliaryModel md_auxiliary_model) { + _lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model, md_auxiliary_model); _obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model); _fwf = new FillsWindowFactory(this, bus, orders_model); _plwf = new PAndLWindowFactory(this, bus, pandl_model); diff --git a/src/client/spark/core/FontsAndColors/BTSColors.cs b/src/client/spark/core/FontsAndColors/BTSColors.cs index 24d8cbb59..cb52e8c2e 100644 --- a/src/client/spark/core/FontsAndColors/BTSColors.cs +++ b/src/client/spark/core/FontsAndColors/BTSColors.cs @@ -608,6 +608,12 @@ namespace mbts.ui { public static BTSColor positive_diff { get { return Color.FromRgb(0, 90, 50); } } } + public static class LadderSpecial { + public static BTSColor passive_volume_bar { get; } = Colors.SlateBlue; + public static BTSColor total_volume_bar { get; } = 0x009B69; + public static BTSColor default_column_background { get; } = 0xBABABA; + } + // Style Guide Colors public static Color brand_lightest { get { return UI.brand_lightest; } } public static Brush brand_lightest_brush { get { return new SolidColorBrush(brand_lightest); } } diff --git a/src/client/spark/core/GlobalExtensions/GenericCollectionExtensions.cs b/src/client/spark/core/GlobalExtensions/GenericCollectionExtensions.cs index 851d21db9..553db5df5 100644 --- a/src/client/spark/core/GlobalExtensions/GenericCollectionExtensions.cs +++ b/src/client/spark/core/GlobalExtensions/GenericCollectionExtensions.cs @@ -271,9 +271,6 @@ namespace System.Collections.Generic { #endregion #region Max - public static T Max(this IEnumerable enumerable, Func select_value) where T : IComparable => - Max(enumerable, select_value, default(T)); - public static T Max(this IEnumerable enumerable, Func select_value, T return_if_empty) where T : IComparable { var etr = enumerable.GetEnumerator(); if (!etr.MoveNext()) return return_if_empty; diff --git a/src/client/spark/core/Utility/RefValueContainer.cs b/src/client/spark/core/Utility/RefValueContainer.cs new file mode 100644 index 000000000..1938241c0 --- /dev/null +++ b/src/client/spark/core/Utility/RefValueContainer.cs @@ -0,0 +1,36 @@ +namespace mbts.utils { + /// + /// Facilities multi-threaded read/write operations to a particular reference. + /// + /// The reference type to be read/written. + class RefValueContainer where T : class { + readonly object _lock = new object(); + T _value; + bool _is_dirty; + + /// + /// Returns the current value if it has not been read already, otherwise returns null. + /// + public T read_new_value() { + lock (_lock) { + if (_is_dirty) { + _is_dirty = false; + return _value; + } + return null; + } + } + + public void write_value(T value) { + lock (_lock) { + _is_dirty = true; + _value = value; + } + } + + public void mark_dirty() { + lock (_lock) + _is_dirty = true; + } + } +} \ No newline at end of file diff --git a/src/client/spark/core/Utility/ViewModelBase.cs b/src/client/spark/core/Utility/ViewModelBase.cs index 50d8efe5b..4fd8401d2 100644 --- a/src/client/spark/core/Utility/ViewModelBase.cs +++ b/src/client/spark/core/Utility/ViewModelBase.cs @@ -110,7 +110,7 @@ namespace mbts.ui.shell #endregion #region automatic property - protected delegate void PropertyChange(T oldval, T newval); + protected internal delegate void PropertyChange(T oldval, T newval); // use nameof() to add compile time checks protected Property backing(string name, T default_value = default(T)) => diff --git a/src/client/spark/core/data/Proto/ColorShowProto.cs b/src/client/spark/core/data/Proto/ColorShowProto.cs new file mode 100644 index 000000000..12b2acfe2 --- /dev/null +++ b/src/client/spark/core/data/Proto/ColorShowProto.cs @@ -0,0 +1,47 @@ +using bts.utils; +using mbts.ui; +using ProtoBuf; +using System; +using System.Windows.Media; + +namespace bts.proto { + [ProtoContract] + struct ColorShowProto : IEquatable { + [ProtoMember(1)] + readonly uint _value; + [ProtoMember(2)] + readonly bool _show; + + public ColorShowProto(Color value, bool show = true) + : this(value.rgb_color(), show) { } + + public ColorShowProto(uint value, bool show = true) { + _value = value; + _show = show; + } + + public Color color => _value.rgb_color(); + public bool show => _show; + + public Color? result { + get { + if (_show) + return color; + return null; + } + } + + public bool Equals(ColorShowProto other) => + _value == other._value && + _show == other._show; + + public override bool Equals(object obj) => + obj is ColorShowProto ncp && Equals(ncp); + + public override int GetHashCode() => + EqualityComparer.aggregate_hash(_value.GetHashCode(), _show.GetHashCode()); + + public static bool operator ==(ColorShowProto a, ColorShowProto b) => a.Equals(b); + public static bool operator !=(ColorShowProto a, ColorShowProto b) => !a.Equals(b); + } +} \ No newline at end of file diff --git a/src/client/spark/view/Converters/AnonymousValueConverter.cs b/src/client/spark/view/Converters/AnonymousValueConverter.cs index 5b7a396f5..10882f471 100644 --- a/src/client/spark/view/Converters/AnonymousValueConverter.cs +++ b/src/client/spark/view/Converters/AnonymousValueConverter.cs @@ -9,6 +9,9 @@ namespace mbts.ui.converters { public static IMultiValueConverter create(Func convert) => new AnonymousMultiValueConverter(convert); + + public static IMultiValueConverter create(Func convert) => + new AnonymousMultiValueConverter(convert); } public class AnonymousValueConverter : IValueConverter { @@ -50,4 +53,17 @@ namespace mbts.ui.converters { public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); } + + public class AnonymousMultiValueConverter : IMultiValueConverter { + readonly Func _convert; + + public AnonymousMultiValueConverter(Func convert) => _convert = convert; + + public TOut ErrorValue { get; set; } + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) => + ObjectHelper.try_convert_args(values, out TIn1 a, out TIn2 b, out TIn3 c) ? _convert(a, b, c) : ErrorValue; + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index c6c249fd3..16cc3a977 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -1,5 +1,6 @@ using bts; using bts.api; +using bts.api.md.auxiliary; using bts.utils; using mbts.co2; using mbts.ui; @@ -38,9 +39,13 @@ namespace mbts.spark { _bid_vis_tup = new VisualGlyphTuple(), _ask_vis_tup = new VisualGlyphTuple(), _price_vis_tup = new VisualGlyphTuple(), - _last_vis_tup = new VisualGlyphTuple(); + _last_vis_tup = new VisualGlyphTuple(), + _volume_vis_tup = new VisualGlyphTuple(); - IDisposable _settings_sub, _tick_sub; + readonly RefValueContainer> _volume_container = + new RefValueContainer>(); + + IDisposable _settings_sub, _volume_sub, _tick_sub; ScrollViewer _scrollViewer; LadderSettings _settings; LadderVM _vm; @@ -75,7 +80,7 @@ namespace mbts.spark { DataContextChanged += (s, e) => _connect_vm(); } - enum ColumnType { Order, Bid, Price, Ask, Last, None } + enum ColumnType { Order, Bid, Price, Ask, Last, Volume, None } public SecurityImmutable security => _vm != null ? _vm.security : SecurityDataHelper.Defaults.SecurityImmutable; public ICommand RecenterCommand { get; } @@ -145,6 +150,17 @@ namespace mbts.spark { lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Ask), width_adjustment); } + public static readonly DependencyProperty VolumeColumnWidthProperty = + WPFHelper.create_dp( + nameof(VolumeColumnWidth), + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + (o, oldval, newval) => o._adjust_column_width(o.columns.First(c => c.column_type == ColumnType.Volume), (double)newval - (double)oldval), + 50u); + + public uint VolumeColumnWidth { + get => (uint)GetValue(VolumeColumnWidthProperty); + set => SetValue(VolumeColumnWidthProperty, value); + } #region Command public ICommand Command { @@ -293,6 +309,7 @@ namespace mbts.spark { case ColumnType.Bid: new_col_width = (int)bid_column_width; break; case ColumnType.Price: new_col_width = (int)price_column_width; break; case ColumnType.Ask: new_col_width = (int)ask_column_width; break; + case ColumnType.Volume: new_col_width = (int)VolumeColumnWidth; break; default: new_col_width = (int)Math.Max((double)column.settings.width + adjustment, _column_min_width); break; } int true_adj = new_col_width - (int)column.settings.width; @@ -317,6 +334,10 @@ namespace mbts.spark { security.ContractSpec); break; case ColumnType.Ask: render_market_col(_asks); break; + case ColumnType.Volume: + _volume_container.mark_dirty(); + _refresh_volume_at_price(); + break; } if (Array.IndexOf(columns, column) <= Array.IndexOf(columns, _price_column)) { if (_mkt_break_lines.low_render_index.HasValue) @@ -341,25 +362,38 @@ namespace mbts.spark { new Column(_settings.bid_column, ColumnType.Bid, _bid_vis_tup, (x += _settings.orders_column.show ? _settings.orders_column.width : 0)), _price_column = new Column(_settings.price_column, ColumnType.Price, _price_vis_tup, (x += _settings.bid_column.show ? _settings.bid_column.width : 0)), new Column(_settings.ask_column, ColumnType.Ask, _ask_vis_tup, (x += _settings.price_column.show ? _settings.price_column.width : 0)), - new Column(_settings.last_column, ColumnType.Last, _last_vis_tup, (x += _settings.ask_column.show ? _settings.ask_column.width : 0)) + new Column(_settings.volume_column.general, ColumnType.Volume, _volume_vis_tup, (x += _settings.ask_column.show ? _settings.ask_column.width : 0)), + new Column(_settings.last_column, ColumnType.Last, _last_vis_tup, (x += _settings.volume_column.general.show ? _settings.volume_column.general.width : 0)) }; } #endregion void _connect_vm() { - Disposable.dispose(ref _settings_sub); - Disposable.dispose(ref _tick_sub); + _dispose_resources(); _vm = DataContext as LadderVM; if (_vm != null) { + _vm.OnDisposed += _on_vm_disposed; _prices_updated = 0; _price_comparer = Comparer.Create((x, y) => security.ContractSpec.tick_equals(x, y) ? 0 : y.CompareTo(x)); _price_dirty = true; _vm.on_tick += _tick; _settings_sub = _vm.settings.OnDispatcher().Subscribe(_settings_updated); + _volume_sub = _vm.model.md_auxiliary_model.security_volume_at_price(security.Id).Subscribe(t => _volume_container.write_value(t.state.Values)); _tick_sub = Disposable.Create(() => _vm.on_tick -= _tick); } } + void _on_vm_disposed(bool obj) => + _dispose_resources(); + + void _dispose_resources() { + Disposable.dispose(ref _settings_sub); + Disposable.dispose(ref _volume_sub); + Disposable.dispose(ref _tick_sub); + if (_vm != null) + _vm.OnDisposed -= _on_vm_disposed; + } + void _scroll_to_center() { if (_scrollViewer != null && _settings != null) { int index = _price_list != null && !double.IsNaN(_last_origin_price) ? @@ -432,6 +466,7 @@ namespace mbts.spark { LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, security.ContractSpec, _settings.ask_column); } } + _refresh_volume_at_price(); _last_origin_price = _mkt_break_lines.origin; _price_dirty = false; } @@ -474,6 +509,20 @@ namespace mbts.spark { LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, security.ContractSpec, _settings.ask_column); } + /// + /// Conditionally refreshes volume at price (if value has changed since last refresh). + /// + void _refresh_volume_at_price() { + if (!_settings.volume_column.general.show) + return; + var vapls = _volume_container.read_new_value(); + if (vapls != null) { + Log.assert_d(_price_list != null); + int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer); + LadderRender.render_volumes(_volume_vis_tup.visual, _volume_vis_tup.glyph_context, origin_index, _price_list, vapls, _settings.row_height, security.ContractSpec, _settings.volume_column); + } + } + void _refresh_depth() { Log.assert_d(_price_list != null); int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer); @@ -599,6 +648,7 @@ namespace mbts.spark { _ask_vis_tup.glyph_context = glyph_context(s.ask_column); _price_vis_tup.glyph_context = glyph_context(s.price_column); _last_vis_tup.glyph_context = glyph_context(s.last_column); + _volume_vis_tup.glyph_context = glyph_context(s.volume_column.general); GlyphContext glyph_context(LadderColumnSettings cs) => GlyphContext.create(cs.font_family, cs.font_weight); @@ -622,6 +672,10 @@ namespace mbts.spark { case ColumnType.Bid: bid_column_width = col.settings.width; break; case ColumnType.Price: price_column_width = col.settings.width; break; case ColumnType.Ask: ask_column_width = col.settings.width; break; + case ColumnType.Volume: + VolumeColumnWidth = col.settings.width; + _volume_container.mark_dirty(); + break; } } _visualHost.AddVisualChild(_mid_mkt_break_vis); diff --git a/src/client/spark/view/Spark/Ladder/LadderModel.cs b/src/client/spark/view/Spark/Ladder/LadderModel.cs index 78af88464..7576b8374 100644 --- a/src/client/spark/view/Spark/Ladder/LadderModel.cs +++ b/src/client/spark/view/Spark/Ladder/LadderModel.cs @@ -6,10 +6,12 @@ using System.Threading.Tasks; namespace mbts.spark { class LadderModel { readonly OrdersModel _order_gateway; + readonly MarketDataAuxiliaryModel _md_auxiliary_model; - public LadderModel(SecurityMD source, OrdersModel ordergw) { + public LadderModel(SecurityMD source, OrdersModel ordergw, MarketDataAuxiliaryModel md_auxiliary_model) { security_md = source; _order_gateway = ordergw; + _md_auxiliary_model = md_auxiliary_model; } public enum CancelType { All, Bids, Asks } @@ -18,6 +20,7 @@ namespace mbts.spark { public SecurityImmutable security => security_md.security; public MDConsumerHandle md => security_md.md; public OrdersModel order_gateway => _order_gateway; + public MarketDataAuxiliaryModel md_auxiliary_model => _md_auxiliary_model; public Task cancel_orders(CancelType ct) { switch (ct) { diff --git a/src/client/spark/view/Spark/Ladder/LadderSettings.cs b/src/client/spark/view/Spark/Ladder/LadderSettings.cs index e71eca84d..ff58c9d07 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettings.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettings.cs @@ -5,6 +5,8 @@ using ProtoBuf; using System; using System.Linq; using System.Windows; +using bts.proto; +using System.Windows.Media; namespace mbts.spark { // IMPORTANT: BE SURE TO ADD ANY NEW PROPERTIES TO THE EQUALS FUNCTION! @@ -74,6 +76,73 @@ namespace mbts.spark { public static bool operator !=(LadderColumnSettings x, LadderColumnSettings y) => !(x == y); } + [ProtoContract] + class HistogramBarAnimation : IEquatable { + [ProtoMember(1)] + public ColorShowProto bars { get; set; } + [ProtoMember(2)] + public bool show_numbers { get; set; } + + public bool active => bars.show || show_numbers; + + public bool Equals(HistogramBarAnimation other) => + other != null && + bars == other.bars && + show_numbers == other.show_numbers; + + public override bool Equals(object obj) => Equals(obj as HistogramBarAnimation); + public override int GetHashCode() => + EqualityComparer.aggregate_hash( + bars.GetHashCode(), + show_numbers.GetHashCode()); + } + + [ProtoContract] + class VolumeColumnSettings : IEquatable { + public static VolumeColumnSettings Default { + get { + var gen = LadderColumnSettings.Default; + gen.background = BTSColors.LadderSpecial.default_column_background; + return new VolumeColumnSettings { + general = gen, + buy = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.MarketData.bid, false) }, + sell = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.MarketData.ask, false) }, + passive = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.LadderSpecial.passive_volume_bar, false) }, + total = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.LadderSpecial.total_volume_bar), show_numbers = true } + }; + } + } + + [ProtoMember(1)] + public LadderColumnSettings general { get; set; } + [ProtoMember(2)] + public HistogramBarAnimation buy { get; set; } + [ProtoMember(3)] + public HistogramBarAnimation sell { get; set; } + [ProtoMember(4)] + public HistogramBarAnimation passive { get; set; } + [ProtoMember(5)] + public HistogramBarAnimation total{ get; set; } + + public bool Equals(VolumeColumnSettings other) => + other != null && + general.Equals(other.general) && + buy.safe_equals(other.buy) && + sell.safe_equals(other.sell) && + passive.safe_equals(other.passive) && + total.safe_equals(other.total); + + public override bool Equals(object obj) => Equals(obj as VolumeColumnSettings); + + public override int GetHashCode() => + EqualityComparer.aggregate_hash( + general.GetHashCode(), + buy.GetHashCode(), + sell.GetHashCode(), + passive.GetHashCode(), + total.GetHashCode()); + } + [ProtoContract] class LadderMarketBreakLine : IEquatable { [ProtoMember(1)] @@ -116,12 +185,12 @@ namespace mbts.spark { ask_column = LadderColumnSettings.Default, price_column = LadderColumnSettings.Default, last_column = LadderColumnSettings.Default; - orders_column.background = 0xBABABA; + orders_column.background = BTSColors.LadderSpecial.default_column_background; bid_column.foreground = 0xFFFFFF; bid_column.background = BTSColors.MarketData.bid; ask_column.foreground = 0xFFFFFF; ask_column.background = BTSColors.MarketData.ask; - price_column.background = 0xBABABA; + price_column.background = BTSColors.LadderSpecial.default_column_background; last_column.background = 0xFFFFFF; return new LadderSettings { row_height = 20, @@ -131,6 +200,7 @@ namespace mbts.spark { ask_column = ask_column, price_column = price_column, last_column = last_column, + volume_column = VolumeColumnSettings.Default, calculator_buttons = new[] { 1, 5, 10, 25, 50, 100 }, scroll_bar_visible = false, default_order_type = CustomOrderDefinition.CustomOneofCase.Limit, @@ -200,6 +270,8 @@ namespace mbts.spark { public LadderMarketBreakLine session_high_line { get; set; } [ProtoMember(24)] public LadderMarketBreakLine session_range_fill { get; set; } + [ProtoMember(25)] + public VolumeColumnSettings volume_column { get; set; } public bool Equals(LadderSettings other) => other != null && @@ -223,7 +295,8 @@ namespace mbts.spark { alternate_order_size == other.alternate_order_size && session_low_line.safe_equals(other.session_low_line) && session_high_line.safe_equals(other.session_high_line) && - session_range_fill.safe_equals(other.session_range_fill); + session_range_fill.safe_equals(other.session_range_fill) && + volume_column.safe_equals(other.volume_column); public override bool Equals(object obj) => Equals(obj as LadderSettings); @@ -249,6 +322,7 @@ namespace mbts.spark { alternate_order_size.GetHashCode(), EqualityComparer.safe_get_hash_code(session_low_line), EqualityComparer.safe_get_hash_code(session_high_line), - EqualityComparer.safe_get_hash_code(session_range_fill)); + EqualityComparer.safe_get_hash_code(session_range_fill), + EqualityComparer.safe_get_hash_code(volume_column)); } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs index 982e843e1..7df82b865 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs @@ -23,6 +23,7 @@ namespace mbts.spark { _price = new LadderColumnSettingsVM("Price"), _ask = new LadderColumnSettingsVM("Ask"), _last = new LadderColumnSettingsVM("Last"); + VolumeColumnVM _volume = new VolumeColumnVM(); LadderMarketBreakLineVM _bid_mkt_line = new LadderMarketBreakLineVM("Bid"), _ask_mkt_line = new LadderMarketBreakLineVM("Ask"), @@ -73,6 +74,7 @@ namespace mbts.spark { yield return _bid; yield return _price; yield return _ask; + yield return _volume.general; yield return _last; } } @@ -81,7 +83,9 @@ namespace mbts.spark { public LadderColumnSettingsVM bid_column => _bid; public LadderColumnSettingsVM price_column => _price; public LadderColumnSettingsVM ask_column => _ask; + public LadderColumnSettingsVM volume_column => _volume.general; public LadderColumnSettingsVM last_column => _last; + public VolumeColumnVM volume => _volume; public LadderMarketBreakLineVM session_range_fill => _session_range_fill; public ObservableEditableCollection> size_presets { get; } public string size_presets_text => string.Join(", ", size_presets); @@ -106,6 +110,7 @@ namespace mbts.spark { _bid.assign_from(_current.bid_column); _ask.assign_from(_current.ask_column); _price.assign_from(_current.price_column); + _volume.assign_from(_current.volume_column); _last.assign_from(_current.last_column); _bid_mkt_line.assign_from(_current.bid_break_line); _ask_mkt_line.assign_from(_current.ask_break_line); @@ -124,6 +129,7 @@ namespace mbts.spark { clone.bid_column = _bid.get_settings(); clone.price_column = _price.get_settings(); clone.ask_column = _ask.get_settings(); + clone.volume_column = _volume.get_settings(); clone.last_column = _last.get_settings(); clone.bid_break_line = _bid_mkt_line.get_settings(); clone.ask_break_line = _ask_mkt_line.get_settings(); @@ -253,5 +259,116 @@ namespace mbts.spark { show = show }; } + + public class VolumeColumnVM : ViewModelBase { + public VolumeColumnVM() { + buy = new VolumeAtPriceAnimationVM("Buy", new VolumeAtPriceSampleData(this, 112, 62, 174)); + sell = new VolumeAtPriceAnimationVM("Sell", new VolumeAtPriceSampleData(this, 93, 78, 155)); + passive = new VolumeAtPriceAnimationVM("Passive", new VolumeAtPriceSampleData(this, 72, 127, 198)); + total = new VolumeAtPriceAnimationVM("Total", new VolumeAtPriceSampleData(this, 81, 100, 144)); + foreach (var vc in volume_components) + vc.show_numbers_changed += on_show_numbers_changed; + + void on_show_numbers_changed() { + foreach (var vc in volume_components) + vc.sample_data.refresh_numbers_text(); + } + } + + public LadderColumnSettingsVM general { get; } = new LadderColumnSettingsVM("Volume"); + public VolumeAtPriceAnimationVM buy { get; } + public VolumeAtPriceAnimationVM sell { get; } + public VolumeAtPriceAnimationVM passive { get; } + public VolumeAtPriceAnimationVM total { get; } + + public IEnumerable volume_components { + get { + yield return buy; + yield return sell; + yield return passive; + yield return total; + } + } + public double max_total_volume => volume_components.Max(v => v.sample_data.total); + + public void assign_from(VolumeColumnSettings settings) { + general.assign_from(settings.general); + buy.assign_from(settings.buy); + sell.assign_from(settings.sell); + passive.assign_from(settings.passive); + total.assign_from(settings.total); + } + + public VolumeColumnSettings get_settings() => + new VolumeColumnSettings { + general = general.get_settings(), + buy = buy.get_settings(), + sell = sell.get_settings(), + passive = passive.get_settings(), + total = total.get_settings(), + }; + } + + internal class VolumeAtPriceSampleData : ViewModelBase { + readonly VolumeColumnVM _owner; + + public VolumeAtPriceSampleData(VolumeColumnVM owner, uint b, uint s, uint p) { + _owner = owner; + buy = b; + sell = s; + passive = p; + } + + public uint buy { get; } + public uint sell { get; } + public uint passive { get; } + public uint total => buy + sell + passive; + public string numbers_text => string.Join("/", _visible_number); + + IEnumerable _visible_number { + get { + if (_owner.buy.show_numbers) + yield return buy; + if (_owner.sell.show_numbers) + yield return sell; + if (_owner.passive.show_numbers) + yield return passive; + if (_owner.total.show_numbers) + yield return total; + } + } + + public void refresh_numbers_text() => RaisePropertyChanged(nameof(numbers_text)); + } + + public class VolumeAtPriceAnimationVM : ViewModelBase { + readonly Property _show_numbers; + + internal VolumeAtPriceAnimationVM(string name, VolumeAtPriceSampleData sample_data) { + this.name = name; + this.sample_data = sample_data; + _show_numbers = backing(nameof(show_numbers), false); + _show_numbers.Changed += (oldval, newval) => show_numbers_changed?.Invoke(); + } + + public event Action show_numbers_changed; + public string name { get; } + public EnabledValueVM bars { get; } = new EnabledValueVM(); + public ObservableCollection palettes { get; } = new ObservableCollection(ColorPalette.all_presets().Select(p => new ColorPaletteVM(p))); + public bool show_numbers { get => _show_numbers.Value; set => _show_numbers.set(value); } + public VolumeAtPriceSampleData sample_data { get; } + + public void assign_from(HistogramBarAnimation settings) { + bars.enabled = settings.bars.show; + bars.value = settings.bars.color; + show_numbers = settings.show_numbers; + } + + public HistogramBarAnimation get_settings() => + new HistogramBarAnimation { + bars = new bts.proto.ColorShowProto(bars.value, bars.enabled), + show_numbers = show_numbers + }; + } } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml index fa2b377ba..f54c12f72 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml @@ -24,12 +24,27 @@ + + + + @@ -93,261 +108,335 @@ Alternate Order Qty - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs index c95184598..8bcf757e9 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs @@ -1,4 +1,6 @@ using mbts.ui.converters; +using System.ComponentModel; +using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; @@ -9,11 +11,30 @@ namespace mbts.ui.spark { /// partial class LadderSettingsView : UserControl { public const string OrdersColumnSampleTemplateKey = "OrdersColumnSampleTemplate"; + public const double VolumeSampleWidth = 120; public LadderSettingsView() => InitializeComponent(); + public enum VolumeDisplayMode { + Total, + [Description("Buy/Sell")] + SplitBySide + } + public static IMultiValueConverter MktBreakLineBrushConverter = ValueConverter.create((color, show) => show ? new SolidColorBrush(color) : Brushes.Transparent); + + public static IMultiValueConverter VolumeBarWidthConverter => + ValueConverter.create((value, max, container_width) => container_width * (double)value / (double)max); + + public static readonly DependencyProperty VolumeComponentValueProperty = + DependencyProperty.RegisterAttached("VolumeComponentValue", + typeof(uint), + typeof(LadderSettingsView), + new PropertyMetadata(0u)); + + public static uint GetVolumeComponentValue(DependencyObject d) => (uint)d.GetValue(VolumeComponentValueProperty); + public static void SetVolumeComponentValue(DependencyObject d, uint value) => d.SetValue(VolumeComponentValueProperty, value); } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderVM.cs b/src/client/spark/view/Spark/Ladder/LadderVM.cs index 3bc639b4a..87992f338 100644 --- a/src/client/spark/view/Spark/Ladder/LadderVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderVM.cs @@ -19,7 +19,7 @@ using System.Windows.Input; using System.Windows.Media; namespace mbts.spark { - class LadderVM : DisposableViewModelBase, IKeyListener { + class LadderVM : DisposableViewModelBase, IWillLetYouKnowWhenIDispose, IKeyListener { readonly IBus _bus; readonly LadderModel _model; readonly Property _high, _low, _change, _open, _settlement, _position; @@ -167,7 +167,8 @@ namespace mbts.spark { orders.sync_with(_slot_price_vs_orders.Values); }), - WorkLoop.Start($"Ladder '{security}'", _tick) + WorkLoop.Start($"Ladder '{security}'", _tick), + Disposable.Create(() => _on_disposed_impl?.Invoke(true)) ); } @@ -198,6 +199,11 @@ namespace mbts.spark { } } + event Action _on_disposed_impl; + event Action IWillLetYouKnowWhenIDispose.OnDisposed { + add => _on_disposed_impl += value; + remove => _on_disposed_impl -= value; + } public SecurityImmutable security => _model.security; public double high => _high.Value; public double low => _low.Value; diff --git a/src/client/spark/view/Spark/Ladder/LadderView.xaml b/src/client/spark/view/Spark/Ladder/LadderView.xaml index 53164d14e..41526be79 100644 --- a/src/client/spark/view/Spark/Ladder/LadderView.xaml +++ b/src/client/spark/view/Spark/Ladder/LadderView.xaml @@ -445,6 +445,7 @@ + diff --git a/src/client/spark/view/Spark/Ladder/LaderRender.cs b/src/client/spark/view/Spark/Ladder/LaderRender.cs index 337c3fffd..391a2a36d 100644 --- a/src/client/spark/view/Spark/Ladder/LaderRender.cs +++ b/src/client/spark/view/Spark/Ladder/LaderRender.cs @@ -1,5 +1,6 @@ using bts; using bts.api; +using bts.api.md.auxiliary; using mbts.co2; using mbts.ui; using mbts.ui.controls; @@ -155,6 +156,98 @@ namespace mbts.spark { } } + public static void render_volumes( + DrawingVisual dv, + GlyphContext glyph_context, + int origin_index, + double[] prices, + IEnumerable data, + double row_height, + ContractSpecificationImmutable spec, + VolumeColumnSettings settings + ) { + if (!data.Any()) { + _clear_visual(dv); + return; + } + /// create a list of 'render components' (buy/sell/passive). This does not include 'total volume,' which is treated a little differently + /// ( for details) + var components = new List<(Func get_volue, Brush bars, bool show_numbers)>(); + if (settings.buy.active) + components.Add((v => v.BuyVolume, settings.buy.bars.result?.to_brush(), settings.buy.show_numbers)); + if (settings.sell.active) + components.Add((v => v.SellVolume, settings.sell.bars.result?.to_brush(), settings.sell.show_numbers)); + if (settings.passive.active) + components.Add((v => v.PassiveVolume, settings.passive.bars.result?.to_brush(), settings.passive.show_numbers)); + /// + Brush total_brush = null; + if (settings.total.bars.result.HasValue) { + total_brush = new SolidColorBrush(settings.total.bars.result.Value); + } else if (components.Count == 0) { + _clear_visual(dv); + return; + } + double max = data.Max(v => v.Volume); + Brush text_brush = settings.general.foreground.rgb_brush(); + _render_cells(dv, origin_index, row_height, data.Select(d => (d.Price, d)), settings.general, prices, spec, render_cell); + /// local methods + void render_cell(DrawingContext dc, int r, VolumeAtPriceLevelImmutable vap) { + double y = row_height * r; + // render the total first; each component (buy/sell/passive) will be rendered on top of the total + if (total_brush != null) { + render_bar(dc, 0, y, vap.Volume, total_brush, out double _); + } + List numbers_text = new List(); + double x_bar = 0; + foreach ((var get_volume, Brush bars, bool show_numbers) in components) { + uint v = get_volume(vap); + render_bar(dc, x_bar, y, v, bars, out double width); + x_bar += width; + if (show_numbers) + numbers_text.Add(v.ToString()); + } + if (settings.total.show_numbers) { + numbers_text.Add(vap.Volume.ToString()); + } + if (numbers_text.Count > 0) { + string str = string.Join("/", numbers_text); + GlyphRun gr = glyph_context.get_glyph_run(str, settings.general.font_size, settings.general.width, row_height, HorizontalAlignment.Center, 0, VerticalAlignment.Center, y, true); + dc.DrawGlyphRun(text_brush, gr); + } + } + + void render_bar(DrawingContext dc, double x, double y, double v, Brush brush, out double width) { + if (v == 0) { + width = 0; + return; + } + double pct = v / max; + width = pct * settings.general.width; + dc.DrawRectangle(brush, null, new Rect(x, y, width, row_height - 1)); + } + } + + public delegate void RenderCell(DrawingContext dc, int row, T data_item); + + static void _render_cells(DrawingVisual dv, int origin_index, double row_height, IEnumerable<(double price, T value)> data, LadderColumnSettings settings, double[] prices, ContractSpecificationImmutable spec, RenderCell render_cell) { + using (var dc = dv.RenderOpen()) + using (_draw_with_clip(dc, settings, row_height, prices.Length)) { + if (!prices.within_range(origin_index) || data.is_null_or_empty()) + return; + int curr_index = origin_index; + double curr_price = prices[origin_index]; + foreach (var pd in data) { + // prices are in descending order, so get diff from curr_price - price + int tick_diff = TickUtils.normalized_tick_diff(spec, curr_price, pd.price); + curr_index += tick_diff; + curr_price = pd.price; + if (curr_index >= 0 && curr_index < prices.Length) { + render_cell(dc, curr_index, pd.value); + } else break; + } + } + } + internal static void render_last(DrawingVisual dv, GlyphContext glyph_context, NativePriceSize last, int index, double row_height, LadderColumnSettings settings) { using (var dc = dv.RenderOpen()) { var gr = format_text(last.size.ToString(), index, row_height, settings, glyph_context); @@ -182,5 +275,9 @@ namespace mbts.spark { dc.PushClip(new RectangleGeometry(new Rect(0, 0, settings.width, row_height * row_count))); return Disposable.Create(dc.Pop); } + + static void _clear_visual(DrawingVisual dv) { + using (dv.RenderOpen()) { } + } } } \ No newline at end of file diff --git a/src/client/spark/view/Utility/WPFHelper.cs b/src/client/spark/view/Utility/WPFHelper.cs index dad292484..275ac1ea2 100644 --- a/src/client/spark/view/Utility/WPFHelper.cs +++ b/src/client/spark/view/Utility/WPFHelper.cs @@ -38,6 +38,9 @@ namespace mbts.ui { public static DependencyProperty create_dp(string name, PropertyChangedCallback callback, TValue default_value = default) => DependencyProperty.Register(name, typeof(TValue), typeof(TOwner), new PropertyMetadata(default_value, prop_changed_callback(callback))); + public static DependencyProperty create_dp(string name, FrameworkPropertyMetadataOptions options, PropertyChangedCallback callback, TValue default_value = default) => + DependencyProperty.Register(name, typeof(TValue), typeof(TOwner), new FrameworkPropertyMetadata(default_value, options, prop_changed_callback(callback))); + public static DependencyPropertyKey create_ro_dp(string name, TValue default_value = default) => DependencyProperty.RegisterReadOnly(name, typeof(TValue), typeof(TOwner), new PropertyMetadata(default_value)); From d7e2c8783057c89c2287a51dc6a3e1510a1833c1 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 25 Sep 2018 15:56:36 -0400 Subject: [PATCH 082/203] Fix SettlementRecorder --- src/lib/md/MDConsumerHandle.h | 6 ++++-- src/lib/md/SettlementRecorder.cpp | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 9c77d393b..f878ccef7 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -32,8 +32,9 @@ inline void read_direct( const HotDepthEntry& hot, size_t count, PriceSize* bid, PriceSize* ask) { - bts_assert_d(count <= COCONUT_MAX_DEPTH); - count = std::min(count, (size_t)COCONUT_MAX_DEPTH); + + bts_assert_d(count <= COCONUT_MAX_DEPTH); + count = std::min(count, (size_t)COCONUT_MAX_DEPTH); for (size_t x = 0; x < count; ++x) { hot.read_direct(x, &bid[x], &ask[x]); } @@ -42,6 +43,7 @@ inline void read_implied( const HotDepthEntry& hot, size_t count, PriceSize* bid, PriceSize* ask) { + bts_assert_d(count <= COCONUT_MAX_IMPLIED_DEPTH); count = std::min(count, (size_t)COCONUT_MAX_IMPLIED_DEPTH); for (size_t x = 0; x < count; ++x) { diff --git a/src/lib/md/SettlementRecorder.cpp b/src/lib/md/SettlementRecorder.cpp index e2b80b2fa..dfa1eb549 100644 --- a/src/lib/md/SettlementRecorder.cpp +++ b/src/lib/md/SettlementRecorder.cpp @@ -65,7 +65,7 @@ void publish_settles(sqlite3_stmt* select, MDProducerHandle handle) { if (result == SQLITE_ROW) { int64_t mantissa = sqlite3_column_int(select, 0); int exponent = sqlite3_column_int(select, 1); - date = Date(sqlite3_column_int(select, 1)); + date = Date(sqlite3_column_int(select, 2)); handle.update_previous_settlement(TimeStamp::now(), {mantissa,(int8_t)exponent}, date); } } @@ -101,18 +101,18 @@ SettlementRecorder::SettlementRecorder(Platform platform) : sqlite3_exec_nofail(db, "PRAGMA syncronous=0"); // key=(security_key+date) sqlite3_exec_nofail(db, - "CREATE TABLE IF NOT EXISTS settlements (key INTEGER NOT NULL, date INTEGER NOT NULL, mantissa INTEGER NOT NULL, exponent INTEGER NOT NULL, PRIMARY KEY (key,date));"); + "CREATE TABLE IF NOT EXISTS settlements2 (key INTEGER NOT NULL, date INTEGER NOT NULL, mantissa INTEGER NOT NULL, exponent INTEGER NOT NULL, PRIMARY KEY (key,date));"); // Delete any records > 10 days old auto cutoff = datetime::local_now() - boost::gregorian::days(10); auto encoded = datetime::ll_to_date(datetime::ptime_to_ll(cutoff)).to_short(); char query[256]; - sprintf(query, "delete from settlements where date < %i", encoded); + sprintf(query, "delete from settlements2 where date < %i", encoded); sqlite3_exec_nofail(db, query); // statements - _self->select_stmt = prepare_query(db, "select mantissa,exponent,date from settlements where key=? order by date desc limit 2"); - _self->insert_stmt = prepare_query(db, "insert or replace into settlements(key, date, mantissa,exponent) values(?,?,?,?)"); + _self->select_stmt = prepare_query(db, "select mantissa,exponent,date from settlements2 where key=? order by date desc limit 2"); + _self->insert_stmt = prepare_query(db, "insert or replace into settlements2(key, date, mantissa,exponent) values(?,?,?,?)"); } SettlementRecorder::~SettlementRecorder() { } From b17a0b1eeabe0a65f25f69a5b1c09e7e1d04711c Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 26 Sep 2018 09:38:36 -0400 Subject: [PATCH 083/203] Fix logging in Queryserver implied --- src/lib/md/IntPrice.h | 1 + src/lib/md/relay/QueryServer.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/md/IntPrice.h b/src/lib/md/IntPrice.h index ee2d1fc7b..b71f2a68d 100644 --- a/src/lib/md/IntPrice.h +++ b/src/lib/md/IntPrice.h @@ -1,3 +1,4 @@ +#pragma once #include "lib/utils/bts_assert.h" #include "lib/utils/bts_array_size.h" diff --git a/src/lib/md/relay/QueryServer.cpp b/src/lib/md/relay/QueryServer.cpp index 47ea8c4d3..05e08ec41 100644 --- a/src/lib/md/relay/QueryServer.cpp +++ b/src/lib/md/relay/QueryServer.cpp @@ -293,7 +293,7 @@ public: } } } - else { + else if (result == HandleReadResult::BUSY) { LOG(md,Info, "Busy read on handle (implied) {}. Retry on next request", instrument.handle.key()); reply->mutable_channel()->set_time(chan.time()); } From 281b4e10b069c6da69e094fd4eef180ba074951d Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Wed, 26 Sep 2018 12:33:08 -0500 Subject: [PATCH 084/203] functionality matches the spec; I still need to clean up some things --- .../Modules/Market/MarketView/MarketViewVM.cs | 2 +- .../Market/MarketView/MarketViewWindow.cs | 2 +- .../MarketView/MarketViewWindowFactory.cs | 4 +- .../Market/TimeAndSales/TimeAndSalesWindow.cs | 2 +- .../UserSettings/UserSettings.cs | 8 +-- .../Settings/SimpleGridSettingsDialog.xaml | 5 +- .../Extensions/Command/FileDialogCommand.cs | 2 + .../Spark/Utilities/WindowSettingsDialogVM.cs | 50 ++++++++++++------- 8 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs index bc7e580b2..a8adfe848 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs @@ -106,7 +106,7 @@ namespace mbts.spark { }; grid_settings_manager = new SimpleGridSettingsManager(columns, col_settings_flags, row_vms); // this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.) - grid_settings_manager.load_from(UserSettings.factory_defaults.market_view_grid_defaults); + grid_settings_manager.load_from(settings_manager.current_settings()); // _resources.AddRange( state.OnDispatcher().Subscribe(s => { diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs index 404b6a696..18630e795 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs @@ -66,7 +66,7 @@ namespace mbts.spark { // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( - _settings_manager, s => s.market_view_grid_defaults, UserSettings.FileExtensions.GRID, () => new SimpleGridSettingsVM(this.observe_title())); + _settings_manager, s => s.market_view_grid, UserSettings.FileExtensions.GRID, () => new SimpleGridSettingsVM(this.observe_title())); // TODO: bind to main window's Title var dialog = new SimpleGridSettingsDialog { Title = "Market View Grid Settings", DataContext = vm }; vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs index 5c6a194e8..244ebf3a6 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs @@ -37,7 +37,7 @@ namespace bts.shell { IShellWindow _create(StartupData state) { var settings_manager = new WindowSettingsManager(_settings_hub, state.grid, - get: us => us.market_view_grid_defaults, + get: us => us.market_view_grid, set: (us, gs) => us.with(provided_market_view_gridsettings: gs)); return new MarketViewWindow(this, _bus, _orders_model, state, settings_manager); } @@ -72,7 +72,7 @@ namespace bts.shell { public IReadOnlyList sorting_criterion => _sorting_criteria ?? (_sorting_criteria = new List()); public static StartupData Default(SecurityCriteria security_criteria) => - new StartupData(security_criteria, new FilterStates(), DefaultGridSettings); + new StartupData(security_criteria, new FilterStates(), null); static class DefaultColumns { diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs index e2d2330ac..b5064a3ef 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs @@ -75,7 +75,7 @@ namespace mbts.spark { // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( - _settings_manager, s => s.market_view_grid_defaults, UserSettings.FileExtensions.GRID, () => new SimpleGridSettingsVM(this.observe_title())); + _settings_manager, s => s.market_view_grid, UserSettings.FileExtensions.GRID, () => new SimpleGridSettingsVM(this.observe_title())); // TODO: bind to main window's Title var dialog = new SimpleGridSettingsDialog { Title = "Time and Sales Grid Settings", DataContext = vm }; vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs index 9eb9b9e5a..f0eda3dbf 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs @@ -14,7 +14,7 @@ namespace mbts.spark { new UserSettings { _ladder = LadderSettings.Default, _notification = NotificationSettings.Default, - _market_view_grid_defaults = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings + _market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings }; [ProtoMember(1)] @@ -24,7 +24,7 @@ namespace mbts.spark { NotificationSettings _notification; [ProtoMember(5)] - GridSettings _market_view_grid_defaults; + GridSettings _market_view_grid; [ProtoMember(6)] GridSettings _time_and_sales; @@ -32,14 +32,14 @@ namespace mbts.spark { public LadderSettings ladder => _ladder ?? (_ladder = LadderSettings.Default); public NotificationSettings notification => _notification ?? (_notification = NotificationSettings.Default); - public GridSettings market_view_grid_defaults => _market_view_grid_defaults ?? (_market_view_grid_defaults = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings); + public GridSettings market_view_grid => _market_view_grid ?? (_market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings); public GridSettings time_and_sales => _time_and_sales ?? (_time_and_sales = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all)); public UserSettings with(LadderSettings ladder = null, NotificationSettings notification = null, GridSettings provided_market_view_gridsettings = null, GridSettings time_and_sales = null) => new UserSettings { _ladder = ladder ?? _ladder, _notification = notification ?? _notification, - _market_view_grid_defaults = provided_market_view_gridsettings ?? _market_view_grid_defaults, + _market_view_grid = provided_market_view_gridsettings ?? _market_view_grid, _time_and_sales = time_and_sales ?? _time_and_sales }; diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml index 90467ae8c..0c8a224a3 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml @@ -73,6 +73,7 @@ + + (stream)); + } catch (ProtoBuf.ProtoException e) when (e.Message.StartsWith("Invalid wire-type;")) { + BTSMessageBox.Show("This doesn't appear to be the correct settings file for this window.\r\n\r\nPlease try importing a different settings file.", error_msg_title ?? "Import", BTSMessageBoxButtons.OK, System.Windows.MessageBoxImage.Hand, true, false, true); } catch (Exception ex) { BTSMessageBox.Show($"Error importing {type_name ?? "item"} at '{file}': {ex}.", error_msg_title ?? "Import", BTSMessageBoxButtons.OK, System.Windows.MessageBoxImage.Error, true, false, true); } diff --git a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs index 4e8d388ea..7475211ab 100644 --- a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs +++ b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs @@ -12,16 +12,18 @@ namespace mbts.spark { } class WindowSettingsDialogVM : ViewModelBase, IRequestViewClose where T : class, IEquatable where TVM : IWindowSettingsVM { - readonly Property _default_tab_selected, _use_default; - readonly DialogSkirtItem _sync_to_factory_item, _sync_to_default_item, _sync_to_instance_item, _ok_item, _apply_item, _cancel_item; + readonly Property /*_default_tab_selected,*/ _use_default; + readonly DialogSkirtItem _sync_to_factory_item, _sync_to_default_item, _sync_to_instance_item, _ok_item, _apply_item, _cancel_item, _apply_to_defaults_item; readonly DelegateProperty> _left_skirt_items; public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext, Func vm_factory) { - var settings = settings_manager.current_settings(); - _default_tab_selected = backing(nameof(default_tab_selected), settings_manager.instance_settings == null); - _use_default = backing(nameof(use_default), default_tab_selected); + //var settings = settings_manager.current_settings(); + //_default_tab_selected = backing(nameof(default_tab_selected), settings_manager.instance_settings == null); + // by default, each new window will use their own instance settings + _use_default = backing(nameof(use_default), settings_manager.instance_settings == null); default_vm = vm_factory(); default_vm.load(settings_manager.default_settings); + // instance vm is what will always be visible on the screen instance_vm = vm_factory(); instance_vm.load(settings_manager.current_settings()); // items @@ -34,15 +36,15 @@ namespace mbts.spark { export_to_custom_cmd.RaiseCanExecuteChanged(); }; action_menu_items = new IMenuItemVM[] { - new MenuItemVM("Import File to Default...", FileDialogCommand.import_proto(s => default_vm.load(s), null, settings_file_ext), Icons.Import, import_group), - new MenuItemVM("Import File to Custom...", import_to_custom_cmd, Icons.Import, import_group), - new MenuItemVM("Export Default to File...", FileDialogCommand.export_proto(() => default_vm.get_result(), filter_items: file_filter), Icons.Export, export_group), - new MenuItemVM("Export Custom to File...", export_to_custom_cmd, Icons.Export, export_group) + /*new MenuItemVM("Import File to Default...", FileDialogCommand.import_proto(s => default_vm.load(s), null, settings_file_ext), Icons.Import, import_group),*/ + new MenuItemVM("Import Settings File ...", import_to_custom_cmd, Icons.Import, import_group), + /*new MenuItemVM("Export Default to File...", FileDialogCommand.export_proto(() => default_vm.get_result(), filter_items: file_filter), Icons.Export, export_group),*/ + new MenuItemVM("Export Settings File...", export_to_custom_cmd, Icons.Export, export_group) }; - _left_skirt_items = backing(nameof(left_skirt_items), left_skirt_factory, nameof(default_tab_selected)); + _left_skirt_items = backing(nameof(left_skirt_items), left_skirt_factory); _sync_to_factory_item = new DialogSkirtItem( "Reset to Factory", - new DelegateCommand(() => (default_tab_selected ? default_vm : instance_vm).load(get_factory_settings(UserSettings.factory_defaults))), + new DelegateCommand(() => instance_vm.load(get_factory_settings(UserSettings.factory_defaults))), ui.spark.LadderSettingsDialog.ResetToFactoryKey); _sync_to_default_item = new DialogSkirtItem( "Copy from Default", @@ -54,23 +56,35 @@ namespace mbts.spark { ui.spark.LadderSettingsDialog.CopyFromOtherKey); _apply_item = new DialogSkirtItem("Apply", new DelegateCommand(apply)); _ok_item = new DialogSkirtItem("OK", new DelegateCommand(() => { + use_default = false; apply(); close(); })) { is_default = true }; _cancel_item = new DialogSkirtItem("Cancel", new DelegateCommand(close)) { is_cancel = true }; + _apply_to_defaults_item = new DialogSkirtItem("Save As Defaults", new DelegateCommand(() => { + use_default = true; + apply(); + close(); + }), ui.spark.LadderSettingsDialog.ResetToFactoryKey); // local methods void apply() { - var my_default = default_vm.get_result(); - if (!my_default.safe_equals(settings_manager.default_settings)) - settings_manager.save_default_settings(my_default).ignore(); if (!use_default) { settings_manager.instance_settings = instance_vm.get_result(); - } else settings_manager.instance_settings = null; + } else { + settings_manager.instance_settings = null; + + var my_default = instance_vm.get_result();// default_vm.get_result(); + if (!my_default.safe_equals(settings_manager.default_settings)) + settings_manager.save_default_settings(my_default).ignore(); + + } + } void close() => RequestClose?.Invoke(this, new EventArgs()); IEnumerable left_skirt_factory() { yield return _sync_to_factory_item; - yield return default_tab_selected ? _sync_to_instance_item : _sync_to_default_item; + yield return _apply_to_defaults_item; + //yield return default_tab_selected ? _sync_to_instance_item : _sync_to_default_item; }; } @@ -87,13 +101,15 @@ namespace mbts.spark { yield return _cancel_item; } } - public bool default_tab_selected { get => _default_tab_selected.Value; set => _default_tab_selected.Value = value; } + //public bool default_tab_selected { get => _default_tab_selected.Value; set => _default_tab_selected.Value = value; } public bool use_default { get => _use_default.Value; set { _use_default.Value = value; + /* if (value) default_tab_selected = true; + */ } } } From 7259397e023ab6cb371e102c83278830bdbae0aa Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 26 Sep 2018 13:47:01 -0400 Subject: [PATCH 085/203] Move SecurityTests to spark_test --- .../mcore => spark_test/Security}/SecurityTests.cs | 14 +++++++------- .../Tests => spark_test/Utils}/MakePublic.cs | 0 src/lib/md/PriceSize.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/client/{spark/Tests/mcore => spark_test/Security}/SecurityTests.cs (73%) rename src/client/{spark/Tests => spark_test/Utils}/MakePublic.cs (100%) diff --git a/src/client/spark/Tests/mcore/SecurityTests.cs b/src/client/spark_test/Security/SecurityTests.cs similarity index 73% rename from src/client/spark/Tests/mcore/SecurityTests.cs rename to src/client/spark_test/Security/SecurityTests.cs index 041b0f644..63104702a 100644 --- a/src/client/spark/Tests/mcore/SecurityTests.cs +++ b/src/client/spark_test/Security/SecurityTests.cs @@ -1,14 +1,14 @@ -using NUnit.Framework; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using bts.api; +using bts.test; namespace bts.mcore { - [TestFixture] + [TestGroup] class SecurityTests { [Test] public void test_security_equal() { @@ -16,10 +16,10 @@ namespace bts.mcore SecurityImmutable b = new Security() { Id = 42 }; SecurityImmutable c = new Security() { Id = 4 }; - Assert.True(a == b); - Assert.True(a.Equals(b)); - Assert.False(a == c); - Assert.False(a.Equals(c)); + Assert.IsTrue(a == b); + Assert.IsTrue(a.Equals(b)); + Assert.IsFalse(a == c); + Assert.IsFalse(a.Equals(c)); Dictionary dict = new Dictionary(); dict[a] = 1; diff --git a/src/client/spark/Tests/MakePublic.cs b/src/client/spark_test/Utils/MakePublic.cs similarity index 100% rename from src/client/spark/Tests/MakePublic.cs rename to src/client/spark_test/Utils/MakePublic.cs diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 00cecba66..0c87bd6cc 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -146,7 +146,7 @@ struct ImpliedPriceLevel : private PriceLevel { return PriceSize(_ask, 0, ImpliedSize(_ask_size)); } }; -static_assert(sizeof(ImpliedPriceLevel) == 24, "PriceLevel should be small"); +static_assert(sizeof(ImpliedPriceLevel) == sizeof(PriceLevel), "ImpliedPriceLevel should not be bigger than PriceLevel"); }}//namespace From 65915a97843c8e72c51ef543bce10a40df0a2c04 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 26 Sep 2018 13:50:20 -0400 Subject: [PATCH 086/203] Enable SyncronizeCollectionTests --- .../core/GlobalExtensions/GlobalExtensions.cs | 34 ----------------- .../spark_core/utils/GlobalExtensions.cs | 37 ++++++++++++++++++- .../Collections}/SyncronizeCollectionTests.cs | 6 +-- 3 files changed, 39 insertions(+), 38 deletions(-) rename src/client/{spark/Tests/mcore => spark_test/Collections}/SyncronizeCollectionTests.cs (96%) diff --git a/src/client/spark/core/GlobalExtensions/GlobalExtensions.cs b/src/client/spark/core/GlobalExtensions/GlobalExtensions.cs index 944ce0305..1bdd74b4d 100644 --- a/src/client/spark/core/GlobalExtensions/GlobalExtensions.cs +++ b/src/client/spark/core/GlobalExtensions/GlobalExtensions.cs @@ -153,40 +153,6 @@ namespace mbts return rv.ToDictionary(kv => kv.Key, kv => kv.Value); } - //this overloaded declaration, (as opposed to one function with comp = null) allows observable_collection.sync_with to be passed into functions as a parameterless Action - public static void sync_with(this IList dest, IEnumerable src) { dest.sync_with(src, EqualityComparer.Default); } - public static void sync_with(this IList dest, IEnumerable src, IEqualityComparer comp) { - Log.notnull(dest); - if (src == null) { - dest.Clear(); - return; - } - var srcList = src as IList ?? src.ToList(); - var changes = bts.utils.Diff.diff(dest, srcList, comp).ToList(); - // Apply the diff - for(int i = 0; i < changes.Count; ++i) { - var c = changes[i]; - // removes - for (var x = 0; x < c.deletedLeft; ++x) { - dest.RemoveAt(c.StartLeft); - } - // inserts - for (var x = 0; x < c.insertedRight; ++x) { - dest.Insert(c.StartLeft + x, srcList[c.StartRight + x]); - } - - // Adjust start index of remaining changes - for (int ii = i + 1; ii < changes.Count; ++ii) { - var cc = changes[ii]; - var delta = c.insertedRight - c.deletedLeft; - if (cc.StartLeft >= c.StartLeft) { - cc.StartLeft += delta; - changes[ii] = cc;//remember: struct - } - } - } - } - public static bool sync_with_setwise(this ObservableCollection dest, IEnumerable src) { return dest.sync_with_setwise(src, EqualityComparer.Default); } public static bool sync_with_setwise(this ObservableCollection dest, IEnumerable src, IEqualityComparer comp) { if (!src.is_distinct(comp)) { diff --git a/src/client/spark_core/utils/GlobalExtensions.cs b/src/client/spark_core/utils/GlobalExtensions.cs index 3c05994a1..9eb55b5fa 100644 --- a/src/client/spark_core/utils/GlobalExtensions.cs +++ b/src/client/spark_core/utils/GlobalExtensions.cs @@ -1,4 +1,5 @@ -using System; +using bts; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -37,6 +38,40 @@ namespace mbts { } public static DateTime lldatetime(this long ll) { return ((ulong)ll).lldatetime(); } + + //this overloaded declaration, (as opposed to one function with comp = null) allows observable_collection.sync_with to be passed into functions as a parameterless Action + public static void sync_with(this IList dest, IEnumerable src) { dest.sync_with(src, EqualityComparer.Default); } + public static void sync_with(this IList dest, IEnumerable src, IEqualityComparer comp) { + Log.notnull(dest); + if (src == null) { + dest.Clear(); + return; + } + var srcList = src as IList ?? src.ToList(); + var changes = bts.utils.Diff.diff(dest, srcList, comp).ToList(); + // Apply the diff + for(int i = 0; i < changes.Count; ++i) { + var c = changes[i]; + // removes + for (var x = 0; x < c.deletedLeft; ++x) { + dest.RemoveAt(c.StartLeft); + } + // inserts + for (var x = 0; x < c.insertedRight; ++x) { + dest.Insert(c.StartLeft + x, srcList[c.StartRight + x]); + } + + // Adjust start index of remaining changes + for (int ii = i + 1; ii < changes.Count; ++ii) { + var cc = changes[ii]; + var delta = c.insertedRight - c.deletedLeft; + if (cc.StartLeft >= c.StartLeft) { + cc.StartLeft += delta; + changes[ii] = cc;//remember: struct + } + } + } + } } } diff --git a/src/client/spark/Tests/mcore/SyncronizeCollectionTests.cs b/src/client/spark_test/Collections/SyncronizeCollectionTests.cs similarity index 96% rename from src/client/spark/Tests/mcore/SyncronizeCollectionTests.cs rename to src/client/spark_test/Collections/SyncronizeCollectionTests.cs index 5eb1d2d79..7389f39e0 100644 --- a/src/client/spark/Tests/mcore/SyncronizeCollectionTests.cs +++ b/src/client/spark_test/Collections/SyncronizeCollectionTests.cs @@ -1,5 +1,4 @@ -using NUnit.Framework; -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; @@ -8,10 +7,11 @@ using System.Text; using System.Threading.Tasks; using mbts; using System.Threading; +using bts.test; namespace bts.mcore { - [TestFixture] + [TestGroup] class SyncronizeCollectionTests { private void swap(List list, int a, int b) { From 4002f80ede405c6543b732e6b1a4e3c72c21e464 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 26 Sep 2018 13:42:48 -0400 Subject: [PATCH 087/203] UserSettingsHub.current getter should be concurrent --- .../UserSettings/UserSettingsHub.cs | 18 +++++++++++++----- .../WindowSettings/IWindowSettingsManager.cs | 3 +-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettingsHub.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettingsHub.cs index 7d8219af5..be09f4632 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettingsHub.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettingsHub.cs @@ -16,7 +16,8 @@ namespace mbts.persist { const string NAME_SPACE = "settings", DEFAULT_KEY = "default"; IUserPersist _persist; UserSettings _default_settings; - readonly Subject _default_settings_subject = new Subject(); + readonly BehaviorSubject _default_settings_subject = new BehaviorSubject(UserSettings.factory_defaults); + readonly object _default_settings_lock = new object(); [ImportingConstructor] public UserSettingsHub(IUserPersist persist) { @@ -33,7 +34,12 @@ namespace mbts.persist { /// Assuming there aren't any rogue agents saving UserSettings by themselves, there should never be any discrepancies. public IObservable subscribe_default_settings() => _default_settings_subject.AsObservable(); - internal UserSettings current => _default_settings ?? UserSettings.factory_defaults; + internal UserSettings current { + get { + lock (_default_settings_lock) + return _default_settings ?? UserSettings.factory_defaults; + } + } public async Task save_default(UserSettings value) { _set_and_notify(value); @@ -41,9 +47,11 @@ namespace mbts.persist { } void _set_and_notify(UserSettings value) { - _default_settings = value; - // notify observers before persisting so the changes are reflected immediately in the ui - _default_settings_subject.OnNext(value); + lock (_default_settings_lock) { + _default_settings = value; + // notify observers before persisting so the changes are reflected immediately in the ui + _default_settings_subject.OnNext(value); + } } } } \ No newline at end of file diff --git a/src/client/spark/core/ShellInterface/UserSettings/WindowSettings/IWindowSettingsManager.cs b/src/client/spark/core/ShellInterface/UserSettings/WindowSettings/IWindowSettingsManager.cs index e6734260f..b1b8e19ba 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/WindowSettings/IWindowSettingsManager.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/WindowSettings/IWindowSettingsManager.cs @@ -54,8 +54,7 @@ namespace mbts.persist { Observable.CombineLatest( Observable.FromEvent, T>(cb => wsm.instance_settings_changed += cb, cb => wsm.instance_settings_changed -= cb) .StartWith(wsm.instance_settings), - wsm.subscribe_default() - .StartWith(wsm.default_settings), + wsm.subscribe_default(), (i, d) => i ?? d ).Where(v => v != null) .DistinctUntilChanged(); From 5af68ccfd02830e6af06dee1cea4b6e3398dc270 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 26 Sep 2018 14:04:39 -0400 Subject: [PATCH 088/203] SPARK-1738 ladders using default settings should clone the settings object, instead of sharing the same one --- .../WindowSettings/IWindowSettingsManager.cs | 45 +++++++++++++++++++ .../spark/view/Spark/Ladder/LadderGridHost.cs | 39 ++++++++++------ .../spark/view/Spark/Ladder/LadderVM.cs | 4 +- 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/client/spark/core/ShellInterface/UserSettings/WindowSettings/IWindowSettingsManager.cs b/src/client/spark/core/ShellInterface/UserSettings/WindowSettings/IWindowSettingsManager.cs index b1b8e19ba..7fca8e3e9 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/WindowSettings/IWindowSettingsManager.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/WindowSettings/IWindowSettingsManager.cs @@ -1,7 +1,9 @@ using mbts.spark; +using mbts.utils; using System; using System.Linq; using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Threading.Tasks; namespace mbts.persist { @@ -11,8 +13,15 @@ namespace mbts.persist { event Action instance_settings_changed; IObservable subscribe_default(); Task save_default_settings(T value); + IWindowSettingsManagerNonShared as_non_shared(); } + /// + /// An implementation of IWindowSettingsManager that uses a clone of the actual default settings; + /// provides a convenient way to avoid problems that arise from sharing a single default settings object among multiple window instances. + /// + interface IWindowSettingsManagerNonShared : IWindowSettingsManager, IDisposable where T : class { } + class WindowSettingsManager : IWindowSettingsManager where T : class { readonly UserSettingsHub _hub; readonly Func _get; @@ -43,6 +52,42 @@ namespace mbts.persist { public IObservable subscribe_default() => _hub.subscribe_default_settings().Select(_get).DistinctUntilChanged(); + + public IWindowSettingsManagerNonShared as_non_shared() => + new NonSharedWrapper(this); + + class NonSharedWrapper : IWindowSettingsManagerNonShared { + readonly WindowSettingsManager _wsm; + readonly BehaviorSubject _default_subject; + IDisposable _dispose; + + public NonSharedWrapper(WindowSettingsManager wsm) { + _wsm = wsm; + _default_subject = new BehaviorSubject(_wsm.default_settings ?? wsm._get(UserSettings.factory_defaults)); + _dispose = wsm.subscribe_default().Subscribe(s => + _default_subject.OnNext(default_settings = MiscUtils.clone_proto(s))); + } + + public T instance_settings { + get => _wsm.instance_settings; + set => _wsm.instance_settings = value; + } + public T default_settings { get; private set; } + public event Action instance_settings_changed { + add => _wsm.instance_settings_changed += value; + remove => _wsm.instance_settings_changed -= value; + } + public async Task save_default_settings(T value) => + await _wsm.save_default_settings(value); + + public IObservable subscribe_default() => + _default_subject.AsObservable(); + + public IWindowSettingsManagerNonShared as_non_shared() => + new NonSharedWrapper(_wsm); + + public void Dispose() => Disposable.dispose(ref _dispose); + } } static class WindowSettingsManagerExtensions { diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index 16cc3a977..240a52ab6 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -664,20 +664,33 @@ namespace mbts.spark { _visualHost.AddVisualChild(_gridvis); // Orders column orders_column_width = s.orders_column.width; - _ordershost.update_settings(_price_list, _price_comparer, s.row_height, s.orders_column); - foreach (var col in columns) - if (col.settings.show && col.visual_tup != null) { - _visualHost.AddVisualChild(col.visual_tup.visual); - switch (col.column_type) { - case ColumnType.Bid: bid_column_width = col.settings.width; break; - case ColumnType.Price: price_column_width = col.settings.width; break; - case ColumnType.Ask: ask_column_width = col.settings.width; break; - case ColumnType.Volume: - VolumeColumnWidth = col.settings.width; - _volume_container.mark_dirty(); - break; + if (init) { + _ordershost.update_settings(_price_list, _price_comparer, s.row_height, s.orders_column); + foreach (var col in columns) + if (col.settings.show && col.visual_tup != null) { + _visualHost.AddVisualChild(col.visual_tup.visual); + switch (col.column_type) { + case ColumnType.Bid: bid_column_width = col.settings.width; break; + case ColumnType.Price: price_column_width = col.settings.width; break; + case ColumnType.Ask: ask_column_width = col.settings.width; break; + case ColumnType.Volume: + VolumeColumnWidth = col.settings.width; + _volume_container.mark_dirty(); + break; + } } - } + } else { + // don't adjust column widths after init + foreach (var col in columns) + if (col.settings.show && col.visual_tup != null) { + _visualHost.AddVisualChild(col.visual_tup.visual); + switch (col.column_type) { + case ColumnType.Volume: + _volume_container.mark_dirty(); + break; + } + } + } _visualHost.AddVisualChild(_mid_mkt_break_vis); _visualHost.AddVisualChild(_ask_mkt_break_vis); _visualHost.AddVisualChild(_bid_mkt_break_vis); diff --git a/src/client/spark/view/Spark/Ladder/LadderVM.cs b/src/client/spark/view/Spark/Ladder/LadderVM.cs index 87992f338..c3ab987c8 100644 --- a/src/client/spark/view/Spark/Ladder/LadderVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderVM.cs @@ -36,7 +36,8 @@ namespace mbts.spark { _model = model; _equality = TickUtils.tick_equality_comparer(_model.security.ContractSpec); _slot_price_vs_orders = new Dictionary(_equality); - _settings_manager = settings_manager; + var non_shared_wsm = settings_manager.as_non_shared(); + _settings_manager = non_shared_wsm; order_properties_vm = new OrderPropertiesEditorVM(_model.order_gateway, Observable.Return(_model.security_md), _settings_manager.subscribe_current().Select(s => new InlineOrderProperties(_model.order_gateway, s))); _high = backing(nameof(high), double.NaN); _low = backing(nameof(low), double.NaN); @@ -77,6 +78,7 @@ namespace mbts.spark { _dispose = Disposable.Create( settings_command, + non_shared_wsm, order_properties_vm, order_properties_vm.observe_account(true).Select(a => a.name).Subscribe(account_positions), account_positions, From 28307b0681ce691dd1bc03e9a4a1194bb0914c07 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 26 Sep 2018 14:45:59 -0400 Subject: [PATCH 089/203] LadderSettingsView.xaml.cs - remove unused field --- src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs index 8bcf757e9..924287cf7 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs @@ -11,7 +11,6 @@ namespace mbts.ui.spark { /// partial class LadderSettingsView : UserControl { public const string OrdersColumnSampleTemplateKey = "OrdersColumnSampleTemplate"; - public const double VolumeSampleWidth = 120; public LadderSettingsView() => InitializeComponent(); From e51d7de3093fb3360185bd8dd29a9e6019b90248 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Wed, 26 Sep 2018 14:57:31 -0500 Subject: [PATCH 090/203] Freezing all icons; changing Picker icons; misc refactoring --- .../spark/Daemons/SecurityPickerDaemon.cs | 4 +- src/client/spark/Images/Icons.cs | 125 +++++++++--------- src/client/spark/Images/Vector/Icons.xaml | 42 +++++- .../Modules/Settings/SettingsShellModule.cs | 2 +- src/client/spark/core/Bus/SparkCommands.cs | 8 +- .../Settings/SimpleGridSettingsDialog.xaml | 33 ----- .../Spark/Utilities/WindowSettingsDialogVM.cs | 55 +++----- 7 files changed, 127 insertions(+), 142 deletions(-) diff --git a/src/client/spark/Daemons/SecurityPickerDaemon.cs b/src/client/spark/Daemons/SecurityPickerDaemon.cs index 5b066f206..81aad4a78 100644 --- a/src/client/spark/Daemons/SecurityPickerDaemon.cs +++ b/src/client/spark/Daemons/SecurityPickerDaemon.cs @@ -53,8 +53,8 @@ namespace bts { } // void handle_bad_icon(string icon_error_desc) { - Log.Api.warn($"{nameof(SecurityPickerDaemon)} received a message '{message.title}' with {icon_error_desc}, will use {nameof(Icons.AdminFrozen)}."); - icon = Icons.AdminFrozen; + Log.Api.warn($"{nameof(SecurityPickerDaemon)} received a message '{message.title}' with {icon_error_desc}, will use {nameof(Icons.Admin)}."); + icon = Icons.Admin; } // var cpv = new ContractPickerView(vm); diff --git a/src/client/spark/Images/Icons.cs b/src/client/spark/Images/Icons.cs index 944f73258..26745cee3 100644 --- a/src/client/spark/Images/Icons.cs +++ b/src/client/spark/Images/Icons.cs @@ -16,11 +16,6 @@ namespace mbts.ui { ToolsGeometryKey = nameof(ToolsGeometryKey), DraggableGeometryKey = nameof(DraggableGeometryKey); - static Icons() { - // TODO: probably do this for all icons, only reason for holding back is the uncertainity of any unexpected impacts - AdminFrozen = Admin.frozen(); - } - public static Geometry PlayGeometry => Geometry.Parse(_get_geometric_path(PlayGeometryKey).FirstOrDefault() ?? string.Empty); static readonly string base_uri = "pack://application:,,,/Images/Icons/"; @@ -35,37 +30,40 @@ namespace mbts.ui { return new BitmapImage(uri(filename)); } + private static ImageSource load_resource_and_freeze(string resource_name) { + return ((DrawingImage)_resources[resource_name]).frozen(); + } + #pragma warning disable CS0618 // Type or member is obsolete public static ImageSource Logo { get { return load_image("logo.ico"); } } - public static ImageSource Admin { get { return (DrawingImage)_resources["Admin"]; } } - public static ImageSource AdminFrozen { get; } - public static ImageSource Analytics { get { return (DrawingImage)_resources["Analytics"]; } } - public static ImageSource Sheets { get { return (DrawingImage)_resources["Sheets"]; } } - public static ImageSource Trades { get { return (DrawingImage)_resources["Trades"]; } } - public static ImageSource Depth { get { return (DrawingImage)_resources["Depth"]; } } - public static ImageSource Ladder { get { return (DrawingImage)_resources["Ladder"]; } } + public static ImageSource Admin => load_resource_and_freeze("Admin"); + public static ImageSource Analytics => load_resource_and_freeze("Analytics"); + public static ImageSource Sheets => load_resource_and_freeze("Sheets"); + public static ImageSource Trades => load_resource_and_freeze("Trades"); + public static ImageSource Depth => load_resource_and_freeze("Depth"); + public static ImageSource Ladder => load_resource_and_freeze("Ladder"); public static ImageSource SheetsRed { get { return load_image("SheetsRed.png"); } } public static ImageSource SheetsGreen { get { return load_image("SheetsGreen.png"); } } - public static ImageSource Spreads { get { return (DrawingImage)_resources["Spreads"]; } } + public static ImageSource Spreads => load_resource_and_freeze("Spreads"); public static ImageSource BlueSpreads { get { return load_image("BlueSpread.ico"); } } - public static ImageSource Reports { get { return (DrawingImage)_resources["Reports"]; } } - public static ImageSource Risk { get { return (DrawingImage)_resources["Risk"]; } } - public static ImageSource Eye { get { return (DrawingImage)_resources["Eye"]; } } - public static ImageSource Coins { get { return (DrawingImage)_resources["Coins"]; } } + public static ImageSource Reports => load_resource_and_freeze("Reports"); + public static ImageSource Risk => load_resource_and_freeze("Risk"); + public static ImageSource Eye => load_resource_and_freeze("Eye"); + public static ImageSource Coins => load_resource_and_freeze("Coins"); public static ImageSource DataCopy { get { return load_image("DataCopy.ico"); } } public static ImageSource Refresh { get { return load_image("Refresh.png"); } } public static ImageSource FavoriteWindow { get { return load_image("FavoriteWindow.png"); } } - public static ImageSource Browser => (DrawingImage)_resources["Browser"]; + public static ImageSource Browser => load_resource_and_freeze("Browser"); public static ImageSource Date { get { return load_image("Date.png"); } } - public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; } } + public static ImageSource Cube3d => load_resource_and_freeze("Cube"); public static ImageSource Algo { get { return Cube3d; } } public static ImageSource Quote { get { return Math; } } - public static ImageSource Math { get { return (DrawingImage)_resources["Surface"]; } } + public static ImageSource Math => load_resource_and_freeze("Surface"); public static ImageSource Abacus { get { return load_image("Abacus.png"); } } public static ImageSource Delete { get { return load_image("Delete.png"); } } @@ -77,58 +75,61 @@ namespace mbts.ui { #pragma warning restore CS0618 // Vector icons - public static ImageSource Bookmarks { get { return (DrawingImage)_resources["Bookmarks"]; } } - public static ImageSource Window { get { return (DrawingImage)_resources["Window"]; } } - public static ImageSource ActiveWindow { get { return (DrawingImage)_resources["ActiveWindow"]; } } - public static ImageSource Trash { get { return (DrawingImage)_resources["Trash"]; } } - public static ImageSource Trash2 { get { return (DrawingImage)_resources["Trash2"]; } } - public static ImageSource Crucible { get { return (DrawingImage)_resources["Crucible"]; } } + public static ImageSource Bookmarks => load_resource_and_freeze("Bookmarks"); + public static ImageSource Window => load_resource_and_freeze("Window"); + public static ImageSource ActiveWindow => load_resource_and_freeze("ActiveWindow"); + public static ImageSource Trash => load_resource_and_freeze("Trash"); + public static ImageSource Trash2 => load_resource_and_freeze("Trash2"); + public static ImageSource Crucible => load_resource_and_freeze("Crucible"); - public static ImageSource Accounting { get { return (DrawingImage)_resources["Accounting"]; } } - public static ImageSource Add { get { return (DrawingImage)_resources["Add"]; } } - public static ImageSource AddEncircled { get { return (DrawingImage)_resources["AddEncircled"]; } } - public static ImageSource Beaker { get { return (DrawingImage)_resources["Beaker"]; } } - public static ImageSource Book { get { return (DrawingImage)_resources["Book"]; } } + public static ImageSource Accounting => load_resource_and_freeze("Accounting"); + public static ImageSource Add => load_resource_and_freeze("Add"); + public static ImageSource AddEncircled => load_resource_and_freeze("AddEncircled"); + public static ImageSource Beaker => load_resource_and_freeze("Beaker"); + public static ImageSource Book => load_resource_and_freeze("Book"); public static ImageSource Orders => create_icon(OrdersGeometryKey); - public static ImageSource AddOrders { get { return (DrawingImage)_resources["AddOrders"]; } } - public static ImageSource CancelOrders { get { return (DrawingImage)_resources["CancelOrders"]; } } - public static ImageSource CancelBuys { get { return (DrawingImage)_resources["CancelBuys"]; } } - public static ImageSource CancelSells { get { return (DrawingImage)_resources["CancelSells"]; } } - public static ImageSource CancelSelected { get { return (DrawingImage)_resources["CancelSelected"]; } } + public static ImageSource AddOrders => load_resource_and_freeze("AddOrders"); + public static ImageSource CancelOrders => load_resource_and_freeze("CancelOrders"); + public static ImageSource CancelBuys => load_resource_and_freeze("CancelBuys"); + public static ImageSource CancelSells => load_resource_and_freeze("CancelSells"); + public static ImageSource CancelSelected => load_resource_and_freeze("CancelSelected"); - public static ImageSource LadderCancelOrders { get { return (DrawingImage)_resources["LadderCancelOrders"]; } } - public static ImageSource LadderCancelBuys { get { return (DrawingImage)_resources["LadderCancelBuys"]; } } - public static ImageSource LadderCancelSells { get { return (DrawingImage)_resources["LadderCancelSells"]; } } + public static ImageSource LadderCancelOrders => load_resource_and_freeze("LadderCancelOrders"); + public static ImageSource LadderCancelBuys => load_resource_and_freeze("LadderCancelBuys"); + public static ImageSource LadderCancelSells => load_resource_and_freeze("LadderCancelSells"); - public static ImageSource Synthetics { get { return (DrawingImage)_resources["Synthetics"]; } } - public static ImageSource AddSynthetic { get { return (DrawingImage)_resources["AddSynthetic"]; } } - public static ImageSource RemoveSynthetic { get { return (DrawingImage)_resources["RemoveSynthetic"]; } } - public static ImageSource ModifySynthetic { get { return (DrawingImage)_resources["ModifySynthetic"]; } } - public static ImageSource CloneSynthetic { get { return (DrawingImage)_resources["CloneSynthetic"]; } } + public static ImageSource Synthetics => load_resource_and_freeze("Synthetics"); + public static ImageSource AddSynthetic => load_resource_and_freeze("AddSynthetic"); + public static ImageSource RemoveSynthetic => load_resource_and_freeze("RemoveSynthetic"); + public static ImageSource ModifySynthetic => load_resource_and_freeze("ModifySynthetic"); + public static ImageSource CloneSynthetic => load_resource_and_freeze("CloneSynthetic"); - public static ImageSource ModifyOrder { get { return (DrawingImage)_resources["ModifyOrder"]; } } - public static ImageSource Clock { get { return (DrawingImage)_resources["Clock"]; } } - public static ImageSource Defrag { get { return (DrawingImage)_resources["Defrag"]; } } - public static ImageSource Display { get { return (DrawingImage)_resources["Display"]; } } + public static ImageSource ModifyOrder => load_resource_and_freeze("ModifyOrder"); + public static ImageSource Clock => load_resource_and_freeze("Clock"); + public static ImageSource Defrag => load_resource_and_freeze("Defrag"); + public static ImageSource Display => load_resource_and_freeze("Display"); public static ImageSource Draggable => create_icon(DraggableGeometryKey); - public static ImageSource Export => (DrawingImage)_resources["Export"]; - public static ImageSource Filter { get { return (DrawingImage)_resources["Filter"]; } } - public static ImageSource FilterActive { get { return (DrawingImage)_resources["FilterActive"]; } } - public static ImageSource FilterError { get { return (DrawingImage)_resources["FilterError"]; } } + public static ImageSource Export => load_resource_and_freeze("Export"); + public static ImageSource Filter => load_resource_and_freeze("Filter"); + public static ImageSource FilterActive => load_resource_and_freeze("FilterActive"); + public static ImageSource FilterError => load_resource_and_freeze("FilterError"); public static ImageSource Gear => create_icon(GearGeometryKey); - public static ImageSource Import => (DrawingImage)_resources["Import"]; - public static ImageSource Knight { get { return (DrawingImage)_resources["Knight"]; } } + public static ImageSource Import => load_resource_and_freeze("Import"); + public static ImageSource Knight => load_resource_and_freeze("Knight"); public static ImageSource Payment => create_icon(PaymentGeometryKey); - public static ImageSource Spawn { get { return (DrawingImage)_resources["Spawn"]; } } - public static ImageSource Target { get { return (DrawingImage)_resources["Target"]; } } - public static ImageSource Tetris { get { return (DrawingImage)_resources["Tetris"]; } } + public static ImageSource Spawn => load_resource_and_freeze("Spawn"); + public static ImageSource Target => load_resource_and_freeze("Target"); + public static ImageSource Tetris => load_resource_and_freeze("Tetris"); public static ImageSource Tools => create_icon(ToolsGeometryKey); - public static ImageSource ViewSettings => (DrawingImage)_resources["ViewSettings"]; - public static ImageSource Wrench { get { return (DrawingImage)_resources["Wrench"]; } } - public static ImageSource RightArrow { get { return ((DrawingImage)_resources["RightArrow"]); } } - public static ImageSource DownArrow { get { return ((DrawingImage)_resources["DownArrow"]); } } - public static ImageSource Support => (DrawingImage)_resources["SupportIcon"]; + public static ImageSource ViewSettings => load_resource_and_freeze("ViewSettings"); + public static ImageSource Wrench => load_resource_and_freeze("Wrench"); + public static ImageSource RightArrow { get { return (load_resource_and_freeze("RightArrow")); } } + public static ImageSource DownArrow { get { return (load_resource_and_freeze("DownArrow")); } } + public static ImageSource Factory { get { return (load_resource_and_freeze("Factory")); } } + public static ImageSource DotsVertical { get { return (load_resource_and_freeze("DotsVertical")); } } + public static ImageSource HandPointingOverDocument => load_resource_and_freeze("HandPointingOverDocument"); + public static ImageSource Support => load_resource_and_freeze("SupportIcon"); public static ImageSource create_icon(string geometric_path_key) => create_icon(geometric_path_key, Brushes.Black); diff --git a/src/client/spark/Images/Vector/Icons.xaml b/src/client/spark/Images/Vector/Icons.xaml index 7460c5d49..d6341a5a2 100644 --- a/src/client/spark/Images/Vector/Icons.xaml +++ b/src/client/spark/Images/Vector/Icons.xaml @@ -813,8 +813,48 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z"> + + + + + + + + + - - : ViewModelBase, IRequestViewClose where T : class, IEquatable where TVM : IWindowSettingsVM { - readonly Property /*_default_tab_selected,*/ _use_default; - readonly DialogSkirtItem _sync_to_factory_item, _sync_to_default_item, _sync_to_instance_item, _ok_item, _apply_item, _cancel_item, _apply_to_defaults_item; + readonly Property _use_default; + readonly DialogSkirtItem _ok_item, _apply_item, _cancel_item, _apply_to_defaults_item; readonly DelegateProperty> _left_skirt_items; public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext, Func vm_factory) { - //var settings = settings_manager.current_settings(); - //_default_tab_selected = backing(nameof(default_tab_selected), settings_manager.instance_settings == null); // by default, each new window will use their own instance settings _use_default = backing(nameof(use_default), settings_manager.instance_settings == null); default_vm = vm_factory(); @@ -29,31 +27,17 @@ namespace mbts.spark { // items const string import_group = "i", export_group = "e"; (string name, string[] extensions) file_filter = ($"{settings_file_ext} settings files", new[] { settings_file_ext }); - var import_to_custom_cmd = FileDialogCommand.import_proto(s => instance_vm.load(s), () => !use_default, settings_file_ext, filter_items: file_filter); - var export_to_custom_cmd = FileDialogCommand.export_proto(() => instance_vm.get_result(), () => !use_default, filter_items: file_filter); - _use_default.Changed += (_, __) => { - import_to_custom_cmd.RaiseCanExecuteChanged(); - export_to_custom_cmd.RaiseCanExecuteChanged(); - }; + var import_to_custom_cmd = FileDialogCommand.import_proto(s => instance_vm.load(s), type_name: settings_file_ext, filter_items: file_filter); + var export_to_custom_cmd = FileDialogCommand.export_proto(() => instance_vm.get_result(), filter_items: file_filter); action_menu_items = new IMenuItemVM[] { - /*new MenuItemVM("Import File to Default...", FileDialogCommand.import_proto(s => default_vm.load(s), null, settings_file_ext), Icons.Import, import_group),*/ - new MenuItemVM("Import Settings File ...", import_to_custom_cmd, Icons.Import, import_group), - /*new MenuItemVM("Export Default to File...", FileDialogCommand.export_proto(() => default_vm.get_result(), filter_items: file_filter), Icons.Export, export_group),*/ - new MenuItemVM("Export Settings File...", export_to_custom_cmd, Icons.Export, export_group) + new MenuItemVM("Load Settings File ...", import_to_custom_cmd, Icons.Import, import_group), + new MenuItemVM("Save Settings File ...", export_to_custom_cmd, Icons.Export, export_group), + new MenuItemVM("Load Factory Defaults", + new DelegateCommand(() => instance_vm.load(get_factory_settings(UserSettings.factory_defaults))), + Icons.Factory + ) }; _left_skirt_items = backing(nameof(left_skirt_items), left_skirt_factory); - _sync_to_factory_item = new DialogSkirtItem( - "Reset to Factory", - new DelegateCommand(() => instance_vm.load(get_factory_settings(UserSettings.factory_defaults))), - ui.spark.LadderSettingsDialog.ResetToFactoryKey); - _sync_to_default_item = new DialogSkirtItem( - "Copy from Default", - new DelegateCommand(() => instance_vm.load(settings_manager.default_settings)), - ui.spark.LadderSettingsDialog.CopyFromOtherKey); - _sync_to_instance_item = new DialogSkirtItem( - "Copy from Custom", - new DelegateCommand(() => default_vm.load(instance_vm.get_result())), - ui.spark.LadderSettingsDialog.CopyFromOtherKey); _apply_item = new DialogSkirtItem("Apply", new DelegateCommand(apply)); _ok_item = new DialogSkirtItem("OK", new DelegateCommand(() => { use_default = false; @@ -66,6 +50,7 @@ namespace mbts.spark { apply(); close(); }), ui.spark.LadderSettingsDialog.ResetToFactoryKey); + // local methods void apply() { if (!use_default) { @@ -73,18 +58,17 @@ namespace mbts.spark { } else { settings_manager.instance_settings = null; - var my_default = instance_vm.get_result();// default_vm.get_result(); + // copy the current instance settings and persist + var my_default = instance_vm.get_result(); if (!my_default.safe_equals(settings_manager.default_settings)) settings_manager.save_default_settings(my_default).ignore(); - } - } + void close() => RequestClose?.Invoke(this, new EventArgs()); + IEnumerable left_skirt_factory() { - yield return _sync_to_factory_item; yield return _apply_to_defaults_item; - //yield return default_tab_selected ? _sync_to_instance_item : _sync_to_default_item; }; } @@ -101,16 +85,9 @@ namespace mbts.spark { yield return _cancel_item; } } - //public bool default_tab_selected { get => _default_tab_selected.Value; set => _default_tab_selected.Value = value; } public bool use_default { get => _use_default.Value; - set { - _use_default.Value = value; - /* - if (value) - default_tab_selected = true; - */ - } + set => _use_default.Value = value; } } } \ No newline at end of file From 5ab506e9cdca461e34d9bb928b3b752ca77d51d9 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 26 Sep 2018 16:41:40 -0400 Subject: [PATCH 091/203] Fix some broken tests so I can more easily write other tests. (tech debt) --- src/client/spark_core/Co2/Co2NativeMethods.cs | 19 +++++++++--- src/client/spark_core/Co2/MDConsumerHandle.cs | 10 ++----- src/client/spark_test/Co2/Co2Tests.cs | 30 ++++++++----------- src/lib/md/MDConsumerInterop.cpp | 10 ------- src/lib/md/test/Co2ManagedTestHelper.cpp | 22 ++++++++++---- src/lib/md/test/md2_tests.cpp | 2 +- 6 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/client/spark_core/Co2/Co2NativeMethods.cs b/src/client/spark_core/Co2/Co2NativeMethods.cs index 9bae6ed87..e9c6dfde4 100644 --- a/src/client/spark_core/Co2/Co2NativeMethods.cs +++ b/src/client/spark_core/Co2/Co2NativeMethods.cs @@ -56,8 +56,6 @@ namespace mbts.co2 [DllImport("libspark")] internal static extern void md_consumer_set_on_security(IntPtr consumer, raw_security_callback on_security); internal unsafe delegate void raw_just_security_callback(security_key key, byte* buffer, UInt64 buffer_size); - [DllImport("libspark")] - internal unsafe static extern void md_consumer_get_single_security(MDConsumerHandle.ColdEntry* entry, raw_just_security_callback on_security); [DllImport("libspark")] internal unsafe static extern bool md_consumer_is_heart_beating(MDConsumerHandle.ColdHeader* header); @@ -104,11 +102,24 @@ namespace mbts.co2 [DllImport("libspark")] internal static extern void co2_get_sample_cold_header(ref MDConsumerHandle.ColdHeader entry); + public class TestMDHandle : IDisposable { + IntPtr _ptr; + internal TestMDHandle(IntPtr p) { _ptr = p; } + public void Dispose() { + if (_ptr != IntPtr.Zero) { + co2_destroy_test_md(_ptr); + _ptr = IntPtr.Zero; + } + } + } + public static TestMDHandle produce_test_md() { + return new TestMDHandle(co2_produce_test_md()); + } [DllImport("libspark")] - internal static extern IntPtr co2_produce_test_md(); + private static extern IntPtr co2_produce_test_md(); [DllImport("libspark")] - internal static extern void co2_destroy_test_md(IntPtr handle); + private static extern void co2_destroy_test_md(IntPtr handle); [DllImport("libspark")] internal static extern void co2_get_zero_and_nan_book(ref NativePriceLevel nps); diff --git a/src/client/spark_core/Co2/MDConsumerHandle.cs b/src/client/spark_core/Co2/MDConsumerHandle.cs index 1d7bfdcc7..1577fa6c0 100644 --- a/src/client/spark_core/Co2/MDConsumerHandle.cs +++ b/src/client/spark_core/Co2/MDConsumerHandle.cs @@ -22,11 +22,6 @@ namespace mbts.co2 { // The exchange key can change at CBOE. This numeric_id will always be correct public ulong numeric_id { get { return _cold->numeric_id; } } - public Security get_security() { - Security result = null; - Co2NativeMethods.md_consumer_get_single_security(_cold, (key, buffer, length) => result = ProtoBuf.Serializer.Deserialize(new UnmanagedMemoryStream(buffer, (long)length))); - return result; - } // This property exists for specific performance optimization purposes // Most callers should just use get_bbo/get_depth @@ -70,7 +65,7 @@ namespace mbts.co2 { xib += 1; } // Bids grow down, take the higher price - else if (implied.price < direct.price) { + else if (implied.is_nan || implied.int_price < direct.int_price) { bid[x] = direct; xdb += 1; } @@ -95,7 +90,7 @@ namespace mbts.co2 { xia += 1; } // asks grow up, take the lower price - else if (direct.int_price < implied.int_price) { + else if (implied.is_nan || direct.int_price < implied.int_price) { ask[x] = direct; xda += 1; } @@ -246,7 +241,6 @@ namespace mbts.co2 { default: throw new ArgumentException("depth"); } } - } [StructLayout(LayoutKind.Sequential, Size = 384)] diff --git a/src/client/spark_test/Co2/Co2Tests.cs b/src/client/spark_test/Co2/Co2Tests.cs index 61fc880c8..89e4fb0d8 100644 --- a/src/client/spark_test/Co2/Co2Tests.cs +++ b/src/client/spark_test/Co2/Co2Tests.cs @@ -163,21 +163,19 @@ namespace mbts.co2.tests Assert.IsTrue(header.gap_count == 104); } - [Test,Ignore("")] + [Test] public void enumerate_securities() { - // config.Configuration.initialize_for_test(); - IntPtr test_md = Co2NativeTestMethods.co2_produce_test_md(); - using (Disposable.Create(() => Co2NativeTestMethods.co2_destroy_test_md(test_md))) { + using (var test_md = Co2NativeTestMethods.produce_test_md()) { using (var consumer = new MDConsumer(new[] { - new UserSymbol("FOOM", Venue.Unset, SecurityType.Future), - new UserSymbol("FOOXM", Venue.Unset, SecurityType.Option) + new UserSymbol("FOOM", Venue.Bts, SecurityType.Future), + new UserSymbol("FOOXM", Venue.Bts, SecurityType.Option), }, false)) { var securities = consumer.all_securities().ToList(); foreach (var s in securities) { Assert.IsTrue(s.security.Id == s.handle.key); //Assert.IsTrue(s.security.numeric_id == s.handle.numeric_id); } - Assert.IsTrue(securities.Count() == 24); + Assert.AreEqual(securities.Count(), 24); var dict = securities.Select(s => s.security).GroupBy(s => s.Symbol).ToDictionary(group => group.Key, group => group.ToList()); Assert.IsTrue(dict.Count == 2); Assert.IsTrue(dict.All(kvp => kvp.Value.Count == 12)); @@ -185,16 +183,14 @@ namespace mbts.co2.tests } } - [Test,Ignore("")] + [Test] public void on_securities() { - config.Configuration.initialize_for_test(); - IntPtr test_md = Co2NativeTestMethods.co2_produce_test_md(); - using (Disposable.Create(() => Co2NativeTestMethods.co2_destroy_test_md(test_md))) { + using (var test_md = Co2NativeTestMethods.produce_test_md()) { using (var consumer = new MDConsumer(new[] { - new UserSymbol("FOOM", Venue.Unset, SecurityType.Future), - new UserSymbol("FOOXM", Venue.Unset, SecurityType.Option) + new UserSymbol("FOOM", Venue.Bts, SecurityType.Future), + new UserSymbol("FOOXM", Venue.Bts, SecurityType.Option) }, false)) { - var securities = new List(); + var securities = new List(); consumer.on_security.Subscribe(secs => { foreach (var s in secs) { Assert.IsTrue(s.security.Id == s.handle.key); @@ -210,11 +206,10 @@ namespace mbts.co2.tests foreach (var sec in securities) { var handle = consumer.try_get_handle(sec.Id); Assert.IsNotNull(handle); - Assert.IsTrue(handle.get_security().Id == sec.Id); Assert.AreEqual(sec.Id, handle.key); Assert.IsTrue(handle.market_timestamp > 0); ulong time = 0; - NativePriceSize bid,ask; + NativePriceSize bid, ask; handle.get_bbo(ref time, out bid, out ask); Assert.IsTrue(bid.price == 1.1); Assert.IsTrue(bid.size == 101); @@ -227,7 +222,7 @@ namespace mbts.co2.tests NativePriceSize last_trade; Assert.IsTrue(handle.get_last_trade(ref time, out last_trade, out trade_date, out total_volume)); Assert.IsTrue(last_trade.price == 5.5 && last_trade.size == 505); - Assert.IsTrue(trade_date.Year == 2015 && trade_date.Month == 9 && trade_date.Day == 22); + //Assert.IsTrue(trade_date.Year == 2015 && trade_date.Month == 9 && trade_date.Day == 22); Assert.IsTrue(total_volume == 4200); Assert.IsTrue(handle.settlement_date.Year == 2000 && handle.settlement_date.Day == 9 && handle.settlement_date.Month == 8); Assert.AreEqual(handle.settlement_price, 6.6); @@ -272,5 +267,6 @@ namespace mbts.co2.tests } + } } diff --git a/src/lib/md/MDConsumerInterop.cpp b/src/lib/md/MDConsumerInterop.cpp index ad46d10a0..7f4848bea 100644 --- a/src/lib/md/MDConsumerInterop.cpp +++ b/src/lib/md/MDConsumerInterop.cpp @@ -102,16 +102,6 @@ CAPI void md_consumer_set_on_security(MDConsumer* consumer, security_cb cb) { } #endif -typedef void(*just_security_cb)(security_key, const char*, size_t); -CAPI void md_consumer_get_single_security(const ColdEntry* entry, just_security_cb cb) { - bts_assert(entry); - bts::api::Security security; - convert_security(entry->security, &security); - std::string buffer; - security.SerializePartialToString(&buffer); - cb(security.id(), buffer.c_str(), buffer.size()); -} - bool is_heart_beat_ok(const ColdHeader &header); CAPI bool md_consumer_is_heart_beating(const ColdHeader* header) { if (header == nullptr) return false; diff --git a/src/lib/md/test/Co2ManagedTestHelper.cpp b/src/lib/md/test/Co2ManagedTestHelper.cpp index e0bc44448..f5bb9a882 100644 --- a/src/lib/md/test/Co2ManagedTestHelper.cpp +++ b/src/lib/md/test/Co2ManagedTestHelper.cpp @@ -108,19 +108,29 @@ extern "C" BTS_EXPORT void co2_get_sample_cold_header(ColdHeader& header) { // Do not write to trade_buffer or rfq_buffer } -extern "C" BTS_EXPORT MDProducer* co2_produce_test_md() { +struct TestProducer { + std::shared_ptr md; + std::vector handles; +}; + +extern "C" BTS_EXPORT TestProducer* co2_produce_test_md() { config::internal::initialize_for_test( "PlatformMap {" " value {" " glbx_platform: 'PLATFORM_SIM'" " }" "}"); - auto* p = new MDProducer(PLATFORM_SIM); - auto producers = create_test_securities(*p); + auto* tp = new TestProducer(); + tp->md = std::make_shared(PLATFORM_SIM, false); + tp->handles = create_test_securities(*(tp->md)); + auto& producers = tp->handles; assign_ids(producers.begin(), producers.end()); for (auto& p : producers) { p.update_bbo(utils::TimeStamp::now(), PriceSize_from_double<3>(1.1, 101), PriceSize_from_double<3>(2.2,202)); - p.add_trade(utils::TimeStamp::now(), PriceSize_from_double<3>(5.5, 505), datetime::ll_from_date(datetime::Date(2015, 9, 22)), AGGRESSOR_SIDE_NONE, 4200); + p.add_trade(utils::TimeStamp::now(), + PriceSize_from_double<3>(5.5, 505), + datetime::ll_from_date(datetime::Date(2015, 9, 22)), + AGGRESSOR_SIDE_NONE, 4200); p.update_settlement(utils::TimeStamp::now(), IntPrice_from_double<7>(6.6), datetime::Date(2000, 8, 8)); p.update_settlement(utils::TimeStamp::now(), IntPrice_from_double<7>(6.6), datetime::Date(2000, 8, 9)); @@ -135,10 +145,10 @@ extern "C" BTS_EXPORT MDProducer* co2_produce_test_md() { p.update_fixing_price(t, 14.14); } - return p; + return tp; } -extern "C" BTS_EXPORT void co2_destroy_test_md(MDProducer* producer) { +extern "C" BTS_EXPORT void co2_destroy_test_md(TestProducer* producer) { if (producer != nullptr) delete producer; config::internal::reset(); } diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index d0eb3ac01..ff1b456b9 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -55,8 +55,8 @@ Platform TEST_PLATFORM = PLATFORM_SIM; // Helper functions for Producer/Consumer tests // vector create_test_securities(MDProducer& producer, const char* postfix ="") { - FixedOptionSecurity fos; FixedFutureSecurity ffs; + FixedOptionSecurity fos; ffs.symbol("FOO"+string(postfix)); fos.symbol("FOOX"+string(postfix)); ffs.exchange = VENUE_BTS; From f61b69f6bd53b9c9e8f1cb8d7179164608a075c7 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 26 Sep 2018 18:24:13 -0400 Subject: [PATCH 092/203] fix grid-column-settings immutable values --- .../Modules/Market/MarketView/MarketViewWindow.cs | 2 +- .../Market/TimeAndSales/TimeAndSalesWindow.cs | 2 +- .../DIYGrid/Settings/SimpleGridSettingsManager.cs | 13 +++++++++++++ src/client/spark/view/Spark/Ladder/LadderVM.cs | 8 +++++++- .../view/Spark/Utilities/WindowSettingsDialogVM.cs | 8 +++----- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs index 18630e795..50991bc72 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs @@ -66,7 +66,7 @@ namespace mbts.spark { // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( - _settings_manager, s => s.market_view_grid, UserSettings.FileExtensions.GRID, () => new SimpleGridSettingsVM(this.observe_title())); + _settings_manager, us => us.market_view_grid, UserSettings.FileExtensions.GRID, s => _vm.grid_settings_manager.create_vm(s, this.observe_title())); // TODO: bind to main window's Title var dialog = new SimpleGridSettingsDialog { Title = "Market View Grid Settings", DataContext = vm }; vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs index b5064a3ef..4c86f3d0a 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs @@ -75,7 +75,7 @@ namespace mbts.spark { // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( - _settings_manager, s => s.market_view_grid, UserSettings.FileExtensions.GRID, () => new SimpleGridSettingsVM(this.observe_title())); + _settings_manager, s => s.market_view_grid, UserSettings.FileExtensions.GRID, s => _vm.value.grid_settings_manager.create_vm(s, this.observe_title())); // TODO: bind to main window's Title var dialog = new SimpleGridSettingsDialog { Title = "Time and Sales Grid Settings", DataContext = vm }; vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs index 3896b258f..eddd45329 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs @@ -47,6 +47,16 @@ namespace mbts.ui { public SolidColorBrush font_color => _font_color.Value; public SolidColorBrush selection_highlight => _selection_highlight.Value; + public SimpleGridSettingsVM create_vm(GridSettings settings, IObservable title) { + var vm = new SimpleGridSettingsVM(title); + vm.load_from(settings); + foreach (var col in vm.columns) { + if (_column_flags.TryGetValue(col.id, out ImmutableColumnSettingStore icss)) + col.apply_flags(icss.flags); + } + return vm; + } + public GridSettings create() { // IMPORTANT: don't overwrite current sorting _grid_settings.set(grid_settings.with(_columns.Select(c => { @@ -58,6 +68,9 @@ namespace mbts.ui { return grid_settings; } + /// + /// This will overwrite the columns' immutable-value store (unlike the IObserver<> 'OnNext' implementation) + /// public void load_from(GridSettings grid) => _load_from_core(grid, true); void IObserver.OnCompleted() { } diff --git a/src/client/spark/view/Spark/Ladder/LadderVM.cs b/src/client/spark/view/Spark/Ladder/LadderVM.cs index 998491700..be44ac956 100644 --- a/src/client/spark/view/Spark/Ladder/LadderVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderVM.cs @@ -64,7 +64,13 @@ namespace mbts.spark { show_book_command = new DelegateCommand(_show_book); show_ticket_command = new DelegateCommand(_show_ticket); settings_command = new DialogCommand(() => { - var vm = new WindowSettingsDialogVM(settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER, () => new LadderSettingsVM()); + var vm = new WindowSettingsDialogVM( + settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER, + s => { + var svm = new LadderSettingsVM(); + svm.load(s); + return svm; + }); var dialog = new ui.spark.LadderSettingsDialog { DataContext = vm, Title = $"Ladder Settings - '{security}'" diff --git a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs index 706f9858b..e7213624e 100644 --- a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs +++ b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs @@ -16,14 +16,12 @@ namespace mbts.spark { readonly DialogSkirtItem _ok_item, _apply_item, _cancel_item, _apply_to_defaults_item; readonly DelegateProperty> _left_skirt_items; - public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext, Func vm_factory) { + public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext, Func vm_factory) { // by default, each new window will use their own instance settings _use_default = backing(nameof(use_default), settings_manager.instance_settings == null); - default_vm = vm_factory(); - default_vm.load(settings_manager.default_settings); + default_vm = vm_factory(settings_manager.default_settings); // instance vm is what will always be visible on the screen - instance_vm = vm_factory(); - instance_vm.load(settings_manager.current_settings()); + instance_vm = vm_factory(settings_manager.current_settings()); // items const string import_group = "i", export_group = "e"; (string name, string[] extensions) file_filter = ($"{settings_file_ext} settings files", new[] { settings_file_ext }); From 065f667a7957a9a98cfed3ffc745dd6c7e9ce024 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 26 Sep 2018 18:26:24 -0400 Subject: [PATCH 093/203] Fix problem with pesky NaN implied --- src/client/spark_core/Co2/MDConsumerHandle.cs | 2 + src/client/spark_test/Co2/Co2Tests.cs | 2 +- src/lib/md/MDConsumerHandle.h | 3 + src/lib/md/test/md2_tests.cpp | 169 ++++++++++++++++++ 4 files changed, 175 insertions(+), 1 deletion(-) diff --git a/src/client/spark_core/Co2/MDConsumerHandle.cs b/src/client/spark_core/Co2/MDConsumerHandle.cs index 1577fa6c0..a5454b6f2 100644 --- a/src/client/spark_core/Co2/MDConsumerHandle.cs +++ b/src/client/spark_core/Co2/MDConsumerHandle.cs @@ -53,6 +53,8 @@ namespace mbts.co2 { Log.assert_d(bid.Length >= count); Log.assert_d(ask.Length >= count); uint xda = 0, xdb = 0, xia = 0, xib = 0; + if (hot.implied(xib).bid.is_nan) xib = NativePriceHelpers.COCONUT_MAX_IMPLIED_DEPTH; + if (hot.implied(xia).ask.is_nan) xia = NativePriceHelpers.COCONUT_MAX_IMPLIED_DEPTH; for (uint x = 0; x < count; ++x) { if (xib < NativePriceHelpers.COCONUT_MAX_IMPLIED_DEPTH) { var direct = hot.direct(xdb).bid; diff --git a/src/client/spark_test/Co2/Co2Tests.cs b/src/client/spark_test/Co2/Co2Tests.cs index ffdb7d1b8..98767cbda 100644 --- a/src/client/spark_test/Co2/Co2Tests.cs +++ b/src/client/spark_test/Co2/Co2Tests.cs @@ -222,7 +222,7 @@ namespace mbts.co2.tests NativePriceSize last_trade; Assert.IsTrue(handle.get_last_trade(ref time, out last_trade, out trade_date, out total_volume)); Assert.IsTrue(last_trade.price == 5.5 && last_trade.size == 505); - //Assert.IsTrue(trade_date.Year == 2015 && trade_date.Month == 9 && trade_date.Day == 22); + Assert.IsTrue(trade_date.Year == 2015 && trade_date.Month == 9 && trade_date.Day == 22); Assert.IsTrue(total_volume == 4200); Assert.IsTrue(handle.settlement_date.Year == 2000 && handle.settlement_date.Day == 9 && handle.settlement_date.Month == 8); Assert.AreEqual(handle.settlement_price, 6.6); diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index f878ccef7..9a0f8dd37 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -56,6 +56,9 @@ inline void read_combined( PriceSize* bid, PriceSize* ask) { bts_assert_d(count < COCONUT_MAX_DEPTH); size_t xda=0, xdb=0, xia=0, xib=0; + if (hot.implied[xib].bid().is_nan()) xib = COCONUT_MAX_IMPLIED_DEPTH; + if (hot.implied[xia].ask().is_nan()) xia = COCONUT_MAX_IMPLIED_DEPTH; + for (size_t x = 0; x < count; ++x) { if (xib < COCONUT_MAX_IMPLIED_DEPTH) { auto direct = hot.direct[xdb].bid(); diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index ff1b456b9..8bb5791eb 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -2053,6 +2053,175 @@ TEST_CASE("MergeImpliedTests", "[md]") { }); } + SECTION("nan_implied") { + direct_bid[0] = { {500, 1}, 1 }; + direct_bid[1] = { {400, 1}, 1 }; + direct_bid[2] = { {300, 1}, 1 }; + direct_bid[3] = { {200, 1}, 1 }; + + direct_ask[0] = { {1000, 1}, 1 }; + direct_ask[1] = { {1100, 1}, 1 }; + direct_ask[2] = { {1200, 1}, 1 }; + direct_ask[3] = { {1300, 1}, 1 }; + + implied_bid[0] = { INT_NAN, 0 }; + implied_bid[1] = { INT_NAN, 0 }; + + implied_ask[0] = { INT_NAN, 0 }; + implied_ask[1] = { INT_NAN, 0 }; + + + ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { + const size_t TEST_DEPTH = 4; + for (const auto& c : consumers) { + std::array bids, asks; + int canary = 0; + TimeStamp read_time; + // get_boo returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < TEST_DEPTH; ++x) { + REQUIRE(bids[x].size() == 1); + REQUIRE(asks[x].size() == 1); + REQUIRE(bids[x].direct_size() == 1); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 1); + REQUIRE(asks[x].implied_size() == 0); + } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 0); + REQUIRE(asks[x].size() == 0); + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 0); + } + read_time = TimeStamp(); + REQUIRE(c.get_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(bids[0].mantissa() == 500); + REQUIRE(bids[0].size() == 1); + REQUIRE(asks[0].mantissa() == 1000); + REQUIRE(asks[0].size() == 1); + REQUIRE(bids[0].direct_size() == 1); + REQUIRE(bids[0].implied_size() == 0); + REQUIRE(asks[0].direct_size() == 1); + REQUIRE(asks[0].implied_size() == 0); + + REQUIRE(bids[1].mantissa() == 400); + REQUIRE(bids[1].size() == 1); + REQUIRE(asks[1].mantissa() == 1100); + REQUIRE(asks[1].size() == 1); + REQUIRE(bids[1].direct_size() == 1); + REQUIRE(bids[1].implied_size() == 0); + REQUIRE(asks[1].direct_size() == 1); + REQUIRE(asks[1].implied_size() == 0); + + REQUIRE(bids[2].mantissa() == 300); + REQUIRE(bids[2].size() == 1); + REQUIRE(asks[2].mantissa() == 1200); + REQUIRE(asks[2].size() == 1); + REQUIRE(bids[2].direct_size() == 1); + REQUIRE(bids[2].implied_size() == 0); + REQUIRE(asks[2].direct_size() == 1); + REQUIRE(asks[2].implied_size() == 0); + + REQUIRE(bids[3].mantissa() == 200); + REQUIRE(bids[3].size() == 1); + REQUIRE(asks[3].mantissa() == 1300); + REQUIRE(asks[3].size() == 1); + REQUIRE(bids[3].direct_size() == 1); + REQUIRE(bids[3].implied_size() == 0); + REQUIRE(asks[3].direct_size() == 1); + REQUIRE(asks[3].implied_size() == 0); + + REQUIRE(canary == 0); + } + }); + } + SECTION("nan_direct") { + direct_bid[0] = { INT_NAN, 0 }; + direct_bid[1] = { INT_NAN, 0 }; + direct_bid[2] = { INT_NAN, 0 }; + direct_bid[3] = { INT_NAN, 0 }; + + direct_ask[0] = { INT_NAN, 0 }; + direct_ask[1] = { INT_NAN, 0 }; + direct_ask[2] = { INT_NAN, 0 }; + direct_ask[3] = { INT_NAN, 0 }; + + implied_bid[0] = { {500, 1}, 2 }; + implied_bid[1] = { {400, 1}, 2 }; + + implied_ask[0] = { {1000, 1}, 2 }; + implied_ask[1] = { {1100, 1}, 2 }; + + + ImpliedTests::test_merge(direct_bid,implied_bid,direct_ask,implied_ask, [=](const vector& consumers) { + const size_t TEST_DEPTH = 4; + for (const auto& c : consumers) { + std::array bids, asks; + int canary = 0; + TimeStamp read_time; + // get_boo returns true becuase this time is after the change + REQUIRE(c.get_direct_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < TEST_DEPTH; ++x) { + REQUIRE(bids[x].size() == 0); + REQUIRE(asks[x].size() == 0); + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 0); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 0); + } + read_time = TimeStamp(); + REQUIRE(c.get_implied_book(read_time, COCONUT_MAX_IMPLIED_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + for (size_t x = 0; x < COCONUT_MAX_IMPLIED_DEPTH; ++x) { + REQUIRE(bids[x].size() == 2); + REQUIRE(asks[x].size() == 2); + REQUIRE(bids[x].direct_size() == 0); + REQUIRE(bids[x].implied_size() == 2); + REQUIRE(asks[x].direct_size() == 0); + REQUIRE(asks[x].implied_size() == 2); + } + read_time = TimeStamp(); + REQUIRE(c.get_book(read_time, TEST_DEPTH, &bids[0], &asks[0]) == HandleReadResult::SUCCESS); + REQUIRE(bids[0].mantissa() == 500); + REQUIRE(bids[0].size() == 2); + REQUIRE(asks[0].mantissa() == 1000); + REQUIRE(asks[0].size() == 2); + REQUIRE(bids[0].direct_size() == 0); + REQUIRE(bids[0].implied_size() == 2); + REQUIRE(asks[0].direct_size() == 0); + REQUIRE(asks[0].implied_size() == 2); + + REQUIRE(bids[1].mantissa() == 400); + REQUIRE(bids[1].size() == 2); + REQUIRE(asks[1].mantissa() == 1100); + REQUIRE(asks[1].size() == 2); + REQUIRE(bids[1].direct_size() == 0); + REQUIRE(bids[1].implied_size() == 2); + REQUIRE(asks[1].direct_size() == 0); + REQUIRE(asks[1].implied_size() == 2); + + REQUIRE(bids[2].size() == 0); + REQUIRE(asks[2].size() == 0); + REQUIRE(bids[2].direct_size() == 0); + REQUIRE(bids[2].implied_size() == 0); + REQUIRE(asks[2].direct_size() == 0); + REQUIRE(asks[2].implied_size() == 0); + + REQUIRE(bids[3].size() == 0); + REQUIRE(asks[3].size() == 0); + REQUIRE(bids[3].direct_size() == 0); + REQUIRE(bids[3].implied_size() == 0); + REQUIRE(asks[3].direct_size() == 0); + REQUIRE(asks[3].implied_size() == 0); + + REQUIRE(canary == 0); + } + }); + } + } }}} From 8ff94037eb2f7d10a50af6f997cea8509a10e578 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Wed, 26 Sep 2018 18:49:52 -0400 Subject: [PATCH 094/203] net price and implieds done for spreader --- src/lib/md/IntPrice.h | 5 +- src/lib/md/PriceSize.h | 6 +- .../orders/spreader_orders/SpreaderOrder.cpp | 2 +- .../spreader/LegPropertyData.h | 3 + .../spreader/SpreaderLegData.cpp | 2 +- .../spreader/SpreaderLegData.h | 57 +++++-------------- .../spreader_orders/spreader/SpreaderUtils.h | 33 +++++++++++ src/lib/sparkproto/SparkProtos.proto | 4 +- 8 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/lib/md/IntPrice.h b/src/lib/md/IntPrice.h index ee2d1fc7b..532197dac 100644 --- a/src/lib/md/IntPrice.h +++ b/src/lib/md/IntPrice.h @@ -47,7 +47,10 @@ class IntPrice { return price() < other.price(); } } - + IntPrice operator - (const IntPrice& o) const { + //if(_exponent==o._exponent) + return IntPrice(_mantissa-o.mantissa(), _exponent); + } int64_t mantissa() const { return _mantissa; } int8_t exponent() const { return _exponent; } double price() const { diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 00cecba66..eb01d2884 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -51,7 +51,11 @@ public: bool operator!=(const PriceSize& other) const { return !(*this == other); } - + PriceSize operator-(IntPrice price) const{ + PriceSize res=*this; + res._price=res._price-price; + return res; + } // // Accessors diff --git a/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp b/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp index c3d762124..adada8a7f 100644 --- a/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp +++ b/src/lib/orderman/orders/spreader_orders/SpreaderOrder.cpp @@ -112,7 +112,7 @@ void SpreaderOrder::_on_settlement_update(const SettlementUpdateEvent& // this is a key->index lookup SpreaderLegData& md_leg = _bookkeeping_data().get_leg_data(settlement_event.key()); - md_leg.update_settlement_price(settlement_event.settlement_price.price()); + md_leg.update_settlement_price(settlement_event.settlement_price); bts::PerfTrace trace; _state_worker->on_book (md_leg.book(),trace); } diff --git a/src/lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h b/src/lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h index b6eba2d11..31d6457b5 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h +++ b/src/lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h @@ -43,6 +43,9 @@ public: bts::spark::LegSpreaderParams::LegMarketDataCalculation leg_md_calc() const { return _leg_params.leg_md_calc(); } + bts::spark::LegSpreaderParams::MarketDataType md_type() const { + return _leg_params.market_data_type(); + } }; }} diff --git a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp index 580b65921..ceb595573 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp +++ b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp @@ -20,7 +20,7 @@ _quoter_child_order_state (quoter_child_order_state), _hedger_data_vector (hedger_data_vector), _book(book), _unhedged_delta(unhedged_delta), -_settlement_price(os::nan()) +_settlement_price(md::INT_NAN) {} SpreaderLegData::SpreaderLegData(SpreaderLegData&& other) diff --git a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h index c38523cd8..f96c8346e 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h +++ b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h @@ -32,7 +32,7 @@ private: std::vector& _hedger_data_vector; Book& _book; UnhedgedDelta& _unhedged_delta; - md::Price _settlement_price; + md::IntPrice _settlement_price; public: SpreaderLegData (const LegPropertyData& leg_property_data, SingleLegQuoter& quoter, @@ -59,7 +59,7 @@ public: SingleLegQuoter& quoter() {return _quoter;} const SingleLegQuoter& quoter() const {return _quoter;} const bts::orderman::Book& book () const {return _book;} - md::Price settlement_price() const {return _settlement_price;} + //md::Price settlement_price() const {return _settlement_price;} SpreaderChildOrderState& order_update_message () {return _quoter_child_order_state;} bts::orderman::OrderKey key() {return _quoter_child_order_state.order_key();} bts::orderman::OrderKey order_key() {return _quoter_child_order_state.order_key();} @@ -104,47 +104,20 @@ public: //////////////////////////////// // called for every book update void update_book(const Book& book) { - if(_leg_property_data.leg_md_calc()== - bts::spark::LegSpreaderParams::BOOK) - _book=book; - else - // this has a problem. if we get events - // in this order - // 1. book - // 2. settlement - // we will continue with an empty - // book until 3 - // 3. book - _offset_book(book, _settlement_price); + SpreaderUtils::copy_book(book, + _settlement_price, + _leg_property_data.md_type(), + _leg_property_data.leg_md_calc(), + _book); } - void update_settlement_price(md::Price price){ - if(_leg_property_data.leg_md_calc()== - bts::spark::LegSpreaderParams::NET_CHANGE){ - if(os::is_nan(_settlement_price)){ - _settlement_price=price; - _offset_book(_book,price); - } - else - LOG(OrderMan,Info,"SpreaderLegData::update_settlement_price " - "ignoring update key:{} curr:{} new:{}", - _leg_property_data.security_key(), - _settlement_price, price); - } - } -private: - void _offset_book(const Book& book, - md::Price settlement_price){ - if(os::is_nan(settlement_price)) - _book=Book(); - else{ - const BookSide& bid_source=book.bid(); - BookSide& bid_target=_book.bid(); - for(size_t i=0;i Date: Thu, 27 Sep 2018 10:46:43 -0500 Subject: [PATCH 095/203] Fixing sorting - too many criterions --- .../Market/MarketView/MarketViewWindowFactory.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs index 244ebf3a6..e19040280 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs @@ -7,6 +7,7 @@ using mbts.ui.settings; using ProtoBuf; using System; using System.Collections.Generic; +using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using static mbts.ui.controls.DIYGrid; @@ -60,16 +61,16 @@ namespace bts.shell { public StartupData(SecurityCriteria security_criteria) : this (security_criteria, new FilterStates()) { } - public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List sorting_criterion = null) { + public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List sort = null) { this.security_criteria = security_criteria; this.filters = filters; this.grid = grid; - _sorting_criteria = sorting_criterion; + _sorting_criteria = sort; } - public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion); + public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion); - public IReadOnlyList sorting_criterion => _sorting_criteria ?? (_sorting_criteria = new List()); + public IReadOnlyList sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList()); public static StartupData Default(SecurityCriteria security_criteria) => new StartupData(security_criteria, new FilterStates(), null); @@ -95,7 +96,7 @@ namespace bts.shell { - public static IEnumerable sorting_criterion { + public static IEnumerable default_sorting_criterion { get { yield return new SortingCriteria(security.id, true); } From eabfb6a6958cc9912cc953629b68e7ed4ec58112 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Thu, 27 Sep 2018 11:49:09 -0400 Subject: [PATCH 096/203] SPARK-1712 Use implied or direct market data for synthetic legs --- src/lib/md/MDConsumerHandle.cpp | 10 ++++++++-- src/lib/md/MDConsumerHandle.h | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/md/MDConsumerHandle.cpp b/src/lib/md/MDConsumerHandle.cpp index 6526acfa6..33bb65925 100644 --- a/src/lib/md/MDConsumerHandle.cpp +++ b/src/lib/md/MDConsumerHandle.cpp @@ -173,8 +173,14 @@ TimeStamp copy_depth(const SyntheticStrategy& synth, size_t count, PriceSize* bi for (size_t x = 0; x < synth.legs.size(); ++x) { std::array bids, asks; TimeStamp t; - // todo: read type of md from synthetic - HandleReadResult r = synth.legs[x].get_direct_book(t, count, &bids[0], &asks[0]); + auto mdtype = synth.strategy.leg_params_map().at(synth.legs[x].key()).market_data_type(); + HandleReadResult r; + if (mdtype == spark::LegSpreaderParams::DIRECT) { + r = synth.legs[x].get_direct_book(t, count, &bids[0], &asks[0]); + } + else { + r = synth.legs[x].get_book(t, count, &bids[0], &asks[0]); + } if (r == HandleReadResult::BUSY || t == TimeStamp::invalid()) return TimeStamp::invalid(); rv = std::max(rv, t); const auto& leg = synth.security.synthetic().legs(x); diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 9a0f8dd37..16eca0c99 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -54,7 +54,7 @@ inline void read_combined( const HotDepthEntry& hot, size_t count, PriceSize* bid, PriceSize* ask) { - bts_assert_d(count < COCONUT_MAX_DEPTH); + bts_assert_d(count <= COCONUT_MAX_DEPTH); size_t xda=0, xdb=0, xia=0, xib=0; if (hot.implied[xib].bid().is_nan()) xib = COCONUT_MAX_IMPLIED_DEPTH; if (hot.implied[xia].ask().is_nan()) xia = COCONUT_MAX_IMPLIED_DEPTH; From 1822ed6aa2dc434e220bbeb8813ab58cfbe1bf71 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 27 Sep 2018 11:29:37 -0500 Subject: [PATCH 097/203] fixing settings immutable bug - default vs NullableBlah?() - i like seeing null, it's make it easier to read --- .../Controls/DIYGrid/Settings/SimpleGridColumnSettingsVM.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridColumnSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridColumnSettingsVM.cs index d992a86bd..11843b02b 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridColumnSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridColumnSettingsVM.cs @@ -52,8 +52,8 @@ namespace mbts.spark { public GridColumnSettings create() => _settings.with(display_name, is_visible, null, - is_font_weight_overridden ? new NullableFontWeightProto(font_weight_override) : default, - is_font_color_overridden ? new NullableColorProto(font_color_override.color) : default, + is_font_weight_overridden ? new NullableFontWeightProto(font_weight_override) : (NullableFontWeightProto?)null, + is_font_color_overridden ? new NullableColorProto(font_color_override.color) : (NullableColorProto?)null, width, horizontal_alignment); From 0ed884a5abadb39cf14fe6dad09b9bcc7b0fd4cc Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Thu, 27 Sep 2018 14:21:52 -0400 Subject: [PATCH 098/203] Enable safe subtration for IntPrice --- src/lib/md/IntPrice.h | 28 +++++++++++++++++++++-- src/lib/md/test/md2_tests.cpp | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/lib/md/IntPrice.h b/src/lib/md/IntPrice.h index e6bd52b4f..90a97cbfb 100644 --- a/src/lib/md/IntPrice.h +++ b/src/lib/md/IntPrice.h @@ -35,6 +35,10 @@ class IntPrice { IntPrice(const IntPrice& other) : _mantissa(other._mantissa), _exponent(other._exponent) {} bool operator==(const IntPrice& other) const { + // this requires both mantissa and exponent to be equal + // logically 100*pow10(1) == 10*pow10(2) + // HOWEVER, once converted to double there are subtelties that can make them not equal so + // this function is conservative return _mantissa == other._mantissa && _exponent == other._exponent; } bool operator!=(const IntPrice& other) const { @@ -49,8 +53,23 @@ class IntPrice { } } IntPrice operator - (const IntPrice& o) const { - //if(_exponent==o._exponent) - return IntPrice(_mantissa-o.mantissa(), _exponent); + if(_exponent==o._exponent) { + return IntPrice(_mantissa-o.mantissa(), _exponent); + } + else { + // not sure there's ever a cause for a positive exponent unless we get into scientific computing + bts_assert_d(o.exponent() <= 0 && _exponent <= 0); + if (o.exponent() < _exponent) { + int8_t delta = _exponent - o.exponent(); + auto m = _mantissa * pow10(delta); + return IntPrice(m - o.mantissa(), o.exponent()); + } + else { + int8_t delta = o.exponent() - _exponent; + auto m = o.mantissa() * pow10(delta); + return IntPrice(_mantissa - m, _exponent); + } + } } int64_t mantissa() const { return _mantissa; } int8_t exponent() const { return _exponent; } @@ -76,4 +95,9 @@ IntPrice IntPrice_from_double(double price) { return {(int64_t)price, 0}; } +inline std::ostream& operator<<(std::ostream& os, const IntPrice& p) { + os << p.price(); + return os; +} + }} \ No newline at end of file diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index 8bb5791eb..caf806ffb 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -2221,7 +2221,49 @@ TEST_CASE("MergeImpliedTests", "[md]") { } }); } +} +TEST_CASE("IntPrice", "[md]") { + SECTION("< equal exponent") { + auto a = IntPrice_from_double<4>(101.111); + auto b = IntPrice_from_double<4>(100.111); + REQUIRE((int)a.exponent() == (int)b.exponent()); + REQUIRE(b < a); + REQUIRE(!(a < b)); + } + + SECTION("< different exponent") { + auto a = IntPrice_from_double<4>(101.111); // -4 + auto b = IntPrice_from_double<0>(100); // 0 + REQUIRE((int)a.exponent() < (int)b.exponent()); + REQUIRE(b < a); + REQUIRE(!(a < b)); + auto c = std::max(a,b); + REQUIRE(c == a); + } + + // subtract with equal exponent + SECTION("subtract equal exponent") { + IntPrice a = IntPrice_from_double<4>(100.333); + IntPrice b = IntPrice_from_double<4>(100.222); + IntPrice delta = a - b; + REQUIRE(delta == IntPrice_from_double<4>(0.111)); + } + + // subtract smaller from greater + SECTION("subtract smaller from greater") { + IntPrice a = IntPrice_from_double<4>(100.333); + IntPrice b = IntPrice_from_double<5>(100.222); + IntPrice delta = a - b; + REQUIRE(delta == IntPrice_from_double<5>(0.111)); + } + // subtract greater from smaller + SECTION("subtract greater from smaller") { + IntPrice a = IntPrice_from_double<4>(100.333); + IntPrice b = IntPrice_from_double<3>(100.111); + IntPrice delta = a - b; + REQUIRE(delta == IntPrice_from_double<4>(0.222)); + } } }}} From d6fafee64c7e049e5feef91707266cb21cf8f5ce Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Thu, 27 Sep 2018 15:23:29 -0400 Subject: [PATCH 099/203] Handle possibility of a missing LegSpreaderParams --- src/lib/md/MDConsumerHandle.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/md/MDConsumerHandle.cpp b/src/lib/md/MDConsumerHandle.cpp index 33bb65925..2a9c02174 100644 --- a/src/lib/md/MDConsumerHandle.cpp +++ b/src/lib/md/MDConsumerHandle.cpp @@ -173,7 +173,14 @@ TimeStamp copy_depth(const SyntheticStrategy& synth, size_t count, PriceSize* bi for (size_t x = 0; x < synth.legs.size(); ++x) { std::array bids, asks; TimeStamp t; - auto mdtype = synth.strategy.leg_params_map().at(synth.legs[x].key()).market_data_type(); + auto key = synth.legs[x].key(); + auto iter = synth.strategy.leg_params_map().find(key); + const spark::LegSpreaderParams& leg_params = + iter == synth.strategy.leg_params_map().end() ? + spark::LegSpreaderParams::default_instance() : + iter->second; + + auto mdtype = leg_params.market_data_type(); HandleReadResult r; if (mdtype == spark::LegSpreaderParams::DIRECT) { r = synth.legs[x].get_direct_book(t, count, &bids[0], &asks[0]); @@ -182,6 +189,16 @@ TimeStamp copy_depth(const SyntheticStrategy& synth, size_t count, PriceSize* bi r = synth.legs[x].get_book(t, count, &bids[0], &asks[0]); } if (r == HandleReadResult::BUSY || t == TimeStamp::invalid()) return TimeStamp::invalid(); + + // NET Change + if (leg_params.leg_md_calc() == spark::LegSpreaderParams::NET_CHANGE) { + auto settle = synth.legs[x].settlement_price(); + for (size_t d = 0; d < COCONUT_MAX_DEPTH; ++d) { + bids[x] = bids[x] - settle; + asks[x] = asks[x] - settle; + } + } + rv = std::max(rv, t); const auto& leg = synth.security.synthetic().legs(x); const auto& eff_bid = leg.side() == api::SIDE_BUY ? bids : asks; From da21c57cca6e09ac6f27c749a9a0d182c50791ba Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Thu, 27 Sep 2018 15:23:52 -0400 Subject: [PATCH 100/203] Enable selecting release for 'runsparkd.cmd' (for benchmarks) --- tools/runsparkd.cmd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/runsparkd.cmd b/tools/runsparkd.cmd index 9a3168b01..00fedf7f3 100644 --- a/tools/runsparkd.cmd +++ b/tools/runsparkd.cmd @@ -1,2 +1,3 @@ @if "%SPARKVM_PORT%"=="" set SPARKVM_PORT=22 -@ssh bts@sparkvm -p %SPARKVM_PORT% "cd /home/bts/spark/build/ninja_debug/server/ && ./sparkd %*" +@if "%SPARKVM_RUNMODE%"=="" set SPARKVM_RUNMODE=debug +@ssh bts@sparkvm -p %SPARKVM_PORT% "cd /home/bts/spark/build/ninja_%SPARKVM_RUNMODE%/server/ && ./sparkd %*" From 81afd71d840d2058f87e472617d77c0e01fa4c96 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 27 Sep 2018 16:59:20 -0400 Subject: [PATCH 101/203] GridSettings dialog - column vms maintain immutable properties through load-factory call --- .../Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs | 6 +----- .../view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs | 6 +++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs index eddd45329..4072621bd 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs @@ -48,12 +48,8 @@ namespace mbts.ui { public SolidColorBrush selection_highlight => _selection_highlight.Value; public SimpleGridSettingsVM create_vm(GridSettings settings, IObservable title) { - var vm = new SimpleGridSettingsVM(title); + var vm = new SimpleGridSettingsVM(title, _column_flags.ToDictionary(kv => kv.Key, kv => kv.Value.flags)); vm.load_from(settings); - foreach (var col in vm.columns) { - if (_column_flags.TryGetValue(col.id, out ImmutableColumnSettingStore icss)) - col.apply_flags(icss.flags); - } return vm; } diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs index aff6a61b4..3c7e00327 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs @@ -31,8 +31,10 @@ namespace mbts.spark { readonly Property _font_display_name, _owner_title; readonly Property _font_size; readonly Property _show_predicate_animations, _show_cell_predicate_animations; + readonly IReadOnlyDictionary _column_flags; - public SimpleGridSettingsVM(IObservable owner_title) { + public SimpleGridSettingsVM(IObservable owner_title, IReadOnlyDictionary column_flags = null) { + _column_flags = column_flags ?? new Dictionary(); _owner_title = backing(nameof(owner_title), owner_title); _font_family = backing(nameof(font_family), TextUtils.StandardFontFamily); _font_size = backing(nameof(font_size), FontSizes.Small); @@ -80,6 +82,8 @@ namespace mbts.spark { columns.SetItems(value.columns.Select(col => { var vm = new SimpleColumnVM(this); vm.load_from(col); + if (_column_flags.TryGetValue(vm.id, out GridColumnSettingsFeatureFlags flags)) + vm.apply_flags(flags); return vm; })); if (curr_col_id == null || !columns.TryGetFirst(c => c.id == curr_col_id, out ISimpleColumnVM curr_column)) From d09dbb942252591a8f9e292273d7c83248bd27bb Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 27 Sep 2018 16:10:01 -0500 Subject: [PATCH 102/203] pair programming at its finest --- .../view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs index 3c7e00327..8746a7fb9 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsVM.cs @@ -82,6 +82,9 @@ namespace mbts.spark { columns.SetItems(value.columns.Select(col => { var vm = new SimpleColumnVM(this); vm.load_from(col); + // reapply our favorite column flags + // this comes in handy when performing Load From Factory - the editor will contain + // a consistent view of what can be overridden and what cannot if (_column_flags.TryGetValue(vm.id, out GridColumnSettingsFeatureFlags flags)) vm.apply_flags(flags); return vm; From 3a5f6b331d37e4db833b271f13319f7775f3d336 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Thu, 27 Sep 2018 17:13:54 -0400 Subject: [PATCH 103/203] SPARK-1740 Replace std::vector with boost::small_vector for 100ns improvement. --- src/lib/md/MDConsumerHandle.cpp | 11 ++-- .../md/test/synthetic_book_merge_tests.cpp | 55 +++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/lib/md/MDConsumerHandle.cpp b/src/lib/md/MDConsumerHandle.cpp index 2a9c02174..23c991cec 100644 --- a/src/lib/md/MDConsumerHandle.cpp +++ b/src/lib/md/MDConsumerHandle.cpp @@ -2,9 +2,13 @@ #include "MDConsumerHandle.h" #include "lib/utils/sane_algorithm.h" #include "lib/domain_model/Security.h" +#include #define never bts_assert(false) +template +using small_vector = boost::container::small_vector; + namespace bts { namespace md { @@ -122,8 +126,7 @@ struct DepthTraversal { } }; - -void copy_depth(size_t count, api::Side side, const api::ContractSpecification& spec, std::vector& leg_travs, Level* out_price) { +void copy_depth(size_t count, api::Side side, const api::ContractSpecification& spec, small_vector& leg_travs, Level* out_price) { size_t out_index = 0; bool first = true; while(out_index < count) { @@ -165,8 +168,8 @@ void copy_depth(size_t count, api::Side side, const api::ContractSpecification& } TimeStamp copy_depth(const SyntheticStrategy& synth, size_t count, PriceSize* bid, PriceSize* ask) { - std::vector bid_leg_travs; - std::vector ask_leg_travs; + small_vector bid_leg_travs; + small_vector ask_leg_travs; bid_leg_travs.reserve(synth.legs.size()); ask_leg_travs.reserve(synth.legs.size()); TimeStamp rv = std::numeric_limits::min(); diff --git a/src/lib/md/test/synthetic_book_merge_tests.cpp b/src/lib/md/test/synthetic_book_merge_tests.cpp index 759b7a19a..edaade063 100644 --- a/src/lib/md/test/synthetic_book_merge_tests.cpp +++ b/src/lib/md/test/synthetic_book_merge_tests.cpp @@ -9,6 +9,7 @@ #include #include #include +#include using namespace std; @@ -86,6 +87,8 @@ tuple,vector> make_securities(c l->set_side(side); synthetic->legs.push_back(instr.consumer()); legs.push_back(instr); + // We *should* insert a LegSpreaderParams into the synthetic->strategy.leg_params_map() + // but c++ makes it really troublesome } return { synthetic, legs }; } @@ -221,6 +224,58 @@ TEST_CASE("SPARK-955", "[md][synthetic]") { { {5.00, 3}, {5.25, 1}, {6.00,3}, {6.50,6} }); } +// +// Benchmarks +// + +void synthetic_get_book_speed2(benchmark::State& state) { + shared_ptr synthetic; + vector test_instr; + tie(synthetic, test_instr) = make_securities({ {1.0, 0.25, api::SIDE_BUY}, {2.0, 0.25, api::SIDE_SELL } }); + auto esm8 = test_instr[0]; + auto esu8 = test_instr[1]; + MDConsumerHandle synthetic_handle(synthetic); + + setup_market(esm8.producer(), + {{2594.50, 47}, {2594.25,63}, {2594.00,117}, {2593.75,137}}, + {{2594.75, 52}, {2595.00,138}, {2594.25,119}, {2595.50,128}}); + setup_market(esu8.producer(), + {{2598.50, 14}, {2598.25, 15}, {2598.00,16}, {2597.75, 15}}, + {{2599.75, 1}, {2600.00, 16}, {2600.25,15}, {2600.50, 75}}); + + for (auto _ : state) { + array raw_bids,raw_asks; + TimeStamp t; + synthetic_handle.get_book(t, COCONUT_MAX_DEPTH, raw_bids.data(), raw_asks.data()); + } +} +BENCHMARK(synthetic_get_book_speed2); +void synthetic_get_book_speed3(benchmark::State& state) { + shared_ptr synthetic; + vector test_instr; + tie(synthetic, test_instr) = make_securities({ {1.0, 0.25, api::SIDE_BUY}, {2.0, 0.25, api::SIDE_SELL }, { 1.0, 0.25, api::SIDE_BUY}}); + auto esm8 = test_instr[0]; + auto esu8 = test_instr[1]; + auto esz8 = test_instr[2]; + MDConsumerHandle synthetic_handle(synthetic); + + setup_market(esm8.producer(), + {{2594.50, 47}, {2594.25,63}, {2594.00,117}, {2593.75,137}}, + {{2594.75, 52}, {2595.00,138}, {2594.25,119}, {2595.50,128}}); + setup_market(esu8.producer(), + {{2598.50, 14}, {2598.25, 15}, {2598.00,16}, {2597.75, 15}}, + {{2599.75, 1}, {2600.00, 16}, {2600.25,15}, {2600.50, 75}}); + setup_market(esz8.producer(), + {{2601.25, 20}, {2600.50, 3}, {2600.25, 100}}, + {{2607.25, 3}, {2607.50, 1}, {2608.25, 900}}); + + for (auto _ : state) { + array raw_bids,raw_asks; + TimeStamp t; + synthetic_handle.get_book(t, COCONUT_MAX_DEPTH, raw_bids.data(), raw_asks.data()); + } +} +BENCHMARK(synthetic_get_book_speed3); }}}//namespace \ No newline at end of file From 9dd6fc8832a14805dde6e2a6feed1bc7a2ea3b8a Mon Sep 17 00:00:00 2001 From: John Colvin Date: Fri, 28 Sep 2018 11:10:15 -0400 Subject: [PATCH 104/203] fix CollectionHelper.sync_with_never_duplicate --- src/client/spark/core/Utility/CollectionHelper.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/client/spark/core/Utility/CollectionHelper.cs b/src/client/spark/core/Utility/CollectionHelper.cs index 1b83ea870..fa96a7f15 100644 --- a/src/client/spark/core/Utility/CollectionHelper.cs +++ b/src/client/spark/core/Utility/CollectionHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -14,16 +13,13 @@ namespace mbts.utils { public static void sync_with_never_duplicate(ObservableCollection dest, IEnumerable src, IEqualityComparer comp) { int i = 0; foreach (var val in src.empty_if_null()) { - int curr_index = dest.TakeWhile(dval => !comp.Equals(dval, val)).Count(); - if (curr_index == dest.Count) { - dest.Add(val); - } else if (curr_index == i) { + int curr_index = dest.IndexOf(val); + if (curr_index == i) { continue; - } else if (curr_index < dest.Count) { + } else if (curr_index > 0 && curr_index < dest.Count) { dest.RemoveAt(curr_index); - dest.Insert(i, val); } - ++i; + dest.Insert(i++, val); } } From 20d77d1cf3ed63ad5eb76f30c381071567d59d1f Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Fri, 28 Sep 2018 11:31:26 -0400 Subject: [PATCH 105/203] Cannot link sdk with benchmarks in sparklib so comment them out --- src/lib/md/test/synthetic_book_merge_tests.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/md/test/synthetic_book_merge_tests.cpp b/src/lib/md/test/synthetic_book_merge_tests.cpp index edaade063..6d0f4dc90 100644 --- a/src/lib/md/test/synthetic_book_merge_tests.cpp +++ b/src/lib/md/test/synthetic_book_merge_tests.cpp @@ -1,3 +1,4 @@ +#ifndef _WIN32 #include "catch/catch.hpp" #include "../MDConsumerHandle.h" #include "Co2TestInstrument.h" @@ -9,7 +10,9 @@ #include #include #include +#if 0 #include +#endif using namespace std; @@ -227,7 +230,7 @@ TEST_CASE("SPARK-955", "[md][synthetic]") { // // Benchmarks // - +#if 0 void synthetic_get_book_speed2(benchmark::State& state) { shared_ptr synthetic; vector test_instr; @@ -276,6 +279,7 @@ void synthetic_get_book_speed3(benchmark::State& state) { } } BENCHMARK(synthetic_get_book_speed3); +#endif - -}}}//namespace \ No newline at end of file +}}}//namespace +#endif \ No newline at end of file From 42b43454855941630520bbef28224d62d45ad35c Mon Sep 17 00:00:00 2001 From: John Colvin Date: Fri, 28 Sep 2018 12:10:41 -0400 Subject: [PATCH 106/203] LadderSettings - user can toggle visibility of 'Volume' and 'Last' columns --- .../spark/core/Exceptions/ExceptionBuilder.cs | 3 + .../spark/view/Spark/Ladder/LadderGridHost.cs | 67 +++++++++++++------ .../view/Spark/Ladder/LadderSettingsVM.cs | 15 +++-- .../view/Spark/Ladder/LadderSettingsView.xaml | 23 ++++++- .../Spark/Ladder/LadderSettingsView.xaml.cs | 5 +- .../spark/view/Spark/Ladder/LadderView.xaml | 29 +------- .../view/Spark/Ladder/LadderView.xaml.cs | 53 ++++++++++++++- 7 files changed, 138 insertions(+), 57 deletions(-) diff --git a/src/client/spark/core/Exceptions/ExceptionBuilder.cs b/src/client/spark/core/Exceptions/ExceptionBuilder.cs index 71d91df74..262f451fe 100644 --- a/src/client/spark/core/Exceptions/ExceptionBuilder.cs +++ b/src/client/spark/core/Exceptions/ExceptionBuilder.cs @@ -5,6 +5,9 @@ namespace mbts.utils { public static class Argument { public static ArgumentException must_be(string description, string name) => new ArgumentException($"Must be {description}.", name); + + public static ArgumentException unexpected_case(T value, string name) => + new ArgumentException($"Unexpected {typeof(T)} '{value}.'", name); } } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index 240a52ab6..2048e3a36 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -80,7 +80,13 @@ namespace mbts.spark { DataContextChanged += (s, e) => _connect_vm(); } - enum ColumnType { Order, Bid, Price, Ask, Last, Volume, None } + public enum ColumnType { Order, Bid, Price, Ask, Last, Volume, None } + + public event Action visible_columns_changed; + + public IEnumerable visible_columns => + columns.Where(c => c.settings.show) + .Select(c => c.column_type); public SecurityImmutable security => _vm != null ? _vm.security : SecurityDataHelper.Defaults.SecurityImmutable; public ICommand RecenterCommand { get; } @@ -162,6 +168,18 @@ namespace mbts.spark { set => SetValue(VolumeColumnWidthProperty, value); } + public static readonly DependencyProperty LastColumnWidthProperty = + WPFHelper.create_dp( + nameof(LastColumnWidth), + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + (o, oldval, newval) => o._adjust_column_width(o.columns.First(c => c.column_type == ColumnType.Last), (double)newval - (double)oldval), + 50u); + + public uint LastColumnWidth { + get => (uint)GetValue(LastColumnWidthProperty); + set => SetValue(LastColumnWidthProperty, value); + } + #region Command public ICommand Command { get => (ICommand)GetValue(CommandProperty); @@ -640,8 +658,28 @@ namespace mbts.spark { #endregion void _settings_updated(LadderSettings s) { - bool init = _settings == null; - double prev_row_height = init ? double.NaN : _settings.row_height; + bool init; + double prev_row_height; + if (_settings == null) { + init = true; + prev_row_height = double.NaN; + } else { + init = false; + prev_row_height = _settings.row_height; + // This is a general issue with default-settings: + // "Not every property in the settings object should be shared across windows." + // Column widths are one such property (they are changed from the actual Ladder window, not the settings dialog). + // Below is a hack which prevents overwriting current column widths; until we come up with a better solution this will suffice. + // TODO: fix this by moving such properties from the settings proto into a separate proto (or come up with a better workaround/solution) + copy_col_width(v => v.orders_column); + copy_col_width(v => v.bid_column); + copy_col_width(v => v.price_column); + copy_col_width(v => v.ask_column); + copy_col_width(v => v.volume_column.general); + copy_col_width(v => v.last_column); + void copy_col_width(Func select_col) => + select_col(s).width = select_col(_settings).width; + } // _settings = s; _bid_vis_tup.glyph_context = glyph_context(s.bid_column); @@ -664,10 +702,10 @@ namespace mbts.spark { _visualHost.AddVisualChild(_gridvis); // Orders column orders_column_width = s.orders_column.width; - if (init) { - _ordershost.update_settings(_price_list, _price_comparer, s.row_height, s.orders_column); - foreach (var col in columns) - if (col.settings.show && col.visual_tup != null) { + _ordershost.update_settings(_price_list, _price_comparer, s.row_height, s.orders_column); + foreach (var col in columns) { + if (col.visual_tup != null) { + if (col.settings.show) { _visualHost.AddVisualChild(col.visual_tup.visual); switch (col.column_type) { case ColumnType.Bid: bid_column_width = col.settings.width; break; @@ -679,17 +717,7 @@ namespace mbts.spark { break; } } - } else { - // don't adjust column widths after init - foreach (var col in columns) - if (col.settings.show && col.visual_tup != null) { - _visualHost.AddVisualChild(col.visual_tup.visual); - switch (col.column_type) { - case ColumnType.Volume: - _volume_container.mark_dirty(); - break; - } - } + } } _visualHost.AddVisualChild(_mid_mkt_break_vis); _visualHost.AddVisualChild(_ask_mkt_break_vis); @@ -706,6 +734,7 @@ namespace mbts.spark { if (init) { _scroll_to_center(); } + visible_columns_changed?.Invoke(); } void _init_tactile_adorner() { @@ -715,7 +744,7 @@ namespace mbts.spark { } void _render_grid_background() { - LadderRender.render_grid(_gridvis, columns, _row_count, _settings.row_height, _settings.grid_line_color); + LadderRender.render_grid(_gridvis, columns.Where(c => c.settings.show), _row_count, _settings.row_height, _settings.grid_line_color); } class VisualGlyphTuple { diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs index 7df82b865..5b738b23e 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs @@ -22,7 +22,7 @@ namespace mbts.spark { _bid = new LadderColumnSettingsVM("Bid"), _price = new LadderColumnSettingsVM("Price"), _ask = new LadderColumnSettingsVM("Ask"), - _last = new LadderColumnSettingsVM("Last"); + _last = new LadderColumnSettingsVM("Last", true); VolumeColumnVM _volume = new VolumeColumnVM(); LadderMarketBreakLineVM _bid_mkt_line = new LadderMarketBreakLineVM("Bid"), @@ -148,18 +148,22 @@ namespace mbts.spark { return vm; } - public class LadderColumnSettingsVM : ViewModelBase { + public class LadderColumnSettingsVM : ViewModelBase, ITemplateSelectorKey { readonly Property _font_size; readonly Property _foreground, _background; readonly Property _typeface; readonly DelegateProperty _font_family; readonly DelegateProperty _font_weight; readonly DelegateProperty _font_display_name; + readonly Property _show; + readonly bool _can_toggle_visibility; - public LadderColumnSettingsVM(string name) { + public LadderColumnSettingsVM(string name, bool can_toggle_visibility = false) { this.name = name; + _can_toggle_visibility = can_toggle_visibility; foreground_palettes = new ObservableCollection(ColorPalette.all_presets().Select(p => new ColorPaletteVM(p))); background_palettes = new ObservableCollection(ColorPalette.all_presets().Select(p => new ColorPaletteVM(p))); + _show = backing(nameof(show), true); _typeface = backing(nameof(typeface), TextUtils.StandardTypeface); _font_family = backing(nameof(font_family), () => typeface.FontFamily, nameof(typeface)); _font_weight = backing(nameof(font_weight), () => typeface.Weight, nameof(typeface)); @@ -178,7 +182,7 @@ namespace mbts.spark { public string font_display_name => _font_display_name.Value; public double font_size { get => _font_size.Value; set => _font_size.Value = value; } public uint width { get; set; } - public bool show { get; set; } + public bool show { get => _show.Value; set => _show.Value = value; } public Color foreground { get => _foreground.Value; set => _foreground.Value = value; } public Color background { get => _background.Value; set => _background.Value = value; } public ObservableCollection foreground_palettes { get; } @@ -186,6 +190,7 @@ namespace mbts.spark { public event Action font_size_changed; public virtual string sample_text => "999-56789.01234"; + string ITemplateSelectorKey.key => _can_toggle_visibility ? ui.spark.LadderSettingsView.ColumnVisibilityToggleTemplateKey : null; public void assign_from(LadderColumnSettings settings) { typeface = new Typeface( @@ -275,7 +280,7 @@ namespace mbts.spark { } } - public LadderColumnSettingsVM general { get; } = new LadderColumnSettingsVM("Volume"); + public LadderColumnSettingsVM general { get; } = new LadderColumnSettingsVM("Volume", true); public VolumeAtPriceAnimationVM buy { get; } public VolumeAtPriceAnimationVM sell { get; } public VolumeAtPriceAnimationVM passive { get; } diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml index f54c12f72..c7e47a2ca 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml @@ -82,6 +82,25 @@ + + + + + + + + + + + + + + + + + + + @@ -140,7 +159,7 @@ - + - + diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs index 924287cf7..3b96f7d63 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml.cs @@ -1,4 +1,5 @@ using mbts.ui.converters; +using mbts.ui.shell; using System.ComponentModel; using System.Windows; using System.Windows.Controls; @@ -10,7 +11,9 @@ namespace mbts.ui.spark { /// Interaction logic for LadderSettingsView.xaml /// partial class LadderSettingsView : UserControl { - public const string OrdersColumnSampleTemplateKey = "OrdersColumnSampleTemplate"; + public const string + OrdersColumnSampleTemplateKey = nameof(OrdersColumnSampleTemplateKey), + ColumnVisibilityToggleTemplateKey = nameof(ColumnVisibilityToggleTemplateKey); public LadderSettingsView() => InitializeComponent(); diff --git a/src/client/spark/view/Spark/Ladder/LadderView.xaml b/src/client/spark/view/Spark/Ladder/LadderView.xaml index 41526be79..68c43b28f 100644 --- a/src/client/spark/view/Spark/Ladder/LadderView.xaml +++ b/src/client/spark/view/Spark/Ladder/LadderView.xaml @@ -422,34 +422,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + _column_headers_presenter.Columns.SetItems( + _ladder.visible_columns.Select(t => ColumnFactory.create(_ladder, t))); this.vm = vm; DataContext = vm; DataContextChanged += datacontext_changed; @@ -28,5 +33,49 @@ namespace mbts.spark { e.Handled = true; e.UseDefaultCursors = true; } + + static class ColumnFactory { + public static GridViewColumn create(LadderGridHost host, LadderGridHost.ColumnType type) { + string header = null, width_prop = null; + switch (type) { + case LadderGridHost.ColumnType.Order: + header = "Orders"; + width_prop = nameof(LadderGridHost.orders_column_width); + break; + case LadderGridHost.ColumnType.Bid: + header = "Bid"; + width_prop = nameof(LadderGridHost.bid_column_width); + break; + case LadderGridHost.ColumnType.Price: + header = "Price"; + width_prop = nameof(LadderGridHost.price_column_width); + break; + case LadderGridHost.ColumnType.Ask: + header = "Ask"; + width_prop = nameof(LadderGridHost.ask_column_width); + break; + case LadderGridHost.ColumnType.Volume: + header = "Volume"; + width_prop = nameof(LadderGridHost.VolumeColumnWidth); + break; + case LadderGridHost.ColumnType.Last: + header = "Last"; + width_prop = nameof(LadderGridHost.LastColumnWidth); + break; + default: throw ExceptionBuilder.Argument.unexpected_case(type, nameof(type)); + } + return _create(host, header, width_prop); + } + + static GridViewColumn _create(LadderGridHost host, string header, string width_prop) { + var gvc = new GridViewColumn { Header = header }; + BindingOperations.SetBinding(gvc, GridViewColumn.WidthProperty, new Binding(width_prop) { + Source = host, + UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, + Mode = BindingMode.TwoWay + }); + return gvc; + } + } } } \ No newline at end of file From 51cc64f031df050e715f61ae9524509186e56d42 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Fri, 28 Sep 2018 13:26:35 -0400 Subject: [PATCH 107/203] throttling the vap timer log --- .../sparkd/md/MarketDataAuxiliaryServiceServer.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp index 87f863edb..ab59068da 100644 --- a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp +++ b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp @@ -25,6 +25,7 @@ struct MarketDataAuxiliaryServiceServer::PImpl{ std::unique_ptr trade_timer; int64_t poll_period_ms; VolumeAtPriceState state; + uint16_t log_counter, log_trunc; PImpl(bts::api::ServiceContext& context, const MarketDataAuxiliaryServiceBase::Volume_at_priceStatefulPublisher& publisher) : @@ -33,7 +34,9 @@ struct MarketDataAuxiliaryServiceServer::PImpl{ publisher(publisher), trade_timer(), poll_period_ms(100), - state(){ + state(), + log_counter(0), + log_trunc(10){ } }; @@ -47,8 +50,8 @@ me(new PImpl(context, auto& threadpool = utils::Dispatcher::get_named("TradesQuery"); me->trade_timer = std::make_unique(threadpool, - me->poll_period_ms, - [this]() { + me->poll_period_ms, + [this]() { me->relay_service.requestmd(me->state.md_req(), [this](const bts::api::Result& md_reply) { @@ -70,9 +73,12 @@ void MarketDataAuxiliaryServiceServer::_scan_trades(const MDReply& md_reply){ total_unique_levels); bts::TimeStamp t1=bts::Timer::now(); double usecs=static_cast(us_diff (t1,t0)); - LOG(OrderMan,Info,"MarketDataAuxiliaryServiceServer::_scan_trades " + if(me->log_counter++ >= me->log_trunc){ + LOG(OrderMan,Info,"MarketDataAuxiliaryServiceServer::_scan_trades " "trades:{} tot_unique:{} usecs:{}", n_trades,total_unique_levels,usecs); + me->log_counter=0; + } me->publisher.flush(); } From 49796a5c33b5c2ee5a3e5f06c08c77fce7265f32 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Fri, 28 Sep 2018 17:35:01 -0400 Subject: [PATCH 108/203] Only publish final settlements. --- .../md/platforms/mdp3_onixs/MDP3Listener.h | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h index 1c9c599d6..e29a6eb72 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h @@ -106,8 +106,18 @@ public: // Misc template void onSettlementPrice(const onixs::Security& sec, const StatisticsArgs& args); - void onSettlementPrice(onixs::Handler&, const onixs::Security& s, const onixs::DailyStatistics49Args& arg) override { onSettlementPrice(s, arg); } - void onSettlementPrice(onixs::Handler&, const onixs::Security& s, const onixs::DailyStatistics33Args& arg) override { onSettlementPrice(s, arg); } + void onSettlementPrice(onixs::Handler&, const onixs::Security& s, const onixs::DailyStatistics49Args& arg) override { + auto type = arg.entry().settlPriceType(); + if (type.final() && type.actual()) { + onSettlementPrice(s, arg); + } + } + void onSettlementPrice(onixs::Handler&, const onixs::Security& s, const onixs::DailyStatistics33Args& arg) override { + auto type = arg.entry().settlPriceType(); + if (type.final() && type.actual()) { + onSettlementPrice(s, arg); + } + } void onSettlementPrice(onixs::Handler&, const onixs::Security& s, const onixs::Recovery52EntryArgs& arg) override { onSettlementPrice(s, arg); } void onSettlementPrice(onixs::Handler&, const onixs::Security& s, const onixs::Recovery38EntryArgs& arg) override { onSettlementPrice(s, arg); } @@ -435,6 +445,7 @@ void MDP3Listener::onSettlementPrice(const onixs::Security& sec, const Statistic onixs::Timestamp time; args.entry().entryPx(price); args.entry().tradingReferenceDate(time); + // args.entry(). auto display_factor = data->handle.security().display_factor; data->handle.update_settlement( utils::TimeStamp::now(), @@ -458,16 +469,19 @@ void MDP3Listener::onOpenInterest(const onixs::Security& sec, const InterestArgs template void MDP3Listener::onSessionOpeningPrice(const onixs::Security& sec, const SessionArgs& args) { tracepoint (bts_trace,trace_order,(char*) "onOpeningPrice.session", _latest_sequence, (uint16_t) _latest_sequence, 0); - auto* data = _index.get_security(sec.id()); - if (data != nullptr) { - onixs::Decimal price = args.entry().entryPx(); - onixs::Timestamp time = args.message().transactTime(); - auto display_factor = data->handle.security().display_factor; - data->handle.update_opening( - utils::TimeStamp::now(), - double_of_price(price, display_factor), - date_of_timestamp(time)); - } + // auto type = args.entry().settlPriceType(); + // if (type.final() && type.actual()) { + auto* data = _index.get_security(sec.id()); + if (data != nullptr) { + onixs::Decimal price = args.entry().entryPx(); + onixs::Timestamp time = args.message().transactTime(); + auto display_factor = data->handle.security().display_factor; + data->handle.update_opening( + utils::TimeStamp::now(), + double_of_price(price, display_factor), + date_of_timestamp(time)); + } + // } template void MDP3Listener::onRecoveryOpeningPrice(const onixs::Security& sec, const RecoveryArgs& args) { From 0f339f603b25b7dd146f866559e6f0393d355b0f Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Mon, 1 Oct 2018 09:36:25 -0400 Subject: [PATCH 109/203] Fix: Only publish final settlements --- src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h index e29a6eb72..6c53abdd7 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h @@ -108,13 +108,13 @@ public: void onSettlementPrice(const onixs::Security& sec, const StatisticsArgs& args); void onSettlementPrice(onixs::Handler&, const onixs::Security& s, const onixs::DailyStatistics49Args& arg) override { auto type = arg.entry().settlPriceType(); - if (type.final() && type.actual()) { + if (type.finalDaily() && type.actual()) { onSettlementPrice(s, arg); } } void onSettlementPrice(onixs::Handler&, const onixs::Security& s, const onixs::DailyStatistics33Args& arg) override { auto type = arg.entry().settlPriceType(); - if (type.final() && type.actual()) { + if (type.finalDaily() && type.actual()) { onSettlementPrice(s, arg); } } From ef03b873570d30ea59194a55803682eeb5739b7c Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 1 Oct 2018 09:26:36 -0500 Subject: [PATCH 110/203] Adding settings to Fills --- .../spark/Modules/Trading/Fills/FillsVM.cs | 99 ++++++++++++------- .../Modules/Trading/Fills/FillsWindow.cs | 49 ++++++++- .../Trading/Fills/FillsWindowFactory.cs | 87 +++++++++++++--- .../Modules/Trading/TradingShellModule.cs | 2 +- .../UserSettings/UserSettings.cs | 12 ++- .../Controls/DIYGrid/Columns/ColumnBase.cs | 6 +- 6 files changed, 199 insertions(+), 56 deletions(-) diff --git a/src/client/spark/Modules/Trading/Fills/FillsVM.cs b/src/client/spark/Modules/Trading/Fills/FillsVM.cs index f4de61a8c..46931d7bf 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsVM.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsVM.cs @@ -1,6 +1,7 @@ using bts.api; using bts.api.orderman; using bts.spark; +using mbts.persist; using mbts.spark.filters; using mbts.spark.utilities; using mbts.ui; @@ -23,13 +24,16 @@ using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase; namespace mbts.spark { class FillsVM : SecurityGrid { readonly OrdersModel _model; - - public FillsVM(IBus bus, OrdersModel model, StartupData state) - : this(bus, model, _get_observable(bus, model, state), state.sorting_criteria, state.filters) { } + IWindowSettingsManager _settings_manager; - FillsVM(IBus bus, OrdersModel model, IObservable> fills_source, IReadOnlyCollection sorting_criteria, FilterStates filters) - : base(RXCollection.CreateIncrementalUpdate(fills_source), sorting_criteria) { + + public FillsVM(IBus bus, OrdersModel model, IObservable state_obs, StartupData state, IWindowSettingsManager settings_manager) + : this(bus, model, state_obs, _get_observable(bus, model, state), settings_manager) { } + + FillsVM(IBus bus, OrdersModel model, IObservable state_obs, IObservable> fills_source, IWindowSettingsManager settings_manager) + : base(RXCollection.CreateIncrementalUpdate(fills_source), null) { _model = model; + _settings_manager = settings_manager; account_filter = DiscreteValuesFilterVM.Create(_model.factory.subscribe_accounts().Select(d => d.Values), r => r.account, nameof(Row.account)); user_filter = DiscreteValuesFilterVM.Create(_model.factory.subscribe_users().Select(d => d.Values), r => r.user, nameof(Row.user)); security_filter = DiscreteValuesFilterVM.Create( @@ -44,30 +48,6 @@ namespace mbts.spark { SecurityDataHelper.EqualityComparer.IDComparer, Comparer.Default ); - if (filters != null) { - fill_time_filter.load_state(filters.fill_time); - time_in_force_filter.load_state(filters.time_in_force); - order_type_filter.load_state(filters.order_type); - account_filter.load_state(filters.account); - user_filter.load_state(filters.user); - exchange_filter.load_state(filters.exchange); - security_filter.load_state(filters.security); - side_filter.load_state(filters.side); - price_filter.load_state(filters.price); - size_filter.load_state(filters.size); - fill_kind_filter.load_state(filters.fill_kind); - fill_key_filter.load_state(filters.fill_key); - order_key_filter.load_state(filters.order_key); - security_key_filter.load_state(filters.security_key); - } else { - fill_time_filter.load_state( - new TimeFilterTuple(ui.controls.TimeFilterMode.CMEOpen, - TimeTextTuple.FromDateTime(DateTime.Today), - TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1)) - ) - ); - fill_kind_filter.load_state(EnumHelper.get_values_except(FillKind.Sod).ToArray()); - } // columns add_col(Columns.Security, DefaultColumnHeaders.Security, r => r.security.ToString(), nameof(Row.security), security_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Security); add_col(Columns.FillTime, DefaultColumnHeaders.FillTime, r => r.fill_time.ToString("M/d h:mm:ss tt"), nameof(Row.fill_time), fill_time_filter, DataGridFilterKnownTemplates.TimeFilter, DefaultColumnWidths.Time); @@ -75,30 +55,75 @@ namespace mbts.spark { add_col(Columns.Account, DefaultColumnHeaders.Account, r => r.account, nameof(Row.account), account_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Account); add_col(Columns.Exchange, DefaultColumnHeaders.Exchange, r => r.exchange.ToString(), nameof(Row.exchange), exchange_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Exchange); add_col(Columns.TimeInForce, DefaultColumnHeaders.TimeInForce, r => r.time_in_force.ToString(), nameof(Row.time_in_force), time_in_force_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.TimeInForce); - columns.Add(new OrderTypeColumn(Columns.OrderType, new Binding(nameof(Row.order_type)), new Binding(nameof(Row.order_metadata)), order_type_filter) { sort_member_path = nameof(Row.order_type) }); - columns.Add(new SideColumn(Columns.Side, r => r.side, side_filter) { sort_member_path = nameof(Row.side) }); + columns.Add(new OrderTypeColumn(Columns.OrderType, new Binding(nameof(Row.order_type)), new Binding(nameof(Row.order_metadata)), order_type_filter) { id = col_id(Columns.OrderType), sort_member_path = nameof(Row.order_type) }); + columns.Add(new SideColumn(Columns.Side, r => r.side, side_filter) { id = col_id(Columns.Side), sort_member_path = nameof(Row.side) }); add_col(Columns.Size, DefaultColumnHeaders.Size, r => r.size.ToString(), nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.Size); add_col(Columns.Price, DefaultColumnHeaders.Price, r => r.format_price(r.price), nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price); - add_col(Columns.FillKind, "Fill Kind", r => r.fill_kind.ToString(), nameof(Row.fill_kind), fill_kind_filter, DataGridFilterKnownTemplates.DiscreteValues, 70); + add_col(Columns.FillKind, DefaultColumnHeaders.FillKind, r => r.fill_kind.ToString(), nameof(Row.fill_kind), fill_kind_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.FillKind); add_col(Columns.FillKey, DefaultColumnHeaders.FillKey, r => r.fill_key.ToString(), nameof(Row.fill_key), fill_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.FillKey); add_col(Columns.OrderKey, DefaultColumnHeaders.OrderKey, r => r.order_key.ToString(), nameof(Row.order_key), order_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.OrderKey); add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey); // void add_col(Columns col_key, string header, Func get_text, string prop, IDataGridColumnFilter filter, string filter_template, double width) => - columns.Add(new RenderTextColumn(col_key, get_text, filter, filter_template) { + columns.Add(new RenderTextColumn(col_key, get_text, filter, filter_template) { + id = col_id(col_key), Header = header, sort_member_path = prop, Width = width }); + grid_settings_manager = new SimpleGridSettingsManager(columns, new Dictionary { }, row_vms); + // this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.) + grid_settings_manager.load_from(settings_manager.current_settings()); + // - _resources.Add(DataGridFilterHelper.hook_filters(rows_view, - fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter, - security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter - )); + _resources.AddRange( + state_obs.OnDispatcher().Subscribe(s => + { + using (rows_view.DeferRefresh()) { + if (s.grid != null) { + // this means we are not using defaults, + // Note: settings_manager in turn notifies grid_settings_manager of this update + _settings_manager.instance_settings = s.grid; + } + if (s.filters is FilterStates filters) { + fill_time_filter.load_state(filters.fill_time); + time_in_force_filter.load_state(filters.time_in_force); + order_type_filter.load_state(filters.order_type); + account_filter.load_state(filters.account); + user_filter.load_state(filters.user); + exchange_filter.load_state(filters.exchange); + security_filter.load_state(filters.security); + side_filter.load_state(filters.side); + price_filter.load_state(filters.price); + size_filter.load_state(filters.size); + fill_kind_filter.load_state(filters.fill_kind); + fill_key_filter.load_state(filters.fill_key); + order_key_filter.load_state(filters.order_key); + security_key_filter.load_state(filters.security_key); + } else { + fill_time_filter.load_state( + new TimeFilterTuple(ui.controls.TimeFilterMode.CMEOpen, + TimeTextTuple.FromDateTime(DateTime.Today), + TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1)) + ) + ); + fill_kind_filter.load_state(EnumHelper.get_values_except(FillKind.Sod).ToArray()); + } + rows_view.set_sort_descriptions(s.sorting_criterion, columns); + } + }), + DataGridFilterHelper.hook_filters(rows_view, + fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter, + security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter + ), + settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager) + ); } public ObservableCollection columns { get; } = new ObservableCollection(); + public SimpleGridSettingsManager grid_settings_manager { get; } + #region Filters public IDataGridColumnPersistentFilter fill_time_filter { get; } = new TimeFilterVM(r => r.fill_time, nameof(Row.fill_time)); public IDataGridColumnPersistentFilter time_in_force_filter { get; } = DiscreteValuesFilterVM.Create(EnumHelper.get_values().ToLArray(), r => r.time_in_force, nameof(Row.time_in_force)); diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs index 325ac30f3..beec072bf 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs @@ -2,9 +2,16 @@ using bts.api; using bts.shell; using bts.spark; +using mbts.persist; using mbts.ui; +using mbts.ui.settings; using mbts.ui.spark; +using mbts.ui.utility; using mbts.utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; using System.Windows; using StartupData = bts.shell.FillsWindowFactory.StartupData; @@ -13,18 +20,26 @@ namespace mbts.spark { const string _window_type = "Fills", _thread_id = "Fills"; readonly AllowOnce _init_guard = new AllowOnce("Fills"); readonly OrdersModel _orders_model; + readonly IWindowSettingsManager _settings_manager; FillsVM _vm; FillsView _view; StartupData _state; + IReadOnlyList _commands; + IDisposable _subscriptions; - public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state) + public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, WindowSettingsManager settings_manager) : base("Fills", bus, factory, SparkCommands.ShowFills.icon) { _orders_model = orders_model; _state = state; + _settings_manager = settings_manager; } + event Action _state_changed; + public override string window_type => _window_type; public override UIElement Content => _view; + public override IEnumerable Commands => _commands; + public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.GetSortingCriteria(), _vm.get_filters()); public override double MinWidth => 1050 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; public string thread_id => _thread_id; @@ -32,10 +47,38 @@ namespace mbts.spark { public void thread_init() { _init_guard.check_access(); Log.notnull(_state); - _vm = new FillsVM(_bus, _orders_model, _state); + var state_src = Observable.FromEvent, FillsWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state); + // Jake: on Monday start passing _state_src to the VM, do the OnDispatcher in the VM too + // there is probably other crap too + _vm = new FillsVM(_bus, _orders_model, state_src, _state, _settings_manager); _view = new FillsView { DataContext = _vm }; + + _commands = new List { + // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes + SparkCommands.window_settings_command(new DialogCommand(() => { + var vm = new WindowSettingsDialogVM( + _settings_manager, us => us.fills_grid, UserSettings.FileExtensions.GRID, s => _vm.grid_settings_manager.create_vm(s, this.observe_title())); + // TODO: bind to main window's Title + var dialog = new SimpleGridSettingsDialog { Title = $"{_window_type} Grid Settings", DataContext = vm }; + vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); + return dialog; + })) + }; + _subscriptions = Disposable.Create(() => + _state_changed?.Invoke( + _state = new FillsWindowFactory.StartupData(_state.orders, + _vm.rows_view.GetSortingCriteria(), + _vm.get_filters(), + _vm.grid_settings_manager.create() + ) + ) + ); } - public override void Dispose() => Disposable.dispose(ref _vm); + public override void Dispose() { + Disposable.dispose(ref _vm); + //_vm.Dispose(); + Commands.OfType().DisposeAll(); + } } } \ No newline at end of file diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindowFactory.cs b/src/client/spark/Modules/Trading/Fills/FillsWindowFactory.cs index a4021e500..8175a03d2 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindowFactory.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindowFactory.cs @@ -1,49 +1,114 @@ using bts.api; using bts.api.orderman; +using mbts.persist; using mbts.spark; using mbts.spark.filters; using mbts.ui; +using mbts.ui.settings; using ProtoBuf; using System; using System.Collections.Generic; +using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; +using static mbts.ui.controls.DIYGrid; +using static mbts.ui.spark.FillsView; namespace bts.shell { class FillsWindowFactory : ShellWindowFactory { readonly IBus _bus; readonly OrdersModel _orders_model; + readonly UserSettingsHub _settings_hub; - public FillsWindowFactory(TradingShellModule module, IBus bus, OrdersModel orders_model) + public FillsWindowFactory(TradingShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) : base(module, "Fills") { _bus = bus; _orders_model = orders_model; + _settings_hub = settings_hub; _dispose = bus.subscribe(SparkTags.ShowFillsTag).OnDispatcher().Subscribe(show); } + public static string col_id(Columns col) => col.ToString(); + public override Task load(StartupData state) => - Task.FromResult(new FillsWindow(this, _bus, _orders_model, state)); + Task.FromResult(_create(state)); public async void show(StartupData state) => _bus.post(SparkTags.ShowWindow, await load(state)); + IShellWindow _create(StartupData state) { + var settings_manager = new WindowSettingsManager(_settings_hub, + state.grid, + get: us => us.fills_grid, + set: (us, gs) => us.with(provided_fills_gridsettings: gs)); + return new FillsWindow(this, _bus, _orders_model, state, settings_manager); + } + [ProtoContract] public class StartupData { public const int Version = 1; - public StartupData() { } - public StartupData(uint[] orders) : this(orders, null, null) { } - public StartupData(FilterStates filters) : this(null, null, filters) { } - public StartupData(uint[] orders, List sorting_criteria, FilterStates filters) { - this.orders = orders; - this.sorting_criteria = sorting_criteria; - this.filters = filters; - } + [ProtoMember(1)] public uint[] orders; [ProtoMember(2)] - public List sorting_criteria; + List _sorting_criteria; [ProtoMember(3)] public FilterStates filters; + [ProtoMember(4)] + public GridSettings grid; + + public StartupData() { } + public StartupData(uint[] orders) : this(orders, null, null) { } + public StartupData(FilterStates filters) : this(null, null, filters) { } + public StartupData(uint[] orders, List sorting_criteria, FilterStates filters, GridSettings grid = default) { + this.orders = orders; + this._sorting_criteria = sorting_criteria; + this.filters = filters; + this.grid = grid; + } + + public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion); + public IReadOnlyList sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList()); + public static StartupData Default() => + new StartupData(null, null, new FilterStates(), null); + + static class DefaultColumns { + public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security); + public static GridColumnSettings fill_time => new GridColumnSettings(col_id(Columns.FillTime), DefaultColumnHeaders.FillTime, width: DefaultColumnWidths.Time); + public static GridColumnSettings user => new GridColumnSettings(col_id(Columns.User), DefaultColumnHeaders.User, width: DefaultColumnWidths.User); + public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account); + public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange); + public static GridColumnSettings time_in_force => new GridColumnSettings(col_id(Columns.TimeInForce), DefaultColumnHeaders.TimeInForce, width: DefaultColumnWidths.TimeInForce); + public static GridColumnSettings order_type => new GridColumnSettings(col_id(Columns.OrderType), DefaultColumnHeaders.OrderType, width: DefaultColumnWidths.OrderType); + public static GridColumnSettings side => new GridColumnSettings(col_id(Columns.Side), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Side); + public static GridColumnSettings quantity => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind); + public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey); + public static IEnumerable default_sorting_criterion { + get { + yield return new SortingCriteria(fill_time.id, false); + } + } + + public static IEnumerable all { + get { + yield return security; + yield return fill_time; + yield return user; + yield return account; + yield return exchange; + yield return time_in_force; + yield return order_type; + yield return side; + yield return quantity; + yield return price; + yield return fill_kind; + yield return security_key; + } + } + } + } [ProtoContract] diff --git a/src/client/spark/Modules/Trading/TradingShellModule.cs b/src/client/spark/Modules/Trading/TradingShellModule.cs index 9924f00ff..84e25772d 100644 --- a/src/client/spark/Modules/Trading/TradingShellModule.cs +++ b/src/client/spark/Modules/Trading/TradingShellModule.cs @@ -22,7 +22,7 @@ namespace bts.shell { public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model) { _lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model); _obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model); - _fwf = new FillsWindowFactory(this, bus, orders_model); + _fwf = new FillsWindowFactory(this, bus, settings_hub, orders_model); _plwf = new PAndLWindowFactory(this, bus, pandl_model); } diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs index f0eda3dbf..63eee22fa 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs @@ -27,6 +27,8 @@ namespace mbts.spark { GridSettings _market_view_grid; [ProtoMember(6)] GridSettings _time_and_sales; + [ProtoMember(7)] + GridSettings _fills_grid; public UserSettings() { } @@ -34,13 +36,19 @@ namespace mbts.spark { public NotificationSettings notification => _notification ?? (_notification = NotificationSettings.Default); public GridSettings market_view_grid => _market_view_grid ?? (_market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings); public GridSettings time_and_sales => _time_and_sales ?? (_time_and_sales = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all)); + public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings); - public UserSettings with(LadderSettings ladder = null, NotificationSettings notification = null, GridSettings provided_market_view_gridsettings = null, GridSettings time_and_sales = null) => + public UserSettings with(LadderSettings ladder = null, + NotificationSettings notification = null, + GridSettings provided_market_view_gridsettings = null, + GridSettings time_and_sales = null, + GridSettings provided_fills_gridsettings = null) => new UserSettings { _ladder = ladder ?? _ladder, _notification = notification ?? _notification, _market_view_grid = provided_market_view_gridsettings ?? _market_view_grid, - _time_and_sales = time_and_sales ?? _time_and_sales + _time_and_sales = time_and_sales ?? _time_and_sales, + _fills_grid = provided_fills_gridsettings ?? _fills_grid }; public static class FileExtensions { diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index a6b455e85..614dfdb5a 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -32,7 +32,8 @@ namespace mbts.ui.controls { TradeKey = 76, User = 53, Aggressor = 80, - Volume = 64; + Volume = 64, + FillKind = 70; } public static class DefaultColumnHeaders { @@ -63,7 +64,8 @@ namespace mbts.ui.controls { Settlement = "Settlement", DayChange = "Change", SessionLow = "Low", - SessionHigh = "High"; + SessionHigh = "High", + FillKind = "Fill Kind"; } public interface IKeyedColumn { From 08a6daab00aa120be714f3ce5887397b1995f5ad Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 1 Oct 2018 09:28:32 -0500 Subject: [PATCH 111/203] misc --- src/client/spark/Modules/Trading/Fills/FillsWindow.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs index beec072bf..0c8508c41 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs @@ -48,8 +48,6 @@ namespace mbts.spark { _init_guard.check_access(); Log.notnull(_state); var state_src = Observable.FromEvent, FillsWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state); - // Jake: on Monday start passing _state_src to the VM, do the OnDispatcher in the VM too - // there is probably other crap too _vm = new FillsVM(_bus, _orders_model, state_src, _state, _settings_manager); _view = new FillsView { DataContext = _vm }; @@ -77,7 +75,6 @@ namespace mbts.spark { public override void Dispose() { Disposable.dispose(ref _vm); - //_vm.Dispose(); Commands.OfType().DisposeAll(); } } From e15764eb2b8369807d92822712ea1de1d8f27f55 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 1 Oct 2018 10:48:04 -0500 Subject: [PATCH 112/203] Fills - save new grid settings with state --- src/client/spark/Modules/Trading/Fills/FillsWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs index 0c8508c41..df5abd8d4 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs @@ -40,7 +40,7 @@ namespace mbts.spark { public override UIElement Content => _view; public override IEnumerable Commands => _commands; - public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.GetSortingCriteria(), _vm.get_filters()); + public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.get_sorting_criteria(_vm.columns), _vm.get_filters(), _vm.grid_settings_manager.create()); public override double MinWidth => 1050 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; public string thread_id => _thread_id; From 2da1644f8efe5df342071cdce4bd2881ba663eba Mon Sep 17 00:00:00 2001 From: John Colvin Date: Mon, 1 Oct 2018 14:28:28 -0400 Subject: [PATCH 113/203] KeyDrivenObservableSeries - check 'is_disposed' before subscribing to next inner observable --- .../spark/core/Utility/KeyDrivenObservable.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/client/spark/core/Utility/KeyDrivenObservable.cs b/src/client/spark/core/Utility/KeyDrivenObservable.cs index b26f9a5d9..68a0211ae 100644 --- a/src/client/spark/core/Utility/KeyDrivenObservable.cs +++ b/src/client/spark/core/Utility/KeyDrivenObservable.cs @@ -17,7 +17,8 @@ namespace mbts.utils { /// class KeyDrivenObservableSeries : ISubject, IDisposable { readonly Func> _selector; - object _lock = new object(); + readonly object _lock = new object(); + bool _is_disposed; Subject _value_subject = new Subject(); IDisposable _current_key_sub; @@ -31,15 +32,19 @@ namespace mbts.utils { // need to lock bc simultaneous callbacks on diff threads could generate 2 subscriptions at once, // only 1 of which would get disposed in the next callback. Disposable.dispose(ref _current_key_sub); - _current_key_sub = _selector(key)?.Subscribe(_value_subject); + if (!_is_disposed) + _current_key_sub = _selector(key)?.Subscribe(_value_subject); } } public IDisposable Subscribe(IObserver observer) => _value_subject.Subscribe(observer); public void Dispose() { - Disposable.dispose(ref _current_key_sub); - Disposable.dispose(ref _value_subject); + lock (_lock) { + _is_disposed = true; + Disposable.dispose(ref _current_key_sub); + Disposable.dispose(ref _value_subject); + } } } From a6214a47b592cb7f6daca360c47047bd8e56e225 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Mon, 1 Oct 2018 14:38:14 -0400 Subject: [PATCH 114/203] Intprice subtraction : handle NaN --- src/lib/md/IntPrice.h | 1 + src/lib/md/test/md2_tests.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/lib/md/IntPrice.h b/src/lib/md/IntPrice.h index 90a97cbfb..725c8a6a0 100644 --- a/src/lib/md/IntPrice.h +++ b/src/lib/md/IntPrice.h @@ -58,6 +58,7 @@ class IntPrice { } else { // not sure there's ever a cause for a positive exponent unless we get into scientific computing + if (is_nan() || o.is_nan()) return IntPrice(); bts_assert_d(o.exponent() <= 0 && _exponent <= 0); if (o.exponent() < _exponent) { int8_t delta = _exponent - o.exponent(); diff --git a/src/lib/md/test/md2_tests.cpp b/src/lib/md/test/md2_tests.cpp index caf806ffb..114cf3f0a 100644 --- a/src/lib/md/test/md2_tests.cpp +++ b/src/lib/md/test/md2_tests.cpp @@ -2264,6 +2264,17 @@ TEST_CASE("IntPrice", "[md]") { IntPrice delta = a - b; REQUIRE(delta == IntPrice_from_double<4>(0.222)); } + + SECTION("subtract NAN from something") { + IntPrice a = IntPrice_from_double<4>(100.333); + IntPrice delta = a - INT_NAN; + REQUIRE(delta == INT_NAN); + } + SECTION("subtract something from NAN") { + IntPrice a = IntPrice_from_double<4>(100.333); + IntPrice delta = INT_NAN - a; + REQUIRE(delta == INT_NAN); + } } }}} From 6db1aeea3fcb400ea291a131f67ad7c00e1ff0dc Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Mon, 1 Oct 2018 14:39:04 -0400 Subject: [PATCH 115/203] SPARK-1666 Add tests for synthetic md calculation --- src/lib/md/MDConsumerHandle.cpp | 4 +- .../md/test/synthetic_book_merge_tests.cpp | 46 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/lib/md/MDConsumerHandle.cpp b/src/lib/md/MDConsumerHandle.cpp index 23c991cec..73b1a587d 100644 --- a/src/lib/md/MDConsumerHandle.cpp +++ b/src/lib/md/MDConsumerHandle.cpp @@ -197,8 +197,8 @@ TimeStamp copy_depth(const SyntheticStrategy& synth, size_t count, PriceSize* bi if (leg_params.leg_md_calc() == spark::LegSpreaderParams::NET_CHANGE) { auto settle = synth.legs[x].settlement_price(); for (size_t d = 0; d < COCONUT_MAX_DEPTH; ++d) { - bids[x] = bids[x] - settle; - asks[x] = asks[x] - settle; + bids[d] = bids[d] - settle; + asks[d] = asks[d] - settle; } } diff --git a/src/lib/md/test/synthetic_book_merge_tests.cpp b/src/lib/md/test/synthetic_book_merge_tests.cpp index 6d0f4dc90..f598a03ec 100644 --- a/src/lib/md/test/synthetic_book_merge_tests.cpp +++ b/src/lib/md/test/synthetic_book_merge_tests.cpp @@ -74,11 +74,15 @@ void expect_market(const MDConsumerHandle& handle, const vector bids, const tuple,vector> make_securities(const vector>& leg_descr) { vector legs; auto synthetic = make_shared(); + uint32_t security_key = 100; for (const auto& ldesc : leg_descr) { double quantity, tick_size; api::Side side; std::tie(quantity, tick_size, side) = ldesc; Co2TestInstrument instr("ES", api::SECURITYTYPE_FUTURE); + instr.producer().set_key(security_key); + ++security_key; + auto& s = synthetic->security; s.mutable_contract_spec()->set_low_tick(tick_size); s.mutable_contract_spec()->set_high_tick(tick_size); @@ -90,12 +94,16 @@ tuple,vector> make_securities(c l->set_side(side); synthetic->legs.push_back(instr.consumer()); legs.push_back(instr); - // We *should* insert a LegSpreaderParams into the synthetic->strategy.leg_params_map() - // but c++ makes it really troublesome + } return { synthetic, legs }; } +void set_leg_params(SyntheticStrategy& strat, uint security_key, const spark::LegSpreaderParams& params) { + auto* map = strat.strategy.mutable_leg_params_map(); + map->insert({ security_key, params }); +} + TEST_CASE("SyntheticBookMerge", "[md][synthetic]") { shared_ptr synthetic; vector test_instr; @@ -227,6 +235,40 @@ TEST_CASE("SPARK-955", "[md][synthetic]") { { {5.00, 3}, {5.25, 1}, {6.00,3}, {6.50,6} }); } +TEST_CASE("Synthetic_net_change", "[md][synthetic]") { + shared_ptr synthetic; + vector test_instr; + tie(synthetic, test_instr) = make_securities({ {1.0, 0.01, api::SIDE_BUY}, {2.0, 0.01, api::SIDE_SELL }}); + auto es_buy1 = test_instr[0]; + auto es_sell2 = test_instr[1]; + MDConsumerHandle synthetic_handle{synthetic}; + + // Set legs to use net change + spark::LegSpreaderParams lparam; + lparam.set_leg_md_calc(spark::LegSpreaderParams::NET_CHANGE); + set_leg_params(*synthetic, es_buy1.producer().key(), lparam); + set_leg_params(*synthetic, es_sell2.producer().key(), lparam); + + // set settlement prices + es_buy1.producer().update_settlement(TimeStamp::now(), { 100, 0 }, datetime::today()); + es_sell2.producer().update_settlement(TimeStamp::now(), { 50, 0 }, datetime::today()); + + SECTION("buy 1 sell 2 Net") { + setup_market(es_buy1.producer(), + {{190,1}, {180,11}, {170,4}, {160,10}}, + {{200,2}, {210,6}, {220,8}, {230,12}}); + setup_market(es_sell2.producer(), + {{90,5}, {80,9}, {70,11}, {60,10}, {50,1}}, + {{100,16}, {110,8}, {120,12}, {130,5}}); + + // Validate merge + expect_market(synthetic_handle, + {{-10,1}, {-20,7}, {-40,4}, {-70,4}, {-80,2}, {-100,2}}, + {{20,2}, {40,1}, {50,4}, {70,1}, {80,4}, {90,1}, {100,3}, {110,1}, {120,1}}); + } +} + + // // Benchmarks // From 080c8cdaa61519d5f517917c7e241982e1174f15 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 1 Oct 2018 14:18:00 -0500 Subject: [PATCH 116/203] WindowSettings - Apply button should only Apply to instance settings --- .../spark/view/Spark/Utilities/WindowSettingsDialogVM.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs index e7213624e..cf105a417 100644 --- a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs +++ b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs @@ -36,7 +36,10 @@ namespace mbts.spark { ) }; _left_skirt_items = backing(nameof(left_skirt_items), left_skirt_factory); - _apply_item = new DialogSkirtItem("Apply", new DelegateCommand(apply)); + _apply_item = new DialogSkirtItem("Apply", new DelegateCommand(() => { + use_default = false; + apply(); + })); _ok_item = new DialogSkirtItem("OK", new DelegateCommand(() => { use_default = false; apply(); From 6d03493bfb7dea722e86a765934daba72f57fd17 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 1 Oct 2018 14:21:46 -0500 Subject: [PATCH 117/203] Fills - forgot to add various key columns; don't always save the current GridSetting as Instance settings (they could be defaults) --- src/client/spark/Modules/Trading/Fills/FillsVM.cs | 7 +++++++ src/client/spark/Modules/Trading/Fills/FillsWindow.cs | 2 +- .../spark/Modules/Trading/Fills/FillsWindowFactory.cs | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/client/spark/Modules/Trading/Fills/FillsVM.cs b/src/client/spark/Modules/Trading/Fills/FillsVM.cs index 46931d7bf..8a26b9ad8 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsVM.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsVM.cs @@ -124,6 +124,13 @@ namespace mbts.spark { public SimpleGridSettingsManager grid_settings_manager { get; } + public GridSettings get_grid_save_state() { + if (_settings_manager.instance_settings == null) + return null; + else + return grid_settings_manager.create(); + } + #region Filters public IDataGridColumnPersistentFilter fill_time_filter { get; } = new TimeFilterVM(r => r.fill_time, nameof(Row.fill_time)); public IDataGridColumnPersistentFilter time_in_force_filter { get; } = DiscreteValuesFilterVM.Create(EnumHelper.get_values().ToLArray(), r => r.time_in_force, nameof(Row.time_in_force)); diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs index df5abd8d4..0b2602617 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs @@ -40,7 +40,7 @@ namespace mbts.spark { public override UIElement Content => _view; public override IEnumerable Commands => _commands; - public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.get_sorting_criteria(_vm.columns), _vm.get_filters(), _vm.grid_settings_manager.create()); + public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.get_sorting_criteria(_vm.columns), _vm.get_filters(), _vm.get_grid_save_state()); public override double MinWidth => 1050 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; public string thread_id => _thread_id; diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindowFactory.cs b/src/client/spark/Modules/Trading/Fills/FillsWindowFactory.cs index 8175a03d2..b4e789c2b 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindowFactory.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindowFactory.cs @@ -84,6 +84,8 @@ namespace bts.shell { public static GridColumnSettings quantity => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind); + public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey); + public static GridColumnSettings order_key => new GridColumnSettings(col_id(Columns.OrderKey), DefaultColumnHeaders.OrderKey, width: DefaultColumnWidths.OrderKey); public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey); public static IEnumerable default_sorting_criterion { get { @@ -104,6 +106,8 @@ namespace bts.shell { yield return quantity; yield return price; yield return fill_kind; + yield return fill_key; + yield return order_key; yield return security_key; } } From 1ff0fe0208a3cc992f1d7be340061c4f6d54091a Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 1 Oct 2018 14:47:26 -0500 Subject: [PATCH 118/203] Fills - Changing from using startup_data to an IObservable thereof, as such our fills_source is now an IConnectableObservable --- src/client/spark/Modules/Trading/Fills/FillsVM.cs | 15 ++++++++++----- .../spark/Modules/Trading/Fills/FillsWindow.cs | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/client/spark/Modules/Trading/Fills/FillsVM.cs b/src/client/spark/Modules/Trading/Fills/FillsVM.cs index 8a26b9ad8..ab88250b9 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsVM.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsVM.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Windows.Data; using static bts.api.orderman.Fill.Types; using static bts.shell.FillsWindowFactory; @@ -27,10 +28,10 @@ namespace mbts.spark { IWindowSettingsManager _settings_manager; - public FillsVM(IBus bus, OrdersModel model, IObservable state_obs, StartupData state, IWindowSettingsManager settings_manager) - : this(bus, model, state_obs, _get_observable(bus, model, state), settings_manager) { } + public FillsVM(IBus bus, OrdersModel model, IObservable state_obs, IWindowSettingsManager settings_manager) + : this(bus, model, state_obs, _get_observable(bus, model, state_obs), settings_manager) { } - FillsVM(IBus bus, OrdersModel model, IObservable state_obs, IObservable> fills_source, IWindowSettingsManager settings_manager) + FillsVM(IBus bus, OrdersModel model, IObservable state_obs, IConnectableObservable> fills_source, IWindowSettingsManager settings_manager) : base(RXCollection.CreateIncrementalUpdate(fills_source), null) { _model = model; _settings_manager = settings_manager; @@ -111,12 +112,13 @@ namespace mbts.spark { } rows_view.set_sort_descriptions(s.sorting_criterion, columns); } - }), + }), DataGridFilterHelper.hook_filters(rows_view, fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter, security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter ), - settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager) + settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager), + fills_source.Connect() ); } @@ -176,6 +178,9 @@ namespace mbts.spark { } else return model.on_fill_tuples; } + static IConnectableObservable> _get_observable(IBus bus, OrdersModel model, IObservable state) => + state.SelectManyInSeries(s => _get_observable(bus, model, s)); + public class Row : SecurityRow, IEquatable, IRowVM { readonly OrdersModel _model; readonly FillImmutable _fill; diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs index 0b2602617..95966fa63 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs @@ -48,7 +48,7 @@ namespace mbts.spark { _init_guard.check_access(); Log.notnull(_state); var state_src = Observable.FromEvent, FillsWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state); - _vm = new FillsVM(_bus, _orders_model, state_src, _state, _settings_manager); + _vm = new FillsVM(_bus, _orders_model, state_src, _settings_manager); _view = new FillsView { DataContext = _vm }; _commands = new List { From 795915fddca9edf1714fa0b94061c6b0e64424d4 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 1 Oct 2018 15:03:50 -0500 Subject: [PATCH 119/203] MarketView - don't always save the current GridSetting as Instance settings, they could be defaults --- src/client/spark/Modules/Market/MarketView/MarketViewVM.cs | 7 +++++++ .../spark/Modules/Market/MarketView/MarketViewWindow.cs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs index a8adfe848..edd4daa06 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs @@ -150,6 +150,13 @@ namespace mbts.spark { public SimpleGridSettingsManager grid_settings_manager { get; } + public GridSettings get_grid_save_state() { + if (_settings_manager.instance_settings == null) + return null; + else + return grid_settings_manager.create(); + } + #region Filters public IDataGridColumnPersistentFilter security_filter { get; } public IDataGridColumnPersistentFilter bid_price_filter { get; } = new DoubleMinMax(r => r.bid_price, nameof(Row.bid_price)); diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs index 50991bc72..cc3b0c5ad 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs @@ -40,7 +40,7 @@ namespace mbts.spark { public override UIElement Content => _view; public override IEnumerable Commands => _commands; public override MarketViewWindowFactory.StartupData SaveState => - _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.grid_settings_manager.create(), _vm.rows_view.get_sorting_criteria(_vm.columns)); + _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.get_grid_save_state(), _vm.rows_view.get_sorting_criteria(_vm.columns)); public override double MinWidth => 1035 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; public string thread_id => _thread_id; From 2825ad833fa58717cddab640a558b4cc31101a27 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Tue, 2 Oct 2018 09:37:59 -0500 Subject: [PATCH 120/203] TaS - Fixing new settings; minor refactoring --- .../Modules/Market/TimeAndSales/TimeAndSalesVM.cs | 12 +++++++++++- .../Market/TimeAndSales/TimeAndSalesWindow.cs | 2 +- .../Modules/Market/TimeAndSalesWindowFactory.cs | 10 ++++++---- .../core/ShellInterface/UserSettings/UserSettings.cs | 6 ++++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs index 8c2bfd1c0..a517f8f90 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs @@ -22,11 +22,14 @@ namespace mbts.spark { class TimeAndSalesVM : SecurityGrid { readonly IBus _bus; readonly TimeAndSalesModel _model; + IWindowSettingsManager _settings_manager; + public TimeAndSalesVM(IBus bus, IWindowSettingsManager settings_manager, TimeAndSalesModel model, StartupData startup_data) : base(RXCollection.CreateBuffer(model.trades, 1000), Enumerable.Empty()) { _bus = bus; _model = model; + _settings_manager = settings_manager; show_ladder = DelegateCommandParamObserver.Create(current_row_observable, r => _show_ladder_async(r).ignore(), r => r != null); security_filter = new RegexFilterVM(r => r.security.ToString(), nameof(Row.security)); if (startup_data.filters is FilterStates filters) { @@ -73,8 +76,15 @@ namespace mbts.spark { public DelegateCommandParamObserver show_ladder { get; } public SimpleGridSettingsManager grid_settings_manager { get; } + public GridSettings get_grid_save_state() { + if (_settings_manager.instance_settings == null) + return null; + else + return grid_settings_manager.create(); + } + public StartupData create() => - new StartupData(_model.security_criteria, _get_filter_states(), grid_settings_manager.create(), rows_view.get_sorting_criteria(columns)); + new StartupData(_model.security_criteria, _get_filter_states(), get_grid_save_state(), rows_view.get_sorting_criteria(columns)); private Brush _get_aggressor_brush(Row r) { BTSColor c; diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs index 4c86f3d0a..bf98b9962 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs @@ -75,7 +75,7 @@ namespace mbts.spark { // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( - _settings_manager, s => s.market_view_grid, UserSettings.FileExtensions.GRID, s => _vm.value.grid_settings_manager.create_vm(s, this.observe_title())); + _settings_manager, s => s.time_and_sales_grid, UserSettings.FileExtensions.GRID, s => _vm.value.grid_settings_manager.create_vm(s, this.observe_title())); // TODO: bind to main window's Title var dialog = new SimpleGridSettingsDialog { Title = "Time and Sales Grid Settings", DataContext = vm }; vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); diff --git a/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs b/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs index 26d9a00b2..14136da6c 100644 --- a/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs +++ b/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs @@ -38,7 +38,7 @@ namespace bts.shell { public override Task load(StartupData state) => Task.FromResult(new TimeAndSalesWindow(this, _bus, _consumer, state, SparkCommands.PickTimeAndSales.icon, - new WindowSettingsManager(_settings_hub, state.grid, s => s.time_and_sales, (s, g) => s.with(time_and_sales:g)))); + new WindowSettingsManager(_settings_hub, state.grid, s => s.time_and_sales_grid, (s, g) => s.with(time_and_sales:g)))); public async void show(SecurityCriteria criteria) { var w = await load(new StartupData(criteria)); @@ -59,7 +59,7 @@ namespace bts.shell { public StartupData() { } public StartupData(SecurityCriteria security_criteria) : - this(security_criteria, new FilterStates()) { } + this(security_criteria, new FilterStates(), null, null) { } public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List sorting_criterion = null) { this.security_criteria = security_criteria; @@ -68,10 +68,12 @@ namespace bts.shell { _sorting_criterion = sorting_criterion; } + public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion); + public List sorting_criterion => _sorting_criterion ?? (_sorting_criterion = new List()); public static StartupData Default(SecurityCriteria security_criteria) => - new StartupData(security_criteria, new FilterStates(), sorting_criterion:DefaultColumns.sorting_criterion.ToList()); + new StartupData(security_criteria, new FilterStates(), null, DefaultColumns.default_sorting_criterion.ToList()); public StartupData with(SecurityCriteria security_criteria) => new StartupData(security_criteria, filters, grid); @@ -85,7 +87,7 @@ namespace bts.shell { public static GridColumnSettings trade_key => new GridColumnSettings(col_id(Columns.TradeKey), DefaultColumnHeaders.TradeKey, width: DefaultColumnWidths.TradeKey); public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey); - public static IEnumerable sorting_criterion { + public static IEnumerable default_sorting_criterion { get { yield return new SortingCriteria(time.id, false); } diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs index 63eee22fa..3b77b519a 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs @@ -14,7 +14,9 @@ namespace mbts.spark { new UserSettings { _ladder = LadderSettings.Default, _notification = NotificationSettings.Default, - _market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings + _market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings, + _fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings, + _time_and_sales = bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultGridSettings }; [ProtoMember(1)] @@ -35,7 +37,7 @@ namespace mbts.spark { public LadderSettings ladder => _ladder ?? (_ladder = LadderSettings.Default); public NotificationSettings notification => _notification ?? (_notification = NotificationSettings.Default); public GridSettings market_view_grid => _market_view_grid ?? (_market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings); - public GridSettings time_and_sales => _time_and_sales ?? (_time_and_sales = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all)); + public GridSettings time_and_sales_grid => _time_and_sales ?? (_time_and_sales = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all)); public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings); public UserSettings with(LadderSettings ladder = null, From f776099c98b4fc837030d8c8ef0c22dec3f855fa Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Tue, 2 Oct 2018 09:41:18 -0500 Subject: [PATCH 121/203] TaS: whoops, more refactoring --- .../core/ShellInterface/UserSettings/UserSettings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs index 3b77b519a..bb0f5aca6 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs @@ -16,7 +16,7 @@ namespace mbts.spark { _notification = NotificationSettings.Default, _market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings, _fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings, - _time_and_sales = bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultGridSettings + _time_and_sales_grid = bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultGridSettings }; [ProtoMember(1)] @@ -28,7 +28,7 @@ namespace mbts.spark { [ProtoMember(5)] GridSettings _market_view_grid; [ProtoMember(6)] - GridSettings _time_and_sales; + GridSettings _time_and_sales_grid; [ProtoMember(7)] GridSettings _fills_grid; @@ -37,7 +37,7 @@ namespace mbts.spark { public LadderSettings ladder => _ladder ?? (_ladder = LadderSettings.Default); public NotificationSettings notification => _notification ?? (_notification = NotificationSettings.Default); public GridSettings market_view_grid => _market_view_grid ?? (_market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings); - public GridSettings time_and_sales_grid => _time_and_sales ?? (_time_and_sales = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all)); + public GridSettings time_and_sales_grid => _time_and_sales_grid ?? (_time_and_sales_grid = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all)); public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings); public UserSettings with(LadderSettings ladder = null, @@ -49,7 +49,7 @@ namespace mbts.spark { _ladder = ladder ?? _ladder, _notification = notification ?? _notification, _market_view_grid = provided_market_view_gridsettings ?? _market_view_grid, - _time_and_sales = time_and_sales ?? _time_and_sales, + _time_and_sales_grid = time_and_sales ?? _time_and_sales_grid, _fills_grid = provided_fills_gridsettings ?? _fills_grid }; From 56ba30e7f4f5fa3ebafc0a581bda334f4a0e17d3 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Mon, 1 Oct 2018 13:00:17 -0400 Subject: [PATCH 122/203] added tests --- src/lib/md/PriceSize.h | 7 +- .../spreader_orders/spreader/SpreaderUtils.h | 17 ++-- .../spreader_orders/tests/spreader_tests.cpp | 90 +++++++++++++++++++ .../spreader_orders/tests/tests_common.h | 36 ++++++++ 4 files changed, 142 insertions(+), 8 deletions(-) diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 387b5b40e..66d18ae57 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -26,6 +26,7 @@ int to_exponent(double value); class PriceSize { IntPrice _price; + // merged size uint32_t _size; uint32_t _direct_size; @@ -35,10 +36,10 @@ public: _size(size), _direct_size(size) { } - PriceSize(IntPrice price, Size size, ImpliedSize implied_size) : + PriceSize(IntPrice price, Size direct_size, ImpliedSize implied_size) : _price(price), - _size(size + implied_size.value()), - _direct_size(size) + _size(direct_size + implied_size.value()), + _direct_size(direct_size) { } PriceSize() : _price(INT_NAN), _size(0), _direct_size(0) {} diff --git a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h index 171fc27e8..6701a8fd4 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h +++ b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h @@ -164,14 +164,21 @@ public: // sets book to nan/0 target=Book(); else{ - // offset the book already built above - for(size_t i=0;i(1999.0), + bts::spark::LegSpreaderParams::DIRECT, + bts::spark::LegSpreaderParams::BOOK, + out_book); + + validate_book(out_book, + api::Side::SIDE_BUY, + 0.01, + new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, + new uint16_t[5] {10, 1, 15, 14, 20}, + true); + } + SECTION ("direct, net_change"){ + Book in_book; + fill_book(in_book, + new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, + new uint16_t[5] {10, 1, 15, 14, 20}, + new uint16_t[5] {1, 12, 0, 0, 0}, + api::Side::SIDE_BUY, + true); + Book out_book; + SpreaderUtils::copy_book(in_book, + md::IntPrice_from_double<7>(1999.0), + bts::spark::LegSpreaderParams::DIRECT, + bts::spark::LegSpreaderParams::NET_CHANGE, + out_book); + + validate_book(out_book, + api::Side::SIDE_BUY, + 0.01, + new double[5] {1, 1.01, 1.03, 1.04, 1.1}, + new uint16_t[5] {10, 1, 15, 14, 20}, + true); + } + SECTION ("merged, book"){ + Book in_book; + fill_book(in_book, + new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, + new uint16_t[5] {10, 1, 15, 14, 20}, + new uint16_t[5] {1, 12, 0, 0, 0}, + api::Side::SIDE_BUY, + true); + Book out_book; + SpreaderUtils::copy_book(in_book, + md::IntPrice_from_double<7>(1999.0), + bts::spark::LegSpreaderParams::MERGED, + bts::spark::LegSpreaderParams::BOOK, + out_book); + + validate_book(out_book, + api::Side::SIDE_BUY, + 0.01, + new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, + new uint16_t[5] {11, 13, 15, 14, 20}, + true); + } + SECTION ("merged, net_change"){ + Book in_book; + fill_book(in_book, + new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, + new uint16_t[5] {10, 1, 15, 14, 20}, + new uint16_t[5] {1, 12, 0, 0, 0}, + api::Side::SIDE_BUY, + true); + Book out_book; + SpreaderUtils::copy_book(in_book, + md::IntPrice_from_double<7>(1999.0), + bts::spark::LegSpreaderParams::MERGED, + bts::spark::LegSpreaderParams::NET_CHANGE, + out_book); + + validate_book(out_book, + api::Side::SIDE_BUY, + 0.01, + new double[5] {1, 1.01, 1.03, 1.04, 1.1}, + new uint16_t[5] {11, 13, 15, 14, 20}, + true); + } +} class RV { public: diff --git a/src/lib/orderman/orders/spreader_orders/tests/tests_common.h b/src/lib/orderman/orders/spreader_orders/tests/tests_common.h index 6139c6f99..a12a10b20 100644 --- a/src/lib/orderman/orders/spreader_orders/tests/tests_common.h +++ b/src/lib/orderman/orders/spreader_orders/tests/tests_common.h @@ -250,6 +250,25 @@ SpreaderPreActivationOrderServices::TestOnlySpreaderPreActivationOrderServices _ /**********************************************************************/ /******************************book************************************/ /**********************************************************************/ + +void fill_book(bts::orderman::Book& book, + double* prices, + uint16_t* direct_sizes, + uint16_t* implied_sizes, + bts::api::Side side, + bool delete_arrays){ + bts::orderman::BookSide& bookside = book.side(side); + for (size_t i = 0; i < book.n_levels(); i++) + bookside[i] = md::PriceSize(md::IntPrice_from_double<7>(prices[i]), + direct_sizes[i], + md::ImpliedSize(implied_sizes[i])); + if (delete_arrays){ + delete[] prices; + delete[] direct_sizes; + delete[] implied_sizes; + } +} + void fill_book(bts::orderman::Book& book, double* prices, uint16_t* sizes, @@ -465,6 +484,23 @@ void validate_book (const bts::orderman::Book& b1, const bts::orderman::Book& b2 REQUIRE (b1.ask()[i].size() == b2.ask()[i].size()); } } +void validate_book (const bts::orderman::Book& book, + api::Side side, + double tick_size, + double* prices, + uint16_t* sizes, + bool delete_arrays){ + const BookSide& md=side==api::Side::SIDE_BUY ? book.bid() : book.ask(); + for(size_t i=0; i Date: Tue, 2 Oct 2018 11:26:35 -0400 Subject: [PATCH 123/203] more tests for implied and net_change --- .../spreader_orders/tests/spreader_tests.cpp | 130 +++++++++++++++++- .../spreader_orders/tests/tests_common.h | 32 +++++ 2 files changed, 161 insertions(+), 1 deletion(-) diff --git a/src/lib/orderman/orders/spreader_orders/tests/spreader_tests.cpp b/src/lib/orderman/orders/spreader_orders/tests/spreader_tests.cpp index aea80e192..90323dcce 100644 --- a/src/lib/orderman/orders/spreader_orders/tests/spreader_tests.cpp +++ b/src/lib/orderman/orders/spreader_orders/tests/spreader_tests.cpp @@ -2868,7 +2868,7 @@ TEST_CASE ("spreader_perf_4legs", "[spreader]") std::cout << "4 leg hedges:" << n_hedges << " unit time(usecs):" << hedge_time << std::endl; } } -TEST_CASE ("book addition", "[spreader]"){ +TEST_CASE ("simple book addition", "[spreader]"){ SECTION ("direct, book"){ Book in_book; fill_book(in_book, @@ -2958,6 +2958,134 @@ TEST_CASE ("book addition", "[spreader]"){ true); } } +TEST_CASE ("random book addition", "[spreader]"){ + size_t n_runs=100; + // generates random prices, with tick size=0.01 + size_t n_levels=5; + double* bid_prices=new double[n_levels], + *ask_prices=new double[n_levels]; + uint16_t* direct_bid_sizes=new uint16_t[n_levels], + *implied_bid_sizes=new uint16_t[n_levels], + *direct_ask_sizes=new uint16_t[n_levels], + *implied_ask_sizes=new uint16_t[n_levels]; + double* exp_bid_prices=new double[n_levels], + *exp_ask_prices=new double[n_levels]; + uint16_t* exp_bid_sizes=new uint16_t[n_levels], + *exp_ask_sizes=new uint16_t[n_levels]; + for(size_t i=0;i(settlement_price), + bts::spark::LegSpreaderParams::DIRECT, + bts::spark::LegSpreaderParams::BOOK, + out_book); + validate_book(out_book, + api::Side::SIDE_BUY, + tick_size, + bid_prices, + direct_bid_sizes, + false); + validate_book(out_book, + api::Side::SIDE_SELL, + tick_size, + ask_prices, + direct_ask_sizes, + false); + } + // "direct, net_change" + { + for(size_t i=0;i(settlement_price), + bts::spark::LegSpreaderParams::DIRECT, + bts::spark::LegSpreaderParams::NET_CHANGE, + out_book); + validate_book(out_book, + api::Side::SIDE_BUY, + tick_size, + exp_bid_prices, + direct_bid_sizes, + false); + validate_book(out_book, + api::Side::SIDE_SELL, + tick_size, + exp_ask_prices, + direct_ask_sizes, + false); + } + // merged, book + { + for(size_t i=0;i(settlement_price), + bts::spark::LegSpreaderParams::MERGED, + bts::spark::LegSpreaderParams::BOOK, + out_book); + validate_book(out_book, + api::Side::SIDE_BUY, + tick_size, + bid_prices, + exp_bid_sizes, + false); + validate_book(out_book, + api::Side::SIDE_SELL, + tick_size, + ask_prices, + exp_ask_sizes, + false); + } + // merged, net change + { + for(size_t i=0;i(settlement_price), + bts::spark::LegSpreaderParams::MERGED, + bts::spark::LegSpreaderParams::NET_CHANGE, + out_book); + validate_book(out_book, + api::Side::SIDE_BUY, + tick_size, + exp_bid_prices, + exp_bid_sizes, + false); + validate_book(out_book, + api::Side::SIDE_SELL, + tick_size, + exp_ask_prices, + exp_ask_sizes, + false); + } + } +} class RV { public: diff --git a/src/lib/orderman/orders/spreader_orders/tests/tests_common.h b/src/lib/orderman/orders/spreader_orders/tests/tests_common.h index a12a10b20..179edc2cd 100644 --- a/src/lib/orderman/orders/spreader_orders/tests/tests_common.h +++ b/src/lib/orderman/orders/spreader_orders/tests/tests_common.h @@ -563,4 +563,36 @@ void generate_driver_set(std::vector& drivers, size_t n_legs){ drivers.push_back(d); } } +void generate_random_book(size_t n_levels, + double tick_size, + double top_book, + double top_ask, + // out vars + double* bid_prices, + uint16_t* direct_bid_sizes, + uint16_t* implied_bid_sizes, + double* ask_prices, + uint16_t* direct_ask_sizes, + uint16_t* implied_ask_sizes, + Book& book){ + double b=top_book, + a=top_ask; + for(size_t i=0;i Date: Tue, 2 Oct 2018 10:36:08 -0500 Subject: [PATCH 124/203] TaS - fix default sorting; create GridSettingsManager with rows_vm so that settings changed can be applied to all existing rows and not just new rows --- .../spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs | 4 ++-- src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs index a517f8f90..deceb1fa6 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesVM.cs @@ -61,8 +61,8 @@ namespace mbts.spark { // be sure to load GridSettings AFTER the columns collection has been populated, the grid settings manager looks columns by id to apply the appropriate settings, // if the collection is not populated, the column settings will not be applied - grid_settings_manager = new SimpleGridSettingsManager(columns); - grid_settings_manager.load_from(startup_data.grid); + grid_settings_manager = new SimpleGridSettingsManager(columns, new Dictionary { }, row_vms); + grid_settings_manager.load_from(settings_manager.current_settings()); _resources.AddRange( show_ladder, DataGridFilterHelper.hook_filters( diff --git a/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs b/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs index 14136da6c..ac5f6bc38 100644 --- a/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs +++ b/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs @@ -70,7 +70,7 @@ namespace bts.shell { public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion); - public List sorting_criterion => _sorting_criterion ?? (_sorting_criterion = new List()); + public List sorting_criterion => _sorting_criterion ?? (_sorting_criterion = DefaultColumns.default_sorting_criterion.ToList()); public static StartupData Default(SecurityCriteria security_criteria) => new StartupData(security_criteria, new FilterStates(), null, DefaultColumns.default_sorting_criterion.ToList()); From df7d791ba1454134786a2614c471ebe716781313 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Tue, 2 Oct 2018 14:03:34 -0400 Subject: [PATCH 125/203] GridSettings - hook up fontfamily --- .../Trading/OrderBook/OrderBookColumns.cs | 36 ++++++++++++------- .../HyperlinkColumn.cs | 4 ++- .../FrameworkElementColumns/TextColumn.cs | 4 ++- .../TimeAndSalesColumn.cs | 4 +-- .../Controls/DIYGrid/Columns/RenderColumn.cs | 10 +++--- .../DIYGrid/Settings/GridSettingsView.xaml | 5 ++- 6 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookColumns.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookColumns.cs index fa1695f6b..f605972ea 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookColumns.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookColumns.cs @@ -321,6 +321,7 @@ namespace mbts.spark { ListSortDirection? _cached_sort_direction; readonly string _filter_template; protected readonly Func _get_cell_store; + GlyphContext _root_glyph_context, _child_glyph_context; protected OrderbookColumnBase(Columns col_key, string header, Func get_cell_store, string sort_prop, double width, IDataGridColumnPersistentFilter filter, string filter_template) : base(col_key) { id = key.ToString(); @@ -331,6 +332,12 @@ namespace mbts.spark { this.filter = filter; _filter_template = filter_template; _cached_sort_direction = SortDirection; + font_params_changed += refresh_glyph_contexts; + refresh_glyph_contexts(); + void refresh_glyph_contexts() { + _root_glyph_context = GlyphContext.create(font_family, RenderUtils.root_font_weight); + _child_glyph_context = GlyphContext.create(font_family, RenderUtils.child_font_weight); + } } public IDataGridColumnPersistentFilter filter { get; } @@ -346,6 +353,7 @@ namespace mbts.spark { _cached_sort_direction = new_value; base.OnSortDirectionChanged(new_value); } + protected GlyphContext _get_glyph_context(OrderBookRowVM row) => row.is_child ? _child_glyph_context : _root_glyph_context; } public class SimpleTextColumn : OrderbookColumnBase, IRenderColumn { @@ -355,7 +363,7 @@ namespace mbts.spark { public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) { var cell = _get_cell_store(row); if (!string.IsNullOrEmpty(cell.current.text)) - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, cell.current.text, position, RowHeight, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, cell.current.text, position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, cell.animations); } } @@ -366,7 +374,7 @@ namespace mbts.spark { : base(Columns.Side, "B/S", r => r.side.store, nameof(OrderBookRowVM.side), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues) { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.side.text, position, RowHeight, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.text, position, RowHeight, RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.side.store.animations); } @@ -377,7 +385,7 @@ namespace mbts.spark { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.quantity.text, position, RowHeight, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.text, position, RowHeight, RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.quantity.store.animations); } @@ -388,7 +396,7 @@ namespace mbts.spark { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.filled_quantity.text, position, RowHeight, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.text, position, RowHeight, RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.filled_quantity.store.animations); } @@ -397,7 +405,7 @@ namespace mbts.spark { : base(Columns.Price, "Price", r => r.price.store, nameof(OrderBookRowVM.price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.price.text, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.price.text, position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.price.store.animations); } @@ -406,7 +414,7 @@ namespace mbts.spark { : base(Columns.AvgFilled, "Avg Filled", r => r.avg_price.store, nameof(OrderBookRowVM.avg_price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) { - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.avg_price.text, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.avg_price.text, position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.avg_price.store.animations); } } @@ -416,7 +424,7 @@ namespace mbts.spark { : base(Columns.LastPrice, "Last Filled", r => r.last_price.store, nameof(OrderBookRowVM.last_price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.last_price.text, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.last_price.text, position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.last_price.store.animations); } @@ -425,7 +433,7 @@ namespace mbts.spark { : base(Columns.Time, "Time", r => r.time_created.store, nameof(OrderBookRowVM.time_created), DefaultColumnWidths.Time, filter, DataGridFilterKnownTemplates.TimeFilter) { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.time_created.text, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.time_created.text, position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.time_created.store.animations); } @@ -434,7 +442,7 @@ namespace mbts.spark { : base(Columns.LastChange, "Last Change", r => r.last_change.store, nameof(OrderBookRowVM.last_change), DefaultColumnWidths.Time, filter, DataGridFilterKnownTemplates.TimeFilter) { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => - CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.last_change.text, + CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.last_change.text, position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.last_change.store.animations); } @@ -521,7 +529,9 @@ namespace mbts.spark { toggle_button.SetBinding(ToggleButton.VisibilityProperty, new Binding(nameof(OrderBookRowVM.is_parent)) { Converter = _expander_visiblilty_converter }); toggle_button.SetResourceReference(ToggleButton.StyleProperty, ExpandCollapseToggleStyleKey); // - var security_view = new TextBlock { FontFamily = font_family, FontSize = font_size, Foreground = font_brush }; + var security_view = new TextBlock { Foreground = font_brush }; + security_view.SetBinding(TextBlock.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this }); + security_view.SetBinding(TextBlock.FontSizeProperty, new Binding(nameof(font_size)) { Source = this }); security_view.SetBinding(TextBlock.TextProperty, $"{nameof(OrderBookRowVM.security)}.{nameof(IGridCell.text)}"); security_view.SetBinding(TextBlock.HorizontalAlignmentProperty, new Binding(nameof(horizontal_alignment)) { Source = this }); security_view.SetBinding(TextBlock.ForegroundProperty, new Binding(nameof(font_brush)) { Source = this }); @@ -548,7 +558,10 @@ namespace mbts.spark { #endregion static class RenderUtils { - public static readonly GlyphContext _child_glyph_context = TextUtils.StandardGlyphContext.with(FontWeights.Light); + public static FontWeight + root_font_weight = FontWeights.Normal, + child_font_weight = FontWeights.Light; + public static double font_size = FontSizes.Small; public static readonly IValueConverter @@ -567,7 +580,6 @@ namespace mbts.spark { return mb; } - public static GlyphContext get_glyph_context(OrderBookRowVM r) => r.is_child ? _child_glyph_context : TextUtils.StandardGlyphContext; public static Brush get_side_brush(OrderBookRowVM r) => r.side.value == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask; public static BindingBase get_font_weight_binding() => new Binding(nameof(OrderBookRowVM.is_child)) { Converter = _font_weight_converter }; } diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs index f76c24da7..726764f99 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs @@ -29,8 +29,10 @@ namespace mbts.ui.controls { background_overlay = selection_fill; public override FrameworkElement view_factory() { - var hl = new HyperlinkCell { Foreground = font_brush, Background = background, FontSize = font_size, Padding = padding }; + var hl = new HyperlinkCell { Foreground = font_brush, Background = background, Padding = padding }; hl.SetBinding(HyperlinkCell.TextProperty, _binding); + hl.SetBinding(HyperlinkCell.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this }); + hl.SetBinding(HyperlinkCell.FontSizeProperty, new Binding(nameof(font_size)) { Source = this }); hl.SetBinding(HyperlinkCell.CommandProperty, _command_binding); if (background_overlay != null) { hl.SetBinding(BackgroundOverlayProperty, new Binding(nameof(ListBoxItem.IsSelected)) { diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TextColumn.cs b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TextColumn.cs index 931dae6b6..6f22c40ef 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TextColumn.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TextColumn.cs @@ -22,8 +22,10 @@ namespace mbts.ui.controls { public VerticalAlignment vertical_alignment { get; set; } = VerticalAlignment.Center; public override FrameworkElement view_factory() { - var v = new TextCell { FontFamily = font_family, FontSize = font_size, Foreground = font_brush, HorizontalAlignment = horizontal_alignment, VerticalAlignment = vertical_alignment }; + var v = new TextCell { Foreground = font_brush, HorizontalAlignment = horizontal_alignment, VerticalAlignment = vertical_alignment }; v.SetBinding(TextCell.TextProperty, _binding); + v.SetBinding(TextCell.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this }); + v.SetBinding(TextCell.FontSizeProperty, new Binding(nameof(font_size)) { Source = this }); try_set_binding(TextCell.BackgroundProperty, background_binding); try_set_binding(TextCell.ForegroundProperty, foreground_binding); try_set_binding(TextCell.FontWeightProperty, font_weight_binding); diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TimeAndSalesColumn.cs b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TimeAndSalesColumn.cs index 853681c16..dafdbe936 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TimeAndSalesColumn.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/TimeAndSalesColumn.cs @@ -11,8 +11,8 @@ namespace mbts.ui.controls { internal class TimeAndSalesColumn : RenderTextColumn, IKeyedColumn { readonly Func _get_brush; - public TimeAndSalesColumn(TimeAndSalesView.Columns key, Func get_brush, IDataGridColumnFilter filter = null, string filter_template = null, GlyphContext glyph_context = null) - : base(r => r[key], filter, filter_template, glyph_context) { + public TimeAndSalesColumn(TimeAndSalesView.Columns key, Func get_brush, IDataGridColumnFilter filter = null, string filter_template = null) + : base(r => r[key], filter, filter_template) { this.key = key; // id is used for settings (another case of ProtoBufs vs Generics - strings seemed like the easiest way to provide an identifier for the common GridColumnSettings proto) id = TimeAndSalesWindowFactory.col_id(key); diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/RenderColumn.cs b/src/client/spark/view/Controls/DIYGrid/Columns/RenderColumn.cs index c825a17ea..f6bb51015 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/RenderColumn.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/RenderColumn.cs @@ -25,13 +25,15 @@ namespace mbts.ui.controls { public RenderTextColumn( Func get_text, IDataGridColumnFilter filter = null, - string filter_template = null, - GlyphContext glyph_context = null) + string filter_template = null) : base() { _get_text = get_text; this.filter = filter; this.filter_template = filter_template; - this.glyph_context = glyph_context ?? TextUtils.StandardGlyphContext; + font_params_changed += refresh_glyph_context; + refresh_glyph_context(); + void refresh_glyph_context() => + glyph_context = GlyphContext.create(font_family, font_weight); } public IDataGridColumnFilter filter { get; } @@ -39,7 +41,7 @@ namespace mbts.ui.controls { public Brush background { get; set; } public VerticalAlignment vertical_alignment { get; set; } = VerticalAlignment.Center; public Thickness padding { get; set; } = DefaultCellPadding; - public GlyphContext glyph_context { get; set; } = TextUtils.StandardGlyphContext; + public GlyphContext glyph_context { get; private set; } public override void draw(TRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => CellRender.draw_text( diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsView.xaml b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsView.xaml index 58bd1cc51..7fc1656d4 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsView.xaml +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsView.xaml @@ -342,9 +342,8 @@ - - Font - + Font + From e241d13edc16eb771f7b5aa4a368f93a5bbc127a Mon Sep 17 00:00:00 2001 From: John Colvin Date: Tue, 2 Oct 2018 15:28:56 -0400 Subject: [PATCH 126/203] SPARK-1665 Net Change and Implied market enums in Synthetic UI --- .../Spreader/ExecutionStrategyEditor.xaml | 37 +++++++++++-------- .../Spreader/ExecutionStrategyEditor.xaml.cs | 4 ++ .../Leg/MarketDataCalculationModel.cs | 18 +++++++++ .../ParameterRows/Leg/MarketDataTypeModel.cs | 18 +++++++++ .../Modules/Spreader/VM/SyntheticEditorVM.cs | 2 + 5 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 src/client/spark/Modules/Spreader/VM/ParameterRows/Leg/MarketDataCalculationModel.cs create mode 100644 src/client/spark/Modules/Spreader/VM/ParameterRows/Leg/MarketDataTypeModel.cs diff --git a/src/client/spark/Modules/Spreader/ExecutionStrategyEditor.xaml b/src/client/spark/Modules/Spreader/ExecutionStrategyEditor.xaml index 4dcc5f268..0393f2601 100644 --- a/src/client/spark/Modules/Spreader/ExecutionStrategyEditor.xaml +++ b/src/client/spark/Modules/Spreader/ExecutionStrategyEditor.xaml @@ -46,27 +46,22 @@ Offset="{StaticResource RowHeight}" RowHeaderWidth="{StaticResource RowHeaderWidth}" /> - + + + + - - - - - - - - - - - - - - - @@ -117,6 +112,16 @@ + + + + + + + + + + diff --git a/src/client/spark/Modules/Spreader/ExecutionStrategyEditor.xaml.cs b/src/client/spark/Modules/Spreader/ExecutionStrategyEditor.xaml.cs index e163b95c5..e06d2a588 100644 --- a/src/client/spark/Modules/Spreader/ExecutionStrategyEditor.xaml.cs +++ b/src/client/spark/Modules/Spreader/ExecutionStrategyEditor.xaml.cs @@ -302,6 +302,8 @@ namespace mbts.ui.spark { public DataTemplate IntTemplate { get; set; } public DataTemplate UIntTemplate { get; set; } public DataTemplate QuantityMeasureTemplate { get; set; } + public DataTemplate MarketDataTypeTemplate { get; set; } + public DataTemplate MarketDataCalculationTemplate { get; set; } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Type t = (Type)value; @@ -310,6 +312,8 @@ namespace mbts.ui.spark { else if (t == typeof(int)) return IntTemplate; else if (t == typeof(uint)) return UIntTemplate; else if (t == typeof(QuantityMeasure)) return QuantityMeasureTemplate; + else if (t == typeof(LegSpreaderParams.Types.MarketDataType)) return MarketDataTypeTemplate; + else if (t == typeof(LegSpreaderParams.Types.LegMarketDataCalculation)) return MarketDataCalculationTemplate; else return null; } diff --git a/src/client/spark/Modules/Spreader/VM/ParameterRows/Leg/MarketDataCalculationModel.cs b/src/client/spark/Modules/Spreader/VM/ParameterRows/Leg/MarketDataCalculationModel.cs new file mode 100644 index 000000000..f526589a3 --- /dev/null +++ b/src/client/spark/Modules/Spreader/VM/ParameterRows/Leg/MarketDataCalculationModel.cs @@ -0,0 +1,18 @@ +using mbts.ui.spark; +using LegMarketDataCalculation = bts.spark.LegSpreaderParams.Types.LegMarketDataCalculation; + +namespace mbts.spark { + partial class SyntheticEditorVM { + class MarketDataCalculationModel : RowModelBase { + public override string display_name { get; } = "Market Data Calculation"; + public override string help_text { get; } + public override string man_page { get; } = string.Empty; + + public override LegMarketDataCalculation get_value(LegSpreaderParamsTuple leg_tup) => + leg_tup.leg_params.LegMdCalc; + + public override void set_value(LegSpreaderParamsTuple leg_tup, LegMarketDataCalculation value) => + leg_tup.leg_params.LegMdCalc = value; + } + } +} \ No newline at end of file diff --git a/src/client/spark/Modules/Spreader/VM/ParameterRows/Leg/MarketDataTypeModel.cs b/src/client/spark/Modules/Spreader/VM/ParameterRows/Leg/MarketDataTypeModel.cs new file mode 100644 index 000000000..4e1125380 --- /dev/null +++ b/src/client/spark/Modules/Spreader/VM/ParameterRows/Leg/MarketDataTypeModel.cs @@ -0,0 +1,18 @@ +using mbts.ui.spark; +using MarketDataType = bts.spark.LegSpreaderParams.Types.MarketDataType; + +namespace mbts.spark { + partial class SyntheticEditorVM { + class MarketDataTypeModel : RowModelBase { + public override string display_name { get; } = "Market Data Type"; + public override string help_text { get; } + public override string man_page { get; } = string.Empty; + + public override MarketDataType get_value(LegSpreaderParamsTuple leg_tup) => + leg_tup.leg_params.MarketDataType; + + public override void set_value(LegSpreaderParamsTuple leg_tup, MarketDataType value) => + leg_tup.leg_params.MarketDataType = value; + } + } +} \ No newline at end of file diff --git a/src/client/spark/Modules/Spreader/VM/SyntheticEditorVM.cs b/src/client/spark/Modules/Spreader/VM/SyntheticEditorVM.cs index 96d766b6b..2b162676a 100644 --- a/src/client/spark/Modules/Spreader/VM/SyntheticEditorVM.cs +++ b/src/client/spark/Modules/Spreader/VM/SyntheticEditorVM.cs @@ -203,6 +203,8 @@ namespace mbts.spark { new IsQuoteModel(), new HedgeRatioModel(), new QuoteHiddenModel(), + new MarketDataTypeModel(), + new MarketDataCalculationModel(), new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks), quote_throttle_inside_ticks, quote_throttle_outside_ticks, From 9ef1fdb7955c2e06d3aab1750c37ae7561d887ac Mon Sep 17 00:00:00 2001 From: John Colvin Date: Tue, 2 Oct 2018 16:35:01 -0400 Subject: [PATCH 127/203] ColumnBase raises PropertyChanged for font properties --- .../view/Controls/DIYGrid/Columns/ColumnBase.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index 614dfdb5a..5c85cacd8 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -171,7 +171,7 @@ namespace mbts.ui.controls { public virtual void load_from(GridColumnSettings settings) { if (settings == null) return; - using (ReflectionHelper.detect_property_changes(this, raise_prop_changed, nameof(is_visible), nameof(is_collapsed), nameof(font_weight_override), nameof(font_weight), + using (ReflectionHelper.detect_property_changes(this, _raise_prop_changed, nameof(is_visible), nameof(is_collapsed), nameof(font_weight_override), nameof(font_weight), nameof(font_color_override), nameof(font_color), nameof(font_brush), nameof(font_family), nameof(font_size), nameof(horizontal_alignment), nameof(horizontal_offset))) { // id = settings.id; @@ -189,21 +189,24 @@ namespace mbts.ui.controls { Width = original_width; } // - void raise_prop_changed(string prop) => - OnPropertyChanged(new PropertyChangedEventArgs(prop)); } // 'fallback' font-params (aka 'grid-level' font-params) public void set_fallback_font_params(FontFamily font_family, double font_size, Color font_color, FontWeight font_weight) { - _fallback_font_family = font_family; - _fallback_font_size = font_size; - _fallback_font_color = font_color; - _fallback_font_weight = font_weight; + using (ReflectionHelper.detect_property_changes(this, _raise_prop_changed, nameof(font_family), nameof(font_size), nameof(font_color), nameof(font_weight))) { + _fallback_font_family = font_family; + _fallback_font_size = font_size; + _fallback_font_color = font_color; + _fallback_font_weight = font_weight; + } font_params_changed?.Invoke(); } protected virtual void OnSortDirectionChanged(ListSortDirection? new_value) => sort_direction_changed?.Invoke(this, new_value); + + void _raise_prop_changed(string prop) => + OnPropertyChanged(new PropertyChangedEventArgs(prop)); } internal abstract class ColumnBase : ColumnBase, IKeyedColumn, IGridColumn { From 188616d1cef694d0fbdb0dd25e129fcc3edc84a9 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Tue, 2 Oct 2018 16:46:09 -0400 Subject: [PATCH 128/203] Adding sub event for settlement (which was causing a crash) --- src/lib/orderman/actors/order_manager/OrderManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/orderman/actors/order_manager/OrderManager.cpp b/src/lib/orderman/actors/order_manager/OrderManager.cpp index d47378e67..dfa72e51c 100644 --- a/src/lib/orderman/actors/order_manager/OrderManager.cpp +++ b/src/lib/orderman/actors/order_manager/OrderManager.cpp @@ -385,6 +385,8 @@ void OrderManager::init_order_services() { }; me->services_data.md_subs = std::make_unique>(*this, on_md_sub); + me->services_data.settlement_subs = + std::make_unique>(*this); me->services_data.time_subs = std::make_unique>(*this); me->services_data.order_creation_subs = From 4aa64a7b5d8edb6e9b9e9f877af5ec5060bba22a Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Tue, 2 Oct 2018 16:47:17 -0500 Subject: [PATCH 129/203] Settings - Be sure to launch SimpleGridSettingsDialog with any changes reflected in the vm_factory (user may have made changes on the grid gui which need to be reflected when the settings form launches) --- .../Modules/Market/MarketView/MarketViewWindow.cs | 11 ++++++++++- .../Modules/Market/TimeAndSales/TimeAndSalesWindow.cs | 11 ++++++++++- src/client/spark/Modules/Trading/Fills/FillsWindow.cs | 11 ++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs index cc3b0c5ad..5f7f06ffd 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs @@ -66,7 +66,16 @@ namespace mbts.spark { // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( - _settings_manager, us => us.market_view_grid, UserSettings.FileExtensions.GRID, s => _vm.grid_settings_manager.create_vm(s, this.observe_title())); + _settings_manager, + us => us.market_view_grid, + UserSettings.FileExtensions.GRID, + s => _vm.grid_settings_manager.create_vm( + // we need to get the most up to date GridSettings when launching b/c users may + // have changed some settings on the grid directly, like column widths. + s.with(_vm.columns.Select( c => c.create())), + this.observe_title() + ) + ); // TODO: bind to main window's Title var dialog = new SimpleGridSettingsDialog { Title = "Market View Grid Settings", DataContext = vm }; vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs index bf98b9962..b31b9419c 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs @@ -75,7 +75,16 @@ namespace mbts.spark { // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( - _settings_manager, s => s.time_and_sales_grid, UserSettings.FileExtensions.GRID, s => _vm.value.grid_settings_manager.create_vm(s, this.observe_title())); + _settings_manager, + s => s.time_and_sales_grid, + UserSettings.FileExtensions.GRID, + s => _vm.value.grid_settings_manager.create_vm( + // we need to get the most up to date GridSettings when launching b/c users may + // have changed some settings on the grid directly, like column widths. + s.with(_vm.value.columns.Select( c => c.create())), + this.observe_title() + ) + ); // TODO: bind to main window's Title var dialog = new SimpleGridSettingsDialog { Title = "Time and Sales Grid Settings", DataContext = vm }; vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs index 95966fa63..a16e4fb53 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs @@ -55,7 +55,16 @@ namespace mbts.spark { // wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( - _settings_manager, us => us.fills_grid, UserSettings.FileExtensions.GRID, s => _vm.grid_settings_manager.create_vm(s, this.observe_title())); + _settings_manager, + us => us.fills_grid, + UserSettings.FileExtensions.GRID, + s => _vm.grid_settings_manager.create_vm( + // we need to get the most up to date GridSettings when launching b/c users may + // have changed some settings on the grid directly, like column widths. + s.with(_vm.columns.Select( c => c.create())), + this.observe_title() + ) + ); // TODO: bind to main window's Title var dialog = new SimpleGridSettingsDialog { Title = $"{_window_type} Grid Settings", DataContext = vm }; vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); From 479eba940eabf7fc3550a58d3450877caeffa230 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 3 Oct 2018 12:55:09 -0400 Subject: [PATCH 130/203] Fix build from merge --- src/server/sparkd/md/ChannelVolume.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/sparkd/md/ChannelVolume.cpp b/src/server/sparkd/md/ChannelVolume.cpp index 76687437c..ac047b9c5 100644 --- a/src/server/sparkd/md/ChannelVolume.cpp +++ b/src/server/sparkd/md/ChannelVolume.cpp @@ -27,7 +27,7 @@ const VolumeAtPriceLevel& ChannelVolume::add_trade(const TradeMessage& m, VolumeAtPriceLevel vol; vol.set_record_id(record_id++); vol.set_security_id(m.key()); - auto p = md::PriceSize(m.mantissa(),m.exponent(),0); + auto p = md::PriceSize({m.mantissa(),(int8_t)m.exponent()},0); vol.set_price(p.price()); price_map[m.mantissa()] = vol; price_it=price_map.find(m.mantissa()); From 1a279298fd93ac3e224f6321b38f72efa337b975 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Wed, 3 Oct 2018 13:12:43 -0400 Subject: [PATCH 131/203] added cons to Book to construct it with a security, thats used by the Spreader now --- src/lib/orderman/orderman_utils/Book.h | 5 ++ .../spreader/SpreaderBookkeepingData.cpp | 2 +- .../spreader_orders/spreader/SpreaderUtils.h | 33 +++++++-- .../spreader_orders/tests/spreader_tests.cpp | 67 +++++++++++++++---- .../spreader_orders/tests/tests_common.h | 12 +++- 5 files changed, 98 insertions(+), 21 deletions(-) diff --git a/src/lib/orderman/orderman_utils/Book.h b/src/lib/orderman/orderman_utils/Book.h index 34ef074be..36b6ae386 100644 --- a/src/lib/orderman/orderman_utils/Book.h +++ b/src/lib/orderman/orderman_utils/Book.h @@ -18,6 +18,11 @@ public: _book() {} + Book (SecurityKey key) : + _security_key(key), + _book() + {} + Book (SecurityKey key, BookSide const&bid, BookSide const&ask) : diff --git a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp index 9c87cd6fb..160d6304a 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp +++ b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp @@ -28,7 +28,7 @@ _reported_state(){ for (const auto& lpd_iter : legs){ _leg_prop_data.push_back(lpd_iter); const LegPropertyData& leg_property_data = _leg_prop_data.back(); - _books.emplace_back(); + _books.emplace_back(lpd_iter.leg_security_key()); _quoter_order_builders.emplace_back(spreader_order_key, leg_property_data.quoter_order_definition(), spreader_pao_services, 10); _hedger_order_builders.emplace_back(spreader_order_key, leg_property_data.hedger_order_definition(), spreader_pao_services, 10); ChildOrder empty_order; diff --git a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h index 6701a8fd4..2374c3ea0 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h +++ b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h @@ -139,12 +139,24 @@ public: } bts_assert(out_quantity.value() <= target_quantity.value()); } + // copies the price/qty content of the source book + // into the target, accounting for md_type and leg_calc. + // (note that the security_key in the target is left untouched + // and should be set by the callee) + // this is not very clean for the reason that the spreader + // uses the "size()" function in the book, so we copy the + // "effective size" to the size var in the Book, which + // for different values of md_type would mean different + // things. the better way to do it is to make a SpreaderBook + // that exposes effective_size() and routes it to the + // right size. void static copy_book ( const Book& source, md::IntPrice settlement_price, bts::spark::LegSpreaderParams::MarketDataType md_type, bts::spark::LegSpreaderParams::LegMarketDataCalculation leg_calc, Book& target){ + SecurityKey target_key=target.security_key(); if(md_type==bts::spark::LegSpreaderParams::DIRECT){ // since the "size()" in Book is the merged size // we put the direct in the size @@ -157,17 +169,21 @@ public: target.ask()[i]=md::PriceSize(a.int_price(),a.direct_size()); } } - else if(md_type==bts::spark::LegSpreaderParams::MERGED) - target=source; + else if(md_type==bts::spark::LegSpreaderParams::MERGED){ + target.bid()=source.bid(); + target.ask()=source.ask(); + } if(leg_calc==bts::spark::LegSpreaderParams::NET_CHANGE){ - if(settlement_price.is_nan()) - // sets book to nan/0 - target=Book(); + if(settlement_price.is_nan()){ + empty_out_book(target.bid(),target.n_levels()); + empty_out_book(target.ask(),target.n_levels()); + } else{ offset_book(target.bid(), target.n_levels(),settlement_price); offset_book(target.ask(), target.n_levels(),settlement_price); } } + bts_assert(target_key==target.security_key()); } void static offset_book(BookSide& book_side, size_t n_levels, @@ -179,6 +195,13 @@ public: break; } } + void static empty_out_book (BookSide& book_side, + size_t n_levels){ + md::PriceSize empty_ps; + for(size_t i=0;i(1999.0), bts::spark::LegSpreaderParams::DIRECT, @@ -2885,6 +2886,7 @@ TEST_CASE ("simple book addition", "[spreader]"){ out_book); validate_book(out_book, + key, api::Side::SIDE_BUY, 0.01, new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, @@ -2892,14 +2894,15 @@ TEST_CASE ("simple book addition", "[spreader]"){ true); } SECTION ("direct, net_change"){ - Book in_book; + SecurityKey key(13579); + Book in_book (key); fill_book(in_book, new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, new uint16_t[5] {10, 1, 15, 14, 20}, new uint16_t[5] {1, 12, 0, 0, 0}, api::Side::SIDE_BUY, true); - Book out_book; + Book out_book(key); SpreaderUtils::copy_book(in_book, md::IntPrice_from_double<7>(1999.0), bts::spark::LegSpreaderParams::DIRECT, @@ -2907,21 +2910,47 @@ TEST_CASE ("simple book addition", "[spreader]"){ out_book); validate_book(out_book, + key, api::Side::SIDE_BUY, 0.01, new double[5] {1, 1.01, 1.03, 1.04, 1.1}, new uint16_t[5] {10, 1, 15, 14, 20}, true); } - SECTION ("merged, book"){ - Book in_book; + SECTION ("direct, net_change_nan"){ + SecurityKey key(13579); + Book in_book (key); fill_book(in_book, new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, new uint16_t[5] {10, 1, 15, 14, 20}, new uint16_t[5] {1, 12, 0, 0, 0}, api::Side::SIDE_BUY, true); - Book out_book; + Book out_book(key); + SpreaderUtils::copy_book(in_book, + md::INT_NAN, + bts::spark::LegSpreaderParams::DIRECT, + bts::spark::LegSpreaderParams::NET_CHANGE, + out_book); + + validate_book(out_book, + key, + api::Side::SIDE_BUY, + 0.01, + new double[5] {os::nan(), os::nan(), os::nan(), os::nan(), os::nan()}, + new uint16_t[5] {0, 0, 0, 0, 0}, + true); + } + SECTION ("merged, book"){ + SecurityKey key(24680); + Book in_book (key); + fill_book(in_book, + new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, + new uint16_t[5] {10, 1, 15, 14, 20}, + new uint16_t[5] {1, 12, 0, 0, 0}, + api::Side::SIDE_BUY, + true); + Book out_book(key); SpreaderUtils::copy_book(in_book, md::IntPrice_from_double<7>(1999.0), bts::spark::LegSpreaderParams::MERGED, @@ -2929,6 +2958,7 @@ TEST_CASE ("simple book addition", "[spreader]"){ out_book); validate_book(out_book, + key, api::Side::SIDE_BUY, 0.01, new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, @@ -2936,14 +2966,15 @@ TEST_CASE ("simple book addition", "[spreader]"){ true); } SECTION ("merged, net_change"){ - Book in_book; + SecurityKey key(54321); + Book in_book (key); fill_book(in_book, new double[5] {2000, 2000.01, 2000.03, 2000.04, 2000.1}, new uint16_t[5] {10, 1, 15, 14, 20}, new uint16_t[5] {1, 12, 0, 0, 0}, api::Side::SIDE_BUY, true); - Book out_book; + Book out_book(key); SpreaderUtils::copy_book(in_book, md::IntPrice_from_double<7>(1999.0), bts::spark::LegSpreaderParams::MERGED, @@ -2951,6 +2982,7 @@ TEST_CASE ("simple book addition", "[spreader]"){ out_book); validate_book(out_book, + key, api::Side::SIDE_BUY, 0.01, new double[5] {1, 1.01, 1.03, 1.04, 1.1}, @@ -2958,7 +2990,7 @@ TEST_CASE ("simple book addition", "[spreader]"){ true); } } -TEST_CASE ("random book addition", "[spreader]"){ +TEST_CASE ("random book addition", "[spreader]"){ size_t n_runs=100; // generates random prices, with tick size=0.01 size_t n_levels=5; @@ -2973,7 +3005,8 @@ TEST_CASE ("random book addition", "[spreader]"){ uint16_t* exp_bid_sizes=new uint16_t[n_levels], *exp_ask_sizes=new uint16_t[n_levels]; for(size_t i=0;i +#include "lib/os/os.h" namespace bts { namespace orderman { @@ -251,7 +252,7 @@ SpreaderPreActivationOrderServices::TestOnlySpreaderPreActivationOrderServices _ /******************************book************************************/ /**********************************************************************/ -void fill_book(bts::orderman::Book& book, +void fill_book(bts::orderman::Book& book, double* prices, uint16_t* direct_sizes, uint16_t* implied_sizes, @@ -485,15 +486,22 @@ void validate_book (const bts::orderman::Book& b1, const bts::orderman::Book& b2 } } void validate_book (const bts::orderman::Book& book, + SecurityKey key, api::Side side, double tick_size, double* prices, uint16_t* sizes, bool delete_arrays){ + REQUIRE(book.security_key()==key); const BookSide& md=side==api::Side::SIDE_BUY ? book.bid() : book.ask(); for(size_t i=0; i Date: Wed, 3 Oct 2018 11:58:00 -0400 Subject: [PATCH 132/203] LadderGridHost - remove redundant 'refresh_depth' method --- .../spark/view/Spark/Ladder/LadderGridHost.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index 90e1add74..b7a77115a 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -457,7 +457,6 @@ namespace mbts.spark { _refresh_session_high_line(); _refresh_session_range(); _refresh_last_trade(); - _refresh_depth(); _refresh_sizes(); } else { if (_mkt_break_lines.bid_dirty) @@ -477,12 +476,8 @@ namespace mbts.spark { if (last_index > 0) LadderRender.render_last(_last_vis_tup.visual, _last_vis_tup.glyph_context, _last_trade, last_index, _settings.row_height, _settings.last_column); } - if (depth_changed) { - Log.assert_d(_price_list != null); - int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer); - LadderRender.render_sizes(_bid_vis_tup.visual, _bid_vis_tup.glyph_context, origin_index, _price_list, _bids, _settings.row_height, security.ContractSpec, _settings.bid_column); - LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, security.ContractSpec, _settings.ask_column); - } + if (depth_changed) + _refresh_sizes(); } _refresh_volume_at_price(); _last_origin_price = _mkt_break_lines.origin; @@ -541,13 +536,6 @@ namespace mbts.spark { } } - void _refresh_depth() { - Log.assert_d(_price_list != null); - int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer); - LadderRender.render_sizes(_bid_vis_tup.visual, _bid_vis_tup.glyph_context, origin_index, _price_list, _bids, _settings.row_height, security.ContractSpec, _settings.bid_column); - LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, security.ContractSpec, _settings.ask_column); - } - static readonly TimeSpan _price_out_of_range_render_interval = TimeSpan.FromSeconds(5); DateTime _last_price_range_calc_time = DateTime.MinValue; From 9ae087eca163299cbab03d84c728a39a113e3d57 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 3 Oct 2018 14:38:45 -0400 Subject: [PATCH 133/203] SPARK-1737 Implied Market in Ladder --- .../spark/view/Spark/Ladder/LadderGridHost.cs | 6 ++--- .../spark/view/Spark/Ladder/LadderSettings.cs | 13 +++++++---- .../view/Spark/Ladder/LadderSettingsVM.cs | 12 ++++++++++ .../view/Spark/Ladder/LadderSettingsView.xaml | 15 +++++++++++-- .../spark/view/Spark/Ladder/LaderRender.cs | 22 ++++++++++++++++--- 5 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index b7a77115a..cca75ccca 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -365,7 +365,7 @@ namespace mbts.spark { } void render_market_col(NativePriceSize[] depth) { var mid = LadderRender.index_of(_price_list, _last_origin_price, _price_comparer); - LadderRender.render_sizes(column.visual_tup.visual, column.visual_tup.glyph_context, mid, _price_list, depth, _settings.row_height, security.ContractSpec, column.settings); + LadderRender.render_sizes(column.visual_tup.visual, column.visual_tup.glyph_context, mid, _price_list, depth, _settings.row_height, security.ContractSpec, column.settings, _settings.size_display); } } _apply_column_visual_offset(); @@ -518,8 +518,8 @@ namespace mbts.spark { void _refresh_sizes() { Log.assert_d(_price_list != null); int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer); - LadderRender.render_sizes(_bid_vis_tup.visual, _bid_vis_tup.glyph_context, origin_index, _price_list, _bids, _settings.row_height, security.ContractSpec, _settings.bid_column); - LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, security.ContractSpec, _settings.ask_column); + LadderRender.render_sizes(_bid_vis_tup.visual, _bid_vis_tup.glyph_context, origin_index, _price_list, _bids, _settings.row_height, security.ContractSpec, _settings.bid_column, _settings.size_display); + LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, security.ContractSpec, _settings.ask_column, _settings.size_display); } /// diff --git a/src/client/spark/view/Spark/Ladder/LadderSettings.cs b/src/client/spark/view/Spark/Ladder/LadderSettings.cs index ff58c9d07..46cbcf42e 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettings.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettings.cs @@ -6,7 +6,6 @@ using System; using System.Linq; using System.Windows; using bts.proto; -using System.Windows.Media; namespace mbts.spark { // IMPORTANT: BE SURE TO ADD ANY NEW PROPERTIES TO THE EQUALS FUNCTION! @@ -213,12 +212,14 @@ namespace mbts.spark { session_low_line = new LadderMarketBreakLine { color = BTSColors.MarketData.session_low, show = true }, session_high_line = new LadderMarketBreakLine { color = BTSColors.MarketData.session_high, show = true }, session_range_fill = new LadderMarketBreakLine { color = BTSColors.MarketData.session_range_fill, show = true }, - alternate_order_size = 0 + alternate_order_size = 0, + size_display = SizeDisplay.Merged }; } } public enum BadgeStyle { Normal, Alternate } + public enum SizeDisplay { Merged, Direct, Implied } [ProtoMember(1)] public double row_height { get; set; } @@ -272,6 +273,8 @@ namespace mbts.spark { public LadderMarketBreakLine session_range_fill { get; set; } [ProtoMember(25)] public VolumeColumnSettings volume_column { get; set; } + [ProtoMember(26)] + public SizeDisplay size_display { get; set; } public bool Equals(LadderSettings other) => other != null && @@ -296,7 +299,8 @@ namespace mbts.spark { session_low_line.safe_equals(other.session_low_line) && session_high_line.safe_equals(other.session_high_line) && session_range_fill.safe_equals(other.session_range_fill) && - volume_column.safe_equals(other.volume_column); + volume_column.safe_equals(other.volume_column) && + size_display == other.size_display; public override bool Equals(object obj) => Equals(obj as LadderSettings); @@ -323,6 +327,7 @@ namespace mbts.spark { EqualityComparer.safe_get_hash_code(session_low_line), EqualityComparer.safe_get_hash_code(session_high_line), EqualityComparer.safe_get_hash_code(session_range_fill), - EqualityComparer.safe_get_hash_code(volume_column)); + EqualityComparer.safe_get_hash_code(volume_column), + size_display.GetHashCode()); } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs index 5b738b23e..466fed907 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs @@ -14,6 +14,7 @@ using TextUtils = mbts.ui.utility.text.TextUtils; namespace mbts.spark { class LadderSettingsVM : ViewModelBase, IWindowSettingsVM { readonly Property _badge_style; + readonly Property _size_display; readonly Property _alternate_order_size; readonly DelegateProperty _row_height; LadderSettings _current; @@ -38,6 +39,7 @@ namespace mbts.spark { _badge_style = backing(nameof(badge_style)); _badge_style.Changed += _badge_style_Changed; _alternate_order_size = backing(nameof(alternate_order_size)); + _size_display = backing(nameof(size_display)); _row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result())); _orders.font_size_changed += on_font_size_changed; _bid.font_size_changed += on_font_size_changed; @@ -66,6 +68,14 @@ namespace mbts.spark { set => _alternate_order_size.Value = value; } + public IEnumerable size_display_choices => + new[] { LadderSettings.SizeDisplay.Direct, LadderSettings.SizeDisplay.Implied, LadderSettings.SizeDisplay.Merged }; + + public LadderSettings.SizeDisplay size_display { + get => _size_display.Value; + set => _size_display.Value = value; + } + public double row_height => _row_height.Value; public IEnumerable column_items { @@ -105,6 +115,7 @@ namespace mbts.spark { _current = settings; badge_style = settings.badge_style; alternate_order_size = settings.alternate_order_size; + size_display = settings.size_display; size_presets.SetItems(settings.calculator_buttons.empty_if_null().Select(s => _create_size_preset_vm((uint)s))); _orders.assign_from(_current.orders_column); _bid.assign_from(_current.bid_column); @@ -124,6 +135,7 @@ namespace mbts.spark { var clone = SparkUtils.clone_proto(_current); clone.badge_style = badge_style; clone.alternate_order_size = alternate_order_size; + clone.size_display = size_display; clone.calculator_buttons = size_presets.Select(vm => (int)vm.value).ToArray(); clone.orders_column = _orders.get_settings(); clone.bid_column = _bid.get_settings(); diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml index c7e47a2ca..1d98b2fa6 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml @@ -126,8 +126,14 @@ Alternate Order Qty - - + + + + + @@ -319,6 +325,11 @@ + + + + + diff --git a/src/client/spark/view/Spark/Ladder/LaderRender.cs b/src/client/spark/view/Spark/Ladder/LaderRender.cs index 391a2a36d..2ae8830c6 100644 --- a/src/client/spark/view/Spark/Ladder/LaderRender.cs +++ b/src/client/spark/view/Spark/Ladder/LaderRender.cs @@ -129,7 +129,8 @@ namespace mbts.spark { NativePriceSize[] rungs, double row_height, ContractSpecificationImmutable spec, - LadderColumnSettings settings + LadderColumnSettings settings, + LadderSettings.SizeDisplay size_display ) { //var background = new SolidColorBrush(settings.background.rgb_color()); using (var dc = dv.RenderOpen()) @@ -140,19 +141,34 @@ namespace mbts.spark { var foreground = settings.foreground.rgb_brush(); int curr_index = origin_index; double curr_price = prices[curr_index]; + Func fmt; + switch (size_display) { + case LadderSettings.SizeDisplay.Direct: fmt = fmt_direct; break; + case LadderSettings.SizeDisplay.Implied: fmt = fmt_implied; break; + case LadderSettings.SizeDisplay.Merged: fmt = fmt_merged; break; + default: + // it is debatable whether to log/throw an exception in this case, + // however the application is not "seriously compromised" by an unexpected enum value here, and a reasonable default-behavior is available. + // let's not freak out and throw an exception that might crash the system + goto case LadderSettings.SizeDisplay.Merged; + } foreach (var nps in rungs) { if (nps.is_nan) break; double price = nps.price; - uint size = nps.size; // prices are in descending order, so get diff from curr_price - price int tick_diff = TickUtils.normalized_tick_diff(spec, curr_price, price); curr_index += tick_diff; curr_price = price; if (curr_index >= 0 && curr_index < prices.Length) { - var gr = format_text(size.ToString(), curr_index, row_height, settings, glyph_context); + var gr = format_text(fmt(nps), curr_index, row_height, settings, glyph_context); dc.DrawGlyphRun(foreground, gr); } else break; } + + string fmt_direct(NativePriceSize nps) => nps.direct_size.ToString(); + string fmt_implied(NativePriceSize nps) => nps.implied_size.ToString(); + string fmt_merged(NativePriceSize nps) => nps.implied_size == 0 ? + nps.size.ToString() : $"{nps.size}({nps.implied_size})"; } } From 41eb89ed3570847cb297985742bf2385e5e4dafc Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 3 Oct 2018 17:46:38 -0400 Subject: [PATCH 134/203] SPARK-1749 Invalidate Synthetic book when metadata changes --- src/lib/md/MDConsumer.cpp | 2 ++ src/lib/md/MDConsumerHandle.cpp | 15 --------------- src/lib/md/MDConsumerHandle.h | 18 +++++++++++++++++- src/lib/md/test/synthetic_book_merge_tests.cpp | 5 +++-- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/lib/md/MDConsumer.cpp b/src/lib/md/MDConsumer.cpp index b534058ec..dba9eda76 100644 --- a/src/lib/md/MDConsumer.cpp +++ b/src/lib/md/MDConsumer.cpp @@ -419,6 +419,7 @@ private: auto iter = _data.find(m.security_key()); if (iter != _data.end()) { iter->second->strategy.CopyFrom(m.strategy()); + iter->second->updated = TimeStamp::now(); } } @@ -476,6 +477,7 @@ private: s->security.CopyFrom(*strategy.security); s->strategy.CopyFrom(strategy.strategy); s->legs = legs; + s->updated = TimeStamp::now(); result.insert( make_pair(strategy.security->id(),s)); } diff --git a/src/lib/md/MDConsumerHandle.cpp b/src/lib/md/MDConsumerHandle.cpp index 73b1a587d..a078dbef1 100644 --- a/src/lib/md/MDConsumerHandle.cpp +++ b/src/lib/md/MDConsumerHandle.cpp @@ -1,6 +1,5 @@ #ifndef NO_CPP0X #include "MDConsumerHandle.h" -#include "lib/utils/sane_algorithm.h" #include "lib/domain_model/Security.h" #include @@ -227,20 +226,6 @@ TimeStamp copy_depth(const SyntheticStrategy& synth, size_t count, PriceSize* bi // END SYNTHETIC DEPTH ///////////////////////////// -TimeStamp MDConsumerHandle::last_market_update() const { - switch(_type) { - case HANDLETYPE_VOID: - return TimeStamp::invalid(); - case HANDLETYPE_DEPTH: - return book.hot->time(); - case HANDLETYPE_BBO: - return bbo.hot->time(); - case HANDLETYPE_SYNTHETIC: - return sane::max(synthetic.def->legs, [](const MDConsumerHandle& h) { return h.last_market_update(); }); - } - never; -} - bool MDConsumerHandle::get_last_trade(TimeStamp& time, PriceSize& trade, datetime::LLDateTime& trade_timestamp, size_t* total_volume) const { switch(_type) { case HANDLETYPE_VOID: return false; diff --git a/src/lib/md/MDConsumerHandle.h b/src/lib/md/MDConsumerHandle.h index 16eca0c99..7f1097d73 100644 --- a/src/lib/md/MDConsumerHandle.h +++ b/src/lib/md/MDConsumerHandle.h @@ -5,6 +5,7 @@ #include "lib/md/Shm/MDStructure.h" #include "lib/sparkproto/SparkProtos.pb.h" #include "UserSymbol.h" +#include "lib/utils/sane_algorithm.h" #include #include @@ -16,6 +17,7 @@ struct SyntheticStrategy { spark::ExecutionStrategy strategy; api::Security security; std::vector legs; + TimeStamp updated; }; TimeStamp copy_bbo(const HotEntry& src, PriceSize& out_bid, PriceSize& out_ask); @@ -186,7 +188,21 @@ public: bts_assert(false); } - TimeStamp last_market_update() const; + TimeStamp last_market_update() const { + switch(_type) { + case HANDLETYPE_VOID: + return TimeStamp::invalid(); + case HANDLETYPE_DEPTH: + return book.hot->time(); + case HANDLETYPE_BBO: + return bbo.hot->time(); + case HANDLETYPE_SYNTHETIC: + return std::max( + synthetic.def->updated, + sane::max(synthetic.def->legs, [](const MDConsumerHandle& h) { return h.last_market_update(); })); + } + bts_assert(false); + } HandleReadResult get_direct_book(TimeStamp& time, uint32_t copydepth, PriceSize* bids, PriceSize* asks) const { return get_book(read_direct, time, copydepth, bids, asks); diff --git a/src/lib/md/test/synthetic_book_merge_tests.cpp b/src/lib/md/test/synthetic_book_merge_tests.cpp index f598a03ec..e6067a64e 100644 --- a/src/lib/md/test/synthetic_book_merge_tests.cpp +++ b/src/lib/md/test/synthetic_book_merge_tests.cpp @@ -10,7 +10,8 @@ #include #include #include -#if 0 +#define ENABLE_BENCHMARK 0 +#if ENABLE_BENCHMARK #include #endif @@ -272,7 +273,7 @@ TEST_CASE("Synthetic_net_change", "[md][synthetic]") { // // Benchmarks // -#if 0 +#if ENABLE_BENCHMARK void synthetic_get_book_speed2(benchmark::State& state) { shared_ptr synthetic; vector test_instr; From 0ffe8ba9720a28c07153a6c01a155393b7165b9f Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Wed, 3 Oct 2018 17:00:19 -0500 Subject: [PATCH 135/203] SyntheticManagement to DIYGrid --- .../Spreader/SyntheticManagementView.xaml | 100 ++++++------------ .../Spreader/SyntheticManagementView.xaml.cs | 9 +- .../VM/SyntheticManagementDialogVM.cs | 46 +++++++- .../Controls/DIYGrid/DIYGridResources.xaml | 2 + 4 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml b/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml index 640a1d914..c005a0d37 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml +++ b/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml @@ -1,8 +1,10 @@ - + @@ -11,70 +13,28 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml.cs b/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml.cs index 8714268be..c3b7e6bea 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml.cs +++ b/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml.cs @@ -1,12 +1,17 @@ -using mbts.spark.filters; +using mbts.spark; +using mbts.ui.controls; using System.Windows.Controls; namespace mbts.ui.spark { /// /// Interaction logic for SyntheticManagementView.xaml /// - internal partial class SyntheticManagementView : UserControl { + partial class SyntheticManagementView : UserControl { + + public enum Columns { Name, Description, n_legs, n_working_orders, SecurityKey} public SyntheticManagementView() => InitializeComponent(); } + + class SyntheticManagementRowView : DIYGrid.HighPerformanceRow { } } \ No newline at end of file diff --git a/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs b/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs index d8d6a31dd..b55579e39 100644 --- a/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs +++ b/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs @@ -3,12 +3,17 @@ using bts.shell; using bts.spark; using mbts.spark.filters; using mbts.ui; +using mbts.ui.controls; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Reactive.Linq; +using System.Windows.Data; using System.Windows.Input; using static bts.shell.SyntheticManagementWindowFactory; +using static mbts.ui.controls.DIYGrid; +using static mbts.ui.spark.SyntheticManagementView; namespace mbts.spark { class SyntheticManagementDialogVM : SecurityMDGrid { @@ -28,6 +33,30 @@ namespace mbts.spark { n_working_orders_filter.load_state(filters.n_working_orders); security_key_filter.load_state(filters.security_key); } + // columns + add_col(Columns.Name, "Name", r => r.name, nameof(Row.name), name_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Security); + add_col(Columns.Description, "Description", r => r.description, nameof(Row.description), description_filter, DataGridFilterKnownTemplates.Regex, 200); + add_col(Columns.n_legs, "# Legs", r => r.n_legs.ToString(), nameof(Row.n_legs), n_legs_filter, DataGridFilterKnownTemplates.IntMinMax, DefaultColumnWidths.Size); + columns.Add(new HyperlinkElementColumn( + Columns.n_working_orders, + new Binding(nameof(Row.n_working_orders)), + new Binding(nameof(Row.show_order_book)), + n_working_orders_filter, + DataGridFilterKnownTemplates.UIntMinMax) { + Header = "# Working Orders", + Width = DefaultColumnWidths.Price + } + ); + add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey); + + // + void add_col(Columns col_key, string header, Func get_text, string prop, IDataGridColumnFilter filter, string filter_template, double width) => + columns.Add(new RenderTextColumn(col_key, get_text, filter, filter_template) { + Header = header, + sort_member_path = prop, + Width = width + }); + // _dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter); } @@ -36,6 +65,8 @@ namespace mbts.spark { protected override void on_row_removed(Row row) => row.Dispose(); + public ObservableCollection columns { get; } = new ObservableCollection(); + #region Filters public IDataGridColumnPersistentFilter create_time_filter { get; } = new TimeFilterVM(r => r.create_time, nameof(Row.create_time)); public IDataGridColumnPersistentFilter name_filter { get; } = new RegexFilterVM(r => r.name, nameof(Row.name)); @@ -55,7 +86,7 @@ namespace mbts.spark { }; #endregion - public class Row : SecurityMDRow, IEquatable { + public class Row : SecurityMDRow, IEquatable, IRowVM, IActionableRowVM { public readonly ExecutionStrategyImmutable? strategy; readonly IBus _bus; @@ -85,6 +116,17 @@ namespace mbts.spark { public uint n_working_orders => _n_working_orders.Value; public ICommand show_ladder { get; } public ICommand show_order_book { get; } + public ICommand default_command => show_ladder; + + + public ObservableCollection row_animations => new ObservableCollection(); + + public IReadOnlyList cell_stores => new IGridCellStore[0]; + + + public event Action invalidated; + + public void raise_invalidated() => invalidated?.Invoke(); public bool Equals(Row other) => other != null && @@ -96,6 +138,8 @@ namespace mbts.spark { public override int GetHashCode() => security_key.GetHashCode(); + + void _show_ladder() => _bus.post(SparkTags.ShowLadderTag, md); diff --git a/src/client/spark/view/Controls/DIYGrid/DIYGridResources.xaml b/src/client/spark/view/Controls/DIYGrid/DIYGridResources.xaml index 0f60b2b9d..0d77a7a05 100644 --- a/src/client/spark/view/Controls/DIYGrid/DIYGridResources.xaml +++ b/src/client/spark/view/Controls/DIYGrid/DIYGridResources.xaml @@ -335,6 +335,8 @@ + + From 9ea0e214b6b04b590eaa8cc98d379518a8bae9e7 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 3 Oct 2018 18:20:32 -0400 Subject: [PATCH 136/203] PriceSize - include 'direct_size' in Equality logic --- src/client/spark_core/Co2/PriceSize.cs | 11 ++++++----- src/lib/md/PriceSize.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/client/spark_core/Co2/PriceSize.cs b/src/client/spark_core/Co2/PriceSize.cs index 0f1876aa8..495733682 100644 --- a/src/client/spark_core/Co2/PriceSize.cs +++ b/src/client/spark_core/Co2/PriceSize.cs @@ -94,13 +94,14 @@ namespace mbts.co2 { } public override int GetHashCode() { - return bts.utils.EqualityComparer.aggregate_hash(_price.GetHashCode(), size.GetHashCode()); + return bts.utils.EqualityComparer.aggregate_hash(_price.GetHashCode(), size.GetHashCode(), _direct_size.GetHashCode()); } - public bool Equals(NativePriceSize other) { - return size == other.size && - _price.Equals(other._price); - } + public bool Equals(NativePriceSize other) => + size == other.size && + _direct_size == other._direct_size && + _price.Equals(other._price); + public static bool operator ==(NativePriceSize a, NativePriceSize b) { return a.Equals(b); } diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index 66d18ae57..fc78701fd 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -47,7 +47,7 @@ public: PriceSize& operator=(const PriceSize& other) = default; bool operator==(const PriceSize& other) const { - return (_size == other._size && int_price() == other.int_price()); + return (_size == other._size && _direct_size == other._direct_size && int_price() == other.int_price()); } bool operator!=(const PriceSize& other) const { return !(*this == other); From 6546ee531f3cb43d88ed538922628f2b47a1f0bd Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 3 Oct 2018 19:45:18 -0400 Subject: [PATCH 137/203] SPARK-1660 Implied Market in MarketView --- .../Market/MarketView/MarketViewRow.cs | 72 ++++++++++++++----- .../Modules/Market/MarketView/MarketViewVM.cs | 51 ++++++++----- .../Market/MarketView/MarketViewView.xaml.cs | 2 +- .../Market/MarketView/MarketViewWindow.cs | 2 +- .../MarketView/MarketViewWindowFactory.cs | 12 +++- .../spark/view/Spark/Ladder/LadderSettings.cs | 6 +- .../view/Spark/Ladder/LadderSettingsVM.cs | 11 +-- .../spark/view/Spark/Ladder/LaderRender.cs | 28 ++------ .../spark/view/Utility/MDDisplayHelper.cs | 26 +++++++ 9 files changed, 143 insertions(+), 67 deletions(-) create mode 100644 src/client/spark/view/Utility/MDDisplayHelper.cs diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewRow.cs b/src/client/spark/Modules/Market/MarketView/MarketViewRow.cs index e5b9b360d..2c87ccf88 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewRow.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewRow.cs @@ -5,11 +5,9 @@ using mbts.collections; using mbts.spark.utilities; using mbts.ui; using mbts.ui.controls; -using mbts.ui.settings; using mbts.ui.spark; using mbts.ui.utility; using mbts.utils; -using bts.utils; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -18,7 +16,6 @@ using System.Linq; using System.Reactive.Linq; using System.Windows.Input; - namespace mbts.spark { partial class MarketViewVM { public class Row : SecurityMDRow, IDisposable, IEquatable, DIYGrid.IRowVM, DIYGrid.IActionableRowVM { @@ -29,7 +26,6 @@ namespace mbts.spark { readonly ObservableFilterCollection _working_orders, _working_bids, _working_asks; readonly string _security_text, _security_key_text; readonly Property _bid_price, _ask_price, _last_price, _day_change, _settlement, _session_high, _session_low; - readonly Property _bid_size, _ask_size; readonly CellProperty _position; readonly CellProperty _last_size; readonly CellProperty _volume; @@ -38,6 +34,8 @@ namespace mbts.spark { readonly CellProperty _working_best_bid, _working_best_ask; readonly CellProperty _working_bids_text, _working_asks_text; readonly MyOrderSummarizer _order_summarizer = new MyOrderSummarizer(); + uint _bid_direct_size, _bid_merged_size, _ask_direct_size, _ask_merged_size; + string _bid_merged_size_text, _ask_merged_size_text; ulong _bbo_ref, _last_ref; public Row(IBus bus, SecurityMD smd, OrdersModel orders_model) : base(smd) { @@ -49,8 +47,6 @@ namespace mbts.spark { _position = new CellProperty(0); _bid_price = backing(nameof(bid_price), double.NaN); _ask_price = backing(nameof(ask_price), double.NaN); - _bid_size = backing(nameof(bid_size), 0); - _ask_size = backing(nameof(ask_size), 0); _last_price = backing(nameof(last_price), double.NaN); _last_size = new CellProperty(0); _day_change = backing(nameof(day_change), double.NaN); @@ -73,10 +69,14 @@ namespace mbts.spark { // default_command = new DelegateCommand(_show_ladder); bid_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Bid)); - bid_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidSize)); + bid_merged_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidMergedSize)); + bid_direct_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidDirectSize)); + bid_implied_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidImpliedSize)); working_bids_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingBids)); ask_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Ask)); - ask_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskSize)); + ask_merged_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskMergedSize)); + ask_direct_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskDirectSize)); + ask_implied_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskImpliedSize)); working_asks_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingAsks)); _animation.AnimationsChanged += on_animations_changed; void on_animations_changed() => RaisePropertyChanged(nameof(animations)); @@ -99,9 +99,15 @@ namespace mbts.spark { public double last_price => _last_price.Value; public uint last_size => _last_size.value; public double bid_price => _bid_price.Value; - public uint bid_size => _bid_size.Value; + public uint bid_direct_size => _bid_direct_size; + public uint bid_implied_size => _bid_merged_size - _bid_direct_size; + public uint bid_merged_size => _bid_merged_size; + public string bid_merged_size_text => _bid_merged_size_text ?? (_bid_merged_size_text = MDDisplayHelper.fmt_merged_size(_bid_merged_size, bid_implied_size)); public double ask_price => _ask_price.Value; - public uint ask_size => _ask_size.Value; + public uint ask_direct_size => _ask_direct_size; + public uint ask_implied_size => _ask_merged_size - _ask_direct_size; + public uint ask_merged_size => _ask_merged_size; + public string ask_merged_size_text => _ask_merged_size_text ?? (_ask_merged_size_text = MDDisplayHelper.fmt_merged_size(_ask_merged_size, ask_implied_size)); public ulong volume => _volume.value; public double settlement => _settlement.Value; public double day_change => _day_change.Value; @@ -115,10 +121,14 @@ namespace mbts.spark { public double working_best_ask => _working_best_ask.value; public IList animations => _animation.get_animations().ToList(); public ICommand bid_price_click { get; } - public ICommand bid_size_click { get; } + public ICommand bid_merged_size_click { get; } + public ICommand bid_direct_size_click { get; } + public ICommand bid_implied_size_click { get; } public ICommand working_bids_click { get; } public ICommand ask_price_click { get; } - public ICommand ask_size_click { get; } + public ICommand ask_merged_size_click { get; } + public ICommand ask_direct_size_click { get; } + public ICommand ask_implied_size_click { get; } public ICommand working_asks_click { get; } public ICommand default_command { get; } @@ -133,9 +143,9 @@ namespace mbts.spark { bool dirty = false; if (md.md.get_bbo(ref _bbo_ref, out NativePriceSize b, out NativePriceSize a)) { _bid_price.set(b.price); - _bid_size.set(b.size); + refresh_size(ref b, ref _bid_direct_size, ref _bid_merged_size, ref _bid_merged_size_text, nameof(bid_merged_size_text)); _ask_price.set(a.price); - _ask_size.set(a.size); + refresh_size(ref a, ref _ask_direct_size, ref _ask_merged_size, ref _ask_merged_size_text, nameof(ask_merged_size_text)); _inside_bids.on_next_distinct(_bid_price.Value >= _working_best_bid.value, ref dirty); _inside_asks.on_next_distinct(_ask_price.Value <= _working_best_ask.value, ref dirty); } @@ -150,6 +160,16 @@ namespace mbts.spark { _day_change.set(last_price - settlement); if (dirty) invalidated?.Invoke(); + + void refresh_size(ref NativePriceSize nps, ref uint direct, ref uint merged, ref string text, string text_prop) { + if (nps.size != merged || nps.direct_size != direct) { + merged = nps.size; + direct = nps.direct_size; + text = null; + dirty = true; + RaisePropertyChanged(text_prop); + } + } } public bool Equals(Row other) => @@ -187,17 +207,33 @@ namespace mbts.spark { order.Side = Side.Sell; _bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, 0u)); break; - case MarketViewView.Columns.BidSize: + case MarketViewView.Columns.BidMergedSize: order.Side = Side.Sell; - _bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_size)); + _bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_merged_size)); + break; + case MarketViewView.Columns.BidDirectSize: + order.Side = Side.Sell; + _bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_direct_size)); + break; + case MarketViewView.Columns.BidImpliedSize: + order.Side = Side.Sell; + _bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_implied_size)); break; case MarketViewView.Columns.Ask: order.Side = Side.Buy; _bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, 0u)); break; - case MarketViewView.Columns.AskSize: + case MarketViewView.Columns.AskMergedSize: order.Side = Side.Buy; - _bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_size)); + _bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_merged_size)); + break; + case MarketViewView.Columns.AskDirectSize: + order.Side = Side.Sell; + _bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_direct_size)); + break; + case MarketViewView.Columns.AskImpliedSize: + order.Side = Side.Sell; + _bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_implied_size)); break; case MarketViewView.Columns.Last: _bus.post(SparkTags.ShowOrderTicketTag, (order, last_price, 0u)); diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs index e6b60120b..db4ccf7fd 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs @@ -28,19 +28,23 @@ namespace mbts.spark { _orders_model = orders_model; security_filter = new RegexFilterVM(r => r.security.ToString(), nameof(Row.security)); // Columns - const string size_col_header = "Qty", working_col_header = "Working"; + const string merged_size_col_header = "Qty", direct_size_col_header = "Direct", implied_size_col_header = "Implied", working_col_header = "Working"; const double working_orders_col_width = 105; IMultiValueConverter price_converter = new ui.spark.converters.TickDisplayConverter(); add_col(Columns.Security, "Security", r => r.security.ToString(), nameof(Row.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex); add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), 64, volume_filter, DataGridFilterKnownTemplates.ULongMinMax); add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax); - add_col(Columns.LastSize, size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax); + add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax); add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true); add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid); - add_hl_size_col(Columns.BidSize, size_col_header, nameof(Row.bid_size), nameof(Row.bid_size_click), bid_size_filter, BTSColors.MarketData.bid); + add_col(Columns.BidImpliedSize, implied_size_col_header, r => r.bid_implied_size.ToString(), nameof(Row.bid_implied_size), DefaultColumnWidths.Size, bid_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid); + add_col(Columns.BidDirectSize, direct_size_col_header, r => r.bid_direct_size.ToString(), nameof(Row.bid_direct_size), DefaultColumnWidths.Size, bid_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid); + add_hl_merged_size_col(Columns.BidMergedSize, merged_size_col_header, nameof(Row.bid_merged_size), nameof(Row.bid_merged_size_text), nameof(Row.bid_merged_size_click), bid_merged_size_filter, BTSColors.MarketData.bid); add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid_brush); add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask_brush); - add_hl_size_col(Columns.AskSize, size_col_header, nameof(Row.ask_size), nameof(Row.ask_size_click), ask_size_filter, BTSColors.MarketData.ask); + add_hl_merged_size_col(Columns.AskMergedSize, merged_size_col_header, nameof(Row.ask_merged_size), nameof(Row.ask_merged_size_text), nameof(Row.ask_merged_size_click), ask_merged_size_filter, BTSColors.MarketData.ask); + add_col(Columns.AskDirectSize, direct_size_col_header, r => r.ask_direct_size.ToString(), nameof(Row.ask_direct_size), DefaultColumnWidths.Size, ask_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask); + add_col(Columns.AskImpliedSize, implied_size_col_header, r => r.ask_implied_size.ToString(), nameof(Row.ask_implied_size), DefaultColumnWidths.Size, ask_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask); add_hl_working_orders_col(Columns.WorkingAsks, working_col_header, nameof(Row.working_asks_text), nameof(Row.working_best_ask), nameof(Row.working_asks_click), working_ask_volume_filter, BTSColors.MarketData.ask); add_col(Columns.Settlement, "Settlement", r => r.format_price(r.settlement), nameof(Row.settlement), DefaultColumnWidths.Price, settlement_filter, DataGridFilterKnownTemplates.DoubleMinMax); add_col(Columns.DayChange, "Change", r => r.format_price(r.day_change), nameof(Row.day_change), DefaultColumnWidths.Price, day_change_filter, DataGridFilterKnownTemplates.DoubleMinMax, true); @@ -48,19 +52,20 @@ namespace mbts.spark { add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax); add_col(Columns.SecurityKey, "Security Key", r => r.security_key.ToString(), nameof(Row.security_key), DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex); // - void add_col(Columns col_key, string header, Func get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false) => + void add_col(Columns col_key, string header, Func get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false, Color? font_color = null) => columns.Add(new RenderTextColumn(col_key, get_text, filter, filter_template) { Header = header, sort_member_path = prop, Width = width, - padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding + padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding, + font_color_override = font_color }); void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background) => add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price, foreground, background, DefaultSelectionHighlight); - void add_hl_size_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground) => - add_hl_col(col_key, header, new Binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, foreground, null, null); + void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground) => + add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, foreground, null, null); void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground) => add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null); @@ -93,9 +98,13 @@ namespace mbts.spark { if (filters != null) { security_filter.load_state(filters.security); bid_price_filter.load_state(filters.bid_price); - bid_size_filter.load_state(filters.bid_size); + bid_merged_size_filter.load_state(filters.bid_merged_size); + bid_direct_size_filter.load_state(filters.bid_direct_size); + bid_implied_size_filter.load_state(filters.bid_implied_size); ask_price_filter.load_state(filters.ask_price); - ask_size_filter.load_state(filters.ask_size); + ask_merged_size_filter.load_state(filters.ask_merged_size); + ask_direct_size_filter.load_state(filters.ask_direct_size); + ask_implied_size_filter.load_state(filters.ask_implied_size); last_price_filter.load_state(filters.last_price); last_size_filter.load_state(filters.last_size); day_change_filter.load_state(filters.day_change); @@ -109,8 +118,10 @@ namespace mbts.spark { security_key_filter.load_state(filters.security_key); } } - }), DataGridFilterHelper.hook_filters(rows_view, - security_filter, bid_price_filter, bid_size_filter, ask_price_filter, ask_size_filter, last_price_filter, last_size_filter, day_change_filter, settlement_filter, + }), DataGridFilterHelper.hook_filters(rows_view, security_filter, + bid_price_filter, bid_merged_size_filter, bid_direct_size_filter, bid_implied_size_filter, + ask_price_filter, ask_merged_size_filter, ask_direct_size_filter, ask_implied_size_filter, + last_price_filter, last_size_filter, day_change_filter, settlement_filter, session_low_filter, session_high_filter, position_filter, working_bid_volume_filter, working_ask_volume_filter, volume_filter, security_key_filter), securities_connectable.Connect(), WorkLoop.Start("MarketView", _refresh) @@ -122,9 +133,13 @@ namespace mbts.spark { #region Filters public IDataGridColumnPersistentFilter security_filter { get; } public IDataGridColumnPersistentFilter bid_price_filter { get; } = new DoubleMinMax(r => r.bid_price, nameof(Row.bid_price)); - public IDataGridColumnPersistentFilter bid_size_filter { get; } = new UIntMinMax(r => r.bid_size, nameof(Row.bid_size)); + public IDataGridColumnPersistentFilter bid_merged_size_filter { get; } = new UIntMinMax(r => r.bid_merged_size, nameof(Row.bid_merged_size)); + public IDataGridColumnPersistentFilter bid_direct_size_filter { get; } = new UIntMinMax(r => r.bid_direct_size, nameof(Row.bid_direct_size)); + public IDataGridColumnPersistentFilter bid_implied_size_filter { get; } = new UIntMinMax(r => r.bid_implied_size, nameof(Row.bid_implied_size)); public IDataGridColumnPersistentFilter ask_price_filter { get; } = new DoubleMinMax(r => r.ask_price, nameof(Row.ask_price)); - public IDataGridColumnPersistentFilter ask_size_filter { get; } = new UIntMinMax(r => r.ask_size, nameof(Row.ask_size)); + public IDataGridColumnPersistentFilter ask_merged_size_filter { get; } = new UIntMinMax(r => r.ask_merged_size, nameof(Row.ask_merged_size)); + public IDataGridColumnPersistentFilter ask_direct_size_filter { get; } = new UIntMinMax(r => r.ask_direct_size, nameof(Row.ask_direct_size)); + public IDataGridColumnPersistentFilter ask_implied_size_filter { get; } = new UIntMinMax(r => r.ask_implied_size, nameof(Row.ask_implied_size)); public IDataGridColumnPersistentFilter last_price_filter { get; } = new DoubleMinMax(r => r.last_price, nameof(Row.last_price)); public IDataGridColumnPersistentFilter last_size_filter { get; } = new UIntMinMax(r => r.last_size, nameof(Row.last_size)); public IDataGridColumnPersistentFilter day_change_filter { get; } = new DoubleMinMax(r => r.day_change, nameof(Row.day_change)); @@ -141,9 +156,13 @@ namespace mbts.spark { return new FilterStates { security = security_filter.get_state(), bid_price = bid_price_filter.get_state(), - bid_size = bid_size_filter.get_state(), + bid_merged_size = bid_merged_size_filter.get_state(), + bid_direct_size = bid_direct_size_filter.get_state(), + bid_implied_size = bid_implied_size_filter.get_state(), ask_price = ask_price_filter.get_state(), - ask_size = ask_size_filter.get_state(), + ask_merged_size = ask_merged_size_filter.get_state(), + ask_direct_size = ask_direct_size_filter.get_state(), + ask_implied_size = ask_implied_size_filter.get_state(), last_price = last_price_filter.get_state(), last_size = last_size_filter.get_state(), day_change = day_change_filter.get_state(), diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewView.xaml.cs b/src/client/spark/Modules/Market/MarketView/MarketViewView.xaml.cs index 9df66c6a0..5f4107956 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewView.xaml.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewView.xaml.cs @@ -8,7 +8,7 @@ namespace mbts.ui.spark { /// Interaction logic for MarketSheetsView.xaml /// partial class MarketViewView : UserControl { - public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidSize, Ask, AskSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey } + public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidMergedSize, BidDirectSize, BidImpliedSize, Ask, AskMergedSize, AskDirectSize, AskImpliedSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey } public MarketViewView() { InitializeComponent(); diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs index 5a66814e0..c3ae0cfb6 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs @@ -35,7 +35,7 @@ namespace mbts.spark { public override UIElement Content => _view; public override IEnumerable Commands => _commands; public override MarketViewWindowFactory.StartupData SaveState => _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states()); - public override double MinWidth => 1035 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; + public override double MinWidth => 1227 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; public string thread_id => _thread_id; public void thread_init() { diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs index 46b4e2764..acd9428a4 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs @@ -57,11 +57,11 @@ namespace bts.shell { [ProtoMember(2)] public DoubleMinMaxState bid_price; [ProtoMember(3)] - public UIntMinMaxState bid_size; + public UIntMinMaxState bid_merged_size; [ProtoMember(4)] public DoubleMinMaxState ask_price; [ProtoMember(5)] - public UIntMinMaxState ask_size; + public UIntMinMaxState ask_merged_size; [ProtoMember(6)] public DoubleMinMaxState last_price; [ProtoMember(7)] @@ -84,6 +84,14 @@ namespace bts.shell { public ULongMinMaxState volume; [ProtoMember(16)] public string security_key; + [ProtoMember(17)] + public UIntMinMaxState bid_direct_size; + [ProtoMember(18)] + public UIntMinMaxState bid_implied_size; + [ProtoMember(19)] + public UIntMinMaxState ask_direct_size; + [ProtoMember(20)] + public UIntMinMaxState ask_implied_size; } } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderSettings.cs b/src/client/spark/view/Spark/Ladder/LadderSettings.cs index 46cbcf42e..49d1c83fa 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettings.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettings.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using System.Windows; using bts.proto; +using mbts.ui.spark; namespace mbts.spark { // IMPORTANT: BE SURE TO ADD ANY NEW PROPERTIES TO THE EQUALS FUNCTION! @@ -213,13 +214,12 @@ namespace mbts.spark { session_high_line = new LadderMarketBreakLine { color = BTSColors.MarketData.session_high, show = true }, session_range_fill = new LadderMarketBreakLine { color = BTSColors.MarketData.session_range_fill, show = true }, alternate_order_size = 0, - size_display = SizeDisplay.Merged + size_display = MDSizeDisplay.Merged }; } } public enum BadgeStyle { Normal, Alternate } - public enum SizeDisplay { Merged, Direct, Implied } [ProtoMember(1)] public double row_height { get; set; } @@ -274,7 +274,7 @@ namespace mbts.spark { [ProtoMember(25)] public VolumeColumnSettings volume_column { get; set; } [ProtoMember(26)] - public SizeDisplay size_display { get; set; } + public MDSizeDisplay size_display { get; set; } public bool Equals(LadderSettings other) => other != null && diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs index 466fed907..402769cac 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs @@ -3,6 +3,7 @@ using mbts.collections; using mbts.ui; using mbts.ui.controls.color_picker; using mbts.ui.shell; +using mbts.ui.spark; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -14,7 +15,7 @@ using TextUtils = mbts.ui.utility.text.TextUtils; namespace mbts.spark { class LadderSettingsVM : ViewModelBase, IWindowSettingsVM { readonly Property _badge_style; - readonly Property _size_display; + readonly Property _size_display; readonly Property _alternate_order_size; readonly DelegateProperty _row_height; LadderSettings _current; @@ -39,7 +40,7 @@ namespace mbts.spark { _badge_style = backing(nameof(badge_style)); _badge_style.Changed += _badge_style_Changed; _alternate_order_size = backing(nameof(alternate_order_size)); - _size_display = backing(nameof(size_display)); + _size_display = backing(nameof(size_display)); _row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result())); _orders.font_size_changed += on_font_size_changed; _bid.font_size_changed += on_font_size_changed; @@ -68,10 +69,10 @@ namespace mbts.spark { set => _alternate_order_size.Value = value; } - public IEnumerable size_display_choices => - new[] { LadderSettings.SizeDisplay.Direct, LadderSettings.SizeDisplay.Implied, LadderSettings.SizeDisplay.Merged }; + public IEnumerable size_display_choices => + new[] { MDSizeDisplay.Direct, MDSizeDisplay.Implied, MDSizeDisplay.Merged }; - public LadderSettings.SizeDisplay size_display { + public MDSizeDisplay size_display { get => _size_display.Value; set => _size_display.Value = value; } diff --git a/src/client/spark/view/Spark/Ladder/LaderRender.cs b/src/client/spark/view/Spark/Ladder/LaderRender.cs index 2ae8830c6..9c33de3f6 100644 --- a/src/client/spark/view/Spark/Ladder/LaderRender.cs +++ b/src/client/spark/view/Spark/Ladder/LaderRender.cs @@ -4,6 +4,7 @@ using bts.api.md.auxiliary; using mbts.co2; using mbts.ui; using mbts.ui.controls; +using mbts.ui.spark; using mbts.ui.utility.text; using System; using System.Collections.Generic; @@ -130,7 +131,7 @@ namespace mbts.spark { double row_height, ContractSpecificationImmutable spec, LadderColumnSettings settings, - LadderSettings.SizeDisplay size_display + MDSizeDisplay size_display ) { //var background = new SolidColorBrush(settings.background.rgb_color()); using (var dc = dv.RenderOpen()) @@ -141,34 +142,19 @@ namespace mbts.spark { var foreground = settings.foreground.rgb_brush(); int curr_index = origin_index; double curr_price = prices[curr_index]; - Func fmt; - switch (size_display) { - case LadderSettings.SizeDisplay.Direct: fmt = fmt_direct; break; - case LadderSettings.SizeDisplay.Implied: fmt = fmt_implied; break; - case LadderSettings.SizeDisplay.Merged: fmt = fmt_merged; break; - default: - // it is debatable whether to log/throw an exception in this case, - // however the application is not "seriously compromised" by an unexpected enum value here, and a reasonable default-behavior is available. - // let's not freak out and throw an exception that might crash the system - goto case LadderSettings.SizeDisplay.Merged; - } - foreach (var nps in rungs) { - if (nps.is_nan) break; - double price = nps.price; + var fmt = MDDisplayHelper.size_formatter(size_display); + for (int i = 0; i < rungs.Length; i++) { + if (rungs[i].is_nan) break; + double price = rungs[i].price; // prices are in descending order, so get diff from curr_price - price int tick_diff = TickUtils.normalized_tick_diff(spec, curr_price, price); curr_index += tick_diff; curr_price = price; if (curr_index >= 0 && curr_index < prices.Length) { - var gr = format_text(fmt(nps), curr_index, row_height, settings, glyph_context); + var gr = format_text(fmt(ref rungs[i]), curr_index, row_height, settings, glyph_context); dc.DrawGlyphRun(foreground, gr); } else break; } - - string fmt_direct(NativePriceSize nps) => nps.direct_size.ToString(); - string fmt_implied(NativePriceSize nps) => nps.implied_size.ToString(); - string fmt_merged(NativePriceSize nps) => nps.implied_size == 0 ? - nps.size.ToString() : $"{nps.size}({nps.implied_size})"; } } diff --git a/src/client/spark/view/Utility/MDDisplayHelper.cs b/src/client/spark/view/Utility/MDDisplayHelper.cs new file mode 100644 index 000000000..2155a2cc4 --- /dev/null +++ b/src/client/spark/view/Utility/MDDisplayHelper.cs @@ -0,0 +1,26 @@ +using mbts.co2; + +namespace mbts.ui.spark { + public delegate string FormatWithRef(ref T value); + + public enum MDSizeDisplay { Merged, Direct, Implied } + + static class MDDisplayHelper { + public static FormatWithRef size_formatter(MDSizeDisplay size_display) { + switch (size_display) { + case MDSizeDisplay.Direct: return fmt_direct; + case MDSizeDisplay.Implied: return fmt_implied; + case MDSizeDisplay.Merged: + default: return fmt_merged; + } + } + + public static string fmt_merged_size(uint merged, uint partial) => + partial == 0u ? merged.ToString() : $"{merged}({partial})"; + + public static string fmt_direct(this ref NativePriceSize nps) => nps.direct_size.ToString(); + public static string fmt_implied(this ref NativePriceSize nps) => nps.implied_size.ToString(); + public static string fmt_merged(this ref NativePriceSize nps) => nps.implied_size == 0 ? + nps.size.ToString() : $"{nps.size}({nps.implied_size})"; + } +} \ No newline at end of file From 7d9aaeac551c9ab75f1bf84e4b6e1d0c51771a7f Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 4 Oct 2018 11:41:14 -0500 Subject: [PATCH 138/203] ISlowWindow and thsis SyntheticManagement are the bane of my existence --- .../Spreader/SyntheticManagementView.xaml | 56 +++++++++-------- .../Spreader/SyntheticManagementWindow.cs | 60 ++++++++++++------- .../SyntheticManagementWindowFactory.cs | 2 +- 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml b/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml index c005a0d37..136e3b0e6 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml +++ b/src/client/spark/Modules/Spreader/SyntheticManagementView.xaml @@ -4,7 +4,9 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:usp="clr-namespace:mbts.ui.spark" - xmlns:c="clr-namespace:mbts.ui.controls"> + xmlns:c="clr-namespace:mbts.ui.controls" + Background="{StaticResource CommonBackground}" + > @@ -12,29 +14,31 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs b/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs index 8bccc80d9..6666d2791 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs +++ b/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs @@ -13,41 +13,60 @@ using System.Windows; using static bts.shell.SyntheticManagementWindowFactory; namespace mbts.spark { - class SyntheticManagementWindow : SparkWindow { + class SyntheticManagementWindow : SparkWindow, ISlowWindow { const string _window_type = "SyntheticManagement"; SingleValuePump _vm = new SingleValuePump(); StartupData _state; OrdersModel _orders_model; SyntheticManagementView _view; IDisposable _subscriptions; + IReadOnlyList _commands; - public static SyntheticManagementWindow create(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state) { - var window = new SyntheticManagementWindow(factory, bus, orders_model); - window.initialize(state); - return window; - } - SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model) + public SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state) : base("Synthetic Management", bus, factory, SparkCommands.ShowSyntheticManagement.icon) { _orders_model = orders_model; + _state = state; + Icon.Freeze(); + + } + + public void thread_init() { + Log.notnull(_state); + var vm_connectable_builder = new ConnectableSeriesBuilder(_vm.observable); var current_row_source = vm_connectable_builder.create(vm => vm.current_row_observable); var current_row_connectable_builder = new ConnectableSeriesBuilder(current_row_source); - Commands = new SparkCommand[] { - SparkCommand.create("Add", Icons.AddSynthetic, SparkTags.ShowSyntheticCreateTag).complete( + var add_synth_frozen = Icons.AddSynthetic; + add_synth_frozen.Freeze(); + var remove_synth_frozen = Icons.RemoveSynthetic; + remove_synth_frozen.Freeze(); + var modify_synth_frozen = Icons.ModifySynthetic; + modify_synth_frozen.Freeze(); + var clone_synth_frozen = Icons.CloneSynthetic; + clone_synth_frozen.Freeze(); + var show_ladder = SparkCommands.ShowLadder; + show_ladder.icon.Freeze(); + + var x = Icons.Book; + x.Freeze(); + var show_orders = SparkCommand.create("Order Book", x, SparkTags.ShowOrderBookTag); + + _commands = new SparkCommand[] { + SparkCommand.create("Add", add_synth_frozen, SparkTags.ShowSyntheticCreateTag).complete( () => new SecurityCriteria(), () => true - ), SparkCommand.create("Remove", Icons.RemoveSynthetic, SparkTags.SyntheticDeleteTag).complete( + ), SparkCommand.create("Remove", remove_synth_frozen, SparkTags.SyntheticDeleteTag).complete( current_row_connectable_builder.create(observe_row_mod_params), can_execute, select_security_id - ), SparkCommand.create("Change", Icons.ModifySynthetic, SparkTags.ShowSyntheticStrategyTag).complete( + ), SparkCommand.create("Change", modify_synth_frozen, SparkTags.ShowSyntheticStrategyTag).complete( current_row_source, r => r != null, r => r?.security ?? SecurityDataHelper.Defaults.SecurityImmutable - ), SparkCommand.create("Clone", Icons.CloneSynthetic, SparkTags.ShowSyntheticCloneTag).complete( + ), SparkCommand.create("Clone", clone_synth_frozen, SparkTags.ShowSyntheticCloneTag).complete( current_row_connectable_builder.create(observe_row_strategy), rs => rs.HasValue, select_row_strategy_tuple - ), SparkCommands.ShowLadder.complete( + ), show_ladder.complete( vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault - ), SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData { + ), show_orders.complete(() => new OrderBookWindowFactory.OrderBookStartupData { filters = new OrderBookWindowFactory.FilterStates { security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security)) } @@ -69,19 +88,18 @@ namespace mbts.spark { uint select_security_id(RowModificationParams? rmp) => rmp.HasValue ? rmp.Value.security.Id : 0; (SecurityImmutable, ExecutionStrategyImmutable) select_row_strategy_tuple(RowStrategy? rs) => rs.HasValue ? (rs.Value.security, rs.Value.strategy) : default; bool can_execute(RowModificationParams? rmp) => rmp.HasValue && !rmp.Value.has_working_orders; - } - - public void initialize(StartupData state) { - Log.notnull(state); - _state = state; - _vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, state.sorting_criteria, state.filters ?? new FilterStates()); + + + + + _vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, _state.sorting_criteria, _state.filters ?? new FilterStates()); if (_view == null) _view = new SyntheticManagementView { DataContext = _vm.value }; else _view.DataContext = _vm.value; } public override string window_type => _window_type; public override UIElement Content => _view; - public override IEnumerable Commands { get; } + public override IEnumerable Commands => _commands; public override StartupData SaveState => _state = new StartupData(_vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states()); public override double MinWidth => 700; diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs b/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs index 11eb6adb2..69870a6f1 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs +++ b/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs @@ -20,7 +20,7 @@ namespace bts.shell { } public override Task load(StartupData state) => - Task.FromResult(SyntheticManagementWindow.create(this, _bus, _orders_model, state)); + Task.FromResult(new SyntheticManagementWindow(this, _bus, _orders_model, state)); public async void show(SparkTags.Nothing nothing) => _bus.post(SparkTags.ShowWindow, await load(new StartupData())); From 911cf8a19c01445ad905c8988e416f4e443cf574 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 4 Oct 2018 12:46:48 -0400 Subject: [PATCH 139/203] SPARK-1750 Ladder - gracefully handle NaNs in the data --- .../core/GlobalExtensions/TickExtensions.cs | 20 ++++++++++++++++--- src/client/spark/view/Spark/Core/TickUtils.cs | 18 ++++++++--------- .../spark/view/Spark/Ladder/LadderGridHost.cs | 6 +++--- .../spark/view/Spark/Ladder/LaderRender.cs | 4 ++-- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/client/spark/core/GlobalExtensions/TickExtensions.cs b/src/client/spark/core/GlobalExtensions/TickExtensions.cs index 648f9b4b7..2ed66ffbc 100644 --- a/src/client/spark/core/GlobalExtensions/TickExtensions.cs +++ b/src/client/spark/core/GlobalExtensions/TickExtensions.cs @@ -103,9 +103,23 @@ namespace mbts { return _round_tick(tuf, val); } - public static (int n_low, int n_high) tickdiff(this RegionalTickFactors regional_tick, double x, double y) { - if (DoubleHelper.IsNaNOrInfinity(x)) throw new ArgumentException("Cannot be NaN or Infinity.", nameof(x)); - if (DoubleHelper.IsNaNOrInfinity(y)) throw new ArgumentException("Cannot be NaN or Infinity.", nameof(y)); + public static bool try_get_tickdiff(this RegionalTickFactors regional_tick, double x, double y, out int n_low, out int n_high) { + if (DoubleHelper.IsNaNOrInfinity(x) || DoubleHelper.IsNaNOrInfinity(y)) { + n_low = default; + n_high = default; + return false; + } + (n_low, n_high) = tickdiff_unchecked(regional_tick, x, y); + return true; + } + + /// + /// Callers should be certain that and are not nan or infinity. + /// + /// + /// + /// + internal static (int n_low, int n_high) tickdiff_unchecked(this RegionalTickFactors regional_tick, double x, double y) { double ltie = regional_tick.low_tick_inclusive_end; double raw_diff = x - y; if (x < ltie) { diff --git a/src/client/spark/view/Spark/Core/TickUtils.cs b/src/client/spark/view/Spark/Core/TickUtils.cs index fad161d3e..903039173 100644 --- a/src/client/spark/view/Spark/Core/TickUtils.cs +++ b/src/client/spark/view/Spark/Core/TickUtils.cs @@ -1,9 +1,7 @@ using bts.api; using bts.utils; using mbts.co2; -using mbts.spark.utilities; using mbts.user.settings; -using mbts.utils; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -62,16 +60,16 @@ namespace mbts.spark { return high; } - public static (int n_low, int n_high) tick_diff(ContractSpecificationImmutable spec, double x, double y) => - get_regional_ticks(spec).tickdiff(x, y); - - public static int normalized_tick_diff(ContractSpecificationImmutable spec, double x, double y) { - var (n_low, n_high) = tick_diff(spec, x, y); - return n_low + n_high; + public static int? tickdiff(this ContractSpecificationImmutable spec, double x, double y) { + if (get_regional_ticks(spec).try_get_tickdiff(x, y, out int n_low, out int n_high)) + return n_low + n_high; + return null; } - public static int normalized_tick_diff(SecurityImmutable security, double x, double y) => - normalized_tick_diff(security.spec_or_default(), x, y); + public static int tickdiff_unchecked(this ContractSpecificationImmutable spec, double x, double y) { + (int n_low, int n_high) = get_regional_ticks(spec).tickdiff_unchecked(x, y); + return n_low + n_high; + } public static double mid_tick(ContractSpecificationImmutable spec, double bid, double ask) { double raw_mid; diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index cca75ccca..971708943 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -569,8 +569,8 @@ namespace mbts.spark { SparkUtils.lazy_warn(!double.IsNaN(_mkt_break_lines.origin) && _mkt_break_lines.origin >= low && _mkt_break_lines.origin <= high, () => fmt_log_msg($"market break ({_mkt_break_lines.origin}) is unacceptable for the market range ({low} - {high})")); int prev_row_count = _row_count; - int n_ticks_to_low = TickUtils.normalized_tick_diff(security.ContractSpec, _mkt_break_lines.origin, low); - int n_ticks_to_high = TickUtils.normalized_tick_diff(security.ContractSpec, high, _mkt_break_lines.origin); + int n_ticks_to_low = security.ContractSpec.tickdiff(_mkt_break_lines.origin, low) ?? 0; + int n_ticks_to_high = security.ContractSpec.tickdiff(high, _mkt_break_lines.origin) ?? 0; int desired_row_count = Math.Max(n_ticks_to_low, n_ticks_to_high) * 2 + 1; _row_count = Math.Min(_max_row_count, desired_row_count); if (_row_count != prev_row_count) { @@ -839,7 +839,7 @@ namespace mbts.spark { } int _get_session_range_size() => - TickUtils.normalized_tick_diff(_owner.security.spec_or_default(), _session_high, _session_low); + _owner.security.spec_or_default().tickdiff(_session_high, _session_low) ?? 0; LadderSettings _settings => _owner._settings; diff --git a/src/client/spark/view/Spark/Ladder/LaderRender.cs b/src/client/spark/view/Spark/Ladder/LaderRender.cs index 9c33de3f6..9de3f925f 100644 --- a/src/client/spark/view/Spark/Ladder/LaderRender.cs +++ b/src/client/spark/view/Spark/Ladder/LaderRender.cs @@ -147,7 +147,7 @@ namespace mbts.spark { if (rungs[i].is_nan) break; double price = rungs[i].price; // prices are in descending order, so get diff from curr_price - price - int tick_diff = TickUtils.normalized_tick_diff(spec, curr_price, price); + int tick_diff = spec.tickdiff_unchecked(curr_price, price); curr_index += tick_diff; curr_price = price; if (curr_index >= 0 && curr_index < prices.Length) { @@ -240,7 +240,7 @@ namespace mbts.spark { double curr_price = prices[origin_index]; foreach (var pd in data) { // prices are in descending order, so get diff from curr_price - price - int tick_diff = TickUtils.normalized_tick_diff(spec, curr_price, pd.price); + int tick_diff = spec.tickdiff(curr_price, pd.price) ?? 0; curr_index += tick_diff; curr_price = pd.price; if (curr_index >= 0 && curr_index < prices.Length) { From 9b2b320255e3ab20e77110c69b06a89e71520092 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 4 Oct 2018 12:42:00 -0500 Subject: [PATCH 140/203] merging synth-to-diy; all is well in the grid-settings branch --- .../Spreader/SyntheticManagementWindow.cs | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs b/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs index 6666d2791..f9f2b5105 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs +++ b/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs @@ -37,36 +37,22 @@ namespace mbts.spark { var vm_connectable_builder = new ConnectableSeriesBuilder(_vm.observable); var current_row_source = vm_connectable_builder.create(vm => vm.current_row_observable); var current_row_connectable_builder = new ConnectableSeriesBuilder(current_row_source); - var add_synth_frozen = Icons.AddSynthetic; - add_synth_frozen.Freeze(); - var remove_synth_frozen = Icons.RemoveSynthetic; - remove_synth_frozen.Freeze(); - var modify_synth_frozen = Icons.ModifySynthetic; - modify_synth_frozen.Freeze(); - var clone_synth_frozen = Icons.CloneSynthetic; - clone_synth_frozen.Freeze(); - var show_ladder = SparkCommands.ShowLadder; - show_ladder.icon.Freeze(); - - var x = Icons.Book; - x.Freeze(); - var show_orders = SparkCommand.create("Order Book", x, SparkTags.ShowOrderBookTag); _commands = new SparkCommand[] { - SparkCommand.create("Add", add_synth_frozen, SparkTags.ShowSyntheticCreateTag).complete( + SparkCommand.create("Add", Icons.AddSynthetic, SparkTags.ShowSyntheticCreateTag).complete( () => new SecurityCriteria(), () => true - ), SparkCommand.create("Remove", remove_synth_frozen, SparkTags.SyntheticDeleteTag).complete( + ), SparkCommand.create("Remove", Icons.RemoveSynthetic, SparkTags.SyntheticDeleteTag).complete( current_row_connectable_builder.create(observe_row_mod_params), can_execute, select_security_id - ), SparkCommand.create("Change", modify_synth_frozen, SparkTags.ShowSyntheticStrategyTag).complete( + ), SparkCommand.create("Change", Icons.ModifySynthetic, SparkTags.ShowSyntheticStrategyTag).complete( current_row_source, r => r != null, r => r?.security ?? SecurityDataHelper.Defaults.SecurityImmutable - ), SparkCommand.create("Clone", clone_synth_frozen, SparkTags.ShowSyntheticCloneTag).complete( + ), SparkCommand.create("Clone", Icons.CloneSynthetic, SparkTags.ShowSyntheticCloneTag).complete( current_row_connectable_builder.create(observe_row_strategy), rs => rs.HasValue, select_row_strategy_tuple - ), show_ladder.complete( + ), SparkCommands.ShowLadder.complete( vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault - ), show_orders.complete(() => new OrderBookWindowFactory.OrderBookStartupData { + ), SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData { filters = new OrderBookWindowFactory.FilterStates { security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security)) } From dd397987cad44c7f272c7941bdfa02f131b0be8e Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 4 Oct 2018 13:56:37 -0400 Subject: [PATCH 141/203] LadderSettingsView - remove unused code --- .../spark/view/Spark/Ladder/LadderSettingsView.xaml | 3 --- .../spark/view/Spark/Ladder/LadderSettingsView.xaml.cs | 8 -------- 2 files changed, 11 deletions(-) diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml index 1d98b2fa6..293e036d9 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml @@ -24,9 +24,6 @@ - - - + @@ -124,7 +130,7 @@ Alternate Order Qty - + + + + @@ -332,8 +357,10 @@ + + - + @@ -427,6 +454,19 @@ + + + + + + + diff --git a/src/client/spark/view/Utility/EnabledValueVM.cs b/src/client/spark/view/Utility/EnabledValueVM.cs index e3acce1d5..db174bcb4 100644 --- a/src/client/spark/view/Utility/EnabledValueVM.cs +++ b/src/client/spark/view/Utility/EnabledValueVM.cs @@ -1,4 +1,5 @@ using mbts.ui.shell; +using System; namespace mbts.ui { public class EnabledValueVM : ViewModelBase { @@ -8,10 +9,13 @@ namespace mbts.ui { public EnabledValueVM() { _enabled = backing(nameof(enabled)); _value = backing(nameof(value)); + _enabled.Changed += (_, __) => enabled_changed?.Invoke(); } public bool enabled { get => _enabled.Value; set => _enabled.set(value); } public T value { get => _value.Value; set => _value.set(value); } + + public event Action enabled_changed; } public class NamedValueVM { From 97a1cf0162532f2dc7ff83df888439bd8c418000 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 4 Oct 2018 15:16:35 -0400 Subject: [PATCH 143/203] SPARK-1743 disable column visibility settings for synth ladders --- .../spark/view/Spark/Ladder/LadderGridHost.cs | 13 ++++++++----- .../spark/view/Spark/Ladder/LadderSettings.cs | 15 +++++++++++++++ .../spark/view/Spark/Ladder/LadderSettingsVM.cs | 10 +++++++--- .../view/Spark/Ladder/LadderSettingsView.xaml | 2 +- src/client/spark/view/Spark/Ladder/LadderVM.cs | 2 +- src/client/spark/view/Spark/Ladder/LaderRender.cs | 9 ++------- .../Spark/Utilities/WindowSettingsDialogVM.cs | 8 ++++---- 7 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index 971708943..f3cc5d3cf 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -375,14 +375,17 @@ namespace mbts.spark { Column _price_column; void _set_columns() { double x = 0; - columns = new[] { + var col_list = new List { new Column(_settings.orders_column, ColumnType.Order, null, x), new Column(_settings.bid_column, ColumnType.Bid, _bid_vis_tup, (x += _settings.orders_column.show ? _settings.orders_column.width : 0)), - _price_column = new Column(_settings.price_column, ColumnType.Price, _price_vis_tup, (x += _settings.bid_column.show ? _settings.bid_column.width : 0)), - new Column(_settings.ask_column, ColumnType.Ask, _ask_vis_tup, (x += _settings.price_column.show ? _settings.price_column.width : 0)), - new Column(_settings.volume_column.general, ColumnType.Volume, _volume_vis_tup, (x += _settings.ask_column.show ? _settings.ask_column.width : 0)), - new Column(_settings.last_column, ColumnType.Last, _last_vis_tup, (x += _settings.volume_column.general.show ? _settings.volume_column.general.width : 0)) + (_price_column = new Column(_settings.price_column, ColumnType.Price, _price_vis_tup, (x += _settings.bid_column.show ? _settings.bid_column.width : 0))), + new Column(_settings.ask_column, ColumnType.Ask, _ask_vis_tup, (x += _settings.price_column.show ? _settings.price_column.width : 0)) }; + if (security.ContractCase != Security.ContractOneofCase.Synthetic) { + col_list.Add(new Column(_settings.volume_column.general, ColumnType.Volume, _volume_vis_tup, (x += _settings.ask_column.show ? _settings.ask_column.width : 0))); + col_list.Add(new Column(_settings.last_column, ColumnType.Last, _last_vis_tup, (x += _settings.volume_column.general.show ? _settings.volume_column.general.width : 0))); + } + columns = col_list.ToArray(); } #endregion diff --git a/src/client/spark/view/Spark/Ladder/LadderSettings.cs b/src/client/spark/view/Spark/Ladder/LadderSettings.cs index 49d1c83fa..606aebcd8 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettings.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettings.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Windows; using bts.proto; using mbts.ui.spark; +using System.Collections.Generic; namespace mbts.spark { // IMPORTANT: BE SURE TO ADD ANY NEW PROPERTIES TO THE EQUALS FUNCTION! @@ -330,4 +331,18 @@ namespace mbts.spark { EqualityComparer.safe_get_hash_code(volume_column), size_display.GetHashCode()); } + + static class LadderSettingsExtensions { + public static IEnumerable all_columns(this LadderSettings settings) { + yield return settings.orders_column; + yield return settings.bid_column; + yield return settings.price_column; + yield return settings.ask_column; + yield return settings.volume_column.general; + yield return settings.last_column; + } + + public static IEnumerable visible_columns(this LadderSettings settings) => + settings.all_columns().Where(c => c.show); + } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs index e4cfe85d7..ca47b2d7c 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs @@ -35,7 +35,8 @@ namespace mbts.spark { _session_high_mkt_line = new LadderMarketBreakLineVM("Session High", 2, 1, false), _session_range_fill = new LadderMarketBreakLineVM("Session Range", 0, 0, false); - public LadderSettingsVM() { + public LadderSettingsVM(bool is_synthetic) { + this.is_synthetic = is_synthetic; size_presets = new ObservableEditableCollection>(_create_size_preset_vm); size_presets.CollectionChanged += (s, e) => RaisePropertyChanged(nameof(size_presets_text)); _badge_style = backing(nameof(badge_style)); @@ -79,6 +80,7 @@ namespace mbts.spark { } public double row_height => _row_height.Value; + public bool is_synthetic { get; } public IEnumerable column_items { get { @@ -86,8 +88,10 @@ namespace mbts.spark { yield return _bid; yield return _price; yield return _ask; - yield return _volume.general; - yield return _last; + if (!is_synthetic) { + yield return _volume.general; + yield return _last; + } } } diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml index 01738b5e2..72305f32d 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml @@ -352,7 +352,7 @@ - + diff --git a/src/client/spark/view/Spark/Ladder/LadderVM.cs b/src/client/spark/view/Spark/Ladder/LadderVM.cs index 658a56d24..36e168be3 100644 --- a/src/client/spark/view/Spark/Ladder/LadderVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderVM.cs @@ -65,7 +65,7 @@ namespace mbts.spark { show_book_command = new DelegateCommand(_show_book); show_ticket_command = new DelegateCommand(_show_ticket); settings_command = new DialogCommand(() => { - var vm = new WindowSettingsDialogVM(settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER); + var vm = new WindowSettingsDialogVM(settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER, () => new LadderSettingsVM(security.ContractCase == Security.ContractOneofCase.Synthetic)); var dialog = new ui.spark.LadderSettingsDialog { DataContext = vm, Title = $"Ladder Settings - '{security}'" diff --git a/src/client/spark/view/Spark/Ladder/LaderRender.cs b/src/client/spark/view/Spark/Ladder/LaderRender.cs index 9de3f925f..c527106a6 100644 --- a/src/client/spark/view/Spark/Ladder/LaderRender.cs +++ b/src/client/spark/view/Spark/Ladder/LaderRender.cs @@ -44,13 +44,8 @@ namespace mbts.spark { // IMPORTANT: this method ONLY WORKS if Badges use the BadgeRender.margin/border_thickness/padding values. // TODO: elminate the dependency on BadgeRender.margin/border_thickness/padding by integrating them into settings. public static double measure_row_height(LadderSettings settings) => - Math.Max(new[] { - settings.bid_column.font_size, - settings.price_column.font_size, - settings.ask_column.font_size, - settings.last_column.font_size - }.Max() + row_padding.Top + row_padding.Bottom, - BadgeRender.measure_height( + Math.Max(settings.visible_columns().Max(c => c.font_size, 0) + row_padding.Top + row_padding.Bottom, + BadgeRender.measure_height( settings.badge_style, Badge.DefaultBorderThickness, Badge.DefaultPadding, settings.orders_column.font_size ) + Badge.DefaultMargin.Top + Badge.DefaultMargin.Bottom); diff --git a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs index ccc6a5bf1..4e8d388ea 100644 --- a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs +++ b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs @@ -11,18 +11,18 @@ namespace mbts.spark { T get_result(); } - class WindowSettingsDialogVM : ViewModelBase, IRequestViewClose where T : class, IEquatable where TVM : IWindowSettingsVM, new() { + class WindowSettingsDialogVM : ViewModelBase, IRequestViewClose where T : class, IEquatable where TVM : IWindowSettingsVM { readonly Property _default_tab_selected, _use_default; readonly DialogSkirtItem _sync_to_factory_item, _sync_to_default_item, _sync_to_instance_item, _ok_item, _apply_item, _cancel_item; readonly DelegateProperty> _left_skirt_items; - public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext) { + public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext, Func vm_factory) { var settings = settings_manager.current_settings(); _default_tab_selected = backing(nameof(default_tab_selected), settings_manager.instance_settings == null); _use_default = backing(nameof(use_default), default_tab_selected); - default_vm = new TVM(); + default_vm = vm_factory(); default_vm.load(settings_manager.default_settings); - instance_vm = new TVM(); + instance_vm = vm_factory(); instance_vm.load(settings_manager.current_settings()); // items const string import_group = "i", export_group = "e"; From f1bb6af757b926a5294b1f01b62fbf3684bc25db Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Thu, 4 Oct 2018 15:58:21 -0400 Subject: [PATCH 144/203] SPARK-1659 Fix for implieds on client side showing up as direct. --- src/client/spark_core/Co2/PriceSize.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/spark_core/Co2/PriceSize.cs b/src/client/spark_core/Co2/PriceSize.cs index 495733682..040bdbd6f 100644 --- a/src/client/spark_core/Co2/PriceSize.cs +++ b/src/client/spark_core/Co2/PriceSize.cs @@ -38,8 +38,8 @@ namespace mbts.co2 { _ask = ask; _ask_size = ask_size; } - public NativePriceSize bid => new NativePriceSize(_bid, _bid_size, 0); - public NativePriceSize ask => new NativePriceSize(_ask, _ask_size, 0); + public NativePriceSize bid => new NativePriceSize(_bid, 0, _bid_size); + public NativePriceSize ask => new NativePriceSize(_ask, 0, _ask_size); public void Deconstruct(out NativePriceSize bid, out NativePriceSize ask) { bid = this.bid; ask = this.ask; From 88e352457f6f8a152cf15bccb56dd80eb0e2d1c7 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 4 Oct 2018 15:49:38 -0500 Subject: [PATCH 145/203] Synthetic Management now support Grid settings; need to test still --- .../Modules/Spreader/SpreaderShellModule.cs | 5 +- .../Spreader/SyntheticManagementWindow.cs | 34 +++++++++--- .../SyntheticManagementWindowFactory.cs | 54 +++++++++++++++++-- .../VM/SyntheticManagementDialogVM.cs | 44 +++++++++++---- src/client/spark/Spark.csproj | 5 ++ .../UserSettings/UserSettings.cs | 12 +++-- .../Controls/DIYGrid/Columns/ColumnBase.cs | 9 +++- 7 files changed, 135 insertions(+), 28 deletions(-) diff --git a/src/client/spark/Modules/Spreader/SpreaderShellModule.cs b/src/client/spark/Modules/Spreader/SpreaderShellModule.cs index f44246cee..6e347c348 100644 --- a/src/client/spark/Modules/Spreader/SpreaderShellModule.cs +++ b/src/client/spark/Modules/Spreader/SpreaderShellModule.cs @@ -1,6 +1,7 @@ using bts.api; using bts.spark; using mbts; +using mbts.persist; using mbts.spark; using mbts.ui; using mbts.ui.shell; @@ -18,8 +19,8 @@ namespace bts.shell LatencyReportWindowFactory _lrwf; [ImportingConstructor] - public SpreaderShellModule(IBus bus, OrdersModel orders_model) { - _smwf = new SyntheticManagementWindowFactory(this, bus, orders_model); + public SpreaderShellModule(IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) { + _smwf = new SyntheticManagementWindowFactory(this, bus, settings_hub, orders_model); _lrwf = new LatencyReportWindowFactory(this, bus); } diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs b/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs index f9f2b5105..6a08c6f42 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs +++ b/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs @@ -2,8 +2,11 @@ using bts.api; using bts.shell; using bts.spark; +using mbts.persist; using mbts.ui; +using mbts.ui.settings; using mbts.ui.spark; +using mbts.ui.utility; using mbts.utils; using System; using System.Collections.Generic; @@ -21,14 +24,15 @@ namespace mbts.spark { SyntheticManagementView _view; IDisposable _subscriptions; IReadOnlyList _commands; + readonly IWindowSettingsManager _settings_manager; - public SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state) + public SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, WindowSettingsManager settings_manager) : base("Synthetic Management", bus, factory, SparkCommands.ShowSyntheticManagement.icon) { _orders_model = orders_model; _state = state; + _settings_manager = settings_manager; Icon.Freeze(); - } public void thread_init() { @@ -56,7 +60,24 @@ namespace mbts.spark { filters = new OrderBookWindowFactory.FilterStates { security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security)) } - }) + } + ), SparkCommands.window_settings_command(new DialogCommand(() => { + var vm = new WindowSettingsDialogVM( + _settings_manager, + us => us.synthetic_management_grid, + UserSettings.FileExtensions.GRID, + s => _vm.value.grid_settings_manager.create_vm( + // we need to get the most up to date GridSettings when launching b/c users may + // have changed some settings on the grid directly, like column widths. + s.with(_vm.value.columns.Select( c => c.create())), + this.observe_title() + ) + ); + // TODO: bind to main window's Title + var dialog = new SimpleGridSettingsDialog { Title = "Synthetic Management Grid Settings", DataContext = vm }; + vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); + return dialog; + })) }; _subscriptions = Disposable.Create( @@ -75,10 +96,7 @@ namespace mbts.spark { (SecurityImmutable, ExecutionStrategyImmutable) select_row_strategy_tuple(RowStrategy? rs) => rs.HasValue ? (rs.Value.security, rs.Value.strategy) : default; bool can_execute(RowModificationParams? rmp) => rmp.HasValue && !rmp.Value.has_working_orders; - - - - _vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, _state.sorting_criteria, _state.filters ?? new FilterStates()); + _vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, _settings_manager, _state.sorting_criteria, _state.filters ?? new FilterStates()); if (_view == null) _view = new SyntheticManagementView { DataContext = _vm.value }; else _view.DataContext = _vm.value; } @@ -86,7 +104,7 @@ namespace mbts.spark { public override string window_type => _window_type; public override UIElement Content => _view; public override IEnumerable Commands => _commands; - public override StartupData SaveState => _state = new StartupData(_vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states()); + public override StartupData SaveState => _state = _vm.value.create(); public override double MinWidth => 700; public override void Dispose() { diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs b/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs index 69870a6f1..ac37786c7 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs +++ b/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs @@ -1,30 +1,46 @@ using bts.api; +using mbts.persist; using mbts.spark; using mbts.spark.filters; using mbts.ui; +using mbts.ui.settings; using ProtoBuf; using System; using System.Collections.Generic; using System.Reactive.Linq; using System.Threading.Tasks; +using static mbts.ui.controls.DIYGrid; +using static mbts.ui.spark.SyntheticManagementView; namespace bts.shell { class SyntheticManagementWindowFactory : ShellWindowFactory { readonly IBus _bus; readonly OrdersModel _orders_model; + readonly UserSettingsHub _settings_hub; - public SyntheticManagementWindowFactory(SpreaderShellModule module, IBus bus, OrdersModel orders_model) : base(module, "SyntheticManagement") { + public SyntheticManagementWindowFactory(SpreaderShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) : base(module, "SyntheticManagement") { _bus = bus; _orders_model = orders_model; + _settings_hub = settings_hub; _dispose = bus.subscribe(SparkTags.ShowSyntheticManagementTag).OnDispatcher().Subscribe(show); } + public static string col_id(Columns col) => col.ToString(); + public override Task load(StartupData state) => - Task.FromResult(new SyntheticManagementWindow(this, _bus, _orders_model, state)); + Task.FromResult(_create(state)); public async void show(SparkTags.Nothing nothing) => _bus.post(SparkTags.ShowWindow, await load(new StartupData())); + IShellWindow _create(StartupData state) { + var settings_manager = new WindowSettingsManager(_settings_hub, + state.grid, + get: us => us.synthetic_management_grid, + set: (us, gs) => us.with(provided_market_view_gridsettings: gs)); + return new SyntheticManagementWindow(this, _bus, _orders_model, state, settings_manager); + } + [ProtoContract] public class StartupData { public const int Version = 1; @@ -33,11 +49,41 @@ namespace bts.shell { public readonly List sorting_criteria; [ProtoMember(2)] public readonly FilterStates filters; + [ProtoMember(3)] + public readonly GridSettings grid; - public StartupData() : this(new List(), null) { } - public StartupData(List sorting_criteria, FilterStates filters) { + public StartupData() : this(new List(), null, null) { } + public StartupData(List sorting_criteria, FilterStates filters, GridSettings grid = default) { this.sorting_criteria = sorting_criteria; this.filters = filters; + this.grid = grid; + } + + public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion); + + static class DefaultColumns { + public static GridColumnSettings name => new GridColumnSettings(col_id(Columns.Name), DefaultColumnHeaders.Name, width: DefaultColumnWidths.Security); + public static GridColumnSettings description => new GridColumnSettings(col_id(Columns.Description), DefaultColumnHeaders.Description, width: DefaultColumnWidths.Description); + public static GridColumnSettings n_legs => new GridColumnSettings(col_id(Columns.n_legs), DefaultColumnHeaders.N_Legs, width: DefaultColumnWidths.Size); + public static GridColumnSettings n_working_orders => new GridColumnSettings(col_id(Columns.n_working_orders), DefaultColumnHeaders.N_Working_Orders, width: DefaultColumnWidths.Size); + public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey); + + public static IEnumerable default_sorting_criterion { + get { + yield return new SortingCriteria(name.id, true); + } + } + + public static IEnumerable all { + get { + yield return name; + yield return description; + yield return n_legs; + yield return n_working_orders; + yield return security_key; + } + } + } } diff --git a/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs b/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs index b55579e39..d7494f0d2 100644 --- a/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs +++ b/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs @@ -1,9 +1,11 @@ using bts.api; using bts.shell; using bts.spark; +using mbts.persist; using mbts.spark.filters; using mbts.ui; using mbts.ui.controls; +using mbts.ui.settings; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -19,9 +21,11 @@ namespace mbts.spark { class SyntheticManagementDialogVM : SecurityMDGrid { readonly IBus _bus; readonly OrdersModel _orders_model; + IWindowSettingsManager _settings_manager; - public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model) : this(bus, orders_model, null, null) { } - public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IReadOnlyCollection sorting, FilterStates filters) + + public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IWindowSettingsManager settings_manager) : this(bus, orders_model, settings_manager, null, null) { } + public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IWindowSettingsManager settings_manager, IReadOnlyCollection sorting, FilterStates filters) : base(orders_model.spreader.strategy_tuple.Select(map => map.Values), sorting) { _bus = bus; _orders_model = orders_model; @@ -33,18 +37,20 @@ namespace mbts.spark { n_working_orders_filter.load_state(filters.n_working_orders); security_key_filter.load_state(filters.security_key); } + _settings_manager = settings_manager; // columns - add_col(Columns.Name, "Name", r => r.name, nameof(Row.name), name_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Security); - add_col(Columns.Description, "Description", r => r.description, nameof(Row.description), description_filter, DataGridFilterKnownTemplates.Regex, 200); - add_col(Columns.n_legs, "# Legs", r => r.n_legs.ToString(), nameof(Row.n_legs), n_legs_filter, DataGridFilterKnownTemplates.IntMinMax, DefaultColumnWidths.Size); + add_col(Columns.Name, DefaultColumnHeaders.Name, r => r.name, nameof(Row.name), name_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Security); + add_col(Columns.Description, DefaultColumnHeaders.Description, r => r.description, nameof(Row.description), description_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Description); + add_col(Columns.n_legs, DefaultColumnHeaders.N_Legs, r => r.n_legs.ToString(), nameof(Row.n_legs), n_legs_filter, DataGridFilterKnownTemplates.IntMinMax, DefaultColumnWidths.Size); columns.Add(new HyperlinkElementColumn( Columns.n_working_orders, new Binding(nameof(Row.n_working_orders)), new Binding(nameof(Row.show_order_book)), n_working_orders_filter, DataGridFilterKnownTemplates.UIntMinMax) { - Header = "# Working Orders", - Width = DefaultColumnWidths.Price + id = col_id(Columns.n_working_orders), + Header = DefaultColumnHeaders.N_Working_Orders, + Width = DefaultColumnWidths.Size } ); add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey); @@ -52,12 +58,20 @@ namespace mbts.spark { // void add_col(Columns col_key, string header, Func get_text, string prop, IDataGridColumnFilter filter, string filter_template, double width) => columns.Add(new RenderTextColumn(col_key, get_text, filter, filter_template) { + id = col_id(col_key), Header = header, sort_member_path = prop, Width = width }); // - _dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter); + grid_settings_manager = new SimpleGridSettingsManager(columns, new Dictionary { }, row_vms); + // this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.) + grid_settings_manager.load_from(settings_manager.current_settings()); + + _resources.AddRange( + DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter), + settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager) + ); } protected override Row row_factory((ExecutionStrategyImmutable? strategy, SecurityMD md) row_model) => @@ -66,6 +80,18 @@ namespace mbts.spark { protected override void on_row_removed(Row row) => row.Dispose(); public ObservableCollection columns { get; } = new ObservableCollection(); + public SimpleGridSettingsManager grid_settings_manager { get; } + + public GridSettings get_grid_save_state() { + if (_settings_manager.instance_settings == null) + return null; + else + return grid_settings_manager.create(); + } + + public StartupData create() => + new StartupData(rows_view.get_sorting_criteria(columns), _get_filter_states(), get_grid_save_state()); + #region Filters public IDataGridColumnPersistentFilter create_time_filter { get; } = new TimeFilterVM(r => r.create_time, nameof(Row.create_time)); @@ -75,7 +101,7 @@ namespace mbts.spark { public IDataGridColumnPersistentFilter n_working_orders_filter { get; } = new UIntMinMax(r => r.n_working_orders, nameof(Row.n_legs)); public IDataGridColumnPersistentFilter security_key_filter { get; } = new RegexFilterVM(r => r.security_key.ToString(), nameof(Row.security_key)); - public FilterStates get_filter_states() => + FilterStates _get_filter_states() => new FilterStates { description = description_filter.get_state(), create_time = create_time_filter.get_state(), diff --git a/src/client/spark/Spark.csproj b/src/client/spark/Spark.csproj index 76a20bc1a..b6f3b57a2 100644 --- a/src/client/spark/Spark.csproj +++ b/src/client/spark/Spark.csproj @@ -75,6 +75,11 @@ Code + + + MSBuild:Compile + + diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs index bb0f5aca6..9122ec794 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs @@ -16,7 +16,8 @@ namespace mbts.spark { _notification = NotificationSettings.Default, _market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings, _fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings, - _time_and_sales_grid = bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultGridSettings + _time_and_sales_grid = bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultGridSettings, + _synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings }; [ProtoMember(1)] @@ -31,6 +32,8 @@ namespace mbts.spark { GridSettings _time_and_sales_grid; [ProtoMember(7)] GridSettings _fills_grid; + [ProtoMember(8)] + GridSettings _synthetic_management_grid; public UserSettings() { } @@ -39,18 +42,21 @@ namespace mbts.spark { public GridSettings market_view_grid => _market_view_grid ?? (_market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings); public GridSettings time_and_sales_grid => _time_and_sales_grid ?? (_time_and_sales_grid = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all)); public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings); + public GridSettings synthetic_management_grid => _synthetic_management_grid ?? (_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings); public UserSettings with(LadderSettings ladder = null, NotificationSettings notification = null, GridSettings provided_market_view_gridsettings = null, GridSettings time_and_sales = null, - GridSettings provided_fills_gridsettings = null) => + GridSettings provided_fills_gridsettings = null, + GridSettings provided_synthetic_management_gridsettings = null) => new UserSettings { _ladder = ladder ?? _ladder, _notification = notification ?? _notification, _market_view_grid = provided_market_view_gridsettings ?? _market_view_grid, _time_and_sales_grid = time_and_sales ?? _time_and_sales_grid, - _fills_grid = provided_fills_gridsettings ?? _fills_grid + _fills_grid = provided_fills_gridsettings ?? _fills_grid, + _synthetic_management_grid = provided_synthetic_management_gridsettings ?? _synthetic_management_grid, }; public static class FileExtensions { diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index 5c85cacd8..d3826b2e4 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -33,7 +33,8 @@ namespace mbts.ui.controls { User = 53, Aggressor = 80, Volume = 64, - FillKind = 70; + FillKind = 70, + Description = 300; } public static class DefaultColumnHeaders { @@ -65,7 +66,11 @@ namespace mbts.ui.controls { DayChange = "Change", SessionLow = "Low", SessionHigh = "High", - FillKind = "Fill Kind"; + FillKind = "Fill Kind", + Name = "Name", + Description = "Description", + N_Legs = "# Legs", + N_Working_Orders = "# Working Orders"; } public interface IKeyedColumn { From b3d30713168c015fa1aebf2c1f6ff4009251b0d7 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 4 Oct 2018 16:09:54 -0500 Subject: [PATCH 146/203] Synth Man - better column width defaults --- .../Modules/Spreader/SyntheticManagementWindowFactory.cs | 6 +++--- .../Modules/Spreader/VM/SyntheticManagementDialogVM.cs | 4 ++-- .../spark/view/Controls/DIYGrid/Columns/ColumnBase.cs | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs b/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs index ac37786c7..afb327aa7 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs +++ b/src/client/spark/Modules/Spreader/SyntheticManagementWindowFactory.cs @@ -37,7 +37,7 @@ namespace bts.shell { var settings_manager = new WindowSettingsManager(_settings_hub, state.grid, get: us => us.synthetic_management_grid, - set: (us, gs) => us.with(provided_market_view_gridsettings: gs)); + set: (us, gs) => us.with(provided_synthetic_management_gridsettings: gs)); return new SyntheticManagementWindow(this, _bus, _orders_model, state, settings_manager); } @@ -64,8 +64,8 @@ namespace bts.shell { static class DefaultColumns { public static GridColumnSettings name => new GridColumnSettings(col_id(Columns.Name), DefaultColumnHeaders.Name, width: DefaultColumnWidths.Security); public static GridColumnSettings description => new GridColumnSettings(col_id(Columns.Description), DefaultColumnHeaders.Description, width: DefaultColumnWidths.Description); - public static GridColumnSettings n_legs => new GridColumnSettings(col_id(Columns.n_legs), DefaultColumnHeaders.N_Legs, width: DefaultColumnWidths.Size); - public static GridColumnSettings n_working_orders => new GridColumnSettings(col_id(Columns.n_working_orders), DefaultColumnHeaders.N_Working_Orders, width: DefaultColumnWidths.Size); + public static GridColumnSettings n_legs => new GridColumnSettings(col_id(Columns.n_legs), DefaultColumnHeaders.N_Legs, width: DefaultColumnWidths.N_Legs); + public static GridColumnSettings n_working_orders => new GridColumnSettings(col_id(Columns.n_working_orders), DefaultColumnHeaders.N_Working_Orders, width: DefaultColumnWidths.N_Working_Orders); public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey); public static IEnumerable default_sorting_criterion { diff --git a/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs b/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs index d7494f0d2..489ba2935 100644 --- a/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs +++ b/src/client/spark/Modules/Spreader/VM/SyntheticManagementDialogVM.cs @@ -41,7 +41,7 @@ namespace mbts.spark { // columns add_col(Columns.Name, DefaultColumnHeaders.Name, r => r.name, nameof(Row.name), name_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Security); add_col(Columns.Description, DefaultColumnHeaders.Description, r => r.description, nameof(Row.description), description_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Description); - add_col(Columns.n_legs, DefaultColumnHeaders.N_Legs, r => r.n_legs.ToString(), nameof(Row.n_legs), n_legs_filter, DataGridFilterKnownTemplates.IntMinMax, DefaultColumnWidths.Size); + add_col(Columns.n_legs, DefaultColumnHeaders.N_Legs, r => r.n_legs.ToString(), nameof(Row.n_legs), n_legs_filter, DataGridFilterKnownTemplates.IntMinMax, DefaultColumnWidths.N_Legs); columns.Add(new HyperlinkElementColumn( Columns.n_working_orders, new Binding(nameof(Row.n_working_orders)), @@ -50,7 +50,7 @@ namespace mbts.spark { DataGridFilterKnownTemplates.UIntMinMax) { id = col_id(Columns.n_working_orders), Header = DefaultColumnHeaders.N_Working_Orders, - Width = DefaultColumnWidths.Size + Width = DefaultColumnWidths.N_Working_Orders } ); add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey); diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index d3826b2e4..e8d46a216 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -34,7 +34,9 @@ namespace mbts.ui.controls { Aggressor = 80, Volume = 64, FillKind = 70, - Description = 300; + Description = 300, + N_Legs = 68, + N_Working_Orders = 123; } public static class DefaultColumnHeaders { From f5d7ea9a47ea9d519b706c2c734b39fdbee385c8 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Fri, 5 Oct 2018 13:36:42 -0400 Subject: [PATCH 147/203] restructure NumericSignBrushConverter --- .../Converters/NumericSignBrushConverter.cs | 56 +++++++++++++------ .../spark/view/Extensions/WPFExtensions.cs | 5 ++ 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/client/spark/view/Converters/NumericSignBrushConverter.cs b/src/client/spark/view/Converters/NumericSignBrushConverter.cs index f3e434d75..fb37c307c 100644 --- a/src/client/spark/view/Converters/NumericSignBrushConverter.cs +++ b/src/client/spark/view/Converters/NumericSignBrushConverter.cs @@ -4,28 +4,48 @@ using System.Windows.Data; using System.Windows.Media; namespace mbts.ui.converters { - public class NumericSignBrushConverter : IValueConverter { - static readonly SolidColorBrush _error_brush = new SolidColorBrush(Colors.DimGray); + struct SignedDataStore { + public readonly T positive, neutral, negative, error; - SolidColorBrush _negative_brush = new SolidColorBrush(Colors.Red), - _neutral_brush = new SolidColorBrush(Colors.Black), - _positive_brush = new SolidColorBrush(Colors.Green); + public SignedDataStore(T positive, T neutral, T negative, T error) { + this.positive = positive; + this.neutral = neutral; + this.negative = negative; + this.error = error; + } + } - static NumericSignBrushConverter() => - _error_brush.Freeze(); + static class SignedDataStoreExtensions { + public static T get(this SignedDataStore store, double value) { + if (double.IsNaN(value)) return store.error; + else if (value > 0) return store.positive; + else if (value < 0) return store.negative; + else return store.neutral; + } - public Color PositiveColor { get => _positive_brush.Color; set => (_positive_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); } - public Color NeutralColor { get => _neutral_brush.Color; set => (_neutral_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); } - public Color NegativeColor { get => _negative_brush.Color; set => (_negative_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); } + public static SignedDataStore with_positive(this SignedDataStore store, T positive) => new SignedDataStore(positive, store.neutral, store.negative, store.error); + public static SignedDataStore with_neutral(this SignedDataStore store, T neutral) => new SignedDataStore(store.positive, neutral, store.negative, store.error); + public static SignedDataStore with_negative(this SignedDataStore store, T negative) => new SignedDataStore(store.positive, store.neutral, negative, store.error); + } - private double _opacity = 1.0; - public Double Opacity { - get { return _opacity; } + class NumericSignBrushConverter : IValueConverter { + SignedDataStore _brushes = new SignedDataStore( + Brushes.Green, Brushes.Black, Brushes.Red, Brushes.DimGray); + + public Color PositiveColor { get => _brushes.positive.Color; set => _brushes = _brushes.with_positive(new SolidColorBrush(value) { Opacity = _opacity }.frozen()); } + public Color NeutralColor { get => _brushes.neutral.Color; set => _brushes = _brushes.with_neutral(new SolidColorBrush(value) { Opacity = _opacity }.frozen()); } + public Color NegativeColor { get => _brushes.negative.Color; set => _brushes = _brushes.with_negative(new SolidColorBrush(value) { Opacity = _opacity }.frozen()); } + + double _opacity = 1.0; + public double Opacity { + get => _opacity; set { _opacity = value; - (_positive_brush = new SolidColorBrush(_positive_brush.Color) { Opacity = _opacity }).Freeze(); - (_negative_brush = new SolidColorBrush(_negative_brush.Color) { Opacity = _opacity }).Freeze(); - (_neutral_brush = new SolidColorBrush(_neutral_brush.Color) { Opacity = _opacity }).Freeze(); + _brushes = new SignedDataStore( + _brushes.positive.with(value).frozen(), + _brushes.positive.with(value).frozen(), + _brushes.positive.with(value).frozen(), + _brushes.error.with(value).frozen()); } } public IComparable Zero { get; set; } = 0.0; @@ -33,9 +53,9 @@ namespace mbts.ui.converters { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var comparable = value as IComparable; if (comparable == null || comparable is double && double.IsNaN((double)value)) - return _error_brush; + return _brushes.error; int comp = comparable.CompareTo(Zero); - return comp == 0 ? _neutral_brush : comp > 0 ? _positive_brush : _negative_brush; + return comp == 0 ? _brushes.neutral : comp > 0 ? _brushes.positive : _brushes.negative; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => diff --git a/src/client/spark/view/Extensions/WPFExtensions.cs b/src/client/spark/view/Extensions/WPFExtensions.cs index 5aa34a548..0d49fce12 100644 --- a/src/client/spark/view/Extensions/WPFExtensions.cs +++ b/src/client/spark/view/Extensions/WPFExtensions.cs @@ -98,6 +98,11 @@ namespace mbts.ui { return brush; } + public static SolidColorBrush with(this SolidColorBrush brush, double opacity) { + brush.Opacity = opacity; + return brush; + } + public static Pen frozen(this Pen pen) { pen.Freeze(); return pen; From db34468bc473bfcaf31aa9a9f50f0469dde78cb8 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Fri, 5 Oct 2018 13:40:38 -0400 Subject: [PATCH 148/203] SPARK-1774 pnl - switch to DIYGrid --- .../view/Controls/DIYGrid/CellProperty.cs | 2 +- .../Controls/DIYGrid/Columns/ColumnBase.cs | 2 + src/client/spark/view/Spark/PAndL/PAndLVM.cs | 46 ++- .../spark/view/Spark/PAndL/PAndLView.xaml | 344 ++++++------------ .../spark/view/Spark/PAndL/PAndLView.xaml.cs | 6 +- .../spark/view/Spark/PAndL/PnLSummaryRow.cs | 239 +++++------- 6 files changed, 246 insertions(+), 393 deletions(-) diff --git a/src/client/spark/view/Controls/DIYGrid/CellProperty.cs b/src/client/spark/view/Controls/DIYGrid/CellProperty.cs index d21e1f98e..758d0474d 100644 --- a/src/client/spark/view/Controls/DIYGrid/CellProperty.cs +++ b/src/client/spark/view/Controls/DIYGrid/CellProperty.cs @@ -15,7 +15,7 @@ namespace mbts.spark { value = initial_value; } - event Action<(T oldval, T newval)> _value_set; + protected event Action<(T oldval, T newval)> _value_set; public T value { get; private set; } public string text => _text ?? (_text = _format(value)); diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index e8d46a216..db518b896 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -24,6 +24,7 @@ namespace mbts.ui.controls { Price = 56, Security = 80, SecurityKey = 90, + SecurityType = 90, Side = 47, Size = 48, Symbol = 48, @@ -50,6 +51,7 @@ namespace mbts.ui.controls { Price = "Price", Security = "Security", SecurityKey = "Security Key", + SecurityType = "Security Type", Side = "Side", Size = "Qty", Symbol = "Symbol", diff --git a/src/client/spark/view/Spark/PAndL/PAndLVM.cs b/src/client/spark/view/Spark/PAndL/PAndLVM.cs index 6a3305db3..7deebb8ae 100644 --- a/src/client/spark/view/Spark/PAndL/PAndLVM.cs +++ b/src/client/spark/view/Spark/PAndL/PAndLVM.cs @@ -11,11 +11,13 @@ using System.Linq; using System.Reactive.Linq; using System.Windows.Data; using static bts.shell.PAndLWindowFactory; -using static bts.api.orderman.Fill.Types; +using static mbts.ui.controls.DIYGrid; +using static mbts.spark.PAndLView; +using System.Windows.Media; +using System.Windows; namespace mbts.spark { class PAndLVM : DisposableViewModelBase { - public PAndLVM(IBus bus, PAndLModel model, PAndLStartupData startup_data) { _bus = bus; _model = model; @@ -66,13 +68,37 @@ namespace mbts.spark { } else { pnl_view.SetSortingCriteria(new List() { new SortingCriteria(nameof(PnLSummaryRow.account), true) - , new SortingCriteria(nameof(PnLSummaryRow.exchange_string), true) + , new SortingCriteria(nameof(PnLSummaryRow.exchange), true) , new SortingCriteria(nameof(PnLSummaryRow.symbol), true) , new SortingCriteria(nameof(PnLSummaryRow.security_text), true) }); } pnl_view.CollectionChanged += Pnl_view_CollectionChanged; + add_col(Columns.Account, DefaultColumnHeaders.Account, r => r.account, nameof(PnLSummaryRow.account), DefaultColumnWidths.Account, account_filter, DataGridFilterKnownTemplates.Regex); + add_col(Columns.Exchange, DefaultColumnHeaders.Exchange, r => r.exchange_string, nameof(PnLSummaryRow.exchange_string), DefaultColumnWidths.Exchange, exchange_string_filter, DataGridFilterKnownTemplates.Regex); + add_col(Columns.Symbol, DefaultColumnHeaders.Symbol, r => r.symbol, nameof(PnLSummaryRow.symbol), DefaultColumnWidths.Symbol, symbol_filter, DataGridFilterKnownTemplates.Regex); + add_col(Columns.Security, DefaultColumnHeaders.Security, r => r.security_text, nameof(PnLSummaryRow.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex); + add_col(Columns.SecurityType, DefaultColumnHeaders.SecurityType, r => r.security_type.ToString(), nameof(PnLSummaryRow.security_type), DefaultColumnWidths.SecurityType, security_type_filter, DataGridFilterKnownTemplates.DiscreteValues); + add_col(Columns.StartNetPos, "Start Net Pos", r => r.start_net_position.ToString(), nameof(PnLSummaryRow.start_net_position), 90, start_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); + add_col(Columns.StartPNL, "Start PnL", r => r.start_pnl_text, nameof(PnLSummaryRow.start_pnl), 90, start_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.start_pnl_background); + add_col(Columns.DayLong, "Day Long", r => r.day_long_quantity.ToString(), nameof(PnLSummaryRow.day_long_quantity), 90, day_long_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right); + add_col(Columns.DayShort, "Day Short", r => r.day_short_quantity.ToString(), nameof(PnLSummaryRow.day_short_quantity), 90, day_short_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right); + add_col(Columns.DayPNL, "Day PnL", r => r.day_pnl_text, nameof(PnLSummaryRow.day_pnl), 90, day_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.day_pnl_background); + add_col(Columns.TotalNetPos, "Total Net Pos", r => r.total_net_position.ToString(), nameof(PnLSummaryRow.total_net_position), 90, total_net_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); + add_col(Columns.TotalPNL, "Total PnL", r => r.total_pnl_text, nameof(PnLSummaryRow.total_pnl), 90, total_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.total_pnl_background); + add_col(Columns.PNLPrice, "PnL Price", r => r.pnl_price_text, nameof(PnLSummaryRow.pnl_price), 90, pnl_price_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); + void add_col(Columns key, string header, Func get_text, string sort_prop, double width, IDataGridColumnFilter filter, string filter_template, HorizontalAlignment h_align = HorizontalAlignment.Left, Func get_background = null) { + var col = get_background == null ? + new RenderTextColumn(key, get_text, filter, filter_template) : + new PnLDelegateBackgroundColumn(key, get_text, get_background, filter, filter_template); + col.Header = header; + col.Width = width; + col.sort_member_path = sort_prop; + col.horizontal_alignment = h_align; + columns.Add(col); + } + // setup the main Disposable and // hook up grid filters _dispose = Disposable.Create(pnl, @@ -96,6 +122,8 @@ namespace mbts.spark { ); } + public ObservableCollection columns { get; } = new ObservableCollection(); + private void Pnl_view_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { _update_totals(); } @@ -235,6 +263,14 @@ namespace mbts.spark { DateTime _last_pnl_update_time = DateTime.MinValue; ObservableCollection _pnl_data = new ObservableCollection(); - } + class PnLDelegateBackgroundColumn : RenderTextColumn { + readonly Func _get_brush; -} + public PnLDelegateBackgroundColumn(Columns key, Func get_text, Func get_brush, IDataGridColumnFilter filter, string filter_template) + : base(key, get_text, filter, filter_template) => _get_brush = get_brush; + + public override void draw(PnLSummaryRow row, DrawingContext dc, ColumnGeometry position, double row_height) => + CellRender.draw_text(dc, glyph_context, font_size, _get_text(row), position, row_height, font_brush, _get_brush.Invoke(row), horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding); + } + } +} \ No newline at end of file diff --git a/src/client/spark/view/Spark/PAndL/PAndLView.xaml b/src/client/spark/view/Spark/PAndL/PAndLView.xaml index 3f75eaa5a..82f340436 100644 --- a/src/client/spark/view/Spark/PAndL/PAndLView.xaml +++ b/src/client/spark/view/Spark/PAndL/PAndLView.xaml @@ -1,240 +1,122 @@ - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + \ No newline at end of file diff --git a/src/client/spark/view/Spark/PAndL/PAndLView.xaml.cs b/src/client/spark/view/Spark/PAndL/PAndLView.xaml.cs index 631b73e7d..19871786a 100644 --- a/src/client/spark/view/Spark/PAndL/PAndLView.xaml.cs +++ b/src/client/spark/view/Spark/PAndL/PAndLView.xaml.cs @@ -1,11 +1,15 @@ -using mbts.spark.filters; +using mbts.ui.controls; using System.Windows.Controls; namespace mbts.spark { internal partial class PAndLView : UserControl { + public enum Columns { Account, Exchange, Symbol, Security, SecurityType, StartNetPos, StartPNL, DayLong, DayShort, DayPNL, TotalNetPos, TotalPNL, PNLPrice } + public PAndLView(PAndLVM vm) { InitializeComponent(); DataContext = vm; } } + + class PnLRowView : DIYGrid.HighPerformanceRow { } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/PAndL/PnLSummaryRow.cs b/src/client/spark/view/Spark/PAndL/PnLSummaryRow.cs index b2de339a5..0f509ece0 100644 --- a/src/client/spark/view/Spark/PAndL/PnLSummaryRow.cs +++ b/src/client/spark/view/Spark/PAndL/PnLSummaryRow.cs @@ -1,36 +1,46 @@ using bts.api; using bts.api.orderman; using mbts.co2; +using mbts.ui; +using mbts.ui.controls; +using mbts.ui.converters; using mbts.ui.shell; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Windows.Media; namespace mbts.spark { - - - internal class PnLSummaryRow : ViewModelBase, IEquatable { + class PnLSummaryRow : ViewModelBase, IEquatable, DIYGrid.IRowVM { + static readonly SignedDataStore _brushes; + readonly PnLDoubleBrushCell _start_pnl, _day_pnl, _total_pnl; + readonly PnLPriceCell _pnl_price, _weighted_average_buy, _weighted_average_sell; (string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md, SecurityType? sec_type) key; - int _start_net_position, _total_net_position; - uint _day_long_quantity, _day_short_quantity; - double _total_pnl, _sod_pnl, _start_pnl, _pnl_price, _weighted_average_buy, _weighted_average_sell; IEnumerable _fills; double _driver; static readonly Dictionary cpp_dict = config.Configuration.get().cpp_entry; + static PnLSummaryRow() { + _brushes = new SignedDataStore(mk_brush(Colors.Green), mk_brush(Colors.Black), mk_brush(Colors.Red), mk_brush(Colors.Black)); + Brush mk_brush(Color c) => + new SolidColorBrush(c) { Opacity = 0.33 }.frozen(); + } public PnLSummaryRow(pnl_calc_type calc_type, (string, Venue?, string, SecurityImmutable?, SecurityMD?, SecurityType?) key, uint day_buy_quant, uint day_sell_quant, int net_pos, int sod_pos, double wavg_buy, double wavg_sell, IEnumerable fills) { this.key = key; + _start_pnl = new PnLDoubleBrushCell(this, nameof(start_pnl)); + _day_pnl = new PnLDoubleBrushCell(this, nameof(day_pnl)); + _total_pnl = new PnLDoubleBrushCell(this, nameof(total_pnl)); day_long_quantity = day_buy_quant; day_short_quantity = day_sell_quant; total_net_position = net_pos; start_net_position = sod_pos; - weighted_average_buy = wavg_buy; - weighted_average_sell = wavg_sell; + _pnl_price = new PnLPriceCell(this, nameof(pnl_price)); + _weighted_average_buy = new PnLPriceCell(this, nameof(weighted_average_buy), wavg_buy); + _weighted_average_sell = new PnLPriceCell(this, nameof(weighted_average_sell), wavg_sell); _fills = fills; // set the initial pnl and pnl_price when there has been a @@ -66,9 +76,9 @@ namespace mbts.spark { } } - public void update_pnl(pnl_calc_type pnl_request) { if (key.sec_md.HasValue) { + bool dirty = false; double cpp = _get_cpp_for_security(key.security.Value); if (pnl_request.HasFlag(pnl_calc_type.bid_or_ask)) { ulong bbo_ref = 0; @@ -86,10 +96,10 @@ namespace mbts.spark { start_pnl_total += _tick_pnl; } } - total_pnl = summary_pnl; - day_pnl = day_pnl_total; - start_pnl = start_pnl_total; - pnl_price = Double.NaN; + _total_pnl.on_next_distinct(summary_pnl, ref dirty); + _day_pnl.on_next_distinct(day_pnl_total, ref dirty); + _start_pnl.on_next_distinct(start_pnl_total, ref dirty); + _pnl_price.on_next_distinct(double.NaN, ref dirty); } else { if (PnLCalculator.get_driver(key.sec_md.Value, pnl_request, ref _driver)) { double summary_pnl = 0.0; @@ -104,17 +114,17 @@ namespace mbts.spark { start_pnl_total += _tick_pnl; } } - total_pnl = summary_pnl; - day_pnl = day_pnl_total; - start_pnl = start_pnl_total; - pnl_price = _driver; + _total_pnl.on_next_distinct(summary_pnl, ref dirty); + _day_pnl.on_next_distinct(day_pnl_total, ref dirty); + _start_pnl.on_next_distinct(start_pnl_total, ref dirty); + _pnl_price.on_next_distinct(_driver, ref dirty); } } + if (dirty) + raise_invalidated(); } } - void ignore() {/*These setters are just for WPF databinding :(*/ throw new NotImplementedException(); } - // probably need to come up with a GetHashCode() while we're in here public bool Equals(PnLSummaryRow other) { return other != null && @@ -140,145 +150,64 @@ namespace mbts.spark { return $"account: {account} exchange: {exchange_string} symbol: {symbol} security: {security_text} buy_quantity: {day_long_quantity} sell_quantity: {day_short_quantity} pnl: {total_pnl}"; } - public string account { - get => key.account; - set => ignore(); - } - - public Venue exchange { - get { - return key.exchange.HasValue ? key.exchange.Value : Venue.Unset; - } - set => ignore(); - } - - public string exchange_string { - get { - return exchange.ToString(); - } - set => ignore(); - } - - public string symbol { - get { - return key.symbol; - } - set => ignore(); - } - + public event Action invalidated; + // non-numeric + public string account => key.account; + public Venue exchange => key.exchange ?? Venue.Unset; + public string exchange_string => exchange.ToString(); + public string symbol => key.symbol; public SecurityImmutable? security => key.security; public SecurityType security_type => key.sec_type.GetValueOrDefault(); + public string security_text => security.display_name(); + // positions + public int start_net_position { get; } + public int total_net_position { get; } + public uint day_long_quantity { get; } + public uint day_short_quantity { get; } + // start pnl + public double start_pnl => _start_pnl.value; + public string start_pnl_text => _start_pnl.text; + public Brush start_pnl_background => _start_pnl.background; + // day pnl + public double day_pnl => _day_pnl.value; + public string day_pnl_text => _day_pnl.text; + public Brush day_pnl_background => _day_pnl.background; + // total pnl + public double total_pnl => _total_pnl.value; + public string total_pnl_text => _total_pnl.text; + public Brush total_pnl_background => _total_pnl.background; + // pnl price + public double pnl_price => _pnl_price.value; + public string pnl_price_text => _pnl_price.text; + // pnl weighted averages + public double weighted_average_buy => _weighted_average_buy.value; + public string weighted_average_buy_text => _weighted_average_buy.text; + public double weighted_average_sell => _weighted_average_sell.value; + public string weighted_average_sell_text => _weighted_average_sell.text; - public string security_text { - get => security.HasValue ? security.Value.ToString() : "Unknown Security"; - set => ignore(); - } - - - public int start_net_position { - get => _start_net_position; - set { - if (_start_net_position != value) { - _start_net_position = value; - RaisePropertyChanged(nameof(start_net_position)); - } - } - } - public double start_pnl { - get => _start_pnl; - set { - if (_start_pnl != value) { - _start_pnl = value; - RaisePropertyChanged(); - } - } - } - public uint day_long_quantity { - get => _day_long_quantity; - set { - if (_day_long_quantity != value) { - _day_long_quantity = value; - RaisePropertyChanged(nameof(day_long_quantity)); - } - } - } - public uint day_short_quantity { - get => _day_short_quantity; - set { - if (_day_short_quantity != value) { - _day_short_quantity = value; - RaisePropertyChanged(nameof(day_short_quantity)); - } - } - } - public double day_pnl { - get => _sod_pnl; - set { - if (_sod_pnl != value) { - _sod_pnl = value; - RaisePropertyChanged(); - } - } - } - - - public int total_net_position { - get => _total_net_position; - set { - if (_total_net_position != value) { - _total_net_position = value; - RaisePropertyChanged(nameof(total_net_position)); - } - } - } - - public double total_pnl { - get => _total_pnl; - set { - if (_total_pnl != value) { - _total_pnl = value; - RaisePropertyChanged(nameof(total_pnl)); - } - } - } - - - - - public double pnl_price { - get => _pnl_price; - set { - if (_pnl_price != value) { - _pnl_price = value; - RaisePropertyChanged(nameof(pnl_price)); - } - } - } - - public double weighted_average_buy { - get => _weighted_average_buy; - set { - if (_weighted_average_buy != value) { - _weighted_average_buy = value; - RaisePropertyChanged(nameof(weighted_average_buy)); - } - } - } - - public double weighted_average_sell { - get => _weighted_average_sell; - set { - if (_weighted_average_sell != value) { - _weighted_average_sell = value; - RaisePropertyChanged(nameof(weighted_average_sell)); - } - } - } + public void raise_invalidated() => invalidated?.Invoke(); + ObservableCollection DIYGrid.IRowVM.row_animations { get; } = new ObservableCollection(); + IReadOnlyList DIYGrid.IRowVM.cell_stores => new DIYGrid.IGridCellStore[0]; //public IEnumerable sod_fills => _fills.Where(f => f.Timestamp.ToDateTime() >= _start_of_day); //public IEnumerable pos_fills => _fills.Where(f => f.Timestamp.ToDateTime() < _start_of_day); + class PnLDoubleBrushCell : CellProperty { + Brush _background; + + public PnLDoubleBrushCell(PnLSummaryRow owner, string prop) : base(d => double.IsNaN(d) ? string.Empty : d.ToString("N2"), double.NaN) => + _value_set += _ => { + _background = null; + owner.RaisePropertyChanged(prop); + }; + + public Brush background => _background ?? (_background = _brushes.get(value)); + } + + class PnLPriceCell : CellProperty { + public PnLPriceCell(PnLSummaryRow owner, string prop, double init = 0) + : base(price => owner.security.spec_or_default().format(price), init) => + _value_set += _ => owner.RaisePropertyChanged(prop); + } } - - -} +} \ No newline at end of file From 7a1ffa1ea9b688c4fe375285bb7a46caae4e84a9 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Fri, 5 Oct 2018 14:00:09 -0400 Subject: [PATCH 149/203] fix NumericSignBrushConverter --- src/client/spark/view/Converters/NumericSignBrushConverter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/spark/view/Converters/NumericSignBrushConverter.cs b/src/client/spark/view/Converters/NumericSignBrushConverter.cs index fb37c307c..da6e261b3 100644 --- a/src/client/spark/view/Converters/NumericSignBrushConverter.cs +++ b/src/client/spark/view/Converters/NumericSignBrushConverter.cs @@ -43,8 +43,8 @@ namespace mbts.ui.converters { _opacity = value; _brushes = new SignedDataStore( _brushes.positive.with(value).frozen(), - _brushes.positive.with(value).frozen(), - _brushes.positive.with(value).frozen(), + _brushes.neutral.with(value).frozen(), + _brushes.negative.with(value).frozen(), _brushes.error.with(value).frozen()); } } From 1a7d0d75a81263a15c5e5d849bd525b7f7a9e696 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Fri, 5 Oct 2018 13:28:05 -0500 Subject: [PATCH 150/203] merge fix problem w/ the DefaultColumns in mview --- .../spark/Modules/Market/MarketView/MarketViewWindowFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs index 6d1f5ce1a..6d8aee17c 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs @@ -85,7 +85,7 @@ namespace bts.shell { public static GridColumnSettings working_bids => new GridColumnSettings(col_id(Columns.WorkingBids), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); public static GridColumnSettings bid_implied_size => new GridColumnSettings(col_id(Columns.BidImpliedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); public static GridColumnSettings bid_direct_size => new GridColumnSettings(col_id(Columns.BidDirectSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); - public static GridColumnSettings bid_merged_size => new GridColumnSettings(col_id(Columns.BidImpliedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); + public static GridColumnSettings bid_merged_size => new GridColumnSettings(col_id(Columns.BidMergedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.Price); public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.Price); public static GridColumnSettings ask_merged_size => new GridColumnSettings(col_id(Columns.AskMergedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size); From f6b5635715693cc03e224a272a082774e35ed4ea Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Fri, 5 Oct 2018 15:45:42 -0400 Subject: [PATCH 151/203] SPARK-1659 packet may contain implied or direct or both. need to preserve the other market. --- src/lib/md/Diag/ShmDumper.cpp | 10 +++---- src/lib/md/PriceSize.h | 2 +- .../md/platforms/mdp3_onixs/MDP3Listener.cpp | 26 +++++++++++++------ .../md/platforms/mdp3_onixs/MDP3Listener.h | 24 +++++++++++------ 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/lib/md/Diag/ShmDumper.cpp b/src/lib/md/Diag/ShmDumper.cpp index b60bbb98d..914f58897 100644 --- a/src/lib/md/Diag/ShmDumper.cpp +++ b/src/lib/md/Diag/ShmDumper.cpp @@ -163,31 +163,31 @@ string dump_entry(const HotDepthEntry& entry) { res << "'timestamp': " << dump(entry.time()) << ", "; res << "'key': " << entry.key << ", "; - res << "'bid': ["; + res << "\n'bid': ["; for (const auto& level : entry.direct) { res << dump(level.bid()) << ", "; } res << "], "; - res << "'ask': ["; + res << "\n'ask': ["; for (const auto& level : entry.direct) { res << dump(level.ask()) << ", "; } res << "], "; - res << "'implied_bid': ["; + res << "'\nimplied_bid': ["; for (const auto& level : entry.implied) { res << dump(level.bid()) << ", "; } res << "], "; - res << "'implied_ask': ["; + res << "'\nimplied_ask': ["; for (const auto& level : entry.implied) { res << dump(level.ask()) << ", "; } res << "], "; - res << "'settlement': '" << dump(entry.settlement) << "', "; + res << "\n'settlement': '" << dump(entry.settlement) << "', "; res << "'status': '" << CoconutSecurityStatus_Name((CoconutSecurityStatus)entry.status) << "', "; res << "'sequence': '" << entry.sequence << "', "; res << " }"; diff --git a/src/lib/md/PriceSize.h b/src/lib/md/PriceSize.h index fc78701fd..c8b647d39 100644 --- a/src/lib/md/PriceSize.h +++ b/src/lib/md/PriceSize.h @@ -32,7 +32,7 @@ class PriceSize { public: PriceSize(IntPrice price, Size size) : - _price(price), + _price(size == 0 ? INT_NAN : price), _size(size), _direct_size(size) { } diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp index 0acd2b757..de7fd2d39 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.cpp @@ -113,11 +113,11 @@ void MDP3Listener::onEndOfPacket(onixs::Handler&, const onixs::PacketArgs&) { // Security discovery void MDP3Listener::onBookUpdate(onixs::Handler&, const onixs::Security& sec, const onixs::DirectBook& book) { - onBookUpdate(sec, book); + onBookUpdate(sec, book, book_type_direct); } void MDP3Listener::onBookUpdate(onixs::Handler&, const onixs::Security& sec, const onixs::ImpliedBook& book) { - onBookUpdate(sec, book); + onBookUpdate(sec, book, book_type_implied); } // @@ -239,11 +239,21 @@ void MDP3Listener::flush_books() { // This is called at the end of the packet // Write all book updates to shared memory and reset for the next packet for (auto& entry : _book_updates) { - uint16_t sequence = (_latest_sequence & 0xFFFF); - entry.handle.update_books(_packet_arrived, - entry.bids.first, entry.asks.first, - entry.bids.second, entry.asks.second, - sequence); + uint32_t sequence = _latest_sequence; + if (entry.type_flags == book_type_direct) { + entry.handle.update_direct(_packet_arrived, + entry.bids.first, entry.asks.first, sequence); + } + else if (entry.type_flags == book_type_implied) { + entry.handle.update_implied(_packet_arrived, + entry.bids.second, entry.asks.second, sequence); + } + else if (entry.type_flags == book_type_both) { + entry.handle.update_books(_packet_arrived, + entry.bids.first, entry.asks.first, + entry.bids.second, entry.asks.second, + sequence); + } } // Reset pending updates _book_updates.clear(); @@ -255,7 +265,7 @@ void MDP3Listener::update_all_status(CoconutSecurityStatus status) { }); } -md::MDP3Listener::BookData::BookData(MDProducerHandle & handle) : handle(handle), bids(), asks() { } +md::MDP3Listener::BookData::BookData(MDProducerHandle & handle) : handle(handle), type_flags(0), bids(), asks() { } }}//namespace diff --git a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h index 6c53abdd7..f0dfb43f6 100644 --- a/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h +++ b/src/server/sparkd/md/platforms/mdp3_onixs/MDP3Listener.h @@ -23,6 +23,11 @@ class MDP3Listener : public onixs::MarketDataListener, public onixs::SecurityListener { + typedef uint32_t BookType; + static constexpr BookType book_type_direct=1; + static constexpr BookType book_type_implied=2; + static constexpr BookType book_type_both=(book_type_direct|book_type_implied); + public: MDP3Listener(CMESecurityIndex& index, MDProducer&, @@ -76,7 +81,7 @@ public: // void onBookUpdate(onixs::Handler&, const onixs::Security&, const onixs::ConsolidatedBook&) override; template - void onBookUpdate(const onixs::Security& sec, const TBook& book); + void onBookUpdate(const onixs::Security& sec, const TBook& book, BookType booktype); // // Trades @@ -197,7 +202,7 @@ private: struct BookData { BookData(MDProducerHandle& handle); MDProducerHandle handle; - + BookType type_flags; std::pair, std::array> bids; std::pair, std::array> asks; }; @@ -352,7 +357,7 @@ void MDP3Listener::onSpread(onixs::Handler&, const onixs::Security&, const Instr } template -void MDP3Listener::onBookUpdate(const onixs::Security& sec, const TBook& onixs_book) { +void MDP3Listener::onBookUpdate(const onixs::Security& sec, const TBook& onixs_book, BookType booktype) { tracepoint (bts_trace,trace_order,(char*) "onBookUpdate", _latest_sequence, (uint16_t) _latest_sequence, 0); auto data = _index.get_security(sec.id()); @@ -370,6 +375,7 @@ void MDP3Listener::onBookUpdate(const onixs::Security& sec, const TBook& onixs_b _book_updates.emplace_back(data->handle); } BookData& cache = found ? *find_iter : _book_updates.back(); + cache.type_flags |= booktype; auto& cache_bids = std::get>(cache.bids); auto& cache_asks = std::get>(cache.asks); @@ -386,12 +392,14 @@ void MDP3Listener::onBookUpdate(const onixs::Security& sec, const TBook& onixs_b for (size_t x = 0; x < asks_size; ++x) { cache_asks[x] = pricesize_of_price_size(onixs_book.offers()[x].price(), display_factor, onixs_book.offers()[x].quantity()); } + if (!found) { // NaN out the remaining depth - for (size_t x = bids_size; x < MAX_DEPTH; ++x) { - cache_bids[x] = NO_MARKET; - } - for (size_t x = asks_size; x < MAX_DEPTH; ++x) { - cache_asks[x] = NO_MARKET; + for (size_t x = bids_size; x < MAX_DEPTH; ++x) { + cache_bids[x] = NO_MARKET; + } + for (size_t x = asks_size; x < MAX_DEPTH; ++x) { + cache_asks[x] = NO_MARKET; + } } } From 1a760a579a24ef9eb9875fd7809ee56b0674af48 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Fri, 5 Oct 2018 16:43:13 -0500 Subject: [PATCH 152/203] PnL now support settings --- .../Modules/Trading/PAndLWindowFactory.cs | 83 +++++++++++++++++-- .../Modules/Trading/TradingShellModule.cs | 2 +- .../UserSettings/UserSettings.cs | 7 +- .../Controls/DIYGrid/Columns/ColumnBase.cs | 13 ++- src/client/spark/view/Spark/PAndL/PAndLVM.cs | 46 +++++++--- .../spark/view/Spark/PAndL/PAndLWindow.cs | 39 ++++++++- 6 files changed, 164 insertions(+), 26 deletions(-) diff --git a/src/client/spark/Modules/Trading/PAndLWindowFactory.cs b/src/client/spark/Modules/Trading/PAndLWindowFactory.cs index 270b95ad4..35fed0a50 100644 --- a/src/client/spark/Modules/Trading/PAndLWindowFactory.cs +++ b/src/client/spark/Modules/Trading/PAndLWindowFactory.cs @@ -1,30 +1,38 @@ using bts.api; using bts.api.orderman; using bts.spark; +using mbts.persist; using mbts.spark; using mbts.spark.filters; using mbts.ui; +using mbts.ui.settings; using mbts.ui.spark; using ProtoBuf; using System; using System.Collections.Generic; +using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; +using static mbts.spark.PAndLView; +using static mbts.ui.controls.DIYGrid; namespace bts.shell { internal class PAndLWindowFactory : ShellWindowFactory { readonly IBus _bus; readonly PAndLModel _model; - public PAndLWindowFactory(IShellModule owner, IBus bus, PAndLModel model) : base(owner, "SparkPAndL") { + readonly UserSettingsHub _settings_hub; + + public PAndLWindowFactory(IShellModule owner, IBus bus, UserSettingsHub settings_hub, PAndLModel model) : base(owner, "SparkPAndL") { _bus = bus; _model = model; - + _settings_hub = settings_hub; _dispose = bus.subscribe(SparkTags.ShowPAndLTag).OnDispatcher().Subscribe(show); } + public static string col_id(Columns col) => col.ToString(); + public override Task load(PAndLStartupData state) { - var w = new PAndLWindow(_model, this, _bus, state, SparkCommands.ShowPAndL.icon) as IShellWindow; - return Task.FromResult(w); + return Task.FromResult(_create(state)); } async void show(PAndLStartupData startup_data) { @@ -32,6 +40,14 @@ namespace bts.shell { _bus.post(SparkTags.ShowWindow, w); } + IShellWindow _create(PAndLStartupData state) { + var settings_manager = new WindowSettingsManager(_settings_hub, + state.grid, + get: us => us.pnl_grid, + set: (us, gs) => us.with(provided_pnl_gridsettings: gs)); + return new PAndLWindow(_model, this, _bus, state, SparkCommands.ShowPAndL.icon, settings_manager); + } + [ProtoContract] public class PAndLStartupData { @@ -40,21 +56,74 @@ namespace bts.shell { /// public PAndLStartupData() { } - public PAndLStartupData(List sorting_criteria, FilterStates filters, pnl_calc_type calc_type) { - this.sort_criteria = sorting_criteria; + public PAndLStartupData(List sorting_criteria, FilterStates filters, pnl_calc_type calc_type, GridSettings grid = default) { + _sort_criteria = sorting_criteria; this.filters = filters; this.calc_type = calc_type; + this.grid = grid; } + public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion); + + public IReadOnlyList sorting_criterion => _sort_criteria ?? (_sort_criteria = DefaultColumns.default_sorting_criterion.ToList()); + public static PAndLStartupData Default() => + new PAndLStartupData(null, new FilterStates(), pnl_calc_type.midpoint); + + [ProtoMember(5)] - public readonly List sort_criteria; + List _sort_criteria; [ProtoMember(10)] public FilterStates filters; [ProtoMember(15)] public pnl_calc_type calc_type; //[ProtoMember(20)] // ProtoMember 20 cannot be used further + [ProtoMember(25)] + public GridSettings grid; + static class DefaultColumns { + + public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account); + public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange); + public static GridColumnSettings symbol => new GridColumnSettings(col_id(Columns.Symbol), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol); + public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security); + public static GridColumnSettings security_type => new GridColumnSettings(col_id(Columns.SecurityType), DefaultColumnHeaders.SecurityType, width: DefaultColumnWidths.SecurityType); + public static GridColumnSettings start_net_position => new GridColumnSettings(col_id(Columns.StartNetPos), DefaultColumnHeaders.StartNetPos, width: DefaultColumnWidths.PnL_Generic); + public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, width: DefaultColumnWidths.PnL_Generic); + public static GridColumnSettings day_long_pnl => new GridColumnSettings(col_id(Columns.DayLong), DefaultColumnHeaders.DayLong, width: DefaultColumnWidths.PnL_Generic); + public static GridColumnSettings day_short_pnl => new GridColumnSettings(col_id(Columns.DayShort), DefaultColumnHeaders.DayShort, width: DefaultColumnWidths.PnL_Generic); + public static GridColumnSettings day_pnl => new GridColumnSettings(col_id(Columns.DayPNL), DefaultColumnHeaders.DayPnL, width: DefaultColumnWidths.PnL_Generic); + public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, width: DefaultColumnWidths.PnL_Generic); + public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, width: DefaultColumnWidths.PnL_Generic); + public static GridColumnSettings pnl_price => new GridColumnSettings(col_id(Columns.PNLPrice), DefaultColumnHeaders.PnLPrice, width: DefaultColumnWidths.PnL_Generic); + + public static IEnumerable default_sorting_criterion { + get { + yield return new SortingCriteria(account.id, true); + yield return new SortingCriteria(exchange.id, true); + yield return new SortingCriteria(symbol.id, true); + yield return new SortingCriteria(security.id, true); + } + } + + public static IEnumerable all { + get { + yield return account; + yield return exchange; + yield return symbol; + yield return security; + yield return security_type; + yield return start_pnl; + yield return day_long_pnl; + yield return day_short_pnl; + yield return day_pnl; + yield return total_net_pos; + yield return total_pnl; + yield return pnl_price; + } + } + + } } [ProtoContract] diff --git a/src/client/spark/Modules/Trading/TradingShellModule.cs b/src/client/spark/Modules/Trading/TradingShellModule.cs index 419064b2f..eede04c5f 100644 --- a/src/client/spark/Modules/Trading/TradingShellModule.cs +++ b/src/client/spark/Modules/Trading/TradingShellModule.cs @@ -25,7 +25,7 @@ namespace bts.shell { _lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model, md_auxiliary_model); _obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model); _fwf = new FillsWindowFactory(this, bus, settings_hub, orders_model); - _plwf = new PAndLWindowFactory(this, bus, pandl_model); + _plwf = new PAndLWindowFactory(this, bus, settings_hub, pandl_model); } public ImageSource icon => Icons.Trades; diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs index 9122ec794..7fbbfac3d 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs @@ -34,6 +34,8 @@ namespace mbts.spark { GridSettings _fills_grid; [ProtoMember(8)] GridSettings _synthetic_management_grid; + [ProtoMember(9)] + GridSettings _pnl_grid; public UserSettings() { } @@ -43,13 +45,15 @@ namespace mbts.spark { public GridSettings time_and_sales_grid => _time_and_sales_grid ?? (_time_and_sales_grid = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all)); public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings); public GridSettings synthetic_management_grid => _synthetic_management_grid ?? (_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings); + public GridSettings pnl_grid => _pnl_grid ?? (_pnl_grid = bts.shell.PAndLWindowFactory.PAndLStartupData.DefaultGridSettings); public UserSettings with(LadderSettings ladder = null, NotificationSettings notification = null, GridSettings provided_market_view_gridsettings = null, GridSettings time_and_sales = null, GridSettings provided_fills_gridsettings = null, - GridSettings provided_synthetic_management_gridsettings = null) => + GridSettings provided_synthetic_management_gridsettings = null, + GridSettings provided_pnl_gridsettings = null) => new UserSettings { _ladder = ladder ?? _ladder, _notification = notification ?? _notification, @@ -57,6 +61,7 @@ namespace mbts.spark { _time_and_sales_grid = time_and_sales ?? _time_and_sales_grid, _fills_grid = provided_fills_gridsettings ?? _fills_grid, _synthetic_management_grid = provided_synthetic_management_gridsettings ?? _synthetic_management_grid, + _pnl_grid = provided_pnl_gridsettings ?? _pnl_grid, }; public static class FileExtensions { diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index db518b896..2cbce30ca 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -37,7 +37,8 @@ namespace mbts.ui.controls { FillKind = 70, Description = 300, N_Legs = 68, - N_Working_Orders = 123; + N_Working_Orders = 123, + PnL_Generic = 90; } public static class DefaultColumnHeaders { @@ -74,7 +75,15 @@ namespace mbts.ui.controls { Name = "Name", Description = "Description", N_Legs = "# Legs", - N_Working_Orders = "# Working Orders"; + N_Working_Orders = "# Working Orders", + StartNetPos = "Start Net Pos", + StartPnL = "Start PnL", + DayLong = "Day Long", + DayShort = "Day Short", + DayPnL = "Day PnL", + TotalNetPos = "Total Net Pos", + TotalPnL = "Total PnL", + PnLPrice = "PnL Price"; } public interface IKeyedColumn { diff --git a/src/client/spark/view/Spark/PAndL/PAndLVM.cs b/src/client/spark/view/Spark/PAndL/PAndLVM.cs index 7deebb8ae..481583c87 100644 --- a/src/client/spark/view/Spark/PAndL/PAndLVM.cs +++ b/src/client/spark/view/Spark/PAndL/PAndLVM.cs @@ -15,12 +15,15 @@ using static mbts.ui.controls.DIYGrid; using static mbts.spark.PAndLView; using System.Windows.Media; using System.Windows; +using mbts.persist; +using mbts.ui.settings; namespace mbts.spark { class PAndLVM : DisposableViewModelBase { - public PAndLVM(IBus bus, PAndLModel model, PAndLStartupData startup_data) { + public PAndLVM(IBus bus, PAndLModel model, PAndLStartupData startup_data, IWindowSettingsManager settings_manager) { _bus = bus; _model = model; + _settings_manager = settings_manager; _selected_pnl_calc_type = backing(nameof(selected_pnl_calc_type)); // get initial value from settings // else default to midpoint @@ -63,8 +66,8 @@ namespace mbts.spark { security_type_filter.load_state(fs.security_types); } // restore sort state or supply default value - if (startup_data?.sort_criteria?.Any() == true) { - pnl_view.SetSortingCriteria(startup_data.sort_criteria); + if (startup_data?.sorting_criterion?.Any() == true) { + pnl_view.SetSortingCriteria(startup_data.sorting_criterion); } else { pnl_view.SetSortingCriteria(new List() { new SortingCriteria(nameof(PnLSummaryRow.account), true) @@ -80,14 +83,14 @@ namespace mbts.spark { add_col(Columns.Symbol, DefaultColumnHeaders.Symbol, r => r.symbol, nameof(PnLSummaryRow.symbol), DefaultColumnWidths.Symbol, symbol_filter, DataGridFilterKnownTemplates.Regex); add_col(Columns.Security, DefaultColumnHeaders.Security, r => r.security_text, nameof(PnLSummaryRow.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex); add_col(Columns.SecurityType, DefaultColumnHeaders.SecurityType, r => r.security_type.ToString(), nameof(PnLSummaryRow.security_type), DefaultColumnWidths.SecurityType, security_type_filter, DataGridFilterKnownTemplates.DiscreteValues); - add_col(Columns.StartNetPos, "Start Net Pos", r => r.start_net_position.ToString(), nameof(PnLSummaryRow.start_net_position), 90, start_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); - add_col(Columns.StartPNL, "Start PnL", r => r.start_pnl_text, nameof(PnLSummaryRow.start_pnl), 90, start_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.start_pnl_background); - add_col(Columns.DayLong, "Day Long", r => r.day_long_quantity.ToString(), nameof(PnLSummaryRow.day_long_quantity), 90, day_long_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right); - add_col(Columns.DayShort, "Day Short", r => r.day_short_quantity.ToString(), nameof(PnLSummaryRow.day_short_quantity), 90, day_short_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right); - add_col(Columns.DayPNL, "Day PnL", r => r.day_pnl_text, nameof(PnLSummaryRow.day_pnl), 90, day_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.day_pnl_background); - add_col(Columns.TotalNetPos, "Total Net Pos", r => r.total_net_position.ToString(), nameof(PnLSummaryRow.total_net_position), 90, total_net_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); - add_col(Columns.TotalPNL, "Total PnL", r => r.total_pnl_text, nameof(PnLSummaryRow.total_pnl), 90, total_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.total_pnl_background); - add_col(Columns.PNLPrice, "PnL Price", r => r.pnl_price_text, nameof(PnLSummaryRow.pnl_price), 90, pnl_price_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); + add_col(Columns.StartNetPos, DefaultColumnHeaders.StartNetPos, r => r.start_net_position.ToString(), nameof(PnLSummaryRow.start_net_position), DefaultColumnWidths.PnL_Generic, start_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); + add_col(Columns.StartPNL, DefaultColumnHeaders.StartPnL, r => r.start_pnl_text, nameof(PnLSummaryRow.start_pnl), DefaultColumnWidths.PnL_Generic, start_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.start_pnl_background); + add_col(Columns.DayLong, DefaultColumnHeaders.DayLong, r => r.day_long_quantity.ToString(), nameof(PnLSummaryRow.day_long_quantity), DefaultColumnWidths.PnL_Generic, day_long_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right); + add_col(Columns.DayShort, DefaultColumnHeaders.DayShort, r => r.day_short_quantity.ToString(), nameof(PnLSummaryRow.day_short_quantity), DefaultColumnWidths.PnL_Generic, day_short_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right); + add_col(Columns.DayPNL, DefaultColumnHeaders.DayPnL, r => r.day_pnl_text, nameof(PnLSummaryRow.day_pnl), 90, day_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.day_pnl_background); + add_col(Columns.TotalNetPos, DefaultColumnHeaders.TotalNetPos, r => r.total_net_position.ToString(), nameof(PnLSummaryRow.total_net_position), DefaultColumnWidths.PnL_Generic, total_net_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); + add_col(Columns.TotalPNL, DefaultColumnHeaders.TotalPnL, r => r.total_pnl_text, nameof(PnLSummaryRow.total_pnl), DefaultColumnWidths.PnL_Generic, total_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.total_pnl_background); + add_col(Columns.PNLPrice, DefaultColumnHeaders.PnLPrice, r => r.pnl_price_text, nameof(PnLSummaryRow.pnl_price), DefaultColumnWidths.PnL_Generic, pnl_price_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); void add_col(Columns key, string header, Func get_text, string sort_prop, double width, IDataGridColumnFilter filter, string filter_template, HorizontalAlignment h_align = HorizontalAlignment.Left, Func get_background = null) { var col = get_background == null ? new RenderTextColumn(key, get_text, filter, filter_template) : @@ -96,12 +99,25 @@ namespace mbts.spark { col.Width = width; col.sort_member_path = sort_prop; col.horizontal_alignment = h_align; + col.id = col_id(key); columns.Add(col); } + var col_settings_flags = new Dictionary { + { Columns.StartPNL.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }, + { Columns.DayPNL.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }, + { Columns.TotalPNL.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }, + }; + + grid_settings_manager = new SimpleGridSettingsManager(columns, col_settings_flags, _pnl_data); + // this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.) + grid_settings_manager.load_from(settings_manager.current_settings()); + + // setup the main Disposable and // hook up grid filters _dispose = Disposable.Create(pnl, + settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager), DataGridFilterHelper.hook_filters(pnl_view, account_filter, exchange_string_filter, @@ -123,6 +139,13 @@ namespace mbts.spark { } public ObservableCollection columns { get; } = new ObservableCollection(); + public SimpleGridSettingsManager grid_settings_manager { get; } + public GridSettings get_grid_save_state() { + if (_settings_manager.instance_settings == null) + return null; + else + return grid_settings_manager.create(); + } private void Pnl_view_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { _update_totals(); @@ -255,6 +278,7 @@ namespace mbts.spark { readonly IBus _bus; readonly PAndLModel _model; + IWindowSettingsManager _settings_manager; static readonly TimeSpan _UPDATE_PERIOD = TimeSpan.FromSeconds(1); static readonly TimeSpan _POSITION_UPDATE_PERIOD = TimeSpan.FromMilliseconds(333); readonly Property _selected_pnl_calc_type; diff --git a/src/client/spark/view/Spark/PAndL/PAndLWindow.cs b/src/client/spark/view/Spark/PAndL/PAndLWindow.cs index 0fa58bd01..1665478b6 100644 --- a/src/client/spark/view/Spark/PAndL/PAndLWindow.cs +++ b/src/client/spark/view/Spark/PAndL/PAndLWindow.cs @@ -1,7 +1,11 @@ using bts.api; using bts.shell; using bts.spark; +using mbts.persist; using mbts.ui; +using mbts.ui.settings; +using mbts.ui.spark; +using mbts.ui.utility; using System; using System.Collections.Generic; using System.Linq; @@ -12,17 +16,21 @@ using System.Windows.Media; namespace mbts.spark { internal class PAndLWindow : SparkWindow, ISharedWindow { - const string _window_type = "PAndL", PNL_THREAD_ID = "PnL"; + readonly IWindowSettingsManager _settings_manager; PAndLWindowFactory.PAndLStartupData _startup_data; PAndLModel _model; PAndLView _view; PAndLVM _vm; PAndLWindowFactory _factory; - public PAndLWindow(PAndLModel model, PAndLWindowFactory factory, IBus bus, PAndLWindowFactory.PAndLStartupData startup_data, ImageSource Icon ) : base("P&L", bus, factory, SparkCommands.ShowPAndL.icon) { + IReadOnlyList _commands; + + public PAndLWindow(PAndLModel model, PAndLWindowFactory factory, IBus bus, PAndLWindowFactory.PAndLStartupData startup_data, ImageSource Icon, WindowSettingsManager settings_manager) : base("P&L", bus, factory, SparkCommands.ShowPAndL.icon) { _model = model; _factory = factory; _startup_data = startup_data; + _settings_manager = settings_manager; + Icon.Freeze(); } public override string window_type => _window_type; @@ -30,20 +38,43 @@ namespace mbts.spark { new PAndLWindowFactory.PAndLStartupData( _vm.get_sorting_states(), _vm.get_filters_states(), - _vm.selected_pnl_calc_type + _vm.selected_pnl_calc_type, + _vm.get_grid_save_state() ); public override UIElement Content => _view; + public override IEnumerable Commands => _commands; + public override double MinWidth => 894; public string thread_id => PNL_THREAD_ID; public void thread_init() { - _vm = new PAndLVM(_bus, _model, _startup_data); + _vm = new PAndLVM(_bus, _model, _startup_data, _settings_manager); _view = new PAndLView(_vm); _view.DataContext = _vm; + _commands = new List { + SparkCommands.window_settings_command(new DialogCommand(() => { + var vm = new WindowSettingsDialogVM( + _settings_manager, + us => us.market_view_grid, + UserSettings.FileExtensions.GRID, + s => _vm.grid_settings_manager.create_vm( + // we need to get the most up to date GridSettings when launching b/c users may + // have changed some settings on the grid directly, like column widths. + s.with(_vm.columns.Select( c => c.create())), + this.observe_title() + ) + ); + // TODO: bind to main window's Title + var dialog = new SimpleGridSettingsDialog { Title = "PnL Grid Settings", DataContext = vm }; + vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); + return dialog; + })) + }; } public override void Dispose() { Disposable.dispose(ref _vm); + Commands.OfType().DisposeAll(); } From b47055d814b978c16cbd12a12bf9674262f48d13 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Fri, 5 Oct 2018 18:36:25 -0400 Subject: [PATCH 153/203] increasing log period of VAP publisher --- src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp index c1546403c..98675ba97 100644 --- a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp +++ b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp @@ -36,7 +36,7 @@ struct MarketDataAuxiliaryServiceServer::PImpl{ poll_period_ms(100), state(), log_counter(0), - log_trunc(10){ + log_trunc(300){ } }; From 1a947097a53af12b034e03566b8ec6c72f2be34b Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Mon, 8 Oct 2018 08:18:55 -0400 Subject: [PATCH 154/203] Wrapper around AdminModel with helper functions --- src/client/spark/Admin/AdminModelWrapper.cs | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/client/spark/Admin/AdminModelWrapper.cs diff --git a/src/client/spark/Admin/AdminModelWrapper.cs b/src/client/spark/Admin/AdminModelWrapper.cs new file mode 100644 index 000000000..eada2a9c8 --- /dev/null +++ b/src/client/spark/Admin/AdminModelWrapper.cs @@ -0,0 +1,38 @@ +using bts; +using bts.api.admin; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace mbts.spark { + class AdminModelWrapper{ + private bts.api.admin.AdminDataImmutable _admin_data; + public AdminModelWrapper(bts.api.admin.AdminDataImmutable admin_data){ + _admin_data=admin_data; + } + // for a given user, returns the accounts the user is a + // part of along with the permissions + public IEnumerable> get_trading_permissions(UInt32 user_id){ + var rv = new List>(); + if(_admin_data!=null){ + var users = _admin_data.Users.Where(u=>u.Id==user_id); + if(users!=null && users.Count()==1){ + var user = users.ElementAt(0); + if(user.HasUserData && user.UserData.HasAccountPermissions){ + foreach (var kv in user.UserData.AccountPermissions.AccountPermissions){ + var account_id=kv.Key; + var accounts=_admin_data.Accounts.Where(a=>a.Id==account_id); + if(accounts!=null&&accounts.Count()==1){ + var account=accounts.ElementAt(0); + var tp=kv.Value.TradingPermission; + var rv_val=new TradingPermissionUnitImmutable(tp); + rv.Add(Tuple.Create(account,rv_val)); + } + } + } + } + } + return rv; + } + } +} \ No newline at end of file From 6de541639426cc78f116b02a57cf8432e351f1a7 Mon Sep 17 00:00:00 2001 From: Kevin Darby Date: Mon, 8 Oct 2018 10:38:22 -0400 Subject: [PATCH 155/203] over to taha --- src/client/spark/Admin/AdminModelWrapper.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client/spark/Admin/AdminModelWrapper.cs b/src/client/spark/Admin/AdminModelWrapper.cs index eada2a9c8..c89600ddc 100644 --- a/src/client/spark/Admin/AdminModelWrapper.cs +++ b/src/client/spark/Admin/AdminModelWrapper.cs @@ -34,5 +34,9 @@ namespace mbts.spark { } return rv; } + public IEnumerable viewable_accounts(string user) + { + return null; + } } } \ No newline at end of file From c3f418353137bfac0751dfec605df6fb8a8dd1f8 Mon Sep 17 00:00:00 2001 From: Kevin Darby Date: Mon, 8 Oct 2018 11:20:16 -0400 Subject: [PATCH 156/203] switching dirs --- src/client/spark/Admin/AdminModel.cs | 3 ++ src/client/spark/Admin/AdminModelWrapper.cs | 48 +++++++++++++-------- src/client/spark/Orders/OrdersModel.cs | 5 ++- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/client/spark/Admin/AdminModel.cs b/src/client/spark/Admin/AdminModel.cs index 52efce6db..3834350a1 100644 --- a/src/client/spark/Admin/AdminModel.cs +++ b/src/client/spark/Admin/AdminModel.cs @@ -18,11 +18,13 @@ namespace mbts.spark { _account_map = new Dictionary(), _user_map = new Dictionary(); IDisposable _dispose; + AdminDataImmutable _data; [ImportingConstructor] AdminModel(AdminService service, [Import("username")]string user_name) { _user_name = user_name; _dispose = service.subscribei_admin_data().Select(update => { + _data = update.all; IEnumerable active_users = from admin_data in update.all from bts_user in admin_data.Users @@ -51,6 +53,7 @@ namespace mbts.spark { }); } + public AdminModelWrapper model_wrapper => new AdminModelWrapper(_data); public IObservable> on_users => _users_subject.AsObservable(); public IObservable> on_accounts => _accounts_subject.AsObservable(); public IObservable> on_user_tuples => on_users.Select(d => d.Select(kv => new UserTuple(kv.Key, kv.Value)).ToList()); diff --git a/src/client/spark/Admin/AdminModelWrapper.cs b/src/client/spark/Admin/AdminModelWrapper.cs index c89600ddc..9175ecc6c 100644 --- a/src/client/spark/Admin/AdminModelWrapper.cs +++ b/src/client/spark/Admin/AdminModelWrapper.cs @@ -7,26 +7,42 @@ using System.Linq; namespace mbts.spark { class AdminModelWrapper{ private bts.api.admin.AdminDataImmutable _admin_data; - public AdminModelWrapper(bts.api.admin.AdminDataImmutable admin_data){ + public AdminModelWrapper(bts.api.admin.AdminDataImmutable admin_data) + { _admin_data=admin_data; } // for a given user, returns the accounts the user is a // part of along with the permissions - public IEnumerable> get_trading_permissions(UInt32 user_id){ + public IEnumerable> get_trading_permissions(UInt32 user_id) + { + return search_admin(u => u.Id == user_id); + } + public IEnumerable viewable_accounts(string user) + { + return null; + } + + IEnumerable search_admin(Func predicate) + { var rv = new List>(); - if(_admin_data!=null){ - var users = _admin_data.Users.Where(u=>u.Id==user_id); - if(users!=null && users.Count()==1){ + if (_admin_data != null) + { + var users = _admin_data.Users.Where(predicate); + if (users != null && users.Count() == 1) + { var user = users.ElementAt(0); - if(user.HasUserData && user.UserData.HasAccountPermissions){ - foreach (var kv in user.UserData.AccountPermissions.AccountPermissions){ - var account_id=kv.Key; - var accounts=_admin_data.Accounts.Where(a=>a.Id==account_id); - if(accounts!=null&&accounts.Count()==1){ - var account=accounts.ElementAt(0); - var tp=kv.Value.TradingPermission; - var rv_val=new TradingPermissionUnitImmutable(tp); - rv.Add(Tuple.Create(account,rv_val)); + if (user.HasUserData && user.UserData.HasAccountPermissions) + { + foreach (var kv in user.UserData.AccountPermissions.AccountPermissions) + { + var account_id = kv.Key; + var accounts = _admin_data.Accounts.Where(a => a.Id == account_id); + if (accounts != null && accounts.Count() == 1) + { + var account = accounts.ElementAt(0); + var tp = kv.Value.TradingPermission; + var rv_val = new TradingPermissionUnitImmutable(tp); + rv.Add(Tuple.Create(account, rv_val)); } } } @@ -34,9 +50,5 @@ namespace mbts.spark { } return rv; } - public IEnumerable viewable_accounts(string user) - { - return null; - } } } \ No newline at end of file diff --git a/src/client/spark/Orders/OrdersModel.cs b/src/client/spark/Orders/OrdersModel.cs index 0b5c3ba1e..bf9b6ca6c 100644 --- a/src/client/spark/Orders/OrdersModel.cs +++ b/src/client/spark/Orders/OrdersModel.cs @@ -30,9 +30,12 @@ namespace mbts.spark { readonly TableUpdateDemuxer _security_orders_demuxer; readonly TableUpdateDemuxer _security_positions_demuxer; IDisposable _dispose; + AdminModelWrapper _admin; [ImportingConstructor] - private OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, SpreaderModel spreader, ErrorReporter error) { + private OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, SpreaderModel spreader, ErrorReporter error, + AdminModel admin_model, [Import("username")]string user_name) { + _admin = new AdminModelWrapper( _bus = bus; _factory = factory; _service = service; From 74cac5815c8b9bccf43907024a1b395face0bf8e Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 10:43:34 -0500 Subject: [PATCH 157/203] Fix a variety of small bugs after implementing GridSettings in PnL --- .../Modules/Trading/PAndLWindowFactory.cs | 17 +++++++------- .../Controls/DIYGrid/Columns/ColumnBase.cs | 12 +++++----- src/client/spark/view/Spark/PAndL/PAndLVM.cs | 22 ++++++------------- .../spark/view/Spark/PAndL/PAndLWindow.cs | 7 ++++-- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/client/spark/Modules/Trading/PAndLWindowFactory.cs b/src/client/spark/Modules/Trading/PAndLWindowFactory.cs index 35fed0a50..27eba0300 100644 --- a/src/client/spark/Modules/Trading/PAndLWindowFactory.cs +++ b/src/client/spark/Modules/Trading/PAndLWindowFactory.cs @@ -88,14 +88,14 @@ namespace bts.shell { public static GridColumnSettings symbol => new GridColumnSettings(col_id(Columns.Symbol), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol); public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security); public static GridColumnSettings security_type => new GridColumnSettings(col_id(Columns.SecurityType), DefaultColumnHeaders.SecurityType, width: DefaultColumnWidths.SecurityType); - public static GridColumnSettings start_net_position => new GridColumnSettings(col_id(Columns.StartNetPos), DefaultColumnHeaders.StartNetPos, width: DefaultColumnWidths.PnL_Generic); - public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, width: DefaultColumnWidths.PnL_Generic); - public static GridColumnSettings day_long_pnl => new GridColumnSettings(col_id(Columns.DayLong), DefaultColumnHeaders.DayLong, width: DefaultColumnWidths.PnL_Generic); - public static GridColumnSettings day_short_pnl => new GridColumnSettings(col_id(Columns.DayShort), DefaultColumnHeaders.DayShort, width: DefaultColumnWidths.PnL_Generic); - public static GridColumnSettings day_pnl => new GridColumnSettings(col_id(Columns.DayPNL), DefaultColumnHeaders.DayPnL, width: DefaultColumnWidths.PnL_Generic); - public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, width: DefaultColumnWidths.PnL_Generic); - public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, width: DefaultColumnWidths.PnL_Generic); - public static GridColumnSettings pnl_price => new GridColumnSettings(col_id(Columns.PNLPrice), DefaultColumnHeaders.PnLPrice, width: DefaultColumnWidths.PnL_Generic); + public static GridColumnSettings start_net_position => new GridColumnSettings(col_id(Columns.StartNetPos), DefaultColumnHeaders.StartNetPos, width: DefaultColumnWidths.Start_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings day_long_pnl => new GridColumnSettings(col_id(Columns.DayLong), DefaultColumnHeaders.DayLong, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings day_short_pnl => new GridColumnSettings(col_id(Columns.DayShort), DefaultColumnHeaders.DayShort, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings day_pnl => new GridColumnSettings(col_id(Columns.DayPNL), DefaultColumnHeaders.DayPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, width: DefaultColumnWidths.Total_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings pnl_price => new GridColumnSettings(col_id(Columns.PNLPrice), DefaultColumnHeaders.PnLPrice, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static IEnumerable default_sorting_criterion { get { @@ -113,6 +113,7 @@ namespace bts.shell { yield return symbol; yield return security; yield return security_type; + yield return start_net_position; yield return start_pnl; yield return day_long_pnl; yield return day_short_pnl; diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index 2cbce30ca..ad32e7821 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -16,18 +16,18 @@ namespace mbts.ui.controls { partial class DIYGrid { public static class DefaultColumnWidths { public const double - Account = 68, - Exchange = 74, + Account = 80, + Exchange = 86, FillKey = 68, OrderKey = 78, OrderType = 88, Price = 56, Security = 80, SecurityKey = 90, - SecurityType = 90, + SecurityType = 96, Side = 47, Size = 48, - Symbol = 48, + Symbol = 58, Time = 100, TimeInForce = 47, TradeKey = 76, @@ -38,7 +38,9 @@ namespace mbts.ui.controls { Description = 300, N_Legs = 68, N_Working_Orders = 123, - PnL_Generic = 90; + Start_Net_Pos = 100, + Total_Net_Pos = 100, + PnL_Generic = 80; } public static class DefaultColumnHeaders { diff --git a/src/client/spark/view/Spark/PAndL/PAndLVM.cs b/src/client/spark/view/Spark/PAndL/PAndLVM.cs index 481583c87..a5ca17a3a 100644 --- a/src/client/spark/view/Spark/PAndL/PAndLVM.cs +++ b/src/client/spark/view/Spark/PAndL/PAndLVM.cs @@ -65,30 +65,19 @@ namespace mbts.spark { pnl_price_filter.load_state(fs.pnl_price); security_type_filter.load_state(fs.security_types); } - // restore sort state or supply default value - if (startup_data?.sorting_criterion?.Any() == true) { - pnl_view.SetSortingCriteria(startup_data.sorting_criterion); - } else { - pnl_view.SetSortingCriteria(new List() { - new SortingCriteria(nameof(PnLSummaryRow.account), true) - , new SortingCriteria(nameof(PnLSummaryRow.exchange), true) - , new SortingCriteria(nameof(PnLSummaryRow.symbol), true) - , new SortingCriteria(nameof(PnLSummaryRow.security_text), true) - }); - } pnl_view.CollectionChanged += Pnl_view_CollectionChanged; add_col(Columns.Account, DefaultColumnHeaders.Account, r => r.account, nameof(PnLSummaryRow.account), DefaultColumnWidths.Account, account_filter, DataGridFilterKnownTemplates.Regex); - add_col(Columns.Exchange, DefaultColumnHeaders.Exchange, r => r.exchange_string, nameof(PnLSummaryRow.exchange_string), DefaultColumnWidths.Exchange, exchange_string_filter, DataGridFilterKnownTemplates.Regex); + add_col(Columns.Exchange, DefaultColumnHeaders.Exchange, r => r.exchange_string, nameof(PnLSummaryRow.exchange), DefaultColumnWidths.Exchange, exchange_string_filter, DataGridFilterKnownTemplates.Regex); add_col(Columns.Symbol, DefaultColumnHeaders.Symbol, r => r.symbol, nameof(PnLSummaryRow.symbol), DefaultColumnWidths.Symbol, symbol_filter, DataGridFilterKnownTemplates.Regex); add_col(Columns.Security, DefaultColumnHeaders.Security, r => r.security_text, nameof(PnLSummaryRow.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex); add_col(Columns.SecurityType, DefaultColumnHeaders.SecurityType, r => r.security_type.ToString(), nameof(PnLSummaryRow.security_type), DefaultColumnWidths.SecurityType, security_type_filter, DataGridFilterKnownTemplates.DiscreteValues); - add_col(Columns.StartNetPos, DefaultColumnHeaders.StartNetPos, r => r.start_net_position.ToString(), nameof(PnLSummaryRow.start_net_position), DefaultColumnWidths.PnL_Generic, start_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); + add_col(Columns.StartNetPos, DefaultColumnHeaders.StartNetPos, r => r.start_net_position.ToString(), nameof(PnLSummaryRow.start_net_position), DefaultColumnWidths.Start_Net_Pos, start_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); add_col(Columns.StartPNL, DefaultColumnHeaders.StartPnL, r => r.start_pnl_text, nameof(PnLSummaryRow.start_pnl), DefaultColumnWidths.PnL_Generic, start_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.start_pnl_background); add_col(Columns.DayLong, DefaultColumnHeaders.DayLong, r => r.day_long_quantity.ToString(), nameof(PnLSummaryRow.day_long_quantity), DefaultColumnWidths.PnL_Generic, day_long_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right); add_col(Columns.DayShort, DefaultColumnHeaders.DayShort, r => r.day_short_quantity.ToString(), nameof(PnLSummaryRow.day_short_quantity), DefaultColumnWidths.PnL_Generic, day_short_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right); add_col(Columns.DayPNL, DefaultColumnHeaders.DayPnL, r => r.day_pnl_text, nameof(PnLSummaryRow.day_pnl), 90, day_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.day_pnl_background); - add_col(Columns.TotalNetPos, DefaultColumnHeaders.TotalNetPos, r => r.total_net_position.ToString(), nameof(PnLSummaryRow.total_net_position), DefaultColumnWidths.PnL_Generic, total_net_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); + add_col(Columns.TotalNetPos, DefaultColumnHeaders.TotalNetPos, r => r.total_net_position.ToString(), nameof(PnLSummaryRow.total_net_position), DefaultColumnWidths.Total_Net_Pos, total_net_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); add_col(Columns.TotalPNL, DefaultColumnHeaders.TotalPnL, r => r.total_pnl_text, nameof(PnLSummaryRow.total_pnl), DefaultColumnWidths.PnL_Generic, total_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.total_pnl_background); add_col(Columns.PNLPrice, DefaultColumnHeaders.PnLPrice, r => r.pnl_price_text, nameof(PnLSummaryRow.pnl_price), DefaultColumnWidths.PnL_Generic, pnl_price_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right); void add_col(Columns key, string header, Func get_text, string sort_prop, double width, IDataGridColumnFilter filter, string filter_template, HorizontalAlignment h_align = HorizontalAlignment.Left, Func get_background = null) { @@ -103,6 +92,9 @@ namespace mbts.spark { columns.Add(col); } + // restore sort state or supply default value + pnl_view.set_sort_descriptions(startup_data.sorting_criterion, columns); + var col_settings_flags = new Dictionary { { Columns.StartPNL.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }, { Columns.DayPNL.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }, @@ -272,7 +264,7 @@ namespace mbts.spark { #endregion - public List get_sorting_states() => pnl_view.GetSortingCriteria(); + public List get_sorting_states() => pnl_view.get_sorting_criteria(columns); #endregion diff --git a/src/client/spark/view/Spark/PAndL/PAndLWindow.cs b/src/client/spark/view/Spark/PAndL/PAndLWindow.cs index 1665478b6..63198e290 100644 --- a/src/client/spark/view/Spark/PAndL/PAndLWindow.cs +++ b/src/client/spark/view/Spark/PAndL/PAndLWindow.cs @@ -44,7 +44,10 @@ namespace mbts.spark { public override UIElement Content => _view; public override IEnumerable Commands => _commands; - public override double MinWidth => 894; + public override double MinWidth => + PAndLWindowFactory.PAndLStartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width) + + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; + public string thread_id => PNL_THREAD_ID; public void thread_init() { @@ -55,7 +58,7 @@ namespace mbts.spark { SparkCommands.window_settings_command(new DialogCommand(() => { var vm = new WindowSettingsDialogVM( _settings_manager, - us => us.market_view_grid, + us => us.pnl_grid, UserSettings.FileExtensions.GRID, s => _vm.grid_settings_manager.create_vm( // we need to get the most up to date GridSettings when launching b/c users may From 9e178cf4a101a5de4b83b5183c806086f5b745b3 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 11:04:50 -0500 Subject: [PATCH 158/203] Remove GetSortingCriteria ext method that is no longer used --- src/client/spark/Modules/Trading/Fills/FillsWindow.cs | 4 ++-- src/client/spark/view/Extensions/WPFExtensions.cs | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs index a16e4fb53..faffbf6a0 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs @@ -73,8 +73,8 @@ namespace mbts.spark { }; _subscriptions = Disposable.Create(() => _state_changed?.Invoke( - _state = new FillsWindowFactory.StartupData(_state.orders, - _vm.rows_view.GetSortingCriteria(), + _state = new StartupData(_state.orders, + _vm.rows_view.get_sorting_criteria(_vm.columns), _vm.get_filters(), _vm.grid_settings_manager.create() ) diff --git a/src/client/spark/view/Extensions/WPFExtensions.cs b/src/client/spark/view/Extensions/WPFExtensions.cs index 0d49fce12..127405dc1 100644 --- a/src/client/spark/view/Extensions/WPFExtensions.cs +++ b/src/client/spark/view/Extensions/WPFExtensions.cs @@ -301,11 +301,6 @@ namespace mbts.ui { return new Size(ft.Width, ft.Height); } - // TODO: mark this method Obsolete ([John]I decided against doing it right now because this is part of a large change that still needs to be merged, - // and adding all the supress warnings to the calling code may add complications). - public static List GetSortingCriteria(this ICollectionView collection_view) => - collection_view.SortDescriptions.Select(sd => new SortingCriteria(sd)).ToList(); - public static void SetSortingCriteria(this ICollectionView collection_view, IEnumerable sorting_criteria) { collection_view.SortDescriptions.Clear(); if (sorting_criteria != null) From 42cfba5ccc59353da477c5dd484b6f002c0193f8 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 11:30:17 -0500 Subject: [PATCH 159/203] TaS spit and polish --- .../Market/TimeAndSales/TimeAndSalesWindow.cs | 13 ++++--------- .../Modules/Market/TimeAndSalesWindowFactory.cs | 4 ++-- src/client/spark/view/Spark/SparkWindow.cs | 1 + 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs index b31b9419c..37d4a4415 100644 --- a/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs +++ b/src/client/spark/Modules/Market/TimeAndSales/TimeAndSalesWindow.cs @@ -44,16 +44,11 @@ namespace mbts.spark { public override string window_type => _window_type; public override IEnumerable Commands => _commands; public override UIElement Content => _view; - public override double MinWidth => DefaultColumnWidths.Security - + DefaultColumnWidths.Time - + DefaultColumnWidths.Price - + DefaultColumnWidths.Size - + DefaultColumnWidths.Aggressor - + DefaultColumnWidths.TradeKey - + DefaultColumnWidths.SecurityKey + public override double MinWidth => StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width) + SPARK_MENU_COLLAPSED_WIDTH - + LAUNCHER_BUTTON_BORDER_WIDTH; - + + LAUNCHER_BUTTON_BORDER_WIDTH + + EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH; + public override StartupData SaveState => _state = _vm.value.create(); public override void Dispose() => diff --git a/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs b/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs index ac5f6bc38..16b1ba341 100644 --- a/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs +++ b/src/client/spark/Modules/Market/TimeAndSalesWindowFactory.cs @@ -81,8 +81,8 @@ namespace bts.shell { public static class DefaultColumns { public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security); public static GridColumnSettings time => new GridColumnSettings(col_id(Columns.Time), DefaultColumnHeaders.Time, width: DefaultColumnWidths.Time); - public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price); - public static GridColumnSettings size => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size); + public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings size => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings aggressor => new GridColumnSettings(col_id(Columns.Aggressor), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor); public static GridColumnSettings trade_key => new GridColumnSettings(col_id(Columns.TradeKey), DefaultColumnHeaders.TradeKey, width: DefaultColumnWidths.TradeKey); public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey); diff --git a/src/client/spark/view/Spark/SparkWindow.cs b/src/client/spark/view/Spark/SparkWindow.cs index 1432a42c8..a65b7d983 100644 --- a/src/client/spark/view/Spark/SparkWindow.cs +++ b/src/client/spark/view/Spark/SparkWindow.cs @@ -15,6 +15,7 @@ namespace bts.spark { protected const double SPARK_MENU_COLLAPSED_WIDTH = 52, LAUNCHER_BUTTON_BORDER_WIDTH = 5, + EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH = 18, TITLE_PANEL_HEIGHT = 30, COLUMN_HEADER_HEIGHT = 27, SPARK_MENU_ITEM_HEIGHT = 45, From 1d5878e2f53b1c23668eb3b6c60e34474b83bd16 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 14:33:09 -0500 Subject: [PATCH 160/203] HyperlinkCell mouseover behavior now works for all HorizontalAlignments --- .../DIYGrid/CellRender/HyperlinkCell.cs | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/client/spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs b/src/client/spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs index 887a379cf..7822b83f3 100644 --- a/src/client/spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs +++ b/src/client/spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs @@ -18,6 +18,7 @@ namespace mbts.ui.controls { HorizontalAlignment _horizontal_alignment; Brush _background, _foreground; double _glyph_width, _font_size; + Size _cell_size; string _text; GlyphRun _glyph_run; GlyphContext _glyph_context; @@ -264,6 +265,7 @@ namespace mbts.ui.controls { } void _set_glyph(Size size) { + _cell_size = size; _glyph_run = _glyph_context.get_glyph_run(_text, _font_size, size, _horizontal_alignment, VerticalAlignment.Center, _padding); _glyph_width = _glyph_run == null ? 0 : _glyph_run.AdvanceWidths.Sum(); } @@ -279,8 +281,21 @@ namespace mbts.ui.controls { drawingContext.DrawGlyphRun(_foreground, _glyph_run); if (_is_mouse_over_text) { // draw underline - var pt = new Point(Padding.Left, 0.5 * (ActualHeight + _font_size) + 1); + Point pt; + switch (_horizontal_alignment) { + case HorizontalAlignment.Left: + pt = new Point(Padding.Left, 0.5 * (ActualHeight + _font_size) + 1); + break; + case HorizontalAlignment.Right: + pt = new Point(ActualWidth - Padding.Right - _glyph_width, 0.5 * (ActualHeight + _font_size) + 1); + break; + case HorizontalAlignment.Center: + default: + pt = new Point(0.5 * (ActualWidth - _glyph_width), 0.5 * (ActualHeight + _font_size) + 1); + break; + } drawingContext.DrawLine(_underline_pen, pt, new Point(pt.X + _glyph_width, pt.Y)); + } drawingContext.Pop(); } @@ -289,14 +304,25 @@ namespace mbts.ui.controls { drawingContext.DrawRectangle(bgo, null, rect); } + bool _point_horizontally_over_text(double x) { + switch (_horizontal_alignment) { + case HorizontalAlignment.Left: + return x >= _padding.Left && x <= _padding.Left + _glyph_width; + case HorizontalAlignment.Right: + return x >= ActualWidth - Padding.Right - _glyph_width && x <= ActualWidth - _padding.Right; + case HorizontalAlignment.Center: + default: + return x >= 0.5 * (ActualWidth - _glyph_width) && x <= 0.5 * (ActualWidth - _glyph_width) + _glyph_width; + } + } + void _check_mouse_over_text(MouseEventArgs e) { var pt = e.GetPosition(this); bool over_text = - _glyph_run != null && - pt.X >= _padding.Left && - pt.X <= _padding.Left + _glyph_width && - pt.Y >= _padding.Top && - pt.Y <= _padding.Top + _font_size; + _glyph_run != null && + _point_horizontally_over_text(pt.X) && + pt.Y >= _padding.Top && + pt.Y <= _padding.Top + _font_size; if (_is_mouse_over_text != over_text) _toggle_mouse_over_text(); } From f17b2cb06d9517f8a4e4522d12f1764702bc2d95 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 14:34:19 -0500 Subject: [PATCH 161/203] HyperlinkColumn now responds to changes in HorizatonalAlignment --- .../DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs index 726764f99..7c19f3e35 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/FrameworkElementColumns/HyperlinkColumn.cs @@ -33,6 +33,7 @@ namespace mbts.ui.controls { hl.SetBinding(HyperlinkCell.TextProperty, _binding); hl.SetBinding(HyperlinkCell.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this }); hl.SetBinding(HyperlinkCell.FontSizeProperty, new Binding(nameof(font_size)) { Source = this }); + hl.SetBinding(HyperlinkCell.HorizontalAlignmentProperty, new Binding(nameof(horizontal_alignment)) { Source = this }); hl.SetBinding(HyperlinkCell.CommandProperty, _command_binding); if (background_overlay != null) { hl.SetBinding(BackgroundOverlayProperty, new Binding(nameof(ListBoxItem.IsSelected)) { From bc362b977827012c34718d2742574f16cd0d0b7a Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 14:35:53 -0500 Subject: [PATCH 162/203] HyperlinkCell - removed a size i never ended up using --- .../spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs b/src/client/spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs index 7822b83f3..6a8041593 100644 --- a/src/client/spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs +++ b/src/client/spark/view/Controls/DIYGrid/CellRender/HyperlinkCell.cs @@ -18,7 +18,6 @@ namespace mbts.ui.controls { HorizontalAlignment _horizontal_alignment; Brush _background, _foreground; double _glyph_width, _font_size; - Size _cell_size; string _text; GlyphRun _glyph_run; GlyphContext _glyph_context; @@ -265,7 +264,6 @@ namespace mbts.ui.controls { } void _set_glyph(Size size) { - _cell_size = size; _glyph_run = _glyph_context.get_glyph_run(_text, _font_size, size, _horizontal_alignment, VerticalAlignment.Center, _padding); _glyph_width = _glyph_run == null ? 0 : _glyph_run.AdvanceWidths.Sum(); } From 344f82dc8f4936896d0aa6ae41e58ecf6c2fc12c Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 15:16:30 -0500 Subject: [PATCH 163/203] MView spit and polish --- .../Modules/Market/MarketView/MarketViewVM.cs | 54 ++++++++++--------- .../Market/MarketView/MarketViewWindow.cs | 6 ++- .../MarketView/MarketViewWindowFactory.cs | 32 +++++------ .../Controls/DIYGrid/Columns/ColumnBase.cs | 8 +-- 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs index f9c1172a8..4699d88d5 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs @@ -40,25 +40,25 @@ namespace mbts.spark { add_col(Columns.Security, "Security", r => r.security.ToString(), nameof(Row.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex); add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), DefaultColumnWidths.Volume, volume_filter, DataGridFilterKnownTemplates.ULongMinMax); add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax); - add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax); - add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true); - add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid); - add_col(Columns.BidImpliedSize, implied_size_col_header, r => r.bid_implied_size.ToString(), nameof(Row.bid_implied_size), DefaultColumnWidths.Size, bid_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid); - add_col(Columns.BidDirectSize, direct_size_col_header, r => r.bid_direct_size.ToString(), nameof(Row.bid_direct_size), DefaultColumnWidths.Size, bid_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid); - add_hl_merged_size_col(Columns.BidMergedSize, merged_size_col_header, nameof(Row.bid_merged_size), nameof(Row.bid_merged_size_text), nameof(Row.bid_merged_size_click), bid_merged_size_filter, BTSColors.MarketData.bid); - add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid_brush); - add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask_brush); - add_hl_merged_size_col(Columns.AskMergedSize, merged_size_col_header, nameof(Row.ask_merged_size), nameof(Row.ask_merged_size_text), nameof(Row.ask_merged_size_click), ask_merged_size_filter, BTSColors.MarketData.ask); - add_col(Columns.AskDirectSize, direct_size_col_header, r => r.ask_direct_size.ToString(), nameof(Row.ask_direct_size), DefaultColumnWidths.Size, ask_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask); - add_col(Columns.AskImpliedSize, implied_size_col_header, r => r.ask_implied_size.ToString(), nameof(Row.ask_implied_size), DefaultColumnWidths.Size, ask_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask); - add_hl_working_orders_col(Columns.WorkingAsks, working_col_header, nameof(Row.working_asks_text), nameof(Row.working_best_ask), nameof(Row.working_asks_click), working_ask_volume_filter, BTSColors.MarketData.ask); - add_col(Columns.Settlement, "Settlement", r => r.format_price(r.settlement), nameof(Row.settlement), DefaultColumnWidths.Price, settlement_filter, DataGridFilterKnownTemplates.DoubleMinMax); - add_col(Columns.DayChange, "Change", r => r.format_price(r.day_change), nameof(Row.day_change), DefaultColumnWidths.Price, day_change_filter, DataGridFilterKnownTemplates.DoubleMinMax, true); - add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax); - add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax); + add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax, h_alignment: System.Windows.HorizontalAlignment.Right); + add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right); + add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid, System.Windows.HorizontalAlignment.Right); + add_col(Columns.BidImpliedSize, implied_size_col_header, r => r.bid_implied_size.ToString(), nameof(Row.bid_implied_size), DefaultColumnWidths.Size, bid_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right); + add_col(Columns.BidDirectSize, direct_size_col_header, r => r.bid_direct_size.ToString(), nameof(Row.bid_direct_size), DefaultColumnWidths.Size, bid_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right); + add_hl_merged_size_col(Columns.BidMergedSize, merged_size_col_header, nameof(Row.bid_merged_size), nameof(Row.bid_merged_size_text), nameof(Row.bid_merged_size_click), bid_merged_size_filter, BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right); + add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid_brush, h_alignment: System.Windows.HorizontalAlignment.Right); + add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask_brush, h_alignment: System.Windows.HorizontalAlignment.Right); + add_hl_merged_size_col(Columns.AskMergedSize, merged_size_col_header, nameof(Row.ask_merged_size), nameof(Row.ask_merged_size_text), nameof(Row.ask_merged_size_click), ask_merged_size_filter, BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right); + add_col(Columns.AskDirectSize, direct_size_col_header, r => r.ask_direct_size.ToString(), nameof(Row.ask_direct_size), DefaultColumnWidths.Size, ask_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right); + add_col(Columns.AskImpliedSize, implied_size_col_header, r => r.ask_implied_size.ToString(), nameof(Row.ask_implied_size), DefaultColumnWidths.Size, ask_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right); + add_hl_working_orders_col(Columns.WorkingAsks, working_col_header, nameof(Row.working_asks_text), nameof(Row.working_best_ask), nameof(Row.working_asks_click), working_ask_volume_filter, BTSColors.MarketData.ask, System.Windows.HorizontalAlignment.Right); + add_col(Columns.Settlement, "Settlement", r => r.format_price(r.settlement), nameof(Row.settlement), DefaultColumnWidths.Settlement, settlement_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right); + add_col(Columns.DayChange, "Change", r => r.format_price(r.day_change), nameof(Row.day_change), DefaultColumnWidths.Price, day_change_filter, DataGridFilterKnownTemplates.DoubleMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right); + add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right); + add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right); add_col(Columns.SecurityKey, "Security Key", r => r.security_key.ToString(), nameof(Row.security_key), DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex); // - void add_col(Columns col_key, string header, Func get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false, Color? font_color = null) => + void add_col(Columns col_key, string header, Func get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false, Color? font_color = null, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) => columns.Add(new RenderTextColumn(col_key, get_text, filter, filter_template) { // it's necessary to have this id populated for grid settings id = bts.shell.MarketViewWindowFactory.col_id(col_key), @@ -66,21 +66,22 @@ namespace mbts.spark { sort_member_path = prop, Width = width, padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding, - font_color_override = font_color + font_color_override = font_color, + horizontal_alignment = h_alignment }); - void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background) => - add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price, foreground, background, DefaultSelectionHighlight); + void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background, System.Windows.HorizontalAlignment h_alignment) => + add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price, foreground, background, DefaultSelectionHighlight, h_alignment); - void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground) => - add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, foreground, null, null); + void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) => + add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, foreground, null, null, h_alignment); - void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground) => - add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null); + void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) => + add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null, h_alignment); void add_hl_col( Columns col_key, string header, BindingBase binding, string prop, string command_prop, - IDataGridColumnFilter filter, string filter_template, double width, Color foreground, Brush background, Brush background_overlay) => + IDataGridColumnFilter filter, string filter_template, double width, Color foreground, Brush background, Brush background_overlay, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) => columns.Add(new HyperlinkElementColumn( col_key, binding, new Binding(command_prop), filter, filter_template) { // it's necessary to have this id populated for grid settings @@ -90,7 +91,8 @@ namespace mbts.spark { font_color_override = foreground, background = background, background_overlay = background_overlay, - Width = DefaultColumnWidths.Price + Width = DefaultColumnWidths.Price, + horizontal_alignment = h_alignment }); BindingBase price_binding(string prop) { diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs index e596e1fba..d93ae0dbf 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindow.cs @@ -42,7 +42,11 @@ namespace mbts.spark { public override MarketViewWindowFactory.StartupData SaveState => _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.get_grid_save_state(), _vm.rows_view.get_sorting_criteria(_vm.columns)); - public override double MinWidth => 1227 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; + public override double MinWidth => MarketViewWindowFactory.StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width) + + SPARK_MENU_COLLAPSED_WIDTH + + LAUNCHER_BUTTON_BORDER_WIDTH + + EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH; + public string thread_id => _thread_id; public void thread_init() { diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs index 6d8aee17c..b0970aaa6 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs @@ -80,22 +80,22 @@ namespace bts.shell { public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security); public static GridColumnSettings volume => new GridColumnSettings(col_id(Columns.Volume), DefaultColumnHeaders.Volume, width: DefaultColumnWidths.Volume); public static GridColumnSettings last => new GridColumnSettings(col_id(Columns.Last), DefaultColumnHeaders.Last, width: DefaultColumnWidths.Price); - public static GridColumnSettings last_size => new GridColumnSettings(col_id(Columns.LastSize), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size); - public static GridColumnSettings position => new GridColumnSettings(col_id(Columns.Position), DefaultColumnHeaders.Pos, width: DefaultColumnWidths.Size); - public static GridColumnSettings working_bids => new GridColumnSettings(col_id(Columns.WorkingBids), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); - public static GridColumnSettings bid_implied_size => new GridColumnSettings(col_id(Columns.BidImpliedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); - public static GridColumnSettings bid_direct_size => new GridColumnSettings(col_id(Columns.BidDirectSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); - public static GridColumnSettings bid_merged_size => new GridColumnSettings(col_id(Columns.BidMergedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size); - public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.Price); - public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.Price); - public static GridColumnSettings ask_merged_size => new GridColumnSettings(col_id(Columns.AskMergedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size); - public static GridColumnSettings ask_direct_size => new GridColumnSettings(col_id(Columns.AskDirectSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size); - public static GridColumnSettings ask_implied_size => new GridColumnSettings(col_id(Columns.AskImpliedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size); - public static GridColumnSettings working_asks => new GridColumnSettings(col_id(Columns.WorkingAsks), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size); - public static GridColumnSettings settlement => new GridColumnSettings(col_id(Columns.Settlement), DefaultColumnHeaders.Settlement, width: DefaultColumnWidths.Price); - public static GridColumnSettings day_change => new GridColumnSettings(col_id(Columns.DayChange), DefaultColumnHeaders.DayChange, width: DefaultColumnWidths.Price); - public static GridColumnSettings session_low => new GridColumnSettings(col_id(Columns.SessionLow), DefaultColumnHeaders.SessionLow, width: DefaultColumnWidths.Price); - public static GridColumnSettings session_high => new GridColumnSettings(col_id(Columns.SessionHigh), DefaultColumnHeaders.SessionHigh, width: DefaultColumnWidths.Price); + public static GridColumnSettings last_size => new GridColumnSettings(col_id(Columns.LastSize), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings position => new GridColumnSettings(col_id(Columns.Position), DefaultColumnHeaders.Pos, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings working_bids => new GridColumnSettings(col_id(Columns.WorkingBids), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Working, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings bid_implied_size => new GridColumnSettings(col_id(Columns.BidImpliedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings bid_direct_size => new GridColumnSettings(col_id(Columns.BidDirectSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings bid_merged_size => new GridColumnSettings(col_id(Columns.BidMergedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings ask_merged_size => new GridColumnSettings(col_id(Columns.AskMergedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings ask_direct_size => new GridColumnSettings(col_id(Columns.AskDirectSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings ask_implied_size => new GridColumnSettings(col_id(Columns.AskImpliedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings working_asks => new GridColumnSettings(col_id(Columns.WorkingAsks), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Working, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings settlement => new GridColumnSettings(col_id(Columns.Settlement), DefaultColumnHeaders.Settlement, width: DefaultColumnWidths.Settlement, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings day_change => new GridColumnSettings(col_id(Columns.DayChange), DefaultColumnHeaders.DayChange, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings session_low => new GridColumnSettings(col_id(Columns.SessionLow), DefaultColumnHeaders.SessionLow, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings session_high => new GridColumnSettings(col_id(Columns.SessionHigh), DefaultColumnHeaders.SessionHigh, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey); diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index ad32e7821..30c0931d0 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -21,7 +21,8 @@ namespace mbts.ui.controls { FillKey = 68, OrderKey = 78, OrderType = 88, - Price = 56, + Price = 68, + Settlement = 82, Security = 80, SecurityKey = 90, SecurityType = 96, @@ -33,14 +34,15 @@ namespace mbts.ui.controls { TradeKey = 76, User = 53, Aggressor = 80, - Volume = 64, + Volume = 74, FillKind = 70, Description = 300, N_Legs = 68, N_Working_Orders = 123, Start_Net_Pos = 100, Total_Net_Pos = 100, - PnL_Generic = 80; + PnL_Generic = 80, + Working = 72; } public static class DefaultColumnHeaders { From ef7782604289253d53d11175076cd77f02cbb9e1 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 15:24:20 -0500 Subject: [PATCH 164/203] MView - shrinking default bid/ask cols from the enlargement I just made --- src/client/spark/Modules/Market/MarketView/MarketViewVM.cs | 2 +- .../Modules/Market/MarketView/MarketViewWindowFactory.cs | 4 ++-- src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs index 4699d88d5..5f536d8c4 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewVM.cs @@ -71,7 +71,7 @@ namespace mbts.spark { }); void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background, System.Windows.HorizontalAlignment h_alignment) => - add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price, foreground, background, DefaultSelectionHighlight, h_alignment); + add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.MViewPrice, foreground, background, DefaultSelectionHighlight, h_alignment); void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) => add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, foreground, null, null, h_alignment); diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs index b0970aaa6..52942ed40 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewWindowFactory.cs @@ -86,8 +86,8 @@ namespace bts.shell { public static GridColumnSettings bid_implied_size => new GridColumnSettings(col_id(Columns.BidImpliedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings bid_direct_size => new GridColumnSettings(col_id(Columns.BidDirectSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings bid_merged_size => new GridColumnSettings(col_id(Columns.BidMergedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); - public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); - public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right); + public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings ask_merged_size => new GridColumnSettings(col_id(Columns.AskMergedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings ask_direct_size => new GridColumnSettings(col_id(Columns.AskDirectSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); public static GridColumnSettings ask_implied_size => new GridColumnSettings(col_id(Columns.AskImpliedSize), DefaultColumnHeaders.Size, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right); diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index 30c0931d0..b7928ab91 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -21,6 +21,7 @@ namespace mbts.ui.controls { FillKey = 68, OrderKey = 78, OrderType = 88, + MViewPrice = 56, Price = 68, Settlement = 82, Security = 80, From 46878f009af8f80ea636cb9efca8ef2544c0079b Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Mon, 8 Oct 2018 15:41:31 -0500 Subject: [PATCH 165/203] Updating MinWidth so that remaining windows will all open without a horizontal scrollbar --- .../spark/Modules/Spreader/SyntheticManagementWindow.cs | 6 +++++- src/client/spark/Modules/Trading/Fills/FillsWindow.cs | 7 ++++++- src/client/spark/view/Spark/PAndL/PAndLWindow.cs | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs b/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs index 6a08c6f42..d7efda3eb 100644 --- a/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs +++ b/src/client/spark/Modules/Spreader/SyntheticManagementWindow.cs @@ -105,7 +105,11 @@ namespace mbts.spark { public override UIElement Content => _view; public override IEnumerable Commands => _commands; public override StartupData SaveState => _state = _vm.value.create(); - public override double MinWidth => 700; + public override double MinWidth => + StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width) + + SPARK_MENU_COLLAPSED_WIDTH + + LAUNCHER_BUTTON_BORDER_WIDTH + + EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH; public override void Dispose() { Disposable.dispose(ref _vm); diff --git a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs index faffbf6a0..cb4e56609 100644 --- a/src/client/spark/Modules/Trading/Fills/FillsWindow.cs +++ b/src/client/spark/Modules/Trading/Fills/FillsWindow.cs @@ -41,7 +41,12 @@ namespace mbts.spark { public override IEnumerable Commands => _commands; public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.get_sorting_criteria(_vm.columns), _vm.get_filters(), _vm.get_grid_save_state()); - public override double MinWidth => 1050 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; + public override double MinWidth => + StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width) + + SPARK_MENU_COLLAPSED_WIDTH + + LAUNCHER_BUTTON_BORDER_WIDTH + + EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH; + public string thread_id => _thread_id; public void thread_init() { diff --git a/src/client/spark/view/Spark/PAndL/PAndLWindow.cs b/src/client/spark/view/Spark/PAndL/PAndLWindow.cs index 63198e290..ec1b37d34 100644 --- a/src/client/spark/view/Spark/PAndL/PAndLWindow.cs +++ b/src/client/spark/view/Spark/PAndL/PAndLWindow.cs @@ -46,7 +46,9 @@ namespace mbts.spark { public override double MinWidth => PAndLWindowFactory.PAndLStartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width) - + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH; + + SPARK_MENU_COLLAPSED_WIDTH + + LAUNCHER_BUTTON_BORDER_WIDTH + + EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH; public string thread_id => PNL_THREAD_ID; From 9630851f1ff861206fa957df76d1a73d183e3012 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 9 Oct 2018 09:52:42 -0400 Subject: [PATCH 166/203] Add ToString to managed PriceSize --- src/client/spark_core/Co2/PriceSize.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/client/spark_core/Co2/PriceSize.cs b/src/client/spark_core/Co2/PriceSize.cs index 040bdbd6f..3b91b089c 100644 --- a/src/client/spark_core/Co2/PriceSize.cs +++ b/src/client/spark_core/Co2/PriceSize.cs @@ -26,6 +26,9 @@ namespace mbts.co2 { } internal static NativePriceLevel empty => new NativePriceLevel(IntPrice.NaN, 0, IntPrice.NaN, 0); + public override string ToString() { + return $"b [{bid.price} {bid.size}] a [{ask.price} {ask.size}]"; + } } public readonly struct ImpliedPriceLevel { readonly IntPrice _bid; @@ -85,7 +88,7 @@ namespace mbts.co2 { public override string ToString() { - return "{0} {1}".fmt(price, size); + return "{0} {1} ({2})".fmt(price, size, implied_size); } public override bool Equals(object obj) { From 1e64f930d98777b4451f730512a42af8be1b3715 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 9 Oct 2018 09:52:52 -0400 Subject: [PATCH 167/203] Pass though impileds and settles in simulator --- .../sparkd/exchange_sim/ExchangeSim.cpp | 31 ++++++++++++++++++- .../exchange_sim/ExchangeSimApplication.cpp | 25 +++++++++++++-- .../exchange_sim/ExchangeSimApplication.h | 5 ++- .../sparkd/exchange_sim/IMDPlayerObserver.h | 5 ++- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/server/sparkd/exchange_sim/ExchangeSim.cpp b/src/server/sparkd/exchange_sim/ExchangeSim.cpp index 7eb6662fd..1e1894157 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSim.cpp +++ b/src/server/sparkd/exchange_sim/ExchangeSim.cpp @@ -75,7 +75,36 @@ struct MDPlayer : md::IRelayWriter size_t n = 0; for (auto &a : book.asks()) asks[n++] = md::PriceSize({a.mantissa(),(int8_t)a.exponent()},a.size()); - obs.on_depth(*sec_ptr, bids, asks); + + + std::array implied_bids, implied_asks; + bool has_implied = false; + {size_t x = 0; + for (const auto& b : cr.implied().bids()) { + implied_bids[x] = md::PriceSize({b.mantissa(), (int8_t)b.exponent()}, b.size()); + x += 1; + has_implied = true; + }} + {size_t x = 0; + for (const auto& b : cr.implied().asks()) { + implied_bids[x] = md::PriceSize({b.mantissa(), (int8_t)b.exponent()}, b.size()); + x += 1; + has_implied = true; + }} + + if (has_implied) { + obs.on_depth(*sec_ptr, bids, asks, &implied_bids, &implied_asks); + } + else { + obs.on_depth(*sec_ptr, bids, asks, nullptr, nullptr); + } + + if (cr.has_misc()) { + if (cr.misc().has_settlement()) { + const auto& s = cr.misc().settlement(); + obs.on_settlement(*sec_ptr, { s.mantissa(), (int8_t)s.exponent() }); + } + } } } IMDPlayerObserver &obs; diff --git a/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp b/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp index 9ea11d065..3fd537de6 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp +++ b/src/server/sparkd/exchange_sim/ExchangeSimApplication.cpp @@ -527,9 +527,21 @@ void ExchangeSimApplication::on_new_security(api::Security const &s) { } } +void ExchangeSimApplication::on_settlement(md::Security const& sec, md::IntPrice price) { + auto sec_desc = get_sim_sec_desc(sec); + auto iter = me->md_producer_handles.find(sec_desc); + if (iter != me->md_producer_handles.end()) { + auto &handle = iter->second; + handle.update_settlement(utils::TimeStamp::now(), price, datetime::today()); + } +} + void ExchangeSimApplication::on_depth( - md::Security const &sec, md::PriceSize bids[md::COCONUT_MAX_DEPTH], - md::PriceSize asks[md::COCONUT_MAX_DEPTH]) { + md::Security const &sec, + md::PriceSize bids[md::COCONUT_MAX_DEPTH], + md::PriceSize asks[md::COCONUT_MAX_DEPTH], + std::array* implied_bids, + std::array* implied_asks) { std::lock_guard l(me->lock); if (sec.has_synthetic()) @@ -564,6 +576,15 @@ void ExchangeSimApplication::on_depth( // we generate a set of orders that we apply to our order book merge_with_order_book(it->second.security.glbx().security_desc(), previous_md_book, current_md_book); + + // pass on implied market unchanged + if (implied_bids || implied_asks) { + auto iter = me->md_producer_handles.find(get_sim_sec_desc(sec)); + if (iter != me->md_producer_handles.end()) { + auto &handle = iter->second; + handle.update_implied(utils::TimeStamp::now(), *implied_bids, *implied_asks); + } + } } void ExchangeSimApplication::merge_with_order_book(std::string const &sec_desc, diff --git a/src/server/sparkd/exchange_sim/ExchangeSimApplication.h b/src/server/sparkd/exchange_sim/ExchangeSimApplication.h index 8f7cdc54e..248017519 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSimApplication.h +++ b/src/server/sparkd/exchange_sim/ExchangeSimApplication.h @@ -99,9 +99,12 @@ public: void persist_book(OrderBook const &b); void on_new_security(md::Security const&) override; + void on_settlement(md::Security const&, md::IntPrice) override; void on_depth(md::Security const&s, md::PriceSize bids[md::COCONUT_MAX_DEPTH], - md::PriceSize asks[md::COCONUT_MAX_DEPTH]) override; + md::PriceSize asks[md::COCONUT_MAX_DEPTH], + std::array* implied_bids, + std::array* implied_asks) override; void merge_with_order_book(std::string const &sec_desc, Book const&previous,Book const¤t); }; }} diff --git a/src/server/sparkd/exchange_sim/IMDPlayerObserver.h b/src/server/sparkd/exchange_sim/IMDPlayerObserver.h index 6ba748133..b2e194b34 100644 --- a/src/server/sparkd/exchange_sim/IMDPlayerObserver.h +++ b/src/server/sparkd/exchange_sim/IMDPlayerObserver.h @@ -9,9 +9,12 @@ namespace sim { struct IMDPlayerObserver { virtual void on_new_security(md::Security const&) = 0; + virtual void on_settlement(const md::Security&, md::IntPrice) = 0; virtual void on_depth(md::Security const&s, md::PriceSize bids[md::COCONUT_MAX_DEPTH], - md::PriceSize asks[md::COCONUT_MAX_DEPTH]) = 0; + md::PriceSize asks[md::COCONUT_MAX_DEPTH], + std::array* implied_bids, + std::array* implied_asks) = 0; }; }} From 1b5256d6993a8949b48480a5e2d0512afc02f490 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Mon, 1 Oct 2018 14:56:35 -0400 Subject: [PATCH 168/203] SPARK-1742 test can fail when time moves foward --- src/client/spark_test/Co2/Co2Tests.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/client/spark_test/Co2/Co2Tests.cs b/src/client/spark_test/Co2/Co2Tests.cs index 98767cbda..a1e17e81f 100644 --- a/src/client/spark_test/Co2/Co2Tests.cs +++ b/src/client/spark_test/Co2/Co2Tests.cs @@ -270,10 +270,21 @@ namespace mbts.co2.tests public void lldatetime_test() { utils.LLDateTime now = DateTime.MinValue, utc_now = DateTime.MinValue; Co2NativeTestMethods.co2_get_sample_datetime(ref now, ref utc_now); - Assert.AreEqual(((DateTime)now).ToShortDateString(), DateTime.Now.ToShortDateString()); - Assert.AreEqual(((DateTime)utc_now).ToShortDateString(), DateTime.UtcNow.ToShortDateString()); - Assert.AreEqual(((DateTime)now).ToString("T"), DateTime.Now.ToString("T")); - Assert.AreEqual(((DateTime)utc_now).ToString("T"), DateTime.UtcNow.ToString("T")); + DateTime dt_now = now; + DateTime dt_utc_now = utc_now; + Assert.AreEqual(dt_now.ToShortDateString(), DateTime.Now.ToShortDateString()); + Assert.AreEqual(dt_utc_now.ToShortDateString(), DateTime.UtcNow.ToShortDateString()); + void assert_about_equal(int a, int b) { + Assert.That(Math.Abs(a - b) <= 1); + } + + assert_about_equal(dt_now.Hour, DateTime.Now.Hour); + assert_about_equal(dt_now.Minute, DateTime.Now.Minute); + assert_about_equal(dt_now.Second, DateTime.Now.Second); + + assert_about_equal(dt_utc_now.Hour, DateTime.UtcNow.Hour); + assert_about_equal(dt_utc_now.Minute, DateTime.UtcNow.Minute); + assert_about_equal(dt_utc_now.Second, DateTime.UtcNow.Second); } From 58481c9b1752cc71e32ef0a95b7e2a5a217b2091 Mon Sep 17 00:00:00 2001 From: Michael Harding Date: Thu, 4 Oct 2018 15:00:33 -0400 Subject: [PATCH 169/203] SPARK-1538: Clones incoming --- src/web/ui/src/Users/State.fs | 4 ++-- src/web/ui/src/Users/Types.fs | 2 +- src/web/ui/src/Users/View.fs | 31 +++++++++---------------------- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/web/ui/src/Users/State.fs b/src/web/ui/src/Users/State.fs index 40db505a7..f2d8dd611 100644 --- a/src/web/ui/src/Users/State.fs +++ b/src/web/ui/src/Users/State.fs @@ -227,8 +227,8 @@ let update msg (model:Users.Types.Model) : Model * Cmd = model, Cmd.Empty | UpdateField field -> { model with scratchUser = applyUpdate field model.scratchUser; ConfirmModal = ("", Msg.SwitchTab model.currentTab) } , Cmd.Empty - | Clone usr -> - { model with scratchUser = { usr with id = 0u; name = usr.name + "_copy" }}, Cmd.Empty + | CloneUser -> + { model with PreviousScratchUser = model.scratchUser; scratchUser = { model.scratchUser with id = 0u; name = sprintf "%s_clone" model.scratchUser.name }; OptionsMenuOpen = ""}, Cmd.ofMsg (SwitchTab General) | CopyFrom (usrSrc, usrDest) -> let nusr = { usrSrc with id = usrDest.id diff --git a/src/web/ui/src/Users/Types.fs b/src/web/ui/src/Users/Types.fs index 985232714..a4f71feca 100644 --- a/src/web/ui/src/Users/Types.fs +++ b/src/web/ui/src/Users/Types.fs @@ -36,7 +36,7 @@ type Msg = | RpcResponse of string | UpdateAdminData of AdminData | UpdateField of UserField - | Clone of BTSUser + | CloneUser | CopyFrom of BTSUser * BTSUser | Delete of BTSUser | AddUser diff --git a/src/web/ui/src/Users/View.fs b/src/web/ui/src/Users/View.fs index 4f3e8a72e..0ecb7f512 100644 --- a/src/web/ui/src/Users/View.fs +++ b/src/web/ui/src/Users/View.fs @@ -178,27 +178,6 @@ let userControls (model:Model) (dispatch:Msg->Unit) = [ saveButton model dispatch Button.button "Revert" "" "" (if dirty then Button.Enabled else Button.Disabled) (fun _ -> SwitchUser (currentUser) |> dispatch) - (*p [ ClassName "control" ] - [ a [ ClassName "button is-light" - Title "Clone" - OnClick (fun _ -> dispatch <| Clone currentUser) - Disabled disabled - ] - [ span [ ClassName "icon is-small" ] - [ i [ ClassName "fa fa-clone" ] - [ ] ] - ] - ]*) - (*p [ ClassName "control" ] - [ a [ ClassName "button is-light" ] - [ span [ ClassName "icon is-small" - Title "Copy from user 1 ..." - OnClick (fun _ -> dispatch <| CopyFrom (usersList.Head, currentUser)) - ] - [ i [ ClassName "fa fa-copy" ] - [ ] ] - ] - ]*) ] @@ -276,7 +255,11 @@ let userSelectionGrid (usersList:BTSUser list) (currentUser:BTSUser) (dispatch:M let menuItem (user:BTSUser) (selectedUser:BTSUser) model dispatch = a [ ClassName (isActiveUser user.id selectedUser) - OnClick (fun _ -> SwitchUser user |> dispatch) + OnClick (fun _ -> + match user.name = model.OptionsMenuOpen with + | false -> SwitchUser user |> dispatch + | _ -> () + ) ] [ span [] [ str user.name ] span [ Class "is-pulled-right menu-icon" ] @@ -305,6 +288,10 @@ let menuItem (user:BTSUser) (selectedUser:BTSUser) model dispatch = OnClick (fun _ -> dispatch <| Delete user) ] [ str "Remove" ] + a [ Class "dropdown-item" + OnClick (fun _ -> dispatch <| CloneUser) + ] + [ str "Clone" ] ] ] ] From 8cbe6d7199d32dfadd4a9bf19a260b16f674fdb7 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Tue, 9 Oct 2018 09:37:56 -0400 Subject: [PATCH 170/203] MD Aux Service's VAP now uses context.poll instead of its own thread --- .../md/MarketDataAuxiliaryServiceServer.cpp | 33 +++++++++---------- .../md/MarketDataAuxiliaryServiceServer.h | 1 + 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp index 98675ba97..17886e386 100644 --- a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp +++ b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.cpp @@ -20,6 +20,7 @@ using namespace bts::api::md::auxiliary; struct MarketDataAuxiliaryServiceServer::PImpl{ bool vap_enabled; + bts::api::ServiceContext& context; MarketDataRelay relay_service; MarketDataAuxiliaryServiceBase::Volume_at_priceStatefulPublisher publisher; std::unique_ptr trade_timer; @@ -30,6 +31,7 @@ struct MarketDataAuxiliaryServiceServer::PImpl{ const MarketDataAuxiliaryServiceBase::Volume_at_priceStatefulPublisher& publisher) : vap_enabled(config::get().enabled()), + context(context), relay_service(context.locate()), publisher(publisher), trade_timer(), @@ -46,21 +48,18 @@ MarketDataAuxiliaryServiceServer::MarketDataAuxiliaryServiceServer( MarketDataAuxiliaryServiceBase(context), me(new PImpl(context, make_volume_at_price_stateful_publisher())){ - if(me->vap_enabled){ - auto& threadpool = utils::Dispatcher::get_named("TradesQuery"); - me->trade_timer = - std::make_unique(threadpool, - me->poll_period_ms, - [this]() { - me->relay_service.requestmd(me->state.md_req(), - [this](const bts::api::Result& md_reply) - { - if(md_reply.result()!=nullptr) - _scan_trades(*md_reply.result());} - ); - }); - me->trade_timer->start(); - } + if(me->vap_enabled) + me->context.post([this](){_poll_md();},me->poll_period_ms); +} + +void MarketDataAuxiliaryServiceServer::_poll_md(){ + me->relay_service.requestmd(me->state.md_req(), + [this](const bts::api::Result& md_reply) + { + if(md_reply.result()!=nullptr) + _scan_trades(*md_reply.result()); + me->context.post([this](){_poll_md();},me->poll_period_ms); + }); } void MarketDataAuxiliaryServiceServer::_scan_trades(const MDReply& md_reply){ @@ -79,9 +78,9 @@ void MarketDataAuxiliaryServiceServer::_scan_trades(const MDReply& md_reply){ n_trades,total_unique_levels,usecs); me->log_counter=0; } - me->publisher.flush(); + me->publisher.flush(); } MarketDataAuxiliaryServiceServer::~MarketDataAuxiliaryServiceServer(){ } -}}} +}}} \ No newline at end of file diff --git a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.h b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.h index c762545eb..04bbd3b3e 100644 --- a/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.h +++ b/src/server/sparkd/md/MarketDataAuxiliaryServiceServer.h @@ -18,6 +18,7 @@ private: struct PImpl; std::unique_ptr me; private: + void _poll_md(); void _scan_trades(const bts::co2::MDReply&); public: MarketDataAuxiliaryServiceServer(bts::api::ServiceContext& context); From 21a47e2d43955964d5dcfb6ce8e75f773b47110c Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Tue, 9 Oct 2018 12:13:54 -0400 Subject: [PATCH 171/203] VAP merging into 1659 made the price go to NAN because of the PrizeSize changes --- src/server/sparkd/md/ChannelVolume.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/sparkd/md/ChannelVolume.cpp b/src/server/sparkd/md/ChannelVolume.cpp index ac047b9c5..89a6132a0 100644 --- a/src/server/sparkd/md/ChannelVolume.cpp +++ b/src/server/sparkd/md/ChannelVolume.cpp @@ -27,7 +27,7 @@ const VolumeAtPriceLevel& ChannelVolume::add_trade(const TradeMessage& m, VolumeAtPriceLevel vol; vol.set_record_id(record_id++); vol.set_security_id(m.key()); - auto p = md::PriceSize({m.mantissa(),(int8_t)m.exponent()},0); + IntPrice p = md::IntPrice (m.mantissa(),(int8_t)m.exponent()); vol.set_price(p.price()); price_map[m.mantissa()] = vol; price_it=price_map.find(m.mantissa()); From c331c8dde53decc39027be9375d12cb3fa50d553 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 9 Oct 2018 14:08:15 -0400 Subject: [PATCH 172/203] Add flag to enable/disable implied forwarding in simulator --- src/lib/config/protos/ExchangeSimCfg.proto | 1 + src/server/sparkd/exchange_sim/ExchangeSim.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/config/protos/ExchangeSimCfg.proto b/src/lib/config/protos/ExchangeSimCfg.proto index 790ef5779..4702536b5 100644 --- a/src/lib/config/protos/ExchangeSimCfg.proto +++ b/src/lib/config/protos/ExchangeSimCfg.proto @@ -3,5 +3,6 @@ package bts.config; message ExchangeSim { optional bool merge_with_md = 1 [default=true]; + optional bool forward_implied = 2 [default = false]; } diff --git a/src/server/sparkd/exchange_sim/ExchangeSim.cpp b/src/server/sparkd/exchange_sim/ExchangeSim.cpp index 1e1894157..549d0cd3a 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSim.cpp +++ b/src/server/sparkd/exchange_sim/ExchangeSim.cpp @@ -11,6 +11,7 @@ #include "lib/os/os.h" #include "lib/config/protos/Ports.pb.h" +#include "lib/config/protos/ExchangeSimCfg.pb.h" #include "lib/config/config.h" #include @@ -46,7 +47,8 @@ struct MDPlayer : md::IRelayWriter { MDPlayer (IMDPlayerObserver &obs,std::map &known_securities) : obs (obs), - known_securities(known_securities) + known_securities(known_securities), + forward_implied(config::get().forward_implied()) {} @@ -109,6 +111,7 @@ struct MDPlayer : md::IRelayWriter } IMDPlayerObserver &obs; std::map &known_securities; + bool forward_implied; }; From 1e8fd9589b8cd1125f819a432db470d8336445d1 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 9 Oct 2018 11:50:52 -0400 Subject: [PATCH 173/203] Fix logging crash during shell shutdown --- src/lib/logging/logging.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/logging/logging.cpp b/src/lib/logging/logging.cpp index 68e03a1f0..429e6cb4b 100644 --- a/src/lib/logging/logging.cpp +++ b/src/lib/logging/logging.cpp @@ -209,7 +209,9 @@ const char* intern(const char* str) { extern "C" BTS_EXPORT void Logger_log (int level, int subsystem, const char *message, const char *file, int line) { - bts::log::_thread_logger.logger->enqueue(log::log_ts(),log::Subsystem{(uint8_t)subsystem}, log::Level{(uint8_t)level}, intern(file), line, "{}", std::string(message)); + if (bts::log::_thread_logger.logger != nullptr) { + bts::log::_thread_logger.logger->enqueue(log::log_ts(), log::Subsystem{ (uint8_t)subsystem }, log::Level{ (uint8_t)level }, intern(file), line, "{}", std::string(message)); + } } } #endif From 65d3b858b36d5583df7a2aabc5f50317c342d874 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Tue, 9 Oct 2018 14:40:07 -0400 Subject: [PATCH 174/203] Fix typo in ExchangeSim which caused implied ask to be shown as bid. --- src/server/sparkd/exchange_sim/ExchangeSim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/sparkd/exchange_sim/ExchangeSim.cpp b/src/server/sparkd/exchange_sim/ExchangeSim.cpp index 549d0cd3a..4678da3b6 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSim.cpp +++ b/src/server/sparkd/exchange_sim/ExchangeSim.cpp @@ -89,7 +89,7 @@ struct MDPlayer : md::IRelayWriter }} {size_t x = 0; for (const auto& b : cr.implied().asks()) { - implied_bids[x] = md::PriceSize({b.mantissa(), (int8_t)b.exponent()}, b.size()); + implied_asks[x] = md::PriceSize({b.mantissa(), (int8_t)b.exponent()}, b.size()); x += 1; has_implied = true; }} From ae2712c9acfbd6a011a8419d8e7556559b10d9f8 Mon Sep 17 00:00:00 2001 From: Kevin Darby Date: Tue, 9 Oct 2018 17:14:01 -0400 Subject: [PATCH 175/203] added Filter primitive to support changing predicates via IObservable> --- .vscode/launch.json | 13 ++- src/client/spark/Admin/AdminModel.cs | 14 ++- src/client/spark/Admin/AdminModelWrapper.cs | 37 ++++--- src/client/spark/Admin/PermissionFilters.cs | 66 +++++++++++ src/client/spark/Orders/OrdersModel.cs | 11 +- src/client/spark_api/api/Update.cs | 68 ++++++++++++ src/client/spark_test/api/ApiUpdateTests.cs | 116 ++++++++++++++++++++ tools/runsparktest.cmd | 4 + 8 files changed, 302 insertions(+), 27 deletions(-) create mode 100644 src/client/spark/Admin/PermissionFilters.cs create mode 100644 tools/runsparktest.cmd diff --git a/.vscode/launch.json b/.vscode/launch.json index 106c3b541..967fe0105 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -42,7 +42,18 @@ "stopAtEntry": false, "externalConsole": true, "requireExactSource": false - }, + }, + { + "name": "Client Tests", + "type": "clr", + "request": "launch", + "program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe", + "args": ["test","test_filter"], + "cwd": "${workspaceRoot}", + "stopAtEntry": false, + "externalConsole": true, + "requireExactSource": false + }, { "name": "Client Attach", "type": "clr", diff --git a/src/client/spark/Admin/AdminModel.cs b/src/client/spark/Admin/AdminModel.cs index 3834350a1..55a314811 100644 --- a/src/client/spark/Admin/AdminModel.cs +++ b/src/client/spark/Admin/AdminModel.cs @@ -11,20 +11,22 @@ namespace mbts.spark { [Export] class AdminModel : IDisposable { readonly string _user_name; + public string user_name => _user_name; + readonly BehaviorSubject> _accounts_subject = new BehaviorSubject>(new Dictionary()), - _users_subject = new BehaviorSubject>(new Dictionary()); + _users_subject = new BehaviorSubject>(new Dictionary()); IReadOnlyDictionary _account_map = new Dictionary(), _user_map = new Dictionary(); - IDisposable _dispose; - AdminDataImmutable _data; + readonly BehaviorSubject _wrapper_subject = new BehaviorSubject(null); + IDisposable _dispose; [ImportingConstructor] AdminModel(AdminService service, [Import("username")]string user_name) { _user_name = user_name; _dispose = service.subscribei_admin_data().Select(update => { - _data = update.all; + _wrapper_subject.OnNext(new AdminModelWrapper(update.all.FirstOrDefault(), _user_name)); IEnumerable active_users = from admin_data in update.all from bts_user in admin_data.Users @@ -53,11 +55,11 @@ namespace mbts.spark { }); } - public AdminModelWrapper model_wrapper => new AdminModelWrapper(_data); public IObservable> on_users => _users_subject.AsObservable(); - public IObservable> on_accounts => _accounts_subject.AsObservable(); + public IObservable> on_accounts => _accounts_subject.AsObservable(); public IObservable> on_user_tuples => on_users.Select(d => d.Select(kv => new UserTuple(kv.Key, kv.Value)).ToList()); public IObservable> on_account_tuples => on_accounts.Select(d => d.Select(kv => new AccountTuple(kv.Key, kv.Value)).ToList()); + public IObservable on_admin_data => _wrapper_subject.AsObservable(); public UserTuple? get_current_user() { foreach (var kv in _user_map) { diff --git a/src/client/spark/Admin/AdminModelWrapper.cs b/src/client/spark/Admin/AdminModelWrapper.cs index 9175ecc6c..bbc001d33 100644 --- a/src/client/spark/Admin/AdminModelWrapper.cs +++ b/src/client/spark/Admin/AdminModelWrapper.cs @@ -4,30 +4,41 @@ using System; using System.Collections.Generic; using System.Linq; -namespace mbts.spark { - class AdminModelWrapper{ - private bts.api.admin.AdminDataImmutable _admin_data; - public AdminModelWrapper(bts.api.admin.AdminDataImmutable admin_data) +namespace mbts.spark +{ + class AdminModelWrapper + { + readonly AdminDataImmutable? _admin_data; + readonly string _user_name; + readonly List _allowed_accounts; + readonly Dictionary _allowed_account_dict = new Dictionary(); + public AdminModelWrapper(AdminDataImmutable? admin_data = null, string user_name = null) { - _admin_data=admin_data; + _admin_data = admin_data; + _user_name = user_name; + _allowed_accounts = search_admin(u => u.Name == _user_name).Select(x => x.Item1).ToList(); + foreach(var val in _allowed_accounts) + _allowed_account_dict[val.Name] = val.Name; } - // for a given user, returns the accounts the user is a - // part of along with the permissions public IEnumerable> get_trading_permissions(UInt32 user_id) { return search_admin(u => u.Id == user_id); } - public IEnumerable viewable_accounts(string user) + public IEnumerable viewable_accounts() { - return null; + return _allowed_accounts; + } + public bool can_view_account(string account) + { + return _allowed_account_dict.ContainsKey(account); } - IEnumerable search_admin(Func predicate) + IEnumerable> search_admin(Func predicate) { var rv = new List>(); - if (_admin_data != null) + if (_admin_data.HasValue) { - var users = _admin_data.Users.Where(predicate); + var users = _admin_data.Value.Users.Where(predicate); if (users != null && users.Count() == 1) { var user = users.ElementAt(0); @@ -36,7 +47,7 @@ namespace mbts.spark { foreach (var kv in user.UserData.AccountPermissions.AccountPermissions) { var account_id = kv.Key; - var accounts = _admin_data.Accounts.Where(a => a.Id == account_id); + var accounts = _admin_data.Value.Accounts.Where(a => a.Id == account_id); if (accounts != null && accounts.Count() == 1) { var account = accounts.ElementAt(0); diff --git a/src/client/spark/Admin/PermissionFilters.cs b/src/client/spark/Admin/PermissionFilters.cs new file mode 100644 index 000000000..115187a45 --- /dev/null +++ b/src/client/spark/Admin/PermissionFilters.cs @@ -0,0 +1,66 @@ +using bts; +using bts.api; +using bts.api.core; +using bts.api.orderman; +using bts.api.position; +using bts.spark; +using mbts.collections; +using mbts.spark; +using mbts.spark.utilities; +using mbts.utils; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.Specialized; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; + +namespace bts.api.core +{ + + static class PermissionFilters + { + public static IObservable> PermissionFilter(this IObservable> source, AdminModel am) + { + var pred = Observable.Create>( observer => + { + return am.on_admin_data.Subscribe(x => + { + Func filter = (z => x.can_view_account(z.Account)); + observer.OnNext(filter); + }); + }); + + return source.Filter(pred); + } + + public static IObservable> PermissionFilter(this IObservable> source, AdminModel am) + { + var pred = Observable.Create>(observer => + { + return am.on_admin_data.Subscribe(x => + { + Func filter = (z => x.can_view_account(z.account())); + observer.OnNext(filter); + }); + }); + + return source.Filter(pred); + } + + public static IObservable> PermissionFilter(this IObservable> source, AdminModel am) + { + var pred = Observable.Create>(observer => + { + return am.on_admin_data.Subscribe(x => + { + Func filter = (z => x.can_view_account(z.Account)); + observer.OnNext(filter); + }); + }); + + return source.Filter(pred); + } + } +} \ No newline at end of file diff --git a/src/client/spark/Orders/OrdersModel.cs b/src/client/spark/Orders/OrdersModel.cs index bf9b6ca6c..168feca9a 100644 --- a/src/client/spark/Orders/OrdersModel.cs +++ b/src/client/spark/Orders/OrdersModel.cs @@ -30,12 +30,9 @@ namespace mbts.spark { readonly TableUpdateDemuxer _security_orders_demuxer; readonly TableUpdateDemuxer _security_positions_demuxer; IDisposable _dispose; - AdminModelWrapper _admin; [ImportingConstructor] - private OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, SpreaderModel spreader, ErrorReporter error, - AdminModel admin_model, [Import("username")]string user_name) { - _admin = new AdminModelWrapper( + private OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, SpreaderModel spreader, ErrorReporter error, AdminModel admin_model) { _bus = bus; _factory = factory; _service = service; @@ -44,15 +41,15 @@ namespace mbts.spark { _error = error; var scheduler = ThreadPoolScheduler.Instance; var interval = TimeSpan.FromMilliseconds(100); - on_orders = _service.subscribei_orders().Naggle(interval, scheduler).Table().Cache(); - on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).Table().Cache(); + on_orders = _service.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache(); + on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache(); working_orders = on_orders.Scan( ImmutableDictionary.Empty, (workers, table) => _update_working_orders(workers, table)) .Replay(1).RefCount(); on_order_tuples = OrderRecordTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders); - on_fills = _service.subscribei_fills().Naggle(interval, scheduler).Table().Cache(); + on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache(); _activity = new OrderActivitySummarizer(bus, on_orders); on_fill_tuples = FillTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders, on_fills); _security_orders_demuxer = new TableUpdateDemuxer(o => o.Key, o => o.security_key()); diff --git a/src/client/spark_api/api/Update.cs b/src/client/spark_api/api/Update.cs index 2853882db..3f4fc2ab6 100644 --- a/src/client/spark_api/api/Update.cs +++ b/src/client/spark_api/api/Update.cs @@ -129,6 +129,7 @@ namespace bts.api.core { public IEnumerable removed { get; } public TKey key(T t) { return _key(t); } + public Func key_extractor => _key; #if DEBUG public override string ToString() { @@ -347,5 +348,72 @@ namespace bts.api.core { return source.Select(t => new LegacyTableUpdate(t)); } + public static IObservable> Filter(this IObservable> source, IObservable> predicate) where TKey : IEquatable + { + return Observable.Create>(observer => + { + Update unfiltered_cache = null, filtered_cache = null; + Func pred_cache = null; + Log.DataFlow.diag($"Create Filter {typeof(T).Name}"); + + return source.CombineLatest(predicate, (upd, pred) => (upd, pred)).Subscribe( tup => + { + bool pred_changed = !object.ReferenceEquals(tup.pred, pred_cache) && (pred_cache != null); + pred_cache = tup.pred; + unfiltered_cache = tup.upd; + if (pred_changed && filtered_cache != null) + { + //clear out existing + var clear = new Update( + (IReadOnlyCollection)Array.Empty(), + Enumerable.Empty(), + Enumerable.Empty>(), + filtered_cache.all, unfiltered_cache.key_extractor); + observer.OnNext(clear); + + var refilter = unfiltered_cache.all.Where(tup.pred); + var readd = new Update( + refilter.ToArray(), refilter, + Enumerable.Empty>(), + Enumerable.Empty(), unfiltered_cache.key_extractor); + observer.OnNext(readd); + filtered_cache = readd; + } + else + { + IReadOnlyCollection all; IEnumerable added, removed; IEnumerable> changed; + apply_predicate(tup.pred, tup.upd, out all, out added, out changed, out removed); + var next = new Update(all, added, changed, removed, tup.upd.key_extractor); + observer.OnNext(next); + filtered_cache = next; + } + }, + () => observer.OnCompleted()); + }); + } + + private static void apply_predicate(Func predicate, Update cache, + out IReadOnlyCollection all, out IEnumerable added, + out IEnumerable> changed, out IEnumerable removed) where TKey : IEquatable + { + var adds = new List(); + adds.AddRange(cache.added.Where(predicate)); + var rems = new List(); + rems.AddRange(cache.removed.Where(predicate)); + var diffs = new List>(); + foreach (var change in cache.changes) + { + if(!predicate(change.Old) && predicate(change.New)) + adds.Add(change.New); + else if(predicate(change.Old) && !predicate(change.New)) + rems.Add(change.Old); + else if(predicate(change.Old) && predicate(change.New)) + diffs.Add(change); + } + all = cache.all.Where(predicate).ToArray(); + added = adds; + changed = diffs; + removed = rems; + } } } \ No newline at end of file diff --git a/src/client/spark_test/api/ApiUpdateTests.cs b/src/client/spark_test/api/ApiUpdateTests.cs index dd65b8edb..791d9a773 100644 --- a/src/client/spark_test/api/ApiUpdateTests.cs +++ b/src/client/spark_test/api/ApiUpdateTests.cs @@ -6,6 +6,7 @@ using System.Reactive.Linq; using System.Threading.Tasks; using System.Linq; using System.Reactive.Concurrency; +using System.Reactive.Subjects; namespace mbts.api.Test { [TestGroup] @@ -104,6 +105,121 @@ namespace mbts.api.Test { } + void validate_filter(IObservable> raw, IObservable> filtered, Func pred) + { + var total = raw.ToEnumerable().Count(); + + // Validate that update is right at each point + for (var count = 1; count < total; ++count) + { + var orig = raw.Take(count).reconstitute(id).ToEnumerable().Last(); + var next = filtered.Take(count).ToEnumerable().Last(); + Assert.That(next.all.All(pred)); + Assert.That(next.added.All(pred)); + Assert.That(next.removed.All(pred)); + Assert.That(next.changed.All(pred)); + } + + // Do it again with TableUpdate + for (var count = 1; count < total; ++count) + { + var orig = raw.Take(count).reconstitute(id).Table().ToEnumerable().Last(); + var next = filtered.Take(count).ToEnumerable().Last(); + Assert.That(next.added.All(pred)); + Assert.That(next.removed.All(pred)); + Assert.That(next.changed.All(pred)); + } + } + + + void validate_filter_sub(IObservable> raw, IObservable> filtered, BehaviorSubject> pred) + { + raw.reconstitute(x => x.id).CombineLatest(filtered, (r, f) => Tuple.Create(r, f)).Subscribe(tup => + { + Assert.That(tup.Item2.all.All(pred.Value)); + Assert.That(tup.Item2.added.All(pred.Value)); + Assert.That(!tup.Item2.removed.Any(pred.Value)); + Assert.That(tup.Item2.changed.All(pred.Value)); + + Dictionary rawd = tup.Item1.added.ToDictionary(x => x.id); + Dictionary fd = tup.Item2.added.ToDictionary(x => x.id); + foreach (var kv in rawd) + { + if(pred.Value(kv.Value)) + Assert.That(fd.ContainsKey(kv.Key)); + else + Assert.That(!fd.ContainsKey(kv.Key)); + } + }); + } + + + [Test] + public void test_filter() + { + var raw = Observable.Create>(observer => + { + RawUpdate rup = new RawUpdate(); + for (int x = 0; x < 50; ++x) + { + rup.Clear(); + var item = _item_pool[x]; + rup.added_items.Add(item); + rup.added_keys.Add(item.id); + observer.OnNext(rup); + } + var rand = new Random(); + for (int r = 0; r < rand.Next(4, 30); r += rand.Next(1, 2)) + { + rup.Clear(); + rup.removed_keys.Add(_item_pool[r].id); + observer.OnNext(rup); + } + rup.Clear(); + for (int c = 31; c < rand.Next(32, 50); c += rand.Next(1, 2)) + { + var ch = new Item(_item_pool[c].id, _item_pool[c].name + "_change"); + rup.changed_items.Add(ch); + rup.changed_keys.Add(ch.id); + } + observer.OnNext(rup); + + observer.OnCompleted(); + return Disposable.empty; + }); + + Func evens = (x => x.id % 2 == 0); + Func odds = (x => x.id % 2 == 1); + var predeven = Observable.Create>(observer => + { + observer.OnNext(evens); + observer.OnCompleted(); + return Disposable.empty; + }); + + var filtered = raw.reconstitute(x=> x.id).Filter(predeven); + validate_filter(raw, filtered, evens); + + var predodd = Observable.Create>(observer => + { + observer.OnNext(odds); + observer.OnCompleted(); + return Disposable.empty; + }); + + filtered = raw.reconstitute(x => x.id).Filter(predodd); + validate_filter(raw, filtered, odds); + + + + var sub= new BehaviorSubject>(evens); + filtered = raw.reconstitute(x => x.id).Filter(sub); + validate_filter_sub(raw, filtered, sub); + + sub.OnNext(odds); + sub.OnCompleted(); + } + [Test] public async Task test_add() { // Add 1 item 10x diff --git a/tools/runsparktest.cmd b/tools/runsparktest.cmd new file mode 100644 index 000000000..903d77d38 --- /dev/null +++ b/tools/runsparktest.cmd @@ -0,0 +1,4 @@ +@echo off +pushd %~dp0\..\src\client\sparkcmd +dotnet watch run test %1 -t +popd From 74b18cf71a77acfffe35ed65b42ecab0d8c5f377 Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Tue, 9 Oct 2018 17:23:33 -0400 Subject: [PATCH 176/203] added a manual test for implieds in the UI --- testing/manual/forms/implied_markets_ui.txt | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 testing/manual/forms/implied_markets_ui.txt diff --git a/testing/manual/forms/implied_markets_ui.txt b/testing/manual/forms/implied_markets_ui.txt new file mode 100644 index 000000000..8970b116b --- /dev/null +++ b/testing/manual/forms/implied_markets_ui.txt @@ -0,0 +1,23 @@ +0. This test should run in sim market with merged turned on + +ExchangeSim {value {merge_with_md:true}} + +1. Bring up a market view for SI futures. There are 3 quantity columns +a. Implied : This is the quantity coming purely from implieds (ex for the spreads, this is +'implied' from the legs) +b. Direct : This comes purely from markets in the security itself +c. Qty (or Merged Qty) == Implied + Direct. + +2. Select a future with only implied markets (SI should have lots of them). Verify that the ladder +agrees with the market view (the ladder shows merged by default). + + +3. Enter buy and sell orders away from the market and make sure they show up as direct market in the ladder. + +4. In the ladder settings -> Market Data, select Direct Only. Make sure the ladder does the right thing. + +5. Go back to Merged and make sure the Ladder does the right thing. + +6. Make you direct bid better than the implied bid (by moving your buy order) and make sure MView does the right thing. + +7. Repeat 6 for sell/ask. \ No newline at end of file From 8ba354eda892cbaf5951ed4cbd91d73fe7dd94fb Mon Sep 17 00:00:00 2001 From: John Colvin Date: Tue, 9 Oct 2018 17:59:27 -0400 Subject: [PATCH 177/203] SPARK-1780 Orderbook settings - use [new]standard SimpleGridSettingsDialog --- .../Modules/Trading/OrderBook/OrderBookVM.cs | 21 +++--- .../Trading/OrderBook/OrderBookWindow.cs | 42 +++++++++-- .../OrderBook/OrderBookWindowFactory.cs | 48 +++++++++++-- .../Modules/Trading/TradingShellModule.cs | 2 +- .../UserSettings/UserSettings.cs | 13 ++-- .../DIYGrid/Settings/GridColumnSettingsVM.cs | 6 +- .../DIYGrid/Settings/GridSettingsManager.cs | 72 ++++++++++++++----- .../DIYGrid/Settings/GridSettingsVM.cs | 66 +++++++++++++---- .../Settings/ImmutableColumnSettingsStore.cs | 72 +++++++++++++++++++ .../Settings/SimpleGridSettingsManager.cs | 70 +----------------- .../Spark/Utilities/WindowSettingsDialogVM.cs | 10 ++- 11 files changed, 294 insertions(+), 128 deletions(-) create mode 100644 src/client/spark/view/Controls/DIYGrid/Settings/ImmutableColumnSettingsStore.cs diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookVM.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookVM.cs index 93996d4fe..ff18e3298 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookVM.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookVM.cs @@ -3,6 +3,7 @@ using bts.api.core; using bts.api.orderman; using bts.shell; using mbts.collections; +using mbts.persist; using mbts.ui; using mbts.ui.controls; using mbts.ui.settings; @@ -22,6 +23,7 @@ using System.Windows.Data; using System.Windows.Input; using static bts.shell.OrderBookWindowFactory; using static mbts.spark.OrderBookView; +using _GridSettingsManager = mbts.ui.GridSettingsManager; namespace mbts.spark { partial class OrderBookVM : DisposableViewModelBase, IHandleRowSelection { @@ -38,7 +40,7 @@ namespace mbts.spark { SetMap _parent_to_children = new SetMap(); SetMap _security_orders = new SetMap(); - public OrderBookVM(IObservable owner_title, IBus bus, INotificationService notification_service, OrdersModel model) { + public OrderBookVM(IObservable owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager settings_manager) { columns = new OrderBookColumnCollection(bus, model, _order_securities_subject); rows_view = CollectionViewSource.GetDefaultView(_order_rows); rows_view.Filter = obj => obj is OrderBookRowVM vm && vm.is_visible && vm.passes_filter; @@ -72,6 +74,7 @@ namespace mbts.spark { _dispose = Disposable.Create( bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(on_securities), model.on_orders.OnDispatcher().AddRemoveOnly().Subscribe(on_orders), + settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager), grid_settings_manager, columns); @@ -219,7 +222,7 @@ namespace mbts.spark { public OrderRecordImmutable? current_order => current_row?.order; public IEnumerable selected_orders => _selected_rows.Select(r => r.order); - public GridSettingsManager grid_settings_manager { get; } + public _GridSettingsManager grid_settings_manager { get; } public ICommand collapse_all_rows_command { get; } public ICommand expand_all_rows_command { get; } @@ -229,21 +232,21 @@ namespace mbts.spark { } public OrderBookStartupData create() { - var (grid, animations) = grid_settings_manager.create(); - return new OrderBookStartupData(columns.create(), grid, animations.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))); + var gs = grid_settings_manager.create(); + return new OrderBookStartupData(columns.create(), gs.general, gs.predicate_animations.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))); } public void load_from(OrderBookStartupData state) { if (state == null) return; _state = state; - grid_settings_manager.load_from((state.grid, state.predicate_animations)); + grid_settings_manager.load_from(state.grid_settings_wrapper()); columns.load_from(state.filters); } internal void remove_branch_filter_root(uint key) => _branch_filter_roots.Remove(key); - static GridSettingsManager _create_grid_settings_manager( + static _GridSettingsManager _create_grid_settings_manager( IObservable owner_title, IBus bus, INotificationService notification_service, OrdersModel model, ObservableCollection rows, ICollectionView rows_view, ObservableCollection columns) { // make font-weight immutable for all columns (the font-weight rule is hard-coded: Regular for root-orders, Light for all others) @@ -255,7 +258,7 @@ namespace mbts.spark { foreach (var c in new[] { Columns.Side, Columns.Size, Columns.Filled }) { col_settings_flags[c.ToString()] = GridColumnSettingsFeatureFlags.ImmutableFontColor | GridColumnSettingsFeatureFlags.ImmutableFontWeight; } - return new GridSettingsManager( + return new _GridSettingsManager( owner_title, bus, notification_service, model, rows, rows_view, columns, () => { var pvm = PredicateBuilderVMWrapper.create( @@ -268,7 +271,9 @@ namespace mbts.spark { OrderRecordTuplePredicateVMFactory.get_oneofs(bus, model), rp => rp.predicate_type_case); pvm.selected_predicate_type = _corresponding_predicate_type(EnumHelper.parse_or_default(col_id)); return pvm; - }, notification_service.data_cache, col_settings_flags); + }, + (gen, anims) => new OrderBookGridSettings(gen, anims.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))), + notification_service.data_cache, col_settings_flags); } static OrderRecordTuplePredicate.PredicateTypeOneofCase _corresponding_predicate_type(Columns col) { diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindow.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindow.cs index cce32129f..ea4c8f1dc 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindow.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindow.cs @@ -2,18 +2,27 @@ using bts.api.orderman; using bts.shell; using bts.spark; +using mbts.persist; using mbts.ui; +using mbts.ui.converters; +using mbts.ui.spark; +using mbts.ui.utility; using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; using System.Windows; +using System.Windows.Data; using System.Windows.Media; +//using IOrderBookWSM = mbts.persist.IWindowSettingsManager>; +using OrderBookGridSettings = bts.shell.OrderBookWindowFactory.OrderBookGridSettings; +using OrderBookGridSettingsVM = mbts.spark.GridSettingsVM; namespace mbts.spark { internal class OrderBookWindow : SparkWindow, ISharedWindow { - const string _window_type = "OrderBook", ORDERBOOK_THREAD_ID = "OrderBook"; + const string _window_type = "OrderBook", ORDERBOOK_THREAD_ID = "OrderBook", _default_title = "Order Book"; readonly INotificationService _notification_service; + readonly IWindowSettingsManager _settings_manager; OrderBookWindowFactory.OrderBookStartupData _state; OrdersModel _model; OrderBookVM _vm; @@ -32,12 +41,14 @@ namespace mbts.spark { , _toggle_expand_all_command , _show_grid_settings_command; - public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service, OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon) - : base("Order Book", bus, factory, SparkCommands.ShowOrders.icon) { + public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service, + OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon, IWindowSettingsManager settings_manager) + : base(_default_title, bus, factory, SparkCommands.ShowOrders.icon) { _model = model; _factory = factory; _notification_service = notification_service; _state = state; + _settings_manager = settings_manager; } public override OrderBookWindowFactory.OrderBookStartupData SaveState => _state = _vm.create(); @@ -55,7 +66,7 @@ namespace mbts.spark { } public void thread_init() { - _vm = new OrderBookVM(this.observe_title(), _bus, _notification_service, _model); + _vm = new OrderBookVM(this.observe_title(), _bus, _notification_service, _model, _settings_manager); _vm.load_from(_state); _view = new OrderBookView(_vm); @@ -106,9 +117,28 @@ namespace mbts.spark { Icons.DownArrow, _vm.expand_all_rows_command); - _show_grid_settings_command = SparkCommands.window_settings_command(_vm.grid_settings_manager.launch_settings_editor); + _show_grid_settings_command = SparkCommands.window_settings_command(new DialogCommand(() => { + var vm = new WindowSettingsDialogVM( + _settings_manager, + us => us.order_book_grid, + UserSettings.FileExtensions.GRID, + s => _vm.grid_settings_manager.create_vm( + // we need to get the most up to date GridSettings when launching b/c users may + // have changed some settings on the grid directly, like column widths. + s.general.with(_vm.columns.Select(c => c.create())), + this.observe_title() + ) + ); + // TODO: bind to main window's Title + var dialog = new SimpleGridSettingsDialog { DataContext = vm }; + dialog.SetBinding(Window.TitleProperty, new Binding($"{nameof(vm.instance_vm)}.{nameof(OrderBookGridSettingsVM.owner_title)}") { + Converter = ValueConverter.create(str => (string.IsNullOrEmpty(str) ? _default_title : str) + " Settings") + }); + vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog); + return dialog; + })); - _commands = new SparkCommand[] { + _commands = new SparkCommand[] { _show_fills_command, _modify_order_command, _cancel_selected_orders_command, diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs index c3037472b..5fe8912a8 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs @@ -1,8 +1,9 @@ using bts.api; using bts.api.orderman; using bts.spark; -using Google.Protobuf.Collections; +using bts.utils; using mbts; +using mbts.persist; using mbts.spark; using mbts.spark.filters; using mbts.ui; @@ -22,19 +23,22 @@ namespace bts.shell { readonly INotificationService _notification_service; readonly INotificationServiceDataCache _notification_service_data_cache; readonly OrdersModel _model; + readonly UserSettingsHub _settings_hub; - public OrderBookWindowFactory(IShellModule owner, IBus bus, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, OrdersModel model) : base(owner, "SparkOrderBookWindow") { + public OrderBookWindowFactory(IShellModule owner, IBus bus, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, OrdersModel model, UserSettingsHub settings_hub) : base(owner, "SparkOrderBookWindow") { _bus = bus; _notification_service = notification_service; _notification_service_data_cache = notification_service_data_cache; _model = model; - + _settings_hub = settings_hub; _dispose = bus.subscribe(SparkTags.ShowOrderBookTag).OnDispatcher().Subscribe(show); } public override Task load(OrderBookStartupData state) { - var w = new OrderBookWindow(_model, this, _bus, _notification_service, state, SparkCommands.ShowOrders.icon) as IShellWindow; - return Task.FromResult(w); + var settings_manager = new WindowSettingsManager(_settings_hub, new OrderBookGridSettings(state.grid, state.predicate_animations), + us => us.order_book_grid, (us, s) => us.with(order_book_grid: s)); + var w = new OrderBookWindow(_model, this, _bus, _notification_service, state, SparkCommands.ShowOrders.icon, settings_manager.as_non_shared()); + return Task.FromResult(w); } async void show(OrderBookStartupData startup_data) { @@ -64,6 +68,7 @@ namespace bts.shell { } public IReadOnlyList predicate_animations => _predicate_animations ?? (_predicate_animations = new List()); + public OrderBookGridSettings grid_settings_wrapper() => new OrderBookGridSettings(grid, predicate_animations); public static OrderBookStartupData Default => new OrderBookStartupData( @@ -171,5 +176,38 @@ namespace bts.shell { [ProtoMember(20)] public string[] symbols; } + + [ProtoContract] + public class OrderBookGridSettings : IGridSettingsWithNotifications, IEquatable { + [ProtoMember(1)] + public GridSettings general { get; set; } + [ProtoMember(2)] + List _predicate_animations; + + public OrderBookGridSettings() { } + public OrderBookGridSettings(GridSettings general = default, IEnumerable predicate_animations = null) { + this.general = general; + _predicate_animations = new List(); + if (predicate_animations != null) + _predicate_animations.AddRange(predicate_animations); + } + + public IReadOnlyList predicate_animations => _predicate_animations ?? (_predicate_animations = new List()); + IEnumerable> IGridSettingsWithNotifications.predicate_animations => predicate_animations; + + public OrderBookGridSettings with(GridSettings general) => + new OrderBookGridSettings(general ?? this.general, predicate_animations); + + public bool Equals(OrderBookGridSettings other) => + other != null && + EqualityComparer.null_check_equals(general, other.general) && + EqualityComparer.safe_sequence_equals(predicate_animations, other.predicate_animations); + + public override bool Equals(object obj) => Equals(obj as OrderBookGridSettings); + public override int GetHashCode() => + EqualityComparer.aggregate_hash( + EqualityComparer.safe_get_hash_code(general), + EqualityComparer.safe_aggregate_hash(predicate_animations)); + } } } \ No newline at end of file diff --git a/src/client/spark/Modules/Trading/TradingShellModule.cs b/src/client/spark/Modules/Trading/TradingShellModule.cs index eede04c5f..22a6c2f14 100644 --- a/src/client/spark/Modules/Trading/TradingShellModule.cs +++ b/src/client/spark/Modules/Trading/TradingShellModule.cs @@ -23,7 +23,7 @@ namespace bts.shell { public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model, MarketDataAuxiliaryModel md_auxiliary_model) { _lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model, md_auxiliary_model); - _obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model); + _obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model, settings_hub); _fwf = new FillsWindowFactory(this, bus, settings_hub, orders_model); _plwf = new PAndLWindowFactory(this, bus, settings_hub, pandl_model); } diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs index 7fbbfac3d..3f08f80d2 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs @@ -1,5 +1,4 @@ -using bts.proto; -using Google.Protobuf.WellKnownTypes; +using Google.Protobuf.WellKnownTypes; using mbts.ui.settings; using ProtoBuf; using System; @@ -17,7 +16,8 @@ namespace mbts.spark { _market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings, _fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings, _time_and_sales_grid = bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultGridSettings, - _synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings + _synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings, + _order_book_grid = bts.shell.OrderBookWindowFactory.OrderBookStartupData.Default.grid_settings_wrapper(), }; [ProtoMember(1)] @@ -36,6 +36,8 @@ namespace mbts.spark { GridSettings _synthetic_management_grid; [ProtoMember(9)] GridSettings _pnl_grid; + [ProtoMember(10)] + bts.shell.OrderBookWindowFactory.OrderBookGridSettings _order_book_grid; public UserSettings() { } @@ -46,6 +48,7 @@ namespace mbts.spark { public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings); public GridSettings synthetic_management_grid => _synthetic_management_grid ?? (_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings); public GridSettings pnl_grid => _pnl_grid ?? (_pnl_grid = bts.shell.PAndLWindowFactory.PAndLStartupData.DefaultGridSettings); + public bts.shell.OrderBookWindowFactory.OrderBookGridSettings order_book_grid => _order_book_grid ?? (_order_book_grid = bts.shell.OrderBookWindowFactory.OrderBookStartupData.Default.grid_settings_wrapper()); public UserSettings with(LadderSettings ladder = null, NotificationSettings notification = null, @@ -53,7 +56,8 @@ namespace mbts.spark { GridSettings time_and_sales = null, GridSettings provided_fills_gridsettings = null, GridSettings provided_synthetic_management_gridsettings = null, - GridSettings provided_pnl_gridsettings = null) => + GridSettings provided_pnl_gridsettings = null, + bts.shell.OrderBookWindowFactory.OrderBookGridSettings order_book_grid = null) => new UserSettings { _ladder = ladder ?? _ladder, _notification = notification ?? _notification, @@ -62,6 +66,7 @@ namespace mbts.spark { _fills_grid = provided_fills_gridsettings ?? _fills_grid, _synthetic_management_grid = provided_synthetic_management_gridsettings ?? _synthetic_management_grid, _pnl_grid = provided_pnl_gridsettings ?? _pnl_grid, + _order_book_grid = order_book_grid ?? _order_book_grid }; public static class FileExtensions { diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs index 57917bf1c..99e12a66f 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs @@ -27,7 +27,7 @@ namespace mbts.spark { ImmutableFontWeight } - partial class GridSettingsVM { + partial class GridSettingsVM { public interface IColumnVM : ISimpleColumnVM, IValueFactory<(GridColumnSettings settings, ILookup animations)>, IDisposable { ObservableEditableCollection predicate_animations { get; } new (GridColumnSettings settings, ILookup animations) create(); @@ -35,7 +35,7 @@ namespace mbts.spark { } public class ColumnVM : DisposableViewModelBase, IColumnVM { - public readonly GridSettingsVM owner; + public readonly GridSettingsVM owner; readonly Property _id, _display_name; readonly Property _width; @@ -44,7 +44,7 @@ namespace mbts.spark { readonly Property _font_weight_override; GridColumnSettings _settings; - public ColumnVM(GridSettingsVM owner) { + public ColumnVM(GridSettingsVM owner) { this.owner = owner; _id = backing(nameof(id), string.Empty); _display_name = backing(nameof(display_name), string.Empty); diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsManager.cs b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsManager.cs index 01cf24d39..f77686dbf 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsManager.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsManager.cs @@ -17,10 +17,10 @@ using System.Windows.Media; using static mbts.ui.controls.DIYGrid; namespace mbts.ui { - class GridSettingsManager : - ViewModelBase, IValueFactory<(GridSettings grid, IEnumerable> predicate_animations_data)>, IDisposable + class GridSettingsManager : ViewModelBase, IValueFactory, IObserver, IDisposable where TRow : IRowVM - where TPredicateBuilder : IPredicateBuilder, new() { + where TPredicateBuilder : IPredicateBuilder, new() + where TSettingsData : class, IGridSettingsWithNotifications { // public readonly ObservableCollection live_sorting_props; @@ -39,12 +39,18 @@ namespace mbts.ui { readonly DelegateProperty _font_color, _selection_highlight; readonly SetMap, AnimationDefinitionWrapper> _animation_defs = new SetMap, AnimationDefinitionWrapper>(); readonly TPredicateBuilderArg _arg; + readonly Func> _create_predicate_vm; + readonly Func> _create_column_predicate_vm; + readonly Func>, TSettingsData> _create_settings_obj; + readonly IReadOnlyDictionary _column_flags; IEnumerable> _predicate_animations_data; public GridSettingsManager( IObservable owner_title, IBus bus, INotificationService notification_service, OrdersModel orders_model, ObservableCollection rows, ICollectionView rows_view, ObservableCollection columns, Func> create_predicate_vm, - Func> create_column_predicate_vm, TPredicateBuilderArg arg, IReadOnlyDictionary column_flags = null) { + Func> create_column_predicate_vm, + Func>, TSettingsData> create_settings_obj, + TPredicateBuilderArg arg, IReadOnlyDictionary column_flags = null) { // _bus = bus; _notification_service = notification_service; @@ -52,7 +58,11 @@ namespace mbts.ui { _rows = rows; _rows_view = rows_view; _columns = columns; + _create_predicate_vm = create_predicate_vm; + _create_column_predicate_vm = create_column_predicate_vm; + _create_settings_obj = create_settings_obj; _arg = arg; + _column_flags = column_flags.ToDictionary(kv => kv.Key, kv => ImmutableColumnSettingStore.create(kv.Value)); _grid_settings = backing(nameof(grid_settings), GridSettings.Default); _font_family = backing(nameof(font_family), () => new FontFamily(grid_settings.font_family), nameof(grid_settings)); _font_weight = backing(nameof(font_weight), () => grid_settings.font_weight, nameof(grid_settings)); @@ -72,10 +82,10 @@ namespace mbts.ui { return pa; } Window create_settings_view() { - var vm = new GridSettingsVM(owner_title, _bus, _orders_model, create_predicate_vm, create_column_predicate_vm); + var vm = new GridSettingsVM(owner_title, _bus, _orders_model, create_predicate_vm, create_column_predicate_vm, _create_settings_obj); // need to refresh before launching the settings-view bc users can chanve some grid-column-settings properties directly from the grid (e.g. width, is_collapsed, etc.) _grid_settings.set(grid_settings.with(columns.Select(c => c.create()))); - vm.load_from((grid_settings, _predicate_animations_data)); + vm.load(_create_settings_obj(grid_settings, _predicate_animations_data)); if (column_flags != null) { foreach (var col in vm.columns) { if (column_flags.TryGetValue(col.id, out GridColumnSettingsFeatureFlags f)) @@ -84,10 +94,10 @@ namespace mbts.ui { } var window = new GridSettingsDialog { DataContext = vm }; vm.committed += () => { - var (grid, predicate_animations_data) = vm.create(); + TSettingsData sd = vm.get_result(); // IMPORTANT: don't overwrite current sorting - grid = grid.with(sorting_criterion: rows_view.get_sorting_criteria(columns)); - load_from((grid, predicate_animations_data)); + sd = _create_settings_obj(sd.general.with(sorting_criterion: rows_view.get_sorting_criteria(columns)), sd.predicate_animations); + load_from(sd); }; vm.RequestClose += (s, e) => WindowUtilities.force_close(window); return window; @@ -107,22 +117,30 @@ namespace mbts.ui { public SolidColorBrush selection_highlight => _selection_highlight.Value; public IDialogCommand launch_settings_editor { get; } - public (GridSettings grid, IEnumerable> predicate_animations_data) create() { - // IMPORTANT: don't overwrite current sorting - _grid_settings.set(grid_settings.with(_columns.Select(c => c.create()), _rows_view.get_sorting_criteria(_columns))); - return (grid_settings, _predicate_animations_data); + public GridSettingsVM create_vm(GridSettings settings, IObservable title) { + var vm = new GridSettingsVM(title, _bus, _orders_model, _create_predicate_vm, _create_column_predicate_vm, _create_settings_obj, _column_flags.ToDictionary(kv => kv.Key, kv => kv.Value.flags)); + vm.load(_create_settings_obj(settings, _predicate_animations_data)); + return vm; } - public void load_from((GridSettings grid, IEnumerable> predicate_animations_data) value) { + public TSettingsData create() { + // IMPORTANT: don't overwrite current sorting + _grid_settings.set(grid_settings.with(_columns.Select(c => c.create()), _rows_view.get_sorting_criteria(_columns))); + return _create_settings_obj(grid_settings, _predicate_animations_data); + } + + public void load_from(TSettingsData value) => _load_from_core(value, true); + + void _load_from_core(TSettingsData value, bool reset_immutable_flag_stores) { Disposable.dispose(_notification_subs); - var grid = value.grid ?? GridSettings.Default; + var grid = value.general ?? GridSettings.Default; _grid_settings.set(grid); foreach (ColumnBase col in _columns) col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight); #pragma warning disable CS0612 // Type or member is obsolete _rows_view.set_sort_descriptions(grid.sorting_criterion, _columns); #pragma warning restore CS0612 // Type or member is obsolete - _predicate_animations_data = value.predicate_animations_data; + _predicate_animations_data = value.predicate_animations; _animation_defs.clear(); foreach (var pa in _predicate_animations_data) { var predicate = pa.predicate_builder.create_predicate(_arg); @@ -142,6 +160,12 @@ namespace mbts.ui { // ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings // don't add insult to injury by crashing ** if (grid_settings.columns != null && _columns != null) { + if (reset_immutable_flag_stores) { + foreach (var kv in _column_flags) { + if (grid.columns.TryGetFirst(c => c.id == kv.Key, out GridColumnSettings cs)) + kv.Value.store(cs); + } + } var col_settings_id_map = grid_settings.columns.ToDictionary(cs => id_key(cs.id)); var columns_id_map = _columns.ToDictionary(c => id_key(c.id)); if (col_settings_id_map.Count == columns_id_map.Count && col_settings_id_map.Count == grid_settings.columns.Count) { @@ -161,5 +185,21 @@ namespace mbts.ui { IGridColumn lookup_column(string col_id) => _columns.OfType>().FirstOrDefault(c => string.Equals(col_id, c.id, StringComparison.CurrentCultureIgnoreCase)); } + + void IObserver.OnNext(TSettingsData sd) { + var grid = sd.general; + if (grid.columns != null) { + var cols_with_applied_flags = grid.columns.ToList(); + for (int i = 0; i < cols_with_applied_flags.Count; i++) { + if (_column_flags.TryGetValue(cols_with_applied_flags[i].id, out ImmutableColumnSettingStore store)) { + cols_with_applied_flags[i] = store.apply(cols_with_applied_flags[i]); + } + } + sd = _create_settings_obj(grid.with(cols_with_applied_flags), sd.predicate_animations); + } + _load_from_core(sd, false); + } + void IObserver.OnError(Exception error) { } + void IObserver.OnCompleted() { } } } \ No newline at end of file diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs index e48955331..4e280d9e6 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs @@ -1,5 +1,6 @@ using bts.api; using bts.shell; +using bts.utils; using mbts.collections; using mbts.collections.extensions; using mbts.ui; @@ -18,14 +19,38 @@ using CellAnimation = mbts.ui.CellAnimation; using FontFamily = System.Windows.Media.FontFamily; namespace mbts.spark { - interface IGridSettingsFactory : IValueFactory<(GridSettings grid, IEnumerable> predicate_animations_data)> - where TPredicateBuilder : IPredicateBuilder, new() { } + interface IGridSettingsWithNotifications where TPredicateBuilder : IPredicateBuilder { + GridSettings general { get; } + IEnumerable> predicate_animations { get; } + } - partial class GridSettingsVM : DisposableViewModelBase, IGridSettingsFactory, IRequestViewClose - where TPredicateBuilder : IPredicateBuilder, new() { + class GridSettingsWithNotifications : IEquatable> where TPredicateBuilder : IPredicateBuilder { + public readonly GridSettings grid; + public readonly IEnumerable> predicate_animations_data; + public GridSettingsWithNotifications(GridSettings grid, IEnumerable> predicate_animations_data) { + this.grid = grid; + this.predicate_animations_data = predicate_animations_data; + } + + public bool Equals(GridSettingsWithNotifications other) => + other != null && + EqualityComparer.null_check_equals(grid, other.grid) && + EqualityComparer.safe_sequence_equals(predicate_animations_data, other.predicate_animations_data); + + public override bool Equals(object obj) => Equals(obj as GridSettingsWithNotifications); + public override int GetHashCode() => + EqualityComparer.aggregate_hash( + EqualityComparer.safe_get_hash_code(grid), + EqualityComparer.safe_aggregate_hash(predicate_animations_data)); + } + + partial class GridSettingsVM : DisposableViewModelBase, IRequestViewClose, IWindowSettingsVM + where TPredicateBuilder : IPredicateBuilder, new() + where TSettingsData :class, IGridSettingsWithNotifications { // protected readonly Func> _create_predicate_vm; protected readonly Func> _create_column_predicate_vm; + readonly Func>, TSettingsData> _create_settings_obj; readonly Property _current_column; readonly Property _font_family; readonly Property _font_weight; @@ -33,16 +58,22 @@ namespace mbts.spark { readonly Property _font_size; readonly Property _show_predicate_animations, _show_cell_predicate_animations; readonly OrdersModel _orders_model; + readonly IReadOnlyDictionary _column_flags; public GridSettingsVM( IObservable owner_title, IBus bus, OrdersModel orders_model, - Func> create_predicate_vm, Func> create_column_predicate_vm) { + Func> create_predicate_vm, + Func> create_column_predicate_vm, + Func>, TSettingsData> create_settings_obj, + IReadOnlyDictionary column_flags = null) { // _owner_title = backing(nameof(owner_title), owner_title); this.bus = bus; _orders_model = orders_model; _create_predicate_vm = create_predicate_vm; _create_column_predicate_vm = create_column_predicate_vm; + _create_settings_obj = create_settings_obj; + _column_flags = column_flags ?? new Dictionary(); _font_family = backing(nameof(font_family), TextUtils.StandardFontFamily); _font_size = backing(nameof(font_size), FontSizes.Small); _font_weight = backing(nameof(font_weight), FontWeights.Normal); @@ -81,7 +112,7 @@ namespace mbts.spark { new DialogSkirtItem("Apply", new DelegateCommand(() => committed?.Invoke())) }; - public (GridSettings grid, IEnumerable> predicate_animations_data) create() { + public TSettingsData get_result() { var animation_map = predicate_animations .Select(p => p.create()) .GroupBy(d => d.predicate) @@ -95,22 +126,22 @@ namespace mbts.spark { foreach (IGrouping g in animations) animation_map.AddOrUpdate(g.Key, p => animation_tuple(c: g), (p, t) => t.cell_animations.AddRange(g)); } - return (new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), col_settings), + return _create_settings_obj(new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), col_settings), animation_map.Select(kv => PredicateAnimations.create_data(kv.Key, kv.Value.cell_animations, kv.Value.row_animations, kv.Value.sounds))); // (List row_animations, List sounds, List cell_animations) animation_tuple(IEnumerable r = null, IEnumerable s = null, IGrouping c = null) => (r?.ToList() ?? new List(), s?.ToList() ?? new List(), c?.ToList() ?? new List()); } - public void load_from((GridSettings grid, IEnumerable> predicate_animations_data) value) { - _font_family.set(new FontFamily(value.grid.font_family)); - _font_weight.set(value.grid.font_weight); - font_color.load_from(value.grid.font_color.rgb_solid_brush()); - selection_highlight.load_from(value.grid.selection_highlight.to_brush()); - _font_size.set(value.grid.font_size); + public void load(TSettingsData sd) { + _font_family.set(new FontFamily(sd.general.font_family)); + _font_weight.set(sd.general.font_weight); + font_color.load_from(sd.general.font_color.rgb_solid_brush()); + selection_highlight.load_from(sd.general.selection_highlight.to_brush()); + _font_size.set(sd.general.font_size); var col_animations = new Dictionary>>(); predicate_animations.Clear(); - foreach (var pa in value.predicate_animations_data) { + foreach (var pa in sd.predicate_animations) { if (pa.row_animations.Count > 0 || pa.sound_notifications.Count > 0) { var vm = new PredicateRowAnimationVM(_create_predicate_vm()); vm.load_from((pa.predicate_builder, pa.row_animations, pa.sound_notifications)); @@ -123,9 +154,14 @@ namespace mbts.spark { } } var curr_col_id = current_column?.id; - columns.SetItems(value.grid.columns.Select(col => { + columns.SetItems(sd.general.columns.Select(col => { var vm = new ColumnVM(this); vm.load_from((col, col_animations.GetValueOrDefault(col.id)?.ToFlatLookup(kv => kv.Key, kv => kv.Value))); + // reapply our favorite column flags + // this comes in handy when performing Load From Factory - the editor will contain + // a consistent view of what can be overridden and what cannot + if (_column_flags.TryGetValue(vm.id, out GridColumnSettingsFeatureFlags flags)) + vm.apply_flags(flags); return vm; })); if (curr_col_id == null || !columns.TryGetFirst(c => c.id == curr_col_id, out IColumnVM curr_column)) diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/ImmutableColumnSettingsStore.cs b/src/client/spark/view/Controls/DIYGrid/Settings/ImmutableColumnSettingsStore.cs new file mode 100644 index 000000000..6e7b4e4f6 --- /dev/null +++ b/src/client/spark/view/Controls/DIYGrid/Settings/ImmutableColumnSettingsStore.cs @@ -0,0 +1,72 @@ +using bts.proto; +using mbts.spark; +using mbts.ui.settings; +using System.Collections.Generic; + +namespace mbts.ui { + abstract class ImmutableColumnSettingStore { + public readonly GridColumnSettingsFeatureFlags flags; + + protected ImmutableColumnSettingStore(GridColumnSettingsFeatureFlags flags) => + this.flags = flags; + + public static ImmutableColumnSettingStore create(GridColumnSettingsFeatureFlags flags) { + var stores_per_flag = new List(); + if ((flags & GridColumnSettingsFeatureFlags.ImmutableFontColor) != 0) { + stores_per_flag.Add(new ImmutableFontColorOverrideStore()); + } + if ((flags & GridColumnSettingsFeatureFlags.ImmutableFontWeight) != 0) { + stores_per_flag.Add(new ImmutableFontWeightOverrideStore()); + } + return new CompositeImmutableSettingsStore(stores_per_flag, flags); + } + + public abstract GridColumnSettings apply(GridColumnSettings settings); + public abstract void store(GridColumnSettings settings); + + class ImmutableFontColorOverrideStore : ImmutableColumnSettingStore { + NullableColorProto _value; + + public ImmutableFontColorOverrideStore() + : base(GridColumnSettingsFeatureFlags.ImmutableFontColor) { } + + public override GridColumnSettings apply(GridColumnSettings settings) => + settings.with(font_color_override: _value); + + public override void store(GridColumnSettings settings) => + _value = new NullableColorProto(settings.font_color_override); + } + + class ImmutableFontWeightOverrideStore : ImmutableColumnSettingStore { + NullableFontWeightProto _value; + + public ImmutableFontWeightOverrideStore() + : base(GridColumnSettingsFeatureFlags.ImmutableFontWeight) { } + + public override GridColumnSettings apply(GridColumnSettings settings) => + settings.with(font_weight_override: _value); + + public override void store(GridColumnSettings settings) => + _value = new NullableFontWeightProto(settings.font_weight_override); + } + + class CompositeImmutableSettingsStore : ImmutableColumnSettingStore { + readonly IReadOnlyList _stores; + public CompositeImmutableSettingsStore(IReadOnlyList stores, GridColumnSettingsFeatureFlags flags) + : base(flags) => _stores = stores; + + public override GridColumnSettings apply(GridColumnSettings settings) { + foreach (var s in _stores) { + settings = s.apply(settings); + } + return settings; + } + + public override void store(GridColumnSettings settings) { + foreach (var s in _stores) { + s.store(settings); + } + } + } + } +} diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs index 4072621bd..e1b1d22bc 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsManager.cs @@ -1,13 +1,10 @@ -using bts.proto; -using mbts.persist; -using mbts.spark; +using mbts.spark; using mbts.ui.settings; using mbts.ui.shell; using mbts.utils; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; using System.Linq; using System.Reactive.Linq; using System.Windows; @@ -120,70 +117,5 @@ namespace mbts.ui { string id_key(string id) => id.ToLower(); } } - - abstract class ImmutableColumnSettingStore { - public readonly GridColumnSettingsFeatureFlags flags; - - protected ImmutableColumnSettingStore(GridColumnSettingsFeatureFlags flags) => - this.flags = flags; - - public static ImmutableColumnSettingStore create(GridColumnSettingsFeatureFlags flags) { - var stores_per_flag = new List(); - if ((flags & GridColumnSettingsFeatureFlags.ImmutableFontColor) != 0) { - stores_per_flag.Add(new ImmutableFontColorOverrideStore()); - } if ((flags & GridColumnSettingsFeatureFlags.ImmutableFontWeight) != 0) { - stores_per_flag.Add(new ImmutableFontWeightOverrideStore()); - } - return new CompositeImmutableSettingsStore(stores_per_flag, flags); - } - - public abstract GridColumnSettings apply(GridColumnSettings settings); - public abstract void store(GridColumnSettings settings); - - class ImmutableFontColorOverrideStore : ImmutableColumnSettingStore { - NullableColorProto _value; - - public ImmutableFontColorOverrideStore() - : base(GridColumnSettingsFeatureFlags.ImmutableFontColor) { } - - public override GridColumnSettings apply(GridColumnSettings settings) => - settings.with(font_color_override: _value); - - public override void store(GridColumnSettings settings) => - _value = new NullableColorProto(settings.font_color_override); - } - - class ImmutableFontWeightOverrideStore : ImmutableColumnSettingStore { - NullableFontWeightProto _value; - - public ImmutableFontWeightOverrideStore() - : base(GridColumnSettingsFeatureFlags.ImmutableFontWeight) { } - - public override GridColumnSettings apply(GridColumnSettings settings) => - settings.with(font_weight_override: _value); - - public override void store(GridColumnSettings settings) => - _value = new NullableFontWeightProto(settings.font_weight_override); - } - - class CompositeImmutableSettingsStore : ImmutableColumnSettingStore { - readonly IReadOnlyList _stores; - public CompositeImmutableSettingsStore(IReadOnlyList stores, GridColumnSettingsFeatureFlags flags) - : base(flags) => _stores = stores; - - public override GridColumnSettings apply(GridColumnSettings settings) { - foreach (var s in _stores) { - settings = s.apply(settings); - } - return settings; - } - - public override void store(GridColumnSettings settings) { - foreach (var s in _stores) { - s.store(settings); - } - } - } - } } } diff --git a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs index cf105a417..5c5867fb3 100644 --- a/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs +++ b/src/client/spark/view/Spark/Utilities/WindowSettingsDialogVM.cs @@ -15,6 +15,7 @@ namespace mbts.spark { readonly Property _use_default; readonly DialogSkirtItem _ok_item, _apply_item, _cancel_item, _apply_to_defaults_item; readonly DelegateProperty> _left_skirt_items; + readonly List _resources = new List(2); // initialize with 2 capacity bc the only potential disposables are the instance/default vms public WindowSettingsDialogVM(IWindowSettingsManager settings_manager, Func get_factory_settings, string settings_file_ext, Func vm_factory) { // by default, each new window will use their own instance settings @@ -51,7 +52,11 @@ namespace mbts.spark { apply(); close(); }), ui.spark.LadderSettingsDialog.ResetToFactoryKey); - + // maybe dispose vms + if (instance_vm is IDisposable dispose_ivm) + _resources.Add(dispose_ivm); + if (default_vm is IDisposable dispose_dvm) + _resources.Add(dispose_dvm); // local methods void apply() { if (!use_default) { @@ -90,5 +95,8 @@ namespace mbts.spark { get => _use_default.Value; set => _use_default.Value = value; } + + public void Dispose() => + Disposable.dispose(_resources); } } \ No newline at end of file From 746e858f765b05c9bd548b2ce563fecae134aa50 Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 10 Oct 2018 09:16:24 -0400 Subject: [PATCH 178/203] SPARK-1659 Typo in simulator implieds --- src/server/sparkd/exchange_sim/ExchangeSim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/sparkd/exchange_sim/ExchangeSim.cpp b/src/server/sparkd/exchange_sim/ExchangeSim.cpp index 549d0cd3a..4678da3b6 100644 --- a/src/server/sparkd/exchange_sim/ExchangeSim.cpp +++ b/src/server/sparkd/exchange_sim/ExchangeSim.cpp @@ -89,7 +89,7 @@ struct MDPlayer : md::IRelayWriter }} {size_t x = 0; for (const auto& b : cr.implied().asks()) { - implied_bids[x] = md::PriceSize({b.mantissa(), (int8_t)b.exponent()}, b.size()); + implied_asks[x] = md::PriceSize({b.mantissa(), (int8_t)b.exponent()}, b.size()); x += 1; has_implied = true; }} From a174336baf1622f52b0359164baf4b659215139c Mon Sep 17 00:00:00 2001 From: Eric Harding Date: Wed, 10 Oct 2018 11:22:51 -0400 Subject: [PATCH 179/203] MDSource should always use the complete book --- src/lib/orderman/actors/MDSource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/orderman/actors/MDSource.h b/src/lib/orderman/actors/MDSource.h index 28b832254..519b54c8b 100644 --- a/src/lib/orderman/actors/MDSource.h +++ b/src/lib/orderman/actors/MDSource.h @@ -82,7 +82,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor { auto start = Timer::now(); for (auto &h : _handles) { auto &handle = h.handle; - if (handle.get_direct_book(h.last, std::tuple_size(), + if (handle.get_book(h.last, std::tuple_size(), &book.bid()[0], &book.ask()[0]) == md::HandleReadResult::SUCCESS) { book._security_key = SecurityKey(handle.key()); From d965674ff50b7debc9d3df7775f2751099654314 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Wed, 10 Oct 2018 15:52:18 -0500 Subject: [PATCH 180/203] ContentTemplateSelector is now used in SimpleGridSettingsDialog; Ladder now uses this instead of LadderSettingsDialog --- .../Settings/SimpleGridSettingsDialog.xaml | 22 ++++++++++++++----- .../Settings/SimpleGridSettingsDialog.xaml.cs | 6 +++++ .../DIYGrid/Settings/SimpleGridSettingsVM.cs | 4 +++- .../view/Spark/Ladder/LadderSettingsVM.cs | 4 +++- .../spark/view/Spark/Ladder/LadderVM.cs | 2 +- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml index 3a6d66da0..bc3c482d0 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml +++ b/src/client/spark/view/Controls/DIYGrid/Settings/SimpleGridSettingsDialog.xaml @@ -65,6 +65,18 @@ + + + + + + + + + + + + @@ -74,11 +86,11 @@ - , IWindowSettingsVM { + partial class SimpleGridSettingsVM : DisposableViewModelBase, IValueFactory, IWindowSettingsVM, ITemplateSelectorKey { readonly Property _current_column; readonly Property _font_family; readonly Property _font_weight; @@ -69,6 +69,8 @@ namespace mbts.spark { new DialogSkirtItem("Apply", new DelegateCommand(() => committed?.Invoke())) }; + public string key => ui.spark.GridOrLadderSettingsSelectorKeys.SimpleGrid; + public GridSettings create() => new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), columns.Select(c => c.create())); diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs index ca47b2d7c..12fbe9c70 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs @@ -14,7 +14,7 @@ using System.Windows.Media; using TextUtils = mbts.ui.utility.text.TextUtils; namespace mbts.spark { - class LadderSettingsVM : ViewModelBase, IWindowSettingsVM { + class LadderSettingsVM : ViewModelBase, IWindowSettingsVM, ITemplateSelectorKey { readonly Property _badge_style; readonly Property _size_display; readonly Property _alternate_order_size; @@ -117,6 +117,8 @@ namespace mbts.spark { } } + public string key => GridOrLadderSettingsSelectorKeys.Ladder; + public void load(LadderSettings settings) { _current = settings; badge_style = settings.badge_style; diff --git a/src/client/spark/view/Spark/Ladder/LadderVM.cs b/src/client/spark/view/Spark/Ladder/LadderVM.cs index 61048c7f9..905b5c072 100644 --- a/src/client/spark/view/Spark/Ladder/LadderVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderVM.cs @@ -72,7 +72,7 @@ namespace mbts.spark { svm.load(s); return svm; }); - var dialog = new ui.spark.LadderSettingsDialog { + var dialog = new ui.spark.SimpleGridSettingsDialog { DataContext = vm, Title = $"Ladder Settings - '{security}'" }; From 2d3e942f935847792ab9dcdba4a7bab5055b1445 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 10 Oct 2018 16:43:24 -0500 Subject: [PATCH 181/203] fix OrderBook grid settings use_defaults behavior --- .../Modules/Trading/OrderBook/OrderBookVM.cs | 10 ++++- .../OrderBook/OrderBookWindowFactory.cs | 23 +++++++++-- .../DIYGrid/Settings/GridSettingsManager.cs | 39 ++++++++++++------- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookVM.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookVM.cs index ff18e3298..fab60913a 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookVM.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookVM.cs @@ -31,6 +31,7 @@ namespace mbts.spark { readonly Property _current_row; readonly Subject<(IEnumerable added, IEnumerable removed)> _order_securities_subject = new Subject<(IEnumerable added, IEnumerable removed)>(); + readonly IWindowSettingsManager _settings_manager; OrderBookStartupData _state; Dictionary _order_rows_cache = new Dictionary(); @@ -41,6 +42,7 @@ namespace mbts.spark { SetMap _security_orders = new SetMap(); public OrderBookVM(IObservable owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager settings_manager) { + _settings_manager = settings_manager; columns = new OrderBookColumnCollection(bus, model, _order_securities_subject); rows_view = CollectionViewSource.GetDefaultView(_order_rows); rows_view.Filter = obj => obj is OrderBookRowVM vm && vm.is_visible && vm.passes_filter; @@ -66,6 +68,7 @@ namespace mbts.spark { }); grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, _order_rows, rows_view, columns); + grid_settings_manager.load_from(OrderBookStartupData.DefaultGrid); _live_sorting_columns = new LiveSortingColumKeys(grid_settings_manager.live_sorting_props); SetMap missing_security_orders = new SetMap(); @@ -232,14 +235,17 @@ namespace mbts.spark { } public OrderBookStartupData create() { + if (_settings_manager.instance_settings == null) + return new OrderBookStartupData(columns.create(), use_defaults: true); var gs = grid_settings_manager.create(); - return new OrderBookStartupData(columns.create(), gs.general, gs.predicate_animations.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))); + return new OrderBookStartupData(columns.create(), gs.general, gs.predicate_animations, use_defaults: false); } public void load_from(OrderBookStartupData state) { if (state == null) return; _state = state; - grid_settings_manager.load_from(state.grid_settings_wrapper()); + var gs = state.grid_settings_wrapper(); + if (gs != null) grid_settings_manager.load_from(gs); columns.load_from(state.filters); } diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs index 5fe8912a8..3ab5b39e1 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs @@ -12,6 +12,7 @@ using mbts.ui.settings; using ProtoBuf; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Reactive.Linq; using System.Threading.Tasks; using System.Windows; @@ -35,7 +36,7 @@ namespace bts.shell { } public override Task load(OrderBookStartupData state) { - var settings_manager = new WindowSettingsManager(_settings_hub, new OrderBookGridSettings(state.grid, state.predicate_animations), + var settings_manager = new WindowSettingsManager(_settings_hub, state.grid_settings_wrapper(), us => us.order_book_grid, (us, s) => us.with(order_book_grid: s)); var w = new OrderBookWindow(_model, this, _bus, _notification_service, state, SparkCommands.ShowOrders.icon, settings_manager.as_non_shared()); return Task.FromResult(w); @@ -53,28 +54,42 @@ namespace bts.shell { [ProtoMember(10)] public FilterStates filters; [ProtoMember(15)] + [Obsolete] public GridSettings grid; [ProtoMember(20)] + [Obsolete] List _predicate_animations; + [ProtoMember(25)] + public bool use_defaults; public OrderBookStartupData() { } - public OrderBookStartupData(FilterStates filters, GridSettings grid = default, IEnumerable predicate_animations = null) { + public OrderBookStartupData(FilterStates filters, GridSettings grid = default, IEnumerable predicate_animations = null, bool use_defaults = false) { this.filters = filters; + this.use_defaults = use_defaults; +#pragma warning disable CS0612 // Type or member is obsolete this.grid = grid; _predicate_animations = new List(); if (predicate_animations != null) _predicate_animations.AddRange(predicate_animations); +#pragma warning restore CS0612 // Type or member is obsolete } +#pragma warning disable CS0612 // Type or member is obsolete + [Obsolete] public IReadOnlyList predicate_animations => _predicate_animations ?? (_predicate_animations = new List()); - public OrderBookGridSettings grid_settings_wrapper() => new OrderBookGridSettings(grid, predicate_animations); + public OrderBookGridSettings grid_settings_wrapper() => use_defaults ? null : new OrderBookGridSettings(grid, predicate_animations); +#pragma warning restore CS0612 // Type or member is obsolete public static OrderBookStartupData Default => new OrderBookStartupData( new FilterStates { statuses = new[] { OrderStatus.Working } - }, GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion)); + }, _default_general_grid, use_defaults: true); + + public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid); + + static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion); static class DefaultColumns { public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security); diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsManager.cs b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsManager.cs index f77686dbf..559efcc80 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsManager.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsManager.cs @@ -1,4 +1,5 @@ -using bts.api; +using bts; +using bts.api; using bts.shell; using mbts.collections; using mbts.spark; @@ -125,21 +126,28 @@ namespace mbts.ui { public TSettingsData create() { // IMPORTANT: don't overwrite current sorting - _grid_settings.set(grid_settings.with(_columns.Select(c => c.create()), _rows_view.get_sorting_criteria(_columns))); + _grid_settings.set(grid_settings.with(_columns.Select(c => { + var col_settings = c.create(); + if (_column_flags.TryGetValue(c.id, out ImmutableColumnSettingStore store)) + col_settings = store.apply(col_settings); + return col_settings; + }), _rows_view.get_sorting_criteria(_columns))); return _create_settings_obj(grid_settings, _predicate_animations_data); } public void load_from(TSettingsData value) => _load_from_core(value, true); - void _load_from_core(TSettingsData value, bool reset_immutable_flag_stores) { + void _load_from_core(TSettingsData value, bool reset_immutables) { Disposable.dispose(_notification_subs); var grid = value.general ?? GridSettings.Default; _grid_settings.set(grid); foreach (ColumnBase col in _columns) col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight); + if (reset_immutables) { #pragma warning disable CS0612 // Type or member is obsolete - _rows_view.set_sort_descriptions(grid.sorting_criterion, _columns); + _rows_view.set_sort_descriptions(grid.sorting_criterion, _columns); #pragma warning restore CS0612 // Type or member is obsolete + } _predicate_animations_data = value.predicate_animations; _animation_defs.clear(); foreach (var pa in _predicate_animations_data) { @@ -160,7 +168,7 @@ namespace mbts.ui { // ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings // don't add insult to injury by crashing ** if (grid_settings.columns != null && _columns != null) { - if (reset_immutable_flag_stores) { + if (reset_immutables) { foreach (var kv in _column_flags) { if (grid.columns.TryGetFirst(c => c.id == kv.Key, out GridColumnSettings cs)) kv.Value.store(cs); @@ -187,17 +195,22 @@ namespace mbts.ui { } void IObserver.OnNext(TSettingsData sd) { - var grid = sd.general; - if (grid.columns != null) { - var cols_with_applied_flags = grid.columns.ToList(); - for (int i = 0; i < cols_with_applied_flags.Count; i++) { - if (_column_flags.TryGetValue(cols_with_applied_flags[i].id, out ImmutableColumnSettingStore store)) { - cols_with_applied_flags[i] = store.apply(cols_with_applied_flags[i]); + if (sd == null) return; + try { + var grid = sd.general; + if (grid.columns != null) { + var cols_with_applied_flags = grid.columns.ToList(); + for (int i = 0; i < cols_with_applied_flags.Count; i++) { + if (_column_flags.TryGetValue(cols_with_applied_flags[i].id, out ImmutableColumnSettingStore store)) { + cols_with_applied_flags[i] = store.apply(cols_with_applied_flags[i]); + } } + sd = _create_settings_obj(grid.with(cols_with_applied_flags), sd.predicate_animations); } - sd = _create_settings_obj(grid.with(cols_with_applied_flags), sd.predicate_animations); + _load_from_core(sd, false); + } catch (Exception ex) { + Log.Ui.error($"Error loading GridSettings (as observer): {ex}"); } - _load_from_core(sd, false); } void IObserver.OnError(Exception error) { } void IObserver.OnCompleted() { } From f97ccf249f93bfa3ba0a88a563cc589ea706bff5 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 11 Oct 2018 09:33:46 -0500 Subject: [PATCH 182/203] GridSettingsVM is now ipmlements ITemplateSelectorKey so that we can actually see the settings --- .../spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs index 4e280d9e6..1666e0fc0 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridSettingsVM.cs @@ -44,7 +44,7 @@ namespace mbts.spark { EqualityComparer.safe_aggregate_hash(predicate_animations_data)); } - partial class GridSettingsVM : DisposableViewModelBase, IRequestViewClose, IWindowSettingsVM + partial class GridSettingsVM : DisposableViewModelBase, IRequestViewClose, IWindowSettingsVM, ITemplateSelectorKey where TPredicateBuilder : IPredicateBuilder, new() where TSettingsData :class, IGridSettingsWithNotifications { // @@ -112,6 +112,8 @@ namespace mbts.spark { new DialogSkirtItem("Apply", new DelegateCommand(() => committed?.Invoke())) }; + public string key => ui.spark.GridOrLadderSettingsSelectorKeys.SimpleGrid; + public TSettingsData get_result() { var animation_map = predicate_animations .Select(p => p.create()) From c90752b4f29fd7cf261d89833c7db0eda969a048 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Thu, 11 Oct 2018 11:49:43 -0500 Subject: [PATCH 183/203] OrderBook - small fixes for grid-settings conversion --- .../spark/core/ShellInterface/UserSettings/UserSettings.cs | 2 +- .../view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs index 3f08f80d2..73c79bb5a 100644 --- a/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs +++ b/src/client/spark/core/ShellInterface/UserSettings/UserSettings.cs @@ -17,7 +17,7 @@ namespace mbts.spark { _fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings, _time_and_sales_grid = bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultGridSettings, _synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings, - _order_book_grid = bts.shell.OrderBookWindowFactory.OrderBookStartupData.Default.grid_settings_wrapper(), + _order_book_grid = bts.shell.OrderBookWindowFactory.OrderBookStartupData.DefaultGrid }; [ProtoMember(1)] diff --git a/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs b/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs index 99e12a66f..8e042dcc6 100644 --- a/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs +++ b/src/client/spark/view/Controls/DIYGrid/Settings/GridColumnSettingsVM.cs @@ -99,6 +99,7 @@ namespace mbts.spark { _horizontal_alignment.set(_settings.horizontal_alignment); _is_visible.set(_settings.is_visible); _is_font_weight_overridden.set(_settings.font_weight_override.HasValue); + _is_font_color_overridden.set(_settings.font_color_override.HasValue); if (is_font_weight_overridden) { _font_weight_override.set(_settings.font_weight_override.Value); } From 4107ac093bada851ee61b0b329c0190cfa9ebf7f Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 11 Oct 2018 14:00:17 -0500 Subject: [PATCH 184/203] OrderBook spit and polish --- .../Modules/Trading/OrderBook/OrderBookColumns.cs | 11 ++++++----- .../Modules/Trading/OrderBook/OrderBookWindow.cs | 7 +++++-- .../Trading/OrderBook/OrderBookWindowFactory.cs | 10 +++++----- .../spark/view/Controls/DIYGrid/Columns/ColumnBase.cs | 9 +++++++-- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookColumns.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookColumns.cs index f605972ea..61d980532 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookColumns.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookColumns.cs @@ -3,6 +3,7 @@ using bts.api.orderman; using bts.utils; using mbts.spark.filters; using mbts.ui; +using mbts.ui.controls; using mbts.ui.converters; using mbts.ui.utility; using mbts.ui.utility.text; @@ -208,7 +209,7 @@ namespace mbts.spark { "Parent Key", nameof(OrderBookRowVM.parent_order_key), r => r.parent_order_key.store, - 80, + DefaultColumnWidths.ParentKey, new RegexFilterVM( r => r.parent_order_key.text, value_path(nameof(OrderBookRowVM.parent_order_key))), @@ -391,7 +392,7 @@ namespace mbts.spark { public class FilledColumn : OrderbookColumnBase, IRenderColumn { public FilledColumn(IDataGridColumnPersistentFilter filter) - : base(Columns.Filled, "Filled Qty", r => r.filled_quantity.store, nameof(OrderBookRowVM.filled_quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax) { + : base(Columns.Filled, "Filled Qty", r => r.filled_quantity.store, nameof(OrderBookRowVM.filled_quantity), DefaultColumnWidths.Filled, filter, DataGridFilterKnownTemplates.UIntMinMax) { sort_member_path = nameof(OrderBookRowVM.filled_quantity); } @@ -411,7 +412,7 @@ namespace mbts.spark { public class AvgFilledColumn : OrderbookColumnBase, IRenderColumn { public AvgFilledColumn(IDataGridColumnPersistentFilter filter) - : base(Columns.AvgFilled, "Avg Filled", r => r.avg_price.store, nameof(OrderBookRowVM.avg_price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { } + : base(Columns.AvgFilled, "Avg Filled", r => r.avg_price.store, nameof(OrderBookRowVM.avg_price), DefaultColumnWidths.AveragePrice, filter, DataGridFilterKnownTemplates.DoubleMinMax) { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) { CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.avg_price.text, @@ -421,7 +422,7 @@ namespace mbts.spark { public class LastFilledColumn : OrderbookColumnBase, IRenderColumn { public LastFilledColumn(IDataGridColumnPersistentFilter filter) - : base(Columns.LastPrice, "Last Filled", r => r.last_price.store, nameof(OrderBookRowVM.last_price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { } + : base(Columns.LastPrice, "Last Filled", r => r.last_price.store, nameof(OrderBookRowVM.last_price), DefaultColumnWidths.LastPrice, filter, DataGridFilterKnownTemplates.DoubleMinMax) { } public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) => CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.last_price.text, @@ -478,7 +479,7 @@ namespace mbts.spark { id = key.ToString(); this.filter = filter; Header = "Last Message"; - Width = 80; + Width = DIYGrid.DefaultColumnWidths.LastMessage; sort_member_path = nameof(OrderBookRowVM.last_msg); tooltip_binding = new Binding(nameof(OrderBookRowVM.last_msg)); } diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindow.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindow.cs index ea4c8f1dc..65474795d 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindow.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindow.cs @@ -56,8 +56,11 @@ namespace mbts.spark { public override string window_type => _window_type; public override UIElement Content => _view; public override IEnumerable Commands => _commands; - public override double MinWidth => 1577; - + public override double MinWidth => + OrderBookWindowFactory.OrderBookStartupData.DefaultGrid.general.columns.Sum(gcs => gcs.width) + + SPARK_MENU_COLLAPSED_WIDTH + + LAUNCHER_BUTTON_BORDER_WIDTH + + EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH; public string thread_id => ORDERBOOK_THREAD_ID; public override void Dispose() { diff --git a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs index 3ab5b39e1..78d8978fd 100644 --- a/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs +++ b/src/client/spark/Modules/Trading/OrderBook/OrderBookWindowFactory.cs @@ -105,14 +105,14 @@ namespace bts.shell { public static GridColumnSettings side => new GridColumnSettings(Columns.Side.ToString(), "B/S", width: DIYGrid.DefaultColumnWidths.Side); public static GridColumnSettings quantity => new GridColumnSettings(Columns.Size.ToString(), "Qty", width: DIYGrid.DefaultColumnWidths.Size, horizontal_alignment: HorizontalAlignment.Right); public static GridColumnSettings price => new GridColumnSettings(Columns.Price.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right); - public static GridColumnSettings filled_quantity => new GridColumnSettings(Columns.Filled.ToString(), "Filled Qty", width: DIYGrid.DefaultColumnWidths.Size, horizontal_alignment: HorizontalAlignment.Right); - public static GridColumnSettings avg_price => new GridColumnSettings(Columns.AvgFilled.ToString(), "Avg Filled", width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right); - public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right); + public static GridColumnSettings filled_quantity => new GridColumnSettings(Columns.Filled.ToString(), "Filled Qty", width: DIYGrid.DefaultColumnWidths.Filled, horizontal_alignment: HorizontalAlignment.Right); + public static GridColumnSettings avg_price => new GridColumnSettings(Columns.AvgFilled.ToString(), "Avg Filled", width: DIYGrid.DefaultColumnWidths.AveragePrice, horizontal_alignment: HorizontalAlignment.Right); + public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right); public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80); - public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: 80); + public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: DIYGrid.DefaultColumnWidths.LastMessage); public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, width: DIYGrid.DefaultColumnWidths.SecurityKey); public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey); - public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.OrderKey); + public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey); public static IEnumerable sorting_criterion { get { diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index b7928ab91..03d4b4cdc 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -23,14 +23,17 @@ namespace mbts.ui.controls { OrderType = 88, MViewPrice = 56, Price = 68, + AveragePrice = 76, + LastPrice = 78, Settlement = 82, Security = 80, SecurityKey = 90, SecurityType = 96, Side = 47, Size = 48, + Filled = 56, Symbol = 58, - Time = 100, + Time = 112, TimeInForce = 47, TradeKey = 76, User = 53, @@ -43,7 +46,9 @@ namespace mbts.ui.controls { Start_Net_Pos = 100, Total_Net_Pos = 100, PnL_Generic = 80, - Working = 72; + Working = 72, + LastMessage = 100, + ParentKey = 100; } public static class DefaultColumnHeaders { From 143f660f371bf617a4a500074c6970c87d239e55 Mon Sep 17 00:00:00 2001 From: Jake Norbut Date: Thu, 11 Oct 2018 14:58:40 -0500 Subject: [PATCH 185/203] adjusting Symbol column default width --- src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs index 03d4b4cdc..8b4491cb8 100644 --- a/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs +++ b/src/client/spark/view/Controls/DIYGrid/Columns/ColumnBase.cs @@ -32,7 +32,7 @@ namespace mbts.ui.controls { Side = 47, Size = 48, Filled = 56, - Symbol = 58, + Symbol = 78, Time = 112, TimeInForce = 47, TradeKey = 76, From e85d7b5d0cef52e98879e49eb03554eba0b18e8d Mon Sep 17 00:00:00 2001 From: Taha Afzal Date: Thu, 11 Oct 2018 17:19:48 -0400 Subject: [PATCH 186/203] 1.IntPrice comparison can now compare IntPrices of different exponents 2.copy_book for direct with holes fixed --- src/lib/md/IntPrice.h | 5 +++- .../spreader_orders/spreader/SpreaderUtils.h | 29 +++++++++++++------ .../spreader_orders/tests/spreader_tests.cpp | 24 +++++++++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/lib/md/IntPrice.h b/src/lib/md/IntPrice.h index 725c8a6a0..c3c6c3573 100644 --- a/src/lib/md/IntPrice.h +++ b/src/lib/md/IntPrice.h @@ -39,7 +39,10 @@ class IntPrice { // logically 100*pow10(1) == 10*pow10(2) // HOWEVER, once converted to double there are subtelties that can make them not equal so // this function is conservative - return _mantissa == other._mantissa && _exponent == other._exponent; + if(_exponent == other._exponent) + return _mantissa == other._mantissa; + else + return price()==other.price(); } bool operator!=(const IntPrice& other) const { return !(*this == other); diff --git a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h index 2374c3ea0..2c28b2e0b 100644 --- a/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h +++ b/src/lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h @@ -160,14 +160,24 @@ public: if(md_type==bts::spark::LegSpreaderParams::DIRECT){ // since the "size()" in Book is the merged size // we put the direct in the size - for(size_t i=0;i0){ + target.bid()[target_i]=md::PriceSize(b.int_price(),b.direct_size()); + target_i++; + } } - for(size_t i=0;i0){ + target.ask()[target_i]=md::PriceSize(a.int_price(),a.direct_size()); + target_i++; + } } + empty_out_book(target.ask(),target_i,target.n_levels()); } else if(md_type==bts::spark::LegSpreaderParams::MERGED){ target.bid()=source.bid(); @@ -175,8 +185,8 @@ public: } if(leg_calc==bts::spark::LegSpreaderParams::NET_CHANGE){ if(settlement_price.is_nan()){ - empty_out_book(target.bid(),target.n_levels()); - empty_out_book(target.ask(),target.n_levels()); + empty_out_book(target.bid(),0,target.n_levels()); + empty_out_book(target.ask(),0,target.n_levels()); } else{ offset_book(target.bid(), target.n_levels(),settlement_price); @@ -196,9 +206,10 @@ public: } } void static empty_out_book (BookSide& book_side, + size_t start_index, size_t n_levels){ md::PriceSize empty_ps; - for(size_t i=0;i(1999.0), + bts::spark::LegSpreaderParams::DIRECT, + bts::spark::LegSpreaderParams::BOOK, + out_book); + + validate_book(out_book, + key, + api::Side::SIDE_BUY, + 0.01, + new double[5] {2000.01, 2000.04, 2000.10, os::nan(), os::nan()}, + new uint16_t[5] {1, 14, 20, 0, 0}, + true); + } SECTION ("direct, net_change"){ SecurityKey key(13579); Book in_book (key); From 4360a1154c02aee51486676dee1e4346e6273a4d Mon Sep 17 00:00:00 2001 From: John Colvin Date: Sun, 14 Oct 2018 16:17:38 -0500 Subject: [PATCH 187/203] Ladder - market size display settings --- .../Market/MarketView/MarketViewRow.cs | 5 +- .../Modules/Trading/LadderWindowFactory.cs | 5 + .../spark/core/Utility/CollectionHelper.cs | 7 + .../spark/view/Spark/Ladder/LadderGridHost.cs | 9 +- .../spark/view/Spark/Ladder/LadderSettings.cs | 8 +- .../view/Spark/Ladder/LadderSettingsVM.cs | 15 +- .../view/Spark/Ladder/LadderSettingsView.xaml | 100 +++++- .../spark/view/Spark/Ladder/LaderRender.cs | 5 +- .../spark/view/Utility/MDDisplayHelper.cs | 288 +++++++++++++++++- 9 files changed, 397 insertions(+), 45 deletions(-) diff --git a/src/client/spark/Modules/Market/MarketView/MarketViewRow.cs b/src/client/spark/Modules/Market/MarketView/MarketViewRow.cs index 2c87ccf88..8c604dca4 100644 --- a/src/client/spark/Modules/Market/MarketView/MarketViewRow.cs +++ b/src/client/spark/Modules/Market/MarketView/MarketViewRow.cs @@ -34,6 +34,7 @@ namespace mbts.spark { readonly CellProperty _working_best_bid, _working_best_ask; readonly CellProperty _working_bids_text, _working_asks_text; readonly MyOrderSummarizer _order_summarizer = new MyOrderSummarizer(); + readonly MDDisplayHelper.SizeFormatter _size_formatter = MDDisplayHelper.get_formatter(MarketSizeDisplay.get_default()); uint _bid_direct_size, _bid_merged_size, _ask_direct_size, _ask_merged_size; string _bid_merged_size_text, _ask_merged_size_text; ulong _bbo_ref, _last_ref; @@ -102,12 +103,12 @@ namespace mbts.spark { public uint bid_direct_size => _bid_direct_size; public uint bid_implied_size => _bid_merged_size - _bid_direct_size; public uint bid_merged_size => _bid_merged_size; - public string bid_merged_size_text => _bid_merged_size_text ?? (_bid_merged_size_text = MDDisplayHelper.fmt_merged_size(_bid_merged_size, bid_implied_size)); + public string bid_merged_size_text => _bid_merged_size_text ?? (_bid_merged_size_text = _size_formatter(_bid_merged_size, bid_implied_size)); public double ask_price => _ask_price.Value; public uint ask_direct_size => _ask_direct_size; public uint ask_implied_size => _ask_merged_size - _ask_direct_size; public uint ask_merged_size => _ask_merged_size; - public string ask_merged_size_text => _ask_merged_size_text ?? (_ask_merged_size_text = MDDisplayHelper.fmt_merged_size(_ask_merged_size, ask_implied_size)); + public string ask_merged_size_text => _ask_merged_size_text ?? (_ask_merged_size_text = _size_formatter(_ask_merged_size, ask_implied_size)); public ulong volume => _volume.value; public double settlement => _settlement.Value; public double day_change => _day_change.Value; diff --git a/src/client/spark/Modules/Trading/LadderWindowFactory.cs b/src/client/spark/Modules/Trading/LadderWindowFactory.cs index 531b50f59..ff6a809de 100644 --- a/src/client/spark/Modules/Trading/LadderWindowFactory.cs +++ b/src/client/spark/Modules/Trading/LadderWindowFactory.cs @@ -4,6 +4,7 @@ using mbts.config; using mbts.persist; using mbts.spark; using mbts.ui; +using mbts.ui.spark; using ProtoBuf; using System; using System.Collections.Generic; @@ -89,6 +90,10 @@ namespace bts.shell { s.session_range_fill = LadderSettings.Default.session_range_fill; if (s.volume_column == null) s.volume_column = LadderSettings.Default.volume_column; + if (s.size_display == null) + s.size_display = MarketSizeDisplay.get_default(); + if (s.size_display.normal == null) + s.size_display = s.size_display.with(MarketSizeDisplay.get_default().normal); s.row_height = LadderRender.measure_row_height(s); return s; } diff --git a/src/client/spark/core/Utility/CollectionHelper.cs b/src/client/spark/core/Utility/CollectionHelper.cs index fa96a7f15..2cbbec8f6 100644 --- a/src/client/spark/core/Utility/CollectionHelper.cs +++ b/src/client/spark/core/Utility/CollectionHelper.cs @@ -45,5 +45,12 @@ namespace mbts.utils { ++i; } } + + public static TResult[] convert_array(T[] source, Func selector) { + TResult[] rv = new TResult[source.Length]; + for (int i = 0; i < rv.Length; i++) + rv[i] = selector(source[i]); + return rv; + } } } \ No newline at end of file diff --git a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs index f3cc5d3cf..426880e4f 100644 --- a/src/client/spark/view/Spark/Ladder/LadderGridHost.cs +++ b/src/client/spark/view/Spark/Ladder/LadderGridHost.cs @@ -5,6 +5,7 @@ using bts.utils; using mbts.co2; using mbts.ui; using mbts.ui.controls; +using mbts.ui.spark; using mbts.ui.utility.text; using mbts.utils; using System; @@ -58,6 +59,7 @@ namespace mbts.spark { double _last_origin_price; MarketBreakLines _mkt_break_lines; NativePriceSize _last_trade; + Func _fmt_size = MDDisplayHelper.get_formatter_nps(MarketSizeDisplay.get_default()); public LadderGridHost() { AllowDrop = true; @@ -365,7 +367,7 @@ namespace mbts.spark { } void render_market_col(NativePriceSize[] depth) { var mid = LadderRender.index_of(_price_list, _last_origin_price, _price_comparer); - LadderRender.render_sizes(column.visual_tup.visual, column.visual_tup.glyph_context, mid, _price_list, depth, _settings.row_height, security.ContractSpec, column.settings, _settings.size_display); + LadderRender.render_sizes(column.visual_tup.visual, column.visual_tup.glyph_context, mid, _price_list, depth, _settings.row_height, security.ContractSpec, column.settings, _fmt_size); } } _apply_column_visual_offset(); @@ -521,8 +523,8 @@ namespace mbts.spark { void _refresh_sizes() { Log.assert_d(_price_list != null); int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer); - LadderRender.render_sizes(_bid_vis_tup.visual, _bid_vis_tup.glyph_context, origin_index, _price_list, _bids, _settings.row_height, security.ContractSpec, _settings.bid_column, _settings.size_display); - LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, security.ContractSpec, _settings.ask_column, _settings.size_display); + LadderRender.render_sizes(_bid_vis_tup.visual, _bid_vis_tup.glyph_context, origin_index, _price_list, _bids, _settings.row_height, security.ContractSpec, _settings.bid_column, _fmt_size); + LadderRender.render_sizes(_ask_vis_tup.visual, _ask_vis_tup.glyph_context, origin_index, _price_list, _asks, _settings.row_height, security.ContractSpec, _settings.ask_column, _fmt_size); } /// @@ -682,6 +684,7 @@ namespace mbts.spark { GlyphContext glyph_context(LadderColumnSettings cs) => GlyphContext.create(cs.font_family, cs.font_weight); + _fmt_size = MDDisplayHelper.get_formatter_nps(_settings.size_display); _set_columns(); _apply_column_visual_offset(); if (prev_row_height != _settings.row_height) diff --git a/src/client/spark/view/Spark/Ladder/LadderSettings.cs b/src/client/spark/view/Spark/Ladder/LadderSettings.cs index 606aebcd8..5d4866828 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettings.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettings.cs @@ -215,7 +215,7 @@ namespace mbts.spark { session_high_line = new LadderMarketBreakLine { color = BTSColors.MarketData.session_high, show = true }, session_range_fill = new LadderMarketBreakLine { color = BTSColors.MarketData.session_range_fill, show = true }, alternate_order_size = 0, - size_display = MDSizeDisplay.Merged + size_display = MarketSizeDisplay.get_default() }; } } @@ -275,7 +275,7 @@ namespace mbts.spark { [ProtoMember(25)] public VolumeColumnSettings volume_column { get; set; } [ProtoMember(26)] - public MDSizeDisplay size_display { get; set; } + public MarketSizeDisplay size_display { get; set; } public bool Equals(LadderSettings other) => other != null && @@ -301,7 +301,7 @@ namespace mbts.spark { session_high_line.safe_equals(other.session_high_line) && session_range_fill.safe_equals(other.session_range_fill) && volume_column.safe_equals(other.volume_column) && - size_display == other.size_display; + size_display.safe_equals(other.size_display); public override bool Equals(object obj) => Equals(obj as LadderSettings); @@ -329,7 +329,7 @@ namespace mbts.spark { EqualityComparer.safe_get_hash_code(session_high_line), EqualityComparer.safe_get_hash_code(session_range_fill), EqualityComparer.safe_get_hash_code(volume_column), - size_display.GetHashCode()); + EqualityComparer.safe_get_hash_code(size_display)); } static class LadderSettingsExtensions { diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs index 12fbe9c70..4047a6ca4 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsVM.cs @@ -7,7 +7,6 @@ using mbts.ui.spark; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Media; @@ -16,7 +15,6 @@ using TextUtils = mbts.ui.utility.text.TextUtils; namespace mbts.spark { class LadderSettingsVM : ViewModelBase, IWindowSettingsVM, ITemplateSelectorKey { readonly Property _badge_style; - readonly Property _size_display; readonly Property _alternate_order_size; readonly DelegateProperty _row_height; LadderSettings _current; @@ -42,7 +40,6 @@ namespace mbts.spark { _badge_style = backing(nameof(badge_style)); _badge_style.Changed += _badge_style_Changed; _alternate_order_size = backing(nameof(alternate_order_size)); - _size_display = backing(nameof(size_display)); _row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result())); _orders.font_size_changed += on_font_size_changed; _bid.font_size_changed += on_font_size_changed; @@ -71,13 +68,7 @@ namespace mbts.spark { set => _alternate_order_size.Value = value; } - public IEnumerable size_display_choices => - new[] { MDSizeDisplay.Direct, MDSizeDisplay.Implied, MDSizeDisplay.Merged }; - - public MDSizeDisplay size_display { - get => _size_display.Value; - set => _size_display.Value = value; - } + public MarketSizeDisplayVM size_display { get; } = new MarketSizeDisplayVM(); public double row_height => _row_height.Value; public bool is_synthetic { get; } @@ -123,7 +114,7 @@ namespace mbts.spark { _current = settings; badge_style = settings.badge_style; alternate_order_size = settings.alternate_order_size; - size_display = settings.size_display; + size_display.load_from(settings.size_display); size_presets.SetItems(settings.calculator_buttons.empty_if_null().Select(s => _create_size_preset_vm((uint)s))); _orders.assign_from(_current.orders_column); _bid.assign_from(_current.bid_column); @@ -143,7 +134,7 @@ namespace mbts.spark { var clone = SparkUtils.clone_proto(_current); clone.badge_style = badge_style; clone.alternate_order_size = alternate_order_size; - clone.size_display = size_display; + clone.size_display = size_display.create(); clone.calculator_buttons = size_presets.Select(vm => (int)vm.value).ToArray(); clone.orders_column = _orders.get_settings(); clone.bid_column = _bid.get_settings(); diff --git a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml index 72305f32d..eeceec542 100644 --- a/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml +++ b/src/client/spark/view/Spark/Ladder/LadderSettingsView.xaml @@ -15,12 +15,15 @@ 4 8 + 24 8 24 4 6 8 24 + + @@ -129,8 +132,8 @@ Alternate Order Qty - - + +