Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1ed33c2ee | ||
|
|
8bc1455813 | ||
|
|
ecaa04ecf8 | ||
|
|
44e3070544 | ||
|
|
1815950835 | ||
|
|
8762fbf7ce | ||
|
|
0459171d6e | ||
|
|
fcdf72c5d9 | ||
|
|
f5caf0b7ba | ||
|
|
a0f2673b3a | ||
|
|
ba3439b049 | ||
|
|
ae4d7f719b | ||
|
|
64916f6bfc | ||
|
|
1a9dd1c37a | ||
|
|
d77925c568 | ||
|
|
f1f1b68ffb | ||
|
|
835f453f03 | ||
|
|
0eb9518485 | ||
|
|
b5c70fc34e | ||
|
|
a1dba1d7d0 | ||
|
|
efdea5256f | ||
|
|
5df1e5f383 | ||
|
|
99750da31d | ||
|
|
49247dba3f | ||
|
|
a25257cb33 | ||
|
|
b1c180d663 | ||
|
|
6afb16abcb | ||
|
|
995fa3df35 | ||
|
|
18eb28dfd2 | ||
|
|
99cdfadf8d |
No files matched your search
@@ -631,54 +631,25 @@ namespace mbts.spark.utilities {
|
|||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CustomOrderDefinition.CustomOneofCase[] algo_config(
|
||||||
|
Func<bts.config.AlgoPermissions, List<string>> selector)
|
||||||
|
{
|
||||||
|
var conf = mbts.config.Configuration.get<bts.config.AlgoPermissions>();
|
||||||
|
return selector(conf).Select(x =>
|
||||||
|
{
|
||||||
|
return (CustomOrderDefinition.CustomOneofCase)System.Enum.Parse(typeof(CustomOrderDefinition.CustomOneofCase), x, true);
|
||||||
|
}).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly CustomOrderDefinition.CustomOneofCase[]
|
public static readonly CustomOrderDefinition.CustomOneofCase[]
|
||||||
|
|
||||||
// exchange_order_types are the types which can be submitted on an exchange security
|
// exchange_order_types are the types which can be submitted on an exchange security
|
||||||
exchange_order_types = new[] {
|
exchange_order_types = algo_config(x => x.exchange_order_types),
|
||||||
CustomOrderDefinition.CustomOneofCase.Market,
|
spreader_order_types = algo_config(x => x.spreader_order_types),
|
||||||
CustomOrderDefinition.CustomOneofCase.Limit,
|
attachable_order_types = algo_config(x => x.attachable_order_types),
|
||||||
CustomOrderDefinition.CustomOneofCase.Stop,
|
outright_order_types = algo_config(x => x.outright_order_types);
|
||||||
CustomOrderDefinition.CustomOneofCase.StopLimit,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.GlbxIceberg,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Join,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Top,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Offset,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Splitter,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
|
||||||
},
|
|
||||||
spreader_order_types = new[] {
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Spreader,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Join,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Top,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Splitter,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
|
||||||
},
|
|
||||||
attachable_order_types = new[] {
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Join,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Top,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Offset,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
|
||||||
},
|
|
||||||
outright_order_types = new[] {
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Market,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Limit,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.Stop,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.StopLimit,
|
|
||||||
CustomOrderDefinition.CustomOneofCase.GlbxIceberg,
|
|
||||||
};
|
|
||||||
public static readonly CustomOrderDefinition.CustomOneofCase[] all_order_types = exchange_order_types.Union(spreader_order_types).ToArray();
|
public static readonly CustomOrderDefinition.CustomOneofCase[] all_order_types = exchange_order_types.Union(spreader_order_types).ToArray();
|
||||||
|
|
||||||
public static void copy_values(CustomOrderDefinitionImmutable source, ref CustomOrderDefinition target) =>
|
public static void copy_values(CustomOrderDefinitionImmutable source, ref CustomOrderDefinition target) =>
|
||||||
|
|||||||
@@ -687,7 +687,10 @@
|
|||||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock VerticalAlignment="Center">Tick Offset</TextBlock>
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center" >Tick Offset</TextBlock>
|
||||||
|
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="How many ticks off the inside market to deploy the order, this guards against crossing the market inadventently" />
|
||||||
|
</StackPanel>
|
||||||
<c:TickUpDownUInt x:Name="PART_TickOffsetOnDeploy_TickUpDownUInt" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
|
<c:TickUpDownUInt x:Name="PART_TickOffsetOnDeploy_TickUpDownUInt" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
|
||||||
<uag:AlgoPickerNodeView
|
<uag:AlgoPickerNodeView
|
||||||
x:Name="PART_Child_AlgoPickerNodeView"
|
x:Name="PART_Child_AlgoPickerNodeView"
|
||||||
@@ -905,7 +908,10 @@
|
|||||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Max Show Quantity</TextBlock>
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Max Show Quantity</TextBlock>
|
||||||
|
<c:HelpButton Margin="{StaticResource Margin.LP3}" ToolTip="Maximum size to show on GLBX, this is the exchange provided iceberg" />
|
||||||
|
</StackPanel>
|
||||||
<c:TickUpDownNullableUInt x:Name="PART_MaxShowQuantity_TickUpDownNullableUInt" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding max_show_quantity}"/>
|
<c:TickUpDownNullableUInt x:Name="PART_MaxShowQuantity_TickUpDownNullableUInt" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding max_show_quantity}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@@ -944,7 +950,10 @@
|
|||||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Show Quantity</TextBlock>
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Show Quantity</TextBlock>
|
||||||
|
<c:HelpButton Margin="{StaticResource Margin.LP3}" ToolTip="Maximum size to show on exchange, this is the BTS provided iceberg" />
|
||||||
|
</StackPanel>
|
||||||
<c:TickUpDownDouble x:Name="PART_ShowQuantity_TickUpDownDouble" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding show_quantity}"/>
|
<c:TickUpDownDouble x:Name="PART_ShowQuantity_TickUpDownDouble" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding show_quantity}"/>
|
||||||
<uag:AlgoPickerNodeView
|
<uag:AlgoPickerNodeView
|
||||||
x:Name="PART_Child_AlgoPickerNodeView"
|
x:Name="PART_Child_AlgoPickerNodeView"
|
||||||
@@ -1022,9 +1031,16 @@
|
|||||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Expose Quantity</TextBlock>
|
<StackPanel Orientation="Horizontal" Grid.Row="0" >
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Expose Quantity</TextBlock>
|
||||||
|
<c:HelpButton Margin="4,0,0,0" ToolTip="The quantity we want visible on the exchange" />
|
||||||
|
</StackPanel>
|
||||||
<c:TickUpDownUInt Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding exposed}"/>
|
<c:TickUpDownUInt Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding exposed}"/>
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Max Order Quantity</TextBlock>
|
|
||||||
|
<StackPanel Orientation="Horizontal" Grid.Row="1" >
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Max Order Quantity</TextBlock>
|
||||||
|
<c:HelpButton Margin="{StaticResource Margin.LP3}" ToolTip="No single order will have a quantity greater than this quantity (Clip Size)" />
|
||||||
|
</StackPanel>
|
||||||
<c:TickUpDownUInt Grid.Row="1" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding max_order_qty}"/>
|
<c:TickUpDownUInt Grid.Row="1" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding max_order_qty}"/>
|
||||||
<uag:AlgoPickerNodeView
|
<uag:AlgoPickerNodeView
|
||||||
x:Name="PART_Child_AlgoPickerNodeView"
|
x:Name="PART_Child_AlgoPickerNodeView"
|
||||||
|
|||||||
@@ -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/service_helpers/AlgoServiceUtils.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.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/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/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.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/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.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/AuthSessionManager.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/crypto/CipherType.cpp;utils/crypto/CryptoInit.cpp;utils/crypto/Digest.cpp;utils/crypto/KeyCodeHelper.cpp;utils/crypto/PasswordCallback.cpp;utils/crypto/PrivateKey.cpp;utils/cryptoLine truncated
|
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/service_helpers/AlgoServiceUtils.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/HistoricalDataService.proto;api/services/MBOService.proto;api/services/MarginService.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/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/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/UI.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.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/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.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/AuthSessionManager.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/crypto/CipherType.cpp;utils/crypto/CryptoInit.cpp;utils/crypto/Digest.cpp;utils/crypto/KeyCodeHelper.cpp;utils/cryLine truncated
|
||||||
@@ -141,4 +141,3 @@ if (WIN32)
|
|||||||
include(${CMAKE_SOURCE_DIR}/tools/vs_filters.cmake)
|
include(${CMAKE_SOURCE_DIR}/tools/vs_filters.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
syntax="proto3";
|
||||||
|
package bts.history;
|
||||||
|
option csharp_namespace="bts.history";
|
||||||
|
option cc_enable_arenas=true;
|
||||||
|
import "lib/api/apioptions.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/protobuf/duration.proto";
|
||||||
|
|
||||||
|
message Slice {
|
||||||
|
google.protobuf.Timestamp begin = 10;
|
||||||
|
google.protobuf.Timestamp end = 20;
|
||||||
|
uint32 buy_volume = 30;
|
||||||
|
double avg_buy_price = 40;
|
||||||
|
uint32 sell_volume = 50;
|
||||||
|
double avg_sell_price = 60;
|
||||||
|
}
|
||||||
|
message Slices {
|
||||||
|
repeated Slice slice_vec = 10;
|
||||||
|
}
|
||||||
|
message SliceDistribution {
|
||||||
|
google.protobuf.Timestamp begin = 10;
|
||||||
|
google.protobuf.Timestamp end = 20;
|
||||||
|
double volume_mean = 30;
|
||||||
|
double volume_variance = 40;
|
||||||
|
double price_mean = 50;
|
||||||
|
double price_variance = 60;
|
||||||
|
}
|
||||||
|
message TradingDistribution {
|
||||||
|
double volume_mean = 10;
|
||||||
|
double volume_variance = 20;
|
||||||
|
double volume_skewness = 30;
|
||||||
|
double price_mean = 40;
|
||||||
|
double price_variance = 50;
|
||||||
|
double price_skewness = 60;
|
||||||
|
repeated SliceDistribution slices = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SlicesQuery {
|
||||||
|
string security_desc = 10;
|
||||||
|
google.protobuf.Timestamp begin = 20;
|
||||||
|
google.protobuf.Timestamp end = 30;
|
||||||
|
google.protobuf.Duration dt = 40;
|
||||||
|
}
|
||||||
|
message TradingDistributionQuery {
|
||||||
|
string security_desc = 10;
|
||||||
|
google.protobuf.Timestamp begin = 20;
|
||||||
|
google.protobuf.Timestamp end = 30;
|
||||||
|
google.protobuf.Duration dt = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
service HistoricalDataService {
|
||||||
|
rpc get_trading_slices(SlicesQuery) returns (Slices);
|
||||||
|
rpc get_trading_distribution (TradingDistributionQuery) returns (TradingDistribution);
|
||||||
|
}
|
||||||
@@ -105,6 +105,11 @@ message CustomOrderDefinition
|
|||||||
ScratchOrderDefinition scratch = 31;
|
ScratchOrderDefinition scratch = 31;
|
||||||
ProRataOrderDefinition prorata = 32;
|
ProRataOrderDefinition prorata = 32;
|
||||||
|
|
||||||
|
TWAPOrderDefinition twap = 100;
|
||||||
|
VWAPOrderDefinition vwap = 101;
|
||||||
|
POVOrderDefinition pov = 102;
|
||||||
|
ArrivalPriceOrderDefinition arrival = 103;
|
||||||
|
|
||||||
DynamicOrderDefinition dynamic = 1000;
|
DynamicOrderDefinition dynamic = 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,6 +235,89 @@ message ProRataOrderDefinition {
|
|||||||
CustomOrderDefinition hedge = 20;
|
CustomOrderDefinition hedge = 20;
|
||||||
CustomOrderDefinition overfill_hedge = 30;
|
CustomOrderDefinition overfill_hedge = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message OrderDuration {
|
||||||
|
//default = 0, when to start the order, 0 = immediately
|
||||||
|
google.protobuf.Timestamp start = 10;
|
||||||
|
oneof end
|
||||||
|
{
|
||||||
|
//when to conclude
|
||||||
|
google.protobuf.Timestamp end_timestamp = 20;
|
||||||
|
//alternative to end_time, minutes from start time at which to stop
|
||||||
|
uint32 duration_in_minutes = 30;
|
||||||
|
}
|
||||||
|
//default = 60
|
||||||
|
uint32 discretization_interval_in_seconds = 40;
|
||||||
|
// default = 1, time in periods which to let unexecuted size remain
|
||||||
|
// 0 means unexecuted size must be resolved (paid up) by end of initial period-- this means orders can only work within their period
|
||||||
|
// 1 means unexecuted size must be resolved (paid up) by end of next period-- this establishes two tracks: 'tactical' and 'aggressive'
|
||||||
|
// orders start in the tactical track and are moved to the aggressive once they work beyond their slippage thresh
|
||||||
|
// 2...etc
|
||||||
|
uint32 tactical_schedule_slippage = 50;
|
||||||
|
}
|
||||||
|
message OrderAggression {
|
||||||
|
// default = 0.0, 0.0 - 99.99 be no more than X% of the market, 0.0 means no cap
|
||||||
|
double max_participation_pct = 10;
|
||||||
|
// default = 0.0, price at which you will take liquidity regardless of other parameters, 0.0 means there is no iwouldprice
|
||||||
|
double i_would_price = 20;
|
||||||
|
// default = 0.0, risk neutral, -1.0 to 1.0. nondimensional parameter. -1 is completely risk averse, 1 is risk seeking
|
||||||
|
// this has the net effect of tilting Arrival and VWAP schedules to do more earlier the more negative the parameter
|
||||||
|
// Constant * lambda in Almgren 2000; Konishi 2002
|
||||||
|
double risk_threshold = 30;
|
||||||
|
}
|
||||||
|
message VolumeDistribution {
|
||||||
|
// default = 20, historical volume distribution time horizon
|
||||||
|
uint32 volume_time_horizon_days = 10;
|
||||||
|
|
||||||
|
// default = 0.0 means use historical mean, another value overrides the computed volume mean
|
||||||
|
double volume_mean_override = 15;
|
||||||
|
// default = 0.0 means use historical variance, another value overrides the computed volume variance
|
||||||
|
double volume_variance_override = 20;
|
||||||
|
}
|
||||||
|
message PriceDistribution {
|
||||||
|
// default = 5 biz, 7 cal (weekly), historical price distribution time horizon
|
||||||
|
uint32 price_time_horizon_days = 10;
|
||||||
|
|
||||||
|
// default = 0.0 means use historical mean, another value overrides the computed price mean
|
||||||
|
double price_mean_override = 15;
|
||||||
|
// default = 0.0 means use historical variance, another value overrides the computed price variance
|
||||||
|
double price_variance_override = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TWAPOrderDefinition {
|
||||||
|
OrderDuration duration = 10;
|
||||||
|
OrderAggression aggression = 20;
|
||||||
|
|
||||||
|
CustomOrderDefinition tactical_child = 200;
|
||||||
|
CustomOrderDefinition aggressive_child = 210;
|
||||||
|
}
|
||||||
|
message VWAPOrderDefinition {
|
||||||
|
OrderDuration duration = 10;
|
||||||
|
OrderAggression aggression = 20;
|
||||||
|
VolumeDistribution volume_distribution = 30;
|
||||||
|
PriceDistribution price_distribution = 40;
|
||||||
|
|
||||||
|
CustomOrderDefinition tactical_child = 200;
|
||||||
|
CustomOrderDefinition aggressive_child = 210;
|
||||||
|
}
|
||||||
|
message POVOrderDefinition {
|
||||||
|
double min_participation_pct = 10;
|
||||||
|
OrderDuration duration = 20;
|
||||||
|
OrderAggression aggression = 30;
|
||||||
|
|
||||||
|
CustomOrderDefinition tactical_child = 200;
|
||||||
|
CustomOrderDefinition aggressive_child = 210;
|
||||||
|
}
|
||||||
|
message ArrivalPriceOrderDefinition {
|
||||||
|
OrderDuration duration = 10;
|
||||||
|
OrderAggression aggression = 20;
|
||||||
|
VolumeDistribution volume_distribution = 30;
|
||||||
|
PriceDistribution price_distribution = 40;
|
||||||
|
|
||||||
|
CustomOrderDefinition tactical_child = 200;
|
||||||
|
CustomOrderDefinition aggressive_child = 210;
|
||||||
|
}
|
||||||
|
|
||||||
message DynamicOrderDefinition {
|
message DynamicOrderDefinition {
|
||||||
string dynamic_order_type = 1;
|
string dynamic_order_type = 1;
|
||||||
map<string, DynamicOrderParam> order_params = 2;
|
map<string, DynamicOrderParam> order_params = 2;
|
||||||
@@ -286,6 +374,11 @@ message OrderState {
|
|||||||
ScratchOrderState scratch = 32;
|
ScratchOrderState scratch = 32;
|
||||||
ProRataOrderState prorata = 33;
|
ProRataOrderState prorata = 33;
|
||||||
|
|
||||||
|
TWAPOrderState twap = 100;
|
||||||
|
VWAPOrderState vwap = 101;
|
||||||
|
POVOrderState pov = 102;
|
||||||
|
ArrivalPriceOrderState arrival = 103;
|
||||||
|
|
||||||
DynamicOrderState dynamic = 255; // do not use anything bigger than 255, or things will break
|
DynamicOrderState dynamic = 255; // do not use anything bigger than 255, or things will break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -366,6 +459,10 @@ message ProRataOrderState {}
|
|||||||
message DynamicOrderState {}
|
message DynamicOrderState {}
|
||||||
message QueueHolderState {}
|
message QueueHolderState {}
|
||||||
message SplitterOrderState {}
|
message SplitterOrderState {}
|
||||||
|
message TWAPOrderState {}
|
||||||
|
message VWAPOrderState {}
|
||||||
|
message POVOrderState {}
|
||||||
|
message ArrivalPriceOrderState {}
|
||||||
|
|
||||||
enum OrderStatus {
|
enum OrderStatus {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
AlgoPermissions {
|
||||||
|
value {
|
||||||
|
exchange_order_types: 'Market'
|
||||||
|
exchange_order_types: 'Limit'
|
||||||
|
exchange_order_types: 'Stop'
|
||||||
|
exchange_order_types: 'StopLimit'
|
||||||
|
exchange_order_types: 'GlbxIceberg'
|
||||||
|
exchange_order_types: 'Iceberg'
|
||||||
|
exchange_order_types: 'Lean'
|
||||||
|
exchange_order_types: 'Join'
|
||||||
|
exchange_order_types: 'Cover'
|
||||||
|
exchange_order_types: 'Top'
|
||||||
|
exchange_order_types: 'Offset'
|
||||||
|
exchange_order_types: 'Splitter'
|
||||||
|
exchange_order_types: 'Scratch'
|
||||||
|
exchange_order_types: 'Prorata'
|
||||||
|
exchange_order_types: 'Twap'
|
||||||
|
|
||||||
|
spreader_order_types: 'Spreader'
|
||||||
|
spreader_order_types: 'Iceberg'
|
||||||
|
spreader_order_types: 'Lean'
|
||||||
|
spreader_order_types: 'Join'
|
||||||
|
spreader_order_types: 'Cover'
|
||||||
|
spreader_order_types: 'Top'
|
||||||
|
spreader_order_types: 'Splitter'
|
||||||
|
spreader_order_types: 'Scratch'
|
||||||
|
spreader_order_types: 'Prorata'
|
||||||
|
|
||||||
|
attachable_order_types: 'Iceberg'
|
||||||
|
attachable_order_types: 'Lean'
|
||||||
|
attachable_order_types: 'Join'
|
||||||
|
attachable_order_types: 'Cover'
|
||||||
|
attachable_order_types: 'Top'
|
||||||
|
attachable_order_types: 'Offset'
|
||||||
|
attachable_order_types: 'Scratch'
|
||||||
|
attachable_order_types: 'Prorata'
|
||||||
|
attachable_order_types: 'Twap'
|
||||||
|
|
||||||
|
outright_order_types: 'Market'
|
||||||
|
outright_order_types: 'Limit'
|
||||||
|
outright_order_types: 'Stop'
|
||||||
|
outright_order_types: 'StopLimit'
|
||||||
|
outright_order_types: 'GlbxIceberg'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -143,4 +143,11 @@ message OffsetAttachUIOptions {
|
|||||||
|
|
||||||
message ManualFillsOptions {
|
message ManualFillsOptions {
|
||||||
optional bool modal_transactions = 1 [default = true];
|
optional bool modal_transactions = 1 [default = true];
|
||||||
|
}
|
||||||
|
|
||||||
|
message AlgoPermissions {
|
||||||
|
repeated string exchange_order_types = 10;
|
||||||
|
repeated string spreader_order_types = 20;
|
||||||
|
repeated string attachable_order_types = 30;
|
||||||
|
repeated string outright_order_types = 40;
|
||||||
}
|
}
|
||||||
@@ -156,6 +156,18 @@ class OrderStateUpdateEvent : public mercury::Event<OrderStateUpdateEvent> {
|
|||||||
case api::orderman::OrderState::kProrata:
|
case api::orderman::OrderState::kProrata:
|
||||||
result.mutable_prorata();
|
result.mutable_prorata();
|
||||||
break;
|
break;
|
||||||
|
case api::orderman::OrderState::kTwap:
|
||||||
|
result.mutable_twap();
|
||||||
|
break;
|
||||||
|
case api::orderman::OrderState::kVwap:
|
||||||
|
result.mutable_vwap();
|
||||||
|
break;
|
||||||
|
case api::orderman::OrderState::kPov:
|
||||||
|
result.mutable_pov();
|
||||||
|
break;
|
||||||
|
case api::orderman::OrderState::kArrival:
|
||||||
|
result.mutable_arrival();
|
||||||
|
break;
|
||||||
case api::orderman::OrderState::kDynamic:
|
case api::orderman::OrderState::kDynamic:
|
||||||
result.mutable_dynamic();
|
result.mutable_dynamic();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -23,6 +23,11 @@
|
|||||||
#include "lib/orderman/orders/algo/algo_orders/OffsetAlgo.h"
|
#include "lib/orderman/orders/algo/algo_orders/OffsetAlgo.h"
|
||||||
#include "lib/orderman/orders/algo/algo_orders/ScratchAlgo.h"
|
#include "lib/orderman/orders/algo/algo_orders/ScratchAlgo.h"
|
||||||
#include "lib/orderman/orders/algo/algo_orders/ProRataAlgo.h"
|
#include "lib/orderman/orders/algo/algo_orders/ProRataAlgo.h"
|
||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/TWAPAlgo.h"
|
||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/VWAPAlgo.h"
|
||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/POVAlgo.h"
|
||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/ArrivalPriceAlgo.h"
|
||||||
|
|
||||||
#include "lib/orderman/orders/book_algos/OCOOrder.h"
|
#include "lib/orderman/orders/book_algos/OCOOrder.h"
|
||||||
#include "lib/orderman/orders/book_algos/PricerOrder.h"
|
#include "lib/orderman/orders/book_algos/PricerOrder.h"
|
||||||
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
|
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
|
||||||
@@ -205,6 +210,22 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
|||||||
order_ptr.reset(
|
order_ptr.reset(
|
||||||
new orderman::AlgoWrapperOrder(std::make_unique<ProRataAlgo>()));
|
new orderman::AlgoWrapperOrder(std::make_unique<ProRataAlgo>()));
|
||||||
break;
|
break;
|
||||||
|
case CustomOrderDefinition::kTwap:
|
||||||
|
order_ptr.reset(
|
||||||
|
new orderman::AlgoWrapperOrder(std::make_unique<TWAPAlgo>()));
|
||||||
|
break;
|
||||||
|
case CustomOrderDefinition::kVwap:
|
||||||
|
order_ptr.reset(
|
||||||
|
new orderman::AlgoWrapperOrder(std::make_unique<VWAPAlgo>()));
|
||||||
|
break;
|
||||||
|
case CustomOrderDefinition::kPov:
|
||||||
|
order_ptr.reset(
|
||||||
|
new orderman::AlgoWrapperOrder(std::make_unique<POVAlgo>()));
|
||||||
|
break;
|
||||||
|
case CustomOrderDefinition::kArrival:
|
||||||
|
order_ptr.reset(
|
||||||
|
new orderman::AlgoWrapperOrder(std::make_unique<ArrivalPriceAlgo>()));
|
||||||
|
break;
|
||||||
case CustomOrderDefinition::kDynamic: {
|
case CustomOrderDefinition::kDynamic: {
|
||||||
order_ptr = me->dynamic_order_registry.build(
|
order_ptr = me->dynamic_order_registry.build(
|
||||||
def.custom_def().dynamic().dynamic_order_type());
|
def.custom_def().dynamic().dynamic_order_type());
|
||||||
@@ -215,6 +236,7 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
|||||||
order_ptr.reset(new orderman::DynamicOrder());
|
order_ptr.reset(new orderman::DynamicOrder());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
case CustomOrderDefinition::kComposite:
|
case CustomOrderDefinition::kComposite:
|
||||||
order_ptr.reset(new orderman::CompositeOrder());
|
order_ptr.reset(new orderman::CompositeOrder());
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/ArrivalPriceAlgo.h"
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||||
|
#include "lib/orderman/orders/algo/DynamicHelpers.h"
|
||||||
|
|
||||||
|
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||||
|
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||||
|
#include "lib/os/os.h"
|
||||||
|
#include "lib/utils/PBUtils.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
using namespace api::orderman;
|
||||||
|
|
||||||
|
enum class ArrivalPriceAlgo::Status { WAITING_FOR_MD, WAITING_TO_SCRATCH, SCRATCHED };
|
||||||
|
|
||||||
|
struct ArrivalPriceAlgo::PImpl
|
||||||
|
{
|
||||||
|
IAlgoServices *algo;
|
||||||
|
OrderDefinition definition;
|
||||||
|
|
||||||
|
Quantity scratch_threshold;
|
||||||
|
Price chase_diff;
|
||||||
|
Status status = Status::WAITING_FOR_MD;
|
||||||
|
OrderKey child_key = null_order_key();
|
||||||
|
ChildTemplate *child_template = nullptr;
|
||||||
|
Book last_book;
|
||||||
|
Book book_at_inception;
|
||||||
|
Price scratch_price=Price(os::nan());
|
||||||
|
};
|
||||||
|
|
||||||
|
ArrivalPriceAlgo::ArrivalPriceAlgo() : me(new PImpl()) {}
|
||||||
|
ArrivalPriceAlgo::~ArrivalPriceAlgo() {}
|
||||||
|
IAlgoServices &ArrivalPriceAlgo::algo() const { return *me->algo; }
|
||||||
|
|
||||||
|
OrderOpResult ArrivalPriceAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv)
|
||||||
|
{
|
||||||
|
const auto& def = serv.definition();
|
||||||
|
bts_assert(def.has_custom_def());
|
||||||
|
bts_assert(def.custom_def().has_scratch());
|
||||||
|
// Validate the scratch part of the definition
|
||||||
|
auto &scratch = def.custom_def().scratch();
|
||||||
|
if (!scratch.has_child() && OrderKey(def.attach_target_order_key()) == null_order_key())
|
||||||
|
{
|
||||||
|
return OrderOpResult{ ORDER_ERROR_INVALID_ORDER_DEFINITON,
|
||||||
|
"Scratch order must have either a child order definition or an order to attach to"};
|
||||||
|
}
|
||||||
|
if (!serv.is_attaching())
|
||||||
|
{
|
||||||
|
// if we are not attaching create a child template so we can
|
||||||
|
// quickly launch our child when the time comes.
|
||||||
|
auto child_def = def;
|
||||||
|
*child_def.mutable_custom_def() = def.custom_def().scratch().child();
|
||||||
|
me->child_template = &(serv.make_child_template(child_def));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrivalPriceAlgo::do_launch(IAlgoServices &algo)
|
||||||
|
{
|
||||||
|
me->algo = &algo;
|
||||||
|
update_scratch_threshold();
|
||||||
|
bts_assert(me->child_template);
|
||||||
|
me->child_key = algo.launch_child(*me->child_template, algo.price(), algo.quantity());
|
||||||
|
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrivalPriceAlgo::do_launch_attached(IAlgoServices &algo, OrderKey k, ChildState st)
|
||||||
|
{
|
||||||
|
me->algo = &algo;
|
||||||
|
update_scratch_threshold();
|
||||||
|
me->child_key = k;
|
||||||
|
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||||
|
on_child_change(k, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrivalPriceAlgo::do_update(api::orderman::Update const &update)
|
||||||
|
{
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} receiving update: {}", algo().order_key().value(), utils::pb::to_string(update));
|
||||||
|
|
||||||
|
if (update.has_custom() && !update.custom().has_scratch())
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Update child_update;
|
||||||
|
Price price_to_pass = get_price_to_pass(Price(update.price()));
|
||||||
|
child_update.set_price(price_to_pass.value());
|
||||||
|
child_update.set_quantity(update.quantity());
|
||||||
|
if (update.has_custom())
|
||||||
|
{
|
||||||
|
update_scratch_threshold();
|
||||||
|
auto &scratch_update = update.custom().scratch();
|
||||||
|
*child_update.mutable_custom() = scratch_update.child();
|
||||||
|
}
|
||||||
|
algo().update_child(me->child_key, child_update);
|
||||||
|
|
||||||
|
// If we were not yet scratched it is possible we now can, since we
|
||||||
|
// could have changed the scratch_threshold
|
||||||
|
if (me->status == Status::WAITING_TO_SCRATCH)
|
||||||
|
try_to_scratch();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrivalPriceAlgo::on_book(SecurityKey, Book const &b)
|
||||||
|
{
|
||||||
|
me->last_book = b;
|
||||||
|
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} on book. BID: {}@{} ASK: {}@{}",
|
||||||
|
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
|
||||||
|
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
|
||||||
|
b.side(api::SIDE_SELL)[0].price());
|
||||||
|
|
||||||
|
switch (me->status)
|
||||||
|
{
|
||||||
|
case Status::WAITING_FOR_MD:
|
||||||
|
{
|
||||||
|
auto other_side = securities::other_side(algo().definition().side());
|
||||||
|
auto &other_side_top = me->last_book.side(other_side)[0];
|
||||||
|
|
||||||
|
if (other_side_top.size() > 0)
|
||||||
|
{
|
||||||
|
me->status = Status::WAITING_TO_SCRATCH;
|
||||||
|
me->book_at_inception = b;
|
||||||
|
LOG(OrderMan, Diag,
|
||||||
|
"ScratchOrder order {} receiving first book: {} Target: {}",
|
||||||
|
algo().order_key().value(), me->scratch_threshold.value(),
|
||||||
|
other_side_top.price());
|
||||||
|
try_to_scratch();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Status::SCRATCHED:
|
||||||
|
break;
|
||||||
|
case Status::WAITING_TO_SCRATCH:
|
||||||
|
try_to_scratch();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrivalPriceAlgo::try_to_scratch()
|
||||||
|
{
|
||||||
|
bts_assert(me->status == Status::WAITING_TO_SCRATCH);
|
||||||
|
auto my_side = algo().definition().side();
|
||||||
|
auto other_side = securities::other_side(my_side);
|
||||||
|
|
||||||
|
Quantity my_quan;
|
||||||
|
auto &my_side_psz = me->last_book.side(my_side)[0];
|
||||||
|
auto &other_side_psz = me->last_book.side(other_side)[0];
|
||||||
|
auto other_side_price = Price(other_side_psz.price());
|
||||||
|
auto inception_other_side_price = Price(me->book_at_inception.side(other_side)[0].price());
|
||||||
|
|
||||||
|
bool market_moved = !securities::is_equal_or_better(other_side, other_side_price, inception_other_side_price);
|
||||||
|
bool on_market = securities::is_same(Price(my_side_psz.price()), Price(algo().price()));
|
||||||
|
if (market_moved || (on_market && my_side_psz.size() <= me->scratch_threshold.value()) )
|
||||||
|
{
|
||||||
|
Price scratch_price = securities::worsen_price(other_side, inception_other_side_price, me->chase_diff);
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} scratching: {}@{}",
|
||||||
|
algo().order_key().value(), algo().quantity().value(), scratch_price);
|
||||||
|
|
||||||
|
// Time to scratch
|
||||||
|
Update child_update;
|
||||||
|
me->scratch_price = scratch_price;
|
||||||
|
child_update.set_price(scratch_price.value());
|
||||||
|
//we still want to try to scratch all of it
|
||||||
|
child_update.set_quantity(algo().quantity().value());
|
||||||
|
algo().update_child(me->child_key, child_update);
|
||||||
|
me->status = Status::SCRATCHED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(OrderMan, Diag,
|
||||||
|
"ScratchOrder order {} still waiting to scratch. Order: {}@{} "
|
||||||
|
"My side: {}@{}"
|
||||||
|
"Thresh : {}@{}",
|
||||||
|
algo().order_key().value(), algo().quantity().value(), algo().price().value(),
|
||||||
|
my_side_psz.size(), my_side_psz.price(),
|
||||||
|
me->scratch_threshold.value(), my_quan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Price ArrivalPriceAlgo::get_price_to_pass (Price price) const
|
||||||
|
{
|
||||||
|
Price rv;
|
||||||
|
switch (me->status) {
|
||||||
|
case Status::WAITING_FOR_MD:
|
||||||
|
case Status::WAITING_TO_SCRATCH:
|
||||||
|
rv = price;
|
||||||
|
break;
|
||||||
|
case Status::SCRATCHED:
|
||||||
|
rv = me->scratch_price;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrivalPriceAlgo::update_scratch_threshold()
|
||||||
|
{
|
||||||
|
if (algo().definition().custom_def().scratch().scratch_threshold().quantity_case() ==
|
||||||
|
bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET)
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid quantity, terminating");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
me->scratch_threshold = SpreaderUtils::_quantity_threshold(
|
||||||
|
algo().definition().custom_def().scratch().scratch_threshold(), algo().quantity());
|
||||||
|
me->chase_diff = Price(securities::tick_size(
|
||||||
|
algo().security()).value() * algo().definition().custom_def().scratch().chase_ticks());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrivalPriceAlgo::on_child_change(OrderKey, ChildState const &st)
|
||||||
|
{
|
||||||
|
algo().update_fill_state(st.total_fills, st.last_price);
|
||||||
|
switch (st.status)
|
||||||
|
{
|
||||||
|
case ChildStatus::WORKING:
|
||||||
|
return;
|
||||||
|
case ChildStatus::COMPLETED:
|
||||||
|
algo().complete();
|
||||||
|
return;
|
||||||
|
case ChildStatus::CANCELED:
|
||||||
|
algo().cancel("Unexpected child cancel");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace orderman
|
||||||
|
} // namespace bts
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
class ArrivalPriceAlgo : public IAlgoOrder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArrivalPriceAlgo();
|
||||||
|
~ArrivalPriceAlgo();
|
||||||
|
|
||||||
|
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices&) override;
|
||||||
|
void do_launch(IAlgoServices &) override;
|
||||||
|
void do_launch_attached(IAlgoServices&,OrderKey, ChildState) override;
|
||||||
|
void do_update(api::orderman::Update const &) override;
|
||||||
|
void on_book(SecurityKey, Book const &) override;
|
||||||
|
void on_child_change(OrderKey, ChildState const &) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class Status;
|
||||||
|
struct PImpl;
|
||||||
|
std::unique_ptr<PImpl> me;
|
||||||
|
IAlgoServices& algo() const;
|
||||||
|
|
||||||
|
void update_scratch_threshold();
|
||||||
|
void try_to_scratch();
|
||||||
|
Price get_price_to_pass (Price price) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/POVAlgo.h"
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||||
|
#include "lib/orderman/orders/algo/DynamicHelpers.h"
|
||||||
|
|
||||||
|
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||||
|
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||||
|
#include "lib/os/os.h"
|
||||||
|
#include "lib/utils/PBUtils.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
using namespace api::orderman;
|
||||||
|
|
||||||
|
enum class POVAlgo::Status { WAITING_FOR_MD, WAITING_TO_SCRATCH, SCRATCHED };
|
||||||
|
|
||||||
|
struct POVAlgo::PImpl
|
||||||
|
{
|
||||||
|
IAlgoServices *algo;
|
||||||
|
OrderDefinition definition;
|
||||||
|
|
||||||
|
Quantity scratch_threshold;
|
||||||
|
Price chase_diff;
|
||||||
|
Status status = Status::WAITING_FOR_MD;
|
||||||
|
OrderKey child_key = null_order_key();
|
||||||
|
ChildTemplate *child_template = nullptr;
|
||||||
|
Book last_book;
|
||||||
|
Book book_at_inception;
|
||||||
|
Price scratch_price=Price(os::nan());
|
||||||
|
};
|
||||||
|
|
||||||
|
POVAlgo::POVAlgo() : me(new PImpl()) {}
|
||||||
|
POVAlgo::~POVAlgo() {}
|
||||||
|
IAlgoServices &POVAlgo::algo() const { return *me->algo; }
|
||||||
|
|
||||||
|
OrderOpResult POVAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv)
|
||||||
|
{
|
||||||
|
const auto& def = serv.definition();
|
||||||
|
bts_assert(def.has_custom_def());
|
||||||
|
bts_assert(def.custom_def().has_scratch());
|
||||||
|
// Validate the scratch part of the definition
|
||||||
|
auto &scratch = def.custom_def().scratch();
|
||||||
|
if (!scratch.has_child() && OrderKey(def.attach_target_order_key()) == null_order_key())
|
||||||
|
{
|
||||||
|
return OrderOpResult{ ORDER_ERROR_INVALID_ORDER_DEFINITON,
|
||||||
|
"Scratch order must have either a child order definition or an order to attach to"};
|
||||||
|
}
|
||||||
|
if (!serv.is_attaching())
|
||||||
|
{
|
||||||
|
// if we are not attaching create a child template so we can
|
||||||
|
// quickly launch our child when the time comes.
|
||||||
|
auto child_def = def;
|
||||||
|
*child_def.mutable_custom_def() = def.custom_def().scratch().child();
|
||||||
|
me->child_template = &(serv.make_child_template(child_def));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void POVAlgo::do_launch(IAlgoServices &algo)
|
||||||
|
{
|
||||||
|
me->algo = &algo;
|
||||||
|
update_scratch_threshold();
|
||||||
|
bts_assert(me->child_template);
|
||||||
|
me->child_key = algo.launch_child(*me->child_template, algo.price(), algo.quantity());
|
||||||
|
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void POVAlgo::do_launch_attached(IAlgoServices &algo, OrderKey k, ChildState st)
|
||||||
|
{
|
||||||
|
me->algo = &algo;
|
||||||
|
update_scratch_threshold();
|
||||||
|
me->child_key = k;
|
||||||
|
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||||
|
on_child_change(k, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void POVAlgo::do_update(api::orderman::Update const &update)
|
||||||
|
{
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} receiving update: {}", algo().order_key().value(), utils::pb::to_string(update));
|
||||||
|
|
||||||
|
if (update.has_custom() && !update.custom().has_scratch())
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Update child_update;
|
||||||
|
Price price_to_pass = get_price_to_pass(Price(update.price()));
|
||||||
|
child_update.set_price(price_to_pass.value());
|
||||||
|
child_update.set_quantity(update.quantity());
|
||||||
|
if (update.has_custom())
|
||||||
|
{
|
||||||
|
update_scratch_threshold();
|
||||||
|
auto &scratch_update = update.custom().scratch();
|
||||||
|
*child_update.mutable_custom() = scratch_update.child();
|
||||||
|
}
|
||||||
|
algo().update_child(me->child_key, child_update);
|
||||||
|
|
||||||
|
// If we were not yet scratched it is possible we now can, since we
|
||||||
|
// could have changed the scratch_threshold
|
||||||
|
if (me->status == Status::WAITING_TO_SCRATCH)
|
||||||
|
try_to_scratch();
|
||||||
|
}
|
||||||
|
|
||||||
|
void POVAlgo::on_book(SecurityKey, Book const &b)
|
||||||
|
{
|
||||||
|
me->last_book = b;
|
||||||
|
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} on book. BID: {}@{} ASK: {}@{}",
|
||||||
|
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
|
||||||
|
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
|
||||||
|
b.side(api::SIDE_SELL)[0].price());
|
||||||
|
|
||||||
|
switch (me->status)
|
||||||
|
{
|
||||||
|
case Status::WAITING_FOR_MD:
|
||||||
|
{
|
||||||
|
auto other_side = securities::other_side(algo().definition().side());
|
||||||
|
auto &other_side_top = me->last_book.side(other_side)[0];
|
||||||
|
|
||||||
|
if (other_side_top.size() > 0)
|
||||||
|
{
|
||||||
|
me->status = Status::WAITING_TO_SCRATCH;
|
||||||
|
me->book_at_inception = b;
|
||||||
|
LOG(OrderMan, Diag,
|
||||||
|
"ScratchOrder order {} receiving first book: {} Target: {}",
|
||||||
|
algo().order_key().value(), me->scratch_threshold.value(),
|
||||||
|
other_side_top.price());
|
||||||
|
try_to_scratch();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Status::SCRATCHED:
|
||||||
|
break;
|
||||||
|
case Status::WAITING_TO_SCRATCH:
|
||||||
|
try_to_scratch();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void POVAlgo::try_to_scratch()
|
||||||
|
{
|
||||||
|
bts_assert(me->status == Status::WAITING_TO_SCRATCH);
|
||||||
|
auto my_side = algo().definition().side();
|
||||||
|
auto other_side = securities::other_side(my_side);
|
||||||
|
|
||||||
|
Quantity my_quan;
|
||||||
|
auto &my_side_psz = me->last_book.side(my_side)[0];
|
||||||
|
auto &other_side_psz = me->last_book.side(other_side)[0];
|
||||||
|
auto other_side_price = Price(other_side_psz.price());
|
||||||
|
auto inception_other_side_price = Price(me->book_at_inception.side(other_side)[0].price());
|
||||||
|
|
||||||
|
bool market_moved = !securities::is_equal_or_better(other_side, other_side_price, inception_other_side_price);
|
||||||
|
bool on_market = securities::is_same(Price(my_side_psz.price()), Price(algo().price()));
|
||||||
|
if (market_moved || (on_market && my_side_psz.size() <= me->scratch_threshold.value()) )
|
||||||
|
{
|
||||||
|
Price scratch_price = securities::worsen_price(other_side, inception_other_side_price, me->chase_diff);
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} scratching: {}@{}",
|
||||||
|
algo().order_key().value(), algo().quantity().value(), scratch_price);
|
||||||
|
|
||||||
|
// Time to scratch
|
||||||
|
Update child_update;
|
||||||
|
me->scratch_price = scratch_price;
|
||||||
|
child_update.set_price(scratch_price.value());
|
||||||
|
//we still want to try to scratch all of it
|
||||||
|
child_update.set_quantity(algo().quantity().value());
|
||||||
|
algo().update_child(me->child_key, child_update);
|
||||||
|
me->status = Status::SCRATCHED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(OrderMan, Diag,
|
||||||
|
"ScratchOrder order {} still waiting to scratch. Order: {}@{} "
|
||||||
|
"My side: {}@{}"
|
||||||
|
"Thresh : {}@{}",
|
||||||
|
algo().order_key().value(), algo().quantity().value(), algo().price().value(),
|
||||||
|
my_side_psz.size(), my_side_psz.price(),
|
||||||
|
me->scratch_threshold.value(), my_quan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Price POVAlgo::get_price_to_pass (Price price) const
|
||||||
|
{
|
||||||
|
Price rv;
|
||||||
|
switch (me->status) {
|
||||||
|
case Status::WAITING_FOR_MD:
|
||||||
|
case Status::WAITING_TO_SCRATCH:
|
||||||
|
rv = price;
|
||||||
|
break;
|
||||||
|
case Status::SCRATCHED:
|
||||||
|
rv = me->scratch_price;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void POVAlgo::update_scratch_threshold()
|
||||||
|
{
|
||||||
|
if (algo().definition().custom_def().scratch().scratch_threshold().quantity_case() ==
|
||||||
|
bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET)
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid quantity, terminating");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
me->scratch_threshold = SpreaderUtils::_quantity_threshold(
|
||||||
|
algo().definition().custom_def().scratch().scratch_threshold(), algo().quantity());
|
||||||
|
me->chase_diff = Price(securities::tick_size(
|
||||||
|
algo().security()).value() * algo().definition().custom_def().scratch().chase_ticks());
|
||||||
|
}
|
||||||
|
|
||||||
|
void POVAlgo::on_child_change(OrderKey, ChildState const &st)
|
||||||
|
{
|
||||||
|
algo().update_fill_state(st.total_fills, st.last_price);
|
||||||
|
switch (st.status)
|
||||||
|
{
|
||||||
|
case ChildStatus::WORKING:
|
||||||
|
return;
|
||||||
|
case ChildStatus::COMPLETED:
|
||||||
|
algo().complete();
|
||||||
|
return;
|
||||||
|
case ChildStatus::CANCELED:
|
||||||
|
algo().cancel("Unexpected child cancel");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace orderman
|
||||||
|
} // namespace bts
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
class POVAlgo : public IAlgoOrder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
POVAlgo();
|
||||||
|
~POVAlgo();
|
||||||
|
|
||||||
|
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices&) override;
|
||||||
|
void do_launch(IAlgoServices &) override;
|
||||||
|
void do_launch_attached(IAlgoServices&,OrderKey, ChildState) override;
|
||||||
|
void do_update(api::orderman::Update const &) override;
|
||||||
|
void on_book(SecurityKey, Book const &) override;
|
||||||
|
void on_child_change(OrderKey, ChildState const &) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class Status;
|
||||||
|
struct PImpl;
|
||||||
|
std::unique_ptr<PImpl> me;
|
||||||
|
IAlgoServices& algo() const;
|
||||||
|
|
||||||
|
void update_scratch_threshold();
|
||||||
|
void try_to_scratch();
|
||||||
|
Price get_price_to_pass (Price price) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/TWAPAlgo.h"
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||||
|
#include "lib/orderman/orders/algo/DynamicHelpers.h"
|
||||||
|
#include "lib/api/services/OrderService.pb.h"
|
||||||
|
|
||||||
|
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||||
|
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||||
|
#include "lib/os/os.h"
|
||||||
|
#include "lib/utils/PBUtils.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
using namespace api::orderman;
|
||||||
|
|
||||||
|
|
||||||
|
struct TWAPAlgo::PImpl
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
TWAPAlgo::TWAPAlgo() : me(new PImpl()) {}
|
||||||
|
TWAPAlgo::~TWAPAlgo() {}
|
||||||
|
|
||||||
|
OrderOpResult TWAPAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv)
|
||||||
|
{
|
||||||
|
const auto& def = serv.definition();
|
||||||
|
bts_assert(def.has_custom_def());
|
||||||
|
bts_assert(def.custom_def().has_twap());
|
||||||
|
// Validate the algo part of the definition
|
||||||
|
auto &algo = def.custom_def().twap();
|
||||||
|
if (!algo.has_tactical_child() || algo.has_aggressive_child())
|
||||||
|
{
|
||||||
|
return OrderOpResult{ ORDER_ERROR_INVALID_ORDER_DEFINITON,
|
||||||
|
"Algo order must have a tactical and aggressive child order definition"};
|
||||||
|
}
|
||||||
|
if (!serv.is_attaching())
|
||||||
|
{
|
||||||
|
// if we are not attaching create a child template so we can
|
||||||
|
// quickly launch our child when the time comes.
|
||||||
|
auto tchild_def = def;
|
||||||
|
*tchild_def.mutable_custom_def() = def.custom_def().twap().tactical_child();
|
||||||
|
tactical_template() = &(serv.make_child_template(tchild_def));
|
||||||
|
|
||||||
|
auto achild_def = def;
|
||||||
|
*achild_def.mutable_custom_def() = def.custom_def().twap().aggressive_child();
|
||||||
|
aggressive_template() = &(serv.make_child_template(achild_def));
|
||||||
|
|
||||||
|
auto mchild_def = def;
|
||||||
|
mchild_def.mutable_custom_def()->mutable_market();
|
||||||
|
market_template() = &(serv.make_child_template(mchild_def));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void TWAPAlgo::do_launch(IAlgoServices &algo)
|
||||||
|
{
|
||||||
|
set_algo(&algo);
|
||||||
|
update_params();
|
||||||
|
bts_assert(tactical_template());
|
||||||
|
bts_assert(aggressive_template());
|
||||||
|
bts_assert(market_template());
|
||||||
|
|
||||||
|
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||||
|
algo.subscribe_ticker(SecurityKey(algo.security().id()));
|
||||||
|
algo.subscribe_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TWAPAlgo::do_launch_attached(IAlgoServices &algo, OrderKey k, ChildState st)
|
||||||
|
{
|
||||||
|
set_algo(&algo);
|
||||||
|
update_params();
|
||||||
|
bts_assert(tactical_template());
|
||||||
|
bts_assert(aggressive_template());
|
||||||
|
bts_assert(market_template());
|
||||||
|
|
||||||
|
add_child(k, TimeSlicer::Channel::Tactical);
|
||||||
|
on_child_change(k, st);
|
||||||
|
|
||||||
|
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||||
|
algo.subscribe_ticker(SecurityKey(algo.security().id()));
|
||||||
|
algo.subscribe_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TWAPAlgo::do_update(const api::orderman::Update& update)
|
||||||
|
{
|
||||||
|
LOG(OrderMan, Diag, "TWAPAlgo order {} receiving update: {}", algo().order_key().value(), utils::pb::to_string(update));
|
||||||
|
_do_update(update, update.custom().twap().tactical_child(), update.custom().twap().aggressive_child());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TWAPAlgo::update_params()
|
||||||
|
{
|
||||||
|
if (!algo().definition().custom_def().twap().has_duration())
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid duration params, terminating");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!algo().definition().custom_def().twap().has_aggression())
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid aggression params, terminating");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
duration().CopyFrom(algo().definition().custom_def().twap().duration());
|
||||||
|
//next timer will pick this up
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace orderman
|
||||||
|
} // namespace bts
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/TimeSlicer.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
class TWAPAlgo : public TimeSlicer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TWAPAlgo();
|
||||||
|
~TWAPAlgo();
|
||||||
|
|
||||||
|
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices&) override;
|
||||||
|
void do_launch(IAlgoServices &) override;
|
||||||
|
void do_launch_attached(IAlgoServices&, OrderKey, ChildState) override;
|
||||||
|
void do_update(const api::orderman::Update& update) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void update_params() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct PImpl;
|
||||||
|
std::unique_ptr<PImpl> me;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/TimeSlicer.h"
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||||
|
#include "lib/orderman/orders/algo/DynamicHelpers.h"
|
||||||
|
#include "lib/api/services/OrderService.pb.h"
|
||||||
|
|
||||||
|
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||||
|
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||||
|
#include "lib/os/os.h"
|
||||||
|
#include "lib/utils/PBUtils.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
using namespace api::orderman;
|
||||||
|
|
||||||
|
enum class TimeSlicer::Status { WAITING_FOR_MD, WAITING_FOR_START, WORKING, DONE };
|
||||||
|
|
||||||
|
struct OrderDef
|
||||||
|
{
|
||||||
|
OrderKey child_key = null_order_key();
|
||||||
|
uint32_t generation = 0u;
|
||||||
|
TimeSlicer::Channel channel = TimeSlicer::Channel::Tactical;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimeSlicer::PImpl
|
||||||
|
{
|
||||||
|
IAlgoServices* algo;
|
||||||
|
|
||||||
|
OrderDuration duration;
|
||||||
|
Status status = Status::WAITING_FOR_MD;
|
||||||
|
google::protobuf::Timestamp start_time, end_time;
|
||||||
|
uint32_t generation = 0; uint32_t expected_generations = 0;
|
||||||
|
|
||||||
|
ChildTemplate* tactical_template = nullptr;
|
||||||
|
ChildTemplate* aggressive_template = nullptr;
|
||||||
|
ChildTemplate* market_template = nullptr;
|
||||||
|
|
||||||
|
std::map<OrderKey, OrderDef> child_orders;
|
||||||
|
std::map<OrderKey, ChildState> child_state;
|
||||||
|
Quantity total_filled = Quantity(0);
|
||||||
|
|
||||||
|
Book last_book;
|
||||||
|
TradeVector last_trades;
|
||||||
|
};
|
||||||
|
OrderDuration& TimeSlicer::duration()
|
||||||
|
{
|
||||||
|
return me->duration;
|
||||||
|
}
|
||||||
|
TimeSlicer::Status& TimeSlicer::status()
|
||||||
|
{
|
||||||
|
return me->status;
|
||||||
|
}
|
||||||
|
google::protobuf::Timestamp& TimeSlicer::start_time()
|
||||||
|
{
|
||||||
|
return me->start_time;
|
||||||
|
}
|
||||||
|
google::protobuf::Timestamp& TimeSlicer::end_time()
|
||||||
|
{
|
||||||
|
return me->end_time;
|
||||||
|
}
|
||||||
|
uint32_t& TimeSlicer::generation()
|
||||||
|
{
|
||||||
|
return me->generation;
|
||||||
|
}
|
||||||
|
uint32_t& TimeSlicer::expected_generations()
|
||||||
|
{
|
||||||
|
return me->expected_generations;
|
||||||
|
}
|
||||||
|
ChildTemplate*& TimeSlicer::tactical_template()
|
||||||
|
{
|
||||||
|
return me->tactical_template;
|
||||||
|
}
|
||||||
|
ChildTemplate*& TimeSlicer::aggressive_template()
|
||||||
|
{
|
||||||
|
return me->aggressive_template;
|
||||||
|
}
|
||||||
|
ChildTemplate*& TimeSlicer::market_template()
|
||||||
|
{
|
||||||
|
return me->market_template;
|
||||||
|
}
|
||||||
|
void TimeSlicer::add_child(OrderKey k, Channel chan)
|
||||||
|
{
|
||||||
|
me->child_orders[k] = OrderDef{ k, me->generation, chan };
|
||||||
|
}
|
||||||
|
Quantity& TimeSlicer::total_filled()
|
||||||
|
{
|
||||||
|
return me->total_filled;
|
||||||
|
}
|
||||||
|
Book& TimeSlicer::last_book()
|
||||||
|
{
|
||||||
|
return me->last_book;
|
||||||
|
}
|
||||||
|
TradeVector& TimeSlicer::last_trades()
|
||||||
|
{
|
||||||
|
return me->last_trades;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TimeSlicer::TimeSlicer() : me(new PImpl()) {}
|
||||||
|
TimeSlicer::~TimeSlicer() {}
|
||||||
|
IAlgoServices& TimeSlicer::algo() const { return *me->algo; }
|
||||||
|
void TimeSlicer::set_algo(IAlgoServices* value) { me->algo = value; }
|
||||||
|
|
||||||
|
void TimeSlicer::on_book(SecurityKey, const Book& b)
|
||||||
|
{
|
||||||
|
me->last_book = b;
|
||||||
|
|
||||||
|
LOG(OrderMan, Diag, "TimeSlicer order {} on book. BID: {}@{} ASK: {}@{}",
|
||||||
|
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
|
||||||
|
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
|
||||||
|
b.side(api::SIDE_SELL)[0].price());
|
||||||
|
|
||||||
|
switch (me->status)
|
||||||
|
{
|
||||||
|
case Status::WAITING_FOR_MD:
|
||||||
|
{
|
||||||
|
me->status = Status::WAITING_FOR_START;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Status::WAITING_FOR_START:
|
||||||
|
case Status::WORKING:
|
||||||
|
case Status::DONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::_do_update(const api::orderman::Update& update,
|
||||||
|
const bts::api::orderman::CustomOrderDefinition& tactical_update,
|
||||||
|
const bts::api::orderman::CustomOrderDefinition& aggressive_update)
|
||||||
|
|
||||||
|
{
|
||||||
|
LOG(OrderMan, Diag, "TimeSlicer order {} receiving update: {}", algo().order_key().value(), utils::pb::to_string(update));
|
||||||
|
|
||||||
|
if (update.has_custom() && !update.custom().has_twap())
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Update child_update;
|
||||||
|
child_update.set_price(update.price());
|
||||||
|
child_update.set_quantity(update.quantity());
|
||||||
|
for(auto& i : me->child_orders)
|
||||||
|
{
|
||||||
|
if (update.has_custom())
|
||||||
|
{
|
||||||
|
update_params();
|
||||||
|
if(i.second.channel == Channel::Tactical)
|
||||||
|
*child_update.mutable_custom() = tactical_update;
|
||||||
|
else if(i.second.channel == Channel::Aggressive)
|
||||||
|
*child_update.mutable_custom() = aggressive_update;
|
||||||
|
else if(i.second.channel == Channel::Market) {}
|
||||||
|
else
|
||||||
|
bts_assert_msg(false, "unknown channel type");
|
||||||
|
}
|
||||||
|
algo().update_child(i.first, child_update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::on_ticker(SecurityKey, const TradeVector& vec)
|
||||||
|
{
|
||||||
|
me->last_trades = vec;
|
||||||
|
LOG(OrderMan, Diag, "TimeSlicer order {} on ticker.", algo().order_key().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
google::protobuf::Timestamp TimeSlicer::compute_end_time()
|
||||||
|
{
|
||||||
|
if (me->duration.end_case() == OrderDuration::EndCase::kDurationInMinutes)
|
||||||
|
{
|
||||||
|
google::protobuf::Timestamp eend;
|
||||||
|
eend.set_seconds(me->start_time.seconds() + me->duration.duration_in_minutes() * 60);
|
||||||
|
return eend;
|
||||||
|
}
|
||||||
|
else if (me->duration.end_case() == OrderDuration::EndCase::kEndTimestamp)
|
||||||
|
return me->duration.end_timestamp();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
algo().terminate_error("Unknown end case");
|
||||||
|
return google::protobuf::Timestamp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::setup_schedule()
|
||||||
|
{
|
||||||
|
me->status = Status::WORKING;
|
||||||
|
me->start_time = utils::pb::pb_now();
|
||||||
|
me->end_time = end_time();
|
||||||
|
me->expected_generations = ( me->end_time.seconds() - me->start_time.seconds() ) / me->duration.discretization_interval_in_seconds();
|
||||||
|
if(me->start_time.seconds() > me->end_time.seconds() || me->expected_generations <= 0 || me->expected_generations > 86400)
|
||||||
|
algo().terminate_error("Invalid scheduling data");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::on_timer()
|
||||||
|
{
|
||||||
|
switch(me->status)
|
||||||
|
{
|
||||||
|
case Status::WAITING_FOR_MD:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Status::WAITING_FOR_START:
|
||||||
|
{
|
||||||
|
if(me->duration.has_start() && me->duration.start().seconds() > 0)
|
||||||
|
{
|
||||||
|
if (me->duration.start().seconds() < utils::pb::pb_now().seconds())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setup_schedule();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Status::WORKING:
|
||||||
|
{
|
||||||
|
if (utils::pb::pb_now().seconds() > me->end_time.seconds())
|
||||||
|
{
|
||||||
|
get_done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(me->generation++ == 0)
|
||||||
|
first_generation();
|
||||||
|
else
|
||||||
|
next_generation();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Status::DONE:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::report_status()
|
||||||
|
{
|
||||||
|
uint32_t fills = 0u; bool all_complete = true; double price = 0.0; int idx = 0;
|
||||||
|
for(const auto& ch : me->child_state)
|
||||||
|
{
|
||||||
|
fills += ch.second.total_fills.value();
|
||||||
|
if(ch.second.status == ChildStatus::COMPLETED )
|
||||||
|
{
|
||||||
|
price += ch.second.avg_price.value();
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
bool complete = (ch.second.status == ChildStatus::COMPLETED || ch.second.status == ChildStatus::CANCELED);
|
||||||
|
if(complete)
|
||||||
|
remove_child(ch.first);
|
||||||
|
all_complete = all_complete && complete;
|
||||||
|
}
|
||||||
|
algo().update_fill_state(Quantity(fills), Price(price / idx));
|
||||||
|
me->total_filled = Quantity(fills);
|
||||||
|
|
||||||
|
if(me->status == Status::DONE)
|
||||||
|
{
|
||||||
|
if(all_complete)
|
||||||
|
algo().complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::on_child_change(OrderKey key, const ChildState& st)
|
||||||
|
{
|
||||||
|
me->child_state[key] = st;
|
||||||
|
report_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimeSlicer::validate_price_size(Price price, Quantity size)
|
||||||
|
{
|
||||||
|
if(os::is_nan(price.value()) || size.value() <= 0 || size.value() > algo().quantity().value())
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid price or size of gen " + std::to_string(me->generation) + " order");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::remove_child(OrderKey k)
|
||||||
|
{
|
||||||
|
me->child_orders.erase(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::cancel_and_remove_child(OrderKey k)
|
||||||
|
{
|
||||||
|
algo().cancel_child(k);
|
||||||
|
me->child_orders.erase(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderKey TimeSlicer::send_child(Price price, Quantity size, Channel chan)
|
||||||
|
{
|
||||||
|
OrderKey child_key = null_order_key();
|
||||||
|
if(validate_price_size(price, size))
|
||||||
|
{
|
||||||
|
if(chan == Channel::Tactical)
|
||||||
|
child_key = algo().launch_child(*me->tactical_template, Price(price), Quantity(size));
|
||||||
|
else if(chan == Channel::Aggressive)
|
||||||
|
child_key = algo().launch_child(*me->aggressive_template, Price(price), Quantity(size));
|
||||||
|
else if(chan == Channel::Market)
|
||||||
|
child_key = algo().launch_child(*me->aggressive_template, Price(price), Quantity(size));
|
||||||
|
me->child_orders[child_key] = OrderDef{ child_key, me->generation, chan };
|
||||||
|
}
|
||||||
|
return child_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderKey TimeSlicer::upgrade(OrderKey key, Price price, Channel chan)
|
||||||
|
{
|
||||||
|
cancel_and_remove_child(key);
|
||||||
|
auto cs = me->child_state.find(key);
|
||||||
|
if(cs == me->child_state.end())
|
||||||
|
{
|
||||||
|
algo().terminate_error("can't find child state");
|
||||||
|
return null_order_key();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32_t quan = cs->second.quantity.value() - cs->second.total_fills.value();
|
||||||
|
return send_child(Price(price), Quantity(quan), chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::first_generation()
|
||||||
|
{
|
||||||
|
bts_assert_msg(me->generation == 0, "generation 0");
|
||||||
|
|
||||||
|
double price = me->last_book.side( algo().definition().side() )[0].price();
|
||||||
|
uint32_t size = algo().quantity().value() / me->expected_generations;
|
||||||
|
if(me->duration.tactical_schedule_slippage() > 0)
|
||||||
|
send_child(Price(price), Quantity(size), Channel::Tactical);
|
||||||
|
else
|
||||||
|
send_child(Price(price), Quantity(size), Channel::Aggressive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::next_generation()
|
||||||
|
{
|
||||||
|
int64_t elapsed = utils::pb::pb_now().seconds() - me->start_time.seconds();
|
||||||
|
double gen = elapsed / me->duration.discretization_interval_in_seconds();
|
||||||
|
if(gen <= me->generation)
|
||||||
|
return;
|
||||||
|
if(me->total_filled.value() >= algo().quantity().value())
|
||||||
|
{
|
||||||
|
get_done(); //early
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
me->generation = (uint32_t)gen;
|
||||||
|
double price = me->last_book.side( algo().definition().side() )[0].price();
|
||||||
|
uint32_t size = algo().quantity().value() / me->expected_generations;
|
||||||
|
for(const auto& ch : me->child_orders)
|
||||||
|
{
|
||||||
|
bts_assert_msg(me->generation >= ch.second.generation, "incorrect generation");
|
||||||
|
uint32_t age = me->generation - ch.second.generation;
|
||||||
|
if(age > me->duration.tactical_schedule_slippage())
|
||||||
|
{
|
||||||
|
if(ch.second.channel == Channel::Tactical) //move to aggressive
|
||||||
|
{
|
||||||
|
upgrade(ch.first, Price(price), Channel::Aggressive);
|
||||||
|
}
|
||||||
|
else if(ch.second.channel == Channel::Aggressive) //move to market
|
||||||
|
{
|
||||||
|
upgrade(ch.first, Price(price), Channel::Market);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
send_child(Price(price), Quantity(size), Channel::Tactical);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSlicer::get_done()
|
||||||
|
{
|
||||||
|
me->status = Status::DONE;
|
||||||
|
|
||||||
|
for(const auto& ch : me->child_orders)
|
||||||
|
cancel_and_remove_child(ch.first);
|
||||||
|
|
||||||
|
if(me->total_filled.value() < algo().quantity().value())
|
||||||
|
{
|
||||||
|
uint32_t make_up = algo().quantity().value() - me->total_filled.value();
|
||||||
|
send_child(Price(0.0), Quantity(make_up), Channel::Market);
|
||||||
|
}
|
||||||
|
report_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace orderman
|
||||||
|
} // namespace bts
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||||
|
#include <google/protobuf/timestamp.pb.h>
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace api {
|
||||||
|
namespace orderman {
|
||||||
|
class OrderDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
class ChildTemplate;
|
||||||
|
struct OrderDef;
|
||||||
|
|
||||||
|
class TimeSlicer : public IAlgoOrder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TimeSlicer();
|
||||||
|
~TimeSlicer();
|
||||||
|
|
||||||
|
void on_child_change(OrderKey, const ChildState&) override;
|
||||||
|
void on_book(SecurityKey, const Book&) override;
|
||||||
|
void on_ticker(SecurityKey, const TradeVector&) override;
|
||||||
|
void on_timer() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class Status;
|
||||||
|
enum class Channel { Tactical, Aggressive, Market };
|
||||||
|
|
||||||
|
void set_algo(IAlgoServices*);
|
||||||
|
IAlgoServices& algo() const;
|
||||||
|
|
||||||
|
google::protobuf::Timestamp compute_end_time();
|
||||||
|
void setup_schedule();
|
||||||
|
void get_done();
|
||||||
|
void report_status();
|
||||||
|
|
||||||
|
bool validate_price_size(Price price, Quantity size);
|
||||||
|
OrderKey send_child(Price price, Quantity size, Channel chan);
|
||||||
|
void remove_child(OrderKey k);
|
||||||
|
void cancel_and_remove_child(OrderKey k);
|
||||||
|
OrderKey upgrade(OrderKey key, Price price, Channel chan);
|
||||||
|
|
||||||
|
void _do_update(const api::orderman::Update&,
|
||||||
|
const bts::api::orderman::CustomOrderDefinition& tactical,
|
||||||
|
const bts::api::orderman::CustomOrderDefinition& aggressive);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void update_params() = 0;
|
||||||
|
virtual void first_generation();
|
||||||
|
virtual void next_generation();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct PImpl;
|
||||||
|
friend struct OrderDef;
|
||||||
|
std::unique_ptr<PImpl> me;
|
||||||
|
protected:
|
||||||
|
|
||||||
|
api::orderman::OrderDuration& duration();
|
||||||
|
Status& status();
|
||||||
|
google::protobuf::Timestamp& start_time();
|
||||||
|
google::protobuf::Timestamp& end_time();
|
||||||
|
uint32_t& generation();
|
||||||
|
uint32_t& expected_generations();
|
||||||
|
|
||||||
|
ChildTemplate*& tactical_template();
|
||||||
|
ChildTemplate*& aggressive_template();
|
||||||
|
ChildTemplate*& market_template();
|
||||||
|
|
||||||
|
void add_child(OrderKey k, Channel chan);
|
||||||
|
Quantity& total_filled();
|
||||||
|
Book& last_book();
|
||||||
|
TradeVector& last_trades();
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
#include "lib/orderman/orders/algo/algo_orders/bluex/VWAPAlgo.h"
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||||
|
#include "lib/orderman/orders/algo/DynamicHelpers.h"
|
||||||
|
|
||||||
|
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||||
|
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||||
|
#include "lib/os/os.h"
|
||||||
|
#include "lib/utils/PBUtils.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
using namespace api::orderman;
|
||||||
|
|
||||||
|
enum class VWAPAlgo::Status { WAITING_FOR_MD, WAITING_TO_SCRATCH, SCRATCHED };
|
||||||
|
|
||||||
|
struct VWAPAlgo::PImpl
|
||||||
|
{
|
||||||
|
IAlgoServices *algo;
|
||||||
|
OrderDefinition definition;
|
||||||
|
|
||||||
|
Quantity scratch_threshold;
|
||||||
|
Price chase_diff;
|
||||||
|
Status status = Status::WAITING_FOR_MD;
|
||||||
|
OrderKey child_key = null_order_key();
|
||||||
|
ChildTemplate *child_template = nullptr;
|
||||||
|
Book last_book;
|
||||||
|
Book book_at_inception;
|
||||||
|
Price scratch_price=Price(os::nan());
|
||||||
|
};
|
||||||
|
|
||||||
|
VWAPAlgo::VWAPAlgo() : me(new PImpl()) {}
|
||||||
|
VWAPAlgo::~VWAPAlgo() {}
|
||||||
|
IAlgoServices &VWAPAlgo::algo() const { return *me->algo; }
|
||||||
|
|
||||||
|
OrderOpResult VWAPAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv)
|
||||||
|
{
|
||||||
|
const auto& def = serv.definition();
|
||||||
|
bts_assert(def.has_custom_def());
|
||||||
|
bts_assert(def.custom_def().has_scratch());
|
||||||
|
// Validate the scratch part of the definition
|
||||||
|
auto &scratch = def.custom_def().scratch();
|
||||||
|
if (!scratch.has_child() && OrderKey(def.attach_target_order_key()) == null_order_key())
|
||||||
|
{
|
||||||
|
return OrderOpResult{ ORDER_ERROR_INVALID_ORDER_DEFINITON,
|
||||||
|
"Scratch order must have either a child order definition or an order to attach to"};
|
||||||
|
}
|
||||||
|
if (!serv.is_attaching())
|
||||||
|
{
|
||||||
|
// if we are not attaching create a child template so we can
|
||||||
|
// quickly launch our child when the time comes.
|
||||||
|
auto child_def = def;
|
||||||
|
*child_def.mutable_custom_def() = def.custom_def().scratch().child();
|
||||||
|
me->child_template = &(serv.make_child_template(child_def));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void VWAPAlgo::do_launch(IAlgoServices &algo)
|
||||||
|
{
|
||||||
|
me->algo = &algo;
|
||||||
|
update_scratch_threshold();
|
||||||
|
bts_assert(me->child_template);
|
||||||
|
me->child_key = algo.launch_child(*me->child_template, algo.price(), algo.quantity());
|
||||||
|
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VWAPAlgo::do_launch_attached(IAlgoServices &algo, OrderKey k, ChildState st)
|
||||||
|
{
|
||||||
|
me->algo = &algo;
|
||||||
|
update_scratch_threshold();
|
||||||
|
me->child_key = k;
|
||||||
|
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||||
|
on_child_change(k, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VWAPAlgo::do_update(api::orderman::Update const &update)
|
||||||
|
{
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} receiving update: {}", algo().order_key().value(), utils::pb::to_string(update));
|
||||||
|
|
||||||
|
if (update.has_custom() && !update.custom().has_scratch())
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Update child_update;
|
||||||
|
Price price_to_pass = get_price_to_pass(Price(update.price()));
|
||||||
|
child_update.set_price(price_to_pass.value());
|
||||||
|
child_update.set_quantity(update.quantity());
|
||||||
|
if (update.has_custom())
|
||||||
|
{
|
||||||
|
update_scratch_threshold();
|
||||||
|
auto &scratch_update = update.custom().scratch();
|
||||||
|
*child_update.mutable_custom() = scratch_update.child();
|
||||||
|
}
|
||||||
|
algo().update_child(me->child_key, child_update);
|
||||||
|
|
||||||
|
// If we were not yet scratched it is possible we now can, since we
|
||||||
|
// could have changed the scratch_threshold
|
||||||
|
if (me->status == Status::WAITING_TO_SCRATCH)
|
||||||
|
try_to_scratch();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VWAPAlgo::on_book(SecurityKey, Book const &b)
|
||||||
|
{
|
||||||
|
me->last_book = b;
|
||||||
|
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} on book. BID: {}@{} ASK: {}@{}",
|
||||||
|
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
|
||||||
|
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
|
||||||
|
b.side(api::SIDE_SELL)[0].price());
|
||||||
|
|
||||||
|
switch (me->status)
|
||||||
|
{
|
||||||
|
case Status::WAITING_FOR_MD:
|
||||||
|
{
|
||||||
|
auto other_side = securities::other_side(algo().definition().side());
|
||||||
|
auto &other_side_top = me->last_book.side(other_side)[0];
|
||||||
|
|
||||||
|
if (other_side_top.size() > 0)
|
||||||
|
{
|
||||||
|
me->status = Status::WAITING_TO_SCRATCH;
|
||||||
|
me->book_at_inception = b;
|
||||||
|
LOG(OrderMan, Diag,
|
||||||
|
"ScratchOrder order {} receiving first book: {} Target: {}",
|
||||||
|
algo().order_key().value(), me->scratch_threshold.value(),
|
||||||
|
other_side_top.price());
|
||||||
|
try_to_scratch();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Status::SCRATCHED:
|
||||||
|
break;
|
||||||
|
case Status::WAITING_TO_SCRATCH:
|
||||||
|
try_to_scratch();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VWAPAlgo::try_to_scratch()
|
||||||
|
{
|
||||||
|
bts_assert(me->status == Status::WAITING_TO_SCRATCH);
|
||||||
|
auto my_side = algo().definition().side();
|
||||||
|
auto other_side = securities::other_side(my_side);
|
||||||
|
|
||||||
|
Quantity my_quan;
|
||||||
|
auto &my_side_psz = me->last_book.side(my_side)[0];
|
||||||
|
auto &other_side_psz = me->last_book.side(other_side)[0];
|
||||||
|
auto other_side_price = Price(other_side_psz.price());
|
||||||
|
auto inception_other_side_price = Price(me->book_at_inception.side(other_side)[0].price());
|
||||||
|
|
||||||
|
bool market_moved = !securities::is_equal_or_better(other_side, other_side_price, inception_other_side_price);
|
||||||
|
bool on_market = securities::is_same(Price(my_side_psz.price()), Price(algo().price()));
|
||||||
|
if (market_moved || (on_market && my_side_psz.size() <= me->scratch_threshold.value()) )
|
||||||
|
{
|
||||||
|
Price scratch_price = securities::worsen_price(other_side, inception_other_side_price, me->chase_diff);
|
||||||
|
LOG(OrderMan, Diag, "ScratchOrder order {} scratching: {}@{}",
|
||||||
|
algo().order_key().value(), algo().quantity().value(), scratch_price);
|
||||||
|
|
||||||
|
// Time to scratch
|
||||||
|
Update child_update;
|
||||||
|
me->scratch_price = scratch_price;
|
||||||
|
child_update.set_price(scratch_price.value());
|
||||||
|
//we still want to try to scratch all of it
|
||||||
|
child_update.set_quantity(algo().quantity().value());
|
||||||
|
algo().update_child(me->child_key, child_update);
|
||||||
|
me->status = Status::SCRATCHED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(OrderMan, Diag,
|
||||||
|
"ScratchOrder order {} still waiting to scratch. Order: {}@{} "
|
||||||
|
"My side: {}@{}"
|
||||||
|
"Thresh : {}@{}",
|
||||||
|
algo().order_key().value(), algo().quantity().value(), algo().price().value(),
|
||||||
|
my_side_psz.size(), my_side_psz.price(),
|
||||||
|
me->scratch_threshold.value(), my_quan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Price VWAPAlgo::get_price_to_pass (Price price) const
|
||||||
|
{
|
||||||
|
Price rv;
|
||||||
|
switch (me->status) {
|
||||||
|
case Status::WAITING_FOR_MD:
|
||||||
|
case Status::WAITING_TO_SCRATCH:
|
||||||
|
rv = price;
|
||||||
|
break;
|
||||||
|
case Status::SCRATCHED:
|
||||||
|
rv = me->scratch_price;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VWAPAlgo::update_scratch_threshold()
|
||||||
|
{
|
||||||
|
if (algo().definition().custom_def().scratch().scratch_threshold().quantity_case() ==
|
||||||
|
bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET)
|
||||||
|
{
|
||||||
|
algo().terminate_error("Invalid quantity, terminating");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
me->scratch_threshold = SpreaderUtils::_quantity_threshold(
|
||||||
|
algo().definition().custom_def().scratch().scratch_threshold(), algo().quantity());
|
||||||
|
me->chase_diff = Price(securities::tick_size(
|
||||||
|
algo().security()).value() * algo().definition().custom_def().scratch().chase_ticks());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VWAPAlgo::on_child_change(OrderKey, ChildState const &st)
|
||||||
|
{
|
||||||
|
algo().update_fill_state(st.total_fills, st.last_price);
|
||||||
|
switch (st.status)
|
||||||
|
{
|
||||||
|
case ChildStatus::WORKING:
|
||||||
|
return;
|
||||||
|
case ChildStatus::COMPLETED:
|
||||||
|
algo().complete();
|
||||||
|
return;
|
||||||
|
case ChildStatus::CANCELED:
|
||||||
|
algo().cancel("Unexpected child cancel");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace orderman
|
||||||
|
} // namespace bts
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||||
|
|
||||||
|
namespace bts {
|
||||||
|
namespace orderman {
|
||||||
|
|
||||||
|
class VWAPAlgo : public IAlgoOrder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VWAPAlgo();
|
||||||
|
~VWAPAlgo();
|
||||||
|
|
||||||
|
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices&) override;
|
||||||
|
void do_launch(IAlgoServices &) override;
|
||||||
|
void do_launch_attached(IAlgoServices&,OrderKey, ChildState) override;
|
||||||
|
void do_update(api::orderman::Update const &) override;
|
||||||
|
void on_book(SecurityKey, Book const &) override;
|
||||||
|
void on_child_change(OrderKey, ChildState const &) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class Status;
|
||||||
|
struct PImpl;
|
||||||
|
std::unique_ptr<PImpl> me;
|
||||||
|
IAlgoServices& algo() const;
|
||||||
|
|
||||||
|
void update_scratch_threshold();
|
||||||
|
void try_to_scratch();
|
||||||
|
Price get_price_to_pass (Price price) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
||||||
Reference in new issue
Block a user