Compare commits

...
Author SHA1 Message Date
Kevin Darby d1ed33c2ee fixed tooltips for non dynamic and non generic algos 2019-08-08 11:58:05 -04:00
Kevin Darby 8bc1455813 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-08 11:07:40 -04:00
Kevin Darby ecaa04ecf8 factor 2019-08-08 09:59:14 -04:00
Kevin Darby 44e3070544 add TimeSlicer 2019-08-08 00:05:41 -04:00
Kevin Darby 1815950835 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-07 17:31:50 -04:00
Kevin Darby 8762fbf7ce added twap to algo types 2019-08-06 18:20:10 -04:00
Kevin Darby 0459171d6e moved algo types to config 2019-08-06 18:15:47 -04:00
Kevin Darby fcdf72c5d9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-06 16:57:36 -04:00
Kevin Darby f5caf0b7ba twap bones 2019-08-06 16:57:02 -04:00
Kevin Darby a0f2673b3a basic timing structure down; maybe all these derive from twap 2019-08-05 23:51:28 -04:00
Kevin Darby ba3439b049 twap 2019-08-05 18:21:03 -04:00
Kevin Darby ae4d7f719b Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-02 17:57:21 -04:00
Kevin Darby 64916f6bfc Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-02 17:38:30 -04:00
Kevin Darby 1a9dd1c37a cleaning up this proto 2019-08-01 17:40:21 -04:00
Kevin Darby d77925c568 Merge branch 'prorata' of bitbucket.org:bluetradesys/spark into vwap 2019-08-01 17:11:38 -04:00
Kevin Darby f1f1b68ffb arrival 2019-07-03 22:14:39 -04:00
Kevin Darby 835f453f03 Merge branch 'prorata' of bitbucket.org:bluetradesys/spark into vwap 2019-07-03 15:17:09 -04:00
Kevin Darby 0eb9518485 didn't get far 2019-06-13 23:41:30 -04:00
Kevin Darby b5c70fc34e added price to slice 2019-06-11 23:56:11 -04:00
Kevin Darby a1dba1d7d0 historicaldataservice 2019-06-11 23:12:20 -04:00
Kevin Darby efdea5256f algoflow will probably not be in python 2019-06-11 21:07:55 -04:00
Kevin Darby 5df1e5f383 py notes 2019-06-09 22:50:46 -04:00
Kevin Darby 99750da31d beginnings of python driver for algo development. goal is to allow visual / interactive development environment for
more complex algos.  Will also offer backtesting of bts algos over MBO data.
2019-06-09 22:47:51 -04:00
Kevin Darby 49247dba3f externals 2019-06-09 21:58:11 -04:00
Kevin Darby a25257cb33 update externals for pybind 2019-06-08 23:33:43 -04:00
Kevin Darby b1c180d663 Merge branch 'prorata' of bitbucket.org:bluetradesys/spark into vwap 2019-06-07 16:01:49 -04:00
Kevin Darby 6afb16abcb bones for bluex orders 2019-06-07 15:53:25 -04:00
Kevin Darby 995fa3df35 . 2019-06-06 23:34:25 -04:00
Kevin Darby 18eb28dfd2 waps 2019-06-06 22:08:03 -04:00
Kevin Darby 99cdfadf8d bones for vwap, twap, arrivalprice, pov 2019-06-05 23:30:52 -04:00
20 changed files with 1646 additions and 51 deletions

No files matched your search

+15 -44
View File
@@ -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
View File
@@ -1 +1 @@
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/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
-1
View File
@@ -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);
}
+97
View File
@@ -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 {
+46
View File
@@ -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'
}
}
+7
View File
@@ -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;
};
}}