Compare commits

...
479 Commits
Author SHA1 Message Date
Kevin Darby 704819c3e3 min cost of a tick 2020-02-24 09:48:42 -05:00
Kevin Darby 526a0a0a29 need a better variance 2020-02-21 16:44:52 -05:00
Kevin Darby 759ba55551 build 2020-02-20 21:58:49 -05:00
Kevin Darby 4ef82cc4ab aggressive vs passive 2020-02-20 21:42:57 -05:00
Kevin Darby a9834e6df8 unsaved vwap changes going home because of snow 2020-02-20 16:31:44 -05:00
Kevin Darby f2d592f9c7 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2020-02-19 18:18:00 -05:00
Kevin Darby 26fda7f88f arrival price 2020-02-19 18:17:44 -05:00
Jake Norbut 9864d0b0fc using enums instead of magic numbers; OrderID to NONE on cancel(replace) rejects 2020-02-19 16:36:08 -06:00
Jake Norbut 59692852b5 New, Cancel-Replace, Cancel client sample FIX sequence 2020-02-19 16:33:49 -06:00
Jake Norbut ed3bc9f71c Cancel and Cancel-Replace shoud use OrigClOrdID to lookup bts key 2020-02-19 16:32:50 -06:00
Jake Norbut a2c7481301 populate new ExternalOrderId field with ClOrdID 2020-02-19 16:30:48 -06:00
Jake Norbut f1fdaf1aca dict for external id vs bts key 2020-02-19 16:28:35 -06:00
Kevin Darby bf34963b88 switch fix to use new streaming marketdata (securities only) 2020-02-19 14:46:39 -05:00
Kevin Darby ed1609a89f fixing casing in MarketDataService.proto 2020-02-19 14:28:03 -05:00
Kevin Darby c66202117f Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2020-02-19 14:22:40 -05:00
Kevin Darby 49342e30dd Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2020-02-19 14:18:59 -05:00
Kevin Darby 20b8d095d8 historical data looking ok 2020-02-19 14:18:11 -05:00
Kevin Darby 995b4cbefe added external_order_id to our order def for echo back in order state to facilitate incoming FIX 2020-02-19 10:48:57 -05:00
Kevin Darby fb8c603b18 added cache to the server, seems like somehow do_before_launch is getting called twice 2020-02-18 18:15:35 -05:00
Kevin Darby 743b606030 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2020-02-18 17:07:32 -05:00
Kevin Darby 4950796660 adding logs 2020-02-18 17:06:51 -05:00
Kevin Darby 417fccb03c utc / local errors in sparkcmd 2020-02-18 16:53:07 -05:00
Jake Norbut 3014ceb85f fixing malformed cancel-reject message 2020-02-18 15:02:07 -06:00
Kevin Darby c1efbc00a5 arrival shaping up 2020-02-17 18:18:20 -05:00
Kevin Darby b8851f36bb getting these algos back in my head 2020-02-17 16:48:59 -05:00
Kevin Darby 9de3d80891 getting TradingDistribution data into algo 2020-02-13 18:37:31 -05:00
Kevin Darby f07fd7044f price / volume distribution looking good 2020-02-12 17:30:19 -05:00
Kevin Darby f025eed845 hooking excel to historical_query for testing 2020-02-11 18:10:01 -05:00
Kevin Darby 6a8930f594 fixed a warning, factored interface 2020-02-04 14:52:58 -05:00
Kevin Darby e49e11feb5 historical data service 2020-02-03 21:45:37 -05:00
Kevin Darby a6d4a05253 filling out volume and price distributions 2020-01-31 18:19:50 -05:00
Kevin Darby db0ff984cb new externals 2020-01-31 10:37:42 -05:00
Kevin Darby 770c54ee88 working on volume distribution 2020-01-30 18:17:46 -05:00
Kevin Darby 7ac87269c2 making my own trades files from mbo data; the exchange supplied stuff doesn't do spreads correctly 2020-01-30 17:14:07 -05:00
Kevin Darby 3a2a52d708 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2020-01-30 09:45:25 -05:00
Kevin Darby 7119e18436 Merge branch 'master' of bitbucket.org:bluetradesys/spark into dotnet 2020-01-21 10:24:48 -05:00
Kevin Darby a7b8d39787 getting twap back working 2020-01-15 18:41:31 -05:00
Kevin Darby ce88ebcebe som setup screen shots 2020-01-15 12:16:45 -05:00
Kevin Darby 1b8cca55e5 fake spreader is probably a little more trouble than worth 2020-01-14 17:10:17 -05:00
Kevin Darby a8f26e998b corn notebook 2020-01-14 14:51:58 -05:00
Kevin Darby 55eb830c7c upstream fix parser changes into historical stuff 2020-01-14 14:30:54 -05:00
Kevin Darby 0230ac06bd Merge branch 'cqg' of bitbucket.org:bluetradesys/spark into vwap 2020-01-14 14:04:50 -05:00
Kevin Darby 83005d7439 algos over fix 2020-01-14 13:28:53 -05:00
Kevin Darby a74da85f18 Merge branch 'cqg' of bitbucket.org:bluetradesys/spark into vwap 2020-01-13 15:54:15 -05:00
Kevin Darby 4872e8ad7f create, update, delete limit orders works 2020-01-13 14:57:11 -05:00
Kevin Darby 065f92937c added note about node 2020-01-11 16:39:01 -05:00
Kevin Darby cdfb60f5d9 native orders going through 2020-01-10 18:14:28 -05:00
Kevin Darby f2b407fdac json file for algos 2020-01-10 13:51:12 -05:00
Kevin Darby 4c4fa77d3a algo structure in a file 2020-01-09 22:47:00 -05:00
Kevin Darby 2aa5a02ce2 putting composites in a file 2020-01-09 18:13:20 -05:00
Kevin Darby 5e1f642076 algo types 2020-01-09 17:18:56 -05:00
Kevin Darby 90b4c7e26f starting cqg integration 2020-01-09 15:50:59 -05:00
Kevin Darby eccc3f5f38 fix merge error 2020-01-09 10:48:34 -05:00
Kevin Darby de20957ae7 change comment 2020-01-09 09:53:58 -05:00
Kevin Darby e410322fcd Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2020-01-08 23:41:57 -05:00
Kevin Darby 85cb481849 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2020-01-08 23:41:23 -05:00
Kevin Darby f7dec1161a build 2020-01-08 18:09:53 -05:00
Kevin Darby f5e826b095 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into cqg 2020-01-08 18:05:25 -05:00
Kevin Darby 4cd3bc40d9 note about not sharing externals 2020-01-08 18:02:00 -05:00
Kevin Darby 3800f3344b netcore 3.1 2020-01-08 17:35:19 -05:00
Kevin Darby 6ad139804f Merge branch 'windows_docker' of bitbucket.org:bluetradesys/spark into dotnet 2020-01-08 17:33:28 -05:00
Kevin Darby 45d5c2d7cd docker / externals 2020-01-08 17:32:19 -05:00
Kevin Darby 0d24062f03 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into windows_docker 2020-01-08 17:25:30 -05:00
Kevin Darby 44e0eae281 Merge branch 'master' of bitbucket.org:bluetradesys/spark into dotnet 2020-01-08 17:24:55 -05:00
Kevin Darby 25805cf675 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2020-01-08 17:22:14 -05:00
Kevin Darby 152ab05b4e remove externals sharing 2020-01-08 17:08:49 -05:00
kevin b7a733a306 fixed_ext 2020-01-08 14:00:16 -06:00
Kevin Darby 11d7291acb Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2020-01-08 14:12:54 -05:00
Kevin Darby d17b643e31 thought better of this assertion 2020-01-08 14:05:20 -05:00
Kevin Darby 8832e97f52 update docker 2020-01-08 12:11:51 -05:00
Kevin Darby c971d07958 MES_ES looking ok 2020-01-07 18:10:30 -05:00
Kevin Darby 55bb13e536 TED spread seems to work... 2020-01-07 17:18:29 -05:00
Kevin Darby f7c37c0a61 more TED 2020-01-07 15:33:37 -05:00
Kevin Darby f742364e46 MES vs ES and TED spread 2020-01-06 18:24:15 -05:00
Kevin Darby 3b6eef465a ted spread 2020-01-06 18:20:28 -05:00
Kevin Darby 3d93038bc6 build trouble 2020-01-05 22:01:47 -05:00
Kevin Darby 8d3b4f4957 somehow onix ice external sym links got delete 2020-01-05 21:24:54 -05:00
Kevin Darby d1764997d3 fix parse bug for scientific notation 2020-01-05 21:24:48 -05:00
Kevin Darby 2cf9974b73 for ted need to multiply quote leg 2020-01-03 18:51:46 -05:00
Kevin Darby 7f41cb6ca1 ted notebook 2020-01-03 18:46:58 -05:00
Kevin Darby 8c408dd06e moved screenshots 2020-01-03 18:26:44 -05:00
Kevin Darby 40aad212db screenshots 2020-01-03 18:25:27 -05:00
Kevin Darby 872c42d12c fix dollar pnl 2020-01-03 18:03:56 -05:00
Kevin Darby c80cdd8a52 added dollars to pnl output and lean_factor to FakeSpreader 2020-01-03 17:26:28 -05:00
Kevin Darby ffcefa06aa gc nb 2020-01-02 18:33:13 -05:00
Kevin Darby c038bf0f89 add price factor for gc/mgc spreader 2020-01-02 18:23:20 -05:00
Kevin Darby f93dd9f307 clean up log 2020-01-02 15:36:29 -05:00
Kevin Darby 1939c9c3c0 bug in stacker for 3 length 2020-01-02 15:34:27 -05:00
Kevin Darby 68b37ccfc8 added order stacker, had to change algo creation a bit to facilitate 2020-01-02 14:37:26 -05:00
Kevin Darby c5d77d4a70 corrected merge issue 2020-01-01 22:50:57 -05:00
Kevin Darby cbec2476be stacker 2020-01-01 18:13:57 -05:00
Kevin Darby 9fd3563ef8 stacker 2020-01-01 18:11:18 -05:00
Kevin Darby b66d7b50f5 changes in roll_query notebook 2020-01-01 16:18:45 -05:00
Kevin Darby fcf6ed5a96 added a get_done() function so we can get pnl for strategies that arent' done executing in our query time 2020-01-01 13:44:48 -05:00
Kevin Darby ae0aa8be2a looks like we could make a little money on roll 2019-12-31 17:31:22 -05:00
Kevin Darby 11ee1357d4 compute algo_pnl 2019-12-31 15:38:26 -05:00
Kevin Darby 937fcf9fb5 added some diagnostics, seems like we can get filled on roll 2019-12-31 15:23:10 -05:00
Kevin Darby 7601fb0a7d Merge branch 'windows_docker' of bitbucket.org:bluetradesys/spark into vwap 2019-12-31 12:40:29 -05:00
Kevin Darby 4257a172c1 fixed shared obj dir on linux build 2019-12-31 12:39:54 -05:00
Kevin Darby e38b173a0e added spark_vm_shell for windows
# Conflicts:
#	src/client/spark_historical/Notebooks/roll_cover.ipynb
2019-12-31 12:39:24 -05:00
Kevin Darby 8afeedc488 fill model logging 2019-12-31 12:14:37 -05:00
Kevin Darby f43bf20a54 Merge branch 'windows_docker' of bitbucket.org:bluetradesys/spark into vwap 2019-12-31 11:16:06 -05:00
Kevin Darby 721a08c4b4 remove these CMakeDeps so gitignore can ignore 2019-12-31 10:44:32 -05:00
Kevin Darby 41a3535e84 made some minor changes to support running a dev environment in our api docker on windows 2019-12-31 10:07:55 -05:00
Kevin Darby 626629bf06 added spark_vm_shell for windows 2019-12-31 10:06:54 -05:00
Kevin Darby 6ec2ee2182 added spark_vm_shell for windows 2019-12-30 12:52:24 -05:00
Kevin Darby 8c89a91164 fix bug in k fill model, change setup_notes to reflect .netcore 3.1 2019-12-30 12:10:08 -05:00
Kevin Darby 58c05b301a KFillModel2, heuristic 2019-12-26 23:07:17 -05:00
Kevin Darby f5e2a3c28f debugging KFillModel, realizing I don't have all the info here 2019-12-24 00:17:40 -05:00
Kevin Darby 0fd45ba92a . 2019-12-23 00:15:10 -05:00
Kevin Darby 4e440fc218 debugging KFillModel 2019-12-22 16:46:06 -05:00
Kevin Darby 41c8351709 ready to test KFillModel 2019-12-22 15:14:38 -05:00
Kevin Darby 1238648c2b leveling applies to those orders that did not receive prorata 2019-12-20 23:43:34 -05:00
Kevin Darby 03ac88683f KFillModel looks to be code complete, needs debug / test 2019-12-20 23:37:39 -05:00
Kevin Darby fe4c057b20 working on KFillModel 2019-12-20 18:18:40 -05:00
Kevin Darby e6c6997e46 prorata start 2019-12-19 23:47:41 -05:00
Kevin Darby ddd8778bab added some comments for taha, some display stuff for notebook 2019-12-19 18:09:02 -05:00
Kevin Darby f95d258210 put real life data with notebooks 2019-12-19 17:13:00 -05:00
Kevin Darby a2e1cf8dd5 moving code around 2019-12-19 15:54:37 -05:00
Kevin Darby 0b8b68e6e6 Merge branch 'csv_export' of bitbucket.org:bluetradesys/spark into vwap 2019-12-19 14:48:38 -05:00
Kevin Darby 8658a11d1f 100 ticks in one day...not enough 2019-12-19 11:20:58 -05:00
Kevin Darby d0aa90131a top doesn't do much 2019-12-19 00:35:11 -05:00
Kevin Darby ffb30177b1 trying top 2019-12-19 00:24:57 -05:00
Kevin Darby 297093f9ce honing in 2019-12-18 23:41:31 -05:00
Kevin Darby 5ba8440877 couple small bugs and some diagnostics 2019-12-18 22:35:35 -05:00
Kevin Darby 228e49d152 taking this home 2019-12-18 18:28:39 -05:00
Kevin Darby a9e983a717 we rollin 2019-12-18 15:32:19 -05:00
Jake Norbut 25445d58d0 refactoring TaS CSV export 2019-12-18 13:48:14 -06:00
Jake Norbut 0b313a8aa8 refactoring Fills CSV export 2019-12-18 13:47:36 -06:00
Jake Norbut 54f954fd4b adding CSV export to order book 2019-12-18 13:47:03 -06:00
Jake Norbut 980f38ba03 using OrderedDictionary to CSVs are output with a predictable column order 2019-12-18 13:46:22 -06:00
Kevin Darby 198086e639 got roll going 2019-12-18 13:16:49 -05:00
Jake Norbut 9eada2724b Having the Command be on the Window rather than the VM; nice thinking John 2019-12-18 10:11:10 -06:00
Jake Norbut b5f53ee918 adding csv_properties to TimeAndSalesVM.Row 2019-12-18 10:10:04 -06:00
Kevin Darby a3a8eb14ad trying another way 2019-12-18 00:07:34 -05:00
Kevin Darby 333acf0822 progress 2019-12-17 18:17:45 -05:00
Jake Norbut efc175f654 adding export to csv command to Fills window 2019-12-17 10:32:58 -06:00
Jake Norbut 6074afd999 adding csv export command to fills vm 2019-12-17 10:32:15 -06:00
Jake Norbut 219dff4055 adding new export_csv IDialogCommand which just creates a new file 2019-12-17 10:28:57 -06:00
Jake Norbut 9a3b07b622 adding ToCsv IEnumerable ext method in a new util namespace 2019-12-17 10:21:38 -06:00
Kevin Darby 1af49376b6 progress in passive fills model 2019-12-16 23:42:58 -05:00
Kevin Darby f90f6cfb33 using sortedlist for our orders in fifo 2019-12-16 23:27:29 -05:00
Kevin Darby 6433f9ba98 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-12-16 21:39:53 -05:00
Kevin Darby cc1882ea1a trying to add iceberg reup to passive fill stream 2019-12-16 18:46:30 -05:00
Kevin Darby 56e83a1e69 fix timestamp bug 2019-12-16 15:17:59 -05:00
Eric Harding 487aaef02f update cmake 2019-12-16 13:12:50 -05:00
Eric Harding f22fc03e56 merge master 2019-12-16 12:54:19 -05:00
Kevin Darby 69d429fa4e nb 2019-12-15 23:08:36 -05:00
Kevin Darby e8c9e70a4a change name of methods 2019-12-15 21:30:22 -05:00
Kevin Darby 6c78850902 need to deduct from our orders in fifo model 2019-12-15 14:24:14 -05:00
Kevin Darby adab377e44 got some passive fills, need to verify 2019-12-13 23:09:31 -05:00
Kevin Darby 7bdaa35768 passive fifo fill model 2019-12-13 22:14:02 -05:00
Kevin Darby 1c7f79f293 icomparer 2019-12-13 18:18:47 -05:00
Kevin Darby 3a7246c5b0 time to leave 2019-12-13 18:16:59 -05:00
Kevin Darby 3bd95de839 bugs 2019-12-13 18:14:23 -05:00
Kevin Darby 576d3836b3 fills by query 2019-12-13 18:05:41 -05:00
Kevin Darby f1788b172c trying to layer these as queries 2019-12-13 16:53:56 -05:00
Kevin Darby ae5912d41d now we're cookin 2019-12-13 14:36:15 -05:00
Kevin Darby adbab4057f renames 2019-12-13 14:15:29 -05:00
Kevin Darby ca4a639b86 changed IFillModel to IMatchingModel, because that's really what it is, now I need fill model 2019-12-13 14:12:55 -05:00
Kevin Darby a539a06f45 interim push to go more stateless 2019-12-13 13:33:18 -05:00
Kevin Darby 1a87544ccb orders 2019-12-12 23:51:18 -05:00
Kevin Darby b97450297e update notes 2019-12-12 23:10:07 -05:00
Kevin Darby 6a1040a339 regen for trade counter parties 2019-12-12 23:07:49 -05:00
Kevin Darby 7fe20129d2 seems like we really can't count on the order of trade vs delete, so we have to use the price book to approximate crosses 2019-12-12 22:46:12 -05:00
Kevin Darby 4f59c1bb47 1 based arrays, so good 2019-12-12 18:26:10 -05:00
Kevin Darby 3ba17f1c87 allow disk in query 2019-12-12 18:19:08 -05:00
Kevin Darby ef874dda7e bigblue is slow, but I think we're on the right track now 2019-12-12 18:10:03 -05:00
Kevin Darby 6e966a6229 write the data with orders in same mongo document 2019-12-12 16:56:43 -05:00
Kevin Darby ed3b890c67 might have to store the data a little differently 2019-12-12 15:16:02 -05:00
Kevin Darby 2a46fd6227 push for pete 2019-12-12 14:27:28 -05:00
Kevin Darby 23e13846c6 botched order priority when only reducing size 2019-12-12 10:13:40 -05:00
Kevin Darby 39e4549972 only got one fill 2019-12-11 22:55:12 -05:00
Kevin Darby d924962546 kinda starting to work 2019-12-11 18:11:13 -05:00
Kevin Darby 0f1c81eb48 roundtrip 2019-12-11 18:07:33 -05:00
Kevin Darby 5e6fe7282d moved code around verified roll_snipe still works 2019-12-11 17:02:47 -05:00
Kevin Darby a47dad1ce4 changes in algo backtester to facilitate jupyter, will probably remove backwards compatibility in a bit 2019-12-11 13:28:48 -05:00
Kevin Darby 1809c778e2 ialgo 2019-12-10 23:40:55 -05:00
Kevin Darby 2178e16d3c restructure for jupyter 2019-12-10 22:50:49 -05:00
Kevin Darby deadbc6cf2 we actually do want one row per order for better query perf 2019-12-10 21:14:57 -05:00
Kevin Darby 445d77223f move some stuff around 2019-12-10 18:18:05 -05:00
Kevin Darby 09b24e33fc Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-12-09 14:05:56 -06:00
Kevin Darby 84553d11c4 new_externals 2019-12-09 14:05:04 -06:00
Kevin Darby eb1bab5878 linux build 2019-12-09 14:18:05 -05:00
Kevin Darby a7a58038b3 release build 2019-12-09 14:15:04 -05:00
Kevin Darby 1b9af8f010 new externals 2019-12-09 11:45:10 -05:00
Kevin Darby 113c4fe539 changes in notebooks, I am matching Taha 2019-12-09 11:12:06 -05:00
Kevin Darby c33662dc2c py 2019-12-09 00:30:36 -05:00
Kevin Darby 8817b22376 write book with trades 2019-12-08 14:37:48 -05:00
Kevin Darby b48da514e5 I think we got it 2019-12-08 00:18:25 -05:00
Kevin Darby 59cba2456b honing in on some timing issue 2019-12-07 22:02:45 -05:00
Kevin Darby 839c3d430b fixed build 2019-12-07 16:27:30 -05:00
Kevin Darby 5b1f2522e1 found bug in synth processor, won't build until I fix 2019-12-06 18:29:58 -05:00
Kevin Darby 6c3ccdf856 that's 400 micros, not nanos 2019-12-06 14:30:15 -05:00
Kevin Darby 157d834cbb getting somewhere with the roll 2019-12-06 13:42:40 -05:00
Kevin Darby 4f2e9b19cc trying to merge some collections 2019-12-05 23:49:11 -05:00
Kevin Darby fed7ca1ff3 honing in 2019-12-05 18:20:28 -05:00
Kevin Darby 73cfa17791 notes 2019-12-05 10:50:54 -05:00
Kevin Darby 474a53ba92 change notebook destination to big blue 2019-12-05 10:50:10 -05:00
Kevin Darby 69612eaee7 thought better of two tables, this makes simulation way easier 2019-12-04 23:33:48 -05:00
Kevin Darby b7ad777de4 have to go back to orders stream 2019-12-04 22:44:22 -05:00
Kevin Darby d6a70d852e added orders to the mongo 2019-12-04 18:10:47 -05:00
Kevin Darby 33f12edce5 finally figured out timestamps 2019-12-03 23:47:48 -05:00
Kevin Darby ab2e9fe6bd I might have fixed the dates 2019-12-03 22:34:04 -05:00
Kevin Darby 8bcffea686 jupyter 2019-12-03 18:17:31 -05:00
Kevin Darby 941472c5c0 bug in spread processing, small things in simulation 2019-12-02 23:35:42 -05:00
Kevin Darby d2a5e03976 notes 2019-12-02 18:14:54 -05:00
Kevin Darby 97e3bce327 moving historical analysis stuff to spark_historical 2019-12-02 16:06:05 -05:00
Kevin Darby e766667584 <= vs < 2019-12-02 14:48:54 -05:00
Kevin Darby a93c110c29 better serialization for pbuf Timestamps 2019-12-02 14:43:29 -05:00
Kevin Darby e1c7b89408 mongo 2019-12-01 21:38:55 -05:00
Kevin Darby ad3ce72a39 added a mongo generator, much nicer 2019-11-29 16:35:46 -05:00
Kevin Darby 57f48472d3 trying out timescaledb, 2019-11-29 15:47:20 -05:00
Kevin Darby 45ae6ac585 parsing this data needs to get faster 2019-11-29 13:33:06 -05:00
Kevin Darby 5a90676a25 need a finally 2019-11-27 21:57:03 -05:00
Kevin Darby 8536f782ce Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 21:47:03 -05:00
Kevin Darby 5f45ddf210 wack vs backwack 2019-11-28 02:46:27 +00:00
Kevin Darby 67c83c7d68 remove postgres tag from spark_vm_shell 2019-11-27 21:10:22 -05:00
Kevin Darby 6f6e1a9e55 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 21:01:43 -05:00
Kevin Darby c80451bc5e added async 2019-11-27 18:20:14 -05:00
Kevin Darby d6a533ca5c PR feedback 2019-11-27 17:21:24 -05:00
Kevin Darby e0112eff89 stuff 2019-11-27 17:20:55 -05:00
Kevin Darby 38b29f0afb more pg 2019-11-27 17:14:29 -05:00
Kevin Darby 8b9d689a5f able to preprocess trades and books into a postgres 2019-11-27 16:18:56 -05:00
Kevin Darby 097edb2726 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 15:09:58 -05:00
Kevin Darby 12e3e9a222 gave up and decided to run postgres in docker on my host 2019-11-27 15:08:51 -05:00
Taha Afzal ce19ba1419 added MarketQueriesHelper 2019-11-27 14:04:32 -05:00
Taha Afzal c81a22e55d Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 11:50:45 -05:00
Taha Afzal bd04f49ff5 Reorg 2019-11-27 11:50:38 -05:00
Eric Harding 3a9e8970d6 merge temp_synthetic_compute 2019-11-27 10:28:22 -05:00
Taha Afzal 9837645d16 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 09:34:43 -05:00
Taha Afzal 1789c02295 skeleton in place 2019-11-27 09:34:28 -05:00
Kevin Darby 3280376df3 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-26 21:31:42 -05:00
Taha Afzal d251593f04 parsing sniper args 2019-11-26 19:49:51 -05:00
Kevin Darby 9afeb29c63 pg 2019-11-26 18:21:06 -05:00
Kevin Darby e4dc07ce3f Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-26 17:40:01 -05:00
Kevin Darby 2bbf22a153 new docker for postges some db code 2019-11-26 16:03:43 -05:00
Taha Afzal a598ae8bd2 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-26 15:40:19 -05:00
Taha Afzal 4d72b9d2e7 filling in 2019-11-26 15:40:10 -05:00
Kevin Darby e000b1d0c0 hmm 2019-11-26 00:07:36 -05:00
Kevin Darby ef4261fe7f small parser tweaks for taha / me 2019-11-25 23:17:30 -05:00
Kevin Darby c3b75aa742 rename packet variable 2019-11-25 15:13:30 -05:00
Taha Afzal 2518be52fb book post processing path formalized 2019-11-25 15:12:29 -05:00
Kevin Darby f3ee6c0e30 TWAP completes 2019-11-25 14:54:15 -05:00
Kevin Darby 94363767ab small bug in slicer 2019-11-25 14:10:54 -05:00
Taha Afzal 9dd6e39665 post processing of books + helper functions to allign boks 2019-11-25 09:34:38 -05:00
Kevin Darby 722ae68cf1 more 2019-11-18 15:08:09 -05:00
Eric Harding e39313ecf6 Add merge_synthetic_book sample test code 2019-11-15 17:42:49 -05:00
Eric Harding 8095100bee p/invoke for synthetic book computation 2019-11-15 16:11:02 -05:00
Kevin Darby a76415a52f native algo state 2019-11-14 17:04:42 -05:00
Kevin Darby 06305ee4ac having trouble routing fills down to c++ 2019-11-14 16:21:51 -05:00
Kevin Darby 70e87151bc upstream changes for PR 2019-11-13 16:49:17 -05:00
Kevin Darby d1299f8a78 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-13 16:47:18 -05:00
Kevin Darby 20e58dc80b address PR concerns 2019-11-13 16:46:36 -05:00
Kevin Darby 9f7b0e5898 running twap from c#, need some stats 2019-11-12 18:12:06 -05:00
Kevin Darby 5e073c2330 interop with c++ algos kinda works 2019-11-12 17:56:25 -05:00
Kevin Darby 1b425d9a7c Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-12 11:31:31 -05:00
Kevin Darby 3546b65c91 don't finalize 2019-11-12 11:18:24 -05:00
Kevin Darby 758da29e25 added assert I think works 2019-11-11 23:19:03 -05:00
Kevin Darby 60e246036b back to state where I can repl dan 2019-11-11 23:14:49 -05:00
Kevin Darby 4b34f959e6 skip default 2019-11-11 18:17:29 -05:00
Kevin Darby 8abbd24cbe bug in orderbook refactor 2019-11-11 17:57:19 -05:00
Kevin Darby 932f852d6a Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-11 17:14:16 -05:00
Kevin Darby 5773945791 add data dir for .net in linux
make sparkcmd build in the right place on windows again
2019-11-11 17:12:55 -05:00
Kevin Darby bda1bd8f96 round trip algo runs on linux 2019-11-11 17:11:27 -05:00
Kevin Darby c3632e02d3 Added NativeOrderWrapper for algo backtesting 2019-11-08 18:04:34 -05:00
Kevin Darby 1b959b09c8 hooking c++ algos to algo backtest framework 2019-11-07 18:01:21 -05:00
Kevin Darby f55637ea08 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-06 21:36:57 -05:00
Kevin Darby 0272df9974 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-06 21:34:45 -05:00
Kevin Darby 0e02428b79 change algoanalyzer startup for taha 2019-11-06 21:34:35 -05:00
Kevin Darby 721c889235 release mode uses published sparkcmd 2019-11-06 18:22:06 -05:00
Kevin Darby 9957af655f hooked sparkcmd linux build to the main server build stuff 2019-11-06 17:47:21 -05:00
Kevin Darby eab92fb486 damn windows casing 2019-11-06 13:55:29 -05:00
Kevin Darby 9f1cd47cfa hooking build to cmake 2019-11-06 13:55:07 -05:00
Kevin Darby 90c3edbbe7 adding core 2 back to docker per eric 2019-11-06 13:24:57 -05:00
Kevin Darby adee8b584c capitalized first letter of proto to work around a protoc bug 2019-11-06 12:43:27 -05:00
Kevin Darby 329ad323e0 delete protos to change case on windows 2019-11-06 12:41:37 -05:00
Kevin Darby a8473f9340 Capitalizing first letter of some protos because of a protoc bug 2019-11-06 12:31:58 -05:00
Kevin Darby 1c0115f8cf had to delete these protos to change case 2019-11-06 12:31:12 -05:00
Kevin Darby a6ef802d3f fix proj files 2019-11-05 18:38:17 -05:00
Kevin Darby 37b5c37015 merging dotnet branch 2019-11-05 18:24:24 -05:00
Kevin Darby 1e28e214a4 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-05 18:23:47 -05:00
Kevin Darby c713b97441 cleanup csproj 2019-11-05 18:23:09 -05:00
Kevin Darby 4e8525f015 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-05 18:21:40 -05:00
Kevin Darby eb79264b9b netcore builds on linux through dotnet build 2019-11-05 18:19:17 -05:00
Kevin Darby 9c359ddb33 for_taha 2019-11-05 16:38:01 -05:00
Kevin Darby 67a16d5085 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-11-03 23:20:44 -05:00
Kevin Darby 6049cc4b43 oil spreads seem to work 2019-10-26 22:38:13 -04:00
Kevin Darby d916180132 back to oil 2019-10-25 23:50:20 -04:00
Kevin Darby ad9f28f782 round trip treasuries...not great 2019-10-25 23:32:02 -04:00
Kevin Darby 2890e4ebcc rt spreader 2019-10-25 18:56:52 -04:00
Kevin Darby 6638e4c765 fixed spreads / outright arbitration 2019-10-25 18:50:04 -04:00
Kevin Darby a251a8f411 clone 2019-10-25 00:50:02 -04:00
Kevin Darby e7790679ca this isn't as fast as I thought it would be 2019-10-25 00:15:34 -04:00
Kevin Darby 2a5e777c15 this is better 2019-10-24 18:30:24 -04:00
Kevin Darby fdbbce9ff3 remove concept of pre_book, instead separate the book arrangement and the trade / cross processing and layer in our orders by timestamp 2019-10-24 18:25:06 -04:00
Kevin Darby d47ac768c4 trying something 2019-10-24 16:30:21 -04:00
Kevin Darby 4867f4fa53 match event 2019-10-24 15:53:16 -04:00
Kevin Darby 902a99e7d0 new files 2019-10-24 14:30:13 -04:00
Kevin Darby faf5f664dc share one order book 2019-10-24 14:29:35 -04:00
Kevin Darby 4af94aaaa8 made some trades 2019-10-23 23:52:23 -04:00
Kevin Darby 2bea0a0a11 replicated some CL spread arbs 2019-10-23 18:34:40 -04:00
Kevin Darby b55067be91 trying 10/21 oil spreads 2019-10-23 00:24:21 -04:00
Kevin Darby 7ea20dbbff had to introduce concept of can_aggress-- if we're first in the queue we can't tell the difference between that and a cross 2019-10-22 23:50:07 -04:00
Kevin Darby 2dcf1424ea . 2019-10-22 18:21:30 -04:00
Kevin Darby caedb1ed64 round trip spreader taking shape 2019-10-22 18:15:13 -04:00
Kevin Darby d39856a93a refactor for multiple securities, implemented round tripper 2019-10-22 16:27:01 -04:00
Kevin Darby f8c7cc87b9 complicated hierarchies 2019-10-22 00:07:35 -04:00
Kevin Darby ee9a9bd721 moving stuff around for spreader case 2019-10-21 18:21:21 -04:00
Kevin Darby 7520d3516a move pnl computation to orderbook 2019-10-21 12:37:12 -04:00
Kevin Darby a2a9df86fc now we need prorata 2019-10-20 22:18:36 -04:00
Kevin Darby aa31aa7a3e testing 2019-10-20 21:27:19 -04:00
Kevin Darby 950b8686f2 small bug here in change / cancel 2019-10-20 21:22:50 -04:00
Kevin Darby b6ab217a11 assert that we are moving forward in time 2019-10-20 21:21:59 -04:00
Kevin Darby 3b8176d326 status event 2019-10-20 21:19:49 -04:00
Kevin Darby e9ec07cb2b have to queue the hedge with latency 2019-10-20 01:13:12 -04:00
Kevin Darby 78659b5d20 if these treasury results are real we are getting into a new business 2019-10-20 00:25:07 -04:00
Kevin Darby 9e26a2c651 adding top 2019-10-19 21:42:08 -04:00
Kevin Darby 3243ce1a45 treasury roll 2019-10-18 23:58:11 -04:00
Kevin Darby 475b6ae25b peg, cover, lean 2019-10-18 22:48:17 -04:00
Kevin Darby b853af59d6 usings 2019-10-18 22:24:10 -04:00
Kevin Darby ded1ad42e7 arbitraged spread and futures streams, separated replay from sending 2019-10-18 22:13:30 -04:00
Kevin Darby f3541b6c7f added latency to the catalyst loop; my clock isn't great though because it relies on the tape 2019-10-18 17:21:55 -04:00
Kevin Darby 3c40c84c02 got the mbo fixed 2019-10-18 16:47:02 -04:00
Kevin Darby d50420edb9 cme can send order updates in MDEntry or OrderIdEntry.... 2019-10-18 15:06:37 -04:00
Kevin Darby e0794f0725 added some assertions, cleaning up, now have to look at q 2019-10-18 13:23:40 -04:00
Kevin Darby fb6282ae9e optimizations 2019-10-18 11:15:08 -04:00
Kevin Darby 23995e5cc6 shaping up 2019-10-17 23:52:20 -04:00
Kevin Darby 161545b864 jcl is small loss always joining at end of q 2019-10-17 22:10:30 -04:00
Kevin Darby 285250dded bugs 2019-10-17 18:22:23 -04:00
Kevin Darby bbc3e03e48 now lets look at this queue 2019-10-17 18:04:03 -04:00
Kevin Darby 6b8d0d203c partials work, the queue seems to have broken 2019-10-17 17:42:03 -04:00
Kevin Darby c0bec8cfa5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-10-17 12:18:01 -04:00
Kevin Darby 19b0d8bf56 typo that I need to fix in master 2019-10-17 11:47:46 -04:00
Kevin Darby ede14ea7b3 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-10-17 11:40:24 -04:00
Kevin Darby 48ae7c4c33 this looks a lot like our c++ interface; I do not know if that's good 2019-10-17 11:36:35 -04:00
Kevin Darby fc217fd904 sweet replication 2019-10-16 23:23:16 -04:00
Kevin Darby b692a3e9d2 partial fills kinda kill everything 2019-10-16 18:37:13 -04:00
Kevin Darby 48f5e47335 now partial fills 2019-10-16 15:38:43 -04:00
Kevin Darby a080a0ccaa using sendingTime instead of transactTime 2019-10-16 14:45:13 -04:00
Kevin Darby 90321045f6 need to filter orders separately 2019-10-15 23:55:16 -04:00
Kevin Darby 8ad9e24415 interesting results 2019-10-15 23:26:10 -04:00
Kevin Darby 2008786c4e some semblance of a simulation going 2019-10-15 18:28:49 -04:00
Kevin Darby 57f8036796 jcl 2019-10-14 18:10:21 -04:00
Kevin Darby 21a304261e merge 2019-10-14 00:14:29 -04:00
Kevin Darby cefb2b2524 Merge branch 'fix' of bitbucket.org:bluetradesys/spark into vwap 2019-10-13 14:12:50 -04:00
Kevin Darby e26bdb228b Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-10-13 14:10:12 -04:00
Kevin Darby 1621ad03bf Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-10-13 14:09:45 -04:00
Kevin Darby 174acf1597 getting join fills 2019-10-11 18:22:53 -04:00
Kevin Darby bf32c6133a mbo parsing in c# 2019-10-11 18:18:22 -04:00
Kevin Darby fe73874b19 more analysis 2019-10-11 17:28:12 -04:00
Kevin Darby 0eb82a0d5c inching towards a simulated pnl of an arbitrary strategy 2019-10-10 18:05:14 -04:00
Kevin Darby fc2165d85d CL, ZN, ZC exhibit spookily similar histograms, ES is a little different 2019-10-10 11:04:34 -04:00
Kevin Darby a368b2ea16 factored out analyzers so we can test Join/Cover/Lean and Spreader 2019-10-09 17:46:10 -04:00
Kevin Darby 6294ef6db2 made some headway in analysis 2019-10-09 16:25:44 -04:00
Kevin Darby a9cca80c6d some corn stats 2019-10-08 17:36:16 -04:00
Kevin Darby aee9066ccf cmake 2019-10-08 15:14:44 -04:00
Kevin Darby ea3c130738 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-10-08 13:13:56 -04:00
Kevin Darby 193f248f06 merge deleted some of my files 2019-10-07 17:40:01 -04:00
Kevin Darby 5212fb272e Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-10-07 17:27:12 -04:00
Kevin Darby 6772c77e01 rudimentary fix orders seem to work 2019-10-07 17:18:20 -04:00
Kevin Darby 95c589f594 hooking up to run this fix server 2019-09-30 15:00:30 -04:00
Kevin Darby 699c7c5bfe Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-09-30 12:02:13 -04:00
Kevin Darby 7de82b5685 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-09-30 11:45:44 -04:00
Kevin Darby a8e9d0d7d8 ordersession and dropcopy session code complete for native orders, time to debug 2019-09-27 18:21:35 -04:00
Kevin Darby d078e73179 order session 2019-09-27 17:58:36 -04:00
Kevin Darby 4351d6e66a drop copy session code complete 2019-09-27 17:04:42 -04:00
Kevin Darby 9850d6bde0 rx-cpp requires concurrent use with prescription medicine 2019-09-27 14:29:22 -04:00
Kevin Darby d23a072ad6 rx 2019-09-26 18:27:41 -04:00
Kevin Darby 6ef0808f49 never thought I'd use rxcpp...it's truly terrible; I feel physical pain 2019-09-26 18:16:48 -04:00
Kevin Darby e1c2d8aaaf it builds, ship it 2019-09-26 12:33:42 -04:00
Kevin Darby 719fd03063 nothing builds 2019-09-26 11:45:08 -04:00
Kevin Darby d436dae7cd more fix 2019-09-24 18:22:09 -04:00
Kevin Darby f66004b465 fix port 2019-09-24 14:37:30 -04:00
Kevin Darby aa8a56787b fix fix fix 2019-09-23 18:16:05 -04:00
Kevin Darby 35a69396f1 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-09-23 17:08:56 -04:00
Kevin Darby ddfb6d1105 update externals 2019-09-23 17:03:49 -04:00
Kevin Darby 005c0f9a5d c++ fix 2019-09-23 16:59:39 -04:00
Kevin Darby cf62eae51b replay book building 2019-09-06 18:13:16 -04:00
Kevin Darby c7e49e8d21 data replay 2019-09-05 18:47:04 -04:00
Kevin Darby 8bc420190b Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-05 18:46:53 -04:00
Kevin Darby 62711a144b got some replay 2019-09-05 14:14:41 -04:00
Kevin Darby 333273b48a testing 2019-09-04 17:41:48 -04:00
Kevin Darby dd194f8ac0 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-04 17:11:44 -04:00
Kevin Darby 5992f4814d testing replay 2019-09-04 17:02:06 -04:00
Kevin Darby d67f15b3fb Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-04 15:12:02 -04:00
Kevin Darby be8635788a Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-04 14:03:41 -04:00
Kevin Darby d96355e6c4 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-03 14:36:29 -04:00
Kevin Darby 4ef2c8a2da conf changes 2019-09-03 14:35:23 -04:00
Kevin Darby 058fbe1aee utils is not in mbts any longer 2019-09-02 17:14:41 -04:00
Kevin Darby bc0f9f574a Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-02 17:05:21 -04:00
Kevin Darby f575b82ce0 almost got the replay going 2019-08-30 17:54:58 -04:00
Kevin Darby 050ea18a02 generation 2019-08-30 00:17:00 -04:00
Kevin Darby 18829279d2 working on replay from parser 2019-08-29 17:44:55 -04:00
Kevin Darby 75703d54ea getting mbo from s3 2019-08-29 12:21:01 -04:00
Kevin Darby 0a3fc1bca0 s3 2019-08-28 18:52:02 -04:00
Kevin Darby bc688ef44d parsing mbo files 2019-08-28 17:47:29 -04:00
Kevin Darby 57af89b628 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-08-28 16:47:20 -04:00
Kevin Darby 4fcc70bcf0 mbo fix parser 2019-08-27 18:06:03 -04:00
Kevin Darby cad6f2c1eb Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-26 17:57:16 -04:00
Kevin Darby c16b898d86 scaffolding for historical data 2019-08-26 17:55:26 -04:00
Kevin Darby 4f98244c3a arrival 2019-08-23 22:23:59 -04:00
Kevin Darby 8770f98fb1 more algo 2019-08-19 11:54:23 -04:00
Kevin Darby bbcf78517f self adjust 2019-08-16 11:30:36 -04:00
Kevin Darby b9a940b4e3 adjust for my size 2019-08-15 18:00:55 -04:00
Kevin Darby d8a4c1aaf7 added VolumeSlicer hierarchy, VolumeSliceAlgo and POVAlgo 2019-08-15 17:41:23 -04:00
Kevin Darby 9803cf31c8 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-15 09:36:00 -04:00
Kevin Darby 4a36177992 externals with master 2019-08-14 16:17:19 -04:00
Kevin Darby 421614198a fixed external 2019-08-14 15:45:31 -04:00
Kevin Darby 51e227e921 timeslicer 2019-08-12 22:33:26 -04:00
Kevin Darby 74948d11c5 build 2019-08-12 18:33:00 -04:00
Kevin Darby f88e1b5903 build 2019-08-12 18:30:27 -04:00
Kevin Darby 59115ce886 volume estimator 2019-08-12 18:24:29 -04:00
Kevin Darby 4b18d15421 refactor into TimeSlicer and VolumeSlicer 2019-08-12 18:07:07 -04:00
Kevin Darby 9be591c634 need a slightly different hierarchy for pov 2019-08-12 16:08:46 -04:00
Kevin Darby 462af2f1f3 make offset algo support a negative offset which means cross 2019-08-12 14:20:45 -04:00
Kevin Darby ab35c943fd working on pov 2019-08-12 13:52:57 -04:00
Kevin Darby aa36b24165 slicer params 2019-08-12 11:11:27 -04:00
Kevin Darby 547cdda3db adding aggression params to base class 2019-08-11 16:06:01 -04:00
Kevin Darby 3b77fd1a6c peg price updates 2019-08-09 18:09:31 -04:00
Kevin Darby a8110c9a8d added peg algo to support vwap/twap 2019-08-09 17:58:09 -04:00
Kevin Darby b6f80d6a4a twap working ok 2019-08-09 14:52:25 -04:00
Kevin Darby 18489e64a2 twapping 2019-08-08 18:16:00 -04:00
Kevin Darby a3d2df9ba2 . 2019-08-08 16:44:35 -04:00
Kevin Darby 314700e2e7 residual quantity 2019-08-08 16:09:06 -04:00
Kevin Darby 461b6c15d6 twap bugs 2019-08-08 15:25:00 -04:00
Kevin Darby d23ac7e8ef allow generic order vm to reflect one level deep into protos to generate a gui; will need john's help on a datetimepicker 2019-08-08 14:54:34 -04:00
Kevin Darby 68c0179465 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-08 12:20:15 -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
Kevin Darby feab3cbb89 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-01-22 16:10:16 -05:00
Kevin Darby 1f7c1be194 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2018-12-28 13:51:59 -05:00
Kevin Darby f78f18b3b4 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2018-12-20 16:20:19 -05:00
Kevin Darby 6e7fa91718 ready for testing 2018-12-14 10:39:34 -05:00
Kevin Darby 8cac406547 fix error handling 2018-12-12 17:33:25 -05:00
Kevin Darby 0d83f28cbc fix lives 2018-12-12 14:26:03 -05:00
Kevin Darby 941f4bf837 Merge branch 'cleanup_mdclient' of bitbucket.org:bluetradesys/spark into fix 2018-12-11 23:05:35 -05:00
Kevin Darby 8bbe9cfd98 Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-06 12:56:02 -05:00
Kevin Darby ba633532eb hit a wall here with MDClient, going to refactor 2018-12-06 12:55:21 -05:00
Kevin Darby 7bf50415ca nearly ready for testing 2018-12-05 17:55:06 -05:00
Kevin Darby edbaa9c435 looks like we have dropcopy 2018-12-05 15:35:49 -05:00
Kevin Darby 7789f39c9d handle some messages 2018-12-04 18:20:23 -05:00
Kevin Darby 6e188b4f73 shaping up 2018-12-04 16:37:41 -05:00
Kevin Darby 33e8ff58c5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-04 09:42:30 -05:00
Kevin Darby 24ae03a9f8 access permissions api so fix session can report fills and orders 2018-12-03 18:19:20 -05:00
Kevin Darby 5fe0335e1a Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 17:02:34 -05:00
Kevin Darby 6cbf707094 Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 16:06:58 -05:00
Kevin Darby 7e4ad55d9d established a session 2018-12-03 15:32:45 -05:00
Kevin Darby 35a0074014 bts fix44 adapter 2018-12-03 14:49:35 -05:00
Kevin Darby b8dd5c119d Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 10:41:34 -05:00
Kevin Darby 8ada73aa64 Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-11-30 18:37:39 -05:00
Kevin Darby a16ff30873 lets see how hard this is 2018-11-30 18:32:51 -05:00
338 changed files with 118111 additions and 435 deletions

No files matched your search

+1
View File
@@ -34,3 +34,4 @@ trace_data/
/doc/user/public/*
.ionide/*
/.ignore
CMakeDeps.cmake
+145 -12
View File
@@ -44,9 +44,10 @@
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
"/home/bts/spark/": "${workspaceRoot}",
"/home/bts/spark_host/": "${workspaceRoot}",
}
},
},
{
"name": "(gdb) Launch",
"type": "cppdbg",
@@ -98,11 +99,52 @@
"externalConsole": true,
"requireExactSource": false
},
{
"name": "sparkcmd Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": [
"historical",
"--server",
//"--rstart", "11/22/2019",
//"--rend", "11/25/2019",
//"--rsymbol", "ES",
//"--rsecuritytype", "Synthetic",
//"--post_process_file_type", "Trade",
//"--upload_to_aws",
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "sparkcmd simulation",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": [
"historical",
"--rstart", "11/26/2019",
"--rend", "11/26/2019",
"--rsymbol", "ZN",
"--rsecuritytype", "Synthetic",
"--algo_security", "ZNH0;ZNZ9-ZNH0;ZNZ9",
"--algo_type", "RoundTrip",
"--algo_params", "size=10;tick_size=0.015625,0.0078125,0.015625;hedge_factors=1,1,-1",
"--skip_preanalysis", "true",
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Excel Launch",
"type": "clr",
"request": "launch",
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
"program": "C:/Program Files/Microsoft Office/Office16/EXCEL.EXE",
"args": [
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"
],
@@ -111,6 +153,20 @@
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Fix Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": [
"fix",
"-order_session",
],
"cwd": "${workspaceRoot}/build/Debug/bin/net471/",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Tests",
"type": "clr",
@@ -126,11 +182,18 @@
"requireExactSource": false
},
{
"name": "Client Attach",
"name": ".NET Framework Attach",
"type": "clr",
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}",
"requireExactSource": false
},
{
"name": "Spreader Tests",
"type": "cppdbg",
@@ -337,20 +400,24 @@
"quoteArgs": true
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
"/home/bts/spark_host/": "${workspaceRoot}"
},
"justMyCode": true
},
{
"name": ".NET Core Remote Launch",
"name": "sparkcmd vsdbg (c#)",
"type": "coreclr",
"request": "launch",
"program": "/usr/bin/dotnet",
"program": "sparkcmd",
"args": [
"sparkcmd.dll",
"test",
"test_join_create_and_takeout_market"
"historical",
"--rstart", "11/20/2019",
"--rend", "11/20/2019",
"--rsymbol", "CL",
"--rsecuritytype", "Spread",
"--post_process_db", "mbo_history",
],
"env": {"LD_LIBRARY_PATH" : "."},
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/server",
"pipeTransport": {
@@ -361,11 +428,77 @@
"debuggerPath": "/home/bts/vsdbg/vsdbg",
"pipeCwd": "${workspaceRoot}",
"quoteArgs": true
},
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
},
"justMyCode": false
},
},
{
"name": "sparkcmd gdb (c++)",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/server/sparkcmd",
"args": [
"historical",
"--rstart", "10/21/2019",
"--rend", "10/21/2019",
"--rsymbol", "CL",
"--rsecuritytype", "Future",
"--algo_security", "CLX9",
"--algo_type", "TWAP",
"--algo_params", "'size=100;profit_ticks=1;tick_size=1,1,1;;hedge_factors=1,1,-1'",
"--skip_preanalysis", "true",
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/server/",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"preLaunchTask": "",
"logging": {
"engineLogging": false
},
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "enable pretty-printer",
"ignoreFailures": true
},
{
"description": "Skip STL",
"text": "skip -gfi /usr/include/c++/7/bits/*.h",
"ignoreFailures": true
},
{
"description": "LD LIBRARY",
"text": "set env LD_LIBRARY_PATH=.",
"ignoreFailures": false
}
],
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
}
},
{
"name": "Jupyter notebook",
"type": "coreclr",
"request": "launch",
"program": "jupyter",
"args": ["notebook", "--notebook-dir=c:\\code\\bts\\spark\\src\\client\\spark_historical\\Notebooks"],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "internalConsole",
},
]
}
+11
View File
@@ -48,6 +48,7 @@ else ()
add_subdirectory(doc/user)
add_subdirectory(src/sdk)
add_subdirectory(src/data_files)
add_subdirectory(src/client)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/server
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/server
@@ -59,7 +60,17 @@ else ()
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libACE-5.7.5.so ${CMAKE_BINARY_DIR}/server
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
COMMENT "Creating server runtime layout"
DEPENDS sparkd data_files_dir spark_client_dir
DEPENDS sparkd data_files_dir)
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
add_custom_command(TARGET server_layout POST_BUILD
COMMAND ln -fs ${CMAKE_BINARY_DIR}/../${CMAKE_BUILD_TYPE}/bin/netcoreapp3.1/linux-x64/sparkcmd ${CMAKE_BINARY_DIR}/server/ )
else()
add_custom_command(TARGET server_layout POST_BUILD
COMMAND ln -fs ${CMAKE_BINARY_DIR}/../${CMAKE_BUILD_TYPE}/bin/netcoreapp3.1/linux-x64/publish/sparkcmd ${CMAKE_BINARY_DIR}/server/ )
endif()
endif()
+1
View File
@@ -0,0 +1 @@
[0108/231015.447:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
+1 -1
View File
@@ -1 +1 @@
set(sources /spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/_index.md;content/bts_spark/Help/_index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Launcher/_index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Logging-In-to-the-System/_index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Market/_index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/_index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/Settings/_index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Spreader/_index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Trading/OrderTypes/index.md;content/bts_spark/Trading/_index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
set(sources /home/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/_index.md;content/bts_spark/Help/_index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Launcher/_index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Logging-In-to-the-System/_index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Market/_index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/_index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/Settings/_index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Spreader/_index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Trading/OrderTypes/index.md;content/bts_spark/Trading/_index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
+51
View File
@@ -23,7 +23,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\sp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_excel", "src\client\spark_excel\spark_excel.csproj", "{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_fix", "src\client\spark_fix\spark_fix.csproj", "{D8031C13-7BEA-430A-BB60-40927D15CFC3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_historical", "src\client\spark_historical\spark_historical.csproj", "{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}"
EndProject
Global
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
@@ -219,6 +226,50 @@ Global
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x64.Build.0 = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x86.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|Any CPU.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x64.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x64.Build.0 = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x86.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|Any CPU.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x86.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x86.Build.0 = Release|x64
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|x64.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|x64.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|x86.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|x86.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|x86.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|Any CPU.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|x64.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|x64.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|x86.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|x86.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+19
View File
@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.5)
project(spark_client)
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
file(GLOB_RECURSE spark_client_sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cs)
file(GLOB_RECURSE spark_client_csproj RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.csproj)
file(GLOB_RECURSE spark_ui_sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} spark/*.cs)
file(GLOB_RECURSE spark_ui_csproj RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} spark/*.csproj)
list(REMOVE_ITEM spark_ui_sources ${spark_client_sources} )
list(REMOVE_ITEM spark_ui_csproj ${spark_client_csproj} )
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/../
COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/build_spark_dotnetcore.sh
ARGS ${CMAKE_BUILD_TYPE} ${CMAKE_BINARY_DIR}
COMMENT "Building sparkcmd in netcore3"
DEPENDS libspark ${spark_client_sources} ${spark_client_csproj} ${CMAKE_CURRENT_SOURCE_DIR}/build_spark_dotnetcore.sh)
add_custom_target(spark_client_dir ALL DEPENDS ${CMAKE_BINARY_DIR}/../)
@@ -5,6 +5,7 @@
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
<Externals>$(SparkRoot)\externals\windows</Externals>
<Externals Condition=" '$(OS)' != 'Windows_NT' ">$(SparkRoot)/externals/linux</Externals>
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
<ErrorReport>None</ErrorReport>
<Prefer32Bit>False</Prefer32Bit>
@@ -19,20 +20,25 @@
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
<BaseIntermediateOutputPath Condition=" '$(OS)' != 'Windows_NT' ">$(BaseOutputPath)\linuxobj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<IntermediateOutputPath Condition=" '$(OS)' != 'Windows_NT' ">$(BaseOutputPath)\linuxobj\$(MSBuildProjectName)</IntermediateOutputPath>
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
</PropertyGroup>
<Import Project="$(ComputerSpecificConfig)" Condition="Exists($(ComputerSpecificConfig))" />
<ItemGroup>
<AvailableItemName Include="Proto2">
<Targets>CompileProto3</Targets>
<Targets Condition=" '$(OS)' == 'Windows_NT' " >CompileProto2</Targets>
<Targets Condition=" '$(OS)' != 'Windows_NT' " >CompileProto2Linux</Targets>
</AvailableItemName>
<AvailableItemName Include="Proto3">
<Targets>CompileProto3</Targets>
<Targets Condition=" '$(OS)' == 'Windows_NT' " >CompileProto3</Targets>
<Targets Condition=" '$(OS)' != 'Windows_NT' " >CompileProto3Linux</Targets>
</AvailableItemName>
<AvailableItemName Include="Proto3Excel">
<Targets>CompileProto3Excel</Targets>
<Targets Condition=" '$(OS)' == 'Windows_NT' ">CompileProto3Excel</Targets>
</AvailableItemName>
</ItemGroup>
@@ -43,6 +49,14 @@
<protogen>$(Externals)\bin\protogen.exe</protogen>
</PropertyGroup>
<PropertyGroup Label="ProtoBuf" Condition=" '$(OS)' != 'Windows_NT' ">
<ProtocPluginBase>$(SparkRoot)/src/lib/api/service_compiler</ProtocPluginBase>
<ProtoInclude>$(Externals)/include</ProtoInclude>
<protoc>$(Externals)/bin/protoc</protoc>
<protoc_rename>$(Externals)/bin/lowercase_files.sh</protoc_rename>
<protogen>$(Externals)/bin/protogen</protogen>
</PropertyGroup>
<ItemDefinitionGroup>
<Proto2>
</Proto2>
@@ -3,7 +3,7 @@
<Target Name="CompileProto2"
Condition="'@(Proto2)'!=''"
Condition="'@(Proto2)'!='' AND '$(OS)' == 'Windows_NT' "
Inputs="$(MSBuildProjectFile);$(protogen);%(Proto2.FullPath)"
Outputs="$(GenDir)\proto2\%(Proto2.filename).cs"
AfterTargets="PreBuildEvent">
@@ -28,7 +28,7 @@
passes wipes out the additional compile includes that we add.
-->
<Target Name="CompileProto3" AfterTargets="GetAssemblyVersion"
Condition="'@(Proto3)'!=''"
Condition="'@(Proto3)'!='' AND '$(OS)' == 'Windows_NT' "
Inputs="$(protoc);
%(Proto3.Identity);$(ProtocPluginBase)\plugin.py;
$(ProtocPluginBase)\templates\c#\client.pb.cs;
@@ -58,7 +58,7 @@
</Target>
<Target Name="CompileProto3Excel" AfterTargets="GetAssemblyVersion"
Condition="'@(Proto3Excel)'!=''"
Condition="'@(Proto3Excel)'!='' AND '$(OS)' == 'Windows_NT' "
Inputs="$(protoc);
%(Proto3Excel.Identity);$(ProtocPluginBase)\plugin.py;
$(ProtocPluginBase)\templates\excel\excel.pb.cs;"
@@ -82,12 +82,74 @@
</ItemGroup>
</Target>
<Target Name="CompileProto2Linux"
Condition="'@(Proto2)'!='' AND '$(OS)' != 'Windows_NT' "
Inputs="$(MSBuildProjectFile);$(protogen);%(Proto2.FullPath)"
Outputs="$(GenDir)/proto2/%(Proto2.filename).cs"
AfterTargets="PreBuildEvent">
<PropertyGroup>
<SourceRelativeFile>$([System.String]::Copy('%(proto2.fullpath)').Replace($(SourceRoot), ''))</SourceRelativeFile>
<SourceRelativeDir>$([System.IO.Path]::GetDirectoryName($(SourceRelativeFile)))</SourceRelativeDir>
<WorkingDirectory>$(GenDir)/proto2</WorkingDirectory>
</PropertyGroup>
<MakeDir Directories="$(WorkingDirectory)" />
<Copy SourceFiles="%(Proto2.FullPath)" DestinationFolder="$(WorkingDirectory)" SkipUnchangedFiles="True" UseHardLinksIfPossible="True" />
<Error Text="protogen.exe does not exist. If you forgot to clone with externals try: git submodule update --init --recursive" Condition="!Exists($(protogen))" />
<Message Importance="High" Text="Doing Proto2 %(proto2.filename)" />
<Exec Command="PYTHONPATH=$(Externals)/python $(Protogen) %(Proto2.filename)%(proto2.extension) --csharp_out=. -I. -I$(SourceRoot) -I$(Externals)/include"
WorkingDirectory="$(WorkingDirectory)" />
<ItemGroup>
<Compile Include="$(WorkingDirectory)/%(Proto2.filename).cs" />
</ItemGroup>
</Target>
<Target Name="CompileProto3Linux" AfterTargets="GetAssemblyVersion"
Condition="'@(Proto3)'!='' AND '$(OS)' != 'Windows_NT' "
Inputs="$(protoc);
%(Proto3.Identity);$(ProtocPluginBase)/plugin.py;
$(ProtocPluginBase)/templates/c#/client.pb.cs;
$(ProtocPluginBase)/templates/c#/server.pb.cs;
$(ProtocPluginBase)/templates/c#/immutable.pb.cs;"
Outputs="%(Proto3.OutCS);%(Proto3.OutCSServer);%(Proto3.OutCSClient);%(Proto3.OutCSImmutable)">
<MakeDir Directories="%(Proto3.OutBase)" />
<MakeDir Directories="$(IntermediateOutputPath)/gen" />
<Message Importance="High" Text="Generating service %(Proto3.FileName)" />
<Exec Command="
PYTHONPATH=$(Externals)/python $(protoc) \
%(Proto3.FullPath) \
--error_format=gcc \
--proto_path=$(protoinc):$(SourceRoot) \
--plugin=&apos;protoc-gen-service=$(ProtocPluginBase)/plugin.py&apos; \
--service_out=lang=c#,strip_out_path=1:%(Proto3.OutBase) \
--csharp_out=%(CompileOptions)%(Proto3.OutBase)"
WorkingDirectory="%(relativedir)" />
<!-- <Exec Command="$(protoc_rename) %(Proto3.OutBase)" /> -->
<ItemGroup>
<Compile Include="%(Proto3.OutCS)" />
<Compile Include="%(Proto3.OutCSClient)" />
<Compile Include="%(Proto3.OutCSServer)" />
<Compile Include="%(Proto3.OutCSImmutable)" />
</ItemGroup>
</Target>
<!-- <Target Name="RenameProto3Linux" AfterTargets="CompileProto3Linux"
Condition="'@(Proto3)'!='' AND '$(OS)' != 'Windows_NT' ">
<Move SourceFiles="$(OutBase)\Apicore.cs" DestinationFiles="$(OutBase)\apicore.cs" />
<Move SourceFiles="$(OutBase)\Apioptions.cs" DestinationFiles="$(OutBase)\apioptions.cs" />
<Move SourceFiles="$(OutBase)\Co2.cs" DestinationFiles="$(OutBase)\co2.cs" />
</Target> -->
<Target Name="AfterClean" AfterTargets="Clean">
<Delete Files="%(Proto3.OutCS)" />
<Delete Files="%(Proto3.OutCSClient)" />
<Delete Files="%(Proto3.OutCSServer)" />
<Delete Files="%(Proto3.OutCSImmutable)" />
<Delete Files="$(GenDir)\proto2\*" />
<Delete Files="$(GenDir)/proto2/*" />
<Delete Files="%(Proto3Excel.OutCSExcel)" />
</Target>
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
#$1 = Debug / Release (CMAKE_BUILD_TYPE), $2 = CMAKE_BINARY_DIR
cd "$(dirname "$0")"
echo $PWD dotnet build -c $1 ./sparkcmd/
dotnet build -c $1 -o $2/../$1/bin/netcoreapp3.1/linux-x64 ./sparkcmd/
if [ "$1" == "Release" ]
then
dotnet publish -c $1 -o $2/../$1/bin/netcoreapp3.1/linux-x64 ./sparkcmd/
ln -fs $2/src/lib/libspark.so $2/../$1/bin/netcoreapp3.1/linux-x64/publish
fi
ln -fs $2/src/lib/libspark.so $2/../$1/bin/netcoreapp3.1/linux-x64
@@ -1,6 +1,7 @@
using bts.api;
using bts.co2;
using bts.collections;
using bts.utils.csv;
using mbts.persist;
using mbts.spark.filters;
using mbts.ui;
@@ -52,7 +53,7 @@ namespace mbts.spark {
columns.Add(new RenderTextColumn<Row, TimeAndSalesView.Columns>(col_key, r => r[col_key], filter, filter_template, r => _get_aggressor_brush(r))
{ id = col_id(col_key), Header = header, Width = width, sort_member_path = sort_member_path, horizontal_alignment = h_alignment }
);
// be sure to load GridSettings AFTER the columns collection has been populated, the grid settings manager looks columns by id to apply the appropriate settings,
// if the collection is not populated, the column settings will not be applied
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, row_vms);
@@ -173,6 +174,19 @@ namespace mbts.spark {
}
}
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(
typeof(TimeAndSalesVM.Row),
new string[] {
nameof(security),
nameof(time),
nameof(price),
nameof(size),
nameof(aggressor_side),
nameof(security_key),
});
public void raise_invalidated() => invalidated?.Invoke();
}
}
@@ -3,6 +3,7 @@ using bts.api;
using bts.shell;
using bts.shell.fills;
using bts.spark;
using bts.utils.csv;
using mbts.co2;
using mbts.persist;
using mbts.ui;
@@ -63,6 +64,7 @@ namespace mbts.spark {
var current_smd_source = connectable_builder.create(vm => vm.current_security_md_observable(_bus)).OnDispatcher();
var selected_secs_source = connectable_builder.create(vm => vm.selected_securities_observable()).OnDispatcher();
_commands = new SparkCommand[] {
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.value.row_vms.ToCsv(TimeAndSalesVM.Row.csv_ordered_properties), "TaS")),
SparkCommands.ShowLadder(SparkCommands.KeyGestures.ShowLadder).complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity(SparkCommands.KeyGestures.ShowOrderTicket).complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills(SparkCommands.KeyGestures.ShowFills).complete(selected_secs_source, always_true, get_fills_startup_data),
@@ -5,6 +5,7 @@ using bts.collections;
using bts.shell.fills;
using bts.spark;
using bts.utils;
using bts.utils.csv;
using mbts.collections;
using mbts.persist;
using mbts.spark.filters;
@@ -23,6 +24,7 @@ using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using static bts.api.orderman.Fill.Types;
using static bts.shell.FillsWindowFactory;
@@ -310,6 +312,7 @@ namespace mbts.spark {
public FillStatus fill_status => _fill.FillStatus;
public string fill_key => _fill.FillKey;
public string order_key => _order.Key.ToString();
public string symbol => fill_tuple.security?.Symbol;
public bool is_selected {
get => _is_selected;
set => Set(ref _is_selected, value);
@@ -330,6 +333,28 @@ namespace mbts.spark {
public override bool Equals(object obj) => Equals(obj as Row);
public override int GetHashCode() => _fill.FillKey.GetHashCode() ^ _order.Key.GetHashCode();
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(typeof(Row),
new string[] {
nameof(security),
nameof(symbol),
nameof(order_type),
nameof(root_algo_display_name),
nameof(side),
nameof(fill_time),
nameof(user),
nameof(account),
nameof(exchange),
nameof(time_in_force),
nameof(size),
nameof(price),
nameof(fill_kind),
nameof(fill_status),
nameof(fill_key),
nameof(order_key),
nameof(security_key)
});
public event Action invalidated;
void IRowVM.raise_invalidated() => invalidated?.Invoke();
}
@@ -3,6 +3,7 @@ using bts.api;
using bts.shell;
using bts.shell.fills;
using bts.spark;
using bts.utils.csv;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
@@ -54,6 +55,7 @@ namespace mbts.spark {
_view = new FillsView { DataContext = _vm };
_commands = new List<SparkCommand> {
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.rows.ToCsv(FillsVM.Row.csv_ordered_properties), "Fills")),
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
SparkCommands.window_settings_command(new DialogCommand(() => {
var vm = new ShellSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
@@ -4,6 +4,7 @@ using bts.api.orderman;
using bts.collections;
using bts.spark;
using bts.utils;
using bts.utils.csv;
using mbts.ui;
using mbts.ui.controls;
using System;
@@ -11,6 +12,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Reactive.Linq;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Windows.Input;
using System.Windows.Media;
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
@@ -60,6 +62,35 @@ namespace mbts.spark {
public event Action<(OrderSecurityRootPair oldval, OrderSecurityRootPair newval)> updated;
public event PropertyChangedEventHandler PropertyChanged;
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(
typeof(OrderBookRowVM),
new string[] {
nameof(security_text),
nameof(symbol),
nameof(time_created),
nameof(last_change),
nameof(user),
nameof(account),
nameof(exchange),
nameof(time_in_force),
nameof(order_type),
nameof(root_algo_display_name),
nameof(order_sub_type),
nameof(side),
nameof(quantity),
nameof(filled_quantity),
nameof(price),
nameof(avg_price),
nameof(last_price),
nameof(piq),
nameof(status),
// last message (could contains commas, so skipping)
nameof(security_key),
nameof(order_key),
nameof(parent_order_key),
});
// Core
public OrderSecurityRootPair order_tuple { get; private set; }
public OrderRecordImmutable order => order_tuple.order.order;
@@ -4,6 +4,7 @@ using bts.shell;
using bts.shell.fills;
using bts.shell.orderbook;
using bts.spark;
using bts.utils.csv;
using bts.view;
using mbts.persist;
using mbts.ui;
@@ -184,6 +185,7 @@ namespace mbts.spark {
show_grid_settings_command.group = CommandGroups.Auxiliary;
_commands = new SparkCommand[] {
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.rows.ToCsv(OrderBookRowVM.csv_ordered_properties), "OrderBook")),
toggle_show_right_panel_command,
attach_selected_orders_command,
detach_selected_orders_command,
+15 -44
View File
@@ -653,54 +653,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) =>
@@ -28,15 +28,17 @@ namespace mbts.spark
where TMutable : class, IMessage, new()
{
TMutable _message;
readonly protected Func<IMessage, IMessage> _message_override;
protected GenericOrderParamVM(FieldDescriptor descriptor)
protected GenericOrderParamVM(FieldDescriptor descriptor, Func<IMessage, IMessage> message_override = null)
{
_message = new TMutable();
_message_override = message_override;
this.descriptor = descriptor;
}
public FieldDescriptor descriptor { get; set; }
public Google.Protobuf.IMessage message
public virtual Google.Protobuf.IMessage message
{
get => _message;
set
@@ -53,11 +55,12 @@ namespace mbts.spark
where TMutable : class, IMessage, new()
{
readonly Func<TModel, TView> _convert;
readonly Func<TView, TModel> _convert_back;
readonly Func<TView, TModel> _convert_back;
TView _value;
public GenericOrderParamVM(FieldDescriptor field_descriptor, Func<TModel, TView> convert, Func<TView, TModel> convert_back) :
base(field_descriptor)
public GenericOrderParamVM(FieldDescriptor field_descriptor, Func<TModel, TView> convert,
Func<TView, TModel> convert_back, Func<IMessage, IMessage> message_override = null) :
base(field_descriptor, message_override)
{
_convert = convert;
_convert_back = convert_back;
@@ -68,7 +71,8 @@ namespace mbts.spark
get => _value;
set
{
this.descriptor.Accessor.SetValue(this.message, _convert_back(_value = value));
this.descriptor.Accessor.SetValue( _message_override == null ? this.message : _message_override(this.message),
_convert_back(_value = value));
RaisePropertyChanged();
}
}
@@ -84,7 +88,7 @@ namespace mbts.spark
protected override void on_param_changed()
{
_value = _convert(ConvertUtils.try_convert<TModel>(this.descriptor.Accessor.GetValue(this.message)));
_value = _convert(ConvertUtils.try_convert<TModel>(this.descriptor.Accessor.GetValue(_message_override == null ? this.message : _message_override(this.message))));
RaisePropertyChanged(nameof(value));
}
}
@@ -92,8 +96,8 @@ namespace mbts.spark
class GenericOrderEnumeratedVM<TMutable> : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<string>
where TMutable : class, IMessage, new()
{
public GenericOrderEnumeratedVM(FieldDescriptor descriptor)
: base(descriptor)
public GenericOrderEnumeratedVM(FieldDescriptor descriptor, Func<IMessage, IMessage> message_override = null)
: base(descriptor, message_override)
{
elements = new ObservableCollection<string>(_get_elements(descriptor));
}
@@ -102,11 +106,13 @@ namespace mbts.spark
public string selected_element
{
get => GenericOrderHelper.get_enumerated_value(message, descriptor);
get => GenericOrderHelper.get_enumerated_value(_message_override == null ? this.message : _message_override(this.message),
descriptor);
set
{
var orig = selected_element;
GenericOrderHelper.set_enumerated_value(message, descriptor, value);
GenericOrderHelper.set_enumerated_value(_message_override == null ? this.message : _message_override(this.message),
descriptor, value);
if (selected_element != orig)
RaisePropertyChanged();
}
@@ -148,22 +154,23 @@ namespace mbts.spark
class GenericOrderQuantityVM<TMutable> : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<QuantityMeasureImmutable?>
where TMutable : class, IMessage, new()
{
public GenericOrderQuantityVM(FieldDescriptor descriptor)
: base(descriptor) { }
public GenericOrderQuantityVM(FieldDescriptor descriptor, Func<IMessage, IMessage> message_override = null)
: base(descriptor, message_override) { }
QuantityMeasureImmutable? IGenericOrderParamVM<QuantityMeasureImmutable?>.value { get => quantity; set => quantity= value; }
public QuantityMeasureImmutable? quantity
{
get => GenericOrderHelper.get_quantity_value(message, descriptor);
get => GenericOrderHelper.get_quantity_value(_message_override == null ? this.message : _message_override(this.message),
descriptor);
set
{
var orig = quantity;
GenericOrderHelper.set_quantity_value(message, descriptor, value);
GenericOrderHelper.set_quantity_value(_message_override == null ? this.message : _message_override(this.message),
descriptor, value);
if (quantity != orig)
RaisePropertyChanged();
}
}
public IObservable<QuantityMeasureImmutable?> subscribe_value()
{
return Observable.Create<QuantityMeasureImmutable?>(o =>
@@ -174,10 +181,9 @@ namespace mbts.spark
o.OnNext(quantity);
});
}
protected override void on_param_changed()
{
RaisePropertyChanged(nameof(quantity));
}
}
}
}
@@ -96,9 +96,41 @@ namespace mbts.spark {
{
Google.Protobuf.Reflection.MessageDescriptor desc = new TMutable().Descriptor;
_parameters.SyncWithDictionary(
desc.Fields.InDeclarationOrder().Where(_is_value_param_desc).ToDictionary(p => p.Name), _mk_param_vm);
desc.Fields.InDeclarationOrder().Where(_is_value_param_desc).ToDictionary(p => p.Name), x => _mk_param_vm(x));
add_message_params(desc);
}
}
void add_message_params(Google.Protobuf.Reflection.MessageDescriptor desc)
{
var msgs = desc.Fields.InDeclarationOrder().Where(x => !_is_value_param_desc(x) &&
x.MessageType.ClrType != typeof(CustomOrderDefinition) );
foreach(var message_field in msgs)
{
foreach (var fd in message_field.MessageType.Fields.InDeclarationOrder())
{
if(_is_value_param_desc(fd)) //one level deep
{
var mk = _mk_param_vm(fd,
outside_object =>
{
var msgfield = message_field.Accessor.GetValue(outside_object);
if(msgfield == null)
{
message_field.Accessor.SetValue(outside_object, Activator.CreateInstance(message_field.MessageType.ClrType));
msgfield = message_field.Accessor.GetValue(outside_object);
}
return (IMessage)msgfield;
});
if(mk == null)
continue;
if(!_parameters.Add(mk))
_parameters.Add(message_field.Name + "_" + mk.descriptor.Name, mk);
}
}
}
}
static bool _is_value_param_desc(FieldDescriptor desc)
{
return (desc.FieldType != Google.Protobuf.Reflection.FieldType.Message ||
@@ -117,50 +149,53 @@ namespace mbts.spark {
return v;
}
GenericOrderParamVM<TMutable> _mk_param_vm(FieldDescriptor descriptor)
GenericOrderParamVM<TMutable> _mk_param_vm(FieldDescriptor descriptor, Func<IMessage, IMessage> message_override = null)
{
ExceptionAssert.Argument.NotNull(descriptor, nameof(descriptor));
if (descriptor.FieldType == FieldType.Message &&
descriptor.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName)
{
var qm = new GenericOrderQuantityVM<TMutable>(descriptor);
return attached(qm, GenericOrderHelper.get_quantity_value, GenericOrderHelper.set_quantity_value);
var qm = new GenericOrderQuantityVM<TMutable>(descriptor, message_override);
return attached(qm, GenericOrderHelper.get_quantity_value, GenericOrderHelper.set_quantity_value, message_override);
}
else if(descriptor.FieldType == FieldType.Enum)
{
var pvm = new GenericOrderEnumeratedVM<TMutable>(descriptor);
attached(pvm, GenericOrderHelper.get_enumerated_value, GenericOrderHelper.set_enumerated_value);
var pvm = new GenericOrderEnumeratedVM<TMutable>(descriptor, message_override);
attached(pvm, GenericOrderHelper.get_enumerated_value, GenericOrderHelper.set_enumerated_value, message_override);
return pvm;
}
else if (descriptor.FieldType == FieldType.Bool)
return mk<bool?, bool>(descriptor, v => v, v => v.GetValueOrDefault());
return mk<bool?, bool>(descriptor, v => v, v => v.GetValueOrDefault(), message_override);
else if (descriptor.FieldType == FieldType.Double || descriptor.FieldType == FieldType.Float)
return mk<double, double>(descriptor, v => v, v => v, double.NaN);
return mk<double, double>(descriptor, v => v, v => v, message_override, double.NaN);
else if (descriptor.FieldType == FieldType.Int32)
return mk<int?, int>(descriptor, v => v, v => v.GetValueOrDefault());
return mk<int?, int>(descriptor, v => v, v => v.GetValueOrDefault(), message_override);
else if (descriptor.FieldType == FieldType.UInt32)
return mk<uint?, uint>(descriptor, v => v, v => v.GetValueOrDefault());
return mk<uint?, uint>(descriptor, v => v, v => v.GetValueOrDefault(), message_override);
else if (descriptor.FieldType == FieldType.String)
return mk<string, string>(descriptor, v => v, v => v);
return mk<string, string>(descriptor, v => v, v => v, message_override);
else
return null;
}
GenericOrderParamVM<TMutable, TView, TModel> mk<TView, TModel>(FieldDescriptor fdesc, Func<TModel, TView> convert, Func<TView, TModel> convert_back, TView default_value = default)
GenericOrderParamVM<TMutable, TView, TModel> mk<TView, TModel>(FieldDescriptor fdesc, Func<TModel, TView> convert, Func<TView, TModel> convert_back,
Func<IMessage, IMessage> message_override = null, TView default_value = default)
{
var pvm = new GenericOrderParamVM<TMutable, TView, TModel>(fdesc, convert, convert_back);
return attached<TView, GenericOrderParamVM<TMutable, TView, TModel>>(pvm, get_from_param, assign_to_param, default_value);
var pvm = new GenericOrderParamVM<TMutable, TView, TModel>(fdesc, convert, convert_back, message_override);
return attached<TView, GenericOrderParamVM<TMutable, TView, TModel>>(pvm, get_from_param, assign_to_param,
message_override, default_value);
TView get_from_param(IMessage param, FieldDescriptor fd) => convert(ConvertUtils.try_convert<TModel>(fdesc.Accessor.GetValue(param)));
void assign_to_param(IMessage param, FieldDescriptor fd, TView value) => fdesc.Accessor.SetValue(param, convert_back(value));
}
TParam attached<T, TParam>(TParam pvm, Func<IMessage, FieldDescriptor, T> selector, Action<IMessage, FieldDescriptor, T> assign_to_param, T default_value = default) where TParam : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<T>
TParam attached<T, TParam>(TParam pvm, Func<IMessage, FieldDescriptor, T> selector, Action<IMessage, FieldDescriptor, T> assign_to_param,
Func<IMessage, IMessage> message_override = null, T default_value = default) where TParam : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<T>
{
add_property<T>(
pvm.descriptor.Name,
mdef => mdef != null ? selector(mdef, pvm.descriptor) : default_value,
(mdef, v) => assign_to_param(mdef, pvm.descriptor, v),
mdef => mdef != null ? selector( message_override == null ? mdef : message_override(mdef), pvm.descriptor) : default_value,
(mdef, v) => assign_to_param(message_override == null ? mdef : message_override(mdef), pvm.descriptor, v),
() => pvm.value,
v => pvm.value = v,
pvm.subscribe_value(),
@@ -4,14 +4,14 @@ using OffsetDefi = bts.api.orderman.OffsetOrderDefinitionImmutable;
namespace mbts.spark {
static partial class OrderTypeVMFactory {
class OffsetOrderVM : OrderTypeVMBase<OffsetDef, OffsetDefi> {
readonly Property<uint?> _tick_offset_on_deploy;
readonly Property<int?> _tick_offset_on_deploy;
public OffsetOrderVM(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions)
: base(key, display_name, editing_permissions) {
_tick_offset_on_deploy = add_nullable_property(nameof(tick_offset_on_deploy), OffsetDef.TickOffsetOnDeployFieldNumber, 0u);
_tick_offset_on_deploy = add_nullable_property(nameof(tick_offset_on_deploy), OffsetDef.TickOffsetOnDeployFieldNumber, 0);
}
public uint? tick_offset_on_deploy { get => _tick_offset_on_deploy.Value; set => _tick_offset_on_deploy.set(value); }
public int? tick_offset_on_deploy { get => _tick_offset_on_deploy.Value; set => _tick_offset_on_deploy.set(value); }
}
}
}
+1 -2
View File
@@ -9,7 +9,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>Debug;Release</Configurations>
<Platforms>x64</Platforms>
<RuntimeIdentifiers>win7-x64</RuntimeIdentifiers>
<PlatformTarget>AnyCPU</PlatformTarget>
<LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets>
<ApplicationIcon>logo.ico</ApplicationIcon>
@@ -36,7 +35,7 @@
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="IronPython" Version="2.7.7" />
<PackageReference Include="NAudio" Version="1.8.4" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
@@ -691,7 +691,7 @@
<TextBlock VerticalAlignment="Center" >Tick Offset</TextBlock>
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="How many ticks off the inside market to deploy the order, this guards against crossing the market inadventently" />
</StackPanel>
<c:TickUpDownUInt x:Name="PART_TickOffsetOnDeploy_TickUpDownUInt" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
<c:TickUpDownInt x:Name="PART_TickOffsetOnDeploy_TickUpDownUInt" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
<uag:AlgoPickerNodeView
x:Name="PART_Child_AlgoPickerNodeView"
Grid.Row="2"
@@ -64,6 +64,25 @@ namespace mbts.ui {
}
}, can_execute == null ? null : new Func<object, bool>(_ => can_execute()));
public static IDialogCommand export_csv(
Func<string> get_file_content,
string source_window_name) =>
new AnonymousFileDialogCommand<object>(_ =>
_create_save_file_dialog(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
$"BTS_Spark_{source_window_name}_{DateTime.Now.ToString("yyyyMMdd_HHmm")}.csv",
new[] { ("CSV (comma delimited)", new string[] { "csv" }) }
),
(_, file) => {
try {
File.WriteAllText(file, get_file_content.Invoke());
} catch (Exception ex) {
BTSMessageBox.Show($"Error exporting data to csv: {ex}.", "CSV Export Error", BTSMessageBoxButtons.OK, System.Windows.MessageBoxImage.Error, true, false, true);
}
},
// we are always able to perform an export, even when no rows (it will just be a header)
(_) => true);
static TDialog _create_file_dialog<TDialog>(string initial_dir = null, IReadOnlyList<(string name, string[] extensions)> filter_items = null) where TDialog : FileDialog, new() {
var rv = new TDialog();
if (!string.IsNullOrEmpty(initial_dir))
@@ -4,6 +4,8 @@ using System.Runtime.InteropServices;
using System.Security;
using Google.Protobuf;
using security_key = System.UInt32;
using bts.co2;
using System.Linq;
namespace mbts.co2
{
@@ -85,6 +87,7 @@ namespace mbts.co2
[DllImport("libspark")]
internal unsafe static extern void post_on_scan_security_thread(Action callback);
}
internal static class Co2NativeTestMethods
@@ -133,5 +136,59 @@ namespace mbts.co2
[DllImport("libspark")]
internal static extern void co2_test_get_constants(out ulong max_depth, out ulong max_implied_depth, out sbyte nan_exponent);
internal unsafe delegate void raw_receive_pb_or_string(byte* buffer, int buffer_size);
[DllImport("libspark")]
private static unsafe extern void raw_co2_compute_synthetic_book(
byte[] security_buffer,
int security_length,
IntPtr[] leg_books,
int[] leg_lengths,
int leg_books_length,
raw_receive_pb_or_string callback);
internal static unsafe BookMessageImmutable co2_compute_synthetic_book(SecurityImmutable synthetic, params BookMessageImmutable[] books) {
BookMessage result = null;
string error = null;
var security_buffer = synthetic.ToByteArray();
// var h = GCHandle.Alloc(null, GCHandleType.Pinned).
var book_buffers =
books
.Select(b => b.ToByteArray())
.ToArray();
GCHandle[] book_buffer_handles =
book_buffers
.Select(b => GCHandle.Alloc(b,GCHandleType.Pinned))
.ToArray();
IntPtr[] book_buffer_pointers =
book_buffer_handles
.Select(h => h.AddrOfPinnedObject())
.ToArray();
var lengths =
book_buffers
.Select(b => b.Length)
.ToArray();
raw_co2_compute_synthetic_book(
security_buffer,
security_buffer.Length,
book_buffer_pointers,
lengths,
book_buffers.Length,
(byte* buffer, int length) => {
if (length < 0) {
error = new string((char*)buffer,0,-1 * length);
}
else {
result = Co2NativeMethods.deserialize<BookMessage>(buffer, (ulong)length);
}
});
if(error != null) {
// C# style multiple return values
throw new Exception(error);
}
return new BookMessageImmutable(result);
}
}
}
+42
View File
@@ -52,7 +52,49 @@ namespace mbts.co2 {
return _mantissa.GetHashCode();
}
public static IntPrice from_double(double price, sbyte precision) {
if (double.IsNaN(price)) return NaN;
if (Math.Abs(price%1) != 0) {
double rounding = 0.5 * Math.Pow(10,-precision);
price += (price > 0) ? rounding : -rounding;
price *= Math.Pow(10,precision);
return new IntPrice((long)price, (sbyte)-precision);
}
return new IntPrice((long)price, 0);
}
public static IntPrice NaN = new IntPrice(0,127);
static double[] powers_10 = {
1E-20, 1E-19, 1E-18, 1E-17, 1E-16, 1E-15, 1E-14, 1E-13, 1E-12, 1E-11, 1E-10, 1E-9, 1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1,
1E0,
1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15, 1E16, 1E17, 1E18, 1E19, 1E20};
static int max_abs_power_10 = powers_10.Length / 2;
static double pow10(int power)
{
int offset = power + max_abs_power_10;
int powers_10_sz = powers_10.Length;
bts.Log.assert(offset >= 0 && offset < powers_10_sz);
if (offset >= 0 && offset < powers_10_sz)
{
return powers_10[offset];
}
return double.NaN;
}
public static IntPrice from_double(double price, int precision = 9)
{
if (Double.IsNaN(price))
return NaN;
if (Math.Abs(price - Math.Truncate(price)) != 0.0)
{
double rounding = 0.5 * pow10(-precision);
price += (price > 0.0) ? rounding : -rounding;
price *= pow10(precision);
return new IntPrice((long)price, Convert.ToSByte(-precision));
}
return new IntPrice((long)price, 0);
}
}
}
@@ -1,4 +1,6 @@
using bts;
using System.Collections.Generic;
using System.Linq;
namespace mbts.co2 {
public static class NativePriceHelpers {
@@ -38,6 +40,60 @@ namespace mbts.co2 {
public const uint COCONUT_MAX_DEPTH = 10;
public const uint COCONUT_MAX_IMPLIED_DEPTH = 2;
internal const sbyte COCONUT_NAN_EXPONENT = 127;
public static double side_at(this bts.co2.BookMessage book, bts.api.Side side, int level)
{
if(side == bts.api.Side.Buy)
return bid_at(book, level);
else
return ask_at(book, level);
}
public static uint sidesize_at(this bts.co2.BookMessage book, bts.api.Side side, int level)
{
if (side == bts.api.Side.Buy)
return bidsize_at(book, level);
else
return asksize_at(book, level);
}
public static double bid_at(this bts.co2.BookMessage book, int level)
{
var lev = book.Bids[level];
return NativePriceHelpers.to_double(lev.Mantissa, (sbyte)lev.Exponent);
}
public static double ask_at(this bts.co2.BookMessage book, int level)
{
var lev = book.Asks[level];
return NativePriceHelpers.to_double(lev.Mantissa, (sbyte)lev.Exponent);
}
public static uint bidsize_at(this bts.co2.BookMessage book, int level)
{
var lev = book.Bids[level];
return lev.Size;
}
public static uint asksize_at(this bts.co2.BookMessage book, int level)
{
var lev = book.Asks[level];
return lev.Size;
}
public static double price(this bts.co2.PriceSizeMessage p)
{
return NativePriceHelpers.to_double(p.Mantissa, (sbyte)p.Exponent);
}
public static double price(this bts.co2.TradeMessage p)
{
return NativePriceHelpers.to_double(p.Mantissa, (sbyte)p.Exponent);
}
public static IList<double> all_bids(this bts.co2.BookMessage book)
{
var lev = book.Bids.Select(x => NativePriceHelpers.to_double(x.Mantissa, (sbyte)x.Exponent)).ToList();
return lev;
}
public static IList<double> all_asks(this bts.co2.BookMessage book)
{
var lev = book.Asks.Select(x => NativePriceHelpers.to_double(x.Mantissa, (sbyte)x.Exponent)).ToList();
return lev;
}
}
}
@@ -3,4 +3,6 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("spark_core")]
[assembly: InternalsVisibleTo("sparkcmd")]
[assembly: InternalsVisibleTo("spark_test")]
[assembly: InternalsVisibleTo("spark_fix")]
[assembly: InternalsVisibleTo("spark_historical")]
+20
View File
@@ -0,0 +1,20 @@
namespace bts.api
{
//keep in sync with apioptions.proto
public static partial class ApioptionsReflection
{
public enum MessageOptions
{
fix_algostrategy_tagbase = 8506,
}
public enum FieldOptions
{
fix_tag = 8507,
fix_tag_is_header = 8509,
}
public enum EnumValueOptions
{
fix_value = 8508,
}
}
}
@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using Google.Protobuf;
using Google.Protobuf.Collections;
using Google.Protobuf.WellKnownTypes;
using mbts.co2;
namespace bts.history
{
public interface IHasMsgSeqNum
{
uint MsgSeqNum { get; }
}
public interface IHasDateTimeStamp
{
DateTime trade_date { get; set;}
DateTime sending_time { get; set;}
DateTime transact_time { get; set; }
}
public interface IHasTimestamp
{
Timestamp SendingTime { get; set; }
Timestamp TransactTime { get; set; }
}
public interface IHasMdPacket
{
MarketDataIncrementalRefresh Md{ get; }
}
public interface IHasMdEntry
{
MDEntry MdEntry { get; }
int EntryIndex { get; }
}
public interface IHasTopBook : IHasMdEntry, IHasMdPacket
{
co2.TopBookMessage TopBook { get; set; }
}
public interface IHasBook : IHasMdEntry, IHasMdPacket
{
co2.BookMessage Book { get; set; }
}
public interface IHasTrade : IHasMdEntry, IHasMdPacket
{
co2.TradeMessage Trade { get; set; }
}
public interface IHasMBO : IHasMdPacket
{
RepeatedField<OrderIDEntry> Orders { get; }
}
public sealed partial class HistoryRow : IHasTopBook, IHasMBO, IHasTrade, IHasBook, IHasDateTimeStamp, IHasTimestamp, IHasMsgSeqNum
{
public DateTime trade_date { get { return DateTime.SpecifyKind(this.TradeDate.ToDateTime(), DateTimeKind.Utc); }
set { this.TradeDate = Timestamp.FromDateTime(value); } }
public DateTime sending_time { get { return DateTime.SpecifyKind(this.SendingTime.ToDateTime(), DateTimeKind.Utc); }
set { this.SendingTime = Timestamp.FromDateTime(value); } }
public DateTime transact_time { get { return DateTime.SpecifyKind(this.TransactTime.ToDateTime(), DateTimeKind.Utc); }
set { this.TransactTime = Timestamp.FromDateTime(value); } }
public HistoryRow add_orders(IEnumerable<OrderIDEntry> orderentries)
{
this.Orders.AddRange(orderentries);
return this;
}
}
public interface IMBOMessage
{
MDUpdateAction OrderUpdateAction {get;}
ulong OrderId {get;}
uint SecurityId {get;}
string SecurityDesc {get;}
ulong MdOrderPriority {get;}
double MdEntryPx {get;}
uint MdPriceLevel {get;}
ulong MdDisplayQty {get;}
uint LastQty {get;}
MDEntryType MdEntryType {get;}
uint ReferenceId {get;}
}
public sealed partial class MBOMessage : IMBOMessage
{
public MDUpdateAction OrderUpdateAction => OrderEntry != null ? OrderEntry.OrderUpdateAction : MdEntry.MdUpdateAction;
public ulong OrderId => OrderEntry != null ? OrderEntry.OrderId : MdEntry.OrderId;
public uint SecurityId => MdEntry.SecurityId;
public string SecurityDesc => MdEntry.Symbol;
public ulong MdOrderPriority => OrderEntry != null ? OrderEntry.MdOrderPriority : MdEntry.MdOrderPriority;
public double MdEntryPx => MdEntry.MdEntryPx;
public uint MdPriceLevel => MdEntry.MdPriceLevel;
public ulong MdDisplayQty => OrderEntry != null ? OrderEntry.MdDisplayQty : MdEntry.MdDisplayQty;
public uint LastQty => OrderEntry != null ? OrderEntry.LastQty : MdEntry.LastQty;
public MDEntryType MdEntryType => MdEntry.MdEntryType;
public uint ReferenceId => OrderEntry != null ? OrderEntry.ReferenceId : MdEntry.ReferenceId;
public uint NumberOfOrders => MdEntry.NumberOfOrders;
}
public sealed partial class MarketDataIncrementalRefresh
{
public MarketDataIncrementalRefresh add_entries(IEnumerable<MDEntry> mdentries, IEnumerable<OrderIDEntry> orderentries)
{
this.MdEntries.AddRange(mdentries);
this.OrderIdEntries.AddRange(orderentries);
return this;
}
}
public interface ITradeDetail
{
ulong OrderId { get; set; }
uint LastQty { get; set; }
ITradeDetail clone();
}
public sealed partial class OrderIDEntry : ITradeDetail
{
ITradeDetail ITradeDetail.clone()
{
return this.Clone();
}
}
}
namespace bts.co2
{
public sealed partial class BookMessage
{
public BookMessage add_bids_and_asks(IEnumerable<PriceSizeMessage> bids, IEnumerable<PriceSizeMessage> asks)
{
this.Bids.AddRange(bids);
this.Asks.AddRange(asks);
return this;
}
}
public sealed partial class TradeMessage
{
public double price => NativePriceHelpers.to_double(this.Mantissa, (sbyte)this.Exponent);
}
}
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using static bts.api.Security;
namespace bts.api {
struct FutureLeg {
public struct FutureLeg {
public readonly Venue exchange;
public readonly string symbol, exchange_desc;
public readonly FutureContractImmutable future;
@@ -4,7 +4,7 @@ using System.Linq;
using static bts.api.Security;
namespace bts.api {
internal static class SecurityHelper {
public static class SecurityHelper {
public static IEnumerable<FutureLeg> get_flat_future_legs(SecurityImmutable sec) {
switch (sec.ContractCase) {
case ContractOneofCase.Spread: return _get_flat_future_legs(sec.Spread.Legs, 1);
@@ -63,6 +63,8 @@ namespace bts.api {
return security.Glbx.SecurityDesc;
else if(security.HasIce)
return security.Ice.SecurityDesc;
else if(security.HasSynthetic)
return security.Synthetic.Name;
else
{
Log.assert(false, "security_desc not implemented for exchange");
+1 -1
View File
@@ -26,7 +26,7 @@
<Proto3 Include="**/*.proto" />
<Proto3 Include="../../lib/api/*.proto" />
<Proto3 Include="../../lib/api/services/*.proto" />
<Proto3 Include="../../lib/md/protos/co2.proto" />
<Proto3 Include="../../lib/md/protos/Co2.proto" />
<Proto3 Include="../../lib/domain_model/Security.proto" />
<Proto3 Include="../../lib/sparkproto/SparkProtos.proto" />
<Proto3 Update="../../lib/api/services/OutOfProcessURLListener.proto">
@@ -28,5 +28,8 @@ namespace bts.utils
[DllImport("libspark")]
public static extern bool initialize_nothrow(StringBuilder buffer, int length);
//public static string bts_data_path()
//use mbts.config.Configuration.get<bts.config.System>().data_path;
}
}
+3 -1
View File
@@ -36,6 +36,7 @@ namespace bts {
Plugin,
Sdk,
Http,
Fix
// Please keep this list in sync with logging.h/logging.cpp
}
@@ -102,9 +103,10 @@ namespace bts {
internal static Logger Assert => new Logger(Subsystem.Assert);
internal static Logger Ui => new Logger(Subsystem.Ui);
internal static Logger Testing => new Logger(Subsystem.Testing);
internal static Logger DataFlow => new Logger(Subsystem.DataFlow);
internal static Logger DataFlow => new Logger(Subsystem.DataFlow);
public static Logger Sdk => new Logger(Subsystem.Sdk);
public static Logger Http => new Logger(Subsystem.Http);
internal static Logger FIX => new Logger(Subsystem.Fix);
internal static void log_at(this Logger logger, Level level, string message) {
switch (level) {
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using Google.Protobuf.WellKnownTypes;
namespace bts.api
{
public static class TimestampExtensions
{
public static Timestamp add_nanos(this Timestamp lhs, int nanos)
{
var c = new Timestamp(lhs);
c.Nanos += nanos;
return c;
}
public static bool less(this Timestamp lhs, Timestamp rhs)
{
return lhs.compare(rhs) < 0;
}
public static bool less_or_equal(this Timestamp lhs, Timestamp rhs)
{
return lhs.compare(rhs) <= 0;
}
public static bool greater(this Timestamp lhs, Timestamp rhs)
{
return lhs.compare(rhs) > 0;
}
public static bool greater_or_equal(this Timestamp lhs, Timestamp rhs)
{
return lhs.compare(rhs) >= 0;
}
public static int compare(this Timestamp lhs, Timestamp rhs)
{
int secdiff = lhs.Seconds.CompareTo(rhs.Seconds);
return (secdiff != 0) ? secdiff : (lhs.Nanos.CompareTo(rhs.Nanos));
}
public static Comparer<Timestamp> timestamp_comparer => Comparer<Timestamp>.Create((x, y) => x.compare(y));
public static Timestamp parse_timestamp(string data)
{
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
try
{
if (data.Length <= 8)
{
dt = DateTime.ParseExact(data, "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture);
}
else
{
int sep = 14;
var yyMMddhhmmss = data.Substring(0, sep);
dt = DateTime.ParseExact(yyMMddhhmmss, "yyyyMMddHHmmss", System.Globalization.CultureInfo.InvariantCulture);
var nnanos = data.Substring(sep, data.Length - sep);
long nano = long.Parse(nnanos);
dt = dt.AddTicks(nano / 100);
}
}
catch (Exception _)
{
System.Diagnostics.Debug.Assert(_ != null); //UNUSED(_)
}
dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
var ts = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(dt);
return ts;
}
public static string to_fix_string(this Timestamp ts)
{
return ts.ToDateTime().ToString("yyyyMMdd-HH:mm:ss.fff");
}
}
}
+28
View File
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
namespace bts.api
{
public class TypeUtils
{
static public List<Type> find_all_types_in_all_assemblies(string type, string assembly_regex = null)
{
List<Type> res = new List<Type>();
var asses = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly a in asses)
{
if (!string.IsNullOrEmpty(assembly_regex))
{
if (!Regex.Match(a.GetName().Name, assembly_regex).Success)
continue;
}
Type t = a.GetType(type, false, true);
if (t != null)
res.Add(t);
}
return res;
}
}
}
@@ -2,4 +2,5 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("spark")]
[assembly: InternalsVisibleTo("sparkcmd")]
[assembly: InternalsVisibleTo("spark_test")]
[assembly: InternalsVisibleTo("spark_historical")]
@@ -70,10 +70,13 @@ namespace bts.test {
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
try{
var p = Process.Start(psi);
p.WaitForExit((int)time_out.TotalMilliseconds);
var errorlevel = p.ExitCode;
string stdout = p.StandardOutput.ReadToEnd();
string stderr = p.StandardError.ReadToEnd();
if (p.HasExited) {
return 0;
}
@@ -0,0 +1,64 @@
using bts;
using bts.api.admin;
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.api.core
{
public class AdminModelWrapper
{
readonly AdminDataImmutable? _admin_data;
public AdminDataImmutable? admin_data => _admin_data;
readonly string _user_name;
readonly List<BTSAccountImmutable> _allowed_accounts;
readonly Dictionary<string, string> _allowed_account_dict = new Dictionary<string, string>();
public AdminModelWrapper(AdminDataImmutable? admin_data = null, string user_name = null)
{
_admin_data = admin_data;
_user_name = user_name ?? string.Empty;
_allowed_accounts = search_admin(u => u.Name == _user_name).Where(x =>
{
return (x.Item2 != null && x.Item2.HasPermissions) || (x.Item3 != null && x.Item3.HasPermissions);
}).Select(xx => xx.Item1).ToList();
foreach (var val in _allowed_accounts)
_allowed_account_dict[val.Name] = val.Name;
}
public IEnumerable<BTSAccountImmutable> viewable_accounts()
{
return _allowed_accounts;
}
public bool can_view_account(string account)
{
return _allowed_account_dict.ContainsKey(account) || _user_name.ToLower().Equals("bts");
}
IEnumerable<Tuple<BTSAccountImmutable, TradingPermissionUnit, AdminPermissionUnit>>
search_admin(Func<BTSUserImmutable, bool> predicate)
{
var rv = new List<Tuple<BTSAccountImmutable, TradingPermissionUnit, AdminPermissionUnit>>();
if (_admin_data.HasValue)
{
var users = _admin_data.Value.Users.Where(predicate);
if (users != null && users.Count() == 1)
{
var user = users.ElementAt(0);
if (user.HasUserData && user.UserData.HasAccountPermissions)
{
foreach (var kv in user.UserData.AccountPermissions.AccountPermissions)
{
var account_id = kv.Key;
var accounts = _admin_data.Value.Accounts.Where(a => a.Id == account_id);
if (accounts != null && accounts.Count() == 1)
{
var account = accounts.ElementAt(0);
rv.Add(Tuple.Create(account, kv.Value.TradingPermission, kv.Value.AdminPermission));
}
}
}
}
}
return rv;
}
}
}
@@ -0,0 +1,64 @@
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.position;
using bts.spark;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
namespace bts.api.core
{
public static class PermissionFilters
{
public static IObservable<Update<PositionUnitImmutable, int>>
PermissionFilter(this IObservable<Update<PositionUnitImmutable, int>> source, IObservable<AdminModelWrapper> am)
{
var pred = Observable.Create<Func<PositionUnitImmutable, bool>>(observer =>
{
return am.Subscribe(x =>
{
Func<PositionUnitImmutable, bool> filter = (z => x.can_view_account(z.Account));
observer.OnNext(filter);
});
});
return source.Filter(pred);
}
public static IObservable<Update<OrderRecordImmutable, uint>>
PermissionFilter(this IObservable<Update<OrderRecordImmutable, uint>> source, IObservable<AdminModelWrapper> am)
{
var pred = Observable.Create<Func<OrderRecordImmutable, bool>>(observer =>
{
return am.Subscribe(x =>
{
Func<OrderRecordImmutable, bool> filter = (z => x.can_view_account(z.Def.Account ?? string.Empty));
observer.OnNext(filter);
});
});
return source.Filter(pred);
}
public static IObservable<Update<FillImmutable, string>>
PermissionFilter(this IObservable<Update<FillImmutable, string>> source, IObservable<AdminModelWrapper> am)
{
var pred = Observable.Create<Func<FillImmutable, bool>>(observer =>
{
return am.Subscribe(x =>
{
Func<FillImmutable, bool> filter = (z => x.can_view_account(z.Account));
observer.OnNext(filter);
});
});
return source.Filter(pred);
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>x64</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
+52
View File
@@ -0,0 +1,52 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
namespace bts.utils.csv {
public static class CsvHelper {
public static string ToCsv<T>(this IEnumerable<T> items, OrderedDictionary properties = null) where T : class {
var output = "";
var delimiter = ",";
if (properties == null) {
properties = new OrderedDictionary();
foreach (var pi in typeof(T).GetProperties()) {
properties.Add(pi.Name, pi);
}
}
using (var sw = new StringWriter()) {
var dict_values = properties.Values.OfType<PropertyInfo>();
var header = dict_values.Select(n => n.Name).Aggregate((first, second) => first + delimiter + second);
sw.WriteLine(header);
foreach (var i in items) {
var row = dict_values.Select(x => x.GetValue(i, null))
.Select(x => x == null ? "null" : x.ToString())
.Aggregate((first, second) => first + delimiter + second);
sw.WriteLine(row);
}
output = sw.ToString();
}
return output;
}
public static OrderedDictionary create_ordering_maintainer(Type t, string[] property_names_in_order) {
OrderedDictionary od = new OrderedDictionary();
var props = t.GetProperties();
foreach (var n in property_names_in_order) {
var query = props.Where(pi => pi.Name == n);
if (query.Any()) {
var property_info = query.First();
if (!od.Contains(property_info)) {
od.Add(n, property_info);
}
}
}
return od;
}
}
}
@@ -66,7 +66,6 @@ namespace mbts {
}
}
}
public static void sync_with_models<T, TModel>(this IList<T> target, IEnumerable<TModel> models, Func<TModel, T> add_factory, Func<T, TModel, T> update_factory) {
var model_list = models as IList<TModel> ?? models.ToList();
if (model_list.Count == 0) {
Binary file not shown.
+1 -1
View File
@@ -26,7 +26,7 @@
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Linq.Dynamic" Version="1.0.7" />
<PackageReference Include="ExcelDna.AddIn" Version="0.34.6" />
+8 -1
View File
@@ -90,7 +90,14 @@ namespace bts.excel
{
if(string.IsNullOrEmpty(json))
json = "{}";
request = JsonParser.Default.Parse(json, desc);
try
{
request = JsonParser.Default.Parse(json, desc);
}
catch(InvalidProtocolBufferException)
{
request = (IMessage) Newtonsoft.Json.JsonConvert.DeserializeObject(json, desc.ClrType);
}
}
}
return request;
+62 -1
View File
@@ -25,7 +25,68 @@ namespace bts.excel
else if (!string.IsNullOrEmpty(key1))
val = jo[key1];
return val.ToString();
}
}
static Google.Protobuf.JsonFormatter _formatter =
new Google.Protobuf.JsonFormatter(new Google.Protobuf.JsonFormatter.Settings(true));
[ExcelFunction]
public static object bts_json_create(string type)
{
System.Type t = find_full_type_in_all_assemblies(type);
object obj = Activator.CreateInstance(t);
foreach(var pr in t.GetProperties())
{
object val = null;
try
{
val = Activator.CreateInstance(pr.PropertyType);
}
catch {}
try
{
pr.SetValue(obj, val);
}
catch {}
}
string s = "{}";
if(obj is Google.Protobuf.IMessage imsg)
s = _formatter.Format(imsg);
else
s = JsonConvert.SerializeObject(obj, Formatting.Indented);
return s;
}
[ExcelFunction]
public static object bts_json_fill(string root, string key, string value)
{
var jo = JObject.Parse(root);
jo[key] = value;
var s = jo.ToString();
return s;
}
[ExcelFunction]
public static object bts_date_to_json(object dt, bool is_duration = false)
{
string s = "{}";
if(is_duration)
{
var span = TimeSpan.FromDays((double)dt);
var ts = Google.Protobuf.WellKnownTypes.Duration.FromTimeSpan(span);
s = JsonConvert.SerializeObject(ts, Formatting.None);
s = _formatter.Format(ts);
s = s.Replace(@"""", "");
}
else
{
var dd = DateTime.FromOADate((double)dt);
var utc = DateTime.SpecifyKind(dd, DateTimeKind.Utc);
var ts = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(utc);
s = JsonConvert.SerializeObject(ts, Formatting.None);
s = _formatter.Format(ts);
s = s.Replace(@"""", "");
}
return s;
}
public static bool parse_venue(string exch, out Venue ven)
{
+61
View File
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QuickFix;
using QuickFix.Fields;
namespace bts.fix
{
public class FixInit
{
static FixInit()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
Log.FIX.error($"Unhandled Exception: {e.ExceptionObject} {exception?.StackTrace}");
if (e.IsTerminating)
{
Log.system_is_crashing("Unhandled exception", e.ExceptionObject);
}
}
public static int start_acceptor(string settings_file, bool orders_allowed, string http_address = null)
{
SessionSettings settings = new SessionSettings(settings_file);
IApplication app = new FixRouter( (th, sess) =>
{
if(orders_allowed)
return new OrderSession(th, sess);
else
return new DropCopySession(th, sess);
});
IMessageStoreFactory storeFactory = new FileStoreFactory(settings);
ILogFactory logFactory = new FileLogFactory(settings);
IAcceptor acceptor = new ThreadedSocketAcceptor(app, storeFactory, settings, logFactory);
acceptor.Start();
HttpServer srv = null;
if (!string.IsNullOrEmpty(http_address))
{
if(!http_address.StartsWith("http"))
http_address = "http://127.0.0.1:5080/";
srv = new HttpServer(http_address, settings);
srv.Start();
}
Console.WriteLine("press <q> to quit");
while (Console.Read() != 'q') { }
acceptor.Stop();
if(srv != null)
srv.Stop();
return 0;
}
}
}
+252
View File
@@ -0,0 +1,252 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Text;
using QuickFix;
using QuickFix.Fields;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.user;
using bts.md;
using bts.api.admin;
using security_key = System.UInt32;
using QuickFix.FIX42;
using Message = QuickFix.Message;
namespace bts.fix
{
interface IFixRouter
{
IEnumerable<SecurityImmutable> get_securities();
SecurityImmutable? lookup(security_key key);
SecurityImmutable? lookup(string desc);
void send(Message mess, SessionID sess);
void send_session_reject(SessionID sess, string text, Message refmessage = null);
void send_biz_reject(SessionID sess, string text, Message refmessage, int reason_code = 0);
}
class FixRouter : QuickFix.MessageCracker, IFIXSession, IFixRouter
{
readonly ConcurrentDictionary<SessionID, QuickFix.IApplication> _sessions = new ConcurrentDictionary<SessionID, IApplication>();
readonly Func<IFixRouter, SessionID, IApplication> _factory;
readonly object _lock = new object();
readonly MarketDataService _md;
private readonly IDisposable _security_sub;
readonly ConcurrentDictionary<security_key, SecurityImmutable> _id_to_security = new ConcurrentDictionary<security_key, SecurityImmutable>();
readonly ConcurrentDictionary<string, SecurityImmutable> _desc_to_security = new ConcurrentDictionary<string, SecurityImmutable>();
public FixRouter(Func<IFixRouter, SessionID, IApplication> factory)
{
_factory = factory;
using(var service_context = new ServiceContext())
{
_md = service_context.locate<MarketDataService>();
_security_sub = _md.subscribe_securities().Subscribe( x =>
{
foreach( var sec in x.all)
{
var imm = sec.immutable();
_id_to_security[sec.Id] = imm;
_desc_to_security[imm.security_desc()] = imm;
}
});
}
}
IApplication session(SessionID sessionID)
{
IApplication app;
if (!_sessions.TryGetValue(sessionID, out app))
{
app = _factory(this, sessionID);
_sessions[sessionID] = app;
}
return app;
}
public void send(Message mess, SessionID sess)
{
lock (_lock)
{
try
{
Session.SendToTarget(mess, sess);
}
catch (SessionNotFound)
{
Log.assert(false, "session not found " + sess);
}
catch (Exception ex)
{
Log.FIX.error(sess.ToString() + " " + ex.Message + " " + sess);
}
}
}
public void send_session_reject(SessionID sess, string text, Message refmessage = null)
{
Reject rej = null;
if (refmessage != null && refmessage.Header.IsSetField(Tags.MsgSeqNum))
rej = new Reject(new QuickFix.Fields.RefSeqNum(refmessage.Header.GetInt(Tags.MsgSeqNum)));
else
rej = new Reject();
rej.Text = new Text(text);
send(rej, sess);
}
public void send_biz_reject(SessionID sess, string text, Message refmessage, int reason_code = 0)
{
BusinessMessageReject rej = new BusinessMessageReject();
rej.RefMsgType = new QuickFix.Fields.RefMsgType(refmessage.Header.GetString(Tags.MsgType));
rej.RefSeqNum = new QuickFix.Fields.RefSeqNum(refmessage.Header.GetInt(Tags.MsgSeqNum));
rej.BusinessRejectReason = new QuickFix.Fields.BusinessRejectReason(reason_code);
rej.Text = new Text(text);
send(rej, sess);
}
void handle_session_exception(Exception ex, SessionID sessionID, Message message = null)
{
string msg = "==ERROR in session " + sessionID.ToString() + "== ";
msg += message?.GetType().Name ?? string.Empty;
msg += ex?.ToString() ?? string.Empty;
if(!string.IsNullOrEmpty(ex.StackTrace))
msg += Environment.NewLine + ex.StackTrace;
bts.Log.FIX.error(msg);
Console.WriteLine(msg);
//now shut this fucker down
try
{
var sess = Session.LookupSession(sessionID);
if(sess != null)
sess.Disconnect(msg);
}
catch { }
}
void handle_field_not_found(FieldNotFoundException ex, SessionID sess, Message message)
{
if(ex.Field > 0)
send_biz_reject(sess, $"Field {ex.Field} was expected but not found", message, 5 );
//QF does not include the tag number....
//throw new FieldNotFoundException($"Field {ex.Field} was expected but not found", ex);
else
throw ex;
}
public void FromAdmin(Message message, SessionID sessionID)
{
try
{
session(sessionID).FromAdmin(message, sessionID);
}
catch (FieldNotFoundException ex) { handle_field_not_found(ex, sessionID, message); }
catch (UnsupportedMessageType) { throw; }
catch (IncorrectTagValue) { throw; }
catch (Exception ex) { handle_session_exception(ex, sessionID, message); }
}
public void FromApp(Message message, SessionID sessionID)
{
try
{
session(sessionID).FromApp(message, sessionID);
}
catch (FieldNotFoundException ex) { handle_field_not_found(ex, sessionID, message); }
catch (UnsupportedMessageType) { throw; }
catch (IncorrectTagValue) { throw; }
catch (Exception ex) { handle_session_exception(ex, sessionID, message); }
}
public void OnCreate(SessionID sessionID)
{
try
{
session(sessionID).OnCreate(sessionID);
}
catch (Exception ex)
{
handle_session_exception(ex, sessionID);
}
}
public void OnLogon(SessionID sessionID)
{
try
{
session(sessionID).OnLogon(sessionID);
}
catch (Exception ex)
{
handle_session_exception(ex, sessionID);
}
}
public void OnLogout(SessionID sessionID)
{
try
{
//TODO stackoverflow?
session(sessionID).OnLogout(sessionID);
}
catch (Exception ex)
{
handle_session_exception(ex, sessionID);
}
}
public void ToAdmin(Message message, SessionID sessionID)
{
try
{
session(sessionID).ToAdmin(message, sessionID);
}
catch (FieldNotFoundException ex) { handle_field_not_found(ex, sessionID, message); }
catch (UnsupportedMessageType) { throw; }
catch (IncorrectTagValue) { throw; }
catch (Exception ex) { handle_session_exception(ex, sessionID, message); }
}
public void ToApp(Message message, SessionID sessionID)
{
try
{
session(sessionID).ToApp(message, sessionID);
}
catch (FieldNotFoundException ex) { handle_field_not_found(ex, sessionID, message); }
catch (UnsupportedMessageType) { throw; }
catch (IncorrectTagValue) { throw; }
catch (Exception ex) { handle_session_exception(ex, sessionID, message); }
}
IEnumerable<SecurityImmutable> IFixRouter.get_securities()
{
return _id_to_security.Values;
}
SecurityImmutable? IFixRouter.lookup(uint key)
{
if(_id_to_security.TryGetValue(key, out SecurityImmutable sec))
return sec;
else
return null;
}
SecurityImmutable? IFixRouter.lookup(string desc)
{
if (_desc_to_security.TryGetValue(desc, out SecurityImmutable sec))
return sec;
else
return null;
}
}
}
+585
View File
@@ -0,0 +1,585 @@
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Web;
using QuickFix;
namespace bts.fix
{
public class HttpServer : IDisposable
{
private readonly HttpListener _httpListener;
private Thread _connectionThread;
private Boolean _running, _disposed;
private readonly SessionSettings _sessionSettings;
private StringBuilder _sbHtmlHeader;
public HttpServer(string prefix, SessionSettings settings)
{
if (!HttpListener.IsSupported)
{
// Requires at least a Windows XP with Service Pack 2
throw new NotSupportedException(
"The Http Server cannot run on this operating system.");
}
_httpListener = new HttpListener();
_httpListener.Prefixes.Add(prefix);
_sessionSettings = settings;
}
public void Start()
{
if (!_httpListener.IsListening)
{
_httpListener.Start();
_running = true;
// Use a thread to listen to the Http requests
_connectionThread = new Thread(ConnectionThreadStart);
_connectionThread.Start();
}
}
public void Stop()
{
if (_httpListener.IsListening)
{
_running = false;
_httpListener.Stop();
}
}
private void ConnectionThreadStart()
{
try
{
while (_running)
{
// Grab the context and pass it to the processor methods to handle it
HttpListenerContext context = _httpListener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
string responseString;
_sbHtmlHeader = new StringBuilder(@"<HEAD><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>");
_sbHtmlHeader.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>]&nbsp;[<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEADER><BODY>", request.Url.OriginalString);
switch (request.Url.AbsolutePath)
{
case "/":
responseString = ProcessRoot(request);
break;
case "/session":
responseString = SessionDetails(request);
break;
case "/resetSession":
responseString = ResetSession(request);
break;
case "/resetSessions":
responseString = ResetSessions(request);
break;
// case "/refreshSession":
// responseString = RefreshSession(request);
// break;
// case "/refreshSessions":
// responseString = RefreshSessions(request);
// break;
case "/enableSessions":
responseString = EnableSessions(request);
break;
case "/disableSessions":
responseString = DisableSessions(request);
break;
default:
responseString = ProcessRoot(request);
break;
}
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
}
}
catch (HttpListenerException)
{
Console.WriteLine("HTTP server was shut down.");
}
}
private string DisableSessions(HttpListenerRequest request)
{
bool confirm = false;
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sessionDetails.Logout();
}
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been disabled</h2></CENTER>", "/");
}
else
{
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to disable all sessions ?</h2></CENTER>");
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, disable sessions</A>]&nbsp;[<A HREF='/'>NO, do not disable sessions</A>]</CENTER>", request.Url.OriginalString);
}
return sbHtmlPageBody.ToString();
}
private string EnableSessions(HttpListenerRequest request)
{
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sessionDetails.Logon();
}
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been enabled</h2></CENTER>", "/");
}
else
{
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to enable all sessions ?</h2></CENTER>");
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, enable sessions</A>]&nbsp;[<A HREF='/'>NO, do not enable sessions</A>]</CENTER>", urlOriginalString);
}
return sbHtmlPageBody.ToString();
}
private string RefreshSession(HttpListenerRequest request)
{
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
Session sessionDetails = Session.LookupSession(sessionId);
if (sessionDetails == null) throw new Exception("Session not found");
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
string url = "/session?" + GetParameterList(urlOriginalString);
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
sessionDetails.Refresh();
url = RemoveQueryStringByKey(urlOriginalString, "confirm");
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL=/session?{0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", GetParameterList(urlOriginalString));
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>]&nbsp;[<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEAD><BODY>", urlOriginalString);
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='/session?{0}'>{1}</A> has been refreshed</h2></CENTER>", GetParameterList(url), sessionId);
}
else
{
sbHtmlPageBody.AppendFormat("<CENTER><h2>Are you sure you want to refresh session <A HREF='{0}'>{1}</A>?</h2></CENTER>", url, sessionId);
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}&confirm=1'>YES, refresh session</A>]&nbsp;[<A HREF='{0}'>NO, do not refresh session</A>]</CENTER>", url);
}
return sbHtmlPageBody.ToString();
}
private string RefreshSessions(HttpListenerRequest request)
{
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sessionDetails.Refresh();
}
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been refreshed</h2></CENTER>", "/");
}
else
{
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to refresh all sessions ?</h2></CENTER>");
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, refresh sessions</A>]&nbsp;[<A HREF='/'>NO, do not refresh sessions</A>]</CENTER>", urlOriginalString);
}
return sbHtmlPageBody.ToString();
}
private string ResetSessions(HttpListenerRequest request)
{
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sessionDetails.Reset("Reset from WebInterface");
}
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL={0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", "/");
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='{0}'>Sessions</A> have been reset</h2></CENTER>", "/");
}
else
{
sbHtmlPageBody.Append("<CENTER><h2>Are you sure you want to reset all sessions ?</h2></CENTER>");
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}?confirm=1'>YES, reset sessions</A>]&nbsp;[<A HREF='/'>NO, do not reset sessions</A>]</CENTER>", urlOriginalString);
}
return sbHtmlPageBody.ToString();
}
private string ResetSession(HttpListenerRequest request)
{
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
Session sessionDetails = Session.LookupSession(sessionId);
if (sessionDetails == null) throw new Exception("Session not found");
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
bool confirm = false;
string urlOriginalString = request.Url.OriginalString;
string url = "/session?" + GetParameterList(urlOriginalString);
if (request.QueryString["confirm"] != null)
{
if (Convert.ToInt16(request.QueryString["confirm"]) != 0)
{
confirm = true;
sessionDetails.Reset("Reset from WebInterface");
url = RemoveQueryStringByKey(urlOriginalString, "confirm");
}
}
if (confirm)
{
sbHtmlPageBody = new StringBuilder();
sbHtmlPageBody.AppendFormat(@"<HEAD><META http-equiv='refresh' content=2;URL=/session?{0} /><CENTER><TITLE>QuickFIX Engine Web Interface</TITLE><H1>QuickFIX Engine Web Interface</H1></CENTER>", GetParameterList(urlOriginalString));
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/'>HOME</A>]&nbsp;[<A HREF='{0}'>RELOAD</A>]</CENTER><HR/></HEAD><BODY>", urlOriginalString);
sbHtmlPageBody.AppendFormat("<CENTER><h2><A HREF='/session?{0}'>{1}</A> has been reset</h2></CENTER>", GetParameterList(url), sessionId);
}
else
{
sbHtmlPageBody.AppendFormat("<CENTER><h2>Are you sure you want to reset session <A HREF='{0}'>{1}</A>?</h2></CENTER>", url, sessionId);
sbHtmlPageBody.AppendFormat("<CENTER>[<A HREF='{0}&confirm=1'>YES, reset session</A>]&nbsp;[<A HREF='{0}'>NO, do not reset session</A>]</CENTER>", url);
}
return sbHtmlPageBody.ToString();
}
private string ProcessRoot(HttpListenerRequest request)
{
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
//Session count
sbHtmlPageBody.AppendFormat("<CENTER><b><i>{0} Sessions managed by QuickFIX</b></i></CENTER><HR/>", _sessionSettings.GetSessions().Count);
//session management links
sbHtmlPageBody.AppendFormat(@"<CENTER><A HREF='/resetSessions{0}'>RESET</A>&nbsp;<A HREF='/refreshSessions'>REFRESH</A>&nbsp;<A HREF='/enableSessions'>ENABLE</A>&nbsp;<A HREF='/disableSessions'>DISABLE</A></CENTER><HR/></HEADER><BODY>", GetParameterList(request.Url.OriginalString));
//Start the table generation
sbHtmlPageBody.Append("<table id=\"sessionlist\" style=\"border-width:1; padding:2; width:100%\">");
sbHtmlPageBody.Append("<tr>");
sbHtmlPageBody.Append(AddCell("Session", true));
sbHtmlPageBody.Append(AddCell("Type", true));
sbHtmlPageBody.Append(AddCell("Enabled", true));
sbHtmlPageBody.Append(AddCell("Session Time", true));
sbHtmlPageBody.Append(AddCell("Logged On", true));
sbHtmlPageBody.Append(AddCell("Next Incoming", true));
sbHtmlPageBody.Append(AddCell("Next Outgoing", true));
sbHtmlPageBody.Append("</tr>");
foreach (SessionID session in _sessionSettings.GetSessions())
{
Session sessionDetails = Session.LookupSession(session);
sbHtmlPageBody.Append("<tr>");
sbHtmlPageBody.Append(AddCell(String.Format(
"<a href=\"session?BeginString={0}&SenderCompID={1}&TargetCompID={2}\">{0}:{1}->{2}</a>",
session.BeginString, session.SenderCompID, session.TargetCompID)));
sbHtmlPageBody.Append(AddCell(sessionDetails.IsInitiator ? "initiator" : "acceptor"));
sbHtmlPageBody.Append(AddCell(sessionDetails.IsEnabled ? "yes" : "no"));
sbHtmlPageBody.Append(AddCell(sessionDetails.IsSessionTime ? "yes" : "no"));
sbHtmlPageBody.Append(AddCell(sessionDetails.IsLoggedOn ? "yes" : "no"));
sbHtmlPageBody.Append(AddCell(sessionDetails.NextTargetMsgSeqNum.ToString()));
sbHtmlPageBody.Append(AddCell(sessionDetails.NextSenderMsgSeqNum.ToString()));
sbHtmlPageBody.Append("</tr>");
}
sbHtmlPageBody.Append("</table>");
return sbHtmlPageBody.ToString();
}
public string SessionDetails(HttpListenerRequest request)
{
SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]);
Session sessionDetails = Session.LookupSession(sessionId);
if (sessionDetails == null) throw new Exception("Session not found");
StringBuilder sbHtmlPageBody = _sbHtmlHeader;
string url = request.Url.OriginalString;
string urlOriginalString = request.Url.OriginalString;
if (request.QueryString["enabled"] != null)
{
if (!Convert.ToBoolean(request.QueryString["enabled"]))
sessionDetails.Logout();
else
sessionDetails.Logon();
url = RemoveQueryStringByKey(urlOriginalString, "Enabled");
}
if (request.QueryString["next incoming"] != null)
{
int value = Convert.ToInt16(request.QueryString["next incoming"]);
sessionDetails.NextTargetMsgSeqNum = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "next incoming");
}
if (request.QueryString["Next Outgoing"] != null)
{
int value = Convert.ToInt16(request.QueryString["Next Outgoing"]);
sessionDetails.NextSenderMsgSeqNum = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "Next Outgoing");
}
if (request.QueryString["SendRedundantResendRequests"] != null)
{
sessionDetails.SendRedundantResendRequests = Convert.ToBoolean(request.QueryString["SendRedundantResendRequests"]);
url = RemoveQueryStringByKey(urlOriginalString, "SendRedundantResendRequests");
}
if (request.QueryString["CheckCompId"] != null)
{
sessionDetails.CheckCompID = Convert.ToBoolean(request.QueryString["CheckCompId"]);
url = RemoveQueryStringByKey(urlOriginalString, "CheckCompId");
}
if (request.QueryString["CheckLatency"] != null)
{
sessionDetails.CheckLatency = Convert.ToBoolean(request.QueryString["CheckLatency"]);
url = RemoveQueryStringByKey(urlOriginalString, "CheckLatency");
}
if (request.QueryString["MaxLatency"] != null)
{
int value = Convert.ToInt16(request.QueryString["MaxLatency"]);
sessionDetails.MaxLatency = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "MaxLatency");
}
if (request.QueryString["LogonTimeout"] != null)
{
int value = Convert.ToInt16(request.QueryString["LogonTimeout"]);
sessionDetails.LogonTimeout = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "LogonTimeout");
}
if (request.QueryString["LogoutTimeout"] != null)
{
int value = Convert.ToInt16(request.QueryString["LogoutTimeout"]);
sessionDetails.LogoutTimeout = value <= 0 ? 1 : value;
url = RemoveQueryStringByKey(urlOriginalString, "LogoutTimeout");
}
if (request.QueryString["ResetOnLogon"] != null)
{
sessionDetails.ResetOnLogon = Convert.ToBoolean(request.QueryString["ResetOnLogon"]);
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnLogon");
}
if (request.QueryString["ResetOnLogout"] != null)
{
sessionDetails.ResetOnLogout = Convert.ToBoolean(request.QueryString["ResetOnLogout"]);
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnLogout");
}
if (request.QueryString["ResetOnDisconnect"] != null)
{
sessionDetails.ResetOnDisconnect = Convert.ToBoolean(request.QueryString["ResetOnDisconnect"]);
url = RemoveQueryStringByKey(urlOriginalString, "ResetOnDisconnect");
}
if (request.QueryString["RefreshOnLogon"] != null)
{
sessionDetails.RefreshOnLogon = Convert.ToBoolean(request.QueryString["RefreshOnLogon"]);
url = RemoveQueryStringByKey(urlOriginalString, "RefreshOnLogon");
}
if (request.QueryString["MillisecondsInTimestamp"] != null)
{
sessionDetails.MillisecondsInTimeStamp = Convert.ToBoolean(request.QueryString["MillisecondsInTimestamp"]);
url = RemoveQueryStringByKey(urlOriginalString, "MillisecondsInTimestamp");
}
if (request.QueryString["PersistMessages"] != null)
{
sessionDetails.PersistMessages = Convert.ToBoolean(request.QueryString["PersistMessages"]);
url = RemoveQueryStringByKey(urlOriginalString, "PersistMessages");
}
sbHtmlPageBody.AppendFormat(@"<CENTER>{0}</CENTER><HR/>", sessionId);
sbHtmlPageBody.AppendFormat(@"<CENTER>[<A HREF='/resetSession?{0}'>RESET</A>]&nbsp;[<A HREF='/refreshSession?{0}'>REFRESH</A>]</CENTER><HR/></HEADER><BODY>", GetParameterList(urlOriginalString));
sbHtmlPageBody.Append("<table id=\"session_details\" style=\"border-width:1; padding:2; width:100%\">");
sbHtmlPageBody.Append(AddRow("Enabled", sessionDetails.IsEnabled, url));
sbHtmlPageBody.Append(AddRow("ConnectionType", sessionDetails.IsInitiator ? "initiator" : "acceptor"));
sbHtmlPageBody.Append(AddRow("SessionTime", sessionDetails.IsSessionTime));
sbHtmlPageBody.Append(AddRow("LoggedOn", sessionDetails.IsLoggedOn));
sbHtmlPageBody.Append(AddRow("Next Incoming", sessionDetails.NextTargetMsgSeqNum, url));
sbHtmlPageBody.Append(AddRow("Next Outgoing", sessionDetails.NextSenderMsgSeqNum, url));
sbHtmlPageBody.Append(AddRow("SendRedundantResendRequests", sessionDetails.SendRedundantResendRequests, url));
sbHtmlPageBody.Append(AddRow("CheckCompId", sessionDetails.CheckCompID, url));
sbHtmlPageBody.Append(AddRow("CheckLatency", sessionDetails.CheckLatency, url));
sbHtmlPageBody.Append(AddRow("MaxLatency", sessionDetails.MaxLatency, url));
sbHtmlPageBody.Append(AddRow("LogonTimeout", sessionDetails.LogonTimeout, url));
sbHtmlPageBody.Append(AddRow("LogoutTimeout", sessionDetails.LogoutTimeout, url));
sbHtmlPageBody.Append(AddRow("ResetOnLogon", sessionDetails.ResetOnLogon, url));
sbHtmlPageBody.Append(AddRow("ResetOnLogout", sessionDetails.ResetOnLogout, url));
sbHtmlPageBody.Append(AddRow("ResetOnDisconnect", sessionDetails.ResetOnDisconnect, url));
sbHtmlPageBody.Append(AddRow("RefreshOnLogon", sessionDetails.RefreshOnLogon, url));
sbHtmlPageBody.Append(AddRow("MillisecondsInTimestamp", sessionDetails.MillisecondsInTimeStamp, url));
sbHtmlPageBody.Append(AddRow("PersistMessages", sessionDetails.PersistMessages, url));
sbHtmlPageBody.Append("</table>");
return sbHtmlPageBody.ToString();
}
public static string RemoveQueryStringByKey(string url, string key)
{
var uri = new Uri(url);
// this gets all the query string key value pairs as a collection
var newQueryString = HttpUtility.ParseQueryString(uri.Query);
// this removes the key if exists
newQueryString.Remove(key);
// this gets the page path from root without QueryString
string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path);
return newQueryString.Count > 0
? $"{pagePathWithoutQueryString}?{newQueryString}"
: pagePathWithoutQueryString;
}
public static string GetParameterList(string url)
{
return HttpUtility.ParseQueryString((new Uri(url).Query)).ToString();
}
private static string AddRow(string colName, bool value, string url = "")
{
string valueAsStr = value ? "yes" : "no";
string innerHtml = url.Length > 0
? $"<a href=\" {url}&{colName}={!value} \">toggle</a>"
: "";
return AddRow(colName, valueAsStr, innerHtml);
}
private static string AddRow(string colName, int value, string url = "")
{
string innerHtml = $"<a href=\" {url}&{colName}={value - 10} \"> << </a>" +
$"<a href=\" {url}&{colName}={value - 1} \"> < </a>" +
" | " +
$"<a href=\" {url}&{colName}={value + 1} \"> > </a>" +
$"<a href=\" {url}&{colName}={value + 10} \"> >> </a>";
return AddRow(colName, value.ToString(), innerHtml);
}
private static string AddRow(string colName, string value, string innerHtml = "")
{
StringBuilder row = new StringBuilder();
row.Append("<tr>");
row.Append(AddCell(colName));
row.Append(AddCell(value));
row.Append(AddCell(innerHtml));
row.Append("</tr>");
return row.ToString();
}
private static string AddCell(string cellContent, bool header = false)
{
string entryType = header ? "th" : "td";
return "<" + entryType + " align=\"left\">" + cellContent + "</" + entryType + ">";
}
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
if (_running)
{
Stop();
}
if (_connectionThread != null)
{
_connectionThread.Abort();
_connectionThread = null;
}
}
_disposed = true;
}
}
}
@@ -0,0 +1,2 @@
using System.Runtime.CompilerServices;
+293
View File
@@ -0,0 +1,293 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Google.Protobuf;
using Google.Protobuf.Reflection;
using System.Linq;
using Google.Protobuf.WellKnownTypes;
using bts.api;
namespace bts.fix
{
public static class ProtoFix
{
static readonly QuickFix.IMessageFactory _factory = new QuickFix.DefaultMessageFactory();
public static T parse_fix<T>(string fixstring) where T : IMessage, new()
{
return (T) parse_fix(new T().Descriptor, fixstring);
}
public static IMessage parse_fix(MessageDescriptor desc, string fixstring)
{
QuickFix.Message fix = new QuickFix.Message(fixstring, false);
var msg = create_message(desc, fix);
return msg;
}
static string get_between(string source, string start, string end)
{
int spos = source.IndexOf(start, 0);
if(spos < 0)
return string.Empty;
spos += start.Length;
int epos = source.IndexOf(end, spos);
if(epos < 0)
return string.Empty;
return source.Substring(spos, epos - spos);
}
public static Timestamp peek_timestamp(string fixstring, int field = 60)
{
string ts = get_between(fixstring, field.ToString() + "=", "\u0001");
if(!string.IsNullOrEmpty(ts))
{
var stamp = TimestampExtensions.parse_timestamp(ts);
return stamp;
}
return null;
}
public static string peek_field(string fixstring, int field)
{
string ts = get_between(fixstring, field.ToString() + "=", "\u0001");
return ts;
}
public static string peek_symbol(string fixstring)
{
return peek_field(fixstring, 55);
}
public static IMessage create_message(MessageDescriptor desc, QuickFix.FieldMap fix)
{
object obj = Activator.CreateInstance(desc.ClrType);
IMessage msg = (IMessage)obj;
return setup_message(msg, fix);
}
public static IMessage setup_message(IMessage msg, QuickFix.FieldMap fix)
{
foreach (var fd in msg.Descriptor.Fields.InDeclarationOrder())
{
try
{
set_field(msg, fd, fix);
}
catch(QuickFix.FieldNotFoundException) {}
}
return msg;
}
static int get_tag(IMessage msg, FieldDescriptor fd, out bool is_header)
{
is_header = false;
int fieldnum = 0;
if(fd.CustomOptions?.TryGetInt32((int)ApioptionsReflection.FieldOptions.fix_tag, out fieldnum) ?? false)
{
fd.CustomOptions.TryGetBool((int)ApioptionsReflection.FieldOptions.fix_tag_is_header, out is_header);
return fieldnum;
}
else
{
int tagbase = 0;
if(msg.Descriptor.CustomOptions?.TryGetInt32((int)ApioptionsReflection.MessageOptions.fix_algostrategy_tagbase, out tagbase) ?? false)
return fd.FieldNumber + tagbase;
}
throw new QuickFix.FieldNotFoundException(fd.FieldNumber);
}
static void set_field(IMessage msg, FieldDescriptor fd, QuickFix.FieldMap fix)
{
switch (fd.FieldType)
{
case FieldType.Bool:
{
int tag = get_tag(msg, fd, out bool is_header);
var data = is_header ? ((QuickFix.Message)fix).Header.GetBoolean(tag) : fix.GetBoolean(tag);
fd.Accessor.SetValue(msg, data);
}
break;
case FieldType.Float:
{
int tag = get_tag(msg, fd, out bool is_header);
var data = is_header ? ((QuickFix.Message)fix).Header.GetDecimal(tag) : fix.GetDecimal(tag);
object conv = Convert.ChangeType(data, fd.Accessor.GetValue(msg).GetType());
fd.Accessor.SetValue(msg, conv);
}
break;
case FieldType.Double:
{
int tag = get_tag(msg, fd, out bool is_header);
var data = is_header ? ((QuickFix.Message)fix).Header.GetDecimal(tag) : fix.GetDecimal(tag);
double d = decimal.ToDouble(data);
fd.Accessor.SetValue(msg, d);
}
break;
case FieldType.Int32:
case FieldType.SInt32:
case FieldType.UInt32:
{
int tag = get_tag(msg, fd, out bool is_header);
var data = is_header ? ((QuickFix.Message)fix).Header.GetInt(tag) : fix.GetInt(tag);
object conv = Convert.ChangeType(data, fd.Accessor.GetValue(msg).GetType());
fd.Accessor.SetValue(msg, conv);
}
break;
case FieldType.Int64:
case FieldType.SInt64:
case FieldType.UInt64:
{
int tag = get_tag(msg, fd, out bool is_header);
try
{
var data = is_header ? ((QuickFix.Message)fix).Header.GetDecimal(tag) : fix.GetDecimal(tag);
object conv = Convert.ChangeType(data, fd.Accessor.GetValue(msg).GetType());
fd.Accessor.SetValue(msg, conv);
}
catch(Exception ex) when (ex is QuickFix.FieldConvertError || ex is FormatException)
{
try
{
var snot = fix.GetString(tag); //scientific notation apparently appears in the files
var data = Double.Parse(snot, System.Globalization.NumberStyles.Float);
object conv = Convert.ChangeType(data, fd.Accessor.GetValue(msg).GetType());
fd.Accessor.SetValue(msg, conv);
}
catch {}
}
}
break;
case FieldType.Enum:
{
EnumValueDescriptor val = find_enum_by_attribute(msg, fd, fix);
if(val == null)
{
int tag = get_tag(msg, fd, out bool is_header);
char data = is_header ? ((QuickFix.Message)fix).Header.GetChar(tag) : fix.GetChar(tag);
val = fd.EnumType.FindValueByNumber((int)data);
val = val ?? fd.EnumType.FindValueByNumber(0);
}
fd.Accessor.SetValue(msg, val.Number);
}
break;
case FieldType.String:
{
int tag = get_tag(msg, fd, out bool is_header);
var data = is_header ? ((QuickFix.Message)fix).Header.GetString(tag) : fix.GetString(tag);
fd.Accessor.SetValue(msg, data);
}
break;
case FieldType.Message:
{
handle_message(msg, fd, fix);
}
break;
default:
throw new NotSupportedException("unsupported field type " + fd.FieldType.ToString());
}
}
private static EnumValueDescriptor find_enum_by_attribute(IMessage msg, FieldDescriptor fd, QuickFix.FieldMap fix)
{
int tag = get_tag(msg, fd, out bool is_header);
var fval = is_header ? ((QuickFix.Message)fix).Header.GetString(tag) : fix.GetString(tag);
foreach(var val in fd.EnumType.Values)
{
string eval = null;
if(val.CustomOptions?.TryGetString((int)ApioptionsReflection.EnumValueOptions.fix_value, out eval) ?? false)
{
if(eval != null && eval == fval)
return val;
}
}
return null;
}
static void handle_timestamp(IMessage msg, FieldDescriptor fd, QuickFix.FieldMap fix)
{
int tag = get_tag(msg, fd, out bool is_header);
var data = is_header ? ((QuickFix.Message)fix).Header.GetString(tag) : fix.GetString(tag);
Timestamp ts = TimestampExtensions.parse_timestamp(data);
fd.Accessor.SetValue(msg, ts);
}
static void handle_quantitymeasure(IMessage msg, FieldDescriptor fd, QuickFix.FieldMap fix)
{
int tag = get_tag(msg, fd, out bool is_header);
var data = is_header ? ((QuickFix.Message)fix).Header.GetInt(tag) : fix.GetInt(tag);
var qm = new bts.spark.QuantityMeasure { Absolute = (uint) Math.Abs(data) };
fd.Accessor.SetValue(msg, qm);
}
static void handle_message(IMessage msg, FieldDescriptor fd, QuickFix.FieldMap fix)
{
if (fd.MessageType.ClrType == typeof(Google.Protobuf.WellKnownTypes.Timestamp))
{
handle_timestamp(msg, fd, fix);
}
else if(fd.MessageType.ClrType == typeof(bts.spark.QuantityMeasure))
{
handle_quantitymeasure(msg, fd, fix);
}
else if(fd.MessageType.ClrType == typeof(bts.api.orderman.CustomOrderDefinition))
{
return;
}
else
{
if (!fd.IsRepeated)
{
var submsg = create_message(fd.MessageType, fix);
fd.Accessor.SetValue(msg, submsg);
}
else //for repeating groups we assume that the fd.FieldNumber corresponds to the "no" (number) tag in the fix
{
int tag = get_tag(msg, fd, out bool is_header);
int group = tag;//fd.FieldNumber;
int nvals = fix.GetInt(group);
IList list = (IList)fd.Accessor.GetValue(msg);
if (nvals == 1)
{
var submsg = create_message(fd.MessageType, fix);
list.Add(submsg);
}
else
{
var delimproto = fd.MessageType.Fields.InDeclarationOrder()[0];
var delim = delimproto.FieldNumber;
List<QuickFix.Fields.IField> repeated = new List<QuickFix.Fields.IField>();
var forder = fix.RepeatedTags.Select(x => x.Tag).Distinct();
var dict = fix.ToDictionary();
foreach(int fo in forder)
{
if(dict.TryGetValue(fo, out QuickFix.Fields.IField field))
repeated.Add(field);
}
repeated.AddRange(fix.RepeatedTags);
QuickFix.FieldMap fm = null;
foreach (var rep in repeated)
{
if (rep.Tag == delim)
{
if (fm != null)
{
var submsg = create_message(fd.MessageType, fm);
list.Add(submsg);
}
fm = new QuickFix.FieldMap();
}
if (fm != null)
fm.SetField(rep);
} //the last one
if (fm != null && fm.Count() > 0)
{
var submsg = create_message(fd.MessageType, fm);
list.Add(submsg);
}
}
}
}
}
}
}
+2
View File
@@ -0,0 +1,2 @@
# BTS Spark FIX Adapter
+84
View File
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.IO;
using bts.api.orderman;
namespace bts.fix
{
public class ATDL
{
readonly Dictionary<string, FIXAlgoDefinition> _atdl = new Dictionary<string, FIXAlgoDefinition>();
public IReadOnlyDictionary<string, FIXAlgoDefinition> algo_definitions => _atdl;
public ATDL(string path = "./spark_algos.json")
{
using(var fs = new StreamReader(path))
{
var algos = Google.Protobuf.JsonParser.Default.Parse<FIXAlgoDefinitions>(fs);
foreach(var algo in algos.AlgoStrategy)
{
_atdl[algo.Name] = algo;
}
}
}
public static void write_json(FIXAlgoDefinitions defs, string path)
{
using (var fs = new StreamWriter(path))
{
var s = Google.Protobuf.JsonFormatter.Settings.Default;
var raw = Google.Protobuf.JsonFormatter.Default.Format(defs);
object parsed = Newtonsoft.Json.JsonConvert.DeserializeObject(raw);
string indented = Newtonsoft.Json.JsonConvert.SerializeObject(parsed, Newtonsoft.Json.Formatting.Indented);
fs.Write(indented);
}
}
public static void write_sample_json(string path = "./spark_algos.json")
{
FIXAlgoDefinition tc = new FIXAlgoDefinition()
{
Name = "TickAndCover",
Definition = new CustomOrderDefinition
{
Cover = new CoverOrderDefinition
{
Child = new CustomOrderDefinition
{
Top = new TopOrderDefinition { Child = new CustomOrderDefinition { Limit = new LimitOrderDefinition() } }
},
Hedge = new CustomOrderDefinition
{
Lean = new LeanOrderDefinition { Child = new CustomOrderDefinition { Limit = new LimitOrderDefinition() } }
}
}
}
};
FIXAlgoDefinition sc = new FIXAlgoDefinition()
{
Name = "SmashAndCover",
Definition = new CustomOrderDefinition
{
Cover = new CoverOrderDefinition
{
Child = new CustomOrderDefinition
{
Lean = new LeanOrderDefinition{ Child = new CustomOrderDefinition { Limit = new LimitOrderDefinition() } }
},
Hedge = new CustomOrderDefinition
{
Lean = new LeanOrderDefinition { Child = new CustomOrderDefinition { Limit = new LimitOrderDefinition() } }
}
}
}
};
FIXAlgoDefinitions defs = new FIXAlgoDefinitions();
defs.AlgoStrategy.Add(tc);
defs.AlgoStrategy.Add(sc);
write_json(defs, path);
}
}
}
@@ -0,0 +1,288 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Text;
using QuickFix;
using QuickFix.Fields;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.user;
using bts.api.admin;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using QuickFix.FIX42;
using Message = QuickFix.Message;
using System.Threading;
using System.Threading.Tasks;
namespace bts.fix
{
class DropCopySession : SessionBase
{
protected IDisposable _fillsdisp;
protected IDisposable _ordersdisp;
protected static Lazy<OrderService> _orderservice = new Lazy<OrderService>(() => bts.api.Locator.locate<OrderService>(), true);
protected readonly ConcurrentDictionary<uint, OrderRecordImmutable> _order_cache = new ConcurrentDictionary<uint, OrderRecordImmutable>();
protected readonly ConcurrentDictionary<uint, string> _fix_clorderids = new ConcurrentDictionary<uint, string>();
protected readonly ConcurrentDictionary<string, uint> _external_id_vs_bts_key = new ConcurrentDictionary<string, uint>();
public DropCopySession(IFixRouter router, SessionID sess) : base(router, sess)
{
}
override public void OnLogon(SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
var msg = sessionID.ToString() + " Logged on";
log_info(msg);
subscribe_fills_and_orders(sessionID);
}
override public void OnLogout(SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
var msg = sessionID.ToString() + " Logged out";
log_info(msg);
if (_ordersdisp != null)
_ordersdisp.Dispose();
if (_fillsdisp != null)
_fillsdisp.Dispose();
}
override public void FromApp(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
MsgType type = Message.IdentifyType(message.ToString());
switch (type.getValue())
{
case MsgType.ORDER_STATUS_REQUEST:
case MsgType.SECURITYDEFINITIONREQUEST:
log_info("IN: " + message);
Crack(message, sessionID);
break;
default:
log_info("UNSUPPORTED MESSAGE IN: " + type.getValue());
throw new UnsupportedMessageType();
}
}
virtual public void OnMessage(OrderStatusRequest req, SessionID s)
{
uint oid = uint.Parse(req.OrderID.getValue());
if (_order_cache.TryGetValue(oid, out OrderRecordImmutable order))
{
var ostat = build_order_status(s, order);
//ostat.OrdStatusReqID = new OrdStatusReqID(req.OrdStatusReqID.getValue());
//TODO
_router.send(ostat, s);
}
}
virtual public void OnMessage(SecurityDefinitionRequest req, SessionID s)
{
if (req.SecurityRequestType.getValue() == SecurityRequestType.REQUEST_LIST_SECURITIES)
{
var secs = _router.get_securities();
if (req.IsSetSymbol())
secs = secs.Where(x => x.Symbol.ToLower() == req.Symbol.getValue().ToLower());
if (req.IsSetSecurityType())
secs = secs.Where(x => security_types_equal(x.security_type(), req.SecurityType));
if (req.IsSetSecurityExchange())
secs = secs.Where(x => exchanges_equal(x.Exchange, req.SecurityExchange));
var def = new SecurityDefinition(req.SecurityReqID,
new SecurityResponseID(Guid.NewGuid().ToString()),
new TotalNumSecurities(secs.Count()));
_router.send(def, s);
}
else
throw new IncorrectTagValue(QuickFix.Fields.Tags.SecurityRequestType);
}
void _add_order_info_to_caches(OrderRecordImmutable o) {
_order_cache[o.Key] = o;
if (o.HasDef && !string.IsNullOrEmpty(o.Def.ExternalOrderId)) {
_external_id_vs_bts_key[o.Def.ExternalOrderId] = o.Key;
}
}
virtual async protected void subscribe_fills_and_orders(SessionID sessionID)
{
var scheduler = TaskPoolScheduler.Default;
var interval = TimeSpan.FromMilliseconds(500);
var on_orders = _orderservice.Value.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(_adminobservable).Table().Cache();
var on_fills = _orderservice.Value.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(_adminobservable).Table().Cache();
var odone = new TaskCompletionSource<int>();
_ordersdisp = on_orders.Subscribe(o =>
{
foreach (var order in o.added)
{
var ostat = build_order_status(sessionID, order);
_add_order_info_to_caches(order);
_router.send(ostat, sessionID);
}
foreach (var order in o.changed)
{
var ostat = build_order_status(sessionID, order, true);
_router.send(ostat, sessionID);
}
odone.TrySetResult(0);
});
await odone.Task;
_fillsdisp = on_fills.Subscribe(f =>
{
foreach (var fill in f.added)
{
var fi = build_fill(sessionID, fill);
_router.send(fi, sessionID);
}
foreach (var fill in f.changed)
{
var fi = build_fill(sessionID, fill, true);
_router.send(fi, sessionID);
}
});
}
virtual protected void process_order_state(in OrderStateImmutable state, ref uint filled, ref uint leaves, ref double avgprx,
ref QuickFix.Fields.OrdStatus fstatus, ref NativeOrderStateImmutable? native, FillImmutable? fill = null)
{
filled = state.FilledQuantity;
leaves = state.Quantity - state.FilledQuantity;
avgprx = state.AvgPrice;
fstatus = translate_status(state, ref native);
}
protected bts.api.SecurityImmutable? resolve_execution_report_security(bts.api.SecurityImmutable? sec, ExecutionReport report)
{
report.SecurityExchange = convert_exchange(sec.Value.Exchange);
var sectype = sec.Value.security_type();
report.SecurityType = convert_security_type(sectype);
if (sec.Value.HasGlbx)
report.SecurityDesc = new SecurityDesc(sec.Value.Glbx.SecurityDesc);
switch (sectype)
{
case bts.api.SecurityType.Future:
report.MaturityMonthYear = new MaturityMonthYear(sec.Value.EarliestExpiration().ToString("yyyyMMdd"));
break;
case bts.api.SecurityType.Spread: //TODO this is not quite right for spreads of spreads
var leg = sec.Value.Spread.Legs.OrderBy(x => x.Security.EarliestExpiration()).First();
report.MaturityMonthYear = new MaturityMonthYear(leg.Security.EarliestExpiration().ToString("yyyyMMdd"));
break;
default:
throw new UnsupportedMessageType();
}
return sec;
}
virtual protected ExecutionReport build_fill(SessionID sess, in FillImmutable fill, bool change = false)
{
var exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.PARTIAL_FILL);
QuickFix.Fields.OrdStatus status = new QuickFix.Fields.OrdStatus();
uint filled = 0; uint leaves = 0; double prx = 0.0; uint orig_qty = 0;
OrderRecordImmutable order; NativeOrderStateImmutable? native = null;
if (_order_cache.TryGetValue(fill.OrderKey, out order))
{
orig_qty = order.State.Quantity;
process_order_state(order.State, ref filled, ref leaves, ref prx, ref status, ref native);
if(Convert.ToDouble(order.State.Quantity) == fill.LastShares)
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.FILL);
}
if (change)
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.TRADE_CORRECT);
var sec = _router.lookup(fill.SecurityKey);
ExecutionReport report = new ExecutionReport(
new OrderID(fill.OrderKey.ToString()),
new ExecID(fill.FillKey.ToString()),
new QuickFix.Fields.ExecTransType( ExecTransType.NEW ),
exec,
status,
new Symbol(sec?.Symbol ?? string.Empty),
new QuickFix.Fields.Side(fill.Side == bts.api.Side.Buy ? QuickFix.Fields.Side.BUY : QuickFix.Fields.Side.SELL),
new LeavesQty(leaves),
new CumQty(filled),
new AvgPx(Convert.ToDecimal(prx)));
report.Account = new Account(fill.Account);
report.OrderQty = new OrderQty(orig_qty);
report.LastShares = new LastShares(Convert.ToDecimal(fill.LastShares));
report.LastPx = new LastPx(Convert.ToDecimal(fill.LastPrice));
var ts = fill.HasTimestamp ? fill.Timestamp.ToDateTime() : DateTime.MinValue;
report.TransactTime = new TransactTime(ts, true);
report.TradeDate = new TradeDate(ts.ToString("yyyyMMdd"));
sec = resolve_execution_report_security(sec, report);
if (native.HasValue && !string.IsNullOrEmpty(native.Value.ExchangeOrderId))
report.SecondaryOrderID = new SecondaryOrderID(native.Value.ExchangeOrderId);
if (sec.HasValue && sec.Value.security_type() == bts.api.SecurityType.Spread ||
sec.Value.security_type() == bts.api.SecurityType.Synthetic)
{
if (fill.FillKind == bts.api.orderman.Fill.Types.FillKind.Leg)
report.MultiLegReportingType = new MultiLegReportingType('2');
else
report.MultiLegReportingType = new MultiLegReportingType('3');
}
return report;
}
virtual protected ExecutionReport build_order_status(SessionID sess, in OrderRecordImmutable order, bool change = false)
{
var exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.NEW);
if (_order_cache.TryGetValue(order.Key, out OrderRecordImmutable _))
exec = new QuickFix.Fields.ExecType(QuickFix.Fields.ExecType.ORDER_STATUS);
QuickFix.Fields.OrdStatus status = new QuickFix.Fields.OrdStatus();
uint filled = 0; uint leaves = 0; double prx = 0.0; NativeOrderStateImmutable? native = null;
process_order_state(order.State, ref filled, ref leaves, ref prx, ref status, ref native);
var sec = _router.lookup(order.Def.SecurityKey);
ExecutionReport report = new ExecutionReport(
new OrderID(order.Key.ToString()),
new ExecID("0"),
new QuickFix.Fields.ExecTransType(ExecTransType.STATUS),
exec,
status,
new Symbol(sec?.Symbol ?? string.Empty),
new QuickFix.Fields.Side(order.Def.Side == bts.api.Side.Buy ? QuickFix.Fields.Side.BUY : QuickFix.Fields.Side.SELL),
new LeavesQty(leaves),
new CumQty(filled),
new AvgPx(Convert.ToDecimal(prx)));
report.Account = new Account(order.Def.Account);
report.OrderQty = new OrderQty(order.State.Quantity);
report.Price = new Price(Convert.ToDecimal(order.State.Price));
report.TimeInForce = new QuickFix.Fields.TimeInForce((char)order.Def.TimeInForce);
var ts = order.State.LastChange.ToDateTime();
report.TransactTime = new TransactTime(ts, true);
if(!string.IsNullOrEmpty(order.State.LastMsg))
report.Text = new Text(order.State.LastMsg);
sec = resolve_execution_report_security(sec, report);
if (native.HasValue)
{
report.OrdType = new OrdType((char)native.Value.NativeType);
if(!string.IsNullOrEmpty(native.Value.ExchangeOrderId))
report.SecondaryOrderID = new SecondaryOrderID(native.Value.ExchangeOrderId);
}
//TODO Big race here, we need a correlation id of some sort
if (_fix_clorderids.TryGetValue(order.Key, out string clorderid))
{
report.ClOrdID = new ClOrdID(clorderid);
}
return report;
}
}
}
@@ -0,0 +1,8 @@
using System;
namespace bts.fix
{
interface IFIXSession : QuickFix.IApplication
{
}
}
@@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QuickFix;
using QuickFix.Fields;
using bts.api;
using bts.api.orderman;
using QuickFix.FIX42;
using Message = QuickFix.Message;
namespace bts.fix
{
/*
create limit
8=FIX.4.29=15635=D49=CQG56=SPARK34=150=3073797=N52=20200109-20:20:5011=900010081=ACC A21=255=SIM:ESH0207=SIM54=160=20200809-18:20:3238=1040=244=320059=010=129
update limit
8=FIX.4.29=11035=G49=CQG56=SPARK37=1341=9000100811=9000200821=155= ESH054=160=20200113-19:45:2938=1040=244=320110=017
delete limit
8=FIX.4.29=9335=F49=CQG56=SPARK41= 9000100837=1311=9000300855=ESH054=160=20200113-19:45:2738=1010=247
create algo
8=FIX.4.29=15635=D49=CQG56=SPARK34=150=3073797=N52=20200109-20:20:5011=900010081=ACC A21=255=SIM:ESH0207=SIM54=160=20200809-18:20:3238=1040=244=320059=06401=Lean8401=118402=28405=010=128
jake:
new
8=FIX.4.29=15635=D49=CQG56=SPARK34=150=3073797=N52=20200109-20:20:5011=86753091=ACC A21=255=SIM:ESH0207=SIM54=160=20200809-18:20:3238=1040=244=320059=010=128
cancel-replace
8=FIX.4.29=11035=G49=CQG56=SPARK37=1341=867530911=867531021=155=ESH054=160=20200113-19:45:2938=1040=244=320110=017
cancel
8=FIX.4.29=8935=F49=CQG56=SPARK41=867530937=1311=867531155=ESH054=160=20200113-19:45:2738=1010=123
*/
class OrderSession : DropCopySession
{
readonly ATDL _atdl = new ATDL();
public OrderSession(IFixRouter router, SessionID sess) : base(router, sess)
{
}
override public void FromApp(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
MsgType type = Message.IdentifyType(message.ToString());
switch (type.getValue())
{
case MsgType.ORDER_STATUS_REQUEST: //handled in DropCopySession
case MsgType.SECURITYDEFINITIONREQUEST: //handled in DropCopySession
case MsgType.NEWORDERSINGLE:
case MsgType.ORDERCANCELREPLACEREQUEST:
case MsgType.ORDERCANCELREQUEST:
log_info("IN: " + message);
Crack(message, sessionID);
break;
default:
log_info("UNSUPPORTED MESSAGE IN: " + type.getValue());
throw new UnsupportedMessageType();
}
}
virtual public void OnMessage(OrderCancelRequest cancel, SessionID s)
{
var original_client_id = cancel.OrigClOrdID.getValue();
if (!base._external_id_vs_bts_key.ContainsKey(original_client_id))
throw new IncorrectTagValue(QuickFix.Fields.Tags.OrigClOrdID);
var key = base._external_id_vs_bts_key[original_client_id];
var resp = _orderservice.Value.cancel_order(new Cancel { OrderKey = key });
if (resp.ErrorCode != OrderError.Ok)
{
send_cancel_reject(s, resp.ErrorCode, "Cancel Rejected: " + resp.ErrorCode.ToString() + " - " + resp.Msg, cancel);
}
}
virtual public void OnMessage(OrderCancelReplaceRequest replace, SessionID s)
{
var original_client_id = replace.OrigClOrdID.getValue();
if (!base._external_id_vs_bts_key.ContainsKey(original_client_id))
throw new IncorrectTagValue(QuickFix.Fields.Tags.OrigClOrdID);
var key = base._external_id_vs_bts_key[original_client_id];
double price = Convert.ToDouble(replace.Price.getValue());
uint sz = Convert.ToUInt32(Math.Abs(replace.OrderQty.getValue()));
var upd = new Update
{
OrderKey = key,
Price = price,
Quantity = sz,
Custom = get_custom(replace),
};
var resp = _orderservice.Value.update_order(upd);
if (resp.ErrorCode != OrderError.Ok)
{
send_cancel_reject(s, resp.ErrorCode, "Cancel Rejected: " + resp.ErrorCode.ToString() + " - " + resp.Msg, replace);
}
}
virtual public void OnMessage(NewOrderSingle create, SessionID s)
{
Venue ven = Venue.Glbx;
if(create.IsSetSecurityExchange())
ven = convert_exchange(create.SecurityExchange);
if(create.IsSetExDestination())
ven = convert_exchange(create.ExDestination);
if (ven == bts.api.Venue.Unset)
throw new IncorrectTagValue(QuickFix.Fields.Tags.SecurityExchange);
var sec = _router.lookup(create.Symbol.getValue());
if (!sec.HasValue)
sec = _router.lookup(create.SecurityDesc.getValue());
if (!sec.HasValue)
throw new IncorrectTagValue(QuickFix.Fields.Tags.Symbol);
OrderDefinition def = (OrderDefinition)ProtoFix.create_message(OrderDefinition.Descriptor, create);
def.SecurityKey = sec.Value.Id;
def.User = _login_name;
def.UserId = lookup_user(_login_name);
def.AccountId = lookup_account(def.Account);
Create cr = new Create();
def.CustomDef = get_custom(create);
cr.NewOrder = def;
try
{
if (create.IsSetPrice())
cr.Price = Convert.ToDouble(create.Price.getValue());
}
catch { throw new IncorrectTagValue(QuickFix.Fields.Tags.Price); }
try
{
if(!create.IsSetOrderQty() || create.OrderQty.getValue() < 0)
throw new IncorrectTagValue(QuickFix.Fields.Tags.OrderQty);
cr.Quantity = Convert.ToUInt32(Math.Abs(create.OrderQty.getValue()));
}
catch { throw new IncorrectTagValue(QuickFix.Fields.Tags.OrderQty); }
if (!create.IsSetClOrdID())
throw new IncorrectTagValue(QuickFix.Fields.Tags.ClOrdID);
else
def.ExternalOrderId = create.ClOrdID.ToString();
var resp = _orderservice.Value.create_order(cr);
_fix_clorderids[resp.OrderKey] = create.ClOrdID.getValue();
if (resp.ErrorCode != OrderError.Ok)
{
_router.send_biz_reject(s, "Order Rejected: " + resp.ErrorCode.ToString() + " - " + resp.Msg, create);
}
}
CustomOrderDefinition get_custom(Message fix)
{
FIXAlgoDefinition.Descriptor.FindFieldByNumber(FIXAlgoDefinition.NameFieldNumber).
CustomOptions.TryGetInt32((int)ApioptionsReflection.FieldOptions.fix_tag, out int algotag);
if(fix.IsSetField(algotag))
{
string algotype = fix.GetString(algotag);
if(!_atdl.algo_definitions.TryGetValue(algotype, out FIXAlgoDefinition algodef))
throw new IncorrectTagValue(algotag);
else
{
var custom = algodef.Definition.Clone();
foreach (var fd in CustomOrderDefinition.Descriptor.Fields.InDeclarationOrder())
{
var val = fd.Accessor.GetValue(custom);
if(val != null && val is Google.Protobuf.IMessage valmsg)
{
ProtoFix.setup_message(valmsg, fix);
break;
}
}
return custom;
}
}
else
{
if(!fix.IsSetField(QuickFix.Fields.Tags.OrdType))
throw new IncorrectTagValue(QuickFix.Fields.Tags.OrdType);
switch (fix.GetChar(QuickFix.Fields.Tags.OrdType))
{
case '1':
return new CustomOrderDefinition { Market = new MarketOrderDefinition() };
case '2':
return new CustomOrderDefinition { Limit = new LimitOrderDefinition() };
case '3':
return new CustomOrderDefinition { Stop = new StopOrderDefinition()
{
StopPrice = Convert.ToDouble(fix.GetDecimal(QuickFix.Fields.Tags.StopPx)) }
};
case '4':
return new CustomOrderDefinition { StopLimit = new StopLimitOrderDefinition()
{
StopPrice = Convert.ToDouble(fix.GetDecimal(QuickFix.Fields.Tags.StopPx)) }
};
default:
throw new IncorrectTagValue(QuickFix.Fields.Tags.OrdType);
}
}
}
}
}
+383
View File
@@ -0,0 +1,383 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Text;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using QuickFix;
using QuickFix.Fields;
using mbts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.user;
using bts.api.admin;
using QuickFix.FIX42;
using Message = QuickFix.Message;
namespace bts.fix
{
abstract class SessionBase : QuickFix.MessageCracker, IFIXSession
{
protected static AuthService _authservice = bts.api.Locator.locate<AuthService>();
protected static Lazy<AdminService> _adminservice = new Lazy<AdminService>(() => bts.api.Locator.locate<AdminService>(), true);
protected static Lazy<bts.metrics.ShellLatencyService> _latencyservice =
new Lazy<bts.metrics.ShellLatencyService>(() => bts.api.Locator.locate<bts.metrics.ShellLatencyService>(), true);
protected string _login_name;
protected AuthToken _token;
protected IDisposable _admindisp;
protected IObservable<AdminModelWrapper> _adminobservable;
protected AdminDataImmutable? _admin_data;
protected readonly IFixRouter _router;
protected readonly SessionID _sess;
public SessionBase(IFixRouter router, SessionID sess)
{
_router = router;
_sess = sess;
_clockdisp = _latencyservice.Value.subscribe_clock().Subscribe(x =>
{
var t = x.all.FirstOrDefault();
if(t != null)
_clock = t.Timestamp;
});
_adminobservable = _adminservice.Value.subscribei_admin_data().Select(x =>
{
return new AdminModelWrapper(x.all.TryFirst(), _login_name);
});
_admindisp = _adminobservable.Subscribe(x => _admin_data = x.admin_data);
}
abstract public void FromApp(Message message, SessionID sessionID);
abstract public void OnLogon(SessionID sessionID); //subscribe to your stuff
abstract public void OnLogout(SessionID sessionID); //unsub
virtual public void ToApp(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
log_info("OUT: " + message);
}
readonly IDisposable _clockdisp; ulong _clock = 0;
virtual public void FromAdmin(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
MsgType type = Message.IdentifyType(message.ToString());
switch (type.getValue())
{
case MsgType.LOGON:
{
try
{
Logon msg = (Logon)message;
var fixlogin = new QuickFix.Fields.SenderCompID();
string login = msg.Header.GetString(fixlogin.Tag).ToLower();
// var fixuser = new QuickFix.Fields.TargetSubID();
// string user = msg.Header.GetString(fixuser.Tag);
_login_name = login;
string pass = string.Empty;
if(msg.IsSetRawData())
pass = msg.RawData.getValue();
var resp = _authservice.authenticate_login(new AuthenticateLoginRequest {
Login = login, Password = pass});
if (resp != null && resp.IsValid)
_token = resp.Token;
else
throw new RejectLogon("Logon rejected " + resp?.Reason ?? string.Empty);
}
catch (RejectLogon)
{
throw;
}
catch (Exception ex)
{
throw new RejectLogon("Logon rejected " + ex.ToString());
}
}
break;
case MsgType.HEARTBEAT:
{
Heartbeat hb = (Heartbeat)message;
handle_heartbeat(hb, sessionID);
}
break;
default:
log_info("ADMININ: " + message);
break;
}
}
async protected void handle_heartbeat(Heartbeat hb, SessionID sessionID)
{
var resp = await _latencyservice.Value.report_async(new bts.metrics.Report { HostName = "BTSFIX_" + sessionID.ToString(),
Timestamp = _clock, User = _login_name });
if (resp.success)
_router.send(hb, sessionID);
}
virtual public void ToAdmin(Message message, SessionID sessionID)
{
Log.assert_d(sessionID.Equals(_sess));
log_info("ADMINOUT: " + message);
}
virtual public void OnCreate(SessionID sessionID)
{
log_info("CREATE");
}
virtual public void OnMessage(BusinessMessageReject n, SessionID s)
{
//Log.assert(false, "biz reject wtf " + n.EncodedText.getValue());
Log.FIX.warn(_sess.ToString() + " Business Reject " + n.BusinessRejectReason.getValue() + " " + n.Text.getValue());
}
virtual public void OnMessage(News n, SessionID s)
{
log_info("news " + n.EncodedHeadline.getValue());
}
protected void log_info(string message, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0)
{
Log.FIX.info(_sess.ToString() + " " + message, file, line);
}
protected void log_error(string message, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0)
{
Log.FIX.error(_sess.ToString() + " " + message, file, line);
}
protected void send_cancel_reject(SessionID sess, OrderError reject_reason, string text, Message refmessage)
{
OrderCancelReject rej = new OrderCancelReject();
if (reject_reason == OrderError.UnknownOrder) {
rej.CxlRejReason = new CxlRejReason(CxlRejReason.UNKNOWN_ORDER);
rej.OrdStatus = new OrdStatus(OrdStatus.REJECTED);
rej.OrderID = new OrderID("NONE");
}
if (refmessage is QuickFix.FIX42.OrderCancelRequest cancel)
{
if (cancel.IsSetClOrdID())
rej.SetField(cancel.ClOrdID);
if (cancel.IsSetOrigClOrdID())
rej.SetField(cancel.OrigClOrdID);
if (cancel.IsSetOrderID() && reject_reason != OrderError.UnknownOrder)
rej.SetField(cancel.OrderID);
rej.CxlRejResponseTo = new CxlRejResponseTo(CxlRejResponseTo.ORDER_CANCEL_REQUEST);
rej.Text = new Text(text);
_router.send(rej, sess);
}
else if (refmessage is OrderCancelReplaceRequest replace)
{
if (replace.IsSetClOrdID())
rej.SetField(replace.ClOrdID);
if (replace.IsSetOrigClOrdID())
rej.SetField(replace.OrigClOrdID);
if (replace.IsSetOrderID() && reject_reason != OrderError.UnknownOrder)
rej.SetField(replace.OrderID);
rej.CxlRejResponseTo = new CxlRejResponseTo(CxlRejResponseTo.ORDER_CANCEL_REPLACE_REQUEST);
rej.Text = new Text(text);
_router.send(rej, sess);
}
else
throw new UnsupportedMessageType();
}
protected bool security_types_equal(bts.api.SecurityType btstype, QuickFix.Fields.SecurityType fixtype)
{
return btstype == convert_security_type(fixtype);
}
protected QuickFix.Fields.SecurityType convert_security_type(bts.api.SecurityType btstype)
{
switch (btstype)
{
case bts.api.SecurityType.Future:
return new QuickFix.Fields.SecurityType("FUT");
case bts.api.SecurityType.Option:
return new QuickFix.Fields.SecurityType("OPT");
case bts.api.SecurityType.Spread:
return new QuickFix.Fields.SecurityType("MLEG");
case bts.api.SecurityType.Stock:
return new QuickFix.Fields.SecurityType("CS");
case bts.api.SecurityType.Synthetic:
return new QuickFix.Fields.SecurityType("SYNTH");
default:
return new QuickFix.Fields.SecurityType();
}
}
protected bts.api.SecurityType convert_security_type(QuickFix.Fields.SecurityType fixtype)
{
switch (fixtype.getValue())
{
case "FUT":
return bts.api.SecurityType.Future;
case "OPT":
return bts.api.SecurityType.Option;
case "MLEG":
return bts.api.SecurityType.Spread;
case "CS":
return bts.api.SecurityType.Stock;
case "SYNTH":
return bts.api.SecurityType.Synthetic;
default:
return bts.api.SecurityType.Unset;
}
}
protected bool exchanges_equal(bts.api.Venue btstype, QuickFix.Fields.SecurityExchange fixtype)
{
return btstype == convert_exchange(fixtype);
}
protected QuickFix.Fields.SecurityExchange convert_exchange(bts.api.Venue btstype)
{
switch (btstype)
{
case bts.api.Venue.Bts:
return new QuickFix.Fields.SecurityExchange("BTS");
case bts.api.Venue.Cboe:
return new QuickFix.Fields.SecurityExchange("XCBO");
case bts.api.Venue.Cboecfe:
return new QuickFix.Fields.SecurityExchange("XCBF");
case bts.api.Venue.Glbx:
return new QuickFix.Fields.SecurityExchange("XCME");
case bts.api.Venue.Ice:
return new QuickFix.Fields.SecurityExchange("IEPA");
case bts.api.Venue.Sim:
return new QuickFix.Fields.SecurityExchange("SIM");
default:
return new QuickFix.Fields.SecurityExchange();
}
}
protected bts.api.Venue convert_exchange(QuickFix.Fields.SecurityExchange fixtype)
{
return convert_exchange(fixtype.getValue());
}
protected bts.api.Venue convert_exchange(QuickFix.Fields.ExDestination fixtype)
{
return convert_exchange(fixtype.getValue());
}
protected bts.api.Venue convert_exchange(string venue)
{
switch (venue)
{
case "BTS":
return bts.api.Venue.Bts;
case "XCBO":
return bts.api.Venue.Cboe;
case "XCBF":
return bts.api.Venue.Cboecfe;
case "GLBX":
case "XCME":
case "XCBT":
case "XCEC":
case "XNYM":
return bts.api.Venue.Glbx;
case "IEPA":
case "IFUS":
case "IFEU":
case "IFLL":
return bts.api.Venue.Ice;
case "SIM":
return bts.api.Venue.Sim;
default:
return bts.api.Venue.Unset;
}
}
protected QuickFix.Fields.OrdStatus translate_status(in bts.api.orderman.OrderStateImmutable state,
ref bts.api.orderman.NativeOrderStateImmutable? native)
{
native = null;
if (state.HasMarket && state.Market.HasNativeState && state.Market.NativeState.NativeStatus != NativeOrderStatus.Unacknowledged)
{
native = state.Market.NativeState;
return new OrdStatus((char)native?.NativeStatus);
}
else if (state.HasLimit && state.Limit.HasNativeState && state.Limit.NativeState.NativeStatus != NativeOrderStatus.Unacknowledged)
{
native = state.Limit.NativeState;
return new OrdStatus((char)native?.NativeStatus);
}
else if (state.HasStop && state.Stop.HasNativeState && state.Stop.NativeState.NativeStatus != NativeOrderStatus.Unacknowledged)
{
native = state.Stop.NativeState;
return new OrdStatus((char)native?.NativeStatus);
}
else if (state.HasStopLimit && state.StopLimit.HasNativeState && state.StopLimit.NativeState.NativeStatus != NativeOrderStatus.Unacknowledged)
{
native = state.StopLimit.NativeState;
return new OrdStatus((char)native?.NativeStatus);
}
else
{
switch (state.Status)
{
case OrderStatus.Canceled:
return new OrdStatus(OrdStatus.CANCELED);
case OrderStatus.Completed:
return new OrdStatus(OrdStatus.FILLED);
case OrderStatus.Disconnected:
return new OrdStatus(OrdStatus.STOPPED);
case OrderStatus.Error:
return new OrdStatus(OrdStatus.REJECTED);
case OrderStatus.Inactive:
return new OrdStatus(OrdStatus.PENDING_NEW);
case OrderStatus.PendingWorking:
return new OrdStatus(OrdStatus.PENDING_NEW);
case OrderStatus.RejectedBySpark:
return new OrdStatus(OrdStatus.REJECTED);
case OrderStatus.Working:
{
if (state.FilledQuantity < state.Quantity)
return new OrdStatus(OrdStatus.PARTIALLY_FILLED);
else
return new OrdStatus(OrdStatus.NEW);
}
default:
return new OrdStatus();
}
}
}
protected uint lookup_account(string acct)
{
if (_admin_data.HasValue)
{
var ac = _admin_data.Value.Accounts.Where(x => x.Name == acct).TryFirst();
if(ac.HasValue)
return ac.Value.Id;
}
throw new IncorrectTagValue(new Account().Tag);
}
protected uint lookup_user(string user)
{
if (_admin_data.HasValue)
{
var ac = _admin_data.Value.Users.Where(x => x.Name == user).TryFirst();
if (ac.HasValue)
return ac.Value.Id;
}
throw new IncorrectTagValue(new Username().Tag);
}
}
}
+90
View File
@@ -0,0 +1,90 @@
{
"algoStrategy": [
{
"name": "TickAndCover",
"definition":
{
"cover":
{
"child":
{
"top": { "child": { "limit": {} } }
},
"hedge":
{
"lean": { "child": { "limit": {} } }
}
}
}
},
{
"name": "SmashAndCover",
"definition":
{
"cover":
{
"child":
{
"lean": { "child": { "limit": {} } }
},
"hedge":
{
"lean": { "child": { "limit": {} } }
}
}
}
},
{
"name": "JoinAndCover",
"definition":
{
"cover":
{
"child":
{
"join": { "child": { "limit": {} } }
},
"hedge":
{
"lean": { "child": { "limit": {} } }
}
}
}
},
{
"name": "Prorata",
"definition":
{
"prorata":
{
"child": { "limit": {} },
"hedge":
{
"scratch": { "child": { "limit": {} } }
},
"overfill_hedge":
{
"lean": { "child": { "limit": {} } }
}
}
}
},
{ "name": "Top", "definition": { "top": { "child": { "limit": {} } } } },
{ "name": "Cover",
"definition":
{
"cover":
{
"child" : { "limit": {} },
"hedge" : { "limit": {} }
}
}
},
{ "name": "Lean", "definition": { "lean": { "child": { "limit": {} } } } },
{ "name": "Join", "definition": { "join": { "child": { "limit": {} } } } },
{ "name": "Iceberg", "definition": { "iceberg": { "child": { "limit": {} } } } },
{ "name": "Scratch", "definition": { "scratch": { "child": { "limit": {} } } } },
{ "name": "Spreader", "definition": { "spreader": { } } },
{ "name": "ISpreader", "definition": { "iceberg": { "child": { "spreader": {} } } } }
]
}
+42
View File
@@ -0,0 +1,42 @@
#http://www.quickfixengine.org/quickfix/doc/html/configuration.html
#https://www.onixs.biz/fix-dictionary.html
[DEFAULT]
ConnectionType=acceptor
SocketAcceptPort=5001
SocketReuseAddress=Y
StartTime=00:00:00
EndTime=00:00:00
FileLogPath=log
UseDataDictionary=N
ResetOnLogon=Y
ResetOnLogout=Y
ResetOnDisconnect=Y
HeartBtInt=30
LogonTimeout=30
SSLEnable=N
# SSLEnable=Y
# # It is recommended to install the certificate and refer to it by name instead of using filename + password
# SSLCertificate=QuickFixn-TestServer.pfx
# SSLCertificatePassword=QuickFixn-TestServer
# # For production refer to certificate by name instead: SSLCertificate=CN=QuickFixn-TestServer
# SSLCACertificate=QuickFixn-TestCA.cer
# SSLCheckCertificateRevocation=N
[SESSION]
BeginString=FIX.4.2
SenderCompID=SPARK
TargetCompID=CQG
FileStorePath=store
DataDictionary=./spec/fix/FIX42.xml
[SESSION]
BeginString=FIX.4.2
SenderCompID=SPARK
TargetCompID=CLIENT2
FileStorePath=store
DataDictionary=./spec/fix/FIX42.xml
+46
View File
@@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<Authors>Blue Trading Systems</Authors>
<Company>Blue Trading Systems</Company>
<Product>Spark</Product>
<Description />
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
<LangVersion>7.2</LangVersion>
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="QuickFix.Net.NETCore" Version="1.8.1" />
<PackageReference Include="QuickFix.Net.NETCore.FIX42" Version="1.8.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../spark_api/spark_api.csproj"/>
<ProjectReference Include="../spark_core/spark_core.csproj"/>
</ItemGroup>
<Target Name="copy_fix" AfterTargets="PostBuildEvent" >
<Copy SourceFiles="./spark_fix.cfg" DestinationFolder="$(OutputPath)/" SkipUnchangedFiles="true" />
<Copy SourceFiles="./spark_algos.json" DestinationFolder="$(OutputPath)/" SkipUnchangedFiles="true" />
<Copy SourceFiles="./spec/fix/FIX42.xml" DestinationFolder="$(OutputPath)/spec/fix/" SkipUnchangedFiles="true" />
<Copy SourceFiles="./spec/fix/FIX44.xml" DestinationFolder="$(OutputPath)/spec/fix/" SkipUnchangedFiles="true" />
</Target>
<Target Name="copy_fix471" AfterTargets="PostBuildEvent" >
<Copy SourceFiles="./spark_fix.cfg" DestinationFolder="$(OutputPath)/../net471" SkipUnchangedFiles="true" />
<Copy SourceFiles="./spark_algos.json" DestinationFolder="$(OutputPath)/../net471" SkipUnchangedFiles="true" />
<Copy SourceFiles="./spec/fix/FIX42.xml" DestinationFolder="$(OutputPath)/../net471/spec/fix/" SkipUnchangedFiles="true" />
<Copy SourceFiles="./spec/fix/FIX44.xml" DestinationFolder="$(OutputPath)/../net471/spec/fix/" SkipUnchangedFiles="true" />
</Target>
</Project>
+828
View File
@@ -0,0 +1,828 @@
<fix major="4" minor="0">
<header>
<field name="BeginString" required="Y"/>
<field name="BodyLength" required="Y"/>
<field name="MsgType" required="Y"/>
<field name="SenderCompID" required="Y"/>
<field name="TargetCompID" required="Y"/>
<field name="OnBehalfOfCompID" required="N"/>
<field name="DeliverToCompID" required="N"/>
<field name="SecureDataLen" required="N"/>
<field name="SecureData" required="N"/>
<field name="MsgSeqNum" required="Y"/>
<field name="SenderSubID" required="N"/>
<field name="TargetSubID" required="N"/>
<field name="OnBehalfOfSubID" required="N"/>
<field name="DeliverToSubID" required="N"/>
<field name="PossDupFlag" required="N"/>
<field name="PossResend" required="N"/>
<field name="SendingTime" required="Y"/>
<field name="OrigSendingTime" required="N"/>
</header>
<trailer>
<field name="SignatureLength" required="N"/>
<field name="Signature" required="N"/>
<field name="CheckSum" required="Y"/>
</trailer>
<messages>
<message name="Heartbeat" msgtype="0" msgcat="admin">
<field name="TestReqID" required="N"/>
</message>
<message name="Logon" msgtype="A" msgcat="admin">
<field name="EncryptMethod" required="Y"/>
<field name="HeartBtInt" required="Y"/>
<field name="RawDataLength" required="N"/>
<field name="RawData" required="N"/>
</message>
<message name="TestRequest" msgtype="1" msgcat="admin">
<field name="TestReqID" required="Y"/>
</message>
<message name="ResendRequest" msgtype="2" msgcat="admin">
<field name="BeginSeqNo" required="Y"/>
<field name="EndSeqNo" required="Y"/>
</message>
<message name="Reject" msgtype="3" msgcat="admin">
<field name="RefSeqNum" required="Y"/>
<field name="Text" required="N"/>
</message>
<message name="SequenceReset" msgtype="4" msgcat="admin">
<field name="GapFillFlag" required="N"/>
<field name="NewSeqNo" required="Y"/>
</message>
<message name="Logout" msgtype="5" msgcat="admin">
<field name="Text" required="N"/>
</message>
<message name="Advertisement" msgtype="7" msgcat="app">
<field name="AdvId" required="Y"/>
<field name="AdvTransType" required="Y"/>
<field name="AdvRefID" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="AdvSide" required="Y"/>
<field name="Shares" required="Y"/>
<field name="Price" required="N"/>
<field name="Currency" required="N"/>
<field name="TransactTime" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="IndicationofInterest" msgtype="6" msgcat="app">
<field name="IOIid" required="Y"/>
<field name="IOITransType" required="Y"/>
<field name="IOIRefID" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
<field name="IOIShares" required="Y"/>
<field name="Price" required="N"/>
<field name="Currency" required="N"/>
<field name="ValidUntilTime" required="N"/>
<field name="IOIQltyInd" required="N"/>
<field name="IOIOthSvc" required="N"/>
<field name="IOINaturalFlag" required="N"/>
<field name="IOIQualifier" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="News" msgtype="B" msgcat="app">
<field name="OrigTime" required="N"/>
<field name="Urgency" required="N"/>
<field name="RelatdSym" required="N" repeating="Y"/>
<group name="LinesOfText" required="Y">
<field name="Text" required="Y"/>
</group>
<field name="RawDataLength" required="N"/>
<field name="RawData" required="N"/>
</message>
<message name="Email" msgtype="C" msgcat="app">
<field name="EmailType" required="Y"/>
<field name="OrigTime" required="N"/>
<field name="RelatdSym" required="N" repeating="Y"/>
<field name="OrderID" required="N"/>
<field name="ClOrdID" required="N"/>
<group name="LinesOfText" required="Y">
<field name="Text" required="Y"/>
</group>
<field name="RawDataLength" required="N"/>
<field name="RawData" required="N"/>
</message>
<message name="QuoteRequest" msgtype="R" msgcat="app">
<field name="QuoteReqID" required="Y"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="PrevClosePx" required="N"/>
<field name="Side" required="N"/>
<field name="OrderQty" required="N"/>
</message>
<message name="Quote" msgtype="S" msgcat="app">
<field name="QuoteReqID" required="N"/>
<field name="QuoteID" required="Y"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="BidPx" required="Y"/>
<field name="OfferPx" required="N"/>
<field name="BidSize" required="N"/>
<field name="OfferSize" required="N"/>
<field name="ValidUntilTime" required="N"/>
</message>
<message name="NewOrderSingle" msgtype="D" msgcat="app">
<field name="ClOrdID" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="Account" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="HandlInst" required="Y"/>
<field name="ExecInst" required="N"/>
<field name="MinQty" required="N"/>
<field name="MaxFloor" required="N"/>
<field name="ExDestination" required="N"/>
<field name="ProcessCode" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="PrevClosePx" required="N"/>
<field name="Side" required="Y"/>
<field name="LocateReqd" required="N"/>
<field name="OrderQty" required="Y"/>
<field name="OrdType" required="Y"/>
<field name="Price" required="N"/>
<field name="StopPx" required="N"/>
<field name="Currency" required="N"/>
<field name="IOIid" required="N"/>
<field name="QuoteID" required="N"/>
<field name="TimeInForce" required="N"/>
<field name="ExpireTime" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<field name="Rule80A" required="N"/>
<field name="ForexReq" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="ExecutionReport" msgtype="8" msgcat="app">
<field name="OrderID" required="Y"/>
<field name="ClOrdID" required="N"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="ListID" required="N"/>
<field name="ExecID" required="Y"/>
<field name="ExecTransType" required="Y"/>
<field name="ExecRefID" required="N"/>
<field name="OrdStatus" required="Y"/>
<field name="OrdRejReason" required="N"/>
<field name="Account" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
<field name="OrderQty" required="Y"/>
<field name="OrdType" required="N"/>
<field name="Price" required="N"/>
<field name="StopPx" required="N"/>
<field name="Currency" required="N"/>
<field name="TimeInForce" required="N"/>
<field name="ExpireTime" required="N"/>
<field name="ExecInst" required="N"/>
<field name="Rule80A" required="N"/>
<field name="LastShares" required="Y"/>
<field name="LastPx" required="Y"/>
<field name="LastMkt" required="N"/>
<field name="LastCapacity" required="N"/>
<field name="CumQty" required="Y"/>
<field name="AvgPx" required="Y"/>
<field name="TradeDate" required="N"/>
<field name="TransactTime" required="N"/>
<field name="ReportToExch" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<group name="NoMiscFees" required="N">
<field name="MiscFeeAmt" required="N"/>
<field name="MiscFeeCurr" required="N"/>
<field name="MiscFeeType" required="N"/>
</group>
<field name="NetMoney" required="N"/>
<field name="SettlCurrAmt" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="DontKnowTrade" msgtype="Q" msgcat="app">
<field name="OrderID" required="N"/>
<field name="ExecID" required="N"/>
<field name="DKReason" required="Y"/>
<field name="Symbol" required="Y"/>
<field name="Side" required="Y"/>
<field name="OrderQty" required="Y"/>
<field name="LastShares" required="Y"/>
<field name="LastPx" required="Y"/>
<field name="Text" required="N"/>
</message>
<message name="OrderCancelReplaceRequest" msgtype="G" msgcat="app">
<field name="OrderID" required="N"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="OrigClOrdID" required="Y"/>
<field name="ClOrdID" required="Y"/>
<field name="ListID" required="N"/>
<field name="Account" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="HandlInst" required="Y"/>
<field name="ExecInst" required="N"/>
<field name="MinQty" required="N"/>
<field name="MaxFloor" required="N"/>
<field name="ExDestination" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
<field name="OrderQty" required="Y"/>
<field name="OrdType" required="Y"/>
<field name="Price" required="N"/>
<field name="StopPx" required="N"/>
<field name="Currency" required="N"/>
<field name="TimeInForce" required="N"/>
<field name="ExpireTime" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<field name="Rule80A" required="N"/>
<field name="ForexReq" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="OrderCancelRequest" msgtype="F" msgcat="app">
<field name="OrigClOrdID" required="Y"/>
<field name="OrderID" required="N"/>
<field name="ClOrdID" required="Y"/>
<field name="ListID" required="N"/>
<field name="CxlType" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
<field name="OrderQty" required="Y"/>
<field name="Text" required="N"/>
</message>
<message name="OrderCancelReject" msgtype="9" msgcat="app">
<field name="OrderID" required="Y"/>
<field name="ClOrdID" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="ListID" required="N"/>
<field name="CxlRejReason" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="OrderStatusRequest" msgtype="H" msgcat="app">
<field name="OrderID" required="N"/>
<field name="ClOrdID" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Side" required="Y"/>
</message>
<message name="Allocation" msgtype="J" msgcat="app">
<field name="AllocID" required="Y"/>
<field name="AllocTransType" required="Y"/>
<field name="RefAllocID" required="N"/>
<group name="NoOrders" required="Y">
<field name="ClOrdID" required="Y"/>
<field name="OrderID" required="N"/>
<field name="ListID" required="N"/>
<field name="WaveNo" required="N"/>
</group>
<group name="NoExecs" required="N">
<field name="ExecID" required="N"/>
<field name="LastShares" required="N"/>
<field name="LastPx" required="N"/>
<field name="LastMkt" required="N"/>
</group>
<field name="Side" required="Y"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="Shares" required="Y"/>
<field name="AvgPx" required="Y"/>
<field name="Currency" required="N"/>
<field name="AvgPrxPrecision" required="N"/>
<field name="TradeDate" required="Y"/>
<field name="TransactTime" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="NetMoney" required="N"/>
<group name="NoMiscFees" required="N">
<field name="MiscFeeAmt" required="N"/>
<field name="MiscFeeCurr" required="N"/>
<field name="MiscFeeType" required="N"/>
</group>
<field name="SettlCurrAmt" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="OpenClose" required="N"/>
<field name="Text" required="N"/>
<group name="NoAllocs" required="Y">
<field name="AllocAccount" required="Y"/>
<field name="AllocShares" required="Y"/>
<field name="ProcessCode" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="ClientID" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<group name="NoDlvyInst" required="N">
<field name="BrokerOfCredit" required="N"/>
<field name="DlvyInst" required="N"/>
</group>
</group>
</message>
<message name="AllocationACK" msgtype="P" msgcat="app">
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="AllocID" required="Y"/>
<field name="TradeDate" required="Y"/>
<field name="TransactTime" required="N"/>
<field name="AllocStatus" required="Y"/>
<field name="AllocRejCode" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="NewOrderList" msgtype="E" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="ListSeqNo" required="Y"/>
<field name="ListNoOrds" required="Y"/>
<field name="ListExecInst" required="N"/>
<field name="ClOrdID" required="Y"/>
<field name="ClientID" required="N"/>
<field name="ExecBroker" required="N"/>
<field name="Account" required="N"/>
<field name="SettlmntTyp" required="N"/>
<field name="FutSettDate" required="N"/>
<field name="HandlInst" required="Y"/>
<field name="ExecInst" required="N"/>
<field name="MinQty" required="N"/>
<field name="MaxFloor" required="N"/>
<field name="ExDestination" required="N"/>
<field name="ProcessCode" required="N"/>
<field name="Symbol" required="Y"/>
<field name="SymbolSfx" required="N"/>
<field name="SecurityID" required="N"/>
<field name="IDSource" required="N"/>
<field name="Issuer" required="N"/>
<field name="SecurityDesc" required="N"/>
<field name="PrevClosePx" required="N"/>
<field name="Side" required="Y"/>
<field name="LocateReqd" required="N"/>
<field name="OrderQty" required="Y"/>
<field name="OrdType" required="Y"/>
<field name="Price" required="N"/>
<field name="StopPx" required="N"/>
<field name="Currency" required="N"/>
<field name="TimeInForce" required="N"/>
<field name="ExpireTime" required="N"/>
<field name="Commission" required="N"/>
<field name="CommType" required="N"/>
<field name="Rule80A" required="N"/>
<field name="ForexReq" required="N"/>
<field name="SettlCurrency" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="ListStatus" msgtype="N" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="NoRpts" required="Y"/>
<field name="RptSeq" required="Y"/>
<group name="NoOrders" required="Y">
<field name="ClOrdID" required="Y"/>
<field name="CumQty" required="Y"/>
<field name="CxlQty" required="Y"/>
<field name="AvgPx" required="Y"/>
</group>
</message>
<message name="ListExecute" msgtype="L" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="ListCancelRequest" msgtype="K" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="Text" required="N"/>
</message>
<message name="ListStatusRequest" msgtype="M" msgcat="app">
<field name="ListID" required="Y"/>
<field name="WaveNo" required="N"/>
<field name="Text" required="N"/>
</message>
</messages>
<fields>
<field number="1" name="Account" type="STRING"/>
<field number="2" name="AdvId" type="INT"/>
<field number="3" name="AdvRefID" type="INT"/>
<field number="4" name="AdvSide" type="STRING">
<value enum="B" description="BUY"/>
<value enum="S" description="SELL"/>
<value enum="X" description="CROSS"/>
<value enum="T" description="TRADE"/>
</field>
<field number="5" name="AdvTransType" type="STRING">
<value enum="N" description="NEW"/>
<value enum="C" description="CANCEL"/>
<value enum="R" description="REPLACE"/>
</field>
<field number="6" name="AvgPx" type="FLOAT"/>
<field number="7" name="BeginSeqNo" type="INT"/>
<field number="8" name="BeginString" type="STRING"/>
<field number="9" name="BodyLength" type="INT"/>
<field number="10" name="CheckSum" type="STRING"/>
<field number="11" name="ClOrdID" type="STRING"/>
<field number="12" name="Commission" type="FLOAT"/>
<field number="13" name="CommType" type="STRING">
<value enum="1" description="PER_SHARE"/>
<value enum="2" description="PERCENTAGE"/>
<value enum="3" description="ABSOLUTE"/>
</field>
<field number="14" name="CumQty" type="INT"/>
<field number="15" name="Currency" type="STRING"/>
<field number="16" name="EndSeqNo" type="INT"/>
<field number="17" name="ExecID" type="INT"/>
<field number="18" name="ExecInst" type="MULTIPLEVALUESTRING">
<value enum="1" description="NOT_HELD"/>
<value enum="2" description="WORK"/>
<value enum="3" description="GO_ALONG"/>
<value enum="4" description="OVER_THE_DAY"/>
<value enum="5" description="HELD"/>
<value enum="6" description="PARTICIPATE_DONT_INITIATE"/>
<value enum="7" description="STRICT_SCALE"/>
<value enum="8" description="TRY_TO_SCALE"/>
<value enum="9" description="STAY_ON_BIDSIDE"/>
<value enum="0" description="STAY_ON_OFFERSIDE"/>
<value enum="A" description="NO_CROSS"/>
<value enum="B" description="OK_TO_CROSS"/>
<value enum="C" description="CALL_FIRST"/>
<value enum="D" description="PERCENT_OF_VOLUME"/>
<value enum="E" description="DO_NOT_INCREASE_DNI"/>
<value enum="F" description="DO_NOT_REDUCE_DNR"/>
<value enum="G" description="ALL_OR_NONE_AON"/>
<value enum="I" description="INSTITUTIONS_ONLY"/>
<value enum="L" description="LAST_PEG"/>
<value enum="M" description="MIDPRICE_PEG"/>
<value enum="N" description="NONNEGOTIABLE"/>
<value enum="O" description="OPENING_PEG"/>
<value enum="P" description="MARKET_PEG"/>
<value enum="R" description="PRIMARY_PEG"/>
<value enum="S" description="SUSPEND"/>
</field>
<field number="19" name="ExecRefID" type="INT"/>
<field number="20" name="ExecTransType" type="STRING">
<value enum="0" description="NEW"/>
<value enum="1" description="CANCEL"/>
<value enum="2" description="CORRECT"/>
<value enum="3" description="STATUS"/>
</field>
<field number="21" name="HandlInst" type="STRING">
<value enum="1" description="AUTOMATED_EXECUTION_ORDER_PRIVATE_NO_BROKER_INTERVENTION"/>
<value enum="2" description="AUTOMATED_EXECUTION_ORDER_PUBLIC_BROKER_INTERVENTION_OK"/>
<value enum="3" description="MANUAL_ORDER_BEST_EXECUTION"/>
</field>
<field number="22" name="IDSource" type="STRING">
<value enum="1" description="CUSIP"/>
<value enum="2" description="SEDOL"/>
<value enum="3" description="QUIK"/>
<value enum="4" description="ISIN_NUMBER"/>
<value enum="5" description="RIC_CODE"/>
</field>
<field number="23" name="IOIid" type="INT"/>
<field number="24" name="IOIOthSvc" type="STRING"/>
<field number="25" name="IOIQltyInd" type="STRING">
<value enum="L" description="LOW"/>
<value enum="M" description="MEDIUM"/>
<value enum="H" description="HIGH"/>
</field>
<field number="26" name="IOIRefID" type="INT"/>
<field number="27" name="IOIShares" type="STRING"/>
<field number="28" name="IOITransType" type="STRING">
<value enum="N" description="NEW"/>
<value enum="C" description="CANCEL"/>
<value enum="R" description="REPLACE"/>
</field>
<field number="29" name="LastCapacity" type="STRING">
<value enum="1" description="AGENT"/>
<value enum="2" description="CROSS_AS_AGENT"/>
<value enum="3" description="CROSS_AS_PRINCIPAL"/>
<value enum="4" description="PRINCIPAL"/>
</field>
<field number="30" name="LastMkt" type="STRING"/>
<field number="31" name="LastPx" type="FLOAT"/>
<field number="32" name="LastShares" type="INT"/>
<field number="33" name="LinesOfText" type="INT"/>
<field number="34" name="MsgSeqNum" type="INT"/>
<field number="35" name="MsgType" type="STRING"/>
<field number="36" name="NewSeqNo" type="INT"/>
<field number="37" name="OrderID" type="STRING"/>
<field number="38" name="OrderQty" type="INT"/>
<field number="39" name="OrdStatus" type="STRING">
<value enum="0" description="NEW"/>
<value enum="1" description="PARTIALLY_FILLED"/>
<value enum="2" description="FILLED"/>
<value enum="3" description="DONE_FOR_DAY"/>
<value enum="4" description="CANCELED"/>
<value enum="5" description="REPLACED"/>
<value enum="6" description="PENDING_CANCELREPLACE"/>
<value enum="7" description="STOPPED"/>
<value enum="8" description="REJECTED"/>
<value enum="9" description="SUSPENDED"/>
<value enum="A" description="PENDING_NEW"/>
<value enum="B" description="CALCULATED"/>
<value enum="C" description="EXPIRED"/>
</field>
<field number="40" name="OrdType" type="STRING">
<value enum="1" description="MARKET"/>
<value enum="2" description="LIMIT"/>
<value enum="3" description="STOP"/>
<value enum="4" description="STOP_LIMIT"/>
<value enum="5" description="MARKET_ON_CLOSE"/>
<value enum="6" description="WITH_OR_WITHOUT"/>
<value enum="7" description="LIMIT_OR_BETTER"/>
<value enum="8" description="LIMIT_WITH_OR_WITHOUT"/>
<value enum="9" description="ON_BASIS"/>
<value enum="A" description="ON_CLOSE"/>
<value enum="B" description="LIMIT_ON_CLOSE"/>
<value enum="C" description="FOREX"/>
<value enum="D" description="PREVIOUSLY_QUOTED"/>
<value enum="E" description="PREVIOUSLY_INDICATED"/>
<value enum="P" description="PEGGED"/>
</field>
<field number="41" name="OrigClOrdID" type="STRING"/>
<field number="42" name="OrigTime" type="TIME"/>
<field number="43" name="PossDupFlag" type="STRING">
<value enum="Y" description="POSSIBLE_DUPLICATE"/>
<value enum="N" description="ORIGINAL_TRANSMISSION"/>
</field>
<field number="44" name="Price" type="FLOAT"/>
<field number="45" name="RefSeqNum" type="INT"/>
<field number="46" name="RelatdSym" type="STRING"/>
<field number="47" name="Rule80A" type="STRING">
<value enum="A" description="AGENCY_SINGLE_ORDER"/>
<value enum="D" description="PROGRAM_ORDER_INDEX_ARB_FOR_MEMBER_FIRMORG"/>
<value enum="C" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_MEMBER_FIRMORG"/>
<value enum="J" description="PROGRAM_ORDER_INDEX_ARB_FOR_INDIVIDUAL_CUSTOMER"/>
<value enum="K" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_INDIVIDUAL_CUSTOMER"/>
<value enum="U" description="PROGRAM_ORDER_INDEX_ARB_FOR_OTHER_AGENCY"/>
<value enum="Y" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_OTHER_AGENCY"/>
<value enum="M" description="PROGRAM_ORDER_INDEX_ARB_FOR_OTHER_MEMBER"/>
<value enum="N" description="PROGRAM_ORDER_NONINDEX_ARB_FOR_OTHER_MEMBER"/>
<value enum="W" description="ALL_OTHER_ORDERS_AS_AGENT_FOR_OTHER_MEMBER"/>
</field>
<field number="48" name="SecurityID" type="STRING"/>
<field number="49" name="SenderCompID" type="STRING"/>
<field number="50" name="SenderSubID" type="STRING"/>
<field number="52" name="SendingTime" type="TIME"/>
<field number="53" name="Shares" type="INT"/>
<field number="54" name="Side" type="STRING">
<value enum="1" description="BUY"/>
<value enum="2" description="SELL"/>
<value enum="3" description="BUY_MINUS"/>
<value enum="4" description="SELL_PLUS"/>
<value enum="5" description="SELL_SHORT"/>
<value enum="6" description="SELL_SHORT_EXEMPT"/>
</field>
<field number="55" name="Symbol" type="STRING"/>
<field number="56" name="TargetCompID" type="STRING"/>
<field number="57" name="TargetSubID" type="STRING"/>
<field number="58" name="Text" type="STRING"/>
<field number="59" name="TimeInForce" type="STRING">
<value enum="0" description="DAY"/>
<value enum="1" description="GOOD_TILL_CANCEL"/>
<value enum="2" description="AT_THE_OPENING"/>
<value enum="3" description="IMMEDIATE_OR_CANCEL"/>
<value enum="4" description="FILL_OR_KILL"/>
<value enum="5" description="GOOD_TILL_CROSSING"/>
<value enum="6" description="GOOD_TILL_DATE"/>
</field>
<field number="60" name="TransactTime" type="TIME"/>
<field number="61" name="Urgency" type="STRING">
<value enum="0" description="NORMAL"/>
<value enum="1" description="FLASH"/>
<value enum="2" description="BACKGROUND"/>
</field>
<field number="62" name="ValidUntilTime" type="TIME"/>
<field number="63" name="SettlmntTyp" type="STRING">
<value enum="0" description="REGULAR"/>
<value enum="1" description="CASH"/>
<value enum="2" description="NEXT_DAY"/>
<value enum="3" description="TPLUS2"/>
<value enum="4" description="TPLUS3"/>
<value enum="5" description="TPLUS4"/>
<value enum="6" description="FUTURE"/>
<value enum="7" description="WHEN_ISSUED"/>
<value enum="8" description="SELLERS_OPTION"/>
<value enum="9" description="TPLUS5"/>
</field>
<field number="64" name="FutSettDate" type="DATE"/>
<field number="65" name="SymbolSfx" type="STRING"/>
<field number="66" name="ListID" type="STRING"/>
<field number="67" name="ListSeqNo" type="INT"/>
<field number="68" name="ListNoOrds" type="INT"/>
<field number="69" name="ListExecInst" type="STRING"/>
<field number="70" name="AllocID" type="INT"/>
<field number="71" name="AllocTransType" type="STRING">
<value enum="0" description="NEW"/>
<value enum="1" description="REPLACE"/>
<value enum="2" description="CANCEL"/>
</field>
<field number="72" name="RefAllocID" type="INT"/>
<field number="73" name="NoOrders" type="INT"/>
<field number="74" name="AvgPrxPrecision" type="INT"/>
<field number="75" name="TradeDate" type="DATE"/>
<field number="76" name="ExecBroker" type="STRING"/>
<field number="77" name="OpenClose" type="STRING"/>
<field number="78" name="NoAllocs" type="INT"/>
<field number="79" name="AllocAccount" type="STRING"/>
<field number="80" name="AllocShares" type="INT"/>
<field number="81" name="ProcessCode" type="STRING">
<value enum="0" description="REGULAR"/>
<value enum="1" description="SOFT_DOLLAR"/>
<value enum="2" description="STEPIN"/>
<value enum="3" description="STEPOUT"/>
<value enum="4" description="SOFTDOLLAR_STEPIN"/>
<value enum="5" description="SOFTDOLLAR_STEPOUT"/>
<value enum="6" description="PLAN_SPONSOR"/>
</field>
<field number="82" name="NoRpts" type="INT"/>
<field number="83" name="RptSeq" type="INT"/>
<field number="84" name="CxlQty" type="INT"/>
<field number="85" name="NoDlvyInst" type="INT"/>
<field number="86" name="DlvyInst" type="STRING"/>
<field number="87" name="AllocStatus" type="INT">
<value enum="0" description="ACCEPTED"/>
<value enum="1" description="REJECTED"/>
<value enum="2" description="PARTIAL_ACCEPT"/>
<value enum="3" description="RECEIVED"/>
</field>
<field number="88" name="AllocRejCode" type="INT">
<value enum="0" description="UNKNOWN_ACCOUNT"/>
<value enum="1" description="INCORRECT_QUANTITY"/>
<value enum="2" description="INCORRECT_AVERAGE_PRICE"/>
<value enum="3" description="UNKNOWN_EXECUTING_BROKER_MNEMONIC"/>
<value enum="4" description="COMMISSION_DIFFERENCE"/>
<value enum="5" description="UNKNOWN_ORDERID"/>
<value enum="6" description="UNKNOWN_LISTID"/>
<value enum="7" description="OTHER"/>
</field>
<field number="89" name="Signature" type="DATA"/>
<field number="90" name="SecureDataLen" type="INT"/>
<field number="91" name="SecureData" type="DATA"/>
<field number="92" name="BrokerOfCredit" type="STRING"/>
<field number="93" name="SignatureLength" type="INT"/>
<field number="94" name="EmailType" type="STRING">
<value enum="0" description="NEW"/>
<value enum="1" description="REPLY"/>
<value enum="2" description="ADMIN_REPLY"/>
</field>
<field number="95" name="RawDataLength" type="INT"/>
<field number="96" name="RawData" type="DATA"/>
<field number="97" name="PossResend" type="STRING"/>
<field number="98" name="EncryptMethod" type="INT">
<value enum="0" description="NONE_OTHER"/>
<value enum="1" description="PKCS"/>
<value enum="2" description="DES"/>
<value enum="3" description="PKCSDES"/>
<value enum="4" description="PGPDES"/>
<value enum="5" description="PGPDESMD5"/>
<value enum="6" description="PEMDESMD5"/>
</field>
<field number="99" name="StopPx" type="FLOAT"/>
<field number="100" name="ExDestination" type="STRING">
<value enum="0" description="NONE"/>
<value enum="4" description="POSIT"/>
</field>
<field number="102" name="CxlRejReason" type="INT">
<value enum="0" description="TOO_LATE_TO_CANCEL"/>
<value enum="1" description="UNKNOWN_ORDER"/>
</field>
<field number="103" name="OrdRejReason" type="INT">
<value enum="0" description="BROKER_OPTION"/>
<value enum="1" description="UNKNOWN_SYMBOL"/>
<value enum="2" description="EXCHANGE_CLOSED"/>
<value enum="3" description="ORDER_EXCEEDS_LIMIT"/>
<value enum="4" description="TOO_LATE_TO_ENTER"/>
</field>
<field number="104" name="IOIQualifier" type="STRING">
<value enum="X" description="CROSSING_OPPORTUNITY"/>
<value enum="O" description="AT_THE_OPEN"/>
<value enum="M" description="MORE_BEHIND"/>
<value enum="P" description="TAKING_A_POSITION"/>
<value enum="V" description="VERSUS"/>
<value enum="Q" description="CURRENT_QUOTE"/>
<value enum="C" description="AT_THE_CLOSE"/>
<value enum="S" description="PORTFOLIO_SHOWN"/>
<value enum="I" description="IN_TOUCH_WITH"/>
<value enum="W" description="INDICATION_WORKING_AWAY"/>
<value enum="A" description="ALL_OR_NONE"/>
<value enum="L" description="LIMIT"/>
<value enum="T" description="THROUGH_THE_DAY"/>
</field>
<field number="105" name="WaveNo" type="STRING"/>
<field number="106" name="Issuer" type="STRING"/>
<field number="107" name="SecurityDesc" type="STRING"/>
<field number="108" name="HeartBtInt" type="INT"/>
<field number="109" name="ClientID" type="STRING"/>
<field number="110" name="MinQty" type="INT"/>
<field number="111" name="MaxFloor" type="INT"/>
<field number="112" name="TestReqID" type="STRING"/>
<field number="113" name="ReportToExch" type="STRING">
<value enum="Y" description="YES"/>
<value enum="N" description="NO"/>
</field>
<field number="114" name="LocateReqd" type="STRING">
<value enum="Y" description="YES"/>
<value enum="N" description="NO"/>
</field>
<field number="115" name="OnBehalfOfCompID" type="STRING"/>
<field number="116" name="OnBehalfOfSubID" type="STRING"/>
<field number="117" name="QuoteID" type="STRING"/>
<field number="118" name="NetMoney" type="FLOAT"/>
<field number="119" name="SettlCurrAmt" type="FLOAT"/>
<field number="120" name="SettlCurrency" type="STRING"/>
<field number="121" name="ForexReq" type="STRING">
<value enum="Y" description="YES"/>
<value enum="N" description="NO"/>
</field>
<field number="122" name="OrigSendingTime" type="TIME"/>
<field number="123" name="GapFillFlag" type="STRING">
<value enum="Y" description="GAP_FILL_MESSAGE_MSGSEQNUM_FIELD_VALID"/>
<value enum="N" description="SEQUENCE_RESET_IGNORE_MSGSEQNUM"/>
</field>
<field number="124" name="NoExecs" type="INT"/>
<field number="125" name="CxlType" type="STRING">
<value enum="P" description="PARTIAL_CANCEL"/>
<value enum="F" description="FULL_REMAINING_QUANTITY"/>
</field>
<field number="126" name="ExpireTime" type="TIME"/>
<field number="127" name="DKReason" type="STRING">
<value enum="A" description="UNKNOWN_SYMBOL"/>
<value enum="B" description="WRONG_SIDE"/>
<value enum="C" description="QUANTITY_EXCEEDS_ORDER"/>
<value enum="D" description="NO_MATCHING_ORDER"/>
<value enum="E" description="PRICE_EXCEEDS_LIMIT"/>
<value enum="Z" description="OTHER" />
</field>
<field number="128" name="DeliverToCompID" type="STRING"/>
<field number="129" name="DeliverToSubID" type="STRING"/>
<field number="130" name="IOINaturalFlag" type="STRING">
<value enum="Y" description="NATURAL"/>
<value enum="N" description="NOT_NATURAL"/>
</field>
<field number="131" name="QuoteReqID" type="STRING"/>
<field number="132" name="BidPx" type="FLOAT"/>
<field number="133" name="OfferPx" type="FLOAT"/>
<field number="134" name="BidSize" type="INT"/>
<field number="135" name="OfferSize" type="INT"/>
<field number="136" name="NoMiscFees" type="INT"/>
<field number="137" name="MiscFeeAmt" type="FLOAT"/>
<field number="138" name="MiscFeeCurr" type="STRING"/>
<field number="139" name="MiscFeeType" type="STRING">
<value enum="1" description="REGULATORY"/>
<value enum="2" description="TAX"/>
<value enum="3" description="LOCAL_COMMISSION"/>
<value enum="4" description="EXCHANGE_FEES"/>
<value enum="5" description="STAMP"/>
<value enum="6" description="LEVY"/>
<value enum="7" description="OTHER"/>
</field>
<field number="140" name="PrevClosePx" type="FLOAT"/>
</fields>
</fix>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+313
View File
@@ -0,0 +1,313 @@
<?xml version='1.0' encoding='UTF-8'?>
<fix type='FIXT' major='1' minor='1' servicepack='0'>
<header>
<field name='BeginString' required='Y'/>
<field name='BodyLength' required='Y'/>
<field name='MsgType' required='Y'/>
<field name='SenderCompID' required='Y'/>
<field name='TargetCompID' required='Y'/>
<field name='OnBehalfOfCompID' required='N'/>
<field name='DeliverToCompID' required='N'/>
<field name='SecureDataLen' required='N'/>
<field name='SecureData' required='N'/>
<field name='MsgSeqNum' required='Y'/>
<field name='SenderSubID' required='N'/>
<field name='SenderLocationID' required='N'/>
<field name='TargetSubID' required='N'/>
<field name='TargetLocationID' required='N'/>
<field name='OnBehalfOfSubID' required='N'/>
<field name='OnBehalfOfLocationID' required='N'/>
<field name='DeliverToSubID' required='N'/>
<field name='DeliverToLocationID' required='N'/>
<field name='PossDupFlag' required='N'/>
<field name='PossResend' required='N'/>
<field name='SendingTime' required='Y'/>
<field name='OrigSendingTime' required='N'/>
<field name='XmlDataLen' required='N'/>
<field name='XmlData' required='N'/>
<field name='MessageEncoding' required='N'/>
<field name='LastMsgSeqNumProcessed' required='N'/>
<component name='HopGrp' required='N'/>
<field name='ApplVerID' required='N'/>
<field name='CstmApplVerID' required='N'/>
</header>
<trailer>
<field name='SignatureLength' required='N'/>
<field name='Signature' required='N'/>
<field name='CheckSum' required='Y'/>
</trailer>
<messages>
<message msgcat='admin' msgtype='0' name='Heartbeat'>
<field name='TestReqID' required='N'/>
</message>
<message msgcat='admin' msgtype='1' name='TestRequest'>
<field name='TestReqID' required='Y'/>
</message>
<message msgcat='admin' msgtype='2' name='ResendRequest'>
<field name='BeginSeqNo' required='Y'/>
<field name='EndSeqNo' required='Y'/>
</message>
<message msgcat='admin' msgtype='3' name='Reject'>
<field name='RefSeqNum' required='Y'/>
<field name='RefTagID' required='N'/>
<field name='RefMsgType' required='N'/>
<field name='SessionRejectReason' required='N'/>
<field name='Text' required='N'/>
<field name='EncodedTextLen' required='N'/>
<field name='EncodedText' required='N'/>
</message>
<message msgcat='admin' msgtype='4' name='SequenceReset'>
<field name='GapFillFlag' required='N'/>
<field name='NewSeqNo' required='Y'/>
</message>
<message msgcat='admin' msgtype='5' name='Logout'>
<field name='Text' required='N'/>
<field name='EncodedTextLen' required='N'/>
<field name='EncodedText' required='N'/>
</message>
<message msgcat='admin' msgtype='A' name='Logon'>
<field name='EncryptMethod' required='Y'/>
<field name='HeartBtInt' required='Y'/>
<field name='RawDataLength' required='N'/>
<field name='RawData' required='N'/>
<field name='ResetSeqNumFlag' required='N'/>
<field name='NextExpectedMsgSeqNum' required='N'/>
<field name='MaxMessageSize' required='N'/>
<field name='TestMessageIndicator' required='N'/>
<field name='Username' required='N'/>
<field name='Password' required='N'/>
<field name='DefaultApplVerID' required='Y'/>
<component name='MsgTypeGrp' required='N'/>
</message>
</messages>
<components>
<component name='HopGrp'>
<group name='NoHops' required='N'>
<field name='HopCompID' required='N'/>
<field name='HopSendingTime' required='N'/>
<field name='HopRefID' required='N'/>
</group>
</component>
<component name='MsgTypeGrp'>
<group name='NoMsgTypes' required='N'>
<field name='RefMsgType' required='N'/>
<field name='MsgDirection' required='N'/>
<field name='RefApplVerID' required='N'/>
<field name='RefCstmApplVerID' required='N'/>
</group>
</component>
</components>
<fields>
<field name='BeginSeqNo' number='7' type='SEQNUM'/>
<field name='BeginString' number='8' type='STRING'/>
<field name='BodyLength' number='9' type='LENGTH'/>
<field name='CheckSum' number='10' type='STRING'/>
<field name='EndSeqNo' number='16' type='SEQNUM'/>
<field name='MsgSeqNum' number='34' type='SEQNUM'/>
<field number='35' name='MsgType' type='STRING'>
<value enum='0' description='HEARTBEAT'/>
<value enum='1' description='TEST_REQUEST'/>
<value enum='2' description='RESEND_REQUEST'/>
<value enum='3' description='REJECT'/>
<value enum='4' description='SEQUENCE_RESET'/>
<value enum='5' description='LOGOUT'/>
<value enum='6' description='INDICATION_OF_INTEREST'/>
<value enum='7' description='ADVERTISEMENT'/>
<value enum='8' description='EXECUTION_REPORT'/>
<value enum='9' description='ORDER_CANCEL_REJECT'/>
<value enum='A' description='LOGON'/>
<value enum='B' description='NEWS'/>
<value enum='C' description='EMAIL'/>
<value enum='D' description='ORDER_SINGLE'/>
<value enum='E' description='ORDER_LIST'/>
<value enum='F' description='ORDER_CANCEL_REQUEST'/>
<value enum='G' description='ORDER_CANCEL_REPLACE_REQUEST'/>
<value enum='H' description='ORDER_STATUS_REQUEST'/>
<value enum='J' description='ALLOCATION_INSTRUCTION'/>
<value enum='K' description='LIST_CANCEL_REQUEST'/>
<value enum='L' description='LIST_EXECUTE'/>
<value enum='M' description='LIST_STATUS_REQUEST'/>
<value enum='N' description='LIST_STATUS'/>
<value enum='P' description='ALLOCATION_INSTRUCTION_ACK'/>
<value enum='Q' description='DONT_KNOW_TRADE'/>
<value enum='R' description='QUOTE_REQUEST'/>
<value enum='S' description='QUOTE'/>
<value enum='T' description='SETTLEMENT_INSTRUCTIONS'/>
<value enum='V' description='MARKET_DATA_REQUEST'/>
<value enum='W' description='MARKET_DATA_SNAPSHOT_FULL_REFRESH'/>
<value enum='X' description='MARKET_DATA_INCREMENTAL_REFRESH'/>
<value enum='Y' description='MARKET_DATA_REQUEST_REJECT'/>
<value enum='Z' description='QUOTE_CANCEL'/>
<value enum='a' description='QUOTE_STATUS_REQUEST'/>
<value enum='b' description='MASS_QUOTE_ACKNOWLEDGEMENT'/>
<value enum='c' description='SECURITY_DEFINITION_REQUEST'/>
<value enum='d' description='SECURITY_DEFINITION'/>
<value enum='e' description='SECURITY_STATUS_REQUEST'/>
<value enum='f' description='SECURITY_STATUS'/>
<value enum='g' description='TRADING_SESSION_STATUS_REQUEST'/>
<value enum='h' description='TRADING_SESSION_STATUS'/>
<value enum='i' description='MASS_QUOTE'/>
<value enum='j' description='BUSINESS_MESSAGE_REJECT'/>
<value enum='k' description='BID_REQUEST'/>
<value enum='l' description='BID_RESPONSE'/>
<value enum='m' description='LIST_STRIKE_PRICE'/>
<value enum='n' description='XML_MESSAGE'/>
<value enum='o' description='REGISTRATION_INSTRUCTIONS'/>
<value enum='p' description='REGISTRATION_INSTRUCTIONS_RESPONSE'/>
<value enum='q' description='ORDER_MASS_CANCEL_REQUEST'/>
<value enum='r' description='ORDER_MASS_CANCEL_REPORT'/>
<value enum='s' description='NEW_ORDER_CROSS'/>
<value enum='t' description='CROSS_ORDER_CANCEL_REPLACE_REQUEST'/>
<value enum='u' description='CROSS_ORDER_CANCEL_REQUEST'/>
<value enum='v' description='SECURITY_TYPE_REQUEST'/>
<value enum='w' description='SECURITY_TYPES'/>
<value enum='x' description='SECURITY_LIST_REQUEST'/>
<value enum='y' description='SECURITY_LIST'/>
<value enum='z' description='DERIVATIVE_SECURITY_LIST_REQUEST'/>
<value enum='AA' description='DERIVATIVE_SECURITY_LIST'/>
<value enum='AB' description='NEW_ORDER_MULTILEG'/>
<value enum='AC' description='MULTILEG_ORDER_CANCEL_REPLACE'/>
<value enum='AD' description='TRADE_CAPTURE_REPORT_REQUEST'/>
<value enum='AE' description='TRADE_CAPTURE_REPORT'/>
<value enum='AF' description='ORDER_MASS_STATUS_REQUEST'/>
<value enum='AG' description='QUOTE_REQUEST_REJECT'/>
<value enum='AH' description='RFQ_REQUEST'/>
<value enum='AI' description='QUOTE_STATUS_REPORT'/>
<value enum='AJ' description='QUOTE_RESPONSE'/>
<value enum='AK' description='CONFIRMATION'/>
<value enum='AL' description='POSITION_MAINTENANCE_REQUEST'/>
<value enum='AM' description='POSITION_MAINTENANCE_REPORT'/>
<value enum='AN' description='REQUEST_FOR_POSITIONS'/>
<value enum='AO' description='REQUEST_FOR_POSITIONS_ACK'/>
<value enum='AP' description='POSITION_REPORT'/>
<value enum='AQ' description='TRADE_CAPTURE_REPORT_REQUEST_ACK'/>
<value enum='AR' description='TRADE_CAPTURE_REPORT_ACK'/>
<value enum='AS' description='ALLOCATION_REPORT'/>
<value enum='AT' description='ALLOCATION_REPORT_ACK'/>
<value enum='AU' description='CONFIRMATION_ACK'/>
<value enum='AV' description='SETTLEMENT_INSTRUCTION_REQUEST'/>
<value enum='AW' description='ASSIGNMENT_REPORT'/>
<value enum='AX' description='COLLATERAL_REQUEST'/>
<value enum='AY' description='COLLATERAL_ASSIGNMENT'/>
<value enum='AZ' description='COLLATERAL_RESPONSE'/>
<value enum='BA' description='COLLATERAL_REPORT'/>
<value enum='BB' description='COLLATERAL_INQUIRY'/>
<value enum='BC' description='NETWORK_STATUS_REQUEST'/>
<value enum='BD' description='NETWORK_STATUS_RESPONSE'/>
<value enum='BE' description='USER_REQUEST'/>
<value enum='BF' description='USER_RESPONSE'/>
<value enum='BG' description='COLLATERAL_INQUIRY_ACK'/>
<value enum='BH' description='CONFIRMATION_REQUEST'/>
<value enum='BI' description='TRADING_SESSION_LIST_REQUEST'/>
<value enum='BJ' description='TRADING_SESSION_LIST'/>
<value enum='BK' description='SECURITY_LIST_UPDATE_REPORT'/>
<value enum='BL' description='ADJUSTED_POSITION_REPORT'/>
<value enum='BM' description='ALLOCATION_INSTRUCTION_ALERT'/>
<value enum='BN' description='EXECUTION_ACKNOWLEDGEMENT'/>
<value enum='BO' description='CONTRARY_INTENTION_REPORT'/>
<value enum='BP' description='SECURITY_DEFINITION_UPDATE_REPORT'/>
</field>
<field name='NewSeqNo' number='36' type='SEQNUM'/>
<field name='PossDupFlag' number='43' type='BOOLEAN'/>
<field name='RefSeqNum' number='45' type='SEQNUM'/>
<field name='SenderCompID' number='49' type='STRING'/>
<field name='SenderSubID' number='50' type='STRING'/>
<field name='SendingTime' number='52' type='UTCTIMESTAMP'/>
<field name='TargetCompID' number='56' type='STRING'/>
<field name='TargetSubID' number='57' type='STRING'/>
<field name='Text' number='58' type='STRING'/>
<field name='Signature' number='89' type='DATA'/>
<field name='SecureDataLen' number='90' type='LENGTH'/>
<field name='SecureData' number='91' type='DATA'/>
<field name='SignatureLength' number='93' type='LENGTH'/>
<field name='RawDataLength' number='95' type='LENGTH'/>
<field name='RawData' number='96' type='DATA'/>
<field name='PossResend' number='97' type='BOOLEAN'/>
<field name='EncryptMethod' number='98' type='INT'>
<value description='NONE_OTHER' enum='0'/>
<value description='PKCS' enum='1'/>
<value description='DES' enum='2'/>
<value description='PKCS_DES' enum='3'/>
<value description='PGP_DES' enum='4'/>
<value description='PGP_DES_MD5' enum='5'/>
<value description='PEM_DES_MD5' enum='6'/>
</field>
<field name='HeartBtInt' number='108' type='INT'/>
<field name='TestReqID' number='112' type='STRING'/>
<field name='OnBehalfOfCompID' number='115' type='STRING'/>
<field name='OnBehalfOfSubID' number='116' type='STRING'/>
<field name='OrigSendingTime' number='122' type='UTCTIMESTAMP'/>
<field name='GapFillFlag' number='123' type='BOOLEAN'/>
<field name='DeliverToCompID' number='128' type='STRING'/>
<field name='DeliverToSubID' number='129' type='STRING'/>
<field name='ResetSeqNumFlag' number='141' type='BOOLEAN'/>
<field name='SenderLocationID' number='142' type='STRING'/>
<field name='TargetLocationID' number='143' type='STRING'/>
<field name='OnBehalfOfLocationID' number='144' type='STRING'/>
<field name='DeliverToLocationID' number='145' type='STRING'/>
<field name='XmlDataLen' number='212' type='LENGTH'/>
<field name='XmlData' number='213' type='DATA'/>
<field number='347' name='MessageEncoding' type='STRING'>
<value enum='ISO-2022-JP' description='ISO_2022_JP'/>
<value enum='EUC-JP' description='EUC_JP'/>
<value enum='SHIFT_JIS' description='SHIFT_JIS'/>
<value enum='UTF-8' description='UTF_8'/>
</field>
<field name='EncodedTextLen' number='354' type='LENGTH'/>
<field name='EncodedText' number='355' type='DATA'/>
<field name='LastMsgSeqNumProcessed' number='369' type='SEQNUM'/>
<field name='RefTagID' number='371' type='INT'/>
<field name='RefMsgType' number='372' type='STRING'/>
<field name='SessionRejectReason' number='373' type='INT'>
<value description='INVALID_TAG_NUMBER' enum='0'/>
<value description='REQUIRED_TAG_MISSING' enum='1'/>
<value description='SENDINGTIME_ACCURACY_PROBLEM' enum='10'/>
<value description='INVALID_MSGTYPE' enum='11'/>
<value description='XML_VALIDATION_ERROR' enum='12'/>
<value description='TAG_APPEARS_MORE_THAN_ONCE' enum='13'/>
<value description='TAG_SPECIFIED_OUT_OF_REQUIRED_ORDER' enum='14'/>
<value description='REPEATING_GROUP_FIELDS_OUT_OF_ORDER' enum='15'/>
<value description='INCORRECT_NUMINGROUP_COUNT_FOR_REPEATING_GROUP' enum='16'/>
<value description='NON_DATA_VALUE_INCLUDES_FIELD_DELIMITER' enum='17'/>
<value description='TAG_NOT_DEFINED_FOR_THIS_MESSAGE_TYPE' enum='2'/>
<value description='UNDEFINED_TAG' enum='3'/>
<value description='TAG_SPECIFIED_WITHOUT_A_VALUE' enum='4'/>
<value description='VALUE_IS_INCORRECT' enum='5'/>
<value description='INCORRECT_DATA_FORMAT_FOR_VALUE' enum='6'/>
<value description='DECRYPTION_PROBLEM' enum='7'/>
<value description='SIGNATURE_PROBLEM' enum='8'/>
<value description='COMPID_PROBLEM' enum='9'/>
<value description='OTHER' enum='99'/>
</field>
<field name='MaxMessageSize' number='383' type='LENGTH'/>
<field name='NoMsgTypes' number='384' type='NUMINGROUP'/>
<field name='MsgDirection' number='385' type='CHAR'>
<value description='RECEIVE' enum='R'/>
<value description='SEND' enum='S'/>
</field>
<field name='TestMessageIndicator' number='464' type='BOOLEAN'/>
<field name='Username' number='553' type='STRING'/>
<field name='Password' number='554' type='STRING'/>
<field name='NoHops' number='627' type='NUMINGROUP'/>
<field name='HopCompID' number='628' type='STRING'/>
<field name='HopSendingTime' number='629' type='UTCTIMESTAMP'/>
<field name='HopRefID' number='630' type='SEQNUM'/>
<field name='NextExpectedMsgSeqNum' number='789' type='SEQNUM'/>
<field name='ApplVerID' number='1128' type='STRING'>
<value description='FIX27' enum='0'/>
<value description='FIX30' enum='1'/>
<value description='FIX40' enum='2'/>
<value description='FIX41' enum='3'/>
<value description='FIX42' enum='4'/>
<value description='FIX43' enum='5'/>
<value description='FIX44' enum='6'/>
<value description='FIX50' enum='7'/>
<value description='FIX50SP1' enum='8'/>
<value description='FIX50SP2' enum='9'/>
</field>
<field name='CstmApplVerID' number='1129' type='STRING'/>
<field name='RefApplVerID' number='1130' type='STRING'/>
<field name='RefCstmApplVerID' number='1131' type='STRING'/>
<field name='DefaultApplVerID' number='1137' type='STRING'/>
</fields>
</fix>
@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using bts.api;
using MathNet.Numerics.Statistics;
using Google.Protobuf.WellKnownTypes;
using bts.co2;
namespace bts.history
{
public interface IAlgo
{
bool is_completed();
void set_param(string name, string val);
void process_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage curr_book,
MarketDataIncrementalRefresh packet);
void process_trades(string security_desc, uint security_key, IEnumerable<co2.TradeMessage> trades,
MarketDataIncrementalRefresh packet);
}
abstract public partial class AlgoBase : IAlgo
{
public readonly Guid guid = Guid.NewGuid();
public ulong order_key { get; private set;}
public string[] security_list { get; private set; }
public Action<FillEntry> on_fill;
public Action<OrderStatus> on_status;
protected IOrderSink _sink;
public IOrderSink sink => _sink;
readonly protected SortedDictionary< string, Func<MDEntry, OrderIDEntry, bool> > _filter = new SortedDictionary<string, Func<MDEntry, OrderIDEntry, bool>>();
protected AlgoBase(IOrderSink sink, string[] security_list)
{
on_fill = new Action<FillEntry>(f => {});
on_status = new Action<OrderStatus>(s => {});
this._sink = sink;
this.security_list = security_list;
foreach (var sec in security_list)
_filter[sec] = (MDEntry md, OrderIDEntry id) => md.Symbol == sec;
}
readonly Dictionary<string, string> _algo_params = new Dictionary<string, string>();
public IReadOnlyDictionary<string, string> algo_params => _algo_params;
public virtual void set_param(string name, string val)
{
_algo_params[name] = val;
var pi = this.GetType().GetProperty(name);
if(pi != null)
{
if(val.Contains(','))
{
var elem = pi.PropertyType.GetElementType();
var ary = val.Split(',').Select(x => Convert.ChangeType(x, elem)).ToArray();
var tary = Array.CreateInstance(elem, ary.Length);
Array.Copy(ary, tary, ary.Length);
pi.SetValue(this, tary);
}
else
pi.SetValue(this, Convert.ChangeType(val, pi.PropertyType));
}
}
public virtual void process_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage curr_book,
MarketDataIncrementalRefresh packet)
{
if(!_filter.ContainsKey(security_desc))
return;
on_book(security_desc, security_key, current_book, curr_book, packet);
}
public virtual void process_trades(string security_desc, uint security_key, IEnumerable<co2.TradeMessage> trades,
MarketDataIncrementalRefresh packet)
{
if (!_filter.ContainsKey(security_desc))
return;
on_trades(security_desc, security_key, trades, packet);
}
public abstract object report();
public abstract bool is_completed();
protected virtual void on_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet) { }
protected virtual void on_trades(string security_desc, uint security_key, IEnumerable<TradeMessage> trades,
MarketDataIncrementalRefresh packet) { }
protected abstract void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key);
protected abstract void do_update(Timestamp asof, double price, uint size = uint.MinValue);
protected virtual void do_update(Timestamp asof, Dictionary<string, string> algo_params, double price, uint size = uint.MinValue)
{
foreach (var s in algo_params)
this.set_param(s.Key, s.Value);
this.do_update(asof, price, size);
}
protected abstract void do_cancel(Timestamp asof);
protected virtual void get_done(Timestamp asof) {}
public class Handle : IEquatable<Handle>
{
readonly AlgoBase _algo;
public Handle(AlgoBase algo)
{
_algo = algo;
}
public AlgoBase algo => _algo;
public ulong order_key => _algo.order_key;
public void launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key = ulong.MinValue)
{
if(order_key == ulong.MinValue)
order_key = IDGenerator.next_order_id();
_algo.do_launch(security_list, side, price, size, asof, order_key);
}
public void update(Timestamp asof, double price, uint size = uint.MinValue)
{
_algo.do_update(asof, price, size);
}
public void update(Timestamp asof, Dictionary<string, string> algo_params, double price, uint size = uint.MinValue)
{
foreach (var s in algo_params)
_algo.set_param(s.Key, s.Value);
update(asof, price, size);
}
public void cancel(Timestamp asof)
{
_algo.do_cancel(asof);
}
public void get_done(Timestamp asof)
{
_algo.get_done(asof);
}
public override int GetHashCode()
{
return this.algo.guid.GetHashCode();
}
public bool Equals(Handle other)
{
return this.algo.guid == other.algo.guid;
}
}
}
}
@@ -0,0 +1,94 @@
using System;
using MathNet.Numerics.Statistics;
using Google.Protobuf.WellKnownTypes;
using System.Collections.Generic;
using System.Reflection;
using bts.api;
namespace bts.history
{
public class AlgoFactory
{
static public AlgoBase algo_factory<AlgoType>(IOrderSink sink,string[] security_list, string algo_params = null)
where AlgoType : AlgoBase
{
return AlgoBase.algo_factory(typeof(AlgoType), sink, security_list, algo_params);
}
static public void set_algo_params(IAlgo inst, string algo_params)
{
AlgoBase.set_algo_params((AlgoBase) inst, algo_params);
}
static public void set_algo_params(IAlgo inst, IEnumerable<string> param)
{
AlgoBase.set_algo_params((AlgoBase)inst, param);
}
public static AlgoBase.Handle launch(AlgoBase algo, api.Side side, double price, uint size, Timestamp asof,
Action<AlgoBase.Handle, FillEntry> more_on_fill = null,
Action<AlgoBase.Handle, OrderStatus> more_on_status = null)
{
var order_key = IDGenerator.next_order_id();
//I am activating myself at the top\
var h = new AlgoBase.Handle(algo);
h.launch(algo.security_list, side, price, size, asof, order_key);
if (more_on_fill != null)
algo.on_fill += f => more_on_fill(h, f);
if (more_on_status != null)
algo.on_status += s => more_on_status(h, s);
return h;
}
}
abstract partial class AlgoBase
{
static public AlgoBase algo_factory(System.Type t, IOrderSink sink,
string[] security_list, string algo_params = null)
{
AlgoBase inst = inst = (AlgoBase)Activator.CreateInstance(t, sink, security_list);
if(algo_params != null)
set_algo_params(inst, algo_params);
return inst;
}
public virtual Handle create<C>(Action<Handle, FillEntry> more_on_fill = null,
Action<Handle, OrderStatus> more_on_status = null)
where C : AlgoBase
{
var child = AlgoBase.algo_factory(typeof(C), _sink, security_list);
Handle h = hookup_child(child, more_on_fill, more_on_status);
return h;
}
protected Handle hookup_child(AlgoBase child, Action<Handle, FillEntry> more_on_fill, Action<Handle, OrderStatus> more_on_status)
{
child.order_key = order_key;
AlgoBase.set_algo_params(child, this.algo_params);
child.on_fill += f => this.on_fill(f);
//child.on_status += s => this.on_status(s);
var h = new Handle(child);
if (more_on_fill != null)
child.on_fill += (f => more_on_fill(h, f));
if (more_on_status != null)
child.on_status += (s => more_on_status(h, s));
return h;
}
static public void set_algo_params(AlgoBase inst, string algo_params)
{
string[] param = algo_params.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
set_algo_params(inst, param);
}
static public void set_algo_params(AlgoBase inst, IEnumerable<string> param)
{
foreach (var par in param)
{
string[] tup = par.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
Log.assert(tup.Length == 2);
inst.set_param(tup[0], tup[1]);
}
}
static public void set_algo_params(AlgoBase inst, IReadOnlyDictionary<string, string> param)
{
foreach (var par in param)
inst.set_param(par.Key, par.Value);
}
}
}
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using mbts.co2;
using bts.api;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
namespace bts.history
{
public class CompletionCoverAlgo<Child, Hedge> : CompositeHedgeAlgo<Child, Hedge>
where Child : AlgoBase
where Hedge : AlgoBase
{
public CompletionCoverAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
string[] _security_list;
Side _myside;
double _myprice;
uint _mysize;
//algo parameters
public double hedge_ticks { get; set; } = 0.0;
public double hedge_factor { get; set; } = 1.0;
protected override void do_launch(string[] security_list, Side side, double price, uint size, Timestamp asof, ulong order_key)
{
_security_list = security_list;
_myside = side;
_myprice = price;
_mysize = size;
_child = this.create<Child>();
_child.launch(security_list, side, price, size, asof);
}
protected override void on_child_status(Handle child, OrderStatus stat)
{
if(_child?.Equals(child) ?? false)
{
if(stat.state.Status == api.orderman.OrderStatus.Completed)
{
var side = Utils.opposite_side(_myside);
uint size = (uint)(_mysize * this.hedge_factor);
double price = (side == Side.Buy) ?
_myprice - this.hedge_ticks :
_myprice + this.hedge_ticks;
send_hedge(_security_list, side, price, size, stat.TransactTime, null,
(status_handle, status_entry) => { this.on_status?.Invoke(status_entry); }
);
}
this.on_status?.Invoke(stat);
}
}
protected override void do_update(Timestamp asof, double price, uint size = uint.MinValue)
{
_child.update(asof, price, size);
}
protected override void do_cancel(Timestamp asof)
{
_child.cancel(asof);
}
protected override void on_child_fill(Handle child, FillEntry fill)
{
}
}
}
@@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MathNet.Numerics.Statistics;
using Google.Protobuf.WellKnownTypes;
namespace bts.history
{
public abstract class CompositeBase<Child, Hedge> : AlgoBase
where Hedge : AlgoBase
{
protected List<Handle> _children = new List<Handle>();
protected Handle _child
{
get
{
Log.assert(_children.Count <= 1);
return _children.FirstOrDefault();
}
set
{
Log.assert(_children.Count <= 1);
if(value == null)
{
if(_children.Count == 1)
_children.RemoveAt(0);
}
else
{
if(_children.Count == 0)
_children.Add(value);
else
_children[0] = value;
}
}
}
public CompositeBase(IOrderSink sink, string[] security_list) : base(sink, security_list)
{
}
public override Handle create<C>(Action<Handle, FillEntry> more_on_fill = null,
Action<Handle, OrderStatus> more_on_status = null)
{
if(typeof(C) == this.GetType())
{
Log.assert(false);
return null;
//return AlgoBase.activate_self(this, side, price, size, asof, more_on_fill, more_on_status);
}
else
{
Action<Handle, FillEntry> of = new Action<Handle, FillEntry>((h, f) => on_child_fill(h, f));
if (more_on_fill != null)
of += more_on_fill;
Action<Handle, OrderStatus> os = new Action<Handle, OrderStatus>((h, s) =>
{
on_child_status(h, s);
if (s.is_terminal())
_children.Remove(h);
});
if (more_on_status != null)
os += more_on_status;
var handle = base_create<C>(of, os);
_children.Add(handle);
return handle;
}
}
protected Handle base_create<C>(Action<Handle, FillEntry> of, Action<Handle, OrderStatus> os)
where C : AlgoBase
{
return base.create<C>(of, os);
}
protected override void do_update(Timestamp asof, double price, uint size = uint.MinValue)
{
foreach(var child in _children.ToArray())
child?.update(asof, price, size);
}
protected override void get_done(Timestamp asof)
{
base.get_done(asof);
foreach (var child in _children.ToArray())
child?.get_done(asof);
}
protected override void do_cancel(Timestamp asof)
{
foreach (var child in _children.ToArray())
child?.cancel(asof);
}
protected virtual void on_child_fill(Handle child, FillEntry fill) { }
protected abstract void on_child_status(Handle child, OrderStatus stat);
}
public abstract class CompositeAlgo<Child> : CompositeBase<Child, NullAlgo>
{
public CompositeAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
public override bool is_completed()
{
bool comp = true;
foreach (var child in _children)
comp &= child.algo.is_completed();
return comp;
}
public override object report()
{
string c = "";
foreach (var child in _children)
c+= child.algo.report();
return $"{c}";
}
public override void set_param(string name, string val)
{
base.set_param(name, val);
foreach (var child in _children)
child?.algo.set_param(name, val);
}
public override void process_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet)
{
base.process_book(security_desc, security_key, current_book, working_book, packet);
foreach (var child in _children.ToArray()) //copy because this could remove / add a child
child?.algo.process_book(security_desc, security_key, current_book, working_book, packet);
}
}
public abstract class CompositeHedgeAlgo<Child, Hedge> : CompositeBase<Child, Hedge>
where Hedge : AlgoBase
{
protected int _payupcount;
protected Dictionary<Handle, Hedge> _hedges = new Dictionary<Handle, Hedge>();
public CompositeHedgeAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) {}
public override bool is_completed()
{
bool comp = true;
foreach (var child in _children)
comp &= child.algo.is_completed();
foreach (var sub in _hedges.Values)
{
var hc = sub.is_completed();
if(!hc)
Log.assert(true);
comp &= hc;
}
return comp;
}
protected override void get_done(Timestamp asof)
{
base.get_done(asof);
foreach (var child in _children.ToArray())
child?.get_done(asof);
foreach (var sub in _hedges.Keys.ToArray())
sub.get_done(asof);
}
public override object report()
{
string c = "";
foreach (var child in _children)
c+= child.algo.report();
int ih = 0;
string sh = string.Empty;
foreach (var sub in _hedges.Values)
{
if(sub.report() is string sr)
sh += sr;
if(sub.report() is int ir)
ih += ir;
}
return $"{c} Hedge Payups {sh} {ih+_payupcount} Incomplete {_hedges.Values.Sum( x => x.is_completed() ? 0u : 1u) } ";
}
public override void set_param(string name, string val)
{
base.set_param(name, val);
foreach (var child in _children)
child?.algo.set_param(name, val);
foreach (var sub in _hedges.Values)
sub.set_param(name, val);
}
public override void process_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet)
{
base.process_book(security_desc, security_key, current_book, working_book, packet);
foreach (var child in _children.ToArray())
child?.algo.process_book(security_desc, security_key, current_book, working_book, packet);
foreach (var sub in _hedges.Values.ToArray())
sub.process_book(security_desc, security_key, current_book, working_book, packet);
}
public override Handle create<C>(Action<Handle, FillEntry> more_on_fill = null,
Action<Handle, OrderStatus> more_on_status = null)
{
if (typeof(Hedge) == typeof(C))
{
Action<Handle, FillEntry> of = new Action<Handle, FillEntry>((h, f) => {} );
if (more_on_fill != null)
of += more_on_fill;
Action<Handle, OrderStatus> os = new Action<Handle, OrderStatus>((h, s) => {} );
if (more_on_status != null)
os += more_on_status;
var handle = base_create<Hedge>(of, os);
_hedges[handle] = (Hedge)handle.algo;
return handle;
}
else
return base.create<C>(more_on_fill, more_on_status);
}
protected void send_hedge(string security_desc, api.Side side, double price, uint size, Timestamp asof,
Action<Handle, FillEntry> more_on_fill = null,
Action<Handle, OrderStatus> more_on_status = null)
{
send_hedge(new[] { security_desc }, side, price, size, asof, more_on_fill, more_on_status);
}
protected void send_hedge(string[] security_list, api.Side side, double price, uint size, Timestamp asof,
Action<Handle, FillEntry> more_on_fill = null,
Action<Handle, OrderStatus> more_on_status = null)
{
Action<Handle, FillEntry> hedge_on_fill = (hand, f) =>
{
if(more_on_fill != null)
more_on_fill(hand, f);
if (f.fully_filled)
{
if (_hedges.TryGetValue(hand, out Hedge h))
{
if (h.report() is int pay)
_payupcount += pay;
_hedges.Remove(hand);
}
}
};
Action<Handle, OrderStatus> hedge_on_status = (hand,s) =>
{
if(more_on_status != null)
more_on_status(hand, s);
if (s.is_terminal())
_hedges.Remove(hand);
};
var handle = this.create<Hedge>(hedge_on_fill, hedge_on_status);
handle.launch(security_list, side, price, size, asof);
}
}
}
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using mbts.co2;
using bts.api;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
namespace bts.history
{
public class CoverAlgo<Child, Hedge> : CompositeHedgeAlgo<Child, Hedge>
where Child : AlgoBase
where Hedge : AlgoBase
{
public CoverAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
//algo state
private MarketDataIncrementalRefresh _packet;
string[] _security_list;
//algo parameters
public double hedge_ticks { get; set; } = 0.0;
public double hedge_factor { get; set; } = 1.0;
protected override void do_launch(string[] security_list, Side side, double price, uint size, Timestamp asof, ulong order_key)
{
_security_list = security_list;
_child = this.create<Child>();
_child.launch(security_list, side, price, size, asof);
}
protected override void on_child_status(Handle child, OrderStatus stat)
{
if(_child?.Equals(child) ?? false)
this.on_status?.Invoke(stat);
}
protected override void do_update(Timestamp asof, double price, uint size = uint.MinValue)
{
_child.update(asof, price, size);
}
protected override void do_cancel(Timestamp asof)
{
_child.cancel(asof);
}
protected override void on_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet)
{
_packet = packet;
}
protected override void on_child_fill(Handle child, FillEntry fill)
{
if(!child.Equals(_child))
return;
base.on_child_fill(child, fill);
var side = Utils.opposite_side(fill.order.side);
uint size = (uint)(fill.size*this.hedge_factor);
double price = (side == Side.Buy) ?
fill.price - this.hedge_ticks :
fill.price + this.hedge_ticks;
send_hedge(fill.order.security_desc, side, price, size, fill.TransactTime);
}
}
}
@@ -0,0 +1,13 @@
namespace bts.history
{
static class IDGenerator
{
static uint __myid = 999_999;
static public uint next_order_id()
{
return (++__myid);
}
}
}
@@ -0,0 +1,153 @@
using System;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
using mbts.co2;
namespace bts.history
{
public class JoinAlgo<Child> : CompositeAlgo<Child>
where Child : AlgoBase
{
enum AlgoState
{
Begin, WaitingForJoin, Joined, Done
}
//algo state
AlgoState _state;
OrderStatus _status;
api.Side _myside;
uint _mysize;
Timestamp _myasof;
co2.BookMessage _snapshot, _currentbook;
int _turncount, _pilecount;
public JoinAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
//algo parameters
public bool initial_pile { get; set; } = false;
public bool should_chase { get; set; } = true;
public double join_factor { get; set; } = 1.0;
public uint join_quantity{ get; set; } = 0;
protected override void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key)
{
_state = AlgoState.Begin;
_myside = side;
_mysize = size;
_myasof = asof;
_child = null;
}
public override bool is_completed()
{
return _state == AlgoState.Done;
}
public override object report()
{
var b = base.report();
return $"{b} Turns,Piles,Join(Size,Price,piq)={_turncount},{_pilecount},({_status?.state?.Quantity},{_status?.state?.Price})";
}
protected override void on_child_status(Handle child, OrderStatus stat)
{
_status = stat;
if(stat.is_terminal())
_state = AlgoState.Done;
this.on_status?.Invoke(_status);
}
bool has_turned()
{
bool turn = ((_myside == api.Side.Buy &&
_currentbook.side_at(_myside, 0) > _snapshot.side_at(_myside, 0)) || //new top level bid price greater than old, market turn
(_myside == api.Side.Sell &&
_currentbook.side_at(_myside, 0) < _snapshot.side_at(_myside, 0))); //new top level ask price less than old, market turn
return turn;
}
protected override void on_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet)
{
if(_snapshot == null)
_snapshot = current_book.Clone();
_currentbook = current_book;
switch (_state)
{
case AlgoState.Begin:
{
if(initial_pile)
{
var thresh = (this.join_quantity > 0 ? this.join_quantity : this.join_factor * _mysize);
if(_currentbook.sidesize_at(_myside, 0) >= thresh)
{
_pilecount++;
deploy_join_function(packet, security_desc, _currentbook.side_at(_myside, 0));
_state = AlgoState.Joined;
_snapshot = _currentbook.Clone();
}
}
else
_state = AlgoState.WaitingForJoin;
}
break;
case AlgoState.WaitingForJoin:
{
bool turn = has_turned();
var thresh = (this.join_quantity > 0 ? this.join_quantity : this.join_factor * _mysize);
if(turn &&_currentbook.sidesize_at(_myside, 0) >= thresh)
{
_turncount++;
deploy_join_function(packet, security_desc, _currentbook.side_at(_myside, 0));
_state = AlgoState.Joined;
}
_snapshot = _currentbook.Clone();
}
break;
case AlgoState.Joined:
{
if (this.should_chase)
{
bool turn = has_turned();
var thresh = (this.join_quantity > 0 ? this.join_quantity : this.join_factor * _mysize);
if (turn && _currentbook.sidesize_at(_myside, 0) >= thresh)
{
_turncount++;
deploy_join_function(packet, security_desc, _currentbook.side_at(_myside, 0));
_snapshot = _currentbook.Clone();
}
}
}
break;
case AlgoState.Done:
break;
default:
Log.assert(false, "invalid algo state");
break;
}
}
void deploy_join_function(MarketDataIncrementalRefresh packet, string security_desc, double joinprice)
{
Log.Testing.info("deploy_join_function ");
if(_currentbook.bid_at(0) == _currentbook.ask_at(0))
{
Log.Testing.error("crossed book");
return;
}
if(_child == null)
{
_child = this.create<Child>();
_child.launch(new[] { security_desc }, _myside, joinprice, _mysize, packet.timestamp());
}
else
{
_child.update(packet.timestamp(), joinprice);
}
}
}
}
@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using mbts.co2;
using System.Linq;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
namespace bts.history
{
public class LeanAlgo<Child> : CompositeAlgo<Child>
where Child : AlgoBase
{
enum AlgoState
{
LeanWatch, Payup, Done
}
//algo state
AlgoState _state;
OrderStatus _status;
api.Side _myside;
double _myprice;
uint _mysize;
co2.BookMessage _snapshot, _currentbook;
int _payupcount;
public LeanAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
//algo parameters
public double lean_factor { get; set; } = 3.0;
public uint lean_quantity { get; set; } = 0;
protected override void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key)
{
_state = AlgoState.LeanWatch;
_myside = side;
_mysize = size;
_myprice = price;
_child = this.create<Child>();
_child.launch(security_list, side, price, size, asof);
}
public override bool is_completed()
{
return _state == AlgoState.Done;
}
public override object report()
{
return _payupcount;
}
protected override void on_child_status(Handle child, OrderStatus stat)
{
_status = stat;
if (stat.is_terminal())
_state = AlgoState.Done;
this.on_status?.Invoke(_status);
}
protected override void on_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet)
{
if (_snapshot == null)
_snapshot = current_book.Clone();
_currentbook = current_book;
switch (_state)
{
case AlgoState.LeanWatch:
{
bool payupprice = false, payupsize = false;
var leansize = (this.lean_quantity == 0) ? this.lean_factor * this._mysize : this.lean_quantity;
if (_myside == api.Side.Buy)
{
double best_ask = _snapshot.ask_at(0);
var best_ask_size = _snapshot.asksize_at(0);
payupprice = _currentbook.ask_at(0) > best_ask;
payupsize = best_ask_size < leansize;
}
else
{
double best_bid = _snapshot.bid_at(0);
var best_bid_size = _snapshot.bidsize_at(0);
payupprice = _currentbook.bid_at(0) < best_bid;
payupsize = best_bid_size < leansize;
}
if (payupprice || payupsize)
{
_payupcount++;
_state = AlgoState.Payup;
payup_function(packet.timestamp());
}
}
break;
case AlgoState.Payup:
break;
case AlgoState.Done:
break;
default:
Log.assert(false, "invalid algo state");
break;
}
}
protected override void get_done(Timestamp asof)
{
payup_function(asof);
}
void payup_function(Timestamp asof)
{
double newprice = 0.0;
//plow through
if (_myside == api.Side.Sell)
newprice = _currentbook.all_bids().Min();
else
newprice = _currentbook.all_asks().Max();
//this should precipitate a fill
_child.update(asof, newprice);
Log.Testing.info($"payup_function ");
}
}
}
@@ -0,0 +1,62 @@
using System;
using bts.api;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
namespace bts.history
{
public class LimitOrder : AlgoBase
{
public LimitOrder(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
protected OrderEntry _myorder;
OrderStatus _status;
uint _mysize;
double _debug_price;
public override object report() { return string.Empty; }
protected override void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key)
{
setup_order(security_list, side, price, size, asof, order_key);
_sink.send(_myorder);
}
protected void setup_order(string[] security_list, Side side, double price, uint size, Timestamp asof, ulong order_key)
{
_debug_price = price;
_mysize = size;
Log.assert(security_list.Length == 1);
var security_desc = security_list[0];
Action<FillEntry> fill_action = f =>
{
if (this.on_fill != null)
this.on_fill(f);
};
Action<OrderStatus> status_action = s =>
{
_status = s;
if (this.on_status != null)
this.on_status(s.clone());
};
Log.assert(_myorder == null);
_myorder = new OrderEntry(asof, order_key, security_desc, side, price, size, fill_action, status_action);
}
protected override void do_update(Timestamp asof, double price, uint size = uint.MinValue)
{
if(size == _mysize)
size = uint.MinValue;
_debug_price = price;
_sink.update(_myorder.security_desc, asof, _myorder.order_id, price, size);
}
protected override void do_cancel(Timestamp asof)
{
if(_myorder == null)
return;
_sink.cancel(_myorder.security_desc, asof, _myorder.order_id);
}
public override bool is_completed() => _status?.is_terminal() ?? false;
}
}
@@ -0,0 +1,19 @@
using System;
using bts.api;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
namespace bts.history
{
public class MarketOrder : LimitOrder
{
public MarketOrder(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
protected override void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key)
{
setup_order(security_list, side, price, size, asof, order_key);
_myorder.market_order = true;
_sink.send(_myorder);
}
}
}
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace bts.history
{
static class AlgoTestNativeMethods
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate void child_launch_callback(byte* OrderDefinition, ulong length, uint order_key, double price, uint size);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate void child_update_callback(byte* Update, ulong length, uint order_key);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate void child_cancel_callback(uint order_key);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate void child_status_callback(byte* OrderState, ulong length, uint order_key);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate void child_fill_callback(byte* Fill, ulong length, uint order_key);
[DllImport("libspark")]
internal static extern IntPtr try_create_algo(api.orderman.CustomOrderDefinition.CustomOneofCase algo_type);
[DllImport("libspark")]
internal static extern IntPtr create_algo_driver(byte[] Security, int seclength,
byte[] OrderDefinition, int deflength,
child_launch_callback launch_cb,
child_update_callback update_cb,
child_cancel_callback cancel_cb,
child_status_callback stat_cb,
child_fill_callback fill_cb);
[DllImport("libspark")]
internal static extern void destroy_algo_driver(IntPtr algo_driver);
[DllImport("libspark")]
internal static extern void destroy_algo(IntPtr algo_driver);
[DllImport("libspark")]
internal static extern void launch_algo(IntPtr algo_driver, uint order_key, double price, uint size);
[DllImport("libspark")]
internal static extern void update_price(IntPtr algo_driver, double price);
[DllImport("libspark")]
internal static extern void update_size(IntPtr algo_driver, uint size);
[DllImport("libspark")]
internal static extern void cancel(IntPtr algo_driver);
[DllImport("libspark")]
internal static extern void advance_time(IntPtr algo_driver, ulong microsecond, uint steps);
[DllImport("libspark")]
internal static extern void update_book(IntPtr algo_driver, uint security_id, byte[] BookMessage, int length);
[DllImport("libspark")]
internal static extern void update_ticker(IntPtr algo_driver, uint security_id, byte[] TradeMessage, int length);
[DllImport("libspark")]
internal static extern void fill(IntPtr algo_driver, uint order_key, double price, uint quantity);
//see AlgoTestDriver.h
internal enum AlgoDriverState
{
CREATED,
// before_Launch() ->
READY,
// launch() ->
RUNNING,
CANCELED,
TERMINATED,
COMPLETED
};
[DllImport("libspark")]
internal static extern AlgoDriverState algo_driver_state(IntPtr algo_driver);
internal static bool state_is_terminal(AlgoDriverState st)
{
return st == AlgoDriverState.CANCELED || st == AlgoDriverState.TERMINATED || st == AlgoDriverState.COMPLETED;
}
}
}
@@ -0,0 +1,320 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
using mbts.co2;
namespace bts.history
{
public class NativeAlgo : CompositeAlgo<AlgoBase>
{
readonly Dictionary<ulong, api.orderman.OrderState> _order_key_vs_state = new Dictionary<ulong, api.orderman.OrderState>();
api.Side _myside;
double _myprice;
uint _mysize;
Timestamp _myasof;
api.Security _sec;
readonly api.orderman.OrderDefinition _def;
IntPtr _proxy;
readonly AlgoTestNativeMethods.child_launch_callback _launchcb;
readonly AlgoTestNativeMethods.child_update_callback _updatecb;
readonly AlgoTestNativeMethods.child_cancel_callback _cancelcb;
readonly AlgoTestNativeMethods.child_status_callback _statcb;
readonly AlgoTestNativeMethods.child_fill_callback _fillcb;
MessageParser<api.orderman.OrderDefinition> _defparser = new MessageParser<api.orderman.OrderDefinition>(() => new api.orderman.OrderDefinition());
MessageParser<api.orderman.Update> _updateparser = new MessageParser<api.orderman.Update>(() => new api.orderman.Update());
MessageParser<api.orderman.OrderState> _stateparser = new MessageParser<api.orderman.OrderState>(() => new api.orderman.OrderState());
MessageParser<api.orderman.Fill> _fillparser = new MessageParser<api.orderman.Fill>(() => new api.orderman.Fill());
OrderEntry _fake_order_for_status;
public NativeAlgo(IOrderSink sink, string[] security_list,
api.orderman.OrderDefinition def) : base(sink, security_list)
{
_def = def;
unsafe
{
_launchcb =
(byte * OrderDefinition, ulong length, uint order_key, double price, uint size) => {
on_native_launch(OrderDefinition, length, order_key, price, size); } ;
_updatecb =
(byte* Update, ulong length, uint order_key) => {
on_native_update(Update, length, order_key); };
_cancelcb =
(uint order_key) => {
on_native_cancel(order_key); };
_statcb =
(byte * OrderState, ulong length, uint order_key) => {
on_native_status(OrderState, length, order_key); } ;
_fillcb =
(byte * Fill, ulong length, uint order_key) => {
on_native_fill(Fill, length, order_key); } ;
}
init_proxy(security_list);
}
void init_proxy(string[] security_list)
{
Log.assert(security_list != null && security_list.Length == 1);
_sec = new api.Security { Id = _def.SecurityKey, Symbol = security_list[0] };
if (_sec.Symbol.Contains(':') || _sec.Symbol.Contains('-'))
_sec.Spread = new api.SpreadContract() { Normalized = true };
else
_sec.Future = new api.FutureContract();
reset();
}
void reset()
{
unsafe
{
if(_proxy != IntPtr.Zero)
{
AlgoTestNativeMethods.destroy_algo_driver(_proxy);
_order_key_vs_state.Clear();
}
var secser = _sec.ToByteArray();
var defser = _def.ToByteArray();
_proxy = AlgoTestNativeMethods.create_algo_driver(secser, secser.Length, defser, defser.Length,
_launchcb, _updatecb, _cancelcb,
_statcb, _fillcb);
Log.assert(_proxy != IntPtr.Zero);
}
}
protected override void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key)
{
Log.assert(security_list != null && security_list.Length == 1);
Log.assert(side == _def.Side);
_myside = side;
_myprice = price;
_mysize = size;
_myasof = asof;
AlgoTestNativeMethods.launch_algo(_proxy, (uint)order_key, price, size);
_fake_order_for_status = new OrderEntry(asof, order_key, security_list[0], side, price, size, null, null, "Native");
}
Handle hookup_child(AlgoBase child, string[] security_list, api.Side side,
double price, uint size, Timestamp asof, uint order_key)
{
Action<Handle, FillEntry> of = new Action<Handle, FillEntry>((h, f) =>
on_child_fill(h, f));
Action<Handle, OrderStatus> os = new Action<Handle, OrderStatus>((h, s) =>
{
on_child_status(h, s);
if (s.is_terminal())
_children.Remove(h);
});
var handle = this.hookup_child(child, of, os);
_children.Add(handle);
handle.launch(security_list, side, price, size, asof, order_key);
return handle;
}
// callback when this child launches other guys
unsafe void on_native_launch(byte* OrderDefinition, ulong length, uint order_key, double price, uint size)
{
byte[] arr = new byte[length];
Marshal.Copy((IntPtr)OrderDefinition, arr, 0, (int)length);
api.orderman.OrderDefinition def = _defparser.ParseFrom(arr);
switch(def.CustomDef.CustomCase)
{
case api.orderman.CustomOrderDefinition.CustomOneofCase.Market:
{
var child = AlgoBase.algo_factory(typeof(MarketOrder), _sink, this.security_list);
hookup_child(child, this.security_list, def.Side, price, size, _myasof, order_key);
}
break;
case api.orderman.CustomOrderDefinition.CustomOneofCase.Limit:
case api.orderman.CustomOrderDefinition.CustomOneofCase.GlbxIceberg:
{
var child = AlgoBase.algo_factory(typeof(LimitOrder), _sink, this.security_list);
hookup_child(child, this.security_list, def.Side, price, size, _myasof, order_key);
}
break;
case api.orderman.CustomOrderDefinition.CustomOneofCase.Stop:
case api.orderman.CustomOrderDefinition.CustomOneofCase.StopLimit:
Log.assert(false);
break;
case api.orderman.CustomOrderDefinition.CustomOneofCase.Dynamic:
Log.assert(false);
break;
default:
{
IntPtr native = AlgoTestNativeMethods.try_create_algo(def.CustomDef.CustomCase);
if(native != IntPtr.Zero)
{
AlgoTestNativeMethods.destroy_algo(native); //TODO, boolean
var child = new NativeAlgo(_sink, this.security_list, def);
hookup_child(child, this.security_list, def.Side, price, size, _myasof, order_key);
// var handle = this.hookup_child(child, security_list, def.Side, price, size, _myasof, null, null, order_key);
// _children.Add(handle);
}
}
break;
}
}
unsafe void on_native_update(byte* Update, ulong length, uint order_key)
{
byte[] arr = new byte[length];
Marshal.Copy((IntPtr)Update, arr, 0, (int)length);
api.orderman.Update upd = _updateparser.ParseFrom(arr);
var f = _children.FirstOrDefault(h => h.order_key == order_key);
if(f != null)
f.update(_lastmd.timestamp(), upd.Price, upd.Quantity);
}
unsafe void on_native_cancel(uint order_key)
{
var f = _children.FirstOrDefault(h => h.order_key == order_key);
if(f != null)
f.cancel(_lastmd.timestamp());
}
protected override void on_child_status(Handle child, OrderStatus stat)
{
_order_key_vs_state[child.order_key] = stat.state;
process_status(stat.state);
}
protected override void on_child_fill(Handle child, FillEntry fill)
{
AlgoTestNativeMethods.fill(_proxy, (uint)fill.order.order_id, fill.price, (uint)fill.size);
this.on_fill?.Invoke(fill);
}
unsafe void on_native_status(byte* OrderState, ulong length, uint order_key)
{
byte[] arr = new byte[length];
Marshal.Copy((IntPtr)OrderState, arr, 0, (int)length);
api.orderman.OrderState curr = _stateparser.ParseFrom(arr);
_order_key_vs_state[order_key] = curr;
process_status(curr);
}
private void process_status(api.orderman.OrderState curr)
{
bool done = is_completed();
api.orderman.OrderState aggstate = new api.orderman.OrderState
{
Status = done ? api.orderman.OrderStatus.Completed : curr.Status,
Price = curr.Price,
Quantity = curr.Quantity,
AvgPrice = _order_key_vs_state.Values.Average(x => x.Price),
FilledQuantity = _order_key_vs_state.Values.Sum(x => x.FilledQuantity),
LastChange = curr.LastChange,
LastMsg = curr.LastMsg,
LastPrice = curr.LastPrice
};
OrderStatus agg = new OrderStatus(_fake_order_for_status, aggstate);
agg.TransactTime = curr.LastChange ?? _lastmd?.SendingTime;
agg.SendingTime = curr.LastChange ?? _lastmd?.SendingTime;
this.on_status?.Invoke(agg);
}
unsafe void on_native_fill(byte* Fill, ulong length, uint order_key)
{
byte[] arr = new byte[length];
Marshal.Copy((IntPtr)Fill, arr, 0, (int)length);
api.orderman.Fill fill = _fillparser.ParseFrom(arr);
//this method is called as a result of us sending a fill down in AlgoTestNativeMethods.fill so we do nothing
}
~NativeAlgo()
{
AlgoTestNativeMethods.destroy_algo_driver(_proxy);
}
public override bool is_completed()
{
bool done = false;
if(_order_key_vs_state.Count > 0)
{
var astate = AlgoTestNativeMethods.algo_driver_state(_proxy);
done = AlgoTestNativeMethods.state_is_terminal(astate);
foreach(var st in _order_key_vs_state.Values)
done &= OrderStatus.is_terminal(st);
}
return done;
}
public override object report()
{
return $"Native";
}
protected override void do_update(Timestamp asof, double price, uint size = uint.MinValue)
{
base.do_update(asof, price, size);
AlgoTestNativeMethods.update_price(_proxy, price);
if(size != uint.MinValue)
{
AlgoTestNativeMethods.update_size(_proxy, size);
}
}
protected override void do_cancel(Timestamp asof)
{
base.do_cancel(asof);
AlgoTestNativeMethods.cancel(_proxy);
}
MarketDataIncrementalRefresh _lastmd;
protected override void on_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet)
{
security_key = _def.SecurityKey; //to fake this up for the AlgoTestDriver
var book = current_book.ToByteArray();
AlgoTestNativeMethods.update_book(_proxy, security_key, book, book.Length);
advance_time(packet);
}
protected override void on_trades(string security_desc, uint security_key, IEnumerable<co2.TradeMessage> trades,
MarketDataIncrementalRefresh packet)
{
security_key = _def.SecurityKey;
foreach(var trade in trades)
{
var tr = trade.ToByteArray();
AlgoTestNativeMethods.update_ticker(_proxy, security_key, tr, tr.Length);
}
advance_time(packet);
}
void advance_time(MarketDataIncrementalRefresh packet)
{
if (_lastmd != null)
{
var dt = packet.timestamp() - _lastmd.timestamp();
if (dt.Seconds >= 0 && dt.Nanos > 0)
{
ulong us = (ulong)dt.Seconds * 1000_000ul + ((ulong)dt.Nanos / 1000ul);
AlgoTestNativeMethods.advance_time(_proxy, us, 1);
}
}
_lastmd = packet;
}
}
}
@@ -0,0 +1,22 @@
using System;
using bts.api;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
namespace bts.history
{
public class NullAlgo : AlgoBase
{
public NullAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
public override bool is_completed() => true;
public override object report() { return string.Empty; }
protected override void do_launch(string[] security_list, Side side, double price, uint size, Timestamp asof, ulong order_key) { }
protected override void do_update(Timestamp asof, double price, uint size = uint.MinValue) { }
protected override void do_cancel(Timestamp asof) { }
}
}
@@ -0,0 +1,324 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
using mbts.co2;
namespace bts.history
{
public class SpreaderAlgo<Child, Hedge> : CompositeHedgeAlgo<Child, Hedge>
where Child : AlgoBase
where Hedge : AlgoBase
{
enum AlgoState
{
Begin, Quoting, Done
}
//algo state
string[] _security_list;
AlgoState _state;
double _myprice;
uint _mysize;
bool _sizeupdate;
api.Side _myside;
SortedList<string, co2.BookMessage> _current_book = new SortedList<string, co2.BookMessage>();
OrderStatus _buystatus, _sellstatus;
Handle _buychild;
Handle _sellchild;
OrderEntry _fake_order_for_status;
private MarketDataIncrementalRefresh _packet;
public SpreaderAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
//algo parameters
public double profit_ticks { get; set; } = 0.0;
public double[] tick_size { get; set; }
public double[] hedge_factors { get; set; }
public double[] price_factors { get; set; }
public double lean_factor { get; set; } = 1.0;
public bool accumulate_delta { get; set; } = false;
public bool dont_cross { get; set; } = false;
public bool wait_for_opposite_side_to_continue { get; set; } = false;
Dictionary<string, double> _tick_size = new Dictionary<string, double>();
Dictionary<string, double> _hedge_factors = new Dictionary<string, double>();
Dictionary<string, double> _price_factors = new Dictionary<string, double>();
Dictionary<string, double> _delta = new Dictionary<string, double>();
string _quote_leg;
readonly List<bool> _sides = new List<bool>();
protected override void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key)
{
_security_list = security_list;
for(int i = 0; i < _security_list.Length; i++)
{
_current_book[_security_list[i]] = null;
_tick_size[_security_list[i]] = tick_size[i];
_hedge_factors[_security_list[i]] = hedge_factors[i];
if(price_factors == null || price_factors.Length < _security_list.Length)
_price_factors[_security_list[i]] = hedge_factors[i];
else
_price_factors[_security_list[i]] = price_factors[i];
_delta[_security_list[i]] = 0.0;
_quote_leg = _security_list[i]; //last
}
_state = AlgoState.Begin;
_myprice = price;
_mysize = size;
_myside = side;
_fake_order_for_status = new OrderEntry(asof, order_key, string.Join("_", security_list.Select(x=>x)),
side, price, size, null, null, "SpreaderAlgo");
if (_myside == api.Side.Buy)
_sides.Add(false);
else if (_myside == api.Side.Sell)
_sides.Add(true);
else
{
_sides.Add(false);
_sides.Add(true);
}
}
protected override void do_update(Timestamp asof, double price, uint size = uint.MinValue)
{
_myprice = price;
if(size > uint.MinValue)
{
_mysize = size;
_sizeupdate = true;
}
}
public override bool is_completed()
{
return base.is_completed() && _state == AlgoState.Done;
}
public override object report()
{
var b = base.report();
string qtleg = _security_list.Last();
return b;
}
protected override void on_child_status(Handle child, OrderStatus stat)
{
if(stat.side == api.Side.Buy && (_buychild?.Equals(child) ?? false))
{
_buystatus = stat;
if(stat.is_terminal() && !this.wait_for_opposite_side_to_continue)
_sellchild?.cancel(_buystatus.SendingTime);
this.on_status?.Invoke(_buystatus);
}
else if(stat.side == api.Side.Sell && (_sellchild?.Equals(child) ?? false))
{
_sellstatus = stat;
if(stat.is_terminal() && !this.wait_for_opposite_side_to_continue)
_buychild?.cancel(_sellstatus.SendingTime);
this.on_status?.Invoke(_sellstatus);
}
//hedges are looked after elsewhere
if( (_buystatus?.is_terminal() ?? true) && (_sellstatus?.is_terminal() ?? true) )
_state = AlgoState.Done;
send_my_status(stat);
}
private void send_my_status(OrderStatus curr)
{
bool done = is_completed();
api.orderman.OrderState aggstate = new api.orderman.OrderState
{
Status = done ? api.orderman.OrderStatus.Completed : api.orderman.OrderStatus.Working,
Price = _myprice,
Quantity = _mysize,
AvgPrice = 0.0,
FilledQuantity = done ? _mysize : 0,
LastChange = curr.TransactTime,
LastMsg = curr.state.LastMsg,
LastPrice = curr.state.LastPrice
};
uint bqty = _buystatus?.state.FilledQuantity ?? 0;
uint sqty = _sellstatus?.state.FilledQuantity ?? 0;
aggstate.FilledQuantity = (uint) ( (bqty + sqty) / this._hedge_factors[_quote_leg] );
OrderStatus agg = new OrderStatus(_fake_order_for_status, aggstate);
agg.TransactTime = curr.TransactTime ?? _fake_order_for_status.last_change;
agg.SendingTime = curr.SendingTime ?? _fake_order_for_status.last_change;
this.on_status?.Invoke(agg);
}
protected override void on_child_fill(Handle child, FillEntry fill)
{
var ff = factor_side(this._hedge_factors[_quote_leg]);
bool invert = ff != fill.order.side;
foreach(var sec in _security_list)
{
if(sec == _quote_leg)
continue;
var hedgeside = factor_side(this._hedge_factors[sec], invert);
var ratio = Math.Abs(this._hedge_factors[sec] / this._hedge_factors[_quote_leg]);
double price = (hedgeside == api.Side.Buy) ? _current_book[sec].ask_at(0) : _current_book[sec].bid_at(0);
uint hedgesize_available = (hedgeside == api.Side.Buy) ? _current_book[sec].asksize_at(0) : _current_book[sec].bidsize_at(0);
uint size = (uint)(fill.size * ratio);
if(this.accumulate_delta)
{
_delta[sec] += fill.size * ratio - size;
if(_delta[sec] > 1.0)
{
var usize = (uint)_delta[sec];
size += usize;
_delta[sec] -= usize;
}
}
if(size > 0)
send_hedge(sec, hedgeside, price, size, fill.TransactTime, handle_hedge_fill, handle_hedge_status);
else
fill.tag = (fill.tag ?? "") + $"SmallHedge: {price.ToString("0")} {hedgesize_available}";
}
}
private void handle_hedge_status(Handle handle, OrderStatus stat)
{
Log.assert(true);
}
private void handle_hedge_fill(Handle handle, FillEntry fill)
{
Log.assert(true);
}
api.Side factor_side(double mult, bool invert = false)
{
api.Side sd = api.Side.Undefined;
if(mult >= 0.0)
sd = api.Side.Buy;
else
sd = api.Side.Sell;
if(invert)
sd = Utils.opposite_side(sd);
return sd;
}
static double round_tick_up(double input, double tick)
{
return Math.Ceiling(input / tick) * tick;
}
static double round_tick_dn(double input, double tick)
{
return Math.Floor(input / tick) * tick;
}
protected override void on_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet)
{
_packet = packet;
_current_book[security_desc] = current_book;
var look_book = _current_book;
switch (_state)
{
case AlgoState.Begin:
{
if( !_current_book.Values.Any(x => x == null) )
_state = AlgoState.Quoting;
}
break;
case AlgoState.Quoting:
{
double qtbid = _myprice - profit_ticks; uint szbid = 0;
double qtask = _myprice + profit_ticks; uint szask = 0;
foreach(var sec in _security_list)
{
var book = look_book[sec];
if (sec != _quote_leg)
{
var hedge_sidebid = factor_side(this._hedge_factors[sec], false);
var hedge_sideask = factor_side(this._hedge_factors[sec], true);
var cross_sidebid = Utils.opposite_side(hedge_sidebid);
var cross_sideask = Utils.opposite_side(hedge_sideask);
var legbid = this._price_factors[sec] * book.side_at(cross_sidebid, 0);
var legask = this._price_factors[sec] * book.side_at(cross_sideask, 0);
qtbid -= legbid;
qtask -= legask;
var hratio = Math.Abs(this._hedge_factors[sec] / this._hedge_factors[_quote_leg]);
hratio *= this.lean_factor;
var quote_size = (uint) (_mysize * Math.Abs(this._hedge_factors[_quote_leg]));
szbid = Math.Min(quote_size, (uint) (book.sidesize_at(cross_sidebid, 0) / hratio));
szask = Math.Min(quote_size, (uint) (book.sidesize_at(cross_sideask, 0) / hratio));
}
else
{
qtbid /= this._price_factors[_quote_leg];
qtask /= this._price_factors[_quote_leg];
double temp = qtbid;
qtbid = qtask;
qtask = temp;
double tick = _tick_size[sec];
qtbid = round_tick_dn(qtbid, tick);
qtask = round_tick_up(qtask, tick);
if (this.dont_cross)
{
qtbid = Math.Min(qtbid, book.bid_at(0));
qtask = Math.Max(qtask, book.ask_at(0));
}
}
}
foreach (var invert in _sides)
{
api.Side quoteside = factor_side(this._hedge_factors[_quote_leg], invert);
if(quoteside == api.Side.Buy && szbid > 0 && (_buystatus?.state?.Price != qtbid || _sizeupdate) && (!_buystatus?.is_terminal() ?? true))
quote_function(packet, _quote_leg, quoteside, qtbid, szbid);
else if(quoteside == api.Side.Sell && szask > 0 && (_sellstatus?.state?.Price != qtask || _sizeupdate) && (!_sellstatus?.is_terminal() ?? true))
quote_function(packet, _quote_leg, quoteside, qtask, szask);
}
}
break;
case AlgoState.Done:
break;
default:
Log.assert(false, "invalid algo state");
break;
}
}
void quote_function(MarketDataIncrementalRefresh packet, string qtleg,
api.Side quoteside, double qt, uint sz)
{
if (quoteside == api.Side.Buy)
{
if (_buychild == null)
{
_buychild = this.create<Child>();
_buychild.launch(new[] { qtleg }, quoteside, qt, sz, packet.timestamp());
}
else if(_buystatus != null)
{
_buychild.update(packet.timestamp(), qt, sz);
_sizeupdate = false;
}
}
else
{
if (_sellchild == null)
{
_sellchild = this.create<Child>();
_sellchild.launch(new[] { qtleg }, quoteside, qt, sz, packet.timestamp());
}
else if (_sellstatus != null)
{
_sellchild.update(packet.timestamp(), qt, sz);
_sizeupdate = false;
}
}
}
}
}
@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using Google.Protobuf.WellKnownTypes;
namespace bts.history
{
public class StackerAlgo<Child> : CompositeAlgo<Child>
where Child : AlgoBase
{
Handle[] _handles;
Dictionary<Handle, OrderStatus> _status = new Dictionary<Handle, OrderStatus>();
api.Side _myside;
double _myprice;
public StackerAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
public double[] tick_size { get; set; }
public uint stack_length { get; set; } = 2;
double _tick_size;
protected override void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key)
{
_myside = side;
_myprice = price;
_handles = new Handle[this.stack_length];
for (int i = 0; i < this.security_list.Length; i++)
{
if(this.security_list[i] == security_list[0])
_tick_size = tick_size[i];
}
if(this.stack_length <= 1)
throw new NotSupportedException("StackerAlgo must have stack_length > 1");
for(int i = 0; i < this.stack_length; i++)
{
if(side == api.Side.Buy)
{
var h = _handles[i] = this.create<Child>();
h.launch(security_list, side, price - i * _tick_size, size, asof);
}
else
{
var h = _handles[i] = this.create<Child>();
h.launch(security_list, side, price + i * _tick_size, size, asof);
}
}
}
protected override void do_update(Timestamp asof, double price, uint size = uint.MinValue)
{
bool better = (_myside == api.Side.Buy ? price > _myprice : price < _myprice);
if(price == _myprice)
{
Log.Testing.error("update of StackerAlgo for same price");
return;
}
double px = _myprice;
if(better)
{
double tick = (_myside == api.Side.Buy ? _tick_size : -_tick_size);
while(Math.Abs(price - px) > _tick_size / 10.0)
{
px += tick;
var mover = _handles[_handles.Length - 1];
for(int j = _handles.Length-1; j >= 1; j--)
_handles[j] = _handles[j-1];
_handles[0] = mover;
mover.update(asof, px, size);
}
var stat0 = _status[_handles[0]];
var stat1 = _status[_handles[1]];
Log.assert(stat0.state.Price == price);
Log.assert(stat1.state.Price == price - tick);
}
else
{
double tick = (_myside == api.Side.Buy ? -_tick_size : _tick_size);
while (Math.Abs(price - px) > _tick_size / 10.0)
{
var mover = _handles[0];
for (int j = 0; j < _handles.Length-1; j++)
_handles[j] = _handles[j+1];
_handles[_handles.Length-1] = mover;
double endprice = px + tick * (_handles.Length);
mover.update(asof, endprice, size);
px += tick;
}
var stat0 = _status[_handles[0]];
var stat1 = _status[_handles[1]];
Log.assert(stat0.state.Price == price);
Log.assert(stat1.state.Price == price + tick);
on_child_status(_handles[0], stat0); //to force status update since this order didn't actually get updated
}
_myprice = px;
}
protected override void on_child_status(Handle child, OrderStatus stat)
{
_status[child] = stat;
if(_handles[0]?.Equals(child) ?? false)
{
this.on_status?.Invoke(stat);
}
}
}
}
@@ -0,0 +1,96 @@
using System;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
using mbts.co2;
namespace bts.history
{
public class TopAlgo<Child> : CompositeAlgo<Child>
where Child : AlgoBase
{
enum AlgoState
{
WaitingToCancel, CancelRequested, Done
}
//algo state
AlgoState _state;
OrderStatus _status;
api.Side _myside;
uint _mysize;
co2.BookMessage _currentbook;
int _topcount;
public TopAlgo(IOrderSink sink, string[] security_list) : base(sink, security_list) { }
//algo parameters
public double top_factor { get; set; } = 1.0;
public uint top_quantity { get; set; } = 0;
protected override void do_launch(string[] security_list, api.Side side, double price, uint size, Timestamp asof, ulong order_key)
{
_state = AlgoState.WaitingToCancel;
_myside = side;
_mysize = size;
_child = this.create<Child>();
_child.launch(security_list, side, price, size, asof);
}
public override bool is_completed()
{
return _state == AlgoState.Done;
}
public override object report()
{
var b = base.report();
return $"{b} Cancels { _topcount }";
}
protected override void on_child_status(Handle child, OrderStatus stat)
{
_status = stat;
if (stat.is_terminal())
_state = AlgoState.Done;
this.on_status?.Invoke(_status);
}
protected override void on_book(string security_desc, uint security_key, co2.BookMessage current_book, co2.BookMessage working_book,
MarketDataIncrementalRefresh packet)
{
_currentbook = current_book;
switch (_state)
{
case AlgoState.WaitingToCancel:
{
bool top = _currentbook.side_at(_myside, 0) == _status?.state?.Price;
var thresh = (this.top_quantity > 0 ? this.top_quantity : this.top_factor * _mysize);
if(top && _currentbook.sidesize_at(_myside, 0) < thresh)
{
cancel_top_function(packet,
security_desc, _currentbook.side_at(_myside, 0));
_state = AlgoState.CancelRequested;
_topcount++;
}
}
break;
case AlgoState.CancelRequested:
break;
case AlgoState.Done:
break;
default:
Log.assert(false, "invalid algo state");
break;
}
}
void cancel_top_function(MarketDataIncrementalRefresh packet, string security_desc, object param)
{
Log.Testing.info("cancel_top_function ");
_child.cancel(packet.timestamp());
_state = AlgoState.Done;
}
}
}
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using bts.api;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
namespace bts.history
{
public class JoinCoverLean : CoverAlgo<JoinAlgo<TopAlgo<LimitOrder>>, LeanAlgo<LimitOrder>>
{
public JoinCoverLean(IOrderSink sink, string[] security_list)
: base(sink, security_list)
{
}
}
public class PegCoverLean : CoverAlgo<JoinAlgo<TopAlgo<LimitOrder>>, LeanAlgo<LimitOrder>>
{
public PegCoverLean(IOrderSink sink, string[] security_list)
: base(sink, security_list)
{
this.set_param(nameof(JoinAlgo<LimitOrder>.initial_pile), true.ToString());
}
}
public class Spreader : SpreaderAlgo<LimitOrder, LeanAlgo<LimitOrder>>
{
public Spreader(IOrderSink sink, string[] security_list)
: base(sink, security_list)
{
}
}
public class StackSpreader : SpreaderAlgo<StackerAlgo<LimitOrder>, LeanAlgo<LimitOrder>>
{
public StackSpreader(IOrderSink sink, string[] security_list)
: base(sink, security_list)
{
}
}
//to differentiate types for factory
public class HedgeSpreader<Child, Hedge> : SpreaderAlgo<Child, Hedge>
where Child : AlgoBase
where Hedge : AlgoBase
{
public HedgeSpreader(IOrderSink sink, string[] security_list)
: base(sink, security_list)
{
}
}
public class DualSpreader : CompletionCoverAlgo< SpreaderAlgo<LimitOrder, LeanAlgo<LimitOrder>>, HedgeSpreader<LimitOrder, LeanAlgo<LimitOrder>> >
{
public DualSpreader(IOrderSink sink, string[] security_list)
: base(sink, security_list)
{
}
}
public class TWAP : NativeAlgo
{
public TWAP(IOrderSink sink, string[] security_list)
: base(sink, security_list, build_def())
{
}
static api.orderman.OrderDefinition build_def()
{
api.orderman.OrderDefinition def = new api.orderman.OrderDefinition();
def.SecurityKey = 51880u;
def.Side = api.Side.Buy;
def.CustomDef = new api.orderman.CustomOrderDefinition { Twap = new api.orderman.TWAPOrderDefinition() };
def.CustomDef.Twap.Duration = new api.orderman.TimeDuration() { DurationInMinutes = 6, IntervalInSeconds = 10 } ;
def.CustomDef.Twap.Aggression = new api.orderman.OrderAggression() { };
def.CustomDef.Twap.TacticalChild = new api.orderman.CustomOrderDefinition
{
Peg = new api.orderman.PegOrderDefinition()
{
Child = new api.orderman.CustomOrderDefinition { Limit = new api.orderman.LimitOrderDefinition() } ,
TickOffset = 0,
}
};
def.CustomDef.Twap.AggressiveChild = new api.orderman.CustomOrderDefinition
{
Peg = new api.orderman.PegOrderDefinition()
{
Child = new api.orderman.CustomOrderDefinition
{
Lean = new api.orderman.LeanOrderDefinition()
{
Child = new api.orderman.CustomOrderDefinition { Limit = new api.orderman.LimitOrderDefinition() },
CrossThreshold = new spark.QuantityMeasure { Ratio = 3.0 },
TradeTowardBehavior = api.orderman.LeanOrderDefinition.Types.OnToward.NoChange,
MaxChaseTicks = 10,
}
},
TickOffset = 0,
}
};
return def;
}
}
}
@@ -0,0 +1,33 @@
using System;
namespace bts.history
{
internal class TradeStats
{
public DateTime security_timestamp { get; set; }
public string security_desc { get; set; }
public double our_average_buy_piq;
public double our_average_buy_q_len;
public double our_average_buy_price { get; set; }
public double our_average_sell_piq;
public double our_average_sell_q_len;
public double our_average_sell_price { get; set; }
public int our_total_buys { get; set; }
public int our_total_sells { get; set; }
public int our_total_volume { get; set; }
public double latest_pnl { get; set; }
public uint total_exchange_bought { get; set; }
public uint total_exchange_sold { get; set; }
public uint total_exchange_volume { get; set; }
public uint best_bid_size { get; set; }
public double best_bid { get; set; }
public double best_ask { get; set; }
public uint best_ask_size { get; set; }
public double last_trade_price { get; set; }
public uint last_trade_size { get; set; }
}
}
@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MathNet.Numerics.Statistics;
using System.Text;
using System.IO;
using mbts.config;
using bts.config;
namespace bts.history
{
class BookAnalyzer : IAnalyzer
{
class SecData{
public uint n_updates = 0;
public System.IO.FileStream str;
public SecData (System.IO.FileStream str_a){
str = str_a;
}
public void inc_count(){
n_updates++;
}
}
private readonly Dictionary<string, co2.BookMessage> _books =
new Dictionary<string, co2.BookMessage>();
private readonly string _book_path;
IDictionary<string, SecData> _book_stream_map =
new Dictionary<string, SecData>();
public BookAnalyzer(string symbol,
HistoricalDataOptions options) {
_book_path = get_book_path(symbol, DateTime.Parse(options.replaystart));
System.IO.Directory.CreateDirectory(_book_path);
}
public void pre_analyze(MarketDataIncrementalRefresh md,
string debug_fix = null)
{}
public void analyze(MarketDataIncrementalRefresh md,
string debug_fix = null)
{
foreach(var entry in md.MdEntries)
{
if(entry.MdEntryType == MDEntryType.Bid ||
entry.MdEntryType == MDEntryType.Offer)
{
if(!_books.TryGetValue(entry.Symbol, out co2.BookMessage book))
{
book = new co2.BookMessage();
Utils.init_book(book);
_books[entry.Symbol] = book;
}
Utils.keep_book(book, entry);
if (Utils.quote_event_done(md))
on_new_book(book, entry, md);
}
// else if (entry.MdEntryType == MDEntryType.Trade)
// handle trades here
}
}
public static string get_book_path (string symbol,
DateTime date){
return String.Format(@"{0}{1}/{2}/{3}/{4}/{5}/",
Configuration.get<bts.config.System>().data_path,
"trade_analysis",
"market_data",
date.ToShortDateString().Replace("/","_"),
"books",
symbol);
}
public static string get_book_path (string symbol,
string sec_name,
DateTime date){
return String.Format(@"{0}{1}",
get_book_path(symbol, date),
sec_name);
}
public static string get_algo_input_path (string algo_name){
return String.Format(@"{0}{1}/{2}/{3}/",
Configuration.get<bts.config.System>().data_path,
"trade_analysis",
"algos",
"algo_name");
}
private void on_new_book(co2.BookMessage book,
MDEntry entry,
MarketDataIncrementalRefresh md_inc){
ProcessedBook pb = new ProcessedBook();
pb.MsgSeqNum = md_inc.MsgSeqNum;
pb.SendingTime = md_inc.SendingTime;
pb.TransactTime = md_inc.TransactTime;
pb.Security = entry.Symbol;
pb.Book = book;
SecData s;
if(!_book_stream_map.TryGetValue(pb.Security, out s)){
string file_name = pb.Security;
file_name = file_name.Replace(":",".");
file_name = String.Format ("{0}{1}",_book_path,file_name);
var str = new FileStream(file_name, FileMode.OpenOrCreate);
s = new SecData (str);
_book_stream_map.Add(pb.Security,s);
}
pb.BookSeqNum = s.n_updates;
s.inc_count();
Google.Protobuf.MessageExtensions.WriteDelimitedTo(pb,s.str);
}
}
}
@@ -0,0 +1,136 @@
using System;
using System.Linq;
using MathNet.Numerics.Statistics;
namespace bts.history
{
interface IAnalyzer
{
//this data comes in before analysis time, but is useful to create complete books etc
void pre_analyze(MarketDataIncrementalRefresh md, string debug_fix = null);
void analyze(MarketDataIncrementalRefresh md, string debug_fix = null);
}
class QueueAnalyzer : IAnalyzer
{
const double _max_nanos = 200_000.0;
readonly DistributionEstimator _turndist = new DistributionEstimator();
readonly DistributionEstimator _stackdist = new DistributionEstimator();
readonly MathNet.Numerics.Statistics.Histogram _turnhist = new MathNet.Numerics.Statistics.Histogram();
readonly MathNet.Numerics.Statistics.Histogram _stackhist = new MathNet.Numerics.Statistics.Histogram();
MarketDataIncrementalRefresh _lastpacket = new MarketDataIncrementalRefresh();
Google.Protobuf.WellKnownTypes.Timestamp _laststacktime = new Google.Protobuf.WellKnownTypes.Timestamp();
Google.Protobuf.WellKnownTypes.Timestamp _lastturntime = new Google.Protobuf.WellKnownTypes.Timestamp();
MDEntry _lastturn = new MDEntry();
MDEntry _lastentry = new MDEntry();
long _samples = 0;
long _stacksamples = 0;
readonly string _security_desc;
readonly MDEntryType _bid_or_ask;
public QueueAnalyzer(string security_desc) : this(security_desc, MDEntryType.Bid) {}
public QueueAnalyzer(string security_desc, MDEntryType bid_or_ask = MDEntryType.Bid)
{
_security_desc = security_desc;
_bid_or_ask = bid_or_ask;
foreach (var hist in new[] { _turnhist, _stackhist } )
{
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(0, 1000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(1000, 2000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(2000, 3000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(3000, 4000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(4000, 5000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(5000, 6000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(6000, 7000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(7000, 8000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(8000, 9000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(9000, 10000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(10000, 20000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(20000, 30000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(30000, 40000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(40000, 50000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(50000, 60000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(60000, 70000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(70000, 80000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(80000, 90000.0.Decrement()));
hist.AddBucket(new MathNet.Numerics.Statistics.Bucket(90000, _max_nanos.Decrement()));
hist.LazySort();
}
}
public void pre_analyze(MarketDataIncrementalRefresh md, string debug_fix = null)
{
}
public void analyze(MarketDataIncrementalRefresh md, string debug_fix = null)
{
var entries = md.MdEntries.Where(x => x.Symbol == _security_desc &&
(x.MdEntryType == _bid_or_ask) &&
(x.MdPriceLevel == 1)
);
foreach(var x in entries)
{
if( (_bid_or_ask == MDEntryType.Bid && x.MdEntryPx > _lastentry.MdEntryPx) || //new top level bid price greater than old, market turn
(_bid_or_ask == MDEntryType.Offer && x.MdEntryPx < _lastentry.MdEntryPx) ) //new top level ask price less than old, market turn
{
record_data(md, x, ref _lastturntime, _lastpacket.timestamp(), ref _samples, _turndist, _turnhist, "TURN");
_lastturn = x;
}
else if (x.MdEntryPx.AlmostEqual(_lastentry.MdEntryPx))
{
if (x.NumberOfOrders > _lastentry.NumberOfOrders) //price is same, norders increase
{
record_data(md, x, ref _laststacktime, _lastturntime, ref _stacksamples, _stackdist, _stackhist, "STACK");
}
}
_lastentry = x;
_lastpacket = md; //inside the loop so we only get filtered packets
}
}
static void record_data(MarketDataIncrementalRefresh md, MDEntry entry,
ref Google.Protobuf.WellKnownTypes.Timestamp last_time,
Google.Protobuf.WellKnownTypes.Timestamp ref_time, ref long samples,
DistributionEstimator dist, Histogram hist, string desc)
{
if(ref_time == null)
return;
const double sumthresh = 10_000;
const double sumthresh2 = 25_000;
last_time = md.timestamp();
var delta = (md.timestamp() - ref_time);
double nanos = delta.Seconds * 1e9 + delta.Nanos;
if (delta.Seconds == 0 && delta.Nanos > 0 && delta.Nanos <= _max_nanos)
{
if (nanos > 100_000.0 || nanos < 1000)
nanos++; //for breakpoint
dist.add(nanos);
hist.AddData(nanos);
if (samples++ % 10 == 0)
{
Console.WriteLine($"{entry.Symbol} { entry.MdEntryType } Trade date {md.TradeDate.ToDateTime().ToShortDateString()}");
Console.WriteLine($"{desc} Current Sample {nanos}, Max filter { _max_nanos } { Environment.NewLine }");
Console.WriteLine($"{desc} Distribution {dist.ToString()}");
Console.WriteLine($"{desc} Histogram {Environment.NewLine + hist.ToString() }");
Console.WriteLine($"% < {sumthresh} {Environment.NewLine + (sumif(sumthresh, hist) / hist.DataCount).ToString("0.0%") }");
Console.WriteLine($"% < {sumthresh2} {Environment.NewLine + (sumif(sumthresh2, hist) / hist.DataCount).ToString("0.0%") }");
Console.WriteLine($"----------------------------------------------------------------------------- {Environment.NewLine}");
}
}
}
static double sumif(double sumthresh, Histogram hist)
{
double sum = 0.0;
for(int i = 0; i < hist.BucketCount; i++)
{
if(hist[i].UpperBound <= sumthresh)
sum += hist[i].Count;
}
return sum;
}
}
}
+86
View File
@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using bts.api;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
using Entry = System.Tuple<bts.api.SecurityType, bts.history.MarketDataIncrementalRefresh, string>;
namespace bts.history
{
class BackTester
{
readonly DataStreamer _stream;
MarketDataIncrementalRefresh _last = new MarketDataIncrementalRefresh() { SendingTime = new Timestamp(), TransactTime = new Timestamp() } ;
public BackTester()
{
_stream = new DataStreamer();
}
async public Task replay(DateTime start, DateTime end, string symbol, SecurityType st,
HistoricalDataOptions o)
{
start = DateTime.SpecifyKind(start, DateTimeKind.Utc);
end = DateTime.SpecifyKind(end, DateTimeKind.Utc);
var query = new MDQuery
{
Exchange = Venue.Glbx,
Symbol = symbol,
SecurityType = st,
Begin = Timestamp.FromDateTime(start),
End = Timestamp.FromDateTime(end),
};
if(DateTime.TryParse(o.replayskip, out DateTime skip) && skip != DateTime.MinValue)
query.Skip = Timestamp.FromDateTime(DateTime.SpecifyKind(skip, DateTimeKind.Utc));
List<IAnalyzer> anal = new List<IAnalyzer>();
string algo_security = o.algo_security ?? string.Empty;
var t = System.Type.GetType(o.algo_type);
if (t == null)
t = System.Type.GetType("bts.history." + o.algo_type);
if (t != null)
{
anal.Add((IAnalyzer)Activator.CreateInstance(t, new[] { algo_security }));
}
else
anal.Add(new BookAnalyzer(algo_security, o));
int count = 0;
var comparer = Comparer<MarketDataIncrementalRefresh>.Create((x, y)
=> x.timestamp().ToDateTime().CompareTo(y.timestamp().ToDateTime()));
await _stream.process_md_stream<MarketDataIncrementalRefresh>(query, (dt, sym, sectype, md, line) =>
{
if (md.timestamp().ToDateTime() < end)
{
bool preanal = (md.timestamp().ToDateTime() < start);
if(o.skip_preanalysis)
preanal = false;
if(preanal && count++ % 50000 == 1)
Console.WriteLine("Pre analysis " + md.timestamp().ToDateTime());
do_anal(md, anal, preanal, line);
Log.assert(md.timestamp().greater_or_equal( _last.timestamp() ));
_last = md;
}
else
throw new System.IO.EndOfStreamException();
}, comparer);
}
void do_anal(MarketDataIncrementalRefresh md, IEnumerable<IAnalyzer> anal, bool is_before_analysis_time, string debug_fix = null)
{
if (is_before_analysis_time)
{
foreach (var d in anal)
d.pre_analyze(md, debug_fix);
}
else
{
foreach (var d in anal)
d.analyze(md, debug_fix);
}
}
}
}
@@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using bts.api;
namespace bts.history
{
class DataDownloader
{
private static AmazonS3Client _client;
public const string _access_key = "AKIAJ4XUDWPQ7M42352A";
public const string _secret_key = "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";
static DataDownloader()
{
AmazonS3Config config = new AmazonS3Config();
//config.ServiceURL = "http://cme-bts-datamine.s3.us-east-1.amazonaws.com/";
config.RegionEndpoint = Amazon.RegionEndpoint.USEast1;
_client = new AmazonS3Client(_access_key, _secret_key, config);
}
readonly string _prefix;
private static string _path = mbts.config.Configuration.get<bts.config.System>().data_path;
public static string config_data_path => _path;
private string _bucket_name; //http://cme-bts-datamine.s3.us-east-1.amazonaws.com/
public DataDownloader(string prefix = MBOParser.prefix, string bucket_name = "cme-bts-datamine")
{
_prefix = prefix;
_bucket_name = bucket_name;
}
bool is_in_cache(string key)
{
return File.Exists(_path + "/" + key);
}
Stream cached(string key)
{
if (is_in_cache(key))
return new FileStream(_path + "/" + key, FileMode.Open, FileAccess.Read, FileShare.Read);
else
return null;
}
public async Task upload_s3(string key, Stream fs)
{
var trans = new TransferUtility(_client);
try
{
await trans.UploadAsync(new TransferUtilityUploadRequest
{
BucketName = _bucket_name,
Key = key,
InputStream = fs,
AutoCloseStream = false,
AutoResetStreamPosition = true,
});
}
catch (AmazonS3Exception e)
{
bts.Log.Gen.error($"S3 Error encountered ***. Message:'{e.Message}' when writing an object {key}");
}
catch (Exception e)
{
bts.Log.Gen.error($"Unknown encountered on server. Message:'{e.Message}' when writing an object {key}");
}
}
async Task<Stream> download_s3(string key)
{
bts.Log.assert(!is_in_cache(key), $"key {key} already exists in cache!");
bts.Log.Gen.info($"downloading {key} from s3");
try
{
// var checkresp = await _client.ListObjectsAsync(new ListObjectsRequest { BucketName = _bucket_name} );
// foreach (S3Object o in checkresp.S3Objects)
// {
// Console.WriteLine("{0}\t{1}\t{2}", o.Key, o.Size, o.LastModified);
// }
GetObjectRequest request = new GetObjectRequest { BucketName = _bucket_name, Key = key };
using (GetObjectResponse response = await _client.GetObjectAsync(request))
{
string title = response.Metadata["x-amz-meta-title"]; // Assume you have "title" as medata added to the object.
string contentType = response.Headers["Content-Type"];
bts.Log.Gen.info($"Object metadata, Title: {title}");
Console.WriteLine($"Downloading Object metadata, Title: {title} Key { key }");
Console.WriteLine($"Content type: {contentType}");
using (Stream response_stream = response.ResponseStream)
{
var fullpath = _path + "/" + key;
new FileInfo(fullpath).Directory.Create();
FileStream writer = new FileStream(fullpath, FileMode.CreateNew);
response_stream.CopyTo(writer);
writer.Seek(0, SeekOrigin.Begin);
return writer;
}
}
}
catch (AmazonS3Exception e)
{
bts.Log.Gen.error($"S3 Error encountered ***. Message:'{e.Message}' when reading an object {key}");
throw;
}
catch (Exception e)
{
bts.Log.Gen.error($"Unknown encountered on server. Message:'{e.Message}' when reading an object {key}");
throw;
}
}
static string map_mic(api.CMEMics mic)
{
switch (mic)
{
case CMEMics.Cbt:
return "xcbt";
case CMEMics.Cme:
return "xcme";
case CMEMics.Comex:
return "xcec";
case CMEMics.Dme:
return "dme";
case CMEMics.Nymex:
return "xnym";
default:
bts.Log.assert(false, "unknown mic map DataDownloader");
return string.Empty;
}
}
string make_key(DateTime dt, api.CMEMics mic, string symbol, api.SecurityType st)
{
string key = file_name(_prefix, dt, mic, symbol, st);
string date = dt.ToString("yyyyMMdd");
string dir = $"mbo/mbo/{date}/";
return dir + key;
}
public static string file_name(string prefix, DateTime dt, CMEMics mic, string symbol, SecurityType st)
{
string micstring = map_mic(mic);
string date = dt.ToString("yyyyMMdd");
string key = date + "-" + prefix + "_" + micstring.ToLower() + "_" + symbol.ToLower();
if (st == api.SecurityType.Future)
key += "_fut_0-eth.gz";
else if (st == api.SecurityType.Spread)
key += "_fut_1-eth.gz";
return key;
}
public static string data_path_no_config_path(DateTime dt)
{
string date = dt.ToString("yyyyMMdd");
string prefix = $"mbo/mbo/{date}/";
return prefix;
}
public static string data_path(DateTime dt)
{
string p = _path + "/" + data_path_no_config_path(dt);
return p;
}
public async Task<Stream> download(DateTime dt, string symbol, api.SecurityType st)
{
var micconf = mbts.config.Configuration.get<bts.config.CmeSymbolMics>();
var micmap = micconf.cme_mic;
if(micmap.TryGetValue(symbol.ToUpper(), out api.CMEMics themic))
{
return await download(dt, themic, symbol, st);
}
throw new NotSupportedException("Can't find mic for symbol " + symbol);
}
public async Task<Stream> download(DateTime dt, api.CMEMics mic, string symbol, api.SecurityType st)
{
string key = make_key(dt, mic, symbol, st);
return await download(key);
}
async Task<Stream> download_s3_uncompress(string key)
{
var gz = await download(key);
var fullpath = _path + "/" + key.Replace(".gz", "");
FileStream writer = new FileStream(fullpath, FileMode.CreateNew);
using ( GZipStream ungz = new GZipStream(gz, CompressionMode.Decompress))
{
ungz.CopyTo(writer);
writer.Seek(0, SeekOrigin.Begin);
return writer;
}
}
public async Task<Stream> download(string key)
{
if (!is_in_cache(key))
return await download_s3(key);
return cached(key);
}
public async Task<Stream> download_uncompressed(string key)
{
var unkey = key.Replace(".gz", "");
if (!is_in_cache(unkey))
return await download_s3_uncompress(key);
return cached(unkey);
}
public async Task<Stream> download_uncompressed(DateTime dt, string symbol, api.SecurityType st)
{
var micconf = mbts.config.Configuration.get<bts.config.CmeSymbolMics>();
var micmap = micconf.cme_mic;
if (micmap.TryGetValue(symbol.ToUpper(), out api.CMEMics themic))
{
return await download_uncompressed(dt, themic, symbol, st);
}
throw new NotSupportedException("Can't find mic for symbol " + symbol);
}
public async Task<Stream> download_uncompressed(DateTime dt, api.CMEMics mic, string symbol, api.SecurityType st)
{
string key = make_key(dt, mic, symbol, st);
Console.WriteLine($"Looking to download or use Key { key }");
return await download_uncompressed(key);
}
}
}
+171
View File
@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using bts.api.core;
using System.Linq;
using System.Threading.Tasks;
using bts.api;
using Google.Protobuf.WellKnownTypes;
using bts.co2;
namespace bts.history
{
class DataSender
{
readonly DataStreamer _stream;
readonly bts.co2.MarketDataReceiverService _mdservice;
Google.Protobuf.WellKnownTypes.Timestamp _time;
Dictionary<Product, co2.ChannelReply> _channels = new Dictionary<Product, co2.ChannelReply>();
public DataSender(ServiceContext context)
{
_stream = new DataStreamer();
_mdservice = context.locate<bts.co2.MarketDataReceiverService>();
}
bts.api.ResultMessage send()
{
co2.MDReply md = new co2.MDReply();
md.Channels.AddRange(_channels.Values);
var x = _mdservice.push_md(md);
return x;
}
co2.ChannelUpdate create(Product prod, MDEntry md)
{
var chanup = new co2.ChannelUpdate()
{
Key = md.SecurityId, //TODO join with the secdefs that I don't have
Security = new Security()
{
Id = md.SecurityId,
Symbol = prod.Symbol,
Exchange = Venue.Glbx,
ContractSpec = new ContractSpecification()
{
MainFraction = 32,
SubFraction = 2,
PriceBreak = 0,
LowTick = 0.015625,
HighTick = 0.015625,
CashPerPoint = 1000.0,
PriceDisplayFormat = 3,
PriceMultiplier = 1.0,
},
Glbx = new GLBXExchangeData()
{
SecurityDesc = md.Symbol,
ExchangeId = md.SecurityId,
CmeSymbol = prod.Symbol,
}
}
};
if (prod.Type == SecurityType.Future)
{
chanup.Security.Future = new FutureContract()
{
ExpirationDay = 1,
ExpirationMonth = 9,
ExpirationYear = 2019
};
}
else if (prod.Type == SecurityType.Spread)
{
chanup.Security.Spread = new SpreadContract()
{
Normalized = true
};
}
else
System.Diagnostics.Trace.Assert(false, "unknown contract type");
return chanup;
}
void process(Product prod, MarketDataIncrementalRefresh mdr, string fix)
{
if (!_channels.TryGetValue(prod, out co2.ChannelReply chan))
{
chan = new co2.ChannelReply()
{
Platform = co2.Platform.Mdp3Onixs,
Channel = new co2.SecurityChannel()
{
Entitled = true,
Exchange = prod.Exchange,
Type = prod.Type,
Symbol = prod.Symbol,
Flags = (uint)co2.MDRequest.Types.Flags.Direct
},
};
_channels.Add(prod, chan);
}
int count = 0;
foreach (var md in mdr.MdEntries)
{
var upd = chan.Updates.FirstOrDefault(x => x.Key == md.SecurityId);
if (upd == null)
{
upd = create(prod, md);
upd.Direct = new BookMessage();
Utils.init_book(upd.Direct);
chan.Updates.Add(upd);
}
Utils.keep_book(upd.Direct, md);
count++;
if (Utils.quote_event_done( mdr ))
Utils.validate_book(upd.Direct, fix);
}
}
async public Task replay(DateTime start, DateTime end, string symbol, SecurityType st,
HistoricalDataOptions o)
{
int us = 20;//o.dt_us;
var query = new MDQuery
{
Exchange = Venue.Glbx,
Symbol = symbol,
SecurityType = st,
Begin = Timestamp.FromDateTime(DateTime.SpecifyKind(start, DateTimeKind.Utc)),
End = Timestamp.FromDateTime(DateTime.SpecifyKind(end, DateTimeKind.Utc))
};
api.Product prod = new Product() { Exchange = query.Exchange, Type = query.SecurityType, Symbol = query.Symbol };
int count = 0;
await _stream.process_md_stream<history.MarketDataIncrementalRefresh>(query, (dt, sym, sectype, md, line) =>
{
if (md.timestamp().ToDateTime() < end)
{
process(prod, md, line);
maybe_send(md, us);
count++;
}
});
}
void maybe_send(history.MarketDataIncrementalRefresh md, int us)
{
if (_time == null)
{
if (Utils.quote_event_done(md))
{
send(); //send the initial books
_time = md.timestamp();
}
else
return;
}
else
{
var ts = (md.timestamp() - _time).ToTimeSpan();
var diff = ts.Ticks / (TimeSpan.TicksPerMillisecond / 1000);
if (diff > us && Utils.quote_event_done(md))
{
send(); //send the next book
_time = md.timestamp();
}
}
}
}
}
+154
View File
@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using bts.api;
using Symbol = System.String;
namespace bts.history
{
class DataStreamer
{
readonly DataDownloader _dl = new DataDownloader();
public DataStreamer()
{
}
#pragma warning disable CS0183
async public Task process_md_stream<T>(MDQuery request, Action<DateTime, Symbol, SecurityType, T, string> action, IComparer<T> comp = null)
where T : Google.Protobuf.IMessage, new()
{
switch(request.QueryType)
{
case MDQuery.Types.MDQueryType.Tick:
{
var dl = new DataDownloader(prefix: TickParser.prefix);
await _process_md_stream<T, TickParser>(dl, request, action, comp);
}
break;
case MDQuery.Types.MDQueryType.Mbo:
default:
await _process_md_stream<T, MBOParser>(_dl, request, action, comp);
break;
}
}
async static Task _process_md_stream<T, P>(DataDownloader dl, MDQuery request,
Action<DateTime, Symbol, SecurityType, T, string> action, IComparer<T> comp = null)
where T : Google.Protobuf.IMessage, new()
where P : IParser, new()
{
DateTime d = request.Begin.ToDateTime();
while (d <= request.End.ToDateTime())
{
try
{
if(request.SecurityType == SecurityType.Synthetic)
{
var s3f = await dl.download_uncompressed(d, request.Symbol, SecurityType.Future);
var s3s = await dl.download_uncompressed(d, request.Symbol, SecurityType.Spread);
using (P fparser = new P())
{
fparser.init(s3f);
using(P sparser = new P())
{
sparser.init(s3s);
var fut = fparser.next<T>().GetEnumerator();
var spr = sparser.next<T>().GetEnumerator();
bool futgo = fut.MoveNext(), sprgo = spr.MoveNext();
while(futgo || sprgo)
{
if(comp.Compare(fut.Current.Item1, spr.Current.Item1) < 0)
{
action(d, request.Symbol, SecurityType.Future, fut.Current.Item1, fut.Current.Item2.Replace('\u0001', '|'));
if(futgo)
futgo = fut.MoveNext();
}
else if(comp.Compare(fut.Current.Item1, spr.Current.Item1) > 0)
{
action(d, request.Symbol, SecurityType.Spread, spr.Current.Item1, spr.Current.Item2.Replace('\u0001', '|'));
if (sprgo)
sprgo = spr.MoveNext();
}
else
{
action(d, request.Symbol, SecurityType.Future, fut.Current.Item1, fut.Current.Item2.Replace('\u0001', '|'));
if (futgo)
futgo = fut.MoveNext();
action(d, request.Symbol, SecurityType.Spread, spr.Current.Item1, spr.Current.Item2.Replace('\u0001', '|'));
if (sprgo)
sprgo = spr.MoveNext();
}
}
}
}
}
else
{
var s3 = await dl.download_uncompressed(d, request.Symbol, request.SecurityType);
using (P parser = new P())
{
parser.init(s3);
foreach (var (mbo,line) in parser.next<T>())
{
action(d, request.Symbol, request.SecurityType, mbo, line.Replace('\u0001', '|'));
}
}
}
}
catch (Amazon.S3.AmazonS3Exception) { }
catch (System.IO.EndOfStreamException) { }
d = d.AddDays(1.0).Date;
}
}
async public Task process_md_stream_by_line(MDQuery request,
Action<DateTime, Symbol, SecurityType, string, string> action, int peek_field = 0)
{
DateTime d = request.Begin.ToDateTime();
while (d <= request.End.ToDateTime())
{
try
{
if (request.SecurityType == SecurityType.Synthetic)
{
var s3f = await _dl.download_uncompressed(d, request.Symbol, SecurityType.Future);
var s3s = await _dl.download_uncompressed(d, request.Symbol, SecurityType.Spread);
using (MBOParser fparser = new MBOParser(s3f))
{
using (MBOParser sparser = new MBOParser(s3s))
{
var fut = fparser.next_line(peek_field).GetEnumerator();
var spr = sparser.next_line(peek_field).GetEnumerator();
bool futgo = fut.MoveNext(), sprgo = spr.MoveNext();
while (futgo || sprgo)
{
action(d, request.Symbol, SecurityType.Future, fut.Current.Item1, fut.Current.Item2);
if (futgo)
futgo = fut.MoveNext();
action(d, request.Symbol, SecurityType.Spread, spr.Current.Item1, spr.Current.Item2);
if (sprgo)
sprgo = spr.MoveNext();
}
}
}
}
else
{
var s3 = await _dl.download_uncompressed(d, request.Symbol, request.SecurityType);
using (MBOParser parser = new MBOParser(s3))
{
foreach (var (peek, line) in parser.next_line(peek_field))
action(d, request.Symbol, request.SecurityType, peek, line);
}
}
}
catch (Amazon.S3.AmazonS3Exception) { }
catch (System.IO.EndOfStreamException) { }
d = d.AddDays(1.0).Date;
}
}
}
}
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using bts.api;
namespace bts.history
{
class DataUploader
{
private static AmazonS3Client _client;
static DataUploader()
{
AmazonS3Config config = new AmazonS3Config();
//config.ServiceURL = "http://cme-bts-datamine.s3.us-east-1.amazonaws.com/";
config.RegionEndpoint = Amazon.RegionEndpoint.USEast1;
_client = new AmazonS3Client(DataDownloader._access_key, DataDownloader._secret_key, config);
}
private static string _path = _path = mbts.config.Configuration.get<bts.config.System>().data_path;
private string _bucket_name; //http://cme-bts-datamine.s3.us-east-1.amazonaws.com/
public string bucket_name => _bucket_name;
public DataUploader(string bucket_name = "cme-bts-datamine")
{
_bucket_name = bucket_name;
}
public async Task upload_s3(string key, Stream fs)
{
var trans = new TransferUtility(_client);
try
{
await trans.UploadAsync(new TransferUtilityUploadRequest
{
BucketName = _bucket_name,
Key = key,
InputStream = fs,
AutoCloseStream = false,
AutoResetStreamPosition = true,
});
}
catch (AmazonS3Exception e)
{
bts.Log.Gen.error($"S3 Error encountered ***. Message:'{e.Message}' when writing an object {key}");
}
catch (Exception e)
{
bts.Log.Gen.error($"Unknown encountered on server. Message:'{e.Message}' when writing an object {key}");
}
}
}
}
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace bts.history
{
class DistributionEstimator
{
List<double> _vals = new List<double>();
NormalEstimator _norm = new NormalEstimator();
public DistributionEstimator()
{
}
public void add(double x)
{
_vals.Add(x);
_norm.add(x);
}
public void delete(double x)
{
_vals.Remove(x);
_norm.delete(x);
}
public long num_samples => _vals.Count;
public double mean() => _norm.mean();
// {
// if (num_samples == 0)
// return 0.0;
// double sum = _vals.Sum();
// return sum / num_samples;
// }
public double sum_sq()
{
double m = mean();
double sq = 0.0;
for (int i = 0; i < num_samples; i++)
sq += (_vals[i] - m) * (_vals[i] - m);
return sq;
}
public double variance() => _norm.variance();
// {
// if (num_samples <= 1)
// return 0.0;
// return sum_sq() / num_samples;
// }
public double unbiased_variance() => _norm.unbiased_variance();
// {
// if (num_samples <= 1)
// return 0.0;
// return sum_sq() / (num_samples - 1);
// }
public double std_deviation() => _norm.std_deviation();
// {
// return Math.Sqrt(variance());
// }
public double unbiased_std_deviation() => _norm.unbiased_std_deviation();
// {
// return Math.Sqrt(unbiased_variance());
// }
public double percentile(int p)
{
_vals.Sort();
double percentile = p / 100.0;
double n = (_vals.Count - 1) * percentile + 1;
if (n <= 1.0)
return _vals[0];
else if (n >= _vals.Count)
return _vals[_vals.Count - 1];
else
{
int k = (int)n;
double d = n - k;
return _vals[k - 1] + d * (_vals[k] - _vals[k - 1]);
}
}
public override string ToString()
{
var f = percentile(5);
var t = percentile(10);
var tw = percentile(25);
var h = percentile(50);
var s = percentile(75);
var nn = percentile(99);
var sum50 = f + t + tw + h;
return $@"
Normal( mean {mean()}, std_dev {std_deviation()} )
5th { f },
10th { t }
25th { tw }
50th { h }
75th { s }
99th { nn }
[numSamples={ num_samples } ]
";
}
}
}
@@ -0,0 +1,232 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Google.Protobuf.WellKnownTypes;
using MathNet.Numerics.Statistics;
using bts.co2;
using System.Threading.Tasks;
using bts.history;
using mbts.co2;
using bts.api;
namespace bts.history
{
public class FIFOFillModel : FillModelBase, IFillModel
{
readonly Dictionary<ulong, List<MBOMessage>> _exchangeorders =
new Dictionary<ulong, List<MBOMessage>>();
public IEnumerable<MBOMessage> exchange_orders_raw => _exchangeorders.SelectValues(_exchangeorders.Keys).SelectMany(x => x);
readonly protected SortedList<Timestamp, OrderStatus> _myorders =
new SortedList<Timestamp, OrderStatus>(HistoricalExtensions.timestamp_comparer);
public IEnumerable<OrderStatus> my_orders => _myorders.Values;
readonly protected Dictionary<ulong, OrderStatus> _status = new Dictionary<ulong, OrderStatus>();
readonly bool _manual_cross;
public FIFOFillModel(Bucket outgoing_latency, bool manual_cross = false) : base(outgoing_latency)
{
_manual_cross = manual_cross;
}
protected void check_all_cross()
{
foreach (var order in _status)
{
if (!order.Value.is_terminal())
check_cross(order.Value);
}
}
public override void process_book(Timestamp transact_time, string security_desc, BookMessage book)
{
base.process_book(transact_time, security_desc, book);
if(_manual_cross)
check_all_cross();
}
public void record_order_status(OrderStatus stat)
{
if (_disqual.ContainsKey(stat.order.order_id) && !stat.is_terminal())
{
Log.Testing.error($"new status for disqualified error, not recording");
}
else if (!check_cross(stat))
{
int tries = 0; bool done = false;
while (tries++ < 99) //100 nano resolution from CME data makes this sorta ok
{
try
{
_myorders.Add(stat.TransactTime, stat);
done = true;
break;
}
catch (ArgumentException)
{
stat.TransactTime.Nanos += 1;
}
}
if (!done)
throw new ArgumentOutOfRangeException("can't place 100th dupe timestamp");
_status[stat.order.order_id] = stat;
}
}
public void process_order(Timestamp transact_time, MBOMessage mbo)
{
get_mbo(mbo.OrderId).Add(mbo);
}
public void process_trade(Timestamp transact_time, string security_desc, co2.TradeMessage trade_summary,
IEnumerable<ITradeDetail> trades)
{
process_trade(transact_time, security_desc, trade_summary, trades, null);
}
public void process_trade(Timestamp transact_time, string security_desc, co2.TradeMessage trade_summary,
IEnumerable<ITradeDetail> trades, string tag)
{
tag = tag ?? "FIFO";
foreach(var trade in trades)
{
if(!_exchangeorders.TryGetValue(trade.OrderId, out List<MBOMessage> operations))
continue;
if(!operations.Any()) //can't find the order, must have been here forever, they get filled not us
continue;
if(_myorders.Count == 0)
continue;
var market_operations = operations.OrderBy(x => x.TransactTime, HistoricalExtensions.timestamp_comparer).ToList();
var market_operation_index = find_first_greater_or_equal(market_operations, transact_time, x=>x.TransactTime,
HistoricalExtensions.timestamp_comparer);
if(market_operation_index == 0) //transact_time is before first market operation we saw, on this order, they get filled not us
continue;
//this is the latest market operation for the filled order at the time of fill
var curr_market_operation = market_operations[market_operation_index-1];
//now we slide backwards to find the first operation that kept priority at the time of fill
for (int i = market_operation_index - 2; i >= 0; i--)
{
if(market_operations[i].MdOrderPriority == curr_market_operation.MdOrderPriority)
{
curr_market_operation = market_operations[i];
}
}
if(curr_market_operation.OrderUpdateAction == MDUpdateAction.Delete)
Log.Testing.error($"deleted order {curr_market_operation.OrderId} filled");
//now we find the first operation for our order greater or equal to the transact time
int our_operation_index = find_first_greater_or_equal(_myorders, transact_time, HistoricalExtensions.timestamp_comparer);
if(our_operation_index == 0) //transact_time before I'm in the market, they get filled not us
continue;
string market_secdesc = curr_market_operation.SecurityDesc;
api.Side market_side = Utils.same_side(curr_market_operation.MdEntryType);
List<FillEntry> fills = new List<FillEntry>();
uint fill_qty = trade.LastQty;
Dictionary<ulong, OrderStatus> latest_state = new Dictionary<ulong, OrderStatus>();
for(int i = our_operation_index - 1; i >= 0; i--)
{
//the closest operation to this trade time
OrderStatus curr_operation = _myorders.Values[i];
if(curr_operation.order.security_desc != market_secdesc)
continue;
if(curr_operation.order.security_desc != security_desc)
continue;
if(curr_operation.order.side != market_side)
continue;
//this order is completely spent, disqualify it from future fills in case a the algo behind it tries to keep updating,
//thus creating new states ahead of this termnal state
if(_disqual.ContainsKey(curr_operation.order.order_id))
continue;
if (curr_operation.is_terminal())
{
_disqual[curr_operation.order.order_id] = 0;
continue;
}
//when we begin searching, we only want to consider the latest valid, non terminal state of our order
//FOR THIS ONE SEARCH, this is to prevent the case where an old state at a previous price is erronenously filled
if(latest_state.ContainsKey(curr_operation.order.order_id))
continue;
latest_state[curr_operation.order.order_id] = curr_operation;
//If our operation did not occur before the market's we are not filled
if (!curr_operation.TransactTime.less_or_equal(curr_market_operation.TransactTime))
continue;
//here we have the latest status of an order on the right side of the market for the right security and the right price
if ( (market_side == api.Side.Buy && curr_operation.state.Price >= trade_summary.price) || //fill us if we are better?
(market_side == api.Side.Sell && curr_operation.state.Price <= trade_summary.price) )
//this has to be be trade price, not mdentryprx because cme will report orders down in the book that suddenly aggress
{
uint size = Math.Min(fill_qty, curr_operation.state.Quantity);
if (size > 0)
{
FillEntry f = generate_fill(transact_time, curr_operation, curr_operation.state.Price, size, null, tag);
f.fill_id = trade.OrderId;
// if(market_side == Side.Buy)
// f.bid = trade_summary.price;
// else
// f.ask = trade_summary.price;
f.total_traded = trade_summary.Size;
f.num_orders = curr_market_operation.NumberOfOrders;
bool same = curr_operation.order.side == Utils.same_side(trade_summary.AggressorSide);
if(same)
f.tag = $"{tag} agg side same {curr_operation.order.side}";
if(f.fully_filled)
{
f.tag = $"{tag} {f.order.order_id} FULLFILL " + (same ? $"agg side same {curr_operation.order.side}" : "");
}
fills.Add(f);
fill_qty -= size;
}
if (fill_qty <= 0)
{
Log.assert(fill_qty == 0);
break;
}
}
}
}
}
static int binary_search<T,U>(IList<U> list, T value, Func<U, T> selector, Comparer<T> comparer = null)
{
if (list == null)
throw new ArgumentNullException("list");
var comp = Comparer<T>.Default;
if (comparer != null)
comp = comparer;
if(selector == null)
selector = (x => (T)(object)x);
int lo = 0, hi = list.Count - 1;
while (lo < hi)
{
int m = lo + (hi - lo) / 2;
if (comp.Compare( selector(list[m]), value) < 0) lo = m + 1;
else hi = m - 1;
}
if (comp.Compare( selector(list[lo]), value) < 0) lo++;
return lo;
}
static int find_first_greater_or_equal<T,U>(IList<U> sorted_list, T key, Func<U, T> selector, Comparer<T> comp)
{
return binary_search(sorted_list, key, selector, comp);
}
static int find_first_greater_or_equal<T, U>(SortedList<T, U> sorted_list, T key, Comparer<T> comp)
{
return binary_search(sorted_list.Keys, key, x => x, comp);
}
List<MBOMessage> get_mbo(ulong id)
{
if (!_exchangeorders.TryGetValue(id, out List<MBOMessage> list))
{
_exchangeorders[id] = list = new List<MBOMessage>();
}
return list;
}
}
}
@@ -0,0 +1,41 @@
using System;
using Google.Protobuf.WellKnownTypes;
namespace bts.history
{
public class FillEntry : IHasTimestamp
{
static uint _fillid = 0;
public FillEntry(Timestamp ts, OrderEntry order, api.Side side, double price, ulong size)
{
this.TransactTime = ts;
this.SendingTime = ts;
this.order = order;
this.side = side;
this.price = price;
this.size = size;
this.fill_id = ++_fillid;
}
public Timestamp TransactTime { get; set;}
public Timestamp SendingTime { get; set;}
public OrderEntry order { get; }
public api.Side side { get; }
public double price { get; }
public ulong size { get; }
public bool fully_filled { get; set; }
public uint total_traded { get; set; }
public uint num_orders { get; set; }
public ulong fill_id { get; set; }
public uint bidsize { get; set; }
public double bid { get; set; }
public uint asksize { get; set; }
public double ask { get; set; }
public string tag { get; set; }
public override string ToString()
{
return $"{TransactTime.ToDateTime().ToLongDateString()} {TransactTime.ToDateTime().ToString("H:mm:ss.ffffff")} {order.tag ?? "Our "} {order.security_desc} {side} {price} {size} -- {bidsize} {bid} at {ask} { asksize } {tag}";
}
}
}
Loaded 100 of 338 files, more files were not shown because too many files have changed in this diff. Show more