Compare commits

..
Author SHA1 Message Date
Taha Afzal 843f42c3e3 Merge branch 'trade_analysis' of bitbucket.org:bluetradesys/spark into trade_analysis 2020-04-06 16:00:24 -04:00
Taha Afzal 3afc3d64b6 changed the keys 2020-04-06 16:00:12 -04:00
pfisher 44f2cdd7c0 change aws keys 2020-03-31 11:00:26 -05:00
Taha Afzal 3f36f54781 Added TopBooks to ProcessedBook 2020-03-03 17:09:56 -05:00
Taha Afzal 59e66cf1ef Sniper reads from multiple files recursively 2020-03-02 18:32:19 -05:00
Taha Afzal dec178f133 ignoring all trades that are not making sense 2020-03-02 09:46:10 -05:00
Taha Afzal e9c9a3bad6 Function to get orders for both sides of the trade 2020-03-02 09:46:10 -05:00
Taha Afzal 46a4aa99df Added quote order marketat the time the quote order was updated 2020-03-02 09:46:10 -05:00
Taha Afzal 1097bbcd60 Q leg display 2020-03-02 09:46:10 -05:00
Taha Afzal 840ba7e0b3 Added total qty and no of orders 2020-03-02 09:46:10 -05:00
Taha Afzal 7e7e88cedc Proximity search bug 2020-03-02 09:46:10 -05:00
Taha Afzal 727a3d5b97 Cleaning up some mess 2020-03-02 09:46:10 -05:00
Taha Afzal 3044d5d5ec Bin searching for trades 2020-03-02 09:46:10 -05:00
Taha Afzal 506084242a Refinement of orders/trades 2020-03-02 09:46:10 -05:00
Taha Afzal 067946127a cleaning up 2020-03-02 09:46:10 -05:00
Taha Afzal 297848bcdb Re-org of passive quote detection 2020-03-02 09:46:10 -05:00
Pedro Pinto 376d64cfbe updated db script to incorporate timestamps 2020-02-06 10:07:14 -05:00
Pedro Pinto bdf3ae7c92 Added unix timestamps to csvs 2020-02-06 09:22:22 -05:00
Pedro Pinto 97d0637d88 Updated tools 2020-02-04 09:27:19 -05:00
Pedro Pinto b4d85cdadf Merge branch 'trade_analysis' of bitbucket.org:bluetradesys/spark into trade_analysis 2020-02-04 09:24:40 -05:00
Pedro Pinto 2b0cb84069 Changed synchronization script to default to synching yesterday 2020-02-04 09:23:25 -05:00
Pedro Pinto 0412c45ed3 Removed build step 2020-02-03 15:48:20 -05:00
Pedro Pinto b984613b8a Non interactive use of postgres 2020-02-03 15:23:21 -05:00
Pedro Pinto e712433d6f More tweaks. 2020-02-03 15:21:27 -05:00
Pedro Pinto 4d8b20d084 Fixed path in postgres sql. 2020-02-03 15:14:08 -05:00
Pedro Pinto df86187457 Tool tweaks 2020-02-03 14:56:01 -05:00
Pedro Pinto 620ea4b4af Tweaked date tool 2020-02-03 13:56:21 -05:00
Pedro Pinto e2951b4436 Added pgadmin tool 2020-02-03 10:12:32 -05:00
Pedro Pinto 0ebc3cf111 Fixed a few issue with analysis tools 2020-02-03 09:58:39 -05:00
Pedro Pinto fd9b2990ae Added easier to use tools for analysis 2020-01-31 16:28:18 -05:00
Pedro Pinto 15d24f96c2 Updated externals, private tools 2020-01-31 16:28:18 -05:00
Pedro Pinto b1bbc6ab0d Updated docker 2020-01-31 16:28:17 -05:00
Pedro Pinto 39be9e41c8 Rewound externals because tip is broken 2020-01-31 16:28:17 -05:00
Pedro Pinto fd91b78742 sync/parse/load tools 2020-01-31 16:28:17 -05:00
Pedro Pinto 95a5dd0ceb updated docker 2020-01-31 16:28:17 -05:00
Pedro Pinto a9aa76deed Updated gitignore and externals 2020-01-31 16:28:17 -05:00
Taha Afzal a80a4f327e Fixed theoutput data zero fills 2020-01-31 13:51:52 -05:00
Taha Afzal 7e9a853a50 filtering books 2020-01-31 11:38:11 -05:00
Taha Afzal 0e0d4412b8 Merge branch 'trade_analysis' of bitbucket.org:bluetradesys/spark into trade_analysis 2020-01-30 22:45:06 -05:00
Taha Afzal b3a19eab05 Filtering quote fills for orders 2020-01-30 22:44:42 -05:00
Pedro Pinto fdb1826e01 Added a tool to download cme historical data 2020-01-30 13:27:02 -05:00
Taha Afzal e2d6bc1c45 Order parsing 2020-01-29 19:43:36 -05:00
Taha Afzal d5a06cc3e7 circle filling in 2020-01-27 22:58:22 -05:00
Taha Afzal 3e7c2e3c18 circle analysis 2020-01-22 11:50:49 -05:00
Taha Afzal ea6eaf6e1d contracts show in entry:exit 2020-01-21 10:40:11 -05:00
Taha Afzal dea2358c51 cleaned up some wrappers to simplify the code 2020-01-17 18:43:08 -05:00
Taha Afzal 491d0c0b7a no exponent mapping 2020-01-17 11:50:06 -05:00
Taha Afzal f593b457a5 Market close times factored in 2020-01-17 11:25:26 -05:00
Taha Afzal 10a3ba52f4 Added max processing time 2020-01-16 11:03:27 -05:00
Taha Afzal 56bc7f216a Parallelized the sniper analysis across file (in batches) 2020-01-15 22:11:04 -05:00
Taha Afzal a57b99b2e8 BEtter formatting of output 2020-01-14 14:49:40 -05:00
Taha Afzal d3a0595934 Cleaning 2020-01-14 10:54:55 -05:00
Taha Afzal a659fbc7cd Sniper analysis works on multiple files now 2020-01-14 09:32:08 -05:00
Taha Afzal 542b8e946d mass sniper analysis 2020-01-13 11:16:06 -05:00
Taha Afzal d4d1f8f533 download args changed 2020-01-12 18:18:56 -05:00
Taha Afzal 624a432c44 ignoring ticks from when the market is closed 2020-01-12 13:13:05 -05:00
Pedro Pinto 68f09c2e27 Fixed crash when the timen take to parse was rounded to 0 secs. 2020-01-11 18:45:36 -05:00
Taha Afzal 3d22a58bac ParseHistoricalFIX takes a path and processes all files with name _raw in it 2020-01-10 19:19:52 -05:00
Taha Afzal 33e46b2fb1 Batch download 2020-01-10 11:14:25 -05:00
Taha Afzal cb357440ed cleaned up some old code 2020-01-09 14:12:04 -05:00
Taha Afzal a88670b95d Added headers to the output 2020-01-09 13:22:39 -05:00
Taha Afzal bb70db70b5 Better output generation 2020-01-09 11:28:41 -05:00
Taha Afzal 2c3e4b7d4d loading and validating 2020-01-06 20:04:45 -05:00
Taha Afzal 843c4f447f Merge branch 'trade_analysis' of bitbucket.org:bluetradesys/spark into trade_analysis 2020-01-03 17:07:26 -05:00
Taha Afzal d41c76e6a3 validatio 2020-01-03 17:06:55 -05:00
Pedro Pinto 805722cdc7 Fixed the calculation of the book in the historical data parser. 2020-01-03 16:07:32 -05:00
Pedro Pinto ef64d3c25e Fixed weird auto&& in HandleIndex.h 2020-01-03 12:27:52 -05:00
Pedro Pinto 0b0cf744a6 Added more information to parser 2020-01-03 12:22:44 -05:00
Pedro Pinto c3410db0aa Fixed clang build. 2020-01-03 12:22:44 -05:00
Eric Harding c5a89b0bb6 fix a couple clang warnings 2020-01-03 11:45:44 -05:00
Taha Afzal 89f5f94588 validation 2020-01-02 19:39:50 -05:00
Taha Afzal c8ac56b0a1 Merge branch 'trade_analysis' of bitbucket.org:bluetradesys/spark into trade_analysis 2020-01-02 19:33:15 -05:00
Pedro Pinto b152b2b853 Added a parser of historical MD to facilitate analysis. 2020-01-02 14:01:57 -05:00
Pedro Pinto 354390ed11 Fixed clang build. 2020-01-02 14:01:48 -05:00
Taha Afzal 08839aadb2 book validation code 2020-01-01 22:03:23 -05:00
Taha Afzal b7cb17500c Downloading new book data 2019-12-31 18:32:45 -05:00
Taha Afzal 01b50e1138 Added function skeleton to compure possible fills 2019-12-30 19:33:04 -05:00
Taha Afzal 8fdb7485ba new order and trade protos 2019-12-30 19:22:40 -05:00
Taha Afzal 293d51038f Circle spreads initial analysis 2019-12-27 18:55:54 -05:00
Taha Afzal 8db81fa248 countering for the trades and orders files 2019-12-27 10:27:18 -05:00
Taha Afzal 47628089ac Recording trades and orders 2019-12-26 21:08:49 -05:00
Taha Afzal 8b2c5a60aa Added NG exp 2019-12-24 12:41:35 -05:00
Taha Afzal f1df673e61 spread parsing gix, Z29 showed up 2019-12-23 18:43:14 -05:00
Taha Afzal 9faee77360 merging vwap 2019-12-23 16:38:58 -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
Kevin Darby 198086e639 got roll going 2019-12-18 13:16:49 -05: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
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
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
Taha Afzal 17a1190e34 added exponents for HE,ZC 2019-12-13 18:16:13 -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
Taha Afzal 82834dc9e6 Parallelized the processing of file for sniper 2019-12-13 14:46:30 -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
Taha Afzal 2ff96e3528 more helper functions 2019-12-13 12:37:17 -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
Taha Afzal 74f1bf2480 functions to remove duplicates 2019-12-12 11:35:19 -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
Taha Afzal 4e48bcf6b5 refactoring 2019-12-11 13:33:07 -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
Taha Afzal 6fd829df49 Silly bug that was overwriting the lined up markets 2019-12-11 11:49:30 -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
Taha Afzal 0dcb3d3dfd Added microscope for a closer look! 2019-12-08 21:53:39 -05:00
Kevin Darby 8817b22376 write book with trades 2019-12-08 14:37:48 -05:00
Taha Afzal 212f2a3d36 can run the sniper on one synthetic only, too 2019-12-08 10:39:04 -05:00
Kevin Darby b48da514e5 I think we got it 2019-12-08 00:18:25 -05:00
Taha Afzal 649225ef6c unifying the spread calc function 2019-12-07 22:55:18 -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
Taha Afzal 35495dc7cf better formatting 2019-12-06 20:17:14 -05:00
Kevin Darby 5b1f2522e1 found bug in synth processor, won't build until I fix 2019-12-06 18:29:58 -05:00
Taha Afzal 8ee78c83d2 writes result to a file: 2019-12-06 18:19:29 -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
Taha Afzal b4beb7c0db moved BookEntry and TimeRange into protos 2019-12-06 13:19:03 -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
Taha Afzal f84517c15c cleaning up some mes 2019-12-05 17:32:05 -05:00
Taha Afzal 075354a975 Sniper analysis works on generated spreaders 2019-12-05 11:08:45 -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
Taha Afzal 4450476a40 simplified lining up of books 2019-12-04 09:49:26 -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
Taha Afzal 3b0bbe6908 simplified 2019-12-02 13:09:11 -05:00
Taha Afzal 1147edcbb9 tests 2019-12-02 12:44:10 -05:00
Kevin Darby e1c7b89408 mongo 2019-12-01 21:38:55 -05:00
Taha Afzal c50133ba02 tests 2019-12-01 16:17:14 -05:00
Taha Afzal f1dbacb989 forcing an exponent to fix the prices 2019-11-29 16:43:21 -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
Taha Afzal 0535eedbbf merging 2019-11-29 15:34:44 -05:00
Kevin Darby 45ae6ac585 parsing this data needs to get faster 2019-11-29 13:33:06 -05:00
Taha Afzal 2740de913c BookState is now called BookSeries 2019-11-29 09:47:46 -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
Taha Afzal ba53543e43 Parsing the sniper input file correctly 2019-11-27 18:07:59 -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
548 changed files with 118434 additions and 9472 deletions

No files matched your search

+3
View File
@@ -1,5 +1,7 @@
nohup.out
build/
build
cme_data/
cmake-build-debug/
.idea/
.dir-locals.el
@@ -35,3 +37,4 @@ trace_data/
.ionide/*
/.ignore
/build
/analysis_data
+3 -8
View File
@@ -5,9 +5,7 @@
"includePath": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}",
"${workspaceFolder}/src",
"${workspaceRoot}/externals/linux/include"
"${workspaceRoot}"
],
"defines": [],
"intelliSenseMode": "clang-x64",
@@ -15,16 +13,13 @@
"path": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}",
"${workspaceFolder}/src",
"${workspaceRoot}/externals/linux/include"
"${workspaceRoot}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"cStandard": "c11",
"cppStandard": "c++17",
"compileCommands": "${workspaceFolder}/build/ninja_debug/compile_commands.json"
"cppStandard": "c++17"
},
{
"name": "Windows",
+130 -8
View File
@@ -46,7 +46,7 @@
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
}
},
},
{
"name": "(gdb) Launch",
"type": "cppdbg",
@@ -98,6 +98,46 @@
"externalConsole": true,
"requireExactSource": false
},
{
"name": "sparkcmd Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": [
"historical",
"--rstart", "11/20/2019",
"--rend", "11/29/2019",
"--rsymbol", "NG",
"--rsecuritytype", "Synthetic",
"--post_process_db", "NG_201911_W4",
"--post_process_db_host", "bigblue",
],
"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",
@@ -131,6 +171,18 @@
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"name": ".NET Framework Attach",
"type": "clr",
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"name": "Spreader Tests",
"type": "cppdbg",
@@ -342,15 +394,19 @@
"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 +417,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",
},
]
}
+10 -19
View File
@@ -14,21 +14,24 @@
"focus": false,
"panel": "shared"
},
"group": "build"
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "server_build",
"label": "server_build",
"type": "shell",
"command": ".\\tools\\buildsparkd.cmd",
"problemMatcher": {
"base": "$gcc",
"problemMatcher": {
"base": "$gcc",
"owner": "cpp",
"fileLocation": [
"relative",
"${workspaceFolder}"
"${workspaceFolder}"
],
"pattern": {
"regexp": "(.?)(home)(.?)(bts)(.?)(spark)(.?)(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)",
"regexp": "(.?)(home)(.?)(bts)(.?)(spark)(.?)(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)",
"file": 8,
"line": 9,
"column": 10,
@@ -46,18 +49,6 @@
"kind": "build",
"isDefault": true
}
},
{
"label": "linux build (Debug)",
"type": "shell",
"command": "cd build/ninja_debug; time ninja",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$gcc"
]
}
}
]
}
+11 -1
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,16 @@ 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)
DEPENDS sparkd data_files_dir spark_client_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.0/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.0/linux-x64/publish/sparkcmd ${CMAKE_BINARY_DIR}/server/ )
endif()
endif()
+1 -1
View File
@@ -1,6 +1,6 @@
+++
title="Client API"
redirect_to = "client/getting-started/overview/"
template="client.html"
sort_by="weight"
+++
@@ -1,5 +1,6 @@
+++
title="Getting Started"
template="client.html"
sort_by="weight"
weight = 1
+++
@@ -1,25 +1,9 @@
+++
title = "Install Prerequisites"
weight = 10
template="client.html"
weight = 1
+++
# C#/.NET
We target [.NET Standard 2.0](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) so you can use .NET Framework 4.6.1+ or .NET Core 2.0+. Simply add a reference to the [Spark Nuget package](https://www.nuget.org/packages/spark_sdk/) to get started.
```
dotnet new console -lang c#
dotnet add package spark_sdk
dotnet build
```
# C++
The easiest way to get started with C++ is to use the Spark Server SDK to develop your application. Follow the instructions to [download and install the Spark Server SDK](/server/getting-started/install) and open the client_sdk sample project. If you want to run your C++ Client SDK application on Windows [contact us](mailto://api@bluetradesys.com) to get the required headers and binaries.
# JavaScript
We use JavaScript internally with the Spark Client API over a websocket connection but the documentation and packaging isn't ready yet. If this is something you're interested in get in touch with us at [api@bluetradesys.com](mailto:api@bluetradesys.com).
## Coming Soon
* Install .net core
@@ -1,31 +0,0 @@
+++
title = "Overview"
weight = 1
+++
The Spark Client SDK is a high level command and control API which can be used to build scripts or integrate Spark into your existing ecosystem. The API is divided into several independent **"services"** responsible for different aspects of the system. A Service can define both [remote procedure call (RPC)](/client/getting-started/services#rpc) and incremental [streaming](/client/getting-started/services#streams) data.
The [Spark Desktop](https://www.bluetradingsystems.com/bts-spark/) is built on top of this API so you can be assured that it is complete and well tested.
Spark uses [Google Protocol Buffers](https://developers.google.com/protocol-buffers/) as an interface definition language similar to [GRPC](https://grpc.io) but backed by our custom high-performance TCP protocol. We currently support C#/.NET and C++ <!--and Javascript--> but the architecture is designed for easy extension so contact us about your specific business needs.
## Orders
Use the [OrderService](/client/services/orderservice) to send and manage orders including native exchange orders, spreader orders, and custom Algo orders created using the [Spark Server SDK](https://api.bluetradesys.com/server/).
## Spreader
Use the [SyntheticSecurityService](/client/services/syntheticsecurityservice) to create and manage synthetic spreads. Once created these spreads show up in the security and market data feeds just like exchange listed spreads.
## Position
Use the [PositionService](/client/services/positionservice) to get your position and P&L reports or add fills manually to keep the Spark system up to date with an external system.
## Administration
Use the [AdminService](/client/services/adminservice) to manage users and control limits.
## Market Data
For .NET subscribe to market data though the high level MarketDataClient interface. From C++ or JavaScript use the MarketDataRelay service directly.
@@ -1,127 +0,0 @@
+++
title = "Services"
weight = 20
+++
The Spark Client SDK uses [Google Protocol Buffers](https://developers.google.com/protocol-buffers/) to define the interface for each service in a language independent manner. Services have both functions which return a single result and streams of data which return values continuously while subscribed. This page describes the mapping from the Protocol Buffer definition to the concrete definition in each supported language.
# ServiceContext
To connect your client to the Spark Server you need to get or create a `ServiceContext` instance. A `ServiceContext` represents a connection to the Spark Server and a background thread for handling messages on that connection. Generally there should be a single `ServiceContext` per process. All of the methods on the `ServiceContext` are safe thread safe.
Construct a ServiceContext by calling the default constructor. The ServiceContext will find the Spark Server by loading the server configuration over an HTTP connection. To determine the URL it will search the following locations and use the first one that is present.
* A command line argument `-configserver <url>`
* An environment variable `BTSCONFIG`
* `%APPDATA%/bts/conf/spark.conf` (Windows)
* /sparkdata/conf/spark.conf (Linux)
* $HOME/sparkdata/conf/spark.conf (Linux)
If your application is running on a system with the BTS Client installed this will probably work without any further configuration. If you plan to run your client application on a separate machine we recommend setting the `BTSCONFIG` environment variable.
<div class="info">
<i class="fa fa-lightbulb-o fa-fw" aria-hidden="true"></i>
If you are using the Spark Server SDK to run a local copy of the Spark server set <p><strong>BTSCONFIG = http://localhost:9000/config/</strong></p>
</div>
To get a proxy for a given service call ServiceContext.locate&lt;T>("qualifier") where T is the type of the service and "qualifier" is an optional qualifier used to disambiguate between 2 instances of the same service. The locate call cannot fail but it may return a disconnected proxy. When you make a call on the proxy you should check the call result to see if the method call succeeded.
# RPC
Here is the OrderService reduced to a single method definition. A RPC method always takes exactly 1 argument and returns 1 value. In this example the `create_order` call takes a `Create` instance and returns a `Response`.
```C
service OrderService {
rpc create_order(Create) returns (Response);
...
}
```
The `Create` and `Response` types are also defined as protocol buffer messages. Each field can either be a primitive type another message or a list or map.
```C
message Create {
OrderDefinition new_order = 1;
double price = 2;
uint32 quantity = 3;
}
message Response {
uint32 order_key = 1;
OrderError error_code = 2;
string msg = 3;
}
```
Each RPC call generates both synchronous (blocking) and asynchronous variants. In C# the asynchronous calls have the `_async` suffix and return a `Task`. In C++ the call is overloaded to either return a value synchronously or accept a callback function to be called on the ServiceContext thread when the call completes. <!-- In Javascript only async methods returning a Promise are generated because it never makes sense to make a blocking call in the browser. -->
To call the resulting service locate a proxy using the [ServiceContext](#servicecontext) and then call `cancel_order` or `cancel_order_async`. If you are calling the Spark API from a GUI application you should always prefer the `_async` version of the calls.
### C# example
```c#
var ctx = new ServiceContext();
var orders = ctx.locate<OrderService>();
var response =
await orders.create_order_async(
new Create { price = 2.25, quantity = 42 });
if (response.success) {
Console.WriteLine($"success: {response.value}");
}
else {
Console.WriteLine($"Error: {response.error_message}");
}
```
### C++ example
```cpp
auto ctx = ServiceContext{};
auto orders = ctx.locate<OrderService>();
Create c;
c.set_price(2.25);
c.set_quantity(42);
// Instead of providing an _async method C++ uses overloading.
// To make a syncronous call simply omit the callback
orders.create_order(c, [=](const Result<Response>& response) {
if(response.success()) {
std::cout << "success.";
}
else {
std::cout << "error: " << response.error_message();
}
});
```
# Streams
Streams allow you to subscribe to live updating data. Each stream represents a changing collection of protocol buffer objects. Similar to a database row each item has an `id` which uniquely identifies it within the collection. Updates from the Spark server can add, remove or replace entries in the collection. Since only the changed items are sent over the wire the stream can remain responsive even when it represents a large collection.
Streams are implemented using the language bindings for [ReativeX](http://reactivex.io/) which provide utility functions to drill down to the specific data your application needs. In C# we provide both mutable and immutable versions of the generated data types (C++ users can rely on `const`).
When defined in a proto file streams use the `stream` keyword on the result type.
```C
service OrderService {
...
rpc fills (FillsSub) returns (stream Fill);
}
```
### C# example
```c#
var sc = new ServiceContext();
var orders = sc.locate<OrderService>();
var fills_observable = orders.subscribei_fills();
var unsub = fills_observable.Subscribe(update => {
Console.WriteLine(
"Added {0} items, Removed {1} items, Updated {2} items. Total items {3}",
update.added.Count(),
update.removed.Count(),
update.changes.Count(),
update.all.Count());
});
Thread.Sleep(TimeSpan.FromMinutes(5));
unsub.Dispose();
```
### C++ Example
```cpp
```
-6
View File
@@ -1,6 +0,0 @@
+++
title="Samples"
sort_by="weight"
weight = 30
+++
@@ -1,3 +0,0 @@
+++
title = "AdminService"
+++
@@ -1,3 +0,0 @@
+++
title = "OrderService"
+++
@@ -1,3 +0,0 @@
+++
title = "PositionService"
+++
@@ -1,3 +0,0 @@
+++
title = "SyntheticService"
+++
@@ -1,6 +0,0 @@
+++
title="Services"
#sort_by="weight"
weight = 10
+++
@@ -1,3 +0,0 @@
+++
title = "Market Data"
+++
+1
View File
@@ -1,5 +1,6 @@
+++
title="Algo API"
template="documentation.html"
redirect_to = "server/getting-started/overview/"
sort_by="weight"
+++
@@ -1,5 +1,6 @@
+++
title="Getting Started"
template="documentation.html"
sort_by="weight"
weight = 1
+++
@@ -71,12 +71,12 @@ The first step is to start the spark_sdk Docker container with the examples dire
Windows Command Prompt:
{% clipable() %}
docker run --rm -it -v %CD%:/examples -u bts -w /examples --shm-size 2048m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
docker run --rm -it -v %CD%:/examples -u bts -w /examples --shm-size 1024m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
{% end %}
**Powershell** / Linux / Mac:
{% clipable() %}
docker run --rm -it -v ${PWD}:/examples -u bts -w /examples --shm-size 2048m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
docker run --rm -it -v ${PWD}:/examples -u bts -w /examples --shm-size 1024m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
{% end %}
### Attach
@@ -5,7 +5,7 @@ weight = 2
## Request Access 🔑
To start using the Server SDK you will need to request access though our sales department. Please email <a href="mailto:api@bluetradesys.com">api@bluetradesys.com</a> or call <a href="tel:919-913-0850">(919) 913-0850</a> and we will provide download credentials.
To start using the Server SDK you will need to request access though our sales department. Please email <a href="mailto:info@bluetradesys.com">info@bluetradesys.com</a> or call <a href="tel:919-913-0850">(919) 913-0850</a> and we will provide download credentials.
## Download
File renamed without changes.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

+1
View File
@@ -1,5 +1,6 @@
+++
title="Reference"
template="documentation.html"
sort_by="weight"
weight = 2
+++
-6
View File
@@ -6,9 +6,3 @@ weight = 40
## I need to make a call that might take a few seconds. What is the best way to do this?
Create a background thread in [do_before_launch](/server/ref/initialization#before-launch) or [do_launch](/server/ref/initialization#launch-launch-attached) to do the work. Use [on_timer](/server/ref/subscriptions#timer) to poll until the result is available.
## I don't see the header files in the .zip file. Where are they?
The SDK headers are baked into the docker image along with the build tools. Once you've loaded the image with `docker load < spark_sdk.img` you can run this command to copy them out to your local file system `docker run --rm -it -v $PWD:/out spark_sdk /bin/tar -cvf /out/headers.tar /sdk/include` This will copy the headers to the current directory in headers.tar. We still recommend doing the actual build inside the docker environment but having a copy of the headers can be useful for setting up your development environment.
+1
View File
@@ -1,5 +1,6 @@
+++
title="Samples"
template="documentation.html"
sort_by="weight"
weight = 3
+++
+31
View File
@@ -0,0 +1,31 @@
{% extends "index.html" %}
{% block extra_nav_class %}{% endblock extra_nav_class %}
{% block extra_content_class %}content--reversed{% endblock extra_content_class %}
{% block content %}
{% set section = get_section(path="client/_index.md") %}
<div class="documentation">
<aside class="documentation__sidebar">
<ul>
{% for p in section.subsections %}
{% set subsection = get_section(path=p) %}
<li>
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
<ul>
{% for page in subsection.pages %}
<li class="{% if current_path == page.path %}active{% endif %}">
<a href="{{page.permalink}}">{{page.title}}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</aside>
<div class="documentation__content">
{% block doc_content %}
{% endblock doc_content %}
</div>
</div>
{% endblock content %}
+36
View File
@@ -0,0 +1,36 @@
{% extends "index.html" %}
{% block extra_nav_class %}{% endblock extra_nav_class %}
{% block extra_content_class %}content--reversed{% endblock extra_content_class %}
{% block content %}
{%if page.extra.section %}
{% set section = get_section(path="{{page.extra.section}}/_index.md") %}
{% else %}
{% set section = get_section(path="server/_index.md") %}
{% endif %}
<div class="documentation">
<aside class="documentation__sidebar">
<ul>
{% for p in section.subsections %}
{% set subsection = get_section(path=p) %}
<li>
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
<ul>
{% for page in subsection.pages %}
<li class="{% if current_path == page.path %}active{% endif %}">
<a href="{{page.permalink}}">{{page.title}}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</aside>
<div class="documentation__content">
{% block doc_content %}
{% endblock doc_content %}
{% block next_link %} {% endblock next_link %}
</div>
</div>
{% endblock content %}
+15 -15
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en-us">
<html lang="en-gb">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -14,7 +14,6 @@
<link rel="stylesheet" href="{{ get_url(path="site.css") }}"/>
<link rel="icon" href="{{ get_url(path="favicon.ico") }}">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script> dataLayer = [{ 'visitorType': 'geek' }]; </script>
</head>
<body>
@@ -37,13 +36,13 @@
</nav>
</header>
<div class="content content--reversed">
<div class="content {% block extra_content_class %}{% endblock extra_content_class %}">
{% block content %}
<div class="hero">
<h1>Spark SDK</h1>
<p class="hero__tagline">
Spark consists of a client UI
(<a href="https://www.bluetradingsystems.com/bts-spark#ladder-algo-screen">Spark Client</a>),
(<a class="white" href="https://www.bluetradingsystems.com/bts-spark#ladder-algo-screen">Spark Client</a>),
a high-performance professional futures front end, connected via our proprietary protocol over TCP to our Spark Server, which in turn connects to execution venues via their native protocols.
Use the Spark SDK to extend Spark Server with your custom Algos, then use the Client SDK to launch and control them.
</p>
@@ -65,7 +64,7 @@
<a href="{{ get_url(path="@/server/_index.md") }}" class="button">Get started</a>
</div>
<div class="selling-point">
<div class="selling-point disabled">
<h2>
Client SDK
<i class="fa fa-gamepad" aria-hidden="true"></i>
@@ -73,9 +72,19 @@
<p>
The Spark client SDK allows you to integrate spark with your existing ecosystem. The Spark Client is built entirely on this simple, powerful API.
</p>
<a href="{{ get_url(path="@/client/_index.md") }}" class="button">Get started</a>
<a href="" class="button" disabled>Get started</a>
</div>
<!-- <div class="selling-point disabled">
<h2>
Microsoft Excel
<i class="fa fa-table" aria-hidden="true"></i>
</h2>
<p>
More of your business is built on excel than probably should be. Continue this worrying trend with the Spark Excel API.
</p>
<a href="" class="button" disabled>Get started</a>
</div> -->
</div>
</div>
@@ -85,15 +94,6 @@
© Blue Trading Systems
</footer>
<script>
if (location.hostname == "api.bluetradesys.com") {
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-56FHG8R');
}
</script>
<script type="text/javascript" src="{{ get_url(path="elasticlunr.min.js") }}"></script>
<script type="text/javascript" src="{{ get_url(path="search_index.en.js") }}"></script>
<script type="text/javascript" src="{{ get_url(path="search.js") }}"></script>
+8 -36
View File
@@ -1,39 +1,13 @@
{% extends "index.html" %}
{# this has access to the "page" variable but documentation.html does not #}
{% extends "documentation.html" %}
{% block title %}{{ page.title }} | {{ super() }} {% endblock title %}
{% block doc_content %}
<h1>{{page.title}}</h1>
{{page.content | safe}}
{% endblock doc_content %}
{% block next_link %}
{% endblock next_link %}
{% block content %}
{% set section = get_section(path=page.ancestors[1]) %}
{#
{#<div>{{ section.title }} </div>#}
<div class="documentation">
<aside class="documentation__sidebar">
<ul>
{% for p in section.subsections %}
{% set subsection = get_section(path=p) %}
<li>
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
<ul>
{% for page in subsection.pages %}
<li class="{% if current_path == page.path %}active{% endif %}">
<a href="{{page.permalink}}">{{page.title}}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</aside>
<div class="documentation__content">
<h1>{{page.title}}</h1>
{{page.content | safe}}
{# next/pre links #}
<p>
<p>
{% if page.lighter %}
<a href="{{ page.lighter.permalink }}">
<i class="fa fa-chevron-left fa-fw" aria-hidden="true"></i>
@@ -47,7 +21,5 @@
<i class="fa fa-chevron-right fa-fw" aria-hidden="true"></i>
</a>
{% endif %}
</p>
</div>
</div>
{% endblock content %}
</p>
{% endblock next_link %}
-31
View File
@@ -4,37 +4,6 @@ weight = 3
sort_by = "weight"
insert_anchor_links = "right"
+++
## 2.15.3
### Bug Fixes
- Fixed a bug where some CME currency spreads exhibited incorrect tick values
## 2.15.2
### User Visible Changes
- Excel RTD plugin can now receive ICE market data
## 2.15.1
### Bug Fixes
- Fixed a FIX log parsing issue that resulted in exchange gateway logon problems
## 2.15.0
### User Visible Changes
- Added ability to hide Ladder status messages via Ladder Settings -> Left Panel tab -> Show/Hide groupbox -> Status Message
- Synthetic cloning now allows for any parameter to be changed, including tick size and leg contracts
- Added ability to import synthetics from a CSV file; see our manual for further details on this process
- Reduced Iceberg reloading latency
### Bug Fixes
- Fixed a bug where highlighted fills could lose their highlight status
- Fixed a bug where the (Mini) OrderBook PIQ could be displaying an old value
## 2.14.3
### Bug Fixes
- Fixed a bug regarding ICE logon messaging
## 2.14.2
### Bug Fixes
- Fixed a bug regarding ICE messaging
## 2.14.1
### Bug Fixes
- Fixed a bug regarding ICE logon authentication
## 2.14.0
### User Visible Changes
- All ladders can now be recentered by (shift or alt) + double clicking over the Price column in a ladder
+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
+123
View File
@@ -0,0 +1,123 @@
drop table if exists msgs ;
create table msgs (
MsgID Int4,
TradeDate varchar,
SeqNum Int4,
SendingTime varchar,
TransactTime varchar,
MatchEventIndicator varchar,
NoMDEntries Int2,
NoOrderIDEntries Int2,
SendingTime_UnixTS Int8,
TransactTime_UnixTS Int8
);
drop table if exists books ;
create table books (
MsgID Int4,
SendingTime varchar,
TransactTime varchar,
SeqNum Int4,
bid_p_0 Real,
bid_sz_0 Int4,
bid_p_1 Real,
bid_sz_1 Int4,
bid_p_2 Real,
bid_sz_2 Int4,
bid_p_3 Real,
bid_sz_3 Int4,
bid_p_4 Real,
bid_sz_4 Int4,
bid_p_5 Real,
bid_sz_5 Int4,
bid_p_6 Real,
bid_sz_6 Int4,
bid_p_7 Real,
bid_sz_7 Int4,
bid_p_8 Real,
bid_sz_8 Int4,
bid_p_9 Real,
bid_sz_9 Int4,
Symbol Varchar,
ask_p_0 Real,
ask_sz_0 Int4,
ask_p_1 Real,
ask_sz_1 Int4,
ask_p_2 Real,
ask_sz_2 Int4,
ask_p_3 Real,
ask_sz_3 Int4,
ask_p_4 Real,
ask_sz_4 Int4,
ask_p_5 Real,
ask_sz_5 Int4,
ask_p_6 Real,
ask_sz_6 Int4,
ask_p_7 Real,
ask_sz_7 Int4,
ask_p_8 Real,
ask_sz_8 Int4,
ask_p_9 Real,
ask_sz_9 Int4,
SendingTime_UnixTS Int8,
TransactTime_UnixTS Int8
);
drop table if exists trades;
create table trades (
MsgID Int4,
Symbol varchar,
SendingTime varchar,
TransactTime varchar,
TradeDate varchar,
MDEntryPx Real,
MDnetrySize Int4,
NumberOfOrders Int4,
AgressorSide Int4,
MDTradeEntryID Int4,
AgressorOID varchar,
RestingOID varchar,
PartialQty Int4,
SendingTime_UnixTS Int8,
TransactTime_UnixTS Int8
);
drop table if exists orders;
create table orders (
MsgID Int4,
Symbol varchar,
SendingTime varchar,
TransactTime varchar,
MDEntryPx Real,
OrderID varchar,
MDDisplayQty Int4,
MDOrderPriority Int8,
OrderUpdateAction Int4,
Side Int4,
SendingTime_UnixTS Int8,
TransactTime_UnixTS Int8
);
-- create unique index msg_msg_id on msgs msgid;
-- create view ranges as (
-- select
-- sendingtime,
-- transacttime,
-- bid_p_0 price,
-- case when lag(bid_p_0) over (order by SendingTime) = bid_p_0 then null else 1 end seq
-- from
-- u0v0_books);
-- create view groups as (
-- select
-- sendingtime,
-- transacttime,
-- price,
-- sum(seq) over (order by SendingTime) grp
-- from
-- ranges);
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
DB=$1
TABLE=$2
FILE=$3
zcat $FILE | psql -U postgres -d $DB -c "copy $TABLE from STDIN with delimiter as ',' CSV HEADER"
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
docker exec -it postgres psql -U postgres "$@"
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
#docker run --net host -e PGADMIN_DEFAULT_EMAIL=bts -e PGADMIN_DEFAULT_PASSWORD=BT5u53r dpage/pgadmin4
docker run \
--rm \
-d \
-p 8000:80 \
-e PGADMIN_DEFAULT_EMAIL=bts \
-e PGADMIN_DEFAULT_PASSWORD=BT5u53r \
--name pgadmin \
dpage/pgadmin4
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SRC_ROOT=$DIR/../../..
DATA_DIR=`readlink -f $SRC_ROOT/analysis_data`
DB_DIR=$DATA_DIR/db
mkdir -p $DB_DIR
docker run \
--rm \
-d \
--name postgres \
--net host \
-v $DB_DIR:/var/lib/postgresql/data \
-v /home/$USER:/home/$USER \
-v $DATA_DIR:$DATA_DIR \
-v /etc/group:/etc/group \
-v /run/user/:/run/user/ \
-u $UID \
-t postgres \
postgres
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SRC_ROOT=$DIR/../../
DATE=$1
DB=md_$DATE
$DIR/db/psql.sh -c "drop database if exists $DB"
$DIR/db/psql.sh -c "create database $DB"
$DIR/db/psql.sh -d md_$DATE -f $DIR/db/create_db.sql
PROCESSED_DATA=$SRC_ROOT/analysis_data/processed/$DATE
ALL_COMMANDS=""
for TABLE in trades orders msgs books
do
for ITEM in $PROCESSED_DATA/*.$TABLE.gz
do
ALL_COMMANDS+="docker exec -t postgres /bin/bash -c \""
ALL_COMMANDS+="$DIR/db/import_csv.sh $DB $TABLE $ITEM"
ALL_COMMANDS+="\"\n"
done
done
echo -e $ALL_COMMANDS | parallel --will-cite
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DATE=$1
SRC_ROOT=$DIR/../../
SPARK_BIN_ROOT=`readlink -f $SRC_ROOT/build`/ninja_release/server
RAW_DATA=$SRC_ROOT/analysis_data/raw/$DATE
PROCESSED_DATA=$SRC_ROOT/analysis_data/processed/$DATE
mkdir -p $PROCESSED_DATA
rm $PROCESSED_DATA/* >/dev/null
ALL_COMMANDS=""
for IN_PATH in $RAW_DATA/*.gz
do
BASE_NAME=`basename $IN_PATH`
BASE_NAME_STEM=${BASE_NAME%.*}
for CMD in trades orders msgs books
do
OUT_PATH=$PROCESSED_DATA/$BASE_NAME_STEM.$CMD.gz
ALL_COMMANDS+="(cd $SPARK_BIN_ROOT && zcat $IN_PATH | ./sparkd parsehistoricalfix $CMD | gzip >$OUT_PATH)\n"
done
done
echo -e $ALL_COMMANDS | parallel --will-cite
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
d=$1
end=${2-`date +%Y%m%d`}
until [[ $d > $end ]]; do
echo "$d"
d=$(date -d "$d + 1 day" +%Y%m%d)
done
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DATE=${1-`date -d "yesterday" +%Y%m%d`}
$DIR/sync.sh $DATE &&
$DIR/parse.sh $DATE &&
$DIR/load.sh $DATE
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DATE=$1
SRC_ROOT=$DIR/../../
RAW_DATA_ROOT=$SRC_ROOT/analysis_data/raw/$DATE
mkdir -p $RAW_DATA_ROOT
AWS_ACCESS_KEY_ID=AKIAIFIGOOWGBLEMO3PQ AWS_SECRET_ACCESS_KEY=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 AWS_REGION=us-east-1 aws s3 sync s3://cme-bts-datamine/mbo/mbo/$DATE/ $RAW_DATA_ROOT
+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>
@@ -22,17 +23,20 @@
<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 +47,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 ./sparkcmd/
if [ "$1" == "Release" ]
then
dotnet publish -c $1 ./sparkcmd/
ln -fs $2/src/lib/libspark.so $2/../$1/bin/netcoreapp3.0/linux-x64/publish
fi
ln -fs $2/src/lib/libspark.so $2/../$1/bin/netcoreapp3.0/linux-x64
@@ -4,112 +4,43 @@ using mbts;
using mbts.spark;
using mbts.ui;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Windows;
namespace bts {
[Export, Export(typeof(IShellDaemon))]
class DeleteSyntheticDaemon : IShellDaemon, IDisposable {
// request_id is noted in the series of log messages that occur during a particular delete op.
static int _request_id = 1;
readonly OrdersModel _orders_model;
readonly ErrorReporter _error_reporter;
IReadOnlyDictionary<uint, ExecutionStrategyImmutable> _strategy_map =
DictionaryHelper.empty<uint, ExecutionStrategyImmutable>();
IDisposable _dispose;
[ImportingConstructor]
public DeleteSyntheticDaemon(IBus bus, OrdersModel orders_model, ErrorReporter error_reporter) {
_orders_model = orders_model;
_error_reporter = error_reporter;
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SyntheticDeleteTag).Subscribe(_delete_synthetic),
orders_model.spreader.strategy.Subscribe(map => _strategy_map = map));
_dispose = bus.subscribe(SparkTags.SyntheticDeleteTag)
.OnDispatcher()
.Subscribe(_delete_synthetic);
}
public void Dispose() =>
Disposable.dispose(ref _dispose);
async void _delete_synthetic(IImmutableSet<uint> keys) {
var log = new LoggerContext(Log.Ui, $"Synthetic Delete Req {unchecked(Interlocked.Increment(ref _request_id))} - ", null);
if (keys.is_null_or_empty()) {
log.warn("received empty delete request (no securities specified).");
return;
}
log.info($"received request to delete the following {keys.Count} securities: {string.Join(", ", keys)}");
var (orders, orders_success) = await _orders_model.security_order_groups.FirstAsyncTask(SparkUtils.DefaultTimeOut);
async void _delete_synthetic(uint id) {
var (orders, orders_success) = await _orders_model.on_orders.FirstAsyncTask(SparkUtils.DefaultTimeOut);
if (!orders_success) {
var msg = "Unable to delete synthetic securities: " +
$"timed out waiting for orders table (must verify that a synthetic security does not have any working orders before it can be deleted).";
_error_reporter.report_error(msg);
log.warn(msg);
_error_reporter.report_error($"Unable to delete synthetic security ({id}): unable to determine whether or not security ({id}) has working orders (deletion is not permitted while there are working orders).");
Log.Ui.warn($"Ignoring request to delete synthetic security ({id}) because the orders lookup timed out (must confirm no working orders).");
} else if (orders.state.Values.Any(o => o.order.Def.SecurityKey == id && o.order.is_non_terminal())) {
Log.Ui.warn($"Ignoring request to delete synthetic security ({id}) because non-terminal-orders were detected.");
} else {
var working_orders = new List<uint>();
var missing_strategies = new List<uint>();
var items = new List<(uint key, ExecutionStrategyImmutable strategy)>();
var temp_strats = _strategy_map;
foreach (var k in keys) {
if (orders.state.TryGetValue(k, out var sec_group) && sec_group.table.state.Values.Any(tup => tup.order.is_active())) {
working_orders.Add(k);
} else if (!temp_strats.TryGetValue(k, out var strat)) {
missing_strategies.Add(k);
} else {
items.Add((k, strat.with(deleted: true)));
}
ExecutionStrategyImmutable? strategy = await _orders_model.spreader.lookup_strategy_async(id);
if (strategy.HasValue) {
ExecutionStrategyImmutable deleted_strategy = strategy.Value.with(deleted: true);
await _orders_model.spreader.save_strategy(id, deleted_strategy);
}
var lines = new List<string>();
if (working_orders.Count > 0) {
lines.Add($"skipping {working_orders.Count} securities because they have working orders.");
}
if (missing_strategies.Count > 0) {
lines.Add($"skipping {missing_strategies.Count} securities because their execution strategies were not discovered.");
}
if (lines.Count > 0)
log.warn(string.Join("; ", lines));
var errors = await _orders_model.spreader.save_strategies(items);
int error_count = errors.Count(e => !string.IsNullOrEmpty(e));
log.info($"{items.Count - error_count}/{items.Count} attempted deletes were successful (skipped {working_orders.Count + missing_strategies.Count}).");
// display results to user
const string dialog_title = "Synthetic Deletion";
string display_msg;
MessageBoxImage icon = MessageBoxImage.None;
if (keys.Count > 1) {
if (error_count == 0 && working_orders.Count == 0 && missing_strategies.Count == 0) {
display_msg = $"Synthetic securities have been deleted.";
icon = MessageBoxImage.Information;
} else {
display_msg = $"{keys.Count - error_count - working_orders.Count - missing_strategies.Count}/{keys.Count} securities were successfully deleted.";
icon = MessageBoxImage.Error;
}
} else {
if (error_count == 0 && working_orders.Count == 0 && missing_strategies.Count == 0) {
display_msg = "Synthetic security has been deleted.";
icon = MessageBoxImage.Information;
} else {
string reason;
if (error_count > 0) {
reason = errors[0];
} else if (missing_strategies.Count > 0) {
reason = "did not find execution strategy.";
} else if (working_orders.Count > 0) {
reason = "working orders detected.";
} else {
// unexpected code path
reason = "unknown error.";
}
icon = MessageBoxImage.Error;
display_msg = $"Unable to delete synthetic security: " + reason;
}
}
BTSMessageBox.Show(display_msg, dialog_title, BTSMessageBoxButtons.OK, icon);
}
}
}
}
}
+1 -44
View File
@@ -1,15 +1,12 @@
using bts.api;
using bts.api.core;
using bts.support;
using bts.utils;
using mbts;
using mbts.spark;
using mbts.ui;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
@@ -167,45 +164,5 @@ namespace bts.spark {
void _report_error(string msg) =>
error_reporter.report_error($"Error {desc}: {msg}", AlgoStudioWindow.NAME, MessageBoxImage.Warning);
}
public static async Task<(TResult result, string error_msg)[]> safe_execute_batch<T, TResult>(
this ErrorReporter error_reporter,
IEnumerable<Task<T>> execute,
Func<T, (TResult result, string error_msg)> result_selector,
string desc,
string title) {
//
try {
var results = await Task.WhenAll(execute as Task<T>[] ?? ArrayHelper.clone_optimized(execute));
var result_values = new (TResult, string)[results.Length];
StringBuilder error_bd = null;
for (int i = 0; i < results.Length; i++) {
var r = results[i];
var (result, error) = result_selector(r);
if (!string.IsNullOrEmpty(error)) {
if (error_bd == null) {
error_bd = new StringBuilder();
} else {
error_bd.AppendLine();
}
error_bd.Append(error);
}
result_values[i] = (result, error);
}
if (error_bd != null) {
var error_msg = error_bd.ToString();
if (!string.IsNullOrEmpty(error_msg))
_report_error(error_msg);
}
return result_values;
}
catch (Exception ex) {
_report_error(ex.ToString());
}
return null;
//
void _report_error(string msg) =>
error_reporter.report_error($"Error {desc}: {msg}", title, MessageBoxImage.Warning);
}
}
}
}
+2 -2
View File
@@ -39,7 +39,7 @@ namespace bts {
_relay.set_token(_token.Value);
_dispose.Add(
_ss.Value.subscribei_metadata()
.Select(upd => upd.all.Where(i => i.HasStrategy).ToDictionary(i => i.SecurityKey, i => i.Strategy))
.Select(upd => upd.all.ToDictionary(i => i.SecurityKey, i => i.Strategy))
.Subscribe(t => {
var deleted_synthetic_keys = new HashSet<uint>(t.Where(kv => kv.Value.Deleted).Select(kv => kv.Key));
// add back 'undeleted' synthetics
@@ -64,4 +64,4 @@ namespace bts {
Task.Run(run);
}
}
}
}
+4 -23
View File
@@ -3,13 +3,11 @@ using bts.api.core;
using bts.api.orderman;
using bts.spark;
using mbts;
using mbts.config;
using mbts.spark;
using mbts.ui;
using mbts.ui.spark;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Reactive.Linq;
using System.Windows;
@@ -21,26 +19,20 @@ namespace bts.shell {
readonly IBus _bus;
readonly OrdersModel _model;
readonly ErrorReporter _error_reporter;
readonly bool _close_on_send;
IReadOnlyDictionary<uint, SecurityMD> _md_map;
TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint> _orders;
IDisposable _dispose;
[ImportingConstructor]
public OrderTicketDaemon(IBus bus, OrdersModel model, ErrorReporter error_reporter)
: this(bus, model, error_reporter, Configuration.get<bts.config.OrderTicketOptions>().close_on_send) { }
public OrderTicketDaemon(IBus bus, OrdersModel model, ErrorReporter error_reporter, bool close_on_send) {
public OrderTicketDaemon(IBus bus, OrdersModel model, ErrorReporter error_reporter) {
_bus = bus;
_model = model;
_error_reporter = error_reporter;
_close_on_send = close_on_send;
_dispose = Disposable.Create(
model.on_orders.OnDispatcher().Subscribe(o => _orders = o),
bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(m =>_md_map = m),
bus.subscribe(SparkTags.ShowOrderTicketFromSecurityTag).OnDispatcher().Subscribe(_show_order_ticket),
bus.subscribe(SparkTags.ShowOrderTicketTag).OnDispatcher().Subscribe(_show_order_ticket),
bus.subscribe(SparkTags.ShowOrderTicketAutoLookupTag).Subscribe(_show_order_tickets),
bus.subscribe(SparkTags.ShowModifyOrderTag).OnDispatcher().Subscribe(_show_modify_order)
);
}
@@ -62,28 +54,17 @@ namespace bts.shell {
void _show_order_ticket(SecurityMD smd) =>
_show_order_window(new OrderTicket {
DataContext = new NewOrderTicketVM(_model, smd,
Observable.Return(LadderSettings.Default)) {
close_on_send = _close_on_send
}
Observable.Return(LadderSettings.Default))
});
void _show_order_ticket((OrderDefinitionImmutable def, double price, uint size) ops) {
if (_md_map.TryGetValue(ops.def.SecurityKey, out SecurityMD smd))
_show_order_window(new OrderTicket {
DataContext = new NewOrderTicketVM(_model, smd, ops.def, ops.price, ops.size,
Observable.Return(LadderSettings.Default.calculator_buttons)) {
close_on_send = _close_on_send
}
Observable.Return(LadderSettings.Default.calculator_buttons))
});
}
void _show_order_tickets(IImmutableSet<uint> securities) {
foreach (var id in securities.empty_if_null()) {
if (_md_map.TryGetValue(id, out var smd))
_show_order_ticket(smd);
}
}
void _show_modify_order(uint order_key) {
if (_orders.state.TryGetValue(order_key, out var ori) && ori.sec.HasValue
&& (ori.order.HasDef && ori.order.Def.HasCustomDef && !(ori.order.Def.CustomDef.HasStop || ori.order.Def.CustomDef.HasStopLimit))) {
@@ -97,4 +78,4 @@ namespace bts.shell {
}
}
}
}
}
+1 -12
View File
@@ -1,5 +1,4 @@
using bts;
using mbts.ui.dpi;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -26,7 +25,6 @@ namespace mbts.ui {
AttachAlgosKey = nameof(AttachAlgosKey),
CheckMarkGeometryKey = nameof(CheckMarkGeometryKey),
CopyWFGeometryKey = nameof(CopyWFGeometryKey),
CopyBoxesGeometryKey = nameof(CopyBoxesGeometryKey),
DataEditGeometryKey = nameof(DataEditGeometryKey),
DetachAlgosKey = nameof(DetachAlgosKey),
DiskGeometryKey = nameof(DiskGeometryKey),
@@ -46,7 +44,6 @@ namespace mbts.ui {
KeyGeometryKey = nameof(KeyGeometryKey),
LetterAKey = nameof(LetterAKey),
LetterDKey = nameof(LetterDKey),
MenuOrientationGeometryKey = nameof(MenuOrientationGeometryKey),
OrdersGeometryKey = nameof(OrdersGeometryKey),
PauseGeometryKey = nameof(PauseGeometryKey),
PaymentGeometryKey = nameof(PaymentGeometryKey),
@@ -58,14 +55,10 @@ namespace mbts.ui {
StarGeometryKey = nameof(StarGeometryKey),
ToolsGeometryKey = nameof(ToolsGeometryKey),
TrashGeometryKey = nameof(TrashGeometryKey),
UndoCircularGeometryKey = nameof(UndoCircularGeometryKey),
WarningExclamationGeometryKey = nameof(WarningExclamationGeometryKey),
WarningGeometryKey = nameof(WarningGeometryKey);
UndoCircularGeometryKey = nameof(UndoCircularGeometryKey);
public static Geometry PlayGeometry => Geometry.Parse(get_geometric_path(PlayGeometryKey).FirstOrDefault() ?? string.Empty);
static readonly Pen _copy_boxes_pen = new Pen(Brushes.Black, HighDpiExtensions.RoundLayoutValue(5)).frozen();
static readonly string base_uri = "pack://application:,,,/Images/Icons/";
private static ResourceDictionary _resources = (ResourceDictionary)Application.LoadComponent(new Uri("/Images/Vector/Icons.xaml", UriKind.RelativeOrAbsolute));
@@ -160,7 +153,6 @@ namespace mbts.ui {
public static ImageSource CheckMark => create_icon(CheckMarkGeometryKey);
public static ImageSource Clock { get { return load_resource_and_freeze("Clock"); } }
public static ImageSource CopyWF => create_icon(CopyWFGeometryKey);
public static ImageSource CopyBoxes => create_icon(CopyBoxesGeometryKey, null, _copy_boxes_pen);
public static ImageSource DataEdit => create_icon(DataEditGeometryKey);
public static ImageSource Defrag { get { return load_resource_and_freeze("Defrag"); } }
public static ImageSource DetachAlgos => create_icon_and_rotate(DetachAlgosKey, Brushes.Red);
@@ -184,7 +176,6 @@ namespace mbts.ui {
public static ImageSource Import => load_resource_and_freeze("Import");
public static ImageSource Key => create_icon(KeyGeometryKey);
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
public static ImageSource MenuOrientation => create_icon(MenuOrientationGeometryKey);
public static ImageSource Pause => create_icon(PauseGeometryKey);
public static ImageSource Payment => create_icon(PaymentGeometryKey);
public static ImageSource Pencil => create_icon(PencilGeometryKey);
@@ -199,8 +190,6 @@ namespace mbts.ui {
public static ImageSource Tetris { get { return load_resource_and_freeze("Tetris"); } }
public static ImageSource Tools => create_icon(ToolsGeometryKey);
public static ImageSource Trash => create_icon(TrashGeometryKey);
public static ImageSource Warning => create_icon(WarningGeometryKey, Brushes.IndianRed);
public static ImageSource WarningExclamation => create_icon(WarningExclamationGeometryKey, Brushes.IndianRed);
public static ImageSource UndoCircular => create_icon(UndoCircularGeometryKey);
public static ImageSource ViewSettings => load_resource_and_freeze("ViewSettings");
public static ImageSource Wrench { get { return load_resource_and_freeze("Wrench"); } }
-28
View File
@@ -46,18 +46,6 @@
</sys:String>
</x:Array>
<x:Array x:Key="{x:Static mbts:Icons.CopyBoxesGeometryKey}" Type="{x:Type sys:String}">
<sys:String>
M-55,5 L-55,55 L-5,55
</sys:String>
<sys:String>
M-40,-10 L-40,40 L10,40 L10,-10 L-40,-10
</sys:String>
<sys:String>
M-10,10 L40,10 L40,-40 L-10,-40 L-10,10
</sys:String>
</x:Array>
<sys:String x:Key="{x:Static mbts:Icons.CopyWFGeometryKey}">
M14.733902,12.084991L9.751783,17.388992 12.982842,17.436005C13.569886,17.462997 14.5229,17.350998 14.876848,17.005005 14.954852,16.929993 15.031879,16.824005 15.02486,16.593002 14.986895,15.457993 14.838883,13.438004 14.733902,12.084991z M22.507753,11.134453C22.480678,11.134323,22.467085,11.135494,22.469961,11.136993L20.64694,11.145004 16.291904,11.192993C16.388891,12.405991 16.596901,15.100998 16.647928,16.539001 16.668924,17.177994 16.448888,17.742004 16.007904,18.168991 15.210897,18.947998 13.893864,19.065002 13.179865,19.065002L9.2818066,19.005005 9.3258129,25.522003 9.3728109,25.528 9.3278271,25.882996 9.3317944,26.395996 9.3107983,26.397995C9.3167797,26.789993,9.3378369,27.552994,9.3958212,29.065994L9.4068075,29.393005C9.4207855,29.819992 9.5648295,29.994995 9.6707884,30.089005 9.9807898,30.365997 10.539817,30.390991 10.717799,30.369003L10.717799,30.375C10.88284,30.354996 11.280855,30.356003 12.823844,30.365997 13.867863,30.371002 15.206869,30.376999 16.594886,30.376999 20.757965,30.376999 23.270998,30.354996 23.862007,30.311005 24.454971,30.268005 24.661029,29.154999 24.690021,28.729996 24.690021,28.729996 24.677997,27.876999 24.661029,26.582993 24.612016,22.671005 24.51198,14.697998 24.472002,12.802002 24.461013,12.302994 24.316969,11.929993 24.034008,11.662003 23.547475,11.200003 22.697284,11.13536 22.507753,11.134453z M8.8867823,2.8410034L2.5417312,10.296997 6.4037791,10.356995C6.8917614,10.390991 8.3258005,10.341003 8.9567896,9.7299957 9.1738335,9.5189972 9.2667919,9.2659912 9.2558046,8.9089966 9.2067934,7.3889923 9.0258213,4.7480011 8.8867823,2.8410034z M10.424826,1.6239929C10.551842,3.302002 10.816799,6.951004 10.876797,8.8560028 10.903836,9.6750031 10.636804,10.360992 10.083818,10.897995 9.0667769,11.882004 7.3527657,11.987991 6.6627546,11.987991 6.4657918,11.987991 6.3367616,11.979004 6.3177796,11.977005L1.6477377,11.905991C1.6387044,14.278992 1.6236895,20.283997 1.6496909,22.358994 1.651705,22.569992 1.6437093,22.780991 1.6366904,22.992004 1.6117265,23.593994 1.5926833,24.112991 1.8537338,24.386002 2.0716933,24.612991 2.6497034,24.886002 4.1457549,24.886002 6.0127832,24.886002 7.0867711,24.876999 7.6967641,24.867004L7.6457993,17.259995 14.853899,9.5849915 16.681924,9.5650024 16.681924,9.5429993 19.454908,9.5289917 19.406934,2.6210022C19.323925,2.3549957,18.960945,1.6239929,17.387928,1.6239929z M9.1738335,0L17.387928,0C19.94692,0,20.874971,1.5299988,21.022983,2.4329987L21.077976,9.5209961 22.446951,9.5139923C22.43395,9.5130005 24.031017,9.4250031 25.148035,10.481995 25.753999,11.054001 26.071996,11.822998 26.092992,12.766998 26.142006,15.065994 26.281044,26.268997 26.307047,28.412003L26.311014,28.764999C26.279029,29.817993 25.750032,31.802002 23.979013,31.931 23.342958,31.977005 20.887972,32 16.683878,32 13.798893,32 11.107818,31.988998 10.808803,31.987991 10.789821,31.990997 9.5147806,32.112 8.6008288,31.309006 8.0858074,30.854996 7.8107797,30.229004 7.7838017,29.444992L7.7728153,29.126999C7.721789,27.759995 7.6978022,26.970993 7.6898061,26.490005 7.0547882,26.5 5.9867817,26.509003 4.1457549,26.509003 2.4377257,26.509003 1.3387128,26.192001 0.68367504,25.509003 -0.055286743,24.742004 -0.017322397,23.742004 0.012707477,22.938004 0.020702986,22.744003 0.028698692,22.559998 0.026684528,22.378998 -0.00029337304,20.240997 0.016674615,13.856995 0.026684528,11.212997L0.026684528,10.867004 0.14167619,10.610992 8.7147823,0.54100037 8.7088009,0.45899963 8.8018194,0.43800354z
</sys:String>
@@ -292,10 +280,6 @@
M22.399981,2.4000006C22.099982,2.4000006 21.899982,2.5 21.599982,2.6999998 21.199983,3.1000004 21.199983,3.8000002 21.599982,4.2000003L26.199979,8.9000001C26.599978,9.3000002 27.299977,9.3000002 27.699977,8.9000001 28.099977,8.5000002 28.099977,7.8000002 27.699977,7.4000006L23.099981,2.6999998C22.899981,2.5,22.699981,2.4000006,22.399981,2.4000006z M21.499982,0C22.299982,0,23.199981,0.30000019,23.79998,0.90000057L29.299976,6.4000001C30.599975,7.7000003,30.599975,9.7000003,29.299976,11L22.599981,17.7C21.299983,19,19.299984,19,17.999985,17.7L17.299985,17 12.699989,21.6 10.399991,20.900001 10.899991,23.400001 8.4999928,22.8 8.9999924,25.5 6.2999945,25.1 6.7999945,27.6 4.2999964,27 4.7999954,29.6 2.4999971,32 0,26.6 13.499989,13.1 16.999986,16.6 12.699989,12.3C11.399991,11,11.399991,9.0000002,12.699989,7.7000003L19.399984,1C19.899983,0.30000019,20.699983,0,21.499982,0z
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.MenuOrientationGeometryKey}">
M14.997001,19.999038L15.000001,23.499012C15.000001,24.327005,14.327008,25,13.500016,25L9.9970522,25 9.9970522,23.000015 13.000022,23.000015 12.997022,20.001038z M9.000001,19.000006L9.000001,30.000006 30.000001,30.000006 30.000001,19.000006z M2,2L2,23 7.000001,23 7.000001,18.500006C7.000001,17.673006,7.6730013,17.000006,8.500001,17.000006L13.000001,17.000006 13.000001,2z M1.5,0L13.500001,0C14.327001,0,15.000001,0.67300034,15.000001,1.5L15.000001,17.000006 26.956518,17.000006 23.162404,13.207182C22.772376,12.816143 22.772376,12.184205 23.162404,11.793225 23.357907,11.597736 23.613928,11.499992 23.869827,11.499992 24.125725,11.499992 24.381502,11.597736 24.576517,11.793225L25.889371,13.105454 25.866285,12.914201C25.554945,10.547287 24.699391,8.7684691 23.308905,7.6089654 21.974919,6.4954615 20.356436,6.1229606 19.066825,6.0297103L19.059648,6.0292783 20.545799,7.5160289C20.936786,7.9070184 20.936786,8.5390015 20.545799,8.929991 20.350807,9.1249857 20.094815,9.2229831 19.838825,9.2229831 19.582834,9.2229831 19.326843,9.1249857 19.131849,8.929991L15.185988,4.9830971 18.981854,1.1871986C19.371841,0.79620934 20.004819,0.79620934 20.395805,1.1871986 20.785791,1.5771885 20.785791,2.2101717 20.395805,2.601161L18.976535,4.020443 18.999199,4.0214248C20.641604,4.1119318 22.785724,4.567862 24.590892,6.0739603 26.493747,7.660778 27.606404,10.054969 27.91277,13.209869L27.913735,13.221022 29.341699,11.793225C29.536714,11.597736 29.792459,11.499992 30.048328,11.499992 30.304196,11.499992 30.560187,11.597736 30.75569,11.793225 31.146694,12.184205 31.146694,12.816143 30.75569,13.207182L26.962552,17.000006 30.500001,17.000006C31.327001,17.000006,32.000001,17.673006,32.000001,18.500006L32.000001,30.500006C32.000001,31.327005,31.327001,32.000006,30.500001,32.000006L8.500001,32.000006C7.6730013,32.000006,7.000001,31.327005,7.000001,30.500006L7.000001,25 1.5,25C0.67300034,25,0,24.327001,0,23.5L0,1.5C0,0.67300034,0.67300034,0,1.5,0z
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.OrdersGeometryKey}">
M50.14375,33.389126C50.416863,33.394688,50.653496,33.587773,50.705998,33.864617L50.015995,45.114388C49.810295,46.291462,49.130394,45.097489,49.130394,45.097489L47.65659,42.640538C42.747776,49.635197 34.633152,54.221001 25.433924,54.221001 14.884193,54.221001 5.7437552,48.204125 1.2449617,39.419205 0.80087067,38.552021 0.21358696,37.705738 0.052086769,36.736958 -0.49735315,33.490925 3.4507786,32.901036 4.8049124,34.432404 5.3674143,35.067692 6.034036,36.16407 6.5079177,37.026051 10.106778,43.799416 17.2321,48.413819 25.433924,48.413819 32.656544,48.413819 39.026364,44.826695 42.903977,39.351506L40.229367,38.854115C40.229367,38.854115,38.677464,38.647118,39.742466,37.996033L50.024895,33.398526C50.064972,33.391365,50.104733,33.388333,50.14375,33.389126z M23.030291,9.0310004L27.889918,9.0310004 27.889918,13.028299C30.539786,13.205498,32.324932,13.958096,33.464161,14.528294L33.964176,14.775793 32.417435,20.038878 31.706417,19.68068C30.579987,19.11958 28.839241,18.244583 26.149073,18.244583 24.006916,18.244583 22.723183,19.122181 22.723183,20.590876 22.723183,21.894272 23.943314,22.804369 27.551308,24.187165 32.706444,26.124659 34.911,28.702853 34.911,32.817341 34.911,36.892929 32.090527,40.05172 27.673611,40.989117L27.673611,45.540001 22.767485,45.540001 22.767485,41.330414C20.252919,41.155815,17.695551,40.424118,16.101811,39.404721L15.683999,39.137619 17.221739,33.731438 17.969059,34.180634C19.088988,34.851235 21.402748,35.97873 24.340325,35.97873 26.734886,35.97873 28.276728,34.921635 28.276728,33.291336 28.276728,31.986641 27.547409,30.853846 23.986016,29.57255 20.083815,28.144253 16.095511,26.04406 16.095511,21.239174 16.095511,17.280985 18.737379,14.291395 23.030291,13.329098z M25.268765,0C35.819447,2.7347414E-07 44.961449,6.0155495 49.458798,14.804573 49.902695,15.669173 50.490094,16.516972 50.651287,17.48427 51.198281,20.734265 47.255726,21.318962 45.901341,19.789065 45.336346,19.153567 44.669552,18.051968 44.19566,17.19517 40.5957,10.421881 33.473275,5.8072398 25.268765,5.8072393 18.047545,5.8072398 11.678013,9.3918336 7.8003562,14.867073L10.474627,15.367072C10.474627,15.367072,12.026909,15.574172,10.962921,16.223872L0.67933281,20.826762C0.36037083,20.880062,0.057029126,20.669165,0.00099972012,20.354063L0.69093342,9.1118043C0.89552094,7.9296158,1.5738537,9.124924,1.5738539,9.124924L3.0452772,11.583379C7.9566038,4.584462,16.073666,2.7347414E-07,25.268765,0z
</sys:String>
@@ -379,18 +363,6 @@
c-84.15,0-153-68.85-153-153h-51c0,112.2,91.8,204,204,204s204-91.8,204-204S360.825,89.25,248.625,89.25z
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.WarningExclamationGeometryKey}">
M15.998999,17.988002C16.551,17.988002 16.999,18.437009 16.999,18.990018 16.999,19.543027 16.551,19.992034 15.998999,19.992034 15.446999,19.992034 14.998999,19.543027 14.998999,18.990018 14.998999,18.437009 15.446999,17.988002 15.998999,17.988002z
M15.998999,8.0630169C16.552,8.0630169,16.999,8.5110159,16.999,9.0650149L16.999,16.167001C16.999,16.721 16.552,17.168998 15.998999,17.168998 15.446,17.168998 14.998999,16.721 14.998999,16.167001L14.998999,9.0650149C14.998999,8.5110159,15.446,8.0630169,15.998999,8.0630169z
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.WarningGeometryKey}">
M15.998999,17.988002C16.551,17.988002 16.999,18.437009 16.999,18.990018 16.999,19.543027 16.551,19.992034 15.998999,19.992034 15.446999,19.992034 14.998999,19.543027 14.998999,18.990018 14.998999,18.437009 15.446999,17.988002 15.998999,17.988002z
M15.998999,8.0630169C16.552,8.0630169,16.999,8.5110159,16.999,9.0650149L16.999,16.167001C16.999,16.721 16.552,17.168998 15.998999,17.168998 15.446,17.168998 14.998999,16.721 14.998999,16.167001L14.998999,9.0650149C14.998999,8.5110159,15.446,8.0630169,15.998999,8.0630169z
M15.999277,2.3337629L2.3830507,22.996989 29.615442,22.996989z
M15.999513,0C16.50052,-3.8678991E-07,17.001274,0.21826346,17.288297,0.65479128L31.74151,22.585975C32.057495,23.064982 32.084472,23.677966 31.812494,24.184989 31.541494,24.687009 31.02049,24.999999 30.450475,24.999999L1.5480773,24.999999C0.97903845,24.999999 0.45705819,24.687009 0.18703459,24.184989 -0.085979476,23.677966 -0.057963934,23.064982 0.25704309,22.585975L14.709219,0.65479128C14.997247,0.21826346,15.498506,-3.8678991E-07,15.999513,0z
</sys:String>
<!--
'y' for equilateral triangle of side length x:
(x/2)^2 + Y^2 = x^2
@@ -48,5 +48,5 @@ namespace mbts.ui.spark {
}
}
class MarketViewRowView : DIYGrid.HighPerformanceRow<MarketViewVM.Row> { }
class MarketViewRowView : DIYGrid.HighPerformanceRow<MarketViewVM.Row, MarketViewView.Columns> { }
}
@@ -4,10 +4,10 @@ using bts.shell;
using bts.shell.fills;
using bts.spark;
using bts.utils;
using bts.view;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.shell;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
@@ -32,7 +32,6 @@ namespace mbts.spark {
SecurityCriteria
_selected_security_criteria_backing,
_sorted_selected_security_criteria_backing;
SparkMenuDisplayVM _menu_display_vm;
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, IWindowSettingsManager<GridSettings> settings_manager)
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
@@ -49,9 +48,7 @@ namespace mbts.spark {
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public override MarketViewWindowFactory.StartupData SaveState =>
_state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.get_grid_save_state(), _vm.rows_view.get_sorting_criteria(_vm.columns)) {
menu_display = _menu_display_vm.safe_create()
};
_state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.get_grid_save_state(), _vm.rows_view.get_sorting_criteria(_vm.columns));
public override double MinWidth => MarketViewWindowFactory.StartupData.DefaultGridSettings.columns.Where(c => c.is_visible).Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
@@ -104,8 +101,6 @@ namespace mbts.spark {
public void thread_init() {
_init_guard.check_access();
Log.notnull(_state);
_menu_display_vm = new SparkMenuDisplayVM();
_menu_display_vm.load_from(_state.menu_display);
var state_src = Observable.FromEvent<Action<MarketViewWindowFactory.StartupData>, MarketViewWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
_vm = new MarketViewVM(_bus, _orders_model, state_src, _settings_manager);
_view = new MarketViewView { DataContext = _vm };
@@ -165,8 +160,7 @@ namespace mbts.spark {
var dialog = new ShellSettingsDialog { DataContext = vm };
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
})),
_menu_display_vm.spark_cmd
}))
};
//
_vm.selection_changed += on_selection_changed;
@@ -196,8 +190,5 @@ namespace mbts.spark {
_vm.Dispose();
Commands.OfType<IDisposable>().DisposeAll();
}
protected override void OnParentViewCreated(SparkWindowView parent_view) =>
_menu_display_vm.safe_bind_to_view(parent_view);
}
}
@@ -1,7 +1,6 @@
using bts.api;
using bts.proto;
using bts.settings;
using bts.view;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
@@ -54,8 +53,6 @@ namespace bts.shell {
public readonly FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
[ProtoMember(5)]
public SparkMenuDisplaySettings menu_display;
public StartupData() { }
@@ -1,36 +0,0 @@
using mbts.spark.filters;
using mbts.ui;
using ProtoBuf;
using System;
namespace bts.shell.time_and_sales {
[ProtoContract]
class FilterStates {
[ProtoMember(1)]
public TimeFilterTuple time;
[ProtoMember(2)]
public string security;
[ProtoMember(3)]
public DoubleMinMaxState price;
[ProtoMember(4)]
public UIntMinMaxState size;
// [ProtoMember(5)]
// ProtoMember 5 cannot be used further
[ProtoMember(6)]
public string security_key;
[ProtoMember(7)]
public co2.AggressorSide[] aggressor_sides;
}
static class TimeAndSalesFilterStatesExtensions {
public static Func<TimeAndSalesRowVM, bool> build_predicate(this FilterStates fs) {
var pb = new ColumnPredicateBuilder<TimeAndSalesRowVM>();
pb.add_regex(fs.security, r => r.security.ToString());
pb.add_time_filter(fs.time, r => r.time);
pb.add_price(fs.price, r => r.price, r => r.security);
pb.add_numeric(fs.size, r => r.size);
pb.add_discrete_values(fs.aggressor_sides, r => r.aggressor_side);
return pb.to_predicate();
}
}
}
@@ -1,74 +0,0 @@
using mbts.spark;
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System.Collections.Generic;
using System.Linq;
using static mbts.ui.controls.DIYGrid;
using Columns = bts.shell.time_and_sales.TimeAndSalesView.Columns;
namespace bts.shell.time_and_sales {
[ProtoContract]
class TimeAndSalesSettings {
[ProtoMember(1)]
public readonly SecurityCriteria security_criteria;
[ProtoMember(2)]
List<SortingCriteria> _sorting_criterion;
[ProtoMember(3)]
public FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
[ProtoMember(5)]
public SparkMenuDisplaySettings menu_display;
[ProtoMember(6)]
public bool use_defaults;
public TimeAndSalesSettings() { }
public TimeAndSalesSettings(SecurityCriteria security_criteria) :
this(security_criteria, new FilterStates(), null, null) { }
public TimeAndSalesSettings(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List<SortingCriteria> sorting_criterion = null) {
this.security_criteria = security_criteria;
this.filters = filters;
this.grid = grid;
_sorting_criterion = sorting_criterion;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public List<SortingCriteria> sorting_criterion => _sorting_criterion ?? (_sorting_criterion = DefaultColumns.default_sorting_criterion.ToList());
public static TimeAndSalesSettings Default(SecurityCriteria security_criteria) =>
new TimeAndSalesSettings(security_criteria, new FilterStates(), null, DefaultColumns.default_sorting_criterion.ToList());
public TimeAndSalesSettings with(SecurityCriteria security_criteria) =>
new TimeAndSalesSettings(security_criteria, filters, grid);
public static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.col_id(), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings time => new GridColumnSettings(Columns.Time.col_id(), DefaultColumnHeaders.Time, width: DefaultColumnWidths.TimeOfDay);
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.col_id(), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings size => new GridColumnSettings(Columns.Size.col_id(), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings aggressor => new GridColumnSettings(Columns.Aggressor.col_id(), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor);
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.col_id(), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(time.id, false);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return time;
yield return price;
yield return size;
yield return aggressor;
yield return security_key;
}
}
}
}
}
@@ -1,26 +1,27 @@
using bts.api;
using bts.co2;
using mbts;
using mbts.co2;
using mbts.spark;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace bts.shell.time_and_sales {
namespace mbts.spark {
class TimeAndSalesModel : IDisposable {
internal readonly SecurityCriteria security_criteria;
static readonly TimeSpan _poll_interval = TimeSpan.FromSeconds(0.4);
Lazy<IObservable<IReadOnlyList<TradeInfo>>> _trades;
IDisposable _dispose;
public TimeAndSalesModel(IBus bus, MDConsumer consumer, IObservable<SecurityCriteria> security_criteria) {
var curr_channels = new Dictionary<ChannelKey, ChannelConsumer>();
public TimeAndSalesModel(IBus bus, MDConsumer consumer, SecurityCriteria security_criteria) {
this.security_criteria = security_criteria;
var curr_channels = new Dictionary<ChannelKey, ChannelConsumer>();
_trades = new Lazy<IObservable<IReadOnlyList<TradeInfo>>>(() => Observable.Interval(_poll_interval).Select(_ => {
var copy = curr_channels;
return copy.Values.SelectMany(c => c.read_trades()).ToList();
}));
_dispose = security_criteria.SelectManyInSeries(sc =>sc.subscribe_md_map(bus)).Subscribe(
_dispose = security_criteria.subscribe_md_map(bus).Subscribe(
md_map => {
var copy = new Dictionary<ChannelKey, ChannelConsumer>(curr_channels);
copy.sync_with_demux(md_map, get_channel_key, get_channel_consumer, update_channel_consumer);
@@ -94,4 +95,4 @@ namespace bts.shell.time_and_sales {
}
}
}
}
}
@@ -1,70 +0,0 @@
using bts.api;
using bts.co2;
using bts.utils;
using bts.utils.csv;
using mbts;
using mbts.spark;
using mbts.ui.controls;
using mbts.ui.shell;
using mbts.utils;
using System;
using System.Collections.Generic;
namespace bts.shell.time_and_sales {
class TimeAndSalesRowVM : ViewModelBase, DIYGrid.IRowVM, ISecurityRow, IEquatable<TimeAndSalesRowVM> {
// key comparer guarentees deterministic sorting
// if the rows in the collection have unique row_keys.
public static readonly IComparer<TimeAndSalesRowVM> key_comparer =
new ComparerBuilder<TimeAndSalesRowVM>(new[] {
Comparer<DateTime>.Default.wrap<DateTime, TimeAndSalesRowVM>(r => r.time),
Comparer<int>.Default.wrap<int, TimeAndSalesRowVM>(r => r.row_key)
})
.to_comparer()
.to_safe();
readonly TradeMessageImmutable _trade;
public TimeAndSalesRowVM(int row_key, SecurityImmutable security, TradeMessageImmutable trade) {
this.row_key = row_key;
this.security = security;
_trade = trade;
trade_key = trade.Key;
}
public event Action invalidated;
public int row_key { get; }
public uint trade_key { get; }
public uint security_key => security.Id;
public SecurityImmutable security { get; }
public DateTime time => ((ulong)_trade.Time).lldatetime().ToLocalTime();
public double price => _trade.price();
public uint size => _trade.Size;
public AggressorSide aggressor_side => _trade.AggressorSide;
public void raise_invalidated() =>
invalidated?.Invoke();
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(
typeof(TimeAndSalesRowVM),
new string[] {
nameof(security),
nameof(time),
nameof(price),
nameof(size),
nameof(aggressor_side),
nameof(security_key),
});
public bool Equals(TimeAndSalesRowVM other) =>
other != null &&
row_key == other.row_key;
public override bool Equals(object obj) =>
Equals(obj as TimeAndSalesRowVM);
public override int GetHashCode() =>
row_key;
}
}
@@ -1,363 +1,87 @@
using bts.api;
using bts.co2;
using bts.collections;
using bts.utils;
using mbts;
using bts.utils.csv;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
using mbts.ui.shell;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Windows.Controls;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Media;
using static bts.shell.TimeAndSalesWindowFactory;
using static mbts.ui.controls.DIYGrid;
namespace bts.shell.time_and_sales {
class TimeAndSalesVM : DisposableViewModelBase, IHandleGridSorting, IHandleRowSelection {
const int _max_row_count = 1000;
static int _id_counter;
static IReadOnlyList<Func<TimeAndSalesVM, IDataGridColumnFilter<TimeAndSalesRowVM>>> _filter_getters =
ObjectSnapshot.property_delegates_of_type<TimeAndSalesVM, IDataGridColumnFilter<TimeAndSalesRowVM>>();
namespace mbts.spark {
class TimeAndSalesVM : SecurityGrid<TimeAndSalesVM.Row, TradeInfo> {
readonly IBus _bus;
readonly TimeAndSalesModel _model;
IWindowSettingsManager<GridSettings> _settings_manager;
readonly ColumnSortManager _sort_manager;
readonly RowFactory _row_factory = new RowFactory();
readonly LoggerContext _log;
readonly IWindowSettingsManager<GridSettings> _settings_manager;
readonly DeferrableActionWrapper
_refresh_filter,
_refresh_sort,
_refresh_selected_rows;
IComparer<TimeAndSalesRowVM> _comparer_backing;
Func<TimeAndSalesRowVM, bool> _filter_backing;
ImmutableHashSet<TimeAndSalesRowVM> _selected_rows = ImmutableHashSet<TimeAndSalesRowVM>.Empty;
ImmutableList<TimeAndSalesRowVM> _all_rows = ImmutableList<TimeAndSalesRowVM>.Empty;
ImmutableList<TradeInfo> _buffered_trades;
/// Although the sorting criteria can be obtained from the <see cref="_sort_manager"/> (<see cref="ColumnSortManager.SortingCriteria"/>),
/// a local copy is kept because the settings collector may call <see cref="create"/> from a thread
/// other than the dispatcher thread associated with this instance as a viewmodel,
/// and the <see cref="ColumnSortManager.SortingCriteria"/> property is not thread-safe.
/// Therefore, <see cref="create"/> returns this local copy, and thus avoids accessing the non-thread-safe property.
ImmutableList<SortingCriteria> _sorting_criteria = ImmutableList<SortingCriteria>.Empty;
TimeAndSalesRowVM _current_row;
bool _is_paused;
int
_load_settings_count,
_filter_change_count,
_sort_change_count;
public TimeAndSalesVM(IWindowSettingsManager<GridSettings> settings_manager) {
_sort_manager = new ColumnSortManager(columns.WrapSelector(c => c as ISortableDIYGridColumn, null), TimeAndSalesView.Columns.RowKey.ToString());
_comparer_backing = _sort_manager.BuildComparer(_get_column_comparer);
_log = new LoggerContext(Log.Ui, $"Time And Sales VM ({Interlocked.Increment(ref _id_counter)})", null);
public TimeAndSalesVM(IBus bus, IWindowSettingsManager<GridSettings> settings_manager, TimeAndSalesModel model, StartupData startup_data)
: base(RXCollection.CreateBuffer(model.trades, 1000), Enumerable.Empty<SortingCriteria>()) {
_bus = bus;
_model = model;
_settings_manager = settings_manager;
// deferrable filter/sort refresh
_refresh_filter = new DeferrableActionWrapper(_refresh_filter_impl);
_refresh_sort = new DeferrableActionWrapper(_refresh_sort_impl);
_refresh_selected_rows = new DeferrableActionWrapper(() => selected_rows_changed?.Invoke(_selected_rows));
// grid settings
grid_settings_manager = new SimpleGridSettingsManager<TimeAndSalesRowVM>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, rows);
grid_settings_manager.load_from(settings_manager.current_settings());
// Columns
add_column(
TimeAndSalesView.Columns.Security,
r => r.security.ToString(),
DefaultColumnHeaders.Security,
DefaultColumnWidths.Security,
security_filter,
DataGridFilterKnownTemplates.Regex);
add_column(
TimeAndSalesView.Columns.Time,
r => r.time.ToString("h:mm:ss tt"),
DefaultColumnHeaders.Time,
DefaultColumnWidths.Time,
time_filter,
DataGridFilterKnownTemplates.TimeFilter);
add_column(
TimeAndSalesView.Columns.Price,
r => r.security.spec_or_default().format(r.price),
DefaultColumnHeaders.Price,
DefaultColumnWidths.Price,
price_filter,
DataGridFilterKnownTemplates.DoubleMinMax,
System.Windows.HorizontalAlignment.Right);
add_column(
TimeAndSalesView.Columns.Size,
r => r.size.ToString(),
DefaultColumnHeaders.Size,
DefaultColumnWidths.Size,
size_filter,
DataGridFilterKnownTemplates.UIntMinMax,
System.Windows.HorizontalAlignment.Right);
add_column(
TimeAndSalesView.Columns.Aggressor,
r => r.aggressor_side.ToString(),
DefaultColumnHeaders.Aggressor,
DefaultColumnWidths.Aggressor,
aggressor_filter,
DataGridFilterKnownTemplates.EnumValues);
add_column(
TimeAndSalesView.Columns.SecurityKey,
r => r.security_key.ToString(),
DefaultColumnHeaders.SecurityKey,
DefaultColumnWidths.SecurityKey,
security_key_filter,
DataGridFilterKnownTemplates.Regex);
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager);
// commands
show_ladder_cmd = new DelegateCommand(() => {
if (current_row != null)
request_show_ladder?.Invoke(current_row);
});
// init IDisposable resources
var resources = new List<IDisposable>();
resources.AddRange(_filters.Select(f => {
f.filter_changed += _refresh_filter.execute;
return Disposable.Create(() => f.filter_changed -= _refresh_filter.execute);
}));
_sort_manager.active_sorting_changed += _refresh_sort.execute;
resources.Add(Disposable.Create(() => _sort_manager.active_sorting_changed -= _refresh_sort.execute));
_dispose = Disposable.Create(resources.ToArray());
// local methods
void add_column(
TimeAndSalesView.Columns col_key,
Func<TimeAndSalesRowVM, string> get_cell_text,
string header,
double width,
IDataGridColumnFilter filter,
string filter_template,
System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
columns.Add(new RenderTextColumn<TimeAndSalesRowVM, TimeAndSalesView.Columns>(
col_key, get_cell_text, filter, filter_template, _get_aggressor_brush) {
id = col_key.col_id(), Header = header, Width = width, horizontal_alignment = h_alignment
}
);
}
public event Action<ImmutableHashSet<TimeAndSalesRowVM>> selected_rows_changed;
public event Action<TimeAndSalesRowVM> current_row_changed, request_show_ladder;
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
public TimeAndSalesRowVM current_row {
get => _current_row;
set {
if (Set(ref _current_row, value))
current_row_changed?.Invoke(value);
}
}
public ObservableCollection<TimeAndSalesRowVM> rows { get; } = new ObservableCollection<TimeAndSalesRowVM>();
public ImmutableHashSet<TimeAndSalesRowVM> selected_rows => _selected_rows;
public int total_row_count => _all_rows.Count;
public SimpleGridSettingsManager<TimeAndSalesRowVM> grid_settings_manager { get; }
public bool is_paused {
get => _is_paused;
set {
if (_is_paused != value) {
_is_paused = value;
if (value) {
_buffered_trades = ImmutableList<TradeInfo>.Empty;
} else {
var resume = _buffered_trades;
_buffered_trades = null;
on_new_trades(resume);
}
}
}
}
// filters
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, TimeFilterTuple> time_filter { get; } = new TimeFilterVM<TimeAndSalesRowVM>(r => r.time);
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, string> security_filter { get; } = new RegexFilterVM<TimeAndSalesRowVM>(r => r.security.ToString(), nameof(TimeAndSalesRowVM.security));
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<TimeAndSalesRowVM>(r => r.price);
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, UIntMinMaxState> size_filter { get; } = new UIntMinMax<TimeAndSalesRowVM>(r => r.size, nameof(TimeAndSalesRowVM.size));
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, AggressorSide[]> aggressor_filter { get; } = DiscreteValuesFilterVM.Create<TimeAndSalesRowVM, AggressorSide>(EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side);
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, string> security_key_filter { get; } = new RegexFilterVM<TimeAndSalesRowVM>(r => r.security_key.ToString());
// commands
public ICommand show_ladder_cmd { get; }
IComparer<TimeAndSalesRowVM> _comparer {
get => _comparer_backing;
set {
if (value == null) {
_log.warn("Comparer should not be set to null, will use the default comparer instead (based on trade time, then row key).");
_comparer_backing = new ComparerBuilder<TimeAndSalesRowVM>(new[] {
Comparer<DateTime>.Default.wrap<DateTime, TimeAndSalesRowVM>(r => r.time),
Comparer<int>.Default.wrap<int, TimeAndSalesRowVM>(r => r.row_key)
})
.to_comparer()
.to_safe();
} else {
_comparer_backing = value;
}
var sorted_rows = rows.ToArray();
var snapshot = _selected_rows;
Array.Sort(sorted_rows, _comparer_backing);
using (_refresh_selected_rows.defer_execution()) {
for (int i = 0; i < sorted_rows.Length; i++) {
if (rows[i].row_key != sorted_rows[i].row_key)
rows[i] = sorted_rows[i];
}
}
}
}
Func<TimeAndSalesRowVM, bool> _filter {
get => _filter_backing;
set {
var old_filter = _filter_backing;
var new_filter = value;
_filter_backing = new_filter;
if (new_filter != null) {
for (int i = 0; i < rows.Count; i++) {
if (!new_filter(rows[i]))
rows.RemoveAt(i);
}
rows.RemoveWhere(r => !new_filter(r));
if (old_filter != null) {
foreach (var r in _all_rows) {
if (!old_filter(r) && new_filter(r))
rows.InsertSorted(r, _comparer);
}
}
} else if (old_filter != null) {
foreach (var r in _all_rows) {
if (!old_filter(r))
rows.InsertSorted(r, _comparer);
}
}
}
}
IEnumerable<IDataGridColumnFilter<TimeAndSalesRowVM>> _filters =>
_filter_getters.Select(fn => fn(this));
public (FilterStates filters, IReadOnlyList<SortingCriteria> sorting_criteria, GridSettings grid_settings) create() =>
(_get_filter_states(), _sorting_criteria, grid_settings_manager.create());
public void load(FilterStates filters, IEnumerable<SortingCriteria> sorting_criteria, GridSettings grid_settings) {
using (_refresh_filter.defer_execution())
using (_refresh_sort.defer_execution()) {
_log.info($"begin load settings ({++_load_settings_count})...");
var timer = new Stopwatch();
timer.Start();
grid_settings_manager.load_from(grid_settings);
_sort_manager.SortingCriteria = sorting_criteria;
// load filters
show_ladder = DelegateCommandParamObserver.Create(current_row_observable, r => _show_ladder_async(r).ignore(), r => r != null);
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
if (startup_data.filters is FilterStates filters) {
time_filter.load_state(filters.time);
security_filter.load_state(filters.security);
price_filter.load_state(filters.price);
size_filter.load_state(filters.size);
aggressor_filter.load_state(filters.aggressor_sides);
security_key_filter.load_state(filters.security_key);
//
timer.Stop();
_log.info($"load settings ({_load_settings_count}) took {timer.ElapsedMilliseconds:0.00}ms.");
}
// Columns
add_column(TimeAndSalesView.Columns.Security, DefaultColumnHeaders.Security, DefaultColumnWidths.Security, nameof(Row.security), security_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.Time, DefaultColumnHeaders.Time, DefaultColumnWidths.Time, nameof(Row.time), time_filter, DataGridFilterKnownTemplates.TimeFilter);
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DefaultColumnWidths.Aggressor, nameof(Row.aggressor_side), aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DefaultColumnWidths.SecurityKey, nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex);
void add_column(TimeAndSalesView.Columns col_key, string header, double width, string sort_member_path, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
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);
grid_settings_manager.load_from(settings_manager.current_settings());
_resources.AddRange(
show_ladder,
DataGridFilterHelper.hook_filters(
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, security_key_filter),
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager)
);
rows_view.set_sort_descriptions(startup_data.sorting_criterion, columns);
}
public void on_new_trades(IReadOnlyList<TradeInfo> trades) {
/// The total row count is limited by <see cref="_max_row_count"/>;
/// trades are kept in a circular buffer (FIFO).
/// If paused, just need to add trades to <see cref="_buffered_trades"/>.
if (is_paused) {
if (_buffered_trades == null)
_buffered_trades = ImmutableList<TradeInfo>.Empty;
if (trades.Count >= _max_row_count) {
_buffered_trades = _buffered_trades
.Clear()
.AddRange(ReadOnlyListSubRangeWrapper.Create(trades, trades.Count - _max_row_count, _max_row_count));
} else {
int overflow_count = _buffered_trades.Count + trades.Count - _max_row_count;
if (overflow_count > 0) {
var rows_to_remove = _buffered_trades.WrapSubRange(0, overflow_count);
_buffered_trades = _buffered_trades.RemoveRange(0, overflow_count);
}
_buffered_trades = _buffered_trades.AddRange(trades);
}
return;
}
/// If not paused, update in the normal fasion:
/// add to <see cref="_all_rows"/> and <see cref="rows"/>
if (trades.Count >= _max_row_count) {
trades = ReadOnlyListSubRangeWrapper.Create(trades, trades.Count - _max_row_count, _max_row_count);
_all_rows = _all_rows.Clear();
rows.Clear();
} else {
int overflow_count = _all_rows.Count + trades.Count - _max_row_count;
if (overflow_count > 0) {
var rows_to_remove = _all_rows.WrapSubRange(0, overflow_count);
_all_rows = _all_rows.RemoveRange(0, overflow_count);
rows.RemoveSorted(rows_to_remove, _comparer);
}
}
var all_rows_bd = _all_rows.ToBuilder();
foreach (var t in trades) {
var r = _row_factory.create(t.security, t.trade);
all_rows_bd.Add(r);
if (_filter_backing == null || _filter_backing(r))
rows.InsertSorted(r, _comparer);
}
_all_rows = all_rows_bd.ToImmutable();
RaisePropertyChanged(nameof(total_row_count));
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
public DelegateCommandParamObserver<Row> show_ladder { get; }
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
public GridSettings get_grid_save_state() {
if (_settings_manager.instance_settings == null)
return null;
else
return grid_settings_manager.create();
}
public void on_sorting(object sender, DIYGridSortingEventArgs e) {
try {
if (e.Column is ISortableDIYGridColumn sc) {
_log.info($"handling sort event for column {sc.id}.");
_sort_manager.OnColumnClick(sc, KeyboardExtensions.IsShiftDown());
var sorted_rows = rows.ToArray();
var snapshot = _selected_rows;
_refresh_sort_impl();
} else {
_log.warn($"ignoring sort event because column '{e.Column?.id}' does not implement {nameof(ISortableDIYGridColumn)}.");
}
} catch (Exception ex) {
_log.error($"error handling sort event: {ex}.");
}
}
public StartupData create() =>
new StartupData(_model.security_criteria, _get_filter_states(), get_grid_save_state(), rows_view.get_sorting_criteria(columns));
public void row_selection_changed(SelectionChangedEventArgs e) {
var bd = _selected_rows.ToBuilder();
if (e.AddedItems != null)
bd.AddRange(e.AddedItems.OfType<TimeAndSalesRowVM>());
if (e.RemovedItems != null)
bd.removeRange(e.RemovedItems.OfType<TimeAndSalesRowVM>());
_selected_rows = bd.ToImmutable();
_refresh_selected_rows.execute();
}
static IComparer<TimeAndSalesRowVM> _get_column_comparer(string column_name) =>
_get_column_comparer(EnumHelper.parse<TimeAndSalesView.Columns>(column_name));
static IComparer<TimeAndSalesRowVM> _get_column_comparer(TimeAndSalesView.Columns column) {
switch (column) {
case TimeAndSalesView.Columns.Aggressor: return wrap(r => r.aggressor_side);
case TimeAndSalesView.Columns.Time: return wrap(r => r.time);
case TimeAndSalesView.Columns.Security: return wrap(r => r.security);
case TimeAndSalesView.Columns.Price: return wrap(r => r.price);
case TimeAndSalesView.Columns.Size: return wrap(r => r.size);
case TimeAndSalesView.Columns.SecurityKey: return wrap(r => r.security_key);
case TimeAndSalesView.Columns.RowKey: return wrap(r => r.row_key);
default: throw ExceptionBuilder.Argument.unexpected_case(column, nameof(column));
}
IComparer<TimeAndSalesRowVM> wrap<T>(Func<TimeAndSalesRowVM, T> selector) =>
Comparer<T>.Default.wrap(selector);
}
Brush _get_aggressor_brush(TimeAndSalesRowVM r) {
private Brush _get_aggressor_brush(Row r) {
BTSColor c;
Brush b;
if (r.aggressor_side == AggressorSide.Buy) c = BTSColors.MarketData.bid;
@@ -367,6 +91,14 @@ namespace bts.shell.time_and_sales {
return b;
}
#region Filters
public IDataGridColumnPersistentFilter<Row, TimeFilterTuple> time_filter { get; } = new TimeFilterVM<Row>(r => r.time, nameof(Row.time));
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<Row>(r => r.price, nameof(Row.price));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> size_filter { get; } = new UIntMinMax<Row>(r => r.size, nameof(Row.size));
public IDataGridColumnPersistentFilter<Row, AggressorSide[]> aggressor_filter { get; } = DiscreteValuesFilterVM.Create<Row, AggressorSide>(EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side, nameof(Row.aggressor_side));
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
FilterStates _get_filter_states() =>
new FilterStates {
time = time_filter.get_state(),
@@ -376,35 +108,86 @@ namespace bts.shell.time_and_sales {
security_key = security_key_filter.get_state(),
aggressor_sides = aggressor_filter.get_state()
};
#endregion
void _refresh_sort_impl() {
_log.info($"begin change sort ({++_sort_change_count})...");
var timer = new Stopwatch();
timer.Start();
_comparer = _sort_manager.BuildComparer(_get_column_comparer, TimeAndSalesRowVM.key_comparer);
_sorting_criteria = ImmutableList.CreateRange(_sort_manager.SortingCriteria);
timer.Stop();
_log.info($"sort change ({_sort_change_count}) took {timer.ElapsedMilliseconds:0.00}ms.");
protected override IEnumerable<SortingCriteria> get_default_sorting() {
yield return new SortingCriteria(nameof(Row.time), false);
}
void _refresh_filter_impl() {
_log.info($"begin change filter ({++_filter_change_count})...");
var timer = new Stopwatch();
timer.Start();
_filter = _get_filter_states().build_predicate();
timer.Stop();
_log.info($"filter change ({_filter_change_count}) took {timer.ElapsedMilliseconds:0.00}ms.");
protected override Row row_factory(TradeInfo row_model) =>
new Row(this, row_model.security, row_model.trade);
async Task _show_ladder_async(Row row) {
var smd_lookup = await SparkUtils.get_security_md_async(_bus, row.security.Id);
if (smd_lookup.success) _bus.post(SparkTags.ShowLadderTag, smd_lookup.result);
}
class RowFactory {
// the row key must be unique to each row in the collection
int _row_id_counter;
public class Row : SecurityRow, DIYGrid.IRowVM {
readonly uint _trade_key;
public TimeAndSalesRowVM create(TradeInfo trade_info) =>
new TimeAndSalesRowVM(_row_id_counter++, trade_info.security, trade_info.trade);
readonly string
_security_text,
_time_text,
_price_text,
_size_text,
_aggressor_text,
_trade_key_text,
_security_key_text;
public TimeAndSalesRowVM create(SecurityImmutable security, TradeMessageImmutable trade) =>
new TimeAndSalesRowVM(_row_id_counter++, security, trade);
public Row(TimeAndSalesVM parent, SecurityImmutable security, TradeMessageImmutable trade) : base(security) {
_trade_key = trade.Key;
time = ((ulong)trade.Time).lldatetime().ToLocalTime();
price = trade.price();
size = trade.Size;
aggressor_side = trade.AggressorSide;
show_ladder = new DelegateCommand(() => parent._show_ladder_async(this).ignore());
_security_text = security.ToString();
_time_text = time.ToString("M/d h:mm:ss tt");
_price_text = security.ContractSpec.format(price);
_size_text = size.ToString();
_aggressor_text = aggressor_side.ToString();
_trade_key_text = _trade_key.ToString();
_security_key_text = security_key.ToString();
}
public event Action invalidated;
public uint trade_key => _trade_key;
public DateTime time { get; }
public double price { get; }
public uint size { get; }
public AggressorSide aggressor_side { get; }
public ICommand show_ladder { get; }
public string this[TimeAndSalesView.Columns col] {
get {
switch (col) {
case TimeAndSalesView.Columns.Security: return _security_text;
case TimeAndSalesView.Columns.Time: return _time_text;
case TimeAndSalesView.Columns.Price: return _price_text;
case TimeAndSalesView.Columns.Size: return _size_text;
case TimeAndSalesView.Columns.Aggressor: return _aggressor_text;
case TimeAndSalesView.Columns.SecurityKey: return _security_key_text;
default: return null;
}
}
}
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();
}
}
}
}
@@ -1,10 +1,10 @@
<UserControl
x:Class="bts.shell.time_and_sales.TimeAndSalesView"
x:Class="mbts.ui.spark.TimeAndSalesView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:tas="clr-namespace:bts.shell.time_and_sales"
xmlns:usp="clr-namespace:mbts.ui.spark"
FocusManager.IsFocusScope="True"
Background="{StaticResource CommonBackground}"
>
@@ -26,14 +26,13 @@
x:Name="_grid"
Grid.Row="0"
Columns="{Binding columns}"
Rows="{Binding rows}"
Rows="{Binding rows_view}"
CurrentRow="{Binding current_row}"
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
>
>
<c:DIYGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding show_ladder_cmd}"/>
<KeyBinding Key="Return" Command="{Binding show_ladder_cmd}"/>
<KeyBinding Key="A" Modifiers="Ctrl" Command="{Binding select_all_cmd}"/>
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
</c:DIYGrid.InputBindings>
<c:DIYGrid.CommandBindings>
<CommandBinding
@@ -44,18 +43,13 @@
</c:DIYGrid.CommandBindings>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<tas:TimeAndSalesRowView/>
<usp:TimeAndSalesRowView/>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<TextBlock>
<Run Text="Trades:"/>
<Run Text="{Binding rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding total_row_count, Mode=OneWay}" />
</TextBlock>
<TextBlock Text=" "/>
</StatusBarItem>
</StatusBar>
</Grid>
@@ -1,21 +1,17 @@
using mbts.ui.controls;
using mbts.spark;
using mbts.ui.controls;
using System.Windows.Controls;
namespace bts.shell.time_and_sales {
namespace mbts.ui.spark {
/// <summary>
/// Interaction logic for TimeAndSalesView.xaml
/// </summary>
partial class TimeAndSalesView : UserControl {
public enum Columns { Time, Security, Price, Size, SecurityKey, Aggressor, RowKey }
public enum Columns { Time, Security, Price, Size, OrderType, SecurityKey, Aggressor }
public TimeAndSalesView() =>
InitializeComponent();
}
class TimeAndSalesRowView : DIYGrid.HighPerformanceRow<TimeAndSalesRowVM> { }
static class TimeAndSalesViewColumnsExtensions {
public static string col_id(this TimeAndSalesView.Columns column) =>
column.ToString();
}
}
class TimeAndSalesRowView : DIYGrid.HighPerformanceRow<TimeAndSalesVM.Row, TimeAndSalesView.Columns> { }
}
@@ -1,218 +1,127 @@
using bts.api;
using bts;
using bts.api;
using bts.shell;
using bts.shell.fills;
using bts.spark;
using bts.view;
using bts.utils.csv;
using mbts;
using mbts.co2;
using mbts.persist;
using mbts.spark;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.shell;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Media;
using static bts.shell.TimeAndSalesWindowFactory;
namespace bts.shell.time_and_sales {
class TimeAndSalesWindow : SparkWindow<TimeAndSalesSettings>, ISharedWindow {
namespace mbts.spark {
class TimeAndSalesWindow : SparkWindow<StartupData>, ISharedWindow {
const string _window_type = "TimeAndSales", _window_type_display_name = "Time And Sales";
readonly MDConsumer _consumer;
readonly TimeAndSalesWindowFactory _factory;
readonly IWindowSettingsManager<GridSettings> _settings_manager;
readonly bool _can_pause_updates;
TimeAndSalesVM _vm;
SingleValuePump<TimeAndSalesVM> _vm;
TimeAndSalesModel _model;
SecurityCriteria _security_criteria_backing;
TimeAndSalesSettings _state;
StartupData _state;
TimeAndSalesView _view;
SparkCommand[] _commands;
SparkMenuDisplayVM _menu_display_vm;
IDisposable _subscriptions;
public TimeAndSalesWindow(TimeAndSalesWindowFactory factory, IBus bus, MDConsumer consumer, TimeAndSalesSettings state, ImageSource icon, IWindowSettingsManager<GridSettings> settings_manager, bool can_pause_updates)
public TimeAndSalesWindow(TimeAndSalesWindowFactory factory, IBus bus, MDConsumer consumer, StartupData state, ImageSource icon, IWindowSettingsManager<GridSettings> settings_manager)
: base("Time And Sales", bus, factory, SparkCommands.ShowTimeAndSales.icon) {
Log.assert(state != null);
_consumer = consumer;
_factory = factory;
_state = state ?? TimeAndSalesSettings.Default(new SecurityCriteria());
if (_state.security_criteria == null)
_state = _state.with(new SecurityCriteria());
_security_criteria_backing = _state.security_criteria;
_state = state;
_settings_manager = settings_manager;
_can_pause_updates = can_pause_updates;
thread_id = mbts.config.Configuration.get<bts.config.MultiThreadedWindows>().time_and_sales ? _window_type : null;
Icon.Freeze();
}
event Action<SecurityCriteria> _security_criteria_changed;
public override string window_type => _window_type;
public override IEnumerable<SparkCommand> Commands => _commands;
public override UIElement Content => _view;
public override double MinWidth => TimeAndSalesSettings.DefaultGridSettings.columns.Sum(gcs => gcs.width)
public override double MinWidth => StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public string thread_id { get; }
public override TimeAndSalesSettings SaveState {
get {
_ensure_state(true);
return _state;
}
}
SecurityCriteria _security_criteria {
get => _security_criteria_backing;
set {
_security_criteria_backing = value;
_state = null;
_security_criteria_changed?.Invoke(value);
}
}
public override StartupData SaveState => _state = _vm.value.create();
public override void Dispose() =>
_dispose_resources();
public void thread_init() {
_dispose_resources();
_ensure_state();
if (!_state.use_defaults)
_settings_manager.instance_settings = _state.grid;
_menu_display_vm = new SparkMenuDisplayVM();
_menu_display_vm.load_from(_state.menu_display);
_vm = new TimeAndSalesVM(_settings_manager);
var selected_rows_src = Observable.Create<ImmutableHashSet<TimeAndSalesRowVM>>(o => {
_vm.selected_rows_changed += o.OnNext;
return Disposable.Create(() => _vm.selected_rows_changed -= o.OnNext);
});
var current_row_src = Observable.Create<TimeAndSalesRowVM>(o => {
if (_vm.current_row != null)
o.OnNext(_vm.current_row);
_vm.current_row_changed += o.OnNext;
return Disposable.Create(() => _vm.current_row_changed -= o.OnNext);
});
var current_sec_key_src = current_row_src.Select(r => {
var result = ImmutableHashSet<uint>.Empty;
if (r != null)
result = result.Add(r.security_key);
return result;
});
_vm = new SingleValuePump<TimeAndSalesVM>();
var connectable_builder = new ConnectableSeriesBuilder<TimeAndSalesVM>(_vm.observable);
var update_securities_cmd = SparkCommands.change_securities_callback(_window_type_display_name);
var security_criteria_changed = new Action<SecurityCriteria>(sc => { });
SparkCommand toggle_is_paused_cmd = null;
toggle_is_paused_cmd = new SparkCommandCommand(get_is_paused_cmd_title(), Icons.Pause, new DelegateCommand(() => {
_vm.is_paused = !_vm.is_paused;
toggle_is_paused_cmd.title = get_is_paused_cmd_title();
})) {
show_in_toolbar = _can_pause_updates,
show_in_context_menu = _can_pause_updates
};
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[] {
// csv export
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.rows.ToCsv(TimeAndSalesRowVM.csv_ordered_properties), "TaS")),
// ladder
SparkCommands.ShowLadderAutoLookup(SparkCommands.KeyGestures.ShowLadder)
.complete(current_sec_key_src, sec_keys => sec_keys.Count > 0),
// ticket
SparkCommands.ShowOrderTicketAutoLookup(SparkCommands.KeyGestures.ShowOrderTicket)
.complete(current_sec_key_src, sec_keys => sec_keys.Count > 0),
// fills
SparkCommands.ShowFills(SparkCommands.KeyGestures.ShowFills)
.complete(selected_rows_src, always_true, get_fills_startup_data),
// mview
SparkCommands.ShowMarketView
.complete(selected_rows_src, always_true, rows => rows.Count > 0 ? new SecurityCriteria(rows.Select(r => r.security)) : _security_criteria),
// pause
toggle_is_paused_cmd,
// change securities
SparkCommands.change_securities_command(() => (update_securities_cmd, _security_criteria)),
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),
SparkCommands.ShowMarketView.complete(selected_secs_source, always_true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
// 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>(
_settings_manager,
s => s.time_and_sales_grid,
UserSettings.FileExtensions.GRID,
s => _vm.grid_settings_manager.create_vm(
s => _vm.value.grid_settings_manager.create_vm(
// we need to get the most up to date GridSettings when launching b/c users may
// have changed some settings on the grid directly, like column widths.
s.with(_vm.columns.Select(c => c.create()))
s.with(_vm.value.columns.Select(c => c.create()))
)
);
vm.SubscribeDisposed(literal_title_source.OnDispatcher().Subscribe(t => vm.title = t));
var dialog = new ShellSettingsDialog { DataContext = vm };
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
})),
_menu_display_vm.spark_cmd
}))
};
_vm.load(_state.filters, _state.sorting_criterion, _state.grid);
_vm.request_show_ladder += show_ladder;
var security_criteria_src = _bus.subscribe(update_securities_cmd.tag);
var subs = new List<IDisposable> {
security_criteria_src.Subscribe(sc => _security_criteria = sc),
Disposable.Create(() => _vm.request_show_ladder -= show_ladder)
};
_model = new TimeAndSalesModel(_bus, _consumer, Observable.Create<SecurityCriteria>(o => {
o.OnNext(_security_criteria);
return security_criteria_src.Subscribe(o);
}));
subs.Add(_model.trades.OnDispatcher().Subscribe(_vm.on_new_trades));
_subscriptions = Disposable.Create(subs.ToArray());
if (_view == null)
_view = new TimeAndSalesView();
_view.DataContext = _vm;
// local methods
_subscriptions = Disposable.Create(
_bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(update_criteria),
connectable_builder.connect_all());
_initialize();
bool always_true<T>(T _) => true;
FillsSettings get_fills_startup_data(ImmutableHashSet<TimeAndSalesRowVM> rows) =>
FillsSettings get_fills_startup_data(IReadOnlyList<SecurityImmutable> secs) =>
new FillsSettings(new FillsFilterStates {
security = rows.Count > 0 ? new SecurityCriteria(rows.Select(r => r.security)) : _security_criteria
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
});
string get_is_paused_cmd_title() =>
$"{(_vm.is_paused ? "Resume" : "Pause")} Updates";
void show_ladder(TimeAndSalesRowVM row) {
if (row != null)
_bus.post(SparkTags.ShowLadderAutoLookupTag, ImmutableHashSet.Create(new[] { row.security_key }));
void update_criteria(SecurityCriteria c) {
_state = _vm.value.create().with(c);
_initialize();
}
}
protected override void OnParentViewCreated(SparkWindowView parent_view) =>
_menu_display_vm.safe_bind_to_view(parent_view);
void _dispose_resources() {
Disposable.dispose(ref _subscriptions);
Disposable.dispose(ref _vm);
Disposable.dispose(ref _model);
if (_commands != null) {
_commands.OfType<IDisposable>().DisposeAll();
_commands = null;
}
Disposable.dispose(ref _model);
Disposable.dispose(ref _vm);
}
void _ensure_state(bool force_recreate = false) {
if (_state == null || force_recreate) {
if (_vm == null) {
_state = TimeAndSalesSettings.Default(_security_criteria);
} else {
var (filters, sorting, grid) = _vm.create();
_state = new TimeAndSalesSettings(_security_criteria, filters, grid, sorting.ToList()) {
use_defaults = _settings_manager.instance_settings == null
};
}
// should be safe to assume _menu_display_vm and _vm will either both be null, or both be non null,
// but redundant null checks don't hurt, and just may prevent a crash.
if (_menu_display_vm != null)
_state.menu_display = _menu_display_vm.safe_create();
}
void _initialize() {
Disposable.dispose(ref _model);
_model = new TimeAndSalesModel(_bus, _consumer, _state.security_criteria);
// the previous _vm.value is automatically disposed when _vm.value is set - no need to dispose it here
_vm.value = new TimeAndSalesVM(_bus, _settings_manager, _model, _state);
if (_view == null)
_view = new TimeAndSalesView();
_view.DataContext = _vm.value;
}
}
}
}
@@ -1,32 +1,33 @@
using bts.api;
using bts.spark;
using bts.shell.time_and_sales;
using mbts;
using mbts.co2;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Columns = bts.shell.time_and_sales.TimeAndSalesView.Columns;
using mbts.config;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.TimeAndSalesView;
namespace bts.shell {
class TimeAndSalesWindowFactory : ShellWindowFactory<TimeAndSalesSettings> {
class TimeAndSalesWindowFactory : ShellWindowFactory<TimeAndSalesWindowFactory.StartupData> {
readonly IBus _bus;
readonly MDConsumer _consumer;
readonly UserSettingsHub _settings_hub;
readonly TimeAndSalesConfig _time_and_sales_conf;
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
public TimeAndSalesWindowFactory(IShellModule module, IBus bus, MDConsumer consumer, UserSettingsHub settings_hub) : base(module, "TimeAndSales") {
_bus = bus;
_consumer = consumer;
_settings_hub = settings_hub;
_time_and_sales_conf = new TimeAndSalesConfig(Configuration.get<bts.config.TimeAndSalesOptions>());
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
bus.subscribe(SparkTags.ShowTimeAndSalesTag).OnDispatcher().Subscribe(show)
@@ -35,21 +36,91 @@ namespace bts.shell {
public static string col_id(Columns col) => col.ToString();
public override Task<IShellWindow> load(TimeAndSalesSettings state) =>
public override Task<IShellWindow> load(StartupData state) =>
Task.FromResult<IShellWindow>(new TimeAndSalesWindow(this, _bus, _consumer, state, SparkCommands.PickTimeAndSales.icon,
new WindowSettingsManager<GridSettings>(_settings_hub, state.grid, s => s.time_and_sales_grid, (s, g) => s.with(time_and_sales:g)),
_time_and_sales_conf.can_pause_updates));
new WindowSettingsManager<GridSettings>(_settings_hub, state.grid, s => s.time_and_sales_grid, (s, g) => s.with(time_and_sales:g))));
public async void show(SecurityCriteria criteria) {
var w = await load(new TimeAndSalesSettings(criteria));
var w = await load(new StartupData(criteria));
_bus.post(SparkTags.ShowWindow, w);
}
readonly struct TimeAndSalesConfig {
public readonly bool can_pause_updates;
[ProtoContract]
public class StartupData {
[ProtoMember(1)]
public readonly SecurityCriteria security_criteria;
[ProtoMember(2)]
List<SortingCriteria> _sorting_criterion;
[ProtoMember(3)]
public FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
public TimeAndSalesConfig(bts.config.TimeAndSalesOptions options) =>
can_pause_updates = options.can_pause_updates;
public StartupData() { }
public StartupData(SecurityCriteria security_criteria) :
this(security_criteria, new FilterStates(), null, null) { }
public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List<SortingCriteria> sorting_criterion = null) {
this.security_criteria = security_criteria;
this.filters = filters;
this.grid = grid;
_sorting_criterion = sorting_criterion;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public List<SortingCriteria> sorting_criterion => _sorting_criterion ?? (_sorting_criterion = DefaultColumns.default_sorting_criterion.ToList());
public static StartupData Default(SecurityCriteria security_criteria) =>
new StartupData(security_criteria, new FilterStates(), null, DefaultColumns.default_sorting_criterion.ToList());
public StartupData with(SecurityCriteria security_criteria) =>
new StartupData(security_criteria, filters, grid);
public static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings time => new GridColumnSettings(col_id(Columns.Time), DefaultColumnHeaders.Time, width: DefaultColumnWidths.Time);
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings size => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings aggressor => new GridColumnSettings(col_id(Columns.Aggressor), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible:false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(time.id, false);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return time;
yield return price;
yield return size;
yield return aggressor;
yield return security_key;
}
}
}
}
[ProtoContract]
public class FilterStates {
[ProtoMember(1)]
public TimeFilterTuple time;
[ProtoMember(2)]
public string security;
[ProtoMember(3)]
public DoubleMinMaxState price;
[ProtoMember(4)]
public UIntMinMaxState size;
// [ProtoMember(5)]
// ProtoMember 5 cannot be used further
[ProtoMember(6)]
public string security_key;
[ProtoMember(7)]
public co2.AggressorSide[] aggressor_sides;
}
}
}
}
@@ -140,7 +140,7 @@
<DataTemplate>
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:ConditionalQuoting.Types.PriceType}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
</usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
<usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
<DataTemplate>
<Grid IsEnabled="{Binding enabled}">
@@ -333,61 +333,13 @@
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ContractPickerButton" TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="ToolTip" Value="Change Security"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
x:Name="Bd"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Padding="{TemplateBinding Padding}"
>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="MouseStates">
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd" Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="#24FFFFFF"/>
</Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd" Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="#40FFFFFF"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border.Background>
<SolidColorBrush/>
</Border.Background>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataGridTextColumn x:Key="{x:Static usp:ExecutionStrategyEditor.AddColumnPlaceholderKey}" x:Shared="False" IsReadOnly="True">
<DataGridTextColumn.Header>
<c:PopupControl
ShowExpanderArrow="False"
PopupPlacement="Right"
Header="Add Leg"
Padding="6,0"
ShowExpanderArrow="False" PopupPlacement="Right" Header="Add Leg" Padding="6,0"
Background="{StaticResource Button.Static.BackgroundBrush}"
IsOpen="{Binding IsNewLegPopupOpen, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}, Mode=TwoWay}"
IsEnabled="{Binding CanUserAddNewLeg, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}}"
VerticalAlignment="Center"
>
<msp:ContractPickerView
CloseOnOK="False" CloseOnCancel="False"
@@ -396,7 +348,7 @@
</c:PopupControl>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.RemovableColumnHeaderTemplateKey}" DataType="{x:Type usp:LegSpreaderParamsTuple}">
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.RemovableColumnHeaderTemplateKey}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@@ -404,40 +356,13 @@
</Grid.ColumnDefinitions>
<Button
Command="{Binding RemoveLegCommand, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}}"
CommandParameter="{Binding}"
Style="{StaticResource RoundCloseButton}"
ToolTip="Remove Leg"
CommandParameter="{Binding}" Style="{StaticResource RoundCloseButton}"
/>
<c:ContractPickerPopup
Grid.Column="1"
BorderThickness="0"
ButtonStyle="{StaticResource ContractPickerButton}"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding security}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding ContractPickerModel.all_securities, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}, Mode=OneWay}"
SelectedSecurity="{Binding security}"
ShowDialogSkirt="True"
>
<c:ContractPickerPopup.Background>
<SolidColorBrush/>
</c:ContractPickerPopup.Background>
<c:ContractPickerPopup.HeaderTemplate>
<DataTemplate>
<TextBlock Foreground="{x:Static u:BTSColors.header_text_brush}" Text="{Binding}"/>
</DataTemplate>
</c:ContractPickerPopup.HeaderTemplate>
</c:ContractPickerPopup>
<TextBlock Grid.Column="1" Margin="4,0,0,0" Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="ReadOnlyColumnHeaderTemplate" DataType="{x:Type usp:LegSpreaderParamsTuple}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding security}"
/>
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.NonRemovableColumnHeaderTemplateKey}">
<TextBlock Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
</DataTemplate>
<ControlTemplate x:Key="ExecutionStrategyEditorTemplate" TargetType="usp:ExecutionStrategyEditor">
<Border
@@ -635,14 +560,6 @@
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="5,0"/>
<Setter Property="MinHeight" Value="26"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupItemStyle}"/>
</DataGrid.GroupStyle>
@@ -227,8 +227,8 @@ namespace mbts.ui.spark {
}
DataGridColumn _column_factory(LegSpreaderParamsTuple l) {
var col = new DataGridColumnInternal(l, ParameterCellTemplateConverter, ParameterCellEditingTemplateConverter);
col.HeaderTemplate = TryFindResource(IsNew ? RemovableColumnHeaderTemplateKey : NonRemovableColumnHeaderTemplateKey) as DataTemplate;
var col = new DataGridColumnInternal(l.leg.Security, ParameterCellTemplateConverter, ParameterCellEditingTemplateConverter);
col.HeaderTemplate = (DataTemplate)FindResource(IsNew ? RemovableColumnHeaderTemplateKey : NonRemovableColumnHeaderTemplateKey);
DataGridEx.SetRightBorder(col, new DataGridExVerticalLine(false, true, Colors.DarkGray, 1));
return col;
}
@@ -237,12 +237,10 @@ namespace mbts.ui.spark {
private class DataGridColumnInternal : DataGridDynamicColumn {
readonly IValueConverter _cell_template_converter, _cell_editing_template_converter;
readonly LegSpreaderParamsTuple _leg_tup;
public DataGridColumnInternal(LegSpreaderParamsTuple leg_tup, IValueConverter cell_template_converter, IValueConverter cell_editing_template_converter) {
_leg_tup = leg_tup;
Header = leg_tup;
SetValue(DataGridEx.CellKeyProperty, leg_tup);
public DataGridColumnInternal(Security security, IValueConverter cell_template_converter, IValueConverter cell_editing_template_converter) {
Header = security;
SetValue(DataGridEx.CellKeyProperty, security);
_cell_template_converter = cell_template_converter;
_cell_editing_template_converter = cell_editing_template_converter;
}
@@ -250,8 +248,8 @@ namespace mbts.ui.spark {
// I decided this was an easy and simple enough solution to justify breaking the mvvm paradigm
// The alternative - within strict bounds of the paradigm - is to change the DataGridParameterCellTemplateConverter into an IMultiValueConverter, bound to data_type AND read_only
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) {
if (dataItem is RowVM row_vm) {
if (row_vm.read_only || row_vm.get_cell(_leg_tup) is ICell cell_vm && !cell_vm.enabled)
if (dataItem is SyntheticEditorVM.RowVM row_vm) {
if (row_vm.read_only || row_vm.get_cell(Header) is SyntheticEditorVM.ICell cell_vm && !cell_vm.enabled)
return base.GenerateElement(cell, dataItem);
}
return base.GenerateEditingElement(cell, dataItem);
@@ -354,4 +352,4 @@ namespace mbts.ui.spark {
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
throw new NotImplementedException();
}
}
}
@@ -1,25 +0,0 @@
<UserControl
x:Class="bts.shell.spreader.ContractLegTypeView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:spr="clr-namespace:bts.shell.spreader"
>
<ItemsControl ItemsSource="{Binding Controller.flags, RelativeSource={RelativeSource AncestorType=spr:ContractLegTypeView}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox
Margin="2"
IsChecked="{Binding is_selected}"
Content="{Binding value, Converter={x:Static spr:ContractLegTypeView.LegTypeShortDescConverter}}"
ToolTip="{Binding value, Converter={x:Static spr:ContractLegTypeView.LegTypeLongDescConverter}}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
@@ -1,104 +0,0 @@
using bts.collections;
using mbts;
using mbts.ui;
using mbts.ui.converters;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace bts.shell.spreader {
static class EnumFlagItemsVM {
public static EnumFlagItemsVM<T> create<T>(IEnumerable<T> flags) where T : struct =>
new EnumFlagItemsVM<T>(flags);
}
class EnumFlagItemsVM<T> : ViewModelBase where T : struct {
readonly ObservableCollection<SelectableItemVM<T>> _flags = new ObservableCollection<SelectableItemVM<T>>();
bool _is_busy;
T _result;
public EnumFlagItemsVM()
: this(EnumHelper.get_values<T>()) { }
public EnumFlagItemsVM(IEnumerable<T> flags) {
this.flags = ObservableAutoWrapper.create_read_only_wrapper(_flags);
_flags.AddRange(flags.Select(f => new SelectableItemVM<T>(f)));
foreach (var vm in _flags) {
vm.is_selected_changed += on_is_selected_changed;
void on_is_selected_changed(bool value) {
if (!_is_busy) {
_is_busy = true;
result = value ?
EnumHelper.add_flags(result, vm.value) :
EnumHelper.remove_flags(result, vm.value);
_is_busy = false;
}
}
}
}
public event Action result_changed;
public IReadOnlyObservableCollection<SelectableItemVM<T>> flags { get; }
public T result {
get => _result;
set {
if (!EqualityComparer<T>.Default.Equals(_result, value)) {
_result = value;
if (!_is_busy) {
_is_busy = true;
for (int i = 0; i < flags.Count; i++)
flags[i].is_selected = EnumHelper.has_flags(value, flags[i].value);
_is_busy = false;
}
result_changed?.Invoke();
RaisePropertyChanged();
}
}
}
}
/// <summary>
/// Interaction logic for ContractLegTypeView.xaml
/// </summary>
partial class ContractLegTypeView : UserControl {
public static IValueConverter LegTypeShortDescConverter =>
ValueConverter.create<ContractLegType, string>(SyntheticSecurityParser.display_name);
public static IValueConverter LegTypeLongDescConverter =>
ValueConverter.create<ContractLegType, string>(lt =>
string.Join(" & ", EnumHelper.split_flags(lt).Select(f => EnumHelper.get_display_name(f))));
public ContractLegTypeView() {
InitializeComponent();
Controller.result = Value;
Controller.result_changed += _on_controller_result_changed;
}
public static readonly DependencyProperty ValueProperty =
WPFHelper.create_dp<ContractLegType, ContractLegTypeView>(
nameof(Value),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, v) => {
o.Controller.result_changed -= o._on_controller_result_changed;
o.Controller.result = v;
o.Controller.result_changed += o._on_controller_result_changed;
});
public ContractLegType Value {
get => (ContractLegType)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public EnumFlagItemsVM<ContractLegType> Controller { get; } = EnumFlagItemsVM.create(EnumHelper.get_values_except(ContractLegType.None));
void _on_controller_result_changed() =>
SetCurrentValue(ValueProperty, Controller.result);
}
}
@@ -1,47 +0,0 @@
<Window
x:Class="bts.shell.spreader.SyntheticImportConflictsDialog"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:spr="clr-namespace:bts.shell.spreader"
u:WindowStyle.AllowResize="True"
u:WindowStyle.SizeToContent="WidthAndHeight"
u:WindowStyle.TitleContent="Import Synthetics Conflicts"
Style="{StaticResource BTSDialogTitleTemplate}"
>
<Window.MaxHeight>
<Binding Path="SizeToContent" RelativeSource="{RelativeSource Self}">
<Binding.Converter>
<cv:WindowMaxHeightConverter SizeToContentThreshold="{u:DPIDouble 700}"/>
</Binding.Converter>
</Binding>
</Window.MaxHeight>
<u:WindowStyle.TitleContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Image Height="12" Margin="5,2" Source="{x:Static u:Icons.Import}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</u:WindowStyle.TitleContentTemplate>
<Window.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<spr:SyntheticImportConflictsView Margin="12,10,12,8"/>
<c:DialogSkirt Grid.Row="1">
<c:DialogSkirt.RightItems>
<x:Array Type="{x:Type Button}">
<Button Command="{Binding ok_cmd}">Continue</Button>
<Button Command="{Binding cancel_cmd}" IsCancel="True">Cancel</Button>
</x:Array>
</c:DialogSkirt.RightItems>
</c:DialogSkirt>
</Grid>
</Window.Content>
</Window>
@@ -1,11 +0,0 @@
using System.Windows;
namespace bts.shell.spreader {
/// <summary>
/// Interaction logic for SyntheticImportConflictsDialog.xaml
/// </summary>
partial class SyntheticImportConflictsDialog : Window {
public SyntheticImportConflictsDialog() =>
InitializeComponent();
}
}
@@ -1,101 +0,0 @@
using bts.api;
using bts.collections;
using bts.utils;
using mbts;
using mbts.spark;
using mbts.ui;
using mbts.user.settings;
using System.Collections.Generic;
using System.Linq;
namespace bts.shell.spreader {
readonly struct SyntheticComponent {
public readonly SyntheticContractComponent type;
public readonly int leg_index;
public SyntheticComponent(SyntheticContractComponent type, int leg_index) {
this.type = type;
this.leg_index = leg_index;
}
}
class SyntheticImportConflictsVM : ImportConflictsVM<(SyntheticImportRowVM synth, ContractLegVM leg), SyntheticImportRowVM, SyntheticContractComponent>, IRequestViewClose {
IReadOnlyDictionary<uint, SecurityMD> _security_map = DictionaryHelper.empty<uint, SecurityMD>();
public SyntheticImportConflictsVM(int num_synthetics)
: base(num_synthetics, tup => tup.synth) { }
// misc items sources
public IReadOnlyDictionary<uint, SecurityMD> security_map {
get => _security_map;
set {
if (!ReferenceEquals(_security_map, value)) {
_security_map = value;
_resolve_securities();
RaisePropertyChanged();
}
}
}
void _resolve_securities() {
if (_security_map == null)
return;
var reverse_lookup = SyntheticSecurityParser.reverse_lookup(MappedDictionary.create(_security_map, v => v.security.ToString()));
foreach (var r in conflicts) {
if (r.component == SyntheticContractComponent.Security && r.has_error) {
var id = reverse_lookup(r.text);
if (id != 0 && _security_map.TryGetValue(id, out var smd))
r.value = smd.security;
}
}
}
}
class SyntheticImportConflictRowVM : ImportConflictRowVM<(SyntheticImportRowVM synth, ContractLegVM leg), SyntheticContractComponent>, ITemplateSelectorKey {
public SyntheticImportConflictRowVM(
SyntheticContractComponent component,
string text,
IReadOnlyCollection<(SyntheticImportRowVM synth, ContractLegVM leg)> affected_parts,
object error_value,
object value)
: base(component, text, affected_parts, error_value, value, affected_parts.DistinctOn(tup => tup.synth).Count()) { }
public override void on_conflicts_resolved() {
if (has_error)
return;
foreach (var (synth, leg) in affected_parts) {
switch (component) {
case SyntheticContractComponent.Name:
if (value is string str)
synth.name = str;
break;
case SyntheticContractComponent.TickSize:
if (value is double ts && ts.IsNonZero()) {
synth.tick_factors = TickUtils.get_regional_ticks(new ContractSpecificationImmutable(new ContractSpecification {
LowTick = ts,
HighTick = ts
})).low_tick;
} else if (value is TickFactors tf && tf.is_valid())
synth.tick_factors = tf;
break;
case SyntheticContractComponent.Security:
if (value is SecurityImmutable sec && sec.is_exchange_security())
leg.security = sec;
break;
case SyntheticContractComponent.PriceMultiplier:
if (value is double pm && pm.IsNonZero())
leg.price_multiplier = pm;
break;
case SyntheticContractComponent.Quantity:
if (value is double q && q.IsNonZero())
leg.quantity = q;
break;
case SyntheticContractComponent.LegType:
if (value is ContractLegType lt)
leg.leg_type = lt;
break;
}
}
}
}
}
@@ -1,199 +0,0 @@
<UserControl
x:Class="bts.shell.spreader.SyntheticImportConflictsView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:spr="clr-namespace:bts.shell.spreader"
MinWidth="400"
>
<UserControl.Resources>
<Style x:Key="VerticallyCenteredText" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!--Value Cell Template Selector-->
<u:KeyedTemplateSelector x:Key="ValueCellTemplateSelector">
<u:KeyedTemplateSelector.Default>
<DataTemplate>
<TextBlock Text="{Binding value}" VerticalAlignment="Center"/>
</DataTemplate>
</u:KeyedTemplateSelector.Default>
<u:TemplateSelectorKeyValue Key="PriceMultiplier">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={StaticResource DoubleTextConverter}}" VerticalAlignment="Center"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="Quantity">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={StaticResource DoubleTextConverter}}" VerticalAlignment="Center"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="Security">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={x:Static cv:ValueConverters.SecuritySelectorText}}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="LegType">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={x:Static spr:ContractLegTypeView.LegTypeShortDescConverter}}">
<TextBlock.ToolTip>
<TextBlock>
<Run Text="{Binding value, Converter={x:Static spr:ContractLegTypeView.LegTypeLongDescConverter}}"/>
<LineBreak/>
<Run Text="Q = Quote"/>
<LineBreak/>
<Run Text="H = Hedge"/>
<LineBreak/>
<Run Text="N = None"/>
</TextBlock>
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</u:KeyedTemplateSelector>
<!--Value Cell Editing Template Selector-->
<u:KeyedTemplateSelector x:Key="ValueCellEditingTemplateSelector">
<u:TemplateSelectorKeyValue Key="Security">
<DataTemplate>
<c:ContractPickerPopup
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding value, Converter={x:Static cv:ValueConverters.SecuritySelectorText}}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=DataGrid}, Mode=OneWay}"
SelectedSecurity="{Binding value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ShowDialogSkirt="True"
/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="Name">
<DataTemplate>
<TextBox u:Behavior.FocusOnLoaded="True" Text="{Binding value}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="TickSize">
<DataTemplate>
<c:TickUpDownDouble Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="PriceMultiplier">
<DataTemplate>
<c:TickUpDownDouble Value="{Binding value}" DisplayConverter="{StaticResource DoubleTextConverter}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="Quantity">
<DataTemplate>
<c:TickUpDownDouble Value="{Binding value}" DisplayConverter="{StaticResource DoubleTextConverter}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="LegType">
<DataTemplate>
<spr:ContractLegTypeView Value="{Binding value}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</u:KeyedTemplateSelector>
</UserControl.Resources>
<!--Content-->
<Grid x:Name="content_root">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image VerticalAlignment="Center" Margin="0,0,8,0">
<Image.Source>
<Binding Source="{x:Static MessageBoxImage.Exclamation}">
<Binding.Converter>
<cv:SystemIconConverter/>
</Binding.Converter>
</Binding>
</Image.Source>
</Image>
<Canvas x:Name="text_canvas" Grid.Column="1" HorizontalAlignment="Stretch"/>
<TextBlock Grid.Column="1" TextWrapping="Wrap" Width="{Binding ActualWidth, ElementName=text_canvas}">
<Run Text="{Binding num_successfully_parsed_rows, Mode=OneWay}"/>
<Run Text="/"/>
<Run Text="{Binding num_rows, Mode=OneWay}"/>
<Run Text=" synthetics were successfully converted."/>
<Run Text="Resolve conflicts below by selecting a value to assign each affected synthetic,"/>
<Run Text="or modify individual synthetics in the main window."/>
<LineBreak/>
<LineBreak/>
<Run Text="Click 'Continue' to proceed to the main window."/>
</TextBlock>
<DataGrid
x:Name="data_grid"
u:DataGridBehavior.SingleClickEdit="True"
Grid.Row="1"
Grid.ColumnSpan="2"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserSortColumns="True"
HeadersVisibility="All"
ItemsSource="{Binding conflicts_view}"
Margin="0,8,0,0"
MinRowHeight="17"
RowHeaderWidth="22"
SelectionMode="Single"
>
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
<Grid Background="Green">
<Border x:Name="exclamation" HorizontalAlignment="Center">
<Image Height="12" VerticalAlignment="Center" Source="{x:Static u:Icons.WarningExclamation}"/>
</Border>
<ContentControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" ToolTip="{Binding (Validation.Errors), RelativeSource={RelativeSource AncestorType=DataGridRow}}">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Grid Background="Transparent">
<Grid.ToolTip>
<ToolTip Content="{Binding ToolTip, RelativeSource={RelativeSource TemplatedParent}}">
<ToolTip.ContentTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}" DisplayMemberPath="ErrorContent"/>
</DataTemplate>
</ToolTip.ContentTemplate>
</ToolTip>
</Grid.ToolTip>
</Grid>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Grid>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ValidationErrorTemplate" Value="{Binding RowValidationErrorTemplate, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource SelectedRowBackground}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Component" Binding="{Binding component, Converter={StaticResource EnumDescriptionConverter}}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTextColumn Header="Text" Binding="{Binding text}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTextColumn Header="Affected Synthetics" Binding="{Binding num_affected_rows}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTemplateColumn Header="Value" CellTemplateSelector="{StaticResource ValueCellTemplateSelector}" CellEditingTemplateSelector="{StaticResource ValueCellEditingTemplateSelector}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
@@ -1,20 +0,0 @@
using System.Windows.Controls;
namespace bts.shell.spreader {
enum SyntheticContractComponent {
Name,
TickSize,
Security,
PriceMultiplier,
Quantity,
LegType
}
/// <summary>
/// Interaction logic for SyntheticImportConflictsView.xaml
/// </summary>
partial class SyntheticImportConflictsView : UserControl {
public SyntheticImportConflictsView() =>
InitializeComponent();
}
}
@@ -1,45 +0,0 @@
<Window
x:Class="bts.shell.spreader.SyntheticImportDialog"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:spr="clr-namespace:bts.shell.spreader"
u:WindowStyle.AllowResize="True"
u:WindowStyle.SizeToContent="WidthAndHeight"
u:WindowStyle.TitleContent="Import Synthetics"
Style="{StaticResource BTSDialogTitleTemplate}"
Content="{Binding}"
>
<Window.MaxHeight>
<Binding Path="SizeToContent" RelativeSource="{RelativeSource Self}">
<Binding.Converter>
<cv:WindowMaxHeightConverter SizeToContentThreshold="{u:DPIDouble 700}"/>
</Binding.Converter>
</Binding>
</Window.MaxHeight>
<u:WindowStyle.TitleContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Image Height="12" Margin="5,2" Source="{x:Static u:Icons.Import}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</u:WindowStyle.TitleContentTemplate>
<Window.ContentTemplate>
<DataTemplate DataType="{x:Type spr:SyntheticImportVM}">
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<spr:SyntheticImportView/>
<c:DialogSkirtOKCancel Grid.Row="1" OKCommand="{Binding ok_cmd}" CancelCommand="{Binding cancel_cmd}"/>
</Grid>
</AdornerDecorator>
</DataTemplate>
</Window.ContentTemplate>
</Window>
@@ -1,11 +0,0 @@
using System.Windows;
namespace bts.shell.spreader {
/// <summary>
/// Interaction logic for SyntheticImportDialog.xaml
/// </summary>
partial class SyntheticImportDialog : Window {
public SyntheticImportDialog() =>
InitializeComponent();
}
}
@@ -1,236 +0,0 @@
using mbts;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Data;
namespace bts.shell.spreader {
class SyntheticImportRowCollectionView : ListCollectionView {
ReadOnlyObservableCollection<object> _base_groups, _groups_wrapper;
ObservableAutoWrapper<object, object> _groups;
public SyntheticImportRowCollectionView(IList list)
: base(list) { }
public override bool CanGroup => true;
// Must return null if there are no GroupDescriptions (i.e., instead of an empty collection).
// If an empty collection is returned, the view assumes grouping is active and there are no items.
public override ReadOnlyObservableCollection<object> Groups {
get {
if ((GroupDescriptions?.Count ?? 0) == 0)
return null;
if (_groups_wrapper == null) {
_base_groups = base.Groups;
if (_base_groups == null)
return null;
_groups = new ObservableAutoWrapper<object, object>(_base_groups, obj => new SyntheticImportRowCollectionViewGroup((CollectionViewGroup)obj, this));
_groups_wrapper = new ReadOnlyObservableCollection<object>(_groups);
}
return _groups_wrapper;
}
}
public void set_all_expanded(bool is_expanded) {
foreach (var g in Groups.OfTypeSafe<SyntheticImportRowCollectionViewGroup>())
g.set_all_expanded(is_expanded);
}
}
class SyntheticImportRowCollectionViewGroup : CollectionViewGroup, INotifyDataErrorInfo, IRaiseNotifyPropertyChanged, IEquatable<SyntheticImportRowCollectionViewGroup> {
static readonly string _desc_delim = ", ";
readonly SyntheticImportRowCollectionView _root;
readonly CollectionViewGroup _from;
readonly Dictionary<object, InnerGroupSubscription> _inner_groups = new Dictionary<object, InnerGroupSubscription>();
readonly Dictionary<int, IDisposable> _rows = new Dictionary<int, IDisposable>();
string
_leg_type_desc,
_security_desc,
_quantity_desc,
_price_multiplier_desc;
IDisposable _dispose;
bool _is_expanded;
public SyntheticImportRowCollectionViewGroup(CollectionViewGroup from, SyntheticImportRowCollectionView root) : base(from.Name) {
_root = root;
_from = from;
((INotifyCollectionChanged)_from.Items).CollectionChanged += _from_items_CollectionChanged;
_from_items_CollectionChanged(_from.Items, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _from.Items, 0));
}
public event Action ProtectedItemCountChanged;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public override bool IsBottomLevel => _from.IsBottomLevel;
public bool is_expanded { get => _is_expanded; set => _set(ref _is_expanded, value); }
public int level => parent == null ? 1 : parent.level + 1;
public SyntheticImportRowCollectionViewGroup parent { get; private set; }
// leg type desc
public string leg_type_desc =>
_leg_type_desc ?? (_leg_type_desc = string.Join(_desc_delim, ProtectedItems.OfType<ContractLegVM>().Select(l => l.leg_type.display_name())));
// price multiplier desc
public string price_multiplier_desc =>
_price_multiplier_desc ?? (_price_multiplier_desc = string.Join(_desc_delim, ProtectedItems.OfType<ContractLegVM>().Select(l => l.price_multiplier)));
// quantity desc
public string quantity_desc =>
_quantity_desc ?? (_quantity_desc = string.Join(_desc_delim, ProtectedItems.OfType<ContractLegVM>().Select(l => l.quantity)));
// security desc
public string security_desc =>
_security_desc ?? (_security_desc = string.Join(_desc_delim, ProtectedItems.OfType<ContractLegVM>().Select(l => l.security?.ToString() ?? "?").ExceptNull()).if_null_or_empty("(No Legs)"));
new int ProtectedItemCount {
get => base.ProtectedItemCount;
set {
if (base.ProtectedItemCount != value) {
base.ProtectedItemCount = value;
ProtectedItemCountChanged?.Invoke();
}
}
}
public bool HasErrors => Items.OfType<ContractLegVM>().Any(l => l.HasErrors);
public IEnumerable GetErrors(string propertyName) =>
propertyName == nameof(Items) ?
Items.OfType<ContractLegVM>().SelectMany(l => l.AllErrors) :
Enumerable.Empty<string>();
public void set_all_expanded(bool is_expanded) {
this.is_expanded = is_expanded;
foreach (var g in Items.OfTypeSafe<SyntheticImportRowCollectionViewGroup>())
g.set_all_expanded(is_expanded);
}
public bool Equals(SyntheticImportRowCollectionViewGroup other) =>
other != null &&
level == other.level &&
Equals(Name, other.Name);
void IRaiseNotifyPropertyChanged.raise_notify_property_changed(string prop) =>
OnPropertyChanged(new PropertyChangedEventArgs(prop));
void _add_item(int index, object item) {
Log.assert(index >= 0, "Index is less than 0.");
if (item is CollectionViewGroup group) {
var inner_group = new SyntheticImportRowCollectionViewGroup(group, _root);
_inner_groups.Add(group.Name, new InnerGroupSubscription(this, inner_group));
ProtectedItems.Insert(index, inner_group);
} else {
var row = (ContractLegVM)item;
if (_rows.TryPopValue(row.id, out var sub))
sub.Dispose();
_rows[row.id] = _connect_row(row);
ProtectedItems.Insert(index, item);
}
}
void _remove_item(int index, object item) {
if (item is SyntheticImportRowCollectionViewGroup inner_group) {
if (!_inner_groups.TryPopValue(inner_group.Name, out var ig_sub))
throw new InvalidOperationException($"Inner groups cache does not have an entry for the specified name '{inner_group.Name}' (entire tree structure may be compromised).");
ig_sub.Dispose();
Disposable.dispose(ref inner_group._dispose);
}
ProtectedItems.RemoveAt(index);
}
void _from_items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
for (int i = 0; i < e.NewItems.Count; i++)
_add_item(e.NewStartingIndex + i, e.NewItems[i]);
break;
case NotifyCollectionChangedAction.Remove:
for (int i = 0; i < e.OldItems.Count; i++)
_remove_item(e.OldStartingIndex, ProtectedItems[e.OldStartingIndex]);
break;
case NotifyCollectionChangedAction.Replace:
for (int i = 0; i < e.OldItems.Count; i++)
_remove_item(e.OldStartingIndex, ProtectedItems[e.OldStartingIndex]);
for (int i = 0; i < e.NewItems.Count; i++)
_add_item(e.NewStartingIndex + i, e.NewItems[i]);
break;
case NotifyCollectionChangedAction.Reset:
while (ProtectedItems.Count > 0)
_remove_item(0, ProtectedItems[0]);
for (int i = 0; i < _from.Items.Count; i++)
_add_item(i, _from.Items[i]);
break;
}
}
void _invalidate_props(params string[] props) {
for (int i = 0; i < props.Length; i++)
OnPropertyChanged(new PropertyChangedEventArgs(props[i]));
}
void _set<T>(ref T backing, T new_value, [CallerMemberName]string prop = "") =>
_set(ref backing, new_value, EqualityComparer<T>.Default, prop);
void _set<T>(ref T backing, T new_value, IEqualityComparer<T> comparer, [CallerMemberName]string prop = "") {
if (!comparer.Equals(backing, new_value)) {
backing = new_value;
OnPropertyChanged(new PropertyChangedEventArgs(prop));
}
}
IDisposable _connect_row(ContractLegVM row) {
row.result_changed += on_result_changed;
row.ErrorsChanged += on_errors_changed;
return Disposable.Create(() => {
row.result_changed -= on_result_changed;
row.ErrorsChanged -= on_errors_changed;
});
void on_result_changed() {
_security_desc = null;
_price_multiplier_desc = null;
_quantity_desc = null;
_leg_type_desc = null;
_invalidate_props(nameof(security_desc), nameof(price_multiplier_desc), nameof(quantity_desc), nameof(leg_type_desc));
}
void on_errors_changed(object sender, DataErrorsChangedEventArgs e) =>
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Items)));
}
class InnerGroupSubscription : IDisposable {
public readonly SyntheticImportRowCollectionViewGroup _inner_group;
readonly SyntheticImportRowCollectionViewGroup _parent;
int _last_count;
public InnerGroupSubscription(SyntheticImportRowCollectionViewGroup parent, SyntheticImportRowCollectionViewGroup inner_group) {
_parent = parent;
_inner_group = inner_group;
_inner_group.parent = parent;
// init item count
_on_item_count_changed();
// listen to item count changes
_inner_group.ProtectedItemCountChanged += _on_item_count_changed;
}
public void Dispose() {
_inner_group.ProtectedItemCountChanged -= _on_item_count_changed;
_inner_group.parent = null;
// make sure the state is valid (if a bad state goes by undetected here, nefarious/difficult-to-track bugs could arise)
if (_last_count != _inner_group.ProtectedItemCount) {
Log.Ui.warn("Cached inner count is not equal to the inner group's ItemCount (entire tree structure may be compromised).");
}
_parent.ProtectedItemCount -= _last_count;
}
void _on_item_count_changed() {
// add the delta
_parent.ProtectedItemCount += (_inner_group.ProtectedItemCount - _last_count);
// reset the local cache
_last_count = _inner_group.ProtectedItemCount;
}
}
}
}
@@ -1,253 +0,0 @@
using bts.api;
using bts.collections;
using bts.spark;
using mbts;
using mbts.collections;
using mbts.spark;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.utility;
using mbts.user.settings;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Data;
using System.Windows.Input;
using Disposable = mbts.Disposable;
namespace bts.shell.spreader {
class SyntheticImportVM : DisposableViewModelBase, IRequestViewClose {
IReadOnlyDictionary<uint, SecurityMD> _security_map = DictionaryHelper.empty<uint, SecurityMD>();
public SyntheticImportVM() {
var rows_view = new SyntheticImportRowCollectionView(flat_legs);
rows_view.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ContractLegVM.owner)));
this.rows_view = rows_view;
ok_cmd = new DelegateCommand(() => import_request?.Invoke());
cancel_cmd = new DelegateCommand(() => RequestClose?.Invoke(this, new EventArgs()));
_dispose = ObservableAutoWrapper.connect_item_hooks(
rows,
r => {
flat_legs.AddRange(r.legs);
return Disposable.Create(() => flat_legs.removeRange(r.legs));
});
}
public event Action import_request;
public event EventHandler RequestClose;
public ObservableCollection<SyntheticImportRowVM> rows { get; } = new ObservableCollection<SyntheticImportRowVM>();
public ObservableCollection<ContractLegVM> flat_legs { get; } = new ObservableCollection<ContractLegVM>();
public IReadOnlyDictionary<uint, SecurityMD> security_map { get => _security_map; set => Set(ref _security_map, value); }
public ICollectionView rows_view { get; }
// commands
public ICommand ok_cmd { get; }
public ICommand cancel_cmd { get; }
}
class SyntheticImportRowVM : DisposableViewModelBase, IEquatable<SyntheticImportRowVM> {
readonly ReactiveThresholdMonitor<ContractLegVM> _leg_errors_mon = new ReactiveThresholdMonitor<ContractLegVM>(l => l.subscribe_has_errors(true));
readonly Func<string, bool> _get_name_exists;
SecurityImmutable? _security;
ExecutionStrategyImmutable? _strategy;
TickFactors _tick_factors;
string _name;
bool _is_selected;
public SyntheticImportRowVM(uint user_id, Func<string, bool> get_name_exists) {
this.user_id = user_id;
_get_name_exists = get_name_exists;
_validate();
add_leg_cmd = new DelegateCommand<SyntheticImportRowVM>(r => {
if (r != null) {
r.legs.Add(new ContractLegVM {
price_multiplier = 1,
quantity = 1,
owner = r
});
}
});
remove_leg_cmd = new DelegateCommand<ContractLegVM>(l => l?.owner?.legs?.Remove(l));
PropertyChanged += on_property_changed;
var disp = new DisposableCombo(
_leg_errors_mon.OnDispatcher().Subscribe(v => _validate()),
ObservableAutoWrapper.connect_item_hooks(legs,
l => {
l.result_changed += invalidate;
return new DisposableCombo(_leg_errors_mon.join(l), l) +
new Action(() => l.result_changed -= invalidate);
})) +
new Action(() => PropertyChanged -= on_property_changed);
void invalidate() {
_security = null;
_strategy = null;
_validate();
result_changed?.Invoke();
}
void on_property_changed(object sender, PropertyChangedEventArgs e) =>
invalidate();
}
public event Action result_changed;
public SecurityImmutable result =>
_security ?? (_security = new SecurityImmutable(to_security())).Value;
public ExecutionStrategyImmutable strategy =>
_strategy ?? (_strategy = new ExecutionStrategyImmutable(to_strategy())).Value;
public (SecurityImmutable security, ExecutionStrategyImmutable strategy) result_tuple =>
(result, strategy);
public ParsedSyntheticContract parsed_synth { get; set; }
public string name { get => _name; set => Set(ref _name, value); }
public uint user_id { get; }
public TickFactors tick_factors { get => _tick_factors; set => Set(ref _tick_factors, value); }
public ObservableCollection<ContractLegVM> legs { get; } = new ObservableCollection<ContractLegVM>();
public bool is_selected { get => _is_selected; set => Set(ref _is_selected, value); }
public ICommand add_leg_cmd { get; }
public ICommand remove_leg_cmd { get; }
public Security to_security() {
var sc = new SyntheticContract {
Name = name,
UserId = user_id
};
sc.Legs.AddRange(legs.Select(l => l.to_contract_leg()));
return new Security {
Synthetic = sc,
Symbol = sc.Legs.FirstOrDefault()?.Security?.Symbol ?? string.Empty,
Exchange = Venue.Bts,
ContractSpec = tick_factors.to_spec()
};
}
public ExecutionStrategy to_strategy() {
var strat = new ExecutionStrategy();
foreach (var l in legs) {
if (l.security.HasValue)
strat.LegParamsMap[l.security.Value.Id] = l.to_leg_params();
}
return strat;
}
void _validate() {
using (validation()) {
if (string.IsNullOrEmpty(name)) {
validation_assert(false, "Name is not specified.", nameof(name));
} else if (_get_name_exists(name)) {
validation_assert(false, "Name already exists.", nameof(name));
}
validation_assert(tick_factors != null && tick_factors.is_valid(), "Invalid tick", nameof(tick_factors));
validation_assert(legs.Count > 0 && !_leg_errors_mon.is_above_threshold, "Invalid legs.", nameof(legs));
}
}
public bool Equals(SyntheticImportRowVM other) =>
other != null &&
parsed_synth.unpersisted_id == other.parsed_synth.unpersisted_id;
public override bool Equals(object obj) =>
Equals(obj as SyntheticImportRowVM);
public override int GetHashCode() =>
parsed_synth.unpersisted_id;
}
class ContractLegVM : DisposableViewModelBase, IEquatable<ContractLegVM> {
ContractLegImmutable? _leg;
LegSpreaderParamsImmutable? _leg_params;
SecurityImmutable? _security;
double _price_multiplier, _quantity;
ContractLegType _leg_type;
bool _is_selected;
public ContractLegVM() {
PropertyChanged += on_property_changed;
_dispose = Disposable.Create(() => PropertyChanged -= on_property_changed);
_validate();
void on_property_changed(object sender, PropertyChangedEventArgs e) {
switch (e.PropertyName) {
case nameof(security):
case nameof(price_multiplier):
case nameof(quantity):
_leg = null;
break;
case nameof(leg_type):
_leg_params = null;
break;
default: return;
}
_validate();
result_changed?.Invoke();
}
}
public event Action result_changed;
public ParsedContractLeg parsed_leg { get; set; }
public SyntheticImportRowVM owner { get; set; }
public int owner_id => owner.parsed_synth.unpersisted_id;
public int id => parsed_leg.id;
public ContractLegImmutable leg =>
_leg ?? (_leg = new ContractLegImmutable(to_contract_leg())).Value;
public LegSpreaderParamsImmutable leg_params =>
_leg_params ?? (_leg_params = new LegSpreaderParamsImmutable(to_leg_params())).Value;
public (ContractLegImmutable leg, LegSpreaderParamsImmutable leg_params) result_tuple =>
(leg, leg_params);
public SecurityImmutable? security { get => _security; set => Set(ref _security, value); }
public double price_multiplier { get => _price_multiplier; set => Set(ref _price_multiplier, value); }
public double quantity { get => _quantity; set => Set(ref _quantity, value); }
public ContractLegType leg_type { get => _leg_type; set => Set(ref _leg_type, value); }
public bool is_selected { get => _is_selected; set => Set(ref _is_selected, value); }
public void load(ContractLegImmutable leg) {
security = leg.Security;
price_multiplier = leg.PriceMultiplier;
quantity = leg.Quantity;
}
public ContractLeg to_contract_leg() =>
new ContractLeg {
Security = security?.CloneToMutable(),
PriceMultiplier = price_multiplier,
Side = quantity < 0 ? Side.Sell : Side.Buy,
Quantity = Math.Abs(quantity)
};
public LegSpreaderParams to_leg_params() {
var lps = SyntheticDataHelper.Defaults.LegSpreaderParams;
lps.EnableHedging = (leg_type & ContractLegType.Hedge) != 0;
lps.IsActiveQuoting = (leg_type & ContractLegType.Quote) != 0;
return lps;
}
void _validate() {
using (validation()) {
validation_assert(security.is_exchange_security(), "Invalid security", nameof(security));
validation_assert(price_multiplier.IsNonZero(), "Invalid price multiplier", nameof(price_multiplier));
validation_assert(quantity.IsNonZero(), "Invalid quantity", nameof(quantity));
}
}
public bool Equals(ContractLegVM other) =>
other != null && id == other.id;
public override bool Equals(object obj) =>
Equals(obj as ContractLegVM);
public override int GetHashCode() =>
id;
}
}
@@ -1,270 +0,0 @@
<UserControl
x:Class="bts.shell.spreader.SyntheticImportView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:spr="clr-namespace:bts.shell.spreader"
xmlns:pnl="clr-namespace:bts.shell.pnl"
MinWidth="400"
Content="{Binding}"
>
<UserControl.Resources>
<cv:GridLengthConverter x:Key="GridLengthPixelConverter" UnitType="Pixel"/>
<Style x:Key="GroupDesc" TargetType="TextBlock">
<Setter Property="Margin" Value="{x:Static spr:SyntheticImportView.CellPadding}"/>
<Setter Property="ToolTip" Value="{Binding Text, RelativeSource={RelativeSource Self}}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="#80000000"/>
<Setter Property="FontStyle" Value="Italic"/>
</Style>
<DataTemplate DataType="{x:Type spr:ContractLegVM}">
<c:ContractPickerPopup
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding security}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=DataGrid}, Mode=OneWay}"
SelectedSecurity="{Binding security, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ShowDialogSkirt="True"
ToolTip="{Binding SelectedCriteria, RelativeSource={RelativeSource Self}, Converter={x:Static c:ContractPickerPopup.SecurityCriteriaTextConverter}, Mode=OneWay}"
/>
</DataTemplate>
<DataTemplate DataType="{x:Type spr:SyntheticImportVM}">
<c:DIYGrid
Grid.IsSharedSizeScope="True"
Rows="{Binding rows_view}"
RowHeight="{x:Static spr:SyntheticImportView.RowHeight}"
RowsHorizontalScrollBarVisibility="Collapsed"
SelectionHighlight="#30000000"
SelectionMode="Single"
SupportGroupItems="True"
>
<c:DIYGrid.Columns>
<c:DIYGridColumnCollection>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.Name}" Width="106"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.Ticks}" Width="128"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.Security}" Header="Securities" Width="120"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.Quantity}" Header="Quantities" Width="70"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.PriceMultiplier}" Header="Price Multipliers" Width="80"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.LegType}" Header="Leg Types" Width="83"/>
</c:DIYGridColumnCollection>
</c:DIYGrid.Columns>
<c:DIYGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="HorizontalAlignment" Value="Left"/>
<EventSetter Event="MouseDown" Handler="_on_group_item_mouse_down"/>
<!--Note: Tag is bound to 'Items' so errors on the 'Items' prop will be included in Validation.Errors.-->
<Setter Property="Tag">
<Setter.Value>
<MultiBinding>
<MultiBinding.Converter>
<cv:MultiValueConverterPipe/>
</MultiBinding.Converter>
<Binding Path="Name.legs"/>
<Binding Path="Name.name"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Grid
c:DIYGrid.SyncWithColumnGeometry="True"
u:StateManager.VisualState="{Binding IsChecked, ElementName=expander, Converter={x:Static pnl:PNLView.ExpandedVisualStateConverter}}"
Background="{TemplateBinding Background}"
>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ExpansionStates">
<VisualState Name="Expanded"/>
<VisualState Name="Collapsed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="presenter" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding RowHeight, RelativeSource={RelativeSource AncestorType=c:DIYGrid}, Converter={StaticResource GridLengthPixelConverter}}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.ColumnSpan="99" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Fill="Gray" UseLayoutRounding="True"/>
<!--Expander Toggle-->
<c:ExpanderToggleButton
x:Name="expander"
Grid.ColumnSpan="99"
Validation.ValidationAdornerSiteFor="{Binding RelativeSource={RelativeSource TemplatedParent}}"
ArrowPadding="14,2,0,0"
Background="{u:Brush Transparent}"
BorderThickness="0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Height="{Binding RowHeight, RelativeSource={RelativeSource AncestorType=c:DIYGrid}}"
IsChecked="{Binding is_expanded}"
>
<u:StateManager.VisualState>
<Binding Path="TemplatedParent.(Validation.Errors).Count" RelativeSource="{RelativeSource Self}">
<Binding.Converter>
<cv:ValueConverterPipe>
<cv:IntGreaterThanConverter Value="0"/>
<cv:BooleanToObjectConverter ValueForFalse="NoErrors" ValueForTrue="Errors"/>
</cv:ValueConverterPipe>
</Binding.Converter>
</Binding>
</u:StateManager.VisualState>
<u:StateManager.Groups>
<u:StateGroupsManager AttachToMembers="True">
<u:StateDefinitionCollection>
<u:MouseStateDefinition MouseSource="{Binding Group.Target, RelativeSource={RelativeSource Self}}"/>
</u:StateDefinitionCollection>
</u:StateGroupsManager>
</u:StateManager.Groups>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="MouseStates">
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="expander" Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="#10000000"/>
</Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="expander" Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="#10000000"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Validation.ErrorTemplate>
<ControlTemplate TargetType="Control">
<Grid>
<AdornedElementPlaceholder x:Name="adorned_element_pl"/>
<Border x:Name="exclamation" HorizontalAlignment="Left">
<Image Height="12" Margin="6,0,0,0" VerticalAlignment="Center" Source="{x:Static u:Icons.WarningExclamation}"/>
</Border>
<ContentControl u:Behavior.MirrorLayoutFor="{Binding ElementName=exclamation}" ToolTip="{Binding AdornedElement.(Validation.ValidationAdornerSiteFor).(Validation.Errors), ElementName=adorned_element_pl}">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Grid Background="Transparent">
<Grid.ToolTip>
<ToolTip Content="{Binding ToolTip, RelativeSource={RelativeSource TemplatedParent}}">
<ToolTip.ContentTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}" DisplayMemberPath="ErrorContent"/>
</DataTemplate>
</ToolTip.ContentTemplate>
</ToolTip>
</Grid.ToolTip>
</Grid>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Grid>
</ControlTemplate>
</Validation.ErrorTemplate>
<Grid c:DIYGrid.SyncWithColumnGeometry="True" Margin="-22,0,0,0">
<c:EditableTextBlock
x:Name="PART_Name_EditableTextBlock"
Grid.Column="0"
Margin="22,0,0,0"
EndEditOnLostKeyboardFocus="True"
Foreground="{u:Brush Black}"
Text="{Binding Name.name}"
ToolTip="Click to Edit Name"
UseLayoutRounding="True"
VerticalAlignment="Center"
>
<c:EditableTextBlock.CommandBindings>
<CommandBinding Command="{x:Static c:EditableTextBlock.BeginEditCommand}" Executed="{x:Static c:EditableTextBlock.BeginEditExecuted}"/>
<CommandBinding Command="{x:Static c:EditableTextBlock.EndEditCommand}" Executed="{x:Static c:EditableTextBlock.EndEditExecuted}"/>
<CommandBinding Command="{x:Static c:EditableTextBlock.CancelEditCommand}" Executed="{x:Static c:EditableTextBlock.CancelEditExecuted}"/>
</c:EditableTextBlock.CommandBindings>
<c:EditableTextBlock.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{x:Static c:EditableTextBlock.BeginEditCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:EditableTextBlock.EndEditCommand}"/>
<KeyBinding Key="Esc" Command="{x:Static c:EditableTextBlock.CancelEditCommand}"/>
</c:EditableTextBlock.InputBindings>
</c:EditableTextBlock>
<c:TickFactorsPicker
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Ticks}"
Margin="{x:Static spr:SyntheticImportView.CellPadding}"
SelectedTickFactors="{Binding Name.tick_factors}"
UseLayoutRounding="True"
/>
<TextBlock c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Security}" Text="{Binding security_desc}" Style="{StaticResource GroupDesc}"/>
<TextBlock c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Quantity}" Text="{Binding quantity_desc}" Style="{StaticResource GroupDesc}"/>
<TextBlock c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.PriceMultiplier}" Text="{Binding price_multiplier_desc}" Style="{StaticResource GroupDesc}"/>
<TextBlock c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.LegType}" Text="{Binding leg_type_desc}" Style="{StaticResource GroupDesc}">
<TextBlock.ToolTip>
<TextBlock>
<Run Text="{Binding leg_type_desc, Mode=OneWay}"/>
<LineBreak/>
<Run Text="Q = Quote"/>
<LineBreak/>
<Run Text="H = Hedge"/>
<LineBreak/>
<Run Text="N = None"/>
</TextBlock>
</TextBlock.ToolTip>
</TextBlock>
</Grid>
</c:ExpanderToggleButton>
<ItemsPresenter x:Name="presenter" Grid.Row="1" Grid.ColumnSpan="99"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</c:DIYGrid.GroupStyle>
<c:DIYGrid.RowTemplate>
<DataTemplate DataType="{x:Type spr:ContractLegVM}">
<Grid c:DIYGrid.SyncWithColumnGeometry="True">
<c:ContractPickerPopup
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Security}"
Background="Transparent"
BorderThickness="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="{x:Static spr:SyntheticImportView.CellPadding}"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding security, Converter={x:Static cv:ValueConverters.SecuritySelectorText}}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=c:DIYGrid}, Mode=OneWay}"
SelectedSecurity="{Binding security, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ShowDialogSkirt="True"
ToolTip="{Binding SelectedSecurity, RelativeSource={RelativeSource Self}}"
>
</c:ContractPickerPopup>
<c:TickUpDownDouble
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Quantity}"
Margin="{x:Static spr:SyntheticImportView.CellPadding}"
Value="{Binding quantity}"
/>
<c:TickUpDownDouble
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.PriceMultiplier}"
Margin="{x:Static spr:SyntheticImportView.CellPadding}"
Value="{Binding price_multiplier}"
/>
<spr:ContractLegTypeView
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.LegType}"
Value="{Binding leg_type}"
/>
</Grid>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
</DataTemplate>
</UserControl.Resources>
</UserControl>
@@ -1,50 +0,0 @@
using mbts.ui.controls;
using mbts.ui.dpi;
using mbts.ui.utility;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace bts.shell.spreader {
enum SyntheticImportColumns {
Name = 1,
Security,
Ticks,
PriceMultiplier,
Quantity,
LegType
}
/// <summary>
/// Interaction logic for SyntheticImportView.xaml
/// </summary>
partial class SyntheticImportView : UserControl {
public static double RowHeight =>
27.0.RoundLayoutValue();
public static Thickness CellPadding =>
new Thickness(6, 3, 6, 3).RoundToLayout();
public SyntheticImportView() =>
InitializeComponent();
void _on_group_item_mouse_down(object sender, MouseButtonEventArgs e) {
if (!e.Handled && e.ClickCount == 2 && sender is FrameworkElement el && el.DataContext is SyntheticImportRowCollectionViewGroup group) {
group.is_expanded = !group.is_expanded;
e.Handled = true;
}
}
}
class SyntheticImportColumn : DIYGrid.ColumnBase<SyntheticImportColumns> {
public SyntheticImportColumn() =>
is_visible = true;
protected override void on_key_initialized(SyntheticImportColumns key) {
if (Header == null)
Header = EnumHelper.get_display_name(key);
if (id == null)
id = key.ToString();
}
}
}
@@ -1,194 +0,0 @@
using bts;
using bts.api;
using bts.collections;
using bts.shell.spreader;
using mbts.ui;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace mbts.spark {
static class SyntheticImporter {
const string _import_title = "Import Synthetics";
static readonly TimeSpan _import_op_sec_map_timeout = TimeSpan.FromSeconds(30);
public static IDisposable import_from_file(string path, IBus bus, OrdersModel orders_model) {
bool is_disposed = false;
IDisposable dispose_op = null;
dispose_op = Disposable.Create(() => is_disposed = true);
impl();
return Disposable.Create(() => Disposable.dispose(ref dispose_op));
async void impl() {
IReadOnlyDictionary<uint, SecurityMD> sec_md_map = null;
while (true) {
await Task.WhenAny(init_sec_md_map(), Task.Delay(_import_op_sec_map_timeout));
if (is_disposed)
return;
if (sec_md_map != null)
break;
if (!retry_on_error($"Synthetic import op timed out waiting for security map after {_import_op_sec_map_timeout.TotalSeconds:0.00} seconds."))
return;
}
// The following conditions should break the while loop:
// 1) the import succeeds
// 2) the import fails, and the user does not click 'Yes' in the 'try again...' confirmation dialog
while (true) {
string error_msg = null;
try {
var raw_lines = File.ReadAllLines(path);
var used_names = new HashSet<string>(sec_md_map.Values.Where(smd => smd.security.ContractCase == Security.ContractOneofCase.Synthetic)
.Select(smd => smd.security.Synthetic.Name));
var parsed_synths = SyntheticSecurityParser.parse_rows(raw_lines, MappedDictionary.create(sec_md_map, smd => smd.security), used_names).ToList();
var main_vm = new SyntheticImportVM() {
security_map = sec_md_map
};
// add rows/conflicts
main_vm.rows.AddRange(parsed_synths.Select(ps => {
var sec = ps.result;
var r = new SyntheticImportRowVM(orders_model.admin_model.user.id, used_names.Contains) {
tick_factors = TickUtils.get_regional_ticks(sec.ContractSpec).low_tick,
name = sec.Synthetic.Name,
parsed_synth = ps
};
r.legs.AddRange(ps.legs.Zip(sec.Synthetic.Legs).Select(tup => {
var (pl, l) = tup;
var leg_vm = new ContractLegVM {
price_multiplier = l.PriceMultiplier,
quantity = l.Quantity,
parsed_leg = pl,
owner = r,
leg_type =
(pl.result_tuple.leg_params.EnableHedging ? ContractLegType.Hedge : ContractLegType.None) |
(pl.result_tuple.leg_params.IsActiveQuoting ? ContractLegType.Quote : ContractLegType.None)
};
if (l.Security != null)
leg_vm.security = new SecurityImmutable(l.Security);
return leg_vm;
}));
return r;
}));
var main_win = new SyntheticImportDialog { DataContext = main_vm };
ui.WindowStyle.SetSizeToContent(main_win, SizeToContent.WidthAndHeight);
main_vm.import_request += on_main_import_request;
main_win.move_to_mouse(-200, -75);
dispose_op = WindowUtilities.connect_vm(main_win, main_vm, main_vm);
main_win.Show();
// conflicts
var conflicts = SyntheticSecurityParser.get_conflicts(main_vm.rows);
if (conflicts.Values.Any(c => c.Count > 0)) {
var conflicts_vm = new SyntheticImportConflictsVM(parsed_synths.Count) {
security_map = sec_md_map
};
foreach (var kv in conflicts) {
var assign_del = SyntheticSecurityParser.assign_component(kv.Key);
foreach (var text_groups in kv.Value) {
var eval = get_error_value(kv.Key);
var row = new SyntheticImportConflictRowVM(kv.Key, text_groups.Key, text_groups.Value, eval, eval);
conflicts_vm.conflicts.Add(row);
conflicts_vm.committed += () => {
foreach (var (synth, leg_vm) in text_groups.Value) {
var leg_tup = leg_vm?.parsed_leg?.result_tuple;
assign_del(synth.parsed_synth.result, leg_tup?.leg, leg_tup?.leg_params, row.value);
}
row.on_conflicts_resolved();
};
}
}
var conflicts_win = new SyntheticImportConflictsDialog {
DataContext = conflicts_vm,
Owner = main_win,
Title = $"{_import_title} Conflicts",
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
dispose_op = new DisposableCombo(WindowUtilities.connect_vm(conflicts_win, conflicts_vm, conflicts_vm)) + dispose_op;
conflicts_win.ShowDialog();
}
object get_error_value(SyntheticContractComponent component) {
switch (component) {
case SyntheticContractComponent.PriceMultiplier:
case SyntheticContractComponent.Quantity: return double.NaN;
default: return null;
}
}
void on_main_import_request() {
if (!is_disposed)
import_synths(parsed_synths, main_vm.rows);
Disposable.dispose(ref dispose_op);
}
}
catch (Exception ex) {
error_msg = $"Error parsing synthetics from file '{path}': {ex}.";
}
if (error_msg == null) {
// Exit loop: the import succeeded.
break;
}
if (!retry_on_error(error_msg)) {
// Exit loop: the import failed, and the user did not click 'Yes' in the 'try again...' confirmation dialog.
break;
}
// Continue loop: the import failed, and the user wants to try again.
error_msg = null;
}
async void import_synths(IReadOnlyList<ParsedSyntheticContract> parsed_synths, IEnumerable<SyntheticImportRowVM> rows) {
(uint? key, string error_msg)[] new_ids = null;
string error_msg = null;
try {
new_ids = await orders_model.spreader.create_synthetics(
rows.Where(r => !r.HasErrors)
.Select(r => r.result_tuple));
}
catch (Exception ex) {
Log.Ui.error($"Failed to save all synthetics during import op (one or more may have been successfully saved): {ex}.");
error_msg = $"Import was unsuccessful: {ex}.";
}
if (new_ids == null)
error_msg = "An unknown error occurred (check the synthetic management window to see if any synthetics were successfully added).";
if (error_msg != null) {
BTSMessageBox.Show(error_msg, "Import Synthetics", BTSMessageBoxButtons.OK);
} else {
Log.assert(new_ids != null);
var failed = new_ids
.Where(r => !r.error_msg.is_null_or_empty())
.ToList();
int num_succeeded = new_ids.Length - failed.Count;
if (num_succeeded < parsed_synths.Count) {
if (!BTSMessageBox.Confirm(
$"{num_succeeded}/{parsed_synths.Count} synthetics were successfully imported.\n" +
"Would you like to keep the imports that were successful?",
_import_title)) {
bus.post(SparkTags.SyntheticDeleteTag,
ImmutableHashSet.CreateRange(new_ids.Where(r => r.key.HasValue).Select(r => r.key.Value)));
}
} else {
BTSMessageBox.Show(
$"All {num_succeeded} synthetics were successfully imported.",
_import_title,
BTSMessageBoxButtons.OK,
MessageBoxImage.Information);
}
}
}
async Task init_sec_md_map() =>
sec_md_map = await bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().FirstOrDefaultAsync();
bool retry_on_error(string error_msg) {
Log.Ui.warn(error_msg);
return BTSMessageBox.Confirm(error_msg + "\nTry again?", _import_title, MessageBoxImage.Exclamation);
}
}
}
}
}
@@ -1,307 +0,0 @@
using bts.api;
using bts.spark;
using bts.utils;
using mbts;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
namespace bts.shell.spreader {
[Flags]
enum ContractLegType {
None = 0,
Quote = 1,
Hedge = 2
}
class ParsedSyntheticContract : IDisposable {
[ThreadStatic]
static int _id_counter;
public readonly int unpersisted_id = unchecked(_id_counter++);
public readonly Security result = new Security {
Synthetic = new SyntheticContract(),
ContractSpec = new ContractSpecification(),
Exchange = Venue.Bts
};
readonly ReactiveThresholdMonitor<ParsedContractLeg> _legs_valid_mon =
new ReactiveThresholdMonitor<ParsedContractLeg>(l => l.subscribe_is_valid());
IDisposable _dispose;
public ParsedSyntheticContract() {
_dispose = Disposable.Create(
ObservableAutoWrapper.connect_item_hooks(legs, _legs_valid_mon.join),
_legs_valid_mon.Subscribe(_ => _is_dirty = true));
}
bool
_is_dirty,
_is_valid;
(string text, bool success)
_name,
_tick_size;
public (string text, bool success) name { get => _name; set => _set(ref _name, value); }
public (string text, bool success) tick_size { get => _tick_size; set => _set(ref _tick_size, value); }
public ObservableCollection<ParsedContractLeg> legs { get; } = new ObservableCollection<ParsedContractLeg>();
public bool is_valid {
get {
if (_is_dirty) {
_is_valid = tick_size.success && name.success && !_legs_valid_mon.is_above_threshold;
_is_dirty = false;
}
return _is_valid;
}
}
public ParsedContractLeg current_leg =>
legs.LastOrDefault() ?? add_new_leg();
public ParsedContractLeg add_new_leg() {
var leg = new ParsedContractLeg();
legs.Add(leg);
result.Synthetic.Legs.Add(leg.result);
return leg;
}
public void Dispose() =>
Disposable.dispose(ref _dispose);
public bool Equals(ParsedSyntheticContract other) => other != null && unpersisted_id == other.unpersisted_id;
public override bool Equals(object obj) => Equals(obj as ParsedSyntheticContract);
public override int GetHashCode() => unpersisted_id.GetHashCode();
void _set<T>(ref T backing, T value) {
backing = value;
_is_dirty = true;
}
}
class ParsedContractLeg {
[ThreadStatic]
static int _id_counter;
public readonly ContractLeg result = new ContractLeg { };
public readonly LegSpreaderParams leg_params = new LegSpreaderParams();
bool
_is_dirty,
_is_valid;
(string text, bool success)
_security,
_quantity,
_price_multiplier,
_leg_type;
public event Action invalidated;
public int id { get; } = unchecked(_id_counter++);
public (string text, bool success) security { get => _security; set => _set(ref _security, value); }
public (string text, bool success) quantity { get => _quantity; set => _set(ref _quantity, value); }
public (string text, bool success) price_multiplier { get => _price_multiplier; set => _set(ref _price_multiplier, value); }
public (string text, bool success) leg_type { get => _leg_type; set => _set(ref _leg_type, value); }
public (ContractLeg leg, LegSpreaderParams leg_params) result_tuple => (result, leg_params);
public bool is_valid {
get {
if (_is_dirty) {
if (_is_valid != (security.success && quantity.success && price_multiplier.success && leg_type.success)) {
_is_valid = !_is_valid;
invalidated?.Invoke();
}
_is_dirty = false;
}
return _is_valid;
}
}
public IObservable<bool> subscribe_is_valid() =>
Observable.Create<bool>(o => {
o.OnNext(is_valid);
invalidated += on_invalidated;
return Disposable.Create(() => invalidated -= on_invalidated);
void on_invalidated() =>
o.OnNext(is_valid);
});
void _set<T>(ref T backing, T value) {
backing = value;
_is_dirty = true;
}
}
static class SyntheticSecurityParser {
public const string file_extension = "csv";
public static string display_name(this ContractLegType leg_type) {
if (leg_type == ContractLegType.None)
return "N";
var flags = EnumHelper.split_flags(leg_type);
return string.Join("", flags.Select(f => f.ToString())
.Where(str => str.Length > 0)
.Select(str => str.Substring(0, 1)));
}
public static Action<Security, ContractLeg, LegSpreaderParams, object> assign_component(SyntheticContractComponent component) {
switch (component) {
case SyntheticContractComponent.Name: return (sec, leg, leg_params, val) => sec.Synthetic.Name = val?.ToString() ?? string.Empty;
case SyntheticContractComponent.TickSize: return (sec, leg, leg_params, val) => sec.ContractSpec.LowTick = sec.ContractSpec.HighTick = ConvertUtils.try_convert<double?>(val) ?? 0.0;
case SyntheticContractComponent.PriceMultiplier: return (sec, leg, leg_params, val) => leg.PriceMultiplier = ConvertUtils.try_convert<double?>(val) ?? double.NaN;
case SyntheticContractComponent.Quantity: return (sec, leg, leg_params, val) => leg.Quantity = ConvertUtils.try_convert<double?>(val) ?? double.NaN;
case SyntheticContractComponent.Security: return (sec, leg, leg_params, val) => leg.Security = val as Security ?? (val is SecurityImmutable si ? si.CloneToMutable() : null);
case SyntheticContractComponent.LegType: return (sec, leg, leg_params, val) => assign_leg_type(leg_params, ConvertUtils.try_convert(val, out ContractLegType lt) ? lt : ContractLegType.None);
default: throw new NotImplementedException();
}
}
/// Note: the returned dictionary will have entries for each <see cref="SyntheticContractComponent"/>, regardless of whether or not that component has any conflicts.
public static Dictionary<SyntheticContractComponent, Dictionary<string, List<(ParsedSyntheticContract synth, ParsedContractLeg leg)>>> get_conflicts(IEnumerable<ParsedSyntheticContract> parsed_fills) {
var missing_components = EnumHelper.get_values<SyntheticContractComponent>().ToDictionary(c => c, c => new Dictionary<string, List<(ParsedSyntheticContract synth, ParsedContractLeg leg)>>());
var mk_list = new Func<string, List<(ParsedSyntheticContract synth, ParsedContractLeg leg)>>(_ => new List<(ParsedSyntheticContract, ParsedContractLeg)>());
foreach (var psc in parsed_fills) {
if (!psc.name.success) {
add_missing(SyntheticContractComponent.Name, psc.name.text, psc);
}
if (!psc.tick_size.success) {
add_missing(SyntheticContractComponent.TickSize, psc.tick_size.text, psc);
}
foreach (var leg in psc.legs) {
if (!leg.price_multiplier.success)
add_missing(SyntheticContractComponent.PriceMultiplier, leg.price_multiplier.text, psc, leg);
if (!leg.quantity.success)
add_missing(SyntheticContractComponent.Quantity, leg.quantity.text, psc, leg);
if (!leg.security.success)
add_missing(SyntheticContractComponent.Security, leg.security.text, psc, leg);
if (!leg.leg_type.success)
add_missing(SyntheticContractComponent.LegType, leg.leg_type.text, psc, leg);
}
}
return missing_components;
void add_missing(SyntheticContractComponent component, string text, ParsedSyntheticContract result, ParsedContractLeg leg = null) =>
missing_components[component].GetOrAdd(text ?? string.Empty, mk_list).Add((result, leg));
}
public static Dictionary<SyntheticContractComponent, Dictionary<string, List<(SyntheticImportRowVM row, ContractLegVM leg)>>> get_conflicts(IEnumerable<SyntheticImportRowVM> import_rows) {
var missing_components = EnumHelper.get_values<SyntheticContractComponent>().ToDictionary(c => c, c => new Dictionary<string, List<(SyntheticImportRowVM row, ContractLegVM leg)>>());
var mk_list = new Func<string, List<(SyntheticImportRowVM row, ContractLegVM leg)>>(_ => new List<(SyntheticImportRowVM, ContractLegVM)>());
foreach (var ir in import_rows) {
var psc = ir.parsed_synth;
if (!psc.name.success) {
add_missing(SyntheticContractComponent.Name, psc.name.text, ir);
}
if (!psc.tick_size.success) {
add_missing(SyntheticContractComponent.TickSize, psc.tick_size.text, ir);
}
foreach (var leg in ir.legs) {
if (!leg.parsed_leg.price_multiplier.success)
add_missing(SyntheticContractComponent.PriceMultiplier, leg.parsed_leg.price_multiplier.text, ir, leg);
if (!leg.parsed_leg.quantity.success)
add_missing(SyntheticContractComponent.Quantity, leg.parsed_leg.quantity.text, ir, leg);
if (!leg.parsed_leg.security.success)
add_missing(SyntheticContractComponent.Security, leg.parsed_leg.security.text, ir, leg);
if (!leg.parsed_leg.leg_type.success)
add_missing(SyntheticContractComponent.LegType, leg.parsed_leg.leg_type.text, ir, leg);
}
}
return missing_components;
void add_missing(SyntheticContractComponent component, string text, SyntheticImportRowVM row, ContractLegVM leg = null) =>
missing_components[component].GetOrAdd(text ?? string.Empty, mk_list).Add((row, leg));
}
// This implementation prioritizes simplicity over speed.
// The security desc lookup could be better optimized, but the speed at which fills are imported is probably not a paramount concern.
public static IEnumerable<ParsedSyntheticContract> parse_rows(IEnumerable<string> rows, IReadOnlyDictionary<uint, SecurityImmutable> security_map, ISet<string> used_names) {
var sec_lookup = security_map.ToLookup(kv => kv.Value.ToString());
var name_col = Cells.name(used_names.Contains);
var sec_col = Cells.contract_security(lookup_security);
return RowParser.parse_rows(rows, columns(), Cells.delimiter);
Security lookup_security(string sec_text) =>
sec_lookup.Contains(sec_text) ? sec_lookup[sec_text].First().Value.CloneToMutable() : null;
IEnumerable<RowParser.AssignColumnValue<ParsedSyntheticContract>> columns() {
yield return name_col;
yield return Cells.tick_size;
// if an imported synth has > 255 legs, assume it is an error.
const int max_legs = 255;
int leg_count = 0;
do {
yield return sec_col;
yield return Cells.contract_quantity;
yield return Cells.contract_price_multiplier;
yield return Cells.leg_type;
} while (leg_count++ < max_legs);
}
}
public static Func<string, TKey> reverse_lookup<TKey>(IReadOnlyDictionary<TKey, string> map) {
var lookup = map.ToLookup(kv => kv.Value, kv => kv.Key);
return cell_text => lookup.Contains(cell_text) ? lookup[cell_text].First() : default;
}
public static void assign_leg_type(LegSpreaderParams leg_params, ContractLegType leg_type) {
leg_params.IsActiveQuoting = (leg_type & ContractLegType.Quote) != 0;
leg_params.EnableHedging = (leg_type & ContractLegType.Hedge) != 0;
}
static class Cells {
public const char delimiter = ',';
public static RowParser.AssignColumnValue<ParsedSyntheticContract> name(Func<string, bool> name_exists) =>
(ref ParsedSyntheticContract psc, string cell_text) => {
psc.name = (cell_text, !name_exists(cell_text));
psc.result.Synthetic.Name = cell_text;
};
public static void tick_size(ref ParsedSyntheticContract psc, string cell_text) {
psc.tick_size = (cell_text, double.TryParse(cell_text.Trim(), out var val));
if (psc.tick_size.success) {
psc.tick_size = (cell_text, val.IsNonZero());
psc.result.ContractSpec.LowTick = psc.result.ContractSpec.HighTick = val;
}
}
public static RowParser.AssignColumnValue<ParsedSyntheticContract> contract_security(Func<string, Security> reverse_security_lookup) =>
(ref ParsedSyntheticContract psc, string cell_text) => {
var leg = psc.add_new_leg();
leg.result.Security = reverse_security_lookup(cell_text.Trim());
leg.security = (cell_text, leg.result.Security != null);
};
public static void contract_quantity(ref ParsedSyntheticContract psc, string cell_text) {
psc.current_leg.quantity = (cell_text, double.TryParse(cell_text.Trim(), out var val) && val.IsNonZero());
psc.current_leg.result.Quantity = val;
}
public static void contract_price_multiplier(ref ParsedSyntheticContract psc, string cell_text) {
psc.current_leg.price_multiplier = (cell_text, double.TryParse(cell_text.Trim(), out var val) && val.IsNonZero());
psc.current_leg.result.PriceMultiplier = val;
}
public static void leg_type(ref ParsedSyntheticContract psc, string cell_text) {
psc.current_leg.leg_type = (cell_text, _parse_leg_type(cell_text.Trim(), out var leg_type));
assign_leg_type(psc.current_leg.leg_params, leg_type);
}
static bool _parse_leg_type(string cell_text, out ContractLegType result) {
result = ContractLegType.None;
if (cell_text.IsNullOrEmpty())
return false;
switch (char.ToUpper(cell_text[0])) {
case 'Q': result = ContractLegType.Quote; break;
case 'H': result = ContractLegType.Hedge; break;
case 'N': break;
default: return false;
}
return true;
}
}
}
}
@@ -1,52 +1,17 @@
using bts.api;
using bts.spark;
using mbts.ui.shell;
using System;
namespace mbts.ui.spark {
class LegSpreaderParamsTuple : ViewModelBase, IEquatable<LegSpreaderParamsTuple> {
[ThreadStatic]
static int _id_counter;
SecurityImmutable? _security;
namespace mbts.ui.spark
{
internal class LegSpreaderParamsTuple
{
public LegSpreaderParamsTuple(ContractLeg leg, LegSpreaderParams leg_params) {
id = unchecked(_id_counter++);
this.leg = leg;
this.leg_params = leg_params;
if (leg.Security != null)
_security = new SecurityImmutable(leg.Security);
}
public event Action security_changed;
public int id { get; }
/// <summary>
/// Important: do NOT modify security via <see cref="ContractLeg.Security"/>,
/// use <see cref="security"/> instead ([only]the latter raises the PropertyChanged event).
/// </summary>
public ContractLeg leg { get; }
public LegSpreaderParams leg_params { get; }
public SecurityImmutable? security {
get => _security;
set {
if (!_security.Equals(value)) {
_security = value;
leg.Security = value?.CloneToMutable();
security_changed?.Invoke();
RaisePropertyChanged();
}
}
}
public uint SecurityKey => leg.Security.Id;
public bool Equals(LegSpreaderParamsTuple other) =>
ReferenceEquals(this, other);
public override bool Equals(object obj) =>
Equals(obj as LegSpreaderParamsTuple);
public override int GetHashCode() =>
id;
}
}
}
@@ -4,8 +4,6 @@ using bts.api.core;
using bts.api.synthetic;
using bts.collections;
using bts.spark;
using bts.utils;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
@@ -84,61 +82,6 @@ namespace mbts.spark {
(mdc, success) => success ? mdc.Data[0].Strategy : (ExecutionStrategyImmutable?)null,
"lookup strategy");
public Task<(uint? key, string error_msg)[]> create_synthetics(IEnumerable<(SecurityImmutable security, ExecutionStrategyImmutable strategy)> synthetics) {
var tasks = synthetics.Select(tup => {
var (sec, strat) = tup;
if (sec.ContractCase != Security.ContractOneofCase.Synthetic)
return Task.FromException<(Result<CreateResult>, Result<UpdateResult>?)>(ExceptionBuilder.Argument.unexpected_case(sec.ContractCase, nameof(sec)));
if (sec.Synthetic.UserId != _user_id) {
var msec = sec.CloneToMutable();
msec.Synthetic.UserId = _user_id;
sec = new SecurityImmutable(msec);
}
return impl();
async Task<(Result<CreateResult> sec, Result<UpdateResult>? strat)> impl() {
var result = await _service.create_synthetic_async(sec);
Result<UpdateResult>? strategy;
if (result.success && result.value.Error == CreateResult.Types.Error.Ok) {
strategy = await _service.update_security_metadata_async(new Metadata {
SecurityKey = result.value.Key,
Strategy = tup.strategy.CloneToMutable()
});
} else {
strategy = null;
}
return (result, strategy);
}
});
return _error.safe_execute_batch(tasks,
tup => {
var (sec, strat) = tup;
// security
string sec_error_msg = null;
uint? key = null;
if (!sec.success) {
sec_error_msg = sec.error_message;
} else if (sec.value.Error != CreateResult.Types.Error.Ok) {
sec_error_msg = sec.value.Error.ToString();
} else {
key = sec.value.Key;
}
// strategy
string strat_error_msg = null;
if (!strat.HasValue) {
strat_error_msg = "Strategy was not successfully saved";
} else if (!strat.Value.success) {
strat_error_msg = strat.Value.error_message;
} else if (strat.Value.value.Error != UpdateResult.Types.Error.Ok) {
strat_error_msg = strat.Value.value.Error.ToString();
}
// merge error msgs (result should be null if both errors are empty)
var error_msg = string.Join(Environment.NewLine, new[] { sec_error_msg, strat_error_msg }.ExceptNull());
return (key, error_msg.NullIfEmpty());
},
"Synthetic",
"creating synthetics");
}
public Task<uint?> create_synthetic(SecurityImmutable synthetic) {
if (synthetic.ContractCase != Security.ContractOneofCase.Synthetic)
return null;
@@ -163,32 +106,10 @@ namespace mbts.spark {
(r, success) => success,
"saving strategy");
public async Task<string[]> save_strategies(IEnumerable<(uint key, ExecutionStrategyImmutable strategy)> items) {
// Copy everything to a new list, to guarentee that it won't be altered during the op.
// The performance impact should be insignificant, as this should be a rare call.
var item_list = new List<(uint key, ExecutionStrategyImmutable strategy, int index)>(items.Select((item, index) => (item.key, item.strategy, index)));
string[] errors = new string[item_list.Count];
await Task.WhenAll(item_list.Select(impl));
return errors;
async Task impl((uint key, ExecutionStrategyImmutable strategy, int index) tup) {
var (key, strategy, index) = tup;
try {
var result = await _service.update_security_metadata_async(new Metadata {
SecurityKey = key,
Strategy = strategy.CloneToMutable()
});
if (!result.success)
errors[index] = result.error_message;
} catch (Exception ex) {
errors[index] = ex.ToString();
}
}
}
public async Task<bool> verify_name_is_unique(string name) {
var (result, success) = await strategy_tuple.FirstAsyncTask(SparkUtils.DefaultTimeOut);
name = name.Trim().ToLower();
return success && !result.Values.Any(st => st.md.security.Synthetic.Name.Trim().ToLower() == name);
}
}
}
}
@@ -20,5 +20,5 @@ namespace mbts.ui.spark {
InitializeComponent();
}
class SyntheticManagementRowView : DIYGrid.HighPerformanceRow<SyntheticManagementDialogVM.Row> { }
class SyntheticManagementRowView : DIYGrid.HighPerformanceRow<SyntheticManagementDialogVM.Row, SyntheticManagementView.Columns> { }
}
@@ -1,9 +1,7 @@
using bts;
using bts.api;
using bts.shell;
using bts.shell.spreader;
using bts.spark;
using bts.view;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
@@ -20,19 +18,16 @@ using static bts.shell.SyntheticManagementWindowFactory;
namespace mbts.spark {
class SyntheticManagementWindow : SparkWindow<StartupData>, ISharedWindow {
const string
_window_type = "SyntheticManagement",
_import_title = "Import Synthetics";
static readonly TimeSpan _import_op_sec_map_timeout = TimeSpan.FromSeconds(30);
const string _window_type = "SyntheticManagement";
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
StartupData _state;
OrdersModel _orders_model;
SyntheticManagementView _view;
SparkMenuDisplayVM _menu_display_vm;
IDisposable _subscriptions;
IReadOnlyList<SparkCommand> _commands;
readonly IWindowSettingsManager<GridSettings> _settings_manager;
public SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, IWindowSettingsManager<GridSettings> settings_manager)
: base("Synthetic Management", bus, factory, SparkCommands.ShowSyntheticManagement.icon) {
_orders_model = orders_model;
@@ -45,7 +40,7 @@ namespace mbts.spark {
public override string window_type => _window_type;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public override StartupData SaveState => _state = _vm.value.create().with(menu_display: _menu_display_vm.safe_create());
public override StartupData SaveState => _state = _vm.value.create();
public override double MinWidth =>
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
@@ -55,8 +50,6 @@ namespace mbts.spark {
public void thread_init() {
Log.notnull(_state);
_menu_display_vm = new SparkMenuDisplayVM();
_menu_display_vm.load_from(_state.menu_display);
var vm_connectable_builder = new ConnectableSeriesBuilder<SyntheticManagementDialogVM>(_vm.observable);
var current_row_source = vm_connectable_builder.create(vm => vm.current_row_observable);
@@ -97,12 +90,7 @@ namespace mbts.spark {
var dialog = new ShellSettingsDialog { Title = "Synthetic Management Grid Settings", DataContext = vm };
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
})),
new SparkCommandCommand(
"Import Synthetics...", Icons.Import, FileDialogCommand.open_file(import, extensions: SyntheticSecurityParser.file_extension)) {
show_in_toolbar = false
},
_menu_display_vm.spark_cmd
}))
};
_subscriptions = Disposable.Create(
@@ -117,16 +105,13 @@ namespace mbts.spark {
r == null ? Observable.Return(new RowStrategy?()) :
_orders_model.spreader.security_strategy(r.security.Id).Select(strategy => RowStrategy.create(r.security, strategy));
IImmutableSet<uint> select_security_id(RowModificationParams? rmp) => ImmutableHashSet.Create(rmp.HasValue ? rmp.Value.security.Id : 0);
uint select_security_id(RowModificationParams? rmp) => rmp.HasValue ? rmp.Value.security.Id : 0;
(SecurityImmutable, ExecutionStrategyImmutable) select_row_strategy_tuple(RowStrategy? rs) => rs.HasValue ? (rs.Value.security, rs.Value.strategy) : default;
bool can_execute(RowModificationParams? rmp) => rmp.HasValue && !rmp.Value.has_working_orders;
_vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, _settings_manager, _state.sorting_criteria, _state.filters ?? new FilterStates());
if (_view == null) _view = new SyntheticManagementView { DataContext = _vm.value };
else _view.DataContext = _vm.value;
void import(string path) =>
SyntheticImporter.import_from_file(path, _bus, _orders_model);
}
public override void Dispose() {
@@ -134,9 +119,6 @@ namespace mbts.spark {
Commands.OfType<IDisposable>().DisposeAll();
}
protected override void OnParentViewCreated(SparkWindowView parent_view) =>
_menu_display_vm.safe_bind_to_view(parent_view);
struct RowModificationParams {
public readonly SecurityImmutable security;
public readonly bool has_working_orders;
@@ -163,4 +145,4 @@ namespace mbts.spark {
new RowStrategy(security, strategy);
}
}
}
}
@@ -48,28 +48,14 @@ namespace bts.shell {
public readonly FilterStates filters;
[ProtoMember(3)]
public readonly GridSettings grid;
[ProtoMember(4)]
public readonly SparkMenuDisplaySettings menu_display;
public StartupData() : this(new List<SortingCriteria>(), null, null) { }
public StartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, GridSettings grid = default)
: this(sorting_criteria, filters, grid, SparkMenuDisplaySettings.Default) { }
public StartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, GridSettings grid, SparkMenuDisplaySettings menu_display) {
public StartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, GridSettings grid = default) {
this.sorting_criteria = sorting_criteria;
this.filters = filters;
this.grid = grid;
this.menu_display = menu_display;
}
public StartupData with(List<SortingCriteria> sorting_criteria = null, FilterStates filters = null, GridSettings grid = null, SparkMenuDisplaySettings? menu_display = null) =>
new StartupData(
sorting_criteria ?? this.sorting_criteria,
filters ?? this.filters,
grid ?? this.grid,
menu_display ?? this.menu_display);
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
static class DefaultColumns {
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel : RowModelBase<uint> {
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public override string display_name { get; } = "Cross Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross)
omv_cross.CrossTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.CrossTicks);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.CrossTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeOppositeMarketVolumeBasedCrossThresholdModel : RowModelBase<QuantityMeasure> {
IDictionary<int, QuantityMeasure> _value_store = new Dictionary<int, QuantityMeasure>();
IDictionary<uint, QuantityMeasure> _value_store = new Dictionary<uint, QuantityMeasure>();
public override string display_name { get; } = "Threshold";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, QuantityMeasure value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross)
omv_cross.QuantityThreshold = value;
}
@@ -36,7 +36,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross;
QuantityMeasure _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.QuantityThreshold);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.QuantityThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgePriceBasedCrossMarketTicksModel : RowModelBase<uint> {
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public override string display_name { get; } = "Cross Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_price_based_cross(leg_tup) is PostHedgePriceBasedForcedCross price_based_cross)
price_based_cross.MarketCrossTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.PriceBasedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgePriceBasedForcedCross.MarketCrossTicks);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgePriceBasedForcedCross.MarketCrossTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgePriceBasedCrossTicksModel : RowModelBase<uint> {
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public override string display_name { get; } = "Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_price_based_cross(leg_tup) is PostHedgePriceBasedForcedCross price_based_cross)
price_based_cross.TicksThreshold = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.PriceBasedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgePriceBasedForcedCross.TicksThreshold);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgePriceBasedForcedCross.TicksThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeTimeBasedCrossMarketTicksModel : RowModelBase<uint> {
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public override string display_name { get; } = "Cross Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_time_based_cross(leg_tup) is PostHedgeTimeBasedForcedCross time_based_cross)
time_based_cross.MarketCrossTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.TimeBasedForcedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgeTimeBasedForcedCross.MarketCrossTicks);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeTimeBasedForcedCross.MarketCrossTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeTimeBasedCrossTimeModel : RowModelBase<uint> {
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public override string display_name { get; } = "Time(ms)";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_time_based_cross(leg_tup) is PostHedgeTimeBasedForcedCross time_based_cross)
time_based_cross.TimeInMsThreshold = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.TimeBasedForcedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgeTimeBasedForcedCross.TimeInMsThreshold);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeTimeBasedForcedCross.TimeInMsThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PreHedgePaylessThresholdModel : RowModelBase<QuantityMeasure> {
IDictionary<int, QuantityMeasure> _value_store = new Dictionary<int, QuantityMeasure>();
IDictionary<uint, QuantityMeasure> _value_store = new Dictionary<uint, QuantityMeasure>();
public override string display_name { get; } = "Threshold";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, QuantityMeasure value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_payless_ticks(leg_tup) is PreHedgeQuantityBasedPayLessTicks payless_ticks)
payless_ticks.QuantityThreshold = value;
}
@@ -36,7 +36,7 @@ namespace mbts.spark {
leg_tup.leg_params.PreHedgeParams.QuantityBasedPaylessTicks;
QuantityMeasure _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayLessTicks.QuantityThreshold);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayLessTicks.QuantityThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PreHedgePaylessTicksModel : RowModelBase<uint> {
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public override string display_name { get; } = "Payless Ticks";
public override string help_text { get; } = string.Empty;
@@ -23,7 +23,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_payless_ticks(leg_tup) is PreHedgeQuantityBasedPayLessTicks payless_ticks)
payless_ticks.PaylessTicks = value;
}
@@ -35,7 +35,7 @@ namespace mbts.spark {
leg_tup.leg_params.PreHedgeParams.QuantityBasedPaylessTicks;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayLessTicks.PaylessTicks);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayLessTicks.PaylessTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PreHedgePayupThresholdModel : RowModelBase<QuantityMeasure> {
IDictionary<int, QuantityMeasure> _value_store = new Dictionary<int, QuantityMeasure>();
IDictionary<uint, QuantityMeasure> _value_store = new Dictionary<uint, QuantityMeasure>();
public override string display_name { get; } = "Threshold";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, QuantityMeasure value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_payup_ticks(leg_tup) is PreHedgeQuantityBasedPayupTicks payup_ticks)
payup_ticks.QuantityThreshold = value;
}
@@ -36,7 +36,7 @@ namespace mbts.spark {
leg_tup.leg_params.PreHedgeParams.QuantityBasedPayupTicks;
QuantityMeasure _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayUpTicks.QuantityThreshold);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayUpTicks.QuantityThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PreHedgePayupTicksModel : RowModelBase<uint> {
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public override string display_name { get; } = "Payup Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_payup_ticks(leg_tup) is PreHedgeQuantityBasedPayupTicks payup_ticks)
payup_ticks.PayupTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PreHedgeParams.QuantityBasedPayupTicks;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayUpTicks.PayupTicks);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayUpTicks.PayupTicks);
}
}
}
@@ -9,7 +9,7 @@ namespace mbts.spark {
{
class ConditionalQuotingPriceTypeModel : RowModelBase<PriceType>
{
IDictionary<int, PriceType> _value_store = new Dictionary<int, PriceType>();
IDictionary<uint, PriceType> _value_store = new Dictionary<uint, PriceType>();
public override string display_name { get; } = "Price Type";
public override string help_text { get; } = string.Empty;
@@ -26,7 +26,7 @@ namespace mbts.spark {
public override void set_value(LegSpreaderParamsTuple leg_tup, PriceType value)
{
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_value(leg_tup) is ConditionalQuoting cond)
cond.PriceType = value;
}
@@ -37,7 +37,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
PriceType _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.PriceType);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.PriceType);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class ConditionalQuotingTicksModel : RowModelBase<int> {
IDictionary<int, int> _value_store = new Dictionary<int, int>();
IDictionary<uint, int> _value_store = new Dictionary<uint, int>();
public override string display_name { get; } = "Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, int value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
if (_get_value(leg_tup) is ConditionalQuoting cond)
cond.NTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
int _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.NTicks);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.NTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Windows;
namespace mbts.spark {
partial class SyntheticEditorVM {
class QuoteHiddenModel : RowModelBase<bool>, IHaveFontParams {
IDictionary<int, bool> _value_store = new Dictionary<int, bool>();
IDictionary<uint, bool> _value_store = new Dictionary<uint, bool>();
public override string display_name { get; } = "Hidden";
public override string help_text { get; } = "A hidden quote is not sent out until the market crosses the computed quoting price. When that happens, the quote is sent out as an IOC order. This continues until the synthetic spreader order is fully filled, or cancelled.";
@@ -21,7 +21,7 @@ namespace mbts.spark {
_get_throttle(leg_tup) != null;
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
_value_store[leg_tup.id] = value;
_value_store[leg_tup.SecurityKey] = value;
leg_tup.leg_params.QuoteParams.QuoteHidden = value ? new QuoteHidden() : null;
}
@@ -32,7 +32,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.QuoteHidden;
bool _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.id, _ => false);
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => false);
}
}
}
Loaded 100 of 548 files, more files were not shown because too many files have changed in this diff. Show more