Compare commits
30
Commits
push
...
fix_tooltips
| 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;
|
||||
}
|
||||
}
|
||||
|
||||
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[]
|
||||
|
||||
// exchange_order_types are the types which can be submitted on an exchange security
|
||||
exchange_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Market,
|
||||
CustomOrderDefinition.CustomOneofCase.Limit,
|
||||
CustomOrderDefinition.CustomOneofCase.Stop,
|
||||
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,
|
||||
};
|
||||
exchange_order_types = algo_config(x => x.exchange_order_types),
|
||||
spreader_order_types = algo_config(x => x.spreader_order_types),
|
||||
attachable_order_types = algo_config(x => x.attachable_order_types),
|
||||
outright_order_types = algo_config(x => x.outright_order_types);
|
||||
|
||||
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) =>
|
||||
|
||||
@@ -687,7 +687,10 @@
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</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}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
@@ -905,7 +908,10 @@
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</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}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
@@ -944,7 +950,10 @@
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</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}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
@@ -1022,9 +1031,16 @@
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</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}"/>
|
||||
<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}"/>
|
||||
<uag: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)
|
||||
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;
|
||||
ProRataOrderDefinition prorata = 32;
|
||||
|
||||
TWAPOrderDefinition twap = 100;
|
||||
VWAPOrderDefinition vwap = 101;
|
||||
POVOrderDefinition pov = 102;
|
||||
ArrivalPriceOrderDefinition arrival = 103;
|
||||
|
||||
DynamicOrderDefinition dynamic = 1000;
|
||||
}
|
||||
}
|
||||
@@ -230,6 +235,89 @@ message ProRataOrderDefinition {
|
||||
CustomOrderDefinition hedge = 20;
|
||||
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 {
|
||||
string dynamic_order_type = 1;
|
||||
map<string, DynamicOrderParam> order_params = 2;
|
||||
@@ -286,6 +374,11 @@ message OrderState {
|
||||
ScratchOrderState scratch = 32;
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -366,6 +459,10 @@ message ProRataOrderState {}
|
||||
message DynamicOrderState {}
|
||||
message QueueHolderState {}
|
||||
message SplitterOrderState {}
|
||||
message TWAPOrderState {}
|
||||
message VWAPOrderState {}
|
||||
message POVOrderState {}
|
||||
message ArrivalPriceOrderState {}
|
||||
|
||||
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 {
|
||||
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:
|
||||
result.mutable_prorata();
|
||||
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:
|
||||
result.mutable_dynamic();
|
||||
break;
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
#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/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/PricerOrder.h"
|
||||
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
|
||||
@@ -205,6 +210,22 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
order_ptr.reset(
|
||||
new orderman::AlgoWrapperOrder(std::make_unique<ProRataAlgo>()));
|
||||
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: {
|
||||
order_ptr = me->dynamic_order_registry.build(
|
||||
def.custom_def().dynamic().dynamic_order_type());
|
||||
@@ -215,6 +236,7 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
order_ptr.reset(new orderman::DynamicOrder());
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
case CustomOrderDefinition::kComposite:
|
||||
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