Compare commits

...
9 changed files with 264 additions and 26 deletions

No files matched your search

+163 -4
View File
@@ -76,7 +76,7 @@ namespace bts.tests
);
var spreader_order1_response = await OrderUtilities.send_order(_order_service, order1_def, -0.30, 1);
Assert.IsTrue(spreader_order1_response.success, "Failed to send first spreader order");
Assert.IsTrue(spreader_order1_response.success, "Failed to send spreader order");
var (spreader_order_is_working, working_spreader_order1) = await verify_order_status(spreader_order1_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(spreader_order_is_working, "Spreader order is not Working");
@@ -120,6 +120,156 @@ namespace bts.tests
}
[Test(Timeout = 300000)]
public async Task spreader_lean_qty_factor() {
await setup_spreader_tests();
string test_symbol = "SI";
var test_user = (name: "bts", id: 1u);
var test_account = (name: "ACC A", id: 1u);
(SecurityImmutable s1, SecurityImmutable s2) test_securities = await SpreaderUtilities.get_securities_for_common_setup_one(_md_relay, test_symbol);
var setup_product = await SpreaderUtilities.common_setup_one(_order_service, _synthetic_service, test_securities, test_account, test_user);
var order1_def = OrderDefinitionUtilities.create_spreader_order_def(
setup_product.synthetic.security,
Side.Buy,
setup_product.synthetic.strategy,
test_account,
test_user
);
var spreader_order1_response = await OrderUtilities.send_order(_order_service, order1_def, -0.30, 1);
Assert.IsTrue(spreader_order1_response.success, "Failed to send spreader order");
var (spreader_order_is_working, working_spreader_order1) = await verify_order_status(spreader_order1_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(spreader_order_is_working, "Spreader order is not Working");
var (quoting_order_is_working, working_quoting_order) = await verify_child_order_status(spreader_order1_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(quoting_order_is_working, "Order is not Working");
Assert.IsTrue(working_quoting_order.Value.State.Price == 18.6, "Quoting order has the wrong price");
Assert.IsTrue(await OrderUtilities.cancel_orders(_order_service, new List<OrderRecordImmutable> {working_spreader_order1.Value}), "Failed to cancel order");
var (quoting_order_is_canceled, canceled_quoting_order) = await verify_child_order_status(spreader_order1_response.value.OrderKey, OrderStatus.Canceled);
Assert.IsTrue(quoting_order_is_canceled, "Quoting order may not have been canceled");
// change the synthetic
var hedge_leg = setup_product.synthetic.strategy.LegParamsMap.Where(m => !m.Value.IsActiveQuoting).TryFirst();
Assert.IsTrue(hedge_leg.HasValue, "you need more legs");
foreach(var key in setup_product.synthetic.strategy.LegParamsMap.Keys) {
if(!setup_product.synthetic.strategy.LegParamsMap[key].IsActiveQuoting) {
setup_product.synthetic.strategy.LegParamsMap[key].QuoteParams.LeanQuantityRatio = 2d;
break;
}
}
Assert.IsTrue(await SpreaderUtilities.save_strategy(_synthetic_service, setup_product.synthetic.security.Id, (ExecutionStrategyImmutable) setup_product.synthetic.strategy), "Failed to save strategy");
var order2_def = OrderDefinitionUtilities.create_spreader_order_def(
setup_product.synthetic.security,
Side.Buy,
setup_product.synthetic.strategy,
test_account,
test_user
);
var spreader_order2_response = await OrderUtilities.send_order(_order_service, order2_def, -0.30, 1);
Assert.IsTrue(spreader_order2_response.success, "Failed to send spreader order");
var (spreader_order2_is_working, working_spreader_order2) = await verify_order_status(spreader_order2_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(spreader_order2_is_working, "Spreader order is not Working");
var (quoting_order2_is_working, working_quoting_order2) = await verify_child_order_status(spreader_order2_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(quoting_order2_is_working, "Order is not Working");
Assert.IsTrue(working_quoting_order2.Value.State.Price == 18.61, "Quoting order has the wrong price");
Assert.IsTrue(await OrderUtilities.cancel_orders(_order_service, new List<OrderRecordImmutable> {working_spreader_order2.Value}), "Failed to cancel order");
var (quoting_order2_is_canceled, canceled_quoting_order2) = await verify_child_order_status(spreader_order2_response.value.OrderKey, OrderStatus.Canceled);
Assert.IsTrue(quoting_order2_is_canceled, "Quoting order may not have been canceled");
// Order 3
foreach(var key in setup_product.synthetic.strategy.LegParamsMap.Keys) {
if(!setup_product.synthetic.strategy.LegParamsMap[key].IsActiveQuoting) {
setup_product.synthetic.strategy.LegParamsMap[key].QuoteParams.LeanQuantityRatio = 4d;
break;
}
}
Assert.IsTrue(await SpreaderUtilities.save_strategy(_synthetic_service, setup_product.synthetic.security.Id, (ExecutionStrategyImmutable) setup_product.synthetic.strategy), "Failed to save strategy");
var order3_def = OrderDefinitionUtilities.create_spreader_order_def(
setup_product.synthetic.security,
Side.Buy,
setup_product.synthetic.strategy,
test_account,
test_user
);
var spreader_order3_response = await OrderUtilities.send_order(_order_service, order3_def, -0.30, 1);
Assert.IsTrue(spreader_order3_response.success, "Failed to send spreader order");
var (spreader_order3_is_working, working_spreader_order3) = await verify_order_status(spreader_order3_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(spreader_order3_is_working, "Spreader order is not Working");
var (quoting_order3_is_working, working_quoting_order3) = await verify_child_order_status(spreader_order3_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(quoting_order3_is_working, "Order is not Working");
Assert.IsTrue(working_quoting_order3.Value.State.Price == 18.63, "Quoting order has the wrong price");
Assert.IsTrue(await OrderUtilities.cancel_orders(_order_service, new List<OrderRecordImmutable> {working_spreader_order3.Value}), "Failed to cancel order");
var (quoting_order3_is_canceled, canceled_quoting_order3) = await verify_child_order_status(spreader_order3_response.value.OrderKey, OrderStatus.Canceled);
Assert.IsTrue(quoting_order3_is_canceled, "Quoting order may not have been canceled");
// Order 4
foreach(var key in setup_product.synthetic.strategy.LegParamsMap.Keys) {
if(!setup_product.synthetic.strategy.LegParamsMap[key].IsActiveQuoting) {
setup_product.synthetic.strategy.LegParamsMap[key].QuoteParams.LeanQuantityRatio = 5d;
break;
}
}
Assert.IsTrue(await SpreaderUtilities.save_strategy(_synthetic_service, setup_product.synthetic.security.Id, (ExecutionStrategyImmutable) setup_product.synthetic.strategy), "Failed to save strategy");
var order4_def = OrderDefinitionUtilities.create_spreader_order_def(
setup_product.synthetic.security,
Side.Buy,
setup_product.synthetic.strategy,
test_account,
test_user
);
var spreader_order4_response = await OrderUtilities.send_order(_order_service, order4_def, -0.30, 1);
Assert.IsTrue(spreader_order4_response.success, "Failed to send spreader order");
var (spreader_order4_is_working, working_spreader_order4) = await verify_order_status(spreader_order4_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(spreader_order4_is_working, "Spreader order is not Working");
var no_quoting_order4 = await get_order(ord => ord.ParentKey == spreader_order4_response.value.OrderKey, TimeSpan.FromSeconds(10));
Assert.IsFalse(no_quoting_order4.HasValue, "There should not have been a quoting order here...");
OrderRecordImmutable order_from_book = setup_product.book.Where(ordi => ordi.State.Price == 18.30).First();
var mod_resp = await OrderUtilities.modify_order(_order_service, order_from_book, 2);
Assert.IsTrue(mod_resp.success, "Order size was not changed");
var (quoting_order4_is_working, working_quoting_order4) = await verify_child_order_status(spreader_order4_response.value.OrderKey, OrderStatus.Working);
Assert.IsTrue(quoting_order4_is_working, "Order is not Working");
Assert.IsTrue(working_quoting_order4.Value.State.Price == 18.63, "Quoting order has the wrong price");
var mod_resp2 = await OrderUtilities.modify_order(_order_service, order_from_book, 1);
Assert.IsTrue(mod_resp.success, "Order size was not changed");
var (quoting_order5_is_canceled, canceled_quoting_order5) = await verify_child_order_status(spreader_order4_response.value.OrderKey, OrderStatus.Canceled);
Assert.IsTrue(quoting_order5_is_canceled, "Order was not canceld");
Assert.IsTrue(await OrderUtilities.cancel_orders(_order_service, new List<OrderRecordImmutable> {working_spreader_order4.Value}), "Failed to cancel order");
}
private async Task<(Boolean, OrderRecordImmutable?)> verify_child_order_status(uint parent_order_key, OrderStatus expected_status) {
var order = await get_order(ordi => ordi.ParentKey == parent_order_key && ordi.State.Status == expected_status);
return (order.HasValue && order.Value.State.Status == expected_status, order);
@@ -140,13 +290,22 @@ namespace bts.tests
return order.HasValue && (Math.Truncate(order.Value.State.Price * precision) == precision * expected_price);
}
private async Task<OrderRecordImmutable?> get_order(Func<OrderRecordImmutable, bool> predicate)
private async Task<OrderRecordImmutable?> get_order(Func<OrderRecordImmutable, bool> predicate, TimeSpan? timeout = null)
{
if(timeout == null){
timeout = TimeSpan.FromSeconds(60);
}
var orders = await OrderUtilities.wait_for_filtered_orders(_order_service,
o => o.Where(predicate).Count() > 0,
ord => ord.Where(predicate),
TimeSpan.FromSeconds(30));
return orders.result.TryFirst();
timeout);
if(orders.result != null) {
return orders.result.TryFirst();
}
else {
return null;
}
}
@@ -75,6 +75,10 @@ namespace bts.tests
});
var tr = await tcs.Task.timeout(timeout.Value);
sub.Dispose();
if(tr.result == null) {
Console.WriteLine("Timed out while retreving orders.");
}
return tr;
}
@@ -124,7 +128,7 @@ namespace bts.tests
}
/// <summary>
/// We should replace with something like the OrderDataHeler from the Spark project.
/// We should replace with something like the OrderDataHelper from the Spark project.
/// </summary>
public static class OrderDefinitionUtilities {
@@ -27,8 +27,24 @@ namespace bts.tests
Assert.IsNotNull(exps);
Assert.IsTrue(exps.Count() > 2, "Need at least two expirations");
var exp1 = exps[0];
var exp2 = exps[1];
(uint, uint) preferred_expirations = (07,12);
FutureContract exp1;
FutureContract exp2;
if(exps.Exists(expir => expir.ExpirationMonth == preferred_expirations.Item1)) {
exp1 = exps.Where(ex => ex.ExpirationMonth == preferred_expirations.Item1).First();
}
else {
exp1 = exps[0];
}
if(exps.Exists(expir => expir.ExpirationMonth == preferred_expirations.Item2 && expir.ExpirationYear == exp1.ExpirationYear)) {
exp2 = exps.Where(ex => ex.ExpirationMonth == preferred_expirations.Item2 && ex.ExpirationYear == exp1.ExpirationYear).First();
}
else {
exp2 = exps[1];
}
Func<ChannelUpdate, bool> sec1_match = ch =>
ch.Security.Future.ExpirationYear == exp1.ExpirationYear &&
@@ -103,13 +119,15 @@ namespace bts.tests
new_synth.Synthetic.Legs.Add(legs.Select(l => l.contract));
new_synth.Symbol = deduce_symbol(new_synth);
new_synth.ContractSpec = new ContractSpecification();
new_synth.ContractSpec.LowTick = 0.01;
new_synth.ContractSpec.HighTick = 0.01;
new_synth.ContractSpec.LowTick = 0.005;
new_synth.ContractSpec.HighTick = 0.005;
new_synth.ContractSpec.PriceBreak = double.MaxValue;
new_synth.ContractSpec.MainFraction = 1u;
new_synth.ContractSpec.PriceDisplayFormat = 3u;
new_synth.ContractSpec.CashPerPoint = 1u;
new_synth.ContractSpec.PriceMultiplier = 1u;
new_synth.Exchange = Venue.Bts;
new_synth.Synthetic.Name = "TEST_SYNTH" + DateTime.Now.ToString();
new_synth.Synthetic.Name = "TEST_SYNTH" + DateTime.Now.TimeOfDay.ToString();
var result = await service.create_synthetic_async(new_synth);
Assert.IsTrue(result.success, "Unable to create synthetic");
@@ -0,0 +1,52 @@
#!/bin/bash
if [ -d ~/sparkdata/ ]
then
sparkdata_dir=~/sparkdata
else
sparkdata_dir=/sparkdata
fi
./delete_dbs.sh
config=$sparkdata_dir/conf/spark.conf
bak=$sparkdata_dir/conf/spark.conf.bak
[ ! -d "$sparkdata_dir/conf" ] && mkdir $sparkdata_dir/conf/
[ -f "$conf" ] && mv $conf $bak
cat << EOF > $sparkdata_dir/conf/spark.conf
ExchangeSim {
value {
merge_with_md:false
}
}
Logging {
value {
console { default_level: "Error" }
syslog { default_level: "Error" }
file { default_level: "Error" }
}
}
SymbolList {
value {
symbol: "SI"
}
}
EOF
nohup ./sparkd run &
pid=$!
echo "sparkd pid: " $pid
dotnet sparkcmd.dll test spreader_lean_qty_factor
exit_status=$?
pkill -P $$
[ -f "$bak" ] && mv $bak $conf
[ $exit_status -eq 0 ] && echo "Test Finished" || echo "Test failed"
exit $exit_status
+8 -4
View File
@@ -58,7 +58,7 @@ namespace interop {
// Functions used for interop to other languages
/////////////////////////////////////////////////
std::string getbuffer_serialized_buffer;
extern "C" BTS_EXPORT const char* get_buffer(const char* key_name, int& out_length)
{
@@ -69,7 +69,7 @@ extern "C" BTS_EXPORT const char* get_buffer(const char* key_name, int& out_leng
getbuffer_serialized_buffer.clear();
message.SerializePartialToString(&getbuffer_serialized_buffer);
out_length = getbuffer_serialized_buffer.size();
return getbuffer_serialized_buffer.c_str();
}
@@ -89,10 +89,14 @@ namespace internal {
// Functions used in testing
//////////////////////////////////////////
extern "C" BTS_EXPORT void initialize_for_test(const std::string& values, bool print_errors)
extern "C" BTS_EXPORT void initialize_for_test(const char* values, bool print_errors)
{
config_state.reset();
config_state.load_string(values, print_errors);
config_state.load_string(std::string(values), print_errors);
}
void initialize_for_test(const std::string& value, bool print_errors) {
config_state.reset();
config_state.load_string(value, print_errors);
}
extern "C" BTS_EXPORT void reset()
+3 -2
View File
@@ -22,7 +22,7 @@ namespace config {
// Load and initialize config.
// This will be automatically called on the first key request but since loading config
// can result in network requests this function is available for applications that want to
// manage pauses in execution.
// manage pauses in execution.
// Ex: initialize() on a background thread so that config::get<T>() is fast on a UI thread
extern "C" BTS_EXPORT void initialize();
@@ -56,7 +56,8 @@ namespace config {
{
// Initialize configuration state from a string instead of from the filesystem. defaults.conf will still be loaded.
// Calling this a second time clears all values from previous initialization
extern "C" BTS_EXPORT void initialize_for_test(const std::string& configuration, bool print_errors=true);
extern "C" BTS_EXPORT void initialize_for_test(const char* configuration, bool print_errors=true);
void initialize_for_test(const std::string& configuration, bool print_errors=true);
// Releases all resources and returns config to factory defaults
extern "C" BTS_EXPORT void reset();
@@ -2,9 +2,9 @@ This test verifies that fractional hedges work. That is, when a fill in a quotin
not equate to one full contract in the hedging leg, we accumulate the unhedged
delta, until we can send one or more hedging contracts.
1. Create a new synthetic spread. For the legs choose SIF9 and SIH9 (if you dont
1. Create a new synthetic spread. For the legs choose SIF9 and SIH9 (if you don't
have these contracts, other contracts can be chosen too) and for the quantities choose 1 and -1.5
respectively (and same for the Price multiplier).
respectively (and same for the Price multiplier).
Enable quoting for SIH9, and disable all quoting, pre-hedge and post-hedge parameters.
Name the synthetic SIF9-1.5SIH9.
@@ -18,13 +18,13 @@ to enter orders). The quoting order will lean against this.
4. Verify that a 15 lot quoting order is sent for SIH9 at 19.935.
5. Now we will start filling the quoting order and verify the hedges. A 1 lot fill in the
quoting leg means a 2/3 contract in the hedging leg. Since we can not send 2/3 constracts,
quoting leg means a 2/3 contract in the hedging leg. Since we can not send 2/3 contracts,
we will add it to the "unhedged delta" till the unhedged delta > 1, and then we will send one
or more orders to get the unhedged delta < 1.
Force quoting fills based on the left most column (by lifting the quoting offer)
and verify that hedge orders of size "hedge_order_sent" (3rd column) are sent as a result.
The "unhedged_delta" column shows the unhedged delta immediatly after the quoting fill
The "unhedged_delta" column shows the unhedged delta immediately after the quoting fill
and the "remaining_unhedged_delta" column is the unhedged delta after the hedge order
has been sent.
You can check the hedge orders being sent easily by looking at the order reduction
@@ -10,7 +10,7 @@ ExchangeSim {
}
}
1. Open up the mview and find a security with with both direct and implied markets, on the ask side.
1. Open up the mview and find a security with with both direct and implied markets, on the ask side.
Make sure those markets are stable and you can rely on them.
2. Create a synthetic with the security in 1. being the hedge leg (buy) and choose a quoting
@@ -19,11 +19,11 @@ use "Merged"
3. Disable all spreader params.
4. Now enter a spreader order so that it doesnt get filled immediatly. Note the quoting order.
4. Now enter a spreader order so that it doesn't get filled immediately. Note the quoting order.
quoting leg price = hedge_price - spreader_price.
hedge_price is where it can buy the hedge order (which since its a buy will be on the ask side of
the hedging market).
Make sure thta its correct and its using the merged book in the hedging market.
Make sure that its correct and its using the merged book in the hedging market.
5. Cancel the order and change the spreader so its using Direct prices.
Repeat 4, and make sure its using direct price on the hedging market.
+3 -3
View File
@@ -5,8 +5,8 @@ with 1 quoting leg.
1. Create a 4 legged synthetic spread with legs SIF9, SIH9, SIK9, SIN9, with leg quantities and
price multipliers of 1,1,1 and -1 respectively. Leg SIN9 is the only quoting leg.
Disable all Quote, Pre-Hedge and Post-Hedge paramaters (leg contracts are arbitrary, so if you
dont have those contracts, choose any set of 4 contracts).
Disable all Quote, Pre-Hedge and Post-Hedge parameters (leg contracts are arbitrary, so if you
don't have those contracts, choose any set of 4 contracts).
2. Enter a synthetic buy order of 10 lots at 40.00. The ladders will most likely be empty, so
use the market view to enter orders.
@@ -16,7 +16,7 @@ Now enter:
SIH9 10 lot sell @ 19.950
SIK9 10 lot sell @ 20.200
There should still be no quoting order for SIN9 since the SIF9 hedge leg still doesnt have a market.
There should still be no quoting order for SIN9 since the SIF9 hedge leg still doesn't have a market.
Now Enter:
SIF9 1 lot sell @ 20.000