Compare commits

...
789 Commits
Author SHA1 Message Date
Eric Harding 48f56e76d4 simple API explorer tool 2018-06-05 10:03:17 -04:00
Eric Harding 05e6c40a57 Update c# language version 2018-06-05 10:03:17 -04:00
Eric Harding 58464b8a0d Automatically apend install_config.json to bts_installer.exe when access via 2018-06-05 08:29:33 -04:00
Taha Afzal 1785f3e34d reducing excessive logging 2018-06-05 07:33:51 -04:00
Taha Afzal 33aeb6b7ba LegPropertyData just contains references to SpreaderBookkeepingData 2018-06-04 22:51:24 -04:00
Michael Harding d8fbfb49e8 AdminUI: Style cleanup 2018-06-04 17:27:27 -04:00
Pedro Pinto 4803c66506 Fixed a few more hardcoded references to .bts 2018-06-04 17:15:29 -04:00
Pedro Pinto 046a0ad9c8 Fixed fable build depencency tracking. 2018-06-04 17:10:36 -04:00
Pedro Pinto 3f292e87da Towards security name formatting in C++. 2018-06-04 17:10:36 -04:00
Eric Harding 7c8a801407 SPARK-1198 - Include config sub path 2018-06-04 17:10:36 -04:00
Eric Harding 4c7949ff8d SPARK-1198 Directory Shuffle 2018-06-04 17:10:36 -04:00
Taha Afzal fdf727a52a fixing clang build warning 2018-06-04 16:45:36 -04:00
Taha Afzal 035911d126 SpreaderChildOrderState now has one cons only 2018-06-04 16:33:20 -04:00
Taha Afzal 58b16a4b2c culled unused hedger functions 2018-06-04 16:33:20 -04:00
Michael Harding 6e9fd68ac4 Merged adminui into master 2018-06-04 16:28:19 -04:00
Michael Harding c5289a5416 Merged master into adminui 2018-06-04 16:12:27 -04:00
Michael Harding b33fe6c8cc SPARK-1155, SPARK-1163 2018-06-04 15:55:48 -04:00
John Colvin bd95b87798 TableUpdateDemuxer - fix potential race bug 2018-06-04 13:01:04 -04:00
Jake Norbut 8132cb003d Merged in single_badge (pull request #33)
SPARK-1141 - Single badge

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-06-04 16:46:41 +00:00
Jake Norbut 9e59f0c5a2 master -> feature (overwrote John's changes, which we discussed; he will reapply after merge in master) 2018-06-04 11:45:24 -05:00
John Colvin e1e0565fbd SPARK-1196 synth dialog - security text in column header is wrong 2018-06-04 11:43:33 -04:00
Jake Norbut f69ec8d32b SPARK-1195 - GLBX spreas are now formatted by their Security Description 2018-06-04 10:41:59 -05:00
Taha Afzal c3fdf8cb4f logs, odds and ends 2018-06-04 11:12:24 -04:00
Taha Afzal 1670443b0f stylistic changes in the code 2018-06-04 07:10:12 -04:00
Taha Afzal 73fa2b669e cycle_back removed from QuoteLegPriceQuantity 2018-06-04 07:10:12 -04:00
Taha Afzal 2d3f576764 SpreaderHedgerData was unecessarily adding a layer of complexity, gone now 2018-06-04 07:10:12 -04:00
John Colvin 56b855ea83 SPARK-1194 modify ticket should default to original quantity (not working) 2018-06-03 18:12:54 -04:00
John Colvin 8512b40f50 Ladder - use backing-properties instead of detect_property_changes 2018-06-03 14:45:48 -04:00
John Colvin 1983835588 FillsVM - security filter updates itemssource incrementally 2018-06-03 14:45:48 -04:00
John Colvin cf6a9ac4fa upgrade NUnit (3.8.0 -> 3.10.1) in spark_core/spark_sdk 2018-06-03 12:43:11 -04:00
John Colvin c59569310f mview - sort working orders by price to optimize working-orders-summary getter 2018-06-02 19:36:00 -04:00
John Colvin a86b231581 update NUnit package from 3.8.0 -> 3.10.1 2018-06-02 19:27:17 -04:00
John Colvin 46365c4f2e TableUpdateDemuxer - subscribers first notification is an update that adds all existing values 2018-06-02 19:22:27 -04:00
John Colvin c56adb2dc0 SPARK-1184 shell crashed on launch 2018-06-02 16:38:45 -04:00
John Colvin 643482bb09 SecurityFormatter - for unknown security types, don't throw an exception,
return "Unknown-security"
2018-06-02 16:38:45 -04:00
John Colvin 038946eddc DIYGrid - only refresh rows-view once when sorting changes 2018-06-02 16:38:45 -04:00
Pedro Pinto 07348a9ec8 Fixed clang fable build 2018-06-01 17:44:19 -04:00
Eric Harding 0e29a141bf SPARK-1191 security list api 2018-06-01 17:05:52 -04:00
Michael Harding fdefad6a73 AdminUI: Filter Security 2018-06-01 16:33:16 -04:00
Taha Afzal 89ee079320 stylistic changes to the hedger state machine cpp classes 2018-06-01 15:58:46 -04:00
Taha Afzal 5f37bd5d14 got rid of redudnant state in QuotePriceLegState 2018-06-01 15:58:46 -04:00
Taha Afzal 892ba478fd quoter gets some minor cleanup and more comments 2018-06-01 15:58:46 -04:00
John Colvin ab88cd7920 CVOrderBook - add security discrete values filter 2018-06-01 15:22:01 -04:00
John Colvin 02fedc577a discrete values filter supports incremental updates 2018-06-01 15:22:01 -04:00
John Colvin 87568a6a53 CVOrderBook - improve filter/sort performance by storing parent/children locally 2018-06-01 15:22:01 -04:00
Jake Norbut 8440e144b4 master => feature (again) 2018-06-01 13:39:25 -05:00
Jake Norbut 11cbd7440d taha nits 2018-06-01 13:30:39 -05:00
Eric Harding d59bb5ed0d Merged in TactileAdorner (pull request #32)
Quick: Fix problem where you can't see price moves when mouse is over a cell in the ladder

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-06-01 17:33:13 +00:00
Jake Norbut d30c857e50 master -> feature merge 2018-06-01 11:04:59 -05:00
Eric Harding 37aa7088a9 Add visual studio perf session files to gitignore 2018-06-01 10:30:43 -04:00
Michael Harding b92ad91799 fix ui.fsproj 2018-06-01 10:15:58 -04:00
Michael Harding 6d8ee84813 Fix Webpack config for development 2018-06-01 10:15:58 -04:00
Michael Harding 05ac067b23 SPARK-1155 2018-06-01 10:15:58 -04:00
Eric Harding 8e7163c5c6 SPARK-1189 - Windows C++ build is not producing symbols file 2018-06-01 09:51:20 -04:00
Eric Harding 763beda95c Merged in SPARK-1183 (pull request #34)
SPARK-1183 3-5 1000 lot iceberg orders can lag/lock up the UI

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-06-01 00:15:43 +00:00
John Colvin 472e2b2f0c change UpdateDemuxer to TableUpdateDemuxer, which updates incrementally 2018-05-31 20:13:01 -04:00
Jake Norbut 2998a2b753 Stop and Stop Orders will now reside withing the correct badge; when a badge contains multiple orders, it will present with a light purple outline; still need to support buy and sell at same level - we'll likely eliminate different badge styles in the end 2018-05-31 17:00:21 -05:00
Eric Harding 036550aaae SPARK-1183 3-5 1000 lot iceberg orders can lag/lock up the UI 2018-05-31 17:20:08 -04:00
John Colvin 4378947e44 CVOrderBook - fix typeo that trapped the root_node() fn in an endless loop 2018-05-31 16:40:34 -04:00
John Colvin bd7a698259 clean up SecurityFormatter.cs 2018-05-31 15:56:30 -04:00
Eric Harding 65341ee35d SPARK-1176 fable build output should go in ~/build/bla. 2018-05-31 13:19:53 -04:00
Jake Norbut e57a3d37f7 relocating where to draw stop orders; need to test against cme cert 2018-05-31 09:37:09 -05:00
Jake Norbut 437e92b361 merging master in feature branch 2018-05-31 08:55:57 -05:00
John Colvin a9c3c41d6e Merged in ui-improvements (pull request #31)
Ui improvements

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-31 00:56:02 +00:00
John Colvin 5a12a973f3 pr: finishing touches 2018-05-30 20:54:01 -04:00
Jake Norbut 639b2dfe09 make badge a bit bigger to use available space 2018-05-30 16:45:02 -05:00
Jake Norbut 8cdbf873cd Fixing completion since returning the a double will not really work summing over multiple ordres (0 screws it up); also, no more Opacity 2018-05-30 16:26:33 -05:00
Jake Norbut 7990c97921 Changing default Order column size back to single badge values 2018-05-30 15:25:59 -05:00
Jake Norbut fce22c28d1 hooking up crack 2018-05-30 15:06:40 -05:00
Jake Norbut 339cd017d5 Remove Modify context menu item 2018-05-30 14:56:14 -05:00
Eric Harding 3380d2ba0b Fix problem where you can't see price moves when mouse is over a cell in the ladder 2018-05-30 15:47:30 -04:00
Jake Norbut d18d8dcf37 GetHashCode and Equals 2018-05-30 14:40:45 -05:00
Jake Norbut 6bf628da5f SPARK-1141 - single badge 2018-05-30 13:11:17 -05:00
John Colvin bbcc92c38c pr edit: use working_order_background_brush defined in BTSColors 2018-05-30 13:50:46 -04:00
John Colvin 0ad1f73293 pr edit: add working_orders to OrdersModel 2018-05-30 13:30:02 -04:00
Pedro Pinto 1b100bccf7 SPARK-1175, Picked price of the resting rather than the aggressor 2018-05-30 10:39:25 -04:00
Eric Harding 7a74c5852a Merged in SPARK1139 (pull request #29)
SPARK-1139 Improve ladder performance

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-30 13:37:06 +00:00
Eric Harding 49f3c8b329 Code review feedback 2018-05-30 09:33:39 -04:00
John Colvin 9514a10c27 CVOrderBookRowVM GetHashCode returns order key 2018-05-29 19:32:41 -04:00
Pedro Pinto e6a7db2c61 SPARK-1149, Logging is bounded to 2 files at max, 120MB each. 2018-05-29 17:43:10 -04:00
Taha Afzal f69039c043 SpreaderChildOrderState commented and added state to mantain the out order/qty 2018-05-29 17:38:55 -04:00
Taha Afzal 40d6727b02 QuoteLegPriceQuantityState's cpp added 2018-05-29 17:38:55 -04:00
John Colvin 796c39988f Merged master into ui-improvements 2018-05-29 17:01:12 -04:00
Pedro Pinto 0f2a8cd746 SPARK-1178, Passed build binary dir to fable build script. 2018-05-29 16:31:09 -04:00
John Colvin 55d05ecd12 Merged master into ui-improvements 2018-05-29 16:25:22 -04:00
John Colvin 8b61a63634 new orderbook code clean-up 2018-05-29 16:22:18 -04:00
Eric Harding 3431ffc480 SPARK-1139 Improve ladder performance 2018-05-29 13:59:11 -04:00
Jake Norbut c22ce4b886 SPARK-1165 - Fills windows default filtes to since Globex open 2018-05-29 10:19:15 -05:00
Eric Harding eab2852773 Merged in SparkPersist (pull request #28)
SPARK-1162 UserPersist should use ID

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-29 13:15:21 +00:00
Taha Afzal 77d47fd387 header file include culling 2018-05-28 23:44:34 -04:00
Taha Afzal eccd965ce1 adding SpreaderBookkeepingData's cpp file 2018-05-28 22:11:06 -04:00
Taha Afzal ca8b691160 SpreaderLegData broken into h and cpp, adding more comments and tightening some const's 2018-05-28 19:34:06 -04:00
Taha Afzal bcaf65d7c6 code moved for the wrappers around SpreaderorderBuilder into their own cpp's 2018-05-28 15:55:21 -04:00
John Colvin fa7725eba2 OrderBookVM (original) - reduce unneccesary iterations in get_parent_order_vm() 2018-05-28 14:47:34 -04:00
John Colvin 34aa1d20f2 OrderBookVM (original) needs to toggle sort-direction itself 2018-05-28 14:40:08 -04:00
Taha Afzal 85643adcf3 comments and some stylistic changes for the limiter 2018-05-27 22:25:29 -04:00
Taha Afzal 4b9e046046 limits were still using ints for accont ids, fixed that 2018-05-27 22:25:29 -04:00
John Colvin 4bafada602 integrate ICollectionView into OrderBook with 'CVOrderBookVM' 2018-05-27 16:50:07 -04:00
John Colvin 701e883aea DiscreteValuesFilter - guard against null desired-items 2018-05-27 15:42:33 -04:00
John Colvin 7e54eea62f DIYGrid - fix sorting initialization bug (when initial sort comes from Rows.SortDescriptions) 2018-05-27 15:36:36 -04:00
Taha Afzal b2eeddc628 prehedge payup tests 2018-05-26 22:10:50 -04:00
Taha Afzal 9b2a4ca6b1 prehedge payless tests 2018-05-26 19:49:24 -04:00
John Colvin 9488620041 OrderUtils - check State.HasCreated/LastChange as well 2018-05-26 13:36:06 -04:00
John Colvin 0ad78b75ca OrderUtils - check HasDef/HasState before accessing Def/State 2018-05-25 21:16:54 -04:00
Taha Afzal c99a3c9fe9 comments 2018-05-25 18:26:23 -04:00
Taha Afzal bc4643fbf3 spreader tests 2018-05-25 17:33:35 -04:00
Jake Norbut 792801f936 Ladder - restoring badge context menu and hooking up crack 2018-05-25 15:20:36 -05:00
John Colvin dd4ef2429e DIYGrid HighPerformanceRow - InvalidateVisual when DataContext changes 2018-05-25 14:51:45 -04:00
Michael Harding 0af6290566 Order Type Colors 2018-05-25 13:34:12 -04:00
Eric Harding 7e8b804fa0 SPARK-1139 Make Combox faster when system is under load (no AllowsTransparency) 2018-05-25 09:17:00 -04:00
Taha Afzal df769d55e4 re-org'ing SpreaderUtils functions into cpp 2018-05-24 19:38:44 -04:00
Taha Afzal 1ea65185bd SPARK-1119 completed 2018-05-24 16:19:16 -04:00
Eric Harding 8d068221dc Reduced CPU for cert market data during development 2018-05-24 10:05:22 -04:00
Eric Harding d3f2ecc78e SPARK-1147 Add requited glbx fields to enable this work. 2018-05-23 19:27:37 -04:00
Eric Harding 38f212e633 SPARK-1162 UserPersist should use ID 2018-05-23 18:41:19 -04:00
Taha Afzal 97e73a23b0 SPARK-1119 (deleting unused API functions) 2018-05-23 18:12:55 -04:00
Pedro Pinto 7546c5f2e2 CmakeDeps 2018-05-23 21:43:59 +00:00
Pedro Pinto 324d1a3d74 CMakeDeps 2018-05-23 21:40:21 +00:00
Pedro Pinto f9c15a4d44 SPARK-1126. Hopefully finally nailed FIX price encoding. 2018-05-23 21:40:21 +00:00
Pedro Pinto 68fb8dade4 Cmakedeps. 2018-05-23 21:40:21 +00:00
Jake Norbut ee1230c5f9 Merged in taco_settings (pull request #25)
SPARK-1140 - Emulating the Taco

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-23 21:30:37 +00:00
Eric Harding 1c98cfab0d Add user id to UserService responses. Unblocks SPARK-1162 2018-05-23 17:23:25 -04:00
Eric Harding 376e2c3af8 Merged in SPARK1150 (pull request #27)
SPARK-1150 Ladder re-center timer may be leaking

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-23 21:19:46 +00:00
Jake Norbut bbce4bd05c PR suggestions 2018-05-23 16:10:58 -05:00
Jake Norbut c98559beb6 PR suggestions 2018-05-23 16:03:53 -05:00
Eric Harding c13c8ba16f SPARK-1150 Ladder re-center timer may be leaking 2018-05-23 16:14:54 -04:00
Taha Afzal 4d9f26af90 Merged in uint (pull request #26)
Uint
2018-05-23 19:35:42 +00:00
Taha Afzal 23a732d6da SPARK-1119 2018-05-23 15:19:04 -04:00
Jake Norbut 7b2186261a hardening the bevhavior just in case the text hasn't been previously sanitized 2018-05-23 12:41:10 -05:00
Jake Norbut 63d88993a7 Adding an alterntae order size setting; right clicking in a slot will send the alternate order size when it's not 0 2018-05-23 12:35:12 -05:00
Jake Norbut 152c02e45e no more context menu, just directly bring up the modify order ticket on mid click 2018-05-23 10:32:17 -05:00
Eric Harding 7bf4abf38d Add some temporary return data for security_names service 2018-05-23 11:15:05 -04:00
Jake Norbut 3ab3259f9a Badge behavior - Left click remains cancel, right click + drag = move order, mid click = context menu 2018-05-23 09:11:14 -05:00
Eric Harding cf0916379c SPARK-1119 F# part 2018-05-23 09:55:25 -04:00
Eric Harding e8b4567e60 Add request_names to market data service to get a list of symbols for securities 2018-05-23 09:28:36 -04:00
Eric Harding a0e86faa78 Bugfix in c++ service generator. Need full namespace for service with argument types in a different namespace. 2018-05-23 09:23:16 -04:00
Eric Harding 2cbd5510e2 Remove accidental copy of market data. yay c++. 2018-05-23 09:10:00 -04:00
Taha Afzal b9de5602cd SPARK-1119 2018-05-22 22:46:02 -04:00
John Colvin 96369aa5ee Merged in implementing-diy-grid (pull request #24)
Implementing diy grid

Approved-by: John Colvin <john.colvin@bluetradesys.com>
Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-05-22 22:52:06 +00:00
John Colvin 2115ef9b74 pr edits 2018-05-22 18:50:36 -04:00
Jake Norbut 75ee9a954b qty text box - updating behavior name 2018-05-22 17:34:33 -05:00
Jake Norbut 7ed0167c4e Qty button mouse behavior 2018-05-22 17:30:00 -05:00
Taha Afzal b5220050f2 SPARK-1124 2018-05-22 17:07:30 -04:00
Jake Norbut 875f32b980 SPARK-1154 - Order Types in caps on Ladder 2018-05-22 15:31:43 -05:00
Michael Harding e9e0d017aa AdminUI: Improvements 2018-05-22 15:51:21 -04:00
Taha Afzal 1d065678d2 SPARK-1125 (deleting accounts) 2018-05-22 13:36:04 -04:00
Michael Harding bb54a8b448 AdminUI: Improvements 2018-05-22 10:06:39 -04:00
Taha Afzal 0629875c3a SPARK-1124 (deleting users/accounts properly) 2018-05-21 19:59:33 -04:00
John Colvin 38e8463717 OrderBook SimpleTextColumn - check for null/empty text 2018-05-21 19:18:21 -04:00
John Colvin 7a30b58cea HyperlinkCell - fix lingering-underline bug 2018-05-21 19:01:10 -04:00
John Colvin e406f463de disable GlyphRun anti-aliasing for now (doesn't work for all fonts) 2018-05-21 18:55:56 -04:00
Taha Afzal 59d68503a8 SPARK-1124 (deleting users/accounts properly) 2018-05-21 18:45:50 -04:00
John Colvin 909336ccbd pr edits 2018-05-21 17:39:26 -04:00
John Colvin 7d45bc9a36 undo subrepo change 2018-05-21 16:28:16 -04:00
Kevin Darby 23c8b60ea3 spreader post hedge should be < thresh not > 2018-05-21 13:57:36 -04:00
John Colvin 4dc7fb0bea Merged master into implementing-diy-grid 2018-05-21 13:21:11 -04:00
John Colvin 4ea08c09ee DIYGrid - implement OrderBook 2018-05-21 13:19:22 -04:00
Eric Harding 6ec39a9276 SPARK-510 - protobuf defaults require the assignment as well as the DefaultValue (caught in CR) 2018-05-21 11:23:59 -04:00
Eric Harding 4a74066319 Merged in SPARK-510 (pull request #23)
SPARK-510

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2018-05-21 14:59:05 +00:00
Eric Harding 80fde10cd5 Merged in SPARK-1144 (pull request #22)
SPARK-144 Remove Docking Facility from Launcher

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2018-05-21 14:57:49 +00:00
Eric Harding 58c3303004 SPARK-510 Launcher settings 2018-05-21 10:47:37 -04:00
Eric Harding c58b6fee77 SPARK-144 Remove Docking Facility from Launcher 2018-05-21 10:34:13 -04:00
Eric Harding b8b284441c SPARK-948 Test CME decimal precision increase 2018-05-21 10:22:50 -04:00
Eric Harding 07e89ff972 Fix build: unused argument 2018-05-21 08:41:07 -04:00
Eric Harding da90ffef12 SPARK-1080 Disable broken Windows syslog sink 2018-05-21 08:40:05 -04:00
Eric Harding b12f7cac6d WebUI: inline some functions which will no-op in javascript 2018-05-21 08:39:36 -04:00
John Colvin c40dfdd171 post-merge cleanup 2018-05-19 21:09:40 -04:00
John Colvin b8163c7f1e Merge branch 'master' into implementing-diy-grid 2018-05-19 18:29:28 -04:00
Pedro Pinto 7693933a7b SPARK-1132, Tag 50 was not being filled in correctly. 2018-05-18 17:23:06 -04:00
Pedro Pinto 675f3e0675 Merge branch 'master' into prod 2018-05-18 17:00:13 -04:00
Pedro Pinto 44454ef4c0 Autocert 2018-05-18 17:00:01 -04:00
Pedro Pinto f6f59adcc5 Merge 2018-05-18 16:58:48 -04:00
Pedro Pinto dad1e40f93 Merged from dev 2018-05-18 16:58:17 -04:00
Pedro Pinto ab950cba91 Merged from dev 2018-05-18 16:57:03 -04:00
Eric Harding 4f32e86fbd WebUI: Remove '*' from Exchange and SecurityType 2018-05-18 15:32:23 -04:00
Pedro Pinto 132516b0a7 Autocert 2018-05-18 15:08:01 -04:00
Eric Harding b95fd9e604 Web: filter accounts dropdown for already added accounts 2018-05-18 13:47:12 -04:00
Eric Harding 72fa852208 Bikeshedding 2/1E100 2018-05-18 13:06:08 -04:00
Michael Harding 609bd05734 SPARK1128, SPARK1129 2018-05-18 13:00:20 -04:00
Eric Harding 9dbf6cafbd Bikeshedding 2018-05-18 12:51:05 -04:00
Eric Harding 3ae7a3976f WebUI: Rename column headers 2018-05-18 12:49:52 -04:00
Eric Harding 5aff485890 SPARK-1127 Confirmation for removing a user 2018-05-18 12:49:52 -04:00
Michael Harding 430511e4a8 SPARK-1130 2018-05-18 12:46:12 -04:00
Michael Harding a2403c643d SPARK-1127 2018-05-18 12:32:51 -04:00
Jake Norbut 1bd2b4c601 adding order router test 2018-05-18 11:30:18 -05:00
John Colvin 4cd6da6c30 didn't save before last commit 2018-05-18 12:26:14 -04:00
John Colvin c2a5be6d67 use id comparer in OrderActivitySummarizer 2018-05-18 12:21:33 -04:00
Michael Harding 9d70906aca SPARK-1127: 2018-05-18 11:38:31 -04:00
Eric Harding 8e0bbbc312 hey, can you quickly make the warning at the bottom of the ladder higher contrast? 2018-05-18 11:37:11 -04:00
Eric Harding e52fc2d196 Add Lens library for help simplify some verbose code in admin gui 2018-05-18 11:21:22 -04:00
Taha Afzal 47b1e8f64d SPARK-1017 2018-05-18 10:47:24 -04:00
Eric Harding ae3b94d6f4 Web UI: Sort accounts *before* selecting first one 2018-05-18 08:51:08 -04:00
Eric Harding 44cf948d5a SPARK-1108 Enums for Humans 3/3 2018-05-18 08:46:35 -04:00
Eric Harding d983db75c4 SPARK-1108 Enums for Humans 2/3 2018-05-18 08:22:33 -04:00
Eric Harding ba02db4e40 SPARK-1108 Enums for Humans 1/2 2018-05-18 08:18:29 -04:00
Eric Harding 68ca16103d Web UI: Sort account list 2018-05-17 17:29:22 -04:00
Michael Harding e36cb26bec fix selectedaccount 2018-05-17 17:26:23 -04:00
Michael Harding 74bc669083 SPARK1100, 1099, 1083 2018-05-17 17:21:05 -04:00
Michael Harding 986f228113 AdminUI: Hide 'deleted' users 2018-05-17 17:21:04 -04:00
Jake Norbut 92b02833df SPARK-1082 - for now on one line, but next sprint we should do it right like John suggested with a MeasureOverride and what not 2018-05-17 15:16:53 -05:00
John Colvin 96754066bd fix ActivityLog categories typeo 2018-05-17 16:06:02 -04:00
Eric Harding c6ed7a46cd SPARK-1117 Entering text at the start of text fields misbehaves 2018-05-17 16:04:35 -04:00
Pedro Pinto 8a05c1dbf7 Fixed fable build some more. 2018-05-17 14:46:18 -04:00
Pedro Pinto 5fcd2a08b2 AutoCert 2018-05-17 14:46:18 -04:00
John Colvin 6483a58351 SyntheticEditorView - adjust RowHeaderWidth to better fit the help texts 2018-05-17 14:39:13 -04:00
John Colvin 69c64b47f1 SPARK-1104 Implement ui to support payup spread param 2018-05-17 14:39:12 -04:00
John Colvin b2fb253969 Refactor PostHedgeOppositeVolumeBasedForceCross 2018-05-17 14:39:12 -04:00
John Colvin 5967a7c1e7 SyntheticEditor - organize cell-param files 2018-05-17 14:39:12 -04:00
Eric Harding b908c576ba SPARK-1116 2018-05-17 14:37:57 -04:00
Eric Harding 56497df68a SPARK-1110 Select first account by default 2018-05-17 14:31:20 -04:00
Kevin Darby b89c561490 spark-1088 fixed SDK for new admin model 2018-05-17 13:40:05 -04:00
Jake Norbut 7794aa89c2 SPARK-1112 2018-05-17 12:08:36 -05:00
Taha Afzal f11215df36 SPARK-1120 and manual tests changed accordingly 2018-05-17 12:38:47 -04:00
Eric Harding 5c2a1fa48e SPARK-1114 - Hide deleted users in UsersService (also don't let them log in) 2018-05-17 12:15:22 -04:00
Eric Harding 29876ec04a SPARK-1097 Can't delete user 2018-05-17 12:15:22 -04:00
Eric Harding 513f7e1310 SPARK-1097 Can't delete account 2018-05-17 12:15:22 -04:00
Eric Harding 867a52024a SPARK-1122 exception when creating new account 2018-05-17 12:15:22 -04:00
Eric Harding 9d6b985400 Admin UI: reduce logging in typescript 2018-05-17 12:15:22 -04:00
Kevin Darby 706b3b151d Pair programming with Jake, spark-1121 2018-05-17 11:58:29 -04:00
John Colvin e1f6c27a94 SPARK-1094 Hedge Ratio should have floating point increments 2018-05-17 11:36:41 -04:00
John Colvin cdaf9e9875 SyntheticEditor - use header_error_text brush 2018-05-17 11:36:41 -04:00
Eric Harding 71993d4a2d SPARK-1106 - Fix up web ui grids a bit 2018-05-17 11:03:55 -04:00
Taha Afzal 84115df405 SPARK-1017 in progress 2018-05-17 11:03:13 -04:00
Jake Norbut 5407279504 OrderBook - should filter for status = working by default; not currently working 2018-05-17 09:30:25 -05:00
Taha Afzal e0b211d002 SPARK-1078 xchange fields length restriction 2018-05-17 07:19:29 -04:00
John Colvin ec6d4936f1 OrderPropertiesEditorVM - fix internal security source 2018-05-16 19:59:03 -04:00
John Colvin 135803b2ce SPARK-1103 fills - move to separate thread 2018-05-16 19:14:40 -04:00
John Colvin 2fb91e8dd9 TimeAndSales - fix icon thread access bug by freezing icon 2018-05-16 18:46:17 -04:00
John Colvin b12fc408b5 SPARK-1102 mview - move to separate thread 2018-05-16 18:44:26 -04:00
Pedro Pinto 7193122a73 Windows screws up X permissions. 2018-05-16 16:44:30 -04:00
Pedro Pinto 1ea8a1c093 Fixed clang build. 2018-05-16 16:43:06 -04:00
Jake Norbut b7200c0581 Merged in hook_badge_style (pull request #21)
SPARK-1082 - badge sytle changes according to settings selection

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-16 20:23:43 +00:00
Taha Afzal 1ab5b65060 SPARK-1111 removal or users and accounts 2018-05-16 16:15:44 -04:00
Jake Norbut ac53734052 PR suggestion 2018-05-16 15:13:38 -05:00
Jake Norbut cdbce6b141 PR suggestion 2018-05-16 15:11:03 -05:00
Pedro Pinto f18235bf96 Ignored src/web when replicating to windows. 2018-05-16 16:01:55 -04:00
Pedro Pinto f8e638d74a Silence is golden. 2018-05-16 16:01:55 -04:00
Pedro Pinto 32dd64fca7 Autocert. 2018-05-16 16:01:55 -04:00
Pedro Pinto 395f686e19 SPARK-1039, Prettier names for default accounts. 2018-05-16 16:01:55 -04:00
Pedro Pinto 62f91d6ebb Guarded against unresolved securities when enforcing limits. 2018-05-16 16:01:55 -04:00
Pedro Pinto 2de2189710 Fixed typo in comment. 2018-05-16 16:01:55 -04:00
Pedro Pinto 51aa3edb17 Fixed typo in log msg. 2018-05-16 16:01:55 -04:00
John Colvin 01fb88a525 Ladder/Ticket - Iceberg orders' "ShowQuantity" is 1 by default 2018-05-16 15:37:57 -04:00
John Colvin cd52dad334 SPARK-1090 New spreader ladders come up without an order type 2018-05-16 15:08:49 -04:00
John Colvin f1a7fdf5cd OrderPropertiesEditorVM - remove unused code 2018-05-16 15:08:49 -04:00
Jake Norbut 96de949ac5 SPARK-1082 - badge sytle changes according to settings selection 2018-05-16 13:55:27 -05:00
Taha Afzal 23a860f7bb fixing broken build 2018-05-16 14:41:08 -04:00
Taha Afzal 2cb539508a merging 2018-05-16 14:31:21 -04:00
Taha Afzal 7059edc0b6 is_active was breaking tests 2018-05-16 14:00:27 -04:00
Eric Harding d417d99357 SPARK-1098 - Create user or account should switch to General tab 2018-05-16 13:59:09 -04:00
Taha Afzal abc400e8c4 SPARK 1046 manual cases for limiter 2018-05-16 13:22:15 -04:00
Eric Harding 52fd21c933 Add robots.txt to spark webserver 2018-05-16 12:54:07 -04:00
Jake Norbut d93e7a9f84 renaming Kevin badge style to Alternate 2018-05-16 11:08:05 -05:00
Taha Afzal bd8b15bbd9 SPARK-1022 completed 2018-05-16 12:03:52 -04:00
Eric Harding 8ac6993fd8 Serve web ui from sparkd/ui for easier production bundle. 2018-05-16 10:52:29 -04:00
Eric Harding b518ae3eca Fix and re-enable web admin build 2018-05-16 10:11:32 -04:00
Taha Afzal 64f19a93ad SPARK-1022 2018-05-15 22:07:06 -04:00
Pedro Pinto dad45f736a Disabled fable build until we figure this out. 2018-05-15 18:16:20 -04:00
Pedro Pinto 2b6a60a79f More fable build fixes, maybe the brokeness is legit. 2018-05-15 18:08:22 -04:00
Pedro Pinto a0306a412d Fixed fable build. 2018-05-15 17:42:55 -04:00
Pedro Pinto afae51012b Handled trace cancelation/correction msgs, fixed SPARK-1105 2018-05-15 17:42:55 -04:00
Eric Harding eaa12d0788 SPARK-592 - Ensure timestamp write is not re-ordered by compiler for multi-cache line depth update 2018-05-15 17:30:59 -04:00
Michael Harding a5c3d5cf09 SPARK-1100: AdminUI 2018-05-15 17:29:42 -04:00
Michael Harding 1d9333724f AdminUI: User account permissions table style 2018-05-15 17:29:42 -04:00
Eric Harding fb48fd82c7 SPARK-592 Market data atomic depth write 2018-05-15 15:28:36 -04:00
Eric Harding bf13115a14 Fix .all file in WebServer 2018-05-15 15:28:36 -04:00
Eric Harding 4bf147eee4 Update market data tests launch.json 2018-05-15 15:28:35 -04:00
John Colvin 39be08432c SPARK-1081 Last Trade Column in ladder is broken 2018-05-15 14:40:19 -04:00
Jake Norbut 0474ea1d53 Ladder - AdornerDecorator surroiunding control is necessary for red border to show errors when LaddersOnThread = true 2018-05-15 13:08:54 -05:00
Jake Norbut 78090ab15c Merged in selector_validation (pull request #20)
SelectoryProperty validation - prevent users with no accounts from sending orders

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-15 17:32:42 +00:00
Jake Norbut 2d37f88553 PR suggestion, thx John! 2018-05-15 12:22:35 -05:00
John Colvin 97447fe634 DataGridFilters - use Esc/Enter KeyBindings and Commit/Clear RoutedCommands 2018-05-15 12:49:27 -04:00
John Colvin e11835d654 SPARK-1087 typing in filter boxes 2018-05-15 12:49:27 -04:00
Taha Afzal 7797cf7435 SPARK-1046 2018-05-15 12:05:29 -04:00
Taha Afzal 71071edd05 SPARK-1022 2018-05-15 12:05:29 -04:00
Taha Afzal 1fb38badfb Temp fix for the Nullable ID vs 0 problem 2018-05-15 12:05:29 -04:00
Taha Afzal bd3399560b SPARK 1022 (more tests, better error messages etc) 2018-05-15 12:05:29 -04:00
Pedro Pinto 1ef5c670bf fable build 2018-05-15 11:58:26 -04:00
Pedro Pinto 095f258edf Disabled broke unit tests. 2018-05-15 11:58:26 -04:00
Pedro Pinto 5da057dc50 Fixed some unused references in Taha's code. 2018-05-15 11:58:26 -04:00
Pedro Pinto 0a84cb0c81 AutoCert 2018-05-15 11:58:26 -04:00
Pedro Pinto d8bc3c757f Autocert 2018-05-15 11:58:26 -04:00
Pedro Pinto 80cbb9869b AutoCert 2018-05-15 11:58:26 -04:00
Jake Norbut 4a517adefb SelectoryProperty validation - prevent users with no accounts from sending orders 2018-05-15 10:11:03 -05:00
John Colvin 18e12f47b8 Merged in ladder-dispatcher-priority (pull request #19)
Ladder dispatcher priority

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-15 14:41:41 +00:00
Michael Harding b1687f92e0 AdminUI: aesthetic improvements 2018-05-15 09:14:40 -04:00
John Colvin a9fb92e1d2 Ladder dispatcher priority - default priority fallback is ApplicationIdle 2018-05-14 20:07:22 -04:00
John Colvin 4781f0ba06 Ladder dispatcher priority - rename 'one_hundred' const to 'sample_cycle_size' 2018-05-14 19:15:11 -04:00
John Colvin 122451c20f Ladder Dispatcher Priority - pr edits 2018-05-14 18:41:53 -04:00
Kevin Darby 08efcf9737 workspaces.txt edited 2018-05-14 22:00:38 +00:00
John Colvin fad111314d LadderTimer typeos - actually call timer.start and update elapsed_total 2018-05-14 17:52:07 -04:00
John Colvin 8c368affd9 Merged master into ladder-dispatcher-priority 2018-05-14 17:19:15 -04:00
Michael Harding 730d43bfd8 AdminUI: Increase Similitude 2018-05-14 16:28:09 -04:00
Michael Harding 13e95023af AdminUI: Misc Tag Mapping Fields 2018-05-14 16:28:09 -04:00
Eric Harding 283a90bc50 WebUI Build: Suppress "first time experience". Thanks .NET 2018-05-14 16:06:16 -04:00
Eric Harding 3d33efd7a7 SPARK-1041 - Obfuscate missing user in failed login message. 2018-05-14 16:06:16 -04:00
Jake Norbut 8bf0ed7536 Order Properties Editor - selected account on gui does not revert back to first item in the list when an account change from the web is received 2018-05-14 15:01:59 -05:00
Jake Norbut 1b2c0e03e0 PnL - use account from order instead of fill 2018-05-14 13:07:40 -05:00
Eric Harding 1a7e9776dd SPARK-1041 Redirect login service to authenticate with AdminService 2018-05-14 13:09:49 -04:00
Eric Harding 61d297293d advance docker pointer 2018-05-14 12:26:21 -04:00
Eric Harding e9aec2aad6 Fix npm install to not use --production because it fails to install build dependencies. Also alias doesn't work 2018-05-14 12:25:45 -04:00
Michael Harding 96d76d26b5 AdminUI: update page title and favicon 2018-05-14 11:36:23 -04:00
Eric Harding c0bc773bbc Advance docker pointer. Maybe it will stay that way. haha 2018-05-14 10:44:26 -04:00
Eric Harding c0cfd6e167 Remove obj files from web build. Take a temporary dependency on mono 2018-05-14 10:44:26 -04:00
Eric Harding b44fec29b1 NPM: add --production flag so it doesn't try to change anything 2018-05-14 10:44:26 -04:00
Michael Harding 425b1a82fd GTS->BTS 2018-05-14 10:04:47 -04:00
Eric Harding 2cdf21455e Merged in jsmap (pull request #18)
Replace "obj" with JSMap type which emulates Map but is backed by a plain JS object

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2018-05-14 13:49:58 +00:00
Michael Harding fd523a645d Fix line endings in docker_update.sh 2018-05-14 09:30:37 -04:00
Eric Harding 32ff893f99 Replace "obj" with JSMap typ which emulates Map but is backed by a plain
JS object
2018-05-14 08:32:19 -04:00
John Colvin 5927be56a2 fix typeo 2018-05-12 17:12:51 -04:00
John Colvin b35cfbd824 add config for Ladder DispatcherTimer 2018-05-11 22:37:44 -04:00
Jake Norbut 3f1d98a613 accounts changed to SelectorProperty 2018-05-11 16:13:01 -05:00
John Colvin e8423ee11e SPARK-1085 Cancel Buys/Cancel Sells button should reflect bid/ask column color 2018-05-11 15:51:20 -04:00
John Colvin 6cca33841a SPARK-1084 Market Break Settings 2018-05-11 15:26:18 -04:00
John Colvin 11468548c2 LadderSettingsView - reduce font cell width (looked too wide for reasonable font sizes) 2018-05-11 14:57:50 -04:00
John Colvin 402abafaca OrderPropertiesEditorVM - use ViewModelBase.SelectorProperty for TimeInForce 2018-05-11 14:40:50 -04:00
Jake Norbut 664a46d438 Changing About Edge to About Spark 2018-05-11 12:11:23 -05:00
Jake Norbut 098fbb1a23 updating p and l test text 2018-05-11 12:11:23 -05:00
Michael Harding 38e32c645d GTS 2018-05-11 12:08:29 -04:00
Kevin Darby 6de4d74d87 audit uploader needs an example file 2018-05-11 10:03:32 -04:00
Michael Harding b27a54e0fb SPARK-1074 2018-05-11 09:43:22 -04:00
Kevin Darby 3040516f84 adding audit uploader for Karl 2018-05-10 17:29:18 -04:00
John Colvin 5262cb4966 Ladder - marketbreak IndexOutOfRange bug 2018-05-10 17:24:16 -04:00
Michael Harding 5a4199bf93 AdminUI: Add Add User button 2018-05-10 17:18:54 -04:00
Michael Harding 4eb2790ee4 SPARK-1077: Saving or changing a user undirties the ui state 2018-05-10 17:18:54 -04:00
Michael Harding 860640cda5 Dont Expect an Obj to be a Map any more 2018-05-10 17:18:54 -04:00
Michael Harding 354c3e04bf AdminUI: Hide User controls if no user is selected 2018-05-10 17:18:54 -04:00
Jake Norbut ac6a5cd88d mysterious control Error when no Error; possibly a WPF validation bug; thx Eric! 2018-05-10 16:10:26 -05:00
Kevin Darby 7384a11070 fix build 2018-05-10 16:09:26 -04:00
Kevin Darby cc4bc755ae placeholder span command 2018-05-10 15:58:06 -04:00
John Colvin a907675a41 SPARK-1070 Disallow creation of synthetics with more than 2 legs 2018-05-10 15:32:54 -04:00
Eric Harding b25121d94a AdminServiceServer should save data to disk when it changes. 2018-05-10 15:10:56 -04:00
John Colvin 70eed209e5 SPARK-1072 MarketView - Qty columns surrounding Bid Ask columns are not sortable 2018-05-10 15:03:20 -04:00
John Colvin 868aa305d1 OrderBadge - FontFamily AffectsRender and updates text 2018-05-10 14:50:32 -04:00
John Colvin 82d59252ec UserSettingsTest 2018-05-10 14:42:12 -04:00
John Colvin aa921a22b7 LadderSettingsView - show badge in Orders column sample cell 2018-05-10 14:42:11 -04:00
Eric Harding a5152fa4cc SPARK-1071 Web UI cannot be accessed from differnet machines. 2018-05-10 13:48:09 -04:00
Pedro Pinto 5208be6cd1 Updated test script. 2018-05-10 12:00:19 -04:00
Pedro Pinto b764726d99 Refactor tests 2018-05-10 11:32:23 -04:00
John Colvin 3170574272 checking in placeholder-settings-test 2018-05-10 10:55:33 -04:00
Taha Afzal 6b557b1f88 disabling limiter until the defaults work properly 2018-05-10 10:28:02 -04:00
Taha Afzal be062cc014 default sec and prod position limits 2018-05-10 10:10:00 -04:00
Taha Afzal f4a2b788ff SPARK-1022 2018-05-10 10:10:00 -04:00
Pedro Pinto b773e801f7 Wasn't calling update on Limiter when new admin available. 2018-05-10 09:45:58 -04:00
Pedro Pinto 7bf099060a Auto cert 2018-05-10 09:45:58 -04:00
Jake Norbut 0120c58ed4 misc refactoring 2018-05-10 08:14:32 -05:00
Jake Norbut c791dd81d6 Merged in more_stops (pull request #17)
SPARK-975 - StopLimits and disallow both price and size changes

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-10 12:44:05 +00:00
John Colvin 141e0a64d4 thicken shell window border to make resizing threaded windows easier 2018-05-10 00:49:17 -04:00
Eric Harding ab57fac1a8 WebUI: workaround for lack of structural equality in Pojo types 2018-05-09 22:31:15 -04:00
Eric Harding 0cda9ec9ae Strip query string from request path for font-awesome 2018-05-09 20:27:08 -04:00
Eric Harding fefa659150 WebUI: Limits remove functionality 2018-05-09 20:04:46 -04:00
John Colvin 3ea77974d4 CME gen open time config and TimeFilter RadioButton item 2018-05-09 20:02:01 -04:00
Eric Harding 70e264e693 Remove typescript generation from build.sh 2018-05-09 18:34:16 -04:00
John Colvin 6f9d0c4897 LadderSettings - fix startup race, remove WindowManagerFactory 2018-05-09 17:54:15 -04:00
John Colvin 198d006e6d fix LadderSettings Equals fn 2018-05-09 17:54:15 -04:00
Eric Harding 0cbe1a6988 WebUI: Add Position limits grids 2018-05-09 17:39:14 -04:00
Michael Harding 6bd8f1d1ac AdminUI: User Page 2018-05-09 17:20:10 -04:00
Michael Harding 7fed123f99 AdminUI: select name conflict 2018-05-09 17:18:35 -04:00
Michael Harding c6e468aba4 AdminUI: Update TableControl 2018-05-09 17:02:57 -04:00
Michael Harding 2d96a7e9c4 AdminUI: Obj functions in Reflection 2018-05-09 17:02:24 -04:00
Michael Harding aa345e2dd7 AdminUI: Add new Fields 2018-05-09 17:00:36 -04:00
Michael Harding 0946c528a9 AdminUI: Add password field 2018-05-09 16:53:11 -04:00
Michael Harding 0f8ce93a21 AdminUI: Add TableControl 2018-05-09 16:53:11 -04:00
Eric Harding 5106f99870 WebUI: drop down selection in react-data-grid 2018-05-09 15:03:58 -04:00
Jake Norbut eaa09435c8 SPARK-975 - StopLimits and disallow both price and size changes 2018-05-09 12:49:32 -05:00
John Colvin 18f77215f0 LadderSettings/FontPicker font-size-ticker resize bug 2018-05-09 13:31:12 -04:00
John Colvin 2b29a6ae17 accidentally negated LadderOnThreads 2018-05-09 12:20:15 -04:00
John Colvin 73544c13a7 remove superfluous comment 2018-05-09 12:17:00 -04:00
John Colvin b1f1fe682f Ladder - automatically adjust row_height to fit font size 2018-05-09 12:15:47 -04:00
John Colvin 4aa8818cb0 define DialogSkirt default style in xaml (no partial class) 2018-05-09 12:15:46 -04:00
Eric Harding 048d8d1882 WebUI: actually don't use toObject. It doesn't work 2018-05-09 11:20:30 -04:00
Eric Harding 60adb8b55b WebUI: Use toObject/fromObject instead of hand written enum conversion 2018-05-09 10:15:50 -04:00
Eric Harding da5ebf27c8 VSCode wants this file changed 2018-05-09 10:02:35 -04:00
Eric Harding 5dfd0ffbdd Web UI: Enums IV reveng of the integer 2018-05-08 22:18:33 -04:00
Eric Harding 12e4e3ad8d Enums - Never more than possibly correct. 2018-05-08 14:54:42 -04:00
Eric Harding 115bc16b12 Javascript enums are my nemesis 2018-05-08 14:40:20 -04:00
Eric Harding 7b2a81c51d WebUI: add fields.enumeration to make a dropdown 2018-05-08 13:14:53 -04:00
John Colvin 75ccf5a1af Organize the random assortment of files in core\utils, and core\Utility 2018-05-08 12:40:08 -04:00
Eric Harding 09bc617859 Web UI: Improve enum generation 2018-05-07 23:23:09 -04:00
Eric Harding f4890e0adc Web UI: Additional generated enum functions 2018-05-07 22:58:52 -04:00
John Colvin 1fed3a0ccf Ladder - fix badge drag lag 2018-05-07 18:54:48 -04:00
Taha Afzal 075fbff042 SPAKR-1022, just need to link Limiter to LimitStore now 2018-05-07 18:19:12 -04:00
Eric Harding 88073c6c4f Fix adminservice users test 2018-05-07 17:42:54 -04:00
Eric Harding 53d32753f1 Treat 0 as empty for create_account (adminserviceserver.cpp) 2018-05-07 17:38:50 -04:00
Michael Harding 10e62d1ce7 Add smp_instruction and max_order_rate_per_connection AdminService.proto 2018-05-07 16:30:26 -04:00
Michael Harding 013153add7 AdminUI: Checkbox on same line 2018-05-07 16:30:26 -04:00
Taha Afzal cb53e1eb31 AdminService create user returns AdminServiceCreateEntityResult 2018-05-07 16:30:10 -04:00
Taha Afzal 1d94edd4b9 AdminService update_user wholesale for Admin UI 2018-05-07 16:09:21 -04:00
Eric Harding 7dcb1e67d9 Web UI: Accounts->Exchange Settings 2018-05-07 15:41:49 -04:00
John Colvin 19cc2c4ed8 GlyphContext - don't use obsolete GlyphRun constructor (get dpi_ratio) 2018-05-07 14:40:12 -04:00
John Colvin 83cb900c1c GlyphContext - return null if typeface does not have key for char (avoid KeyNotFoundException) 2018-05-07 14:34:10 -04:00
John Colvin 340c270e8a GlyphContext - Lazy glyph-char-mapping 2018-05-07 14:20:38 -04:00
Eric Harding 1263b91cfd WebUI: Accounts->ClipLimits 2018-05-07 14:16:37 -04:00
Taha Afzal 4e27cc6140 SPARK 1022 organizing files 2018-05-07 13:46:49 -04:00
Taha Afzal 688e0d650e SPARK 1022 organizing files 2018-05-07 13:42:51 -04:00
John Colvin d591420054 LadderSettings - use Subject instead of event for default settings changes (thread safe) 2018-05-07 12:55:06 -04:00
Eric Harding 8b0dad3d61 Fix admin service tests 2018-05-07 11:55:46 -04:00
Eric Harding f0605f1e96 WebUI: Save/Create accounts 2018-05-07 11:44:59 -04:00
John Colvin be523c1848 LadderSettingsDialog - bind to 'use default' check box (not default-tab-selected) 2018-05-07 10:59:07 -04:00
Taha Afzal 6d721fa186 SPARK 1022 adding account wholesale update function for the webUI 2018-05-07 10:33:58 -04:00
Taha Afzal 905f079757 SPARK 1022 adding limit functions 2018-05-07 10:12:09 -04:00
Pedro Pinto e53c186ea9 Batched new secs in a single call to limiter. 2018-05-07 10:10:36 -04:00
Jake Norbut 3d08755ffe Merged in ladder_validation_no_account (pull request #16)
Enforcing account validation at the start in the case a user does not have any accounts from the new AdminService
2018-05-07 14:03:11 +00:00
Eric Harding d3422010df Web UI: refactor scratch to plain account 2018-05-07 09:56:02 -04:00
Your Name 70a768e475 Update fable assets - part2/2 (linux) 2018-05-07 12:57:51 +00:00
Eric Harding 2503dd414f Updated fable assets - part1 2018-05-07 08:53:41 -04:00
Taha Afzal 30f5a56a8d SPARK 1022 getting there 2018-05-07 08:34:52 -04:00
Taha Afzal 3d42ef609e SPARK 1022 75% done 2018-05-07 07:44:06 -04:00
John Colvin 4ad679b65d fix typeos 2018-05-06 22:16:33 -04:00
John Colvin fc8f55930f change ColorPicker from UserControl to Control (improves perf by reducing baml load) 2018-05-06 22:14:25 -04:00
John Colvin 8d7c7fc742 ladder settings dialog tweaks 2018-05-06 20:34:53 -04:00
Eric Harding 3ffa831a4e Javascript: A Comedy of Errors 2018-05-05 22:17:40 -04:00
Eric Harding 3d6326502d DataGrid example code. Import is still giving me trouble. 2018-05-05 13:58:47 -04:00
Eric Harding 8ec01e5930 Admin UI: Accounts->General 2018-05-05 10:53:19 -04:00
Jake Norbut ebe0c1e6ec PR suggestion 2, thx john! 2018-05-04 16:33:50 -05:00
Eric Harding 8bbfcd7734 Web UI - some utils 2018-05-04 17:30:19 -04:00
Jake Norbut 68bd4b5a73 PR suggestion 2018-05-04 16:21:25 -05:00
Michael Harding 16d02f30c5 AdminUI: Fields Controls 2018-05-04 15:57:20 -04:00
Pedro Pinto 1f0cf2b3f5 SPARK-1025, Native orders are tagged with their native type. 2018-05-04 15:05:22 -04:00
Pedro Pinto 9a3b806d9e Fixed build in latest clang. 2018-05-04 15:05:22 -04:00
Pedro Pinto 5e4e56dab6 Fixed the docker subrepo ptr again, taha just stop it 2018-05-04 15:05:22 -04:00
John Colvin 8925db961b LadderSettingsDialog - adjust open position 2018-05-04 14:36:59 -04:00
John Colvin 4b0c22b2fa LadderSettings - Import/Export menu items 2018-05-04 14:32:38 -04:00
John Colvin ed4d50a2fd x:Shared=False for Import/Export icons 2018-05-04 14:22:01 -04:00
Jake Norbut 18a60d854d Enforcing account validation at the start in the case a user does not have any accounts from the new AdminService 2018-05-04 12:59:34 -05:00
John Colvin 44caaf6b4c LadderSettings - fix LadderOrderVM 'DependencySource thread bug' 2018-05-04 12:16:00 -04:00
Michael Harding bc7f1af5be forcing things to exist in JS 2018-05-04 11:53:45 -04:00
Taha Afzal d7b3f5df7c SPARK-1020 Implement OrderRouter 2018-05-04 11:34:47 -04:00
Taha Afzal 31ba0f22e9 SPARK-1020 Implement OrderRouter 2018-05-04 11:31:47 -04:00
Pedro Pinto c0cfb63d7b Moved docker sub repo pointer forward 2018-05-04 09:21:44 -04:00
Eric Harding 945ea04102 WebUI: Typo in DynamicService.ts 2018-05-04 09:20:49 -04:00
Eric Harding b3bf25f81d Web: Fill in defaults for returned proto types 2018-05-04 09:14:41 -04:00
John Colvin 8c4a7ae0f0 Ladder - add\implement market-break-line settings 2018-05-03 20:54:07 -04:00
John Colvin d739d19a42 Ladder - "Last" column actually uses its column settings' background 2018-05-03 17:34:34 -04:00
Eric Harding 663dd92751 WebUI: add accounts page skeleton 2018-05-03 17:30:13 -04:00
John Colvin a14ec89b4f Ladder - badge only sets fontsize once: in format_text 2018-05-03 17:10:37 -04:00
John Colvin e821548a5d Ladder - order badges actually use settings fontfamily/size/color 2018-05-03 17:01:52 -04:00
Eric Harding 0dc6c16e6b Web: Account for 'null' in generated code 2018-05-03 16:54:05 -04:00
Eric Harding 3bb527247d Web: Add react grid and higher level wrapper 2018-05-03 16:54:05 -04:00
Taha Afzal 2290c955ef SPARK-964 2018-05-03 14:56:03 -04:00
Taha Afzal d28e8a2eab SPARK-1034 (cleaing up OrderRouter.cpp) 2018-05-03 14:56:03 -04:00
Eric Harding b8674fcbb9 Update Fable and add ReactGrid 2018-05-03 14:51:46 -04:00
Eric Harding adcaee1480 Web UI: Remove counter. Add Accounts 2018-05-03 14:51:46 -04:00
Pedro Pinto c748149174 Fixed build. 2018-05-03 14:47:21 -04:00
Pedro Pinto 65fd36e9ae SPARK-1018, Code complete. 2018-05-03 14:39:05 -04:00
Pedro Pinto e2a3055046 Added a const qualifyer to order::definition 2018-05-03 14:39:05 -04:00
Pedro Pinto 2d9a61ca15 SPARK-1201, Integrated Limiter with NativeOrder 2018-05-03 14:39:05 -04:00
Michael Harding 0fe6da7eab is_active -> isActive 2018-05-03 14:32:06 -04:00
Michael Harding 01660153c0 WebAdmin: Using real user Data 2018-05-03 14:32:06 -04:00
Michael Harding 95d0d17bcf fix tools/docker_update.sh 2018-05-03 14:32:06 -04:00
John Colvin 9009678f7c LadderSettingsVM - remove redundant fn call 2018-05-03 14:28:37 -04:00
John Colvin d55c52df62 Ladder - actually use column settings font size 2018-05-03 14:27:36 -04:00
John Colvin 24a128d041 SPARK-1027 Empty ladder 2018-05-03 14:15:25 -04:00
John Colvin cd699d5ad7 LadderTactileAdorner draws GlyphRuns and uses settings font/foreground 2018-05-03 13:49:37 -04:00
John Colvin 51b62ac66f LadderSettingsView - vertically center align some text 2018-05-03 13:31:24 -04:00
John Colvin fed0427cbc accidentally negated the LaddersOnThreads toggle (changed it for testing purposes) 2018-05-03 13:23:18 -04:00
John Colvin fb498adffe ladder - actually use column settings' fontweight 2018-05-03 13:02:57 -04:00
John Colvin eb84ba8371 fix FontPicker (plus a little refactoring) 2018-05-03 13:02:57 -04:00
Jake Norbut ab078bdad3 updating test to check for our favorite bts user 2018-05-03 11:43:32 -05:00
Jake Norbut 6f0da78530 Merged in account_service_hookup (pull request #15)
Account service hookup

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-03 16:32:15 +00:00
Jake Norbut 8a0daed378 merge redux round 2; fixing user in orderbook to look at user and not login 2018-05-03 11:28:59 -05:00
Eric Harding 2636f5dde4 SPARK-958 Improved code gen 2018-05-03 12:24:11 -04:00
John Colvin 3f1aa74bbb Merged in settings-framework (pull request #14)
Settings framework

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-05-03 16:07:14 +00:00
John Colvin af0152b945 PR edits 2018-05-03 11:57:41 -04:00
Jake Norbut a2d83a79ff resolving merge conflict with master 2018-05-03 10:34:42 -05:00
Taha Afzal e0f2b7e2ed SPARK-1034 (is_valid_session) 2018-05-03 11:00:58 -04:00
Taha Afzal cc919a08d0 SPARK-1034 (connections are correctly read from config now) 2018-05-03 11:00:58 -04:00
Michael Harding b515e55378 WebAdmin: Change Title 2018-05-03 10:55:32 -04:00
Jake Norbut c2b8e9c382 removing the bus too since the moodel doesn't publish anythinng to it at the moment 2018-05-03 09:39:26 -05:00
Jake Norbut e553e5fa1e Since we don't have anhy IDisposables yet, removing the interface; also we're only using the IOBservable, so removing the unused Task 2018-05-03 09:32:38 -05:00
Jake Norbut 1aee8d4920 removing empty file 2018-05-03 09:31:26 -05:00
Michael Harding 07629cadcb Admin-UI: use newer node-sass 2018-05-03 10:02:39 -04:00
Michael Harding f3adfc142d Move FontAwsome 2018-05-03 10:02:39 -04:00
John Colvin 554ff8ea6f rename PropertyUpdateLock to PropertyUpdateGate 2018-05-02 22:48:28 -04:00
John Colvin ff66c3767d fix typeo in class name 2018-05-02 22:33:25 -04:00
Eric Harding af2504b9cd SPARK-1035 Make F# generated code more idiomatic 2018-05-02 20:47:43 -04:00
John Colvin 535b901d6f SPARK-969 design and implement a settings framework (ui) 2018-05-02 20:31:24 -04:00
John Colvin e45952ea2c NotConverter goes both ways 2018-05-02 20:31:24 -04:00
John Colvin f85dbc4b5f add new FontPicker 2018-05-02 20:31:24 -04:00
John Colvin 96d59d1885 add new ColorPicker (remove old one) 2018-05-02 20:31:24 -04:00
John Colvin 9df77f7d70 FileDialogCommands to import/export proto objs 2018-05-02 20:31:23 -04:00
John Colvin 7346b99371 SPARK-969 design and implement a settings framework (no ui yet) 2018-05-02 20:31:23 -04:00
Jake Norbut 47f9c837f0 accounts must be under 12 characters 2018-05-02 17:16:15 -05:00
Jake Norbut 3d88d5b2a6 popuulating order_id and account_id on orders 2018-05-02 16:56:11 -05:00
Eric Harding a9dee31c41 Serve admin UI from sparkd root path 2018-05-02 17:55:05 -04:00
Eric Harding 5abd9879fc immutable.pb.js missing extra {} for exception message 2018-05-02 17:46:42 -04:00
Jake Norbut 6fe3d1664d Start of AdminModel 2018-05-02 16:00:18 -05:00
Pedro Pinto 2edd509635 SPARK-1018, Towards limiting order entry. 2018-05-02 15:53:23 -04:00
Pedro Pinto 7340753458 Added additional methods to OrderRouter.
1) Resolve tags by ids rather than string names
2) Add a reverse route map to help recover when order records are lost.
2018-05-02 15:53:23 -04:00
Taha Afzal 330cd2ba6d SPARK 1-34 almost done 2018-05-02 15:25:57 -04:00
Eric Harding f935c7109f Make this part of build output more obvious 2018-05-02 13:39:28 -04:00
Eric Harding 284ed47ddc Line endings. My eternal foe 2018-05-02 11:20:39 -04:00
Eric Harding e5262ed44f externals 2018-05-02 10:55:31 -04:00
Pedro Pinto 8b812735fb Added account id and user id to Order def for Jake to fill in. 2018-05-02 10:54:05 -04:00
Eric Harding 98524ee1e4 SPARK-1028 2018-05-02 10:48:03 -04:00
Pedro Pinto 141aa3b0ba Fixed race condition that was causing ServiceBridge to crash. 2018-05-02 10:08:36 -04:00
Taha Afzal d81d13b40a fixing build 2018-05-02 10:03:45 -04:00
Taha Afzal cc300a7a2d externals 2018-05-02 09:54:51 -04:00
Taha Afzal a852423f4b externals 2018-05-02 09:37:50 -04:00
Taha Afzal c9e875540b SPARK-1020 getting the connections from config 2018-05-02 09:23:43 -04:00
Taha Afzal b020349fe3 SPARK-1018 OrderRouting cti, account etc done, connection name to be done 2018-05-02 09:23:43 -04:00
Eric Harding bf13877750 SPARK-1031 auto-reconnect websocket more than once 2018-05-01 17:41:57 -04:00
Eric Harding 2408c8dcc7 SPARK-1030 Make authentication real 2018-05-01 17:18:05 -04:00
Jake Norbut 7a329522bb Badges - up to 2 badges side by side, qty in 100s 2018-05-01 15:13:17 -05:00
Eric Harding fca0489085 docker container should restart itself on boot 2018-05-01 16:10:58 -04:00
Eric Harding 70aa95ba36 advance docker pointer 2018-05-01 16:10:45 -04:00
Eric Harding d36b2c6dab SPARK-1028 - these files need to be here until the next version of paket 2018-05-01 16:10:38 -04:00
Kevin Darby 32a9a3c425 badge wars 2018-05-01 14:29:57 -04:00
Pedro Pinto bd15bb43fe Removed assertion that a FIX session must exist for inactive orders. 2018-05-01 14:12:16 -04:00
Your Name ff49178dff SPARK-1028 Update web UI build script to produce full bundle on linux. Also +x 2018-05-01 17:59:46 +00:00
Eric Harding d9f1c17053 Add AdminService to Imports.fs. Remove non-generated HelloService definition. 2018-05-01 11:29:28 -04:00
Eric Harding 55dbccfb35 Advance externals pointer (again) 2018-05-01 10:23:54 -04:00
Eric Harding 630a7aef17 SPARK-1000 Complete code generator for .proto->.ts->.js 2018-05-01 09:43:47 -04:00
Taha Afzal eca65db215 SPARK-1013 adding exchange data to boostrap data 2018-04-30 18:19:53 -04:00
Taha Afzal 8214f838b4 accidentally had some test proto's in AdminService 2018-04-30 18:19:53 -04:00
Taha Afzal 270e882c04 hooking admin services to the stream 2018-04-30 18:19:53 -04:00
Taha Afzal e3ca3143be SPARK-1013 moving the service helper to its own place 2018-04-30 18:19:53 -04:00
Kevin Darby 43e68cd43c slightly better badges; I think the multi-badge idea might be inherently flawed 2018-04-30 18:00:19 -04:00
Michael Harding 242784ab3d add docker_update.sh 2018-04-30 17:03:18 -04:00
Eric Harding 71913cfe83 SPARK-1000 Add DynamicService.ts and initial generator template 2018-04-30 16:10:21 -04:00
Eric Harding ec7a4df5ee Remove underscore restiction on qualifier in ApiServerInterop 2018-04-30 15:55:28 -04:00
Pedro Pinto be918fc3b1 Changed how we define the FIX connections used by spark.
All connections present in the JetFIX key will now be used, the
SparkConnection key is deprecated. Actual selection of connection will
be done through the Admin model.
2018-04-30 15:12:41 -04:00
Pedro Pinto a5421dbe2d SPARK-1026, briding Admin Service to Order Manager. 2018-04-30 14:23:44 -04:00
Taha Afzal 450a7a8c86 SPARK-1013 hooking OrderRouter to new model 2018-04-30 07:47:34 -04:00
Taha Afzal 431bed6819 SPARK-1013 bootstrapping with good values 2018-04-28 22:34:37 -04:00
Eric Harding 02cf1fbed9 Add web api build script 2018-04-28 14:50:31 -04:00
Eric Harding 6b3a88905c merge from JS branch 2018-04-27 17:24:52 -04:00
Eric Harding fc5ebd2cb0 SPARK-1000 Add template for typescript generation. 2018-04-27 16:50:53 -04:00
Taha Afzal 703d03d24a db name change 2018-04-27 15:10:57 -04:00
Taha Afzal ed4fdf4284 SPARK-1013 more tests 2018-04-27 14:40:21 -04:00
Pedro Pinto 10252ba7c0 SPARK-1019, Towards proper order routing 2018-04-27 10:28:22 -04:00
Taha Afzal 49e462b54d SPARK-1013 tests, more to follow 2018-04-27 07:40:05 -04:00
Michael Harding 9bd6c53642 WebUI: add initial Users page 2018-04-26 17:04:45 -04:00
Eric Harding 27940b65ee Web UI - simplify left side menu code 2018-04-26 14:18:13 -04:00
Taha Afzal 7db16e87f4 SPARK-1013 service is complete 2018-04-26 14:00:25 -04:00
Eric Harding 9929f38f4d fix typo 2018-04-26 13:44:03 -04:00
Eric Harding ccef457e41 Anon interface is more type safe than createObj 2018-04-26 13:43:22 -04:00
Eric Harding f169991241 Add UglifyJS to webpack config. This reduces release bundle to 620k->150.gz 2018-04-26 13:42:52 -04:00
Taha Afzal 15c8ecc3e6 SPARK-1013 proto name changes 2018-04-26 12:36:11 -04:00
Eric Harding dfbc319926 Call 'HelloService' on the spark server for 'home' as sample 2018-04-26 12:15:18 -04:00
Eric Harding 6541ca2685 Add test for multiple calls to the same service 2018-04-26 12:14:36 -04:00
Taha Afzal a3b94a3cf3 SPARK-1013 user account permissions 2018-04-26 11:44:11 -04:00
Michael Harding 5fbdedfdca Login Classname 2018-04-26 10:50:51 -04:00
Michael Harding 25c793e232 Login is a form again 2018-04-26 10:50:51 -04:00
Michael Harding e141800e60 Login Message 2018-04-26 10:50:51 -04:00
Michael Harding b15f4cebf1 Logout Button 2018-04-26 10:50:51 -04:00
Taha Afzal a444e71f70 SPARK-1013 helper functions on the admin data 2018-04-26 10:29:53 -04:00
Taha Afzal ed1b3aca92 SPARK-1013 helper functions on the admin data 2018-04-26 07:30:48 -04:00
Taha Afzal a8aa62aaaa SPARK-1013 helper functions on the admin data 2018-04-25 20:11:32 -04:00
Taha Afzal 245b48cb0a SPARK-1013 implementing more API functions 2018-04-25 18:54:33 -04:00
Eric Harding 3dc8dd7acb SPARK-958 APIGateway.cpp RPC handler 2018-04-25 18:01:05 -04:00
Jake Norbut ba4c04f55a badge full vertical height 2018-04-25 13:49:58 -05:00
Taha Afzal 799f06247d SPARK-1013 moving admin data functions into its own util class 2018-04-25 12:56:17 -04:00
Taha Afzal e4b155b270 SPARK-1013 (changing namespaces) 2018-04-25 12:56:17 -04:00
Jake Norbut fcffe2ad11 Badges - going to a version with () for seels 2018-04-25 08:13:33 -05:00
Eric Harding ce44f015e5 Remove underscore restiction on qualifier in ApiServerInterop 2018-04-25 09:09:07 -04:00
Eric Harding f3a2525b8d Typescript shared javascript's flawed notion of 'this'. 2018-04-24 22:19:31 -04:00
Eric Harding 783a71217f Fix typescript unit tests... maybe? 2018-04-24 21:22:55 -04:00
Eric Harding 49d16a3e03 Typescript does not let me type 'export' in bts_assert.ts for no discernable reason. 2018-04-24 17:33:16 -04:00
Eric Harding 9f624d54b3 merge 2018-04-24 15:41:27 -04:00
Eric Harding 643cd33762 Web: Fix jest unit tests. Inject WebSocket from test code 2018-04-24 15:39:23 -04:00
Eric Harding cdc0831ca6 Change typescript output path to fix .d.ts is a source file error 2018-04-24 15:38:08 -04:00
Eric Harding 40d808ed7a SPARK-958 encode request to server 2018-04-24 15:38:08 -04:00
Michael Harding 2eb1468781 Eric's login changes 2018-04-24 15:31:52 -04:00
Kevin Darby d223929caa don't need GC thanks to the CompositeDisposable 2018-04-24 13:57:52 -04:00
Jake Norbut c6783383ef Badges - be able to discern order type from the badge at a glace per pedro + kevin 2018-04-24 13:57:52 -04:00
Eric Harding 9b776c52f7 Commit updated cmakedeps.cmake 2018-04-24 13:57:52 -04:00
Kevin Darby 627f228e4e don't need GC thanks to the CompositeDisposable 2018-04-24 13:56:22 -04:00
Michael Harding 720695b613 The Page is dead, long live the Page 2018-04-24 13:24:20 -04:00
Jake Norbut c7e89a8e0a Badges - be able to discern order type from the badge at a glace per pedro + kevin 2018-04-24 11:46:07 -05:00
Kevin Darby 1704abcd47 Merge branch 'prod' of bitbucket.org:bluetradesys/spark into prod 2018-04-24 12:44:35 -04:00
Eric Harding 3ba69bfdbc Commit updated cmakedeps.cmake 2018-04-24 12:44:13 -04:00
Kevin Darby d8ba3e1198 reduce logging for demo 2018-04-24 12:44:11 -04:00
Kevin Darby 2b4ee75393 exposed immutable in sdk and added ToString() => _impl.ToString()
made a slightly better async model in sdk
2018-04-24 12:38:18 -04:00
Taha Afzal eaf6e27c56 SPARK-1013 (moving files) 2018-04-24 12:30:06 -04:00
Taha Afzal d104a6b3b7 SPARK-1013 in progress 2018-04-24 12:05:35 -04:00
Taha Afzal f188869413 SPARK-1013 in progress 2018-04-24 11:35:27 -04:00
Taha Afzal 6c9ec7238d SPARK-1013 in progress 2018-04-24 10:58:56 -04:00
Taha Afzal d2fb9f2b7b SPARK-1013 in progress 2018-04-23 23:21:34 -04:00
John Colvin dad7fe654a Merged master into prod 2018-04-23 20:21:54 -04:00
John Colvin f6522db4f1 SPARK-1014 Timebox "start immediately" needs 500 ms delay 2018-04-23 20:08:08 -04:00
John Colvin 5ca1c87ba8 Merged master into prod 2018-04-23 19:49:41 -04:00
John Colvin c05be5e6a1 synthman - fix resource-created-on-a-different-thread crash 2018-04-23 19:41:08 -04:00
John Colvin 9f4bed9cfe fix synthetic editor popup crash 2018-04-23 19:17:25 -04:00
John Colvin e1ccbfb672 TextUtils - change default typeface to Segoe UI 2018-04-23 17:24:54 -04:00
Kevin Darby ef65163ed8 reduce logging for demo 2018-04-23 17:21:39 -04:00
Pedro Pinto fa290833ce Downgrading exchange sim log messages. 2018-04-23 16:48:17 -04:00
Pedro Pinto 8b75a6fdc3 Some work to facilitate CME certification. 2018-04-23 16:48:17 -04:00
John Colvin 4df93163f7 mview - do not overwrite initial sorting 2018-04-23 16:16:22 -04:00
John Colvin 5867fba95d DIYGrid - initialize sorting properly 2018-04-23 15:48:23 -04:00
Taha Afzal 1f68d1d7a0 SPARK-1013 in progress 2018-04-23 15:33:06 -04:00
Eric Harding 050057e43f Web: Fix jest unit tests. Inject WebSocket from test code 2018-04-23 15:07:14 -04:00
Eric Harding 4a327411d4 Web: More idiomatic use of Cmd.ofPromise 2018-04-23 14:26:37 -04:00
Eric Harding 62bc828e79 Web UI: Handle login response from server 2018-04-23 13:45:38 -04:00
Eric Harding cb9a9bdd92 Web UI: Contact server for login information 2018-04-23 13:04:03 -04:00
Taha Afzal 5ac21f14fb SPARK-1013 in progress 2018-04-23 12:49:57 -04:00
John Colvin 3cc78e6f72 fills - use DIYGrid 2018-04-23 11:57:39 -04:00
Jake Norbut a4091cc47f Merged in stop_orders (pull request #13)
Stop orders

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-04-23 15:21:42 +00:00
Eric Harding e61be98c61 WUI: fix import 2018-04-23 11:03:34 -04:00
John Colvin 9413e89ce5 mview - remove unused xaml #DIYGrid 2018-04-23 10:43:41 -04:00
Taha Afzal a1ba9579fc SPARK-1013 2018-04-23 08:07:52 -04:00
John Colvin 59467becf9 DIYGrid - fix column-reverse-sort bug 2018-04-22 20:40:14 -04:00
John Colvin 1073ff84e9 mview - some cols weren't updating #DIYGrid 2018-04-22 20:39:28 -04:00
John Colvin d819e440dd DIYGrid - render text with GlyphContext 2018-04-22 19:49:41 -04:00
John Colvin d76f465086 DIYGrid - mview commands 2018-04-22 17:07:24 -04:00
John Colvin ca6e11715d DIYGrid - sorting 2018-04-22 12:28:16 -04:00
John Colvin 2c976f0ad1 TimeAndSalesView - add AdornerDecorator #DIYGrid 2018-04-22 12:26:01 -04:00
John Colvin 9e8e10fce8 DIYGridAnimationAdorner - only InvalidateVisual when necessary 2018-04-22 12:26:01 -04:00
Taha Afzal ac5df69c67 user model service API 2018-04-22 10:20:40 -04:00
John Colvin 671ef41a5b Merge branch 'master' into implementing-diy-grid 2018-04-21 17:13:24 -04:00
John Colvin 33b69b09a0 SPARK-979 on the ladder the order type currently selected is not saved across sessions 2018-04-21 16:03:45 -04:00
John Colvin 6e8c1df339 SPARK-935 Adding securities to market view removes sorting 2018-04-21 15:46:09 -04:00
John Colvin 65c873da9a SPARK-1004 Timebox order start time should be 5 minutes in the future, stop 10 minutes in the future 2018-04-21 13:15:43 -04:00
John Colvin 0480405291 SPARK-1006/SPARK-1011 TimeBox params dropdown needs relative time options 2018-04-21 12:26:32 -04:00
Eric Harding 03e317f1c5 Web UI: Enable clicking login button to move to rest of the app. Next step submit to server 2018-04-20 19:59:39 -04:00
Eric Harding 2e4dbd4431 Web UI: Add basic login page 2018-04-20 19:46:54 -04:00
Taha Afzal 9a32a5ec67 user model service API 2018-04-20 19:24:29 -04:00
Jake Norbut 3bbc42941b Merge branch 'master' of bitbucket.org:bluetradesys/spark into stop_orders 2018-04-20 15:34:10 -05:00
Eric Harding 0d9abc047b Web UI - Remove url parsing 2018-04-20 16:29:30 -04:00
Jake Norbut 53d8c12e3f stop order type will no longer present itself as having properties (no < in ladder now); price will also be set just by clicking in ladder 2018-04-20 15:27:00 -05:00
Eric Harding 98e9dd33b8 Javascript websocket connection and login 2018-04-20 16:08:42 -04:00
John Colvin 467858b101 SPARK-1006/SPARK-1010 OrderTicket - definition params dropdown is missing 2018-04-20 14:15:09 -04:00
Michael Harding f37cbbe0ee Add brand_primary and shell_gray to main.sass 2018-04-20 14:13:35 -04:00
Jake Norbut de2b4ae6fd stops: drawing badge at trigger price when filled qty == 0 2018-04-20 12:06:36 -05:00
John Colvin 5e490f7243 SaveWindowFactory 'Restore window' log uses window.diagnostic_name 2018-04-20 12:23:41 -04:00
John Colvin 633b636c57 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-04-20 12:20:23 -04:00
John Colvin 25c9ab908d Merged in window-manager-with-locks (pull request #12)
SPARK-1008 Closing window/shell while windows are opening crashes

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-04-20 16:19:03 +00:00
John Colvin 6e415700c0 SPARK-1008 - pr edit: 'opening window' log msg 2018-04-20 12:18:12 -04:00
John Colvin 58ab793534 SPARK-1008 pr edits 2018-04-20 11:42:15 -04:00
Taha Afzal 5985d1196e adding margin to accounts proto 2018-04-20 10:56:13 -04:00
Michael Harding e5cf7ae712 webpack looks in public dir 2018-04-20 10:23:21 -04:00
Eric Harding 96655304ff Fix module import paths for typescript and jest 2018-04-19 22:25:35 -04:00
Eric Harding 55194f0830 Enable logon though websocket gateway. Generate js protobufs for all services. 2018-04-19 17:02:51 -04:00
Eric Harding c285cbafa8 do not replicate node_modules to linux 2018-04-19 17:01:57 -04:00
Eric Harding ff20d27f41 Add authentication fields to api request header 2018-04-19 17:00:31 -04:00
John Colvin f46c271194 freeze brushes used by ActivityLog 2018-04-19 13:48:28 -04:00
John Colvin aac84e99e2 SPARK-1008 Closing window/shell while windows are opening crashes 2018-04-19 13:05:56 -04:00
John Colvin 2a9c8f5342 Ladder - change assert to log-warning 2018-04-18 16:40:24 -04:00
John Colvin d32e766561 Merged in ladder-with-glyphs (pull request #11)
Ladder - render GlyphRuns instead of FormattedPrices

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-04-18 20:27:25 +00:00
John Colvin 7f5f3b496a Ladder - render column text with clip 2018-04-18 16:13:13 -04:00
John Colvin 6e066e5031 Ladder - render GlyphRuns instead of FormattedPrices 2018-04-18 14:40:47 -04:00
John Colvin 773ac8ffa0 DIYGridAnimationAdorner was not animating top row 2018-04-17 16:51:30 -04:00
Eric Harding a13be3156b Add typescript api unit tests 2018-04-17 15:38:41 -04:00
John Colvin d2123bef10 create DIYGridAnimationAdorner (used by mview) 2018-04-17 13:49:23 -04:00
John Colvin 1a9d340db1 DIYGrid - mview updates properly 2018-04-17 13:49:09 -04:00
Eric Harding f99e6e9188 Add Imports.fs as a single place to declare import definitions 2018-04-17 12:04:01 -04:00
Eric Harding 2c9c0f8563 Add ts2fable to api project as a way to specify the version 2018-04-17 12:04:01 -04:00
Eric Harding 91939df1e6 Add sln file for web ui. Makes vscode happier. 2018-04-17 12:04:00 -04:00
Eric Harding aa2bb376b1 Sample for importing api module into web ui 2018-04-17 12:04:00 -04:00
Taha Afzal c251e589c5 SPARK-965 2018-04-16 22:11:13 -04:00
John Colvin 8ca84522ac DIYGrid - reorganize columns/rows/cells, HighPerformanceRow can handle FrameworkElementColumns 2018-04-16 20:31:00 -04:00
John Colvin 7a18a58f5d DIYGrid - add optimized text/price/size cell views 2018-04-16 20:01:55 -04:00
John Colvin 00d7c5777c DIYGrid - GlyphRun utils 2018-04-16 19:55:07 -04:00
John Colvin 31bc2a8b9b DIYGrid - cache/freeze brushes 2018-04-16 19:53:26 -04:00
John Colvin 756ffdcea8 DIYGrid - bind column filter properties 2018-04-16 19:38:03 -04:00
John Colvin e10b5e21a5 DIYGrid - fix drag selection 2018-04-16 19:36:56 -04:00
Jake Norbut 999d28c906 SPARK-949 - Sell button hover effect on order ticket now matches ladder 2018-04-16 15:11:00 -05:00
Kevin Darby 69e0968c35 orders sdk 2018-04-16 15:41:42 -04:00
Eric Harding 71c4214cb3 Add fable template src/web/ui 2018-04-16 13:40:22 -04:00
Kevin Darby 786689ecfc more sdk work 2018-04-16 13:20:03 -04:00
Kevin Darby 45e8e8a9b7 added streaming interfaces for accounts and users; Taha can now maybe use these services to serve his types 2018-04-16 13:20:03 -04:00
Eric Harding 2116149a30 Add web/api project to build api web core layer. 2018-04-16 11:28:22 -04:00
John Colvin 674ee6144e Merged in SPARK-1003-multiple-ladders (pull request #10)
SPARK-1003 Market view ladder button only opens 1 ladder despite multiple selected

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-04-16 14:20:17 +00:00
Kevin Darby b5006ff520 add make_ to stateful publisher per Eric 2018-04-16 09:25:20 -04:00
Taha Afzal a6e7d77d1d SPARK-965 2018-04-15 16:44:26 -04:00
Pedro Pinto 46e80e277a SPARK-1007, and just like that sim supports market orders. 2018-04-13 18:06:29 -04:00
Pedro Pinto 194b947e1b Added code to facilitate autocert. 2018-04-13 18:06:05 -04:00
John Colvin e34e3194df Merged master into implementing-diy-grid 2018-04-13 16:17:54 -04:00
John Colvin 9aeab69581 add window_type property to IShellWindow 2018-04-13 15:36:58 -04:00
John Colvin d7186b7052 LadderWindowFactory accidentally lost an ObserveOnDispatcher() 2018-04-13 14:34:49 -04:00
John Colvin 3c51b091b9 SPARK-1003 Market view ladder button only opens 1 ladder despite multiple selected 2018-04-13 14:23:23 -04:00
Eric Harding 5e86462676 Advance the security keys by # replay securities to avoid screwy synthetics 2018-04-13 14:05:18 -04:00
Taha Afzal 36103452da Spark-965 2018-04-13 11:47:36 -04:00
John Colvin fa2f08ba75 remove unused/deprecated extensions from Update.cs 2018-04-13 11:40:10 -04:00
John Colvin 7251abdd60 SPARK-1002 market break {value} is unacceptable for market range assert 2018-04-12 19:18:44 -04:00
John Colvin 80d87c4139 fix typeo in lastordefault extension 2018-04-12 19:11:57 -04:00
John Colvin 7eace1a892 fix FillTuple observable in OrdersModel 2018-04-12 18:05:14 -04:00
Kevin Darby 44684e9377 little cleanup 2018-04-12 16:15:26 -04:00
Pedro Pinto fe7bf108f3 SPARK-994, We are more economical in how we represent the sim book. 2018-04-12 13:25:24 -04:00
Pedro Pinto 16bf0a7236 Changed how we reply to logon in Jetfix, trying to pacify Autocert. 2018-04-12 11:56:17 -04:00
Kevin Darby 48491d6b90 made some changes in SDK but it's still clumsy 2018-04-11 18:41:48 -04:00
Eric Harding 4d3a0375aa Add websocket session tracking 2018-04-11 18:11:50 -04:00
Eric Harding f50118fb45 SPARK-999 C++ http server which can serve config. No python 2018-04-11 14:57:27 -04:00
Kevin Darby 03773e52ca orders in SDK 2018-04-11 11:47:13 -04:00
John Colvin 001d1aa5b4 SparkWindowView - change menu-expander-button width to 5, grid.column to auto 2018-04-11 11:27:08 -04:00
Eric Harding 87df2c4d6d add websocketpp to externals 2018-04-10 17:37:44 -04:00
Eric Harding 59321c0e46 SPARK-975 - "found duplicate entry for platform_id"
CME AutoCert puts the same 0GLB future on 2 channels which confuses the
security db.  There isn't really a fix for this other than to remember
the channel the security was originally found on which is kind of a
pain. Since this has never happened in production I think it might be
AutoCert only behavior.  Just in case I'm putting in an emergency
workaround to just not load the database.  This should allow us to keep
a production customer running while we put in a real fix.
2018-04-10 13:02:38 -04:00
Eric Harding 5ee027020b Merged in ImmutableChange (pull request #9)
Fixes the problem where Immutable wrapper does not discern a null field on the underlying proto

Approved-by: John Colvin <john.colvin@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-04-10 13:33:54 +00:00
Eric Harding 4c81f3bb1e Fix from code review feedback. 2018-04-10 09:27:11 -04:00
John Colvin 83dd20dd55 Ladder Perf - allow row count to increase as-needed up to a hard-coded max 2018-04-09 18:47:31 -04:00
John Colvin f59a2f6f48 Ladder Perf - increase price_out_of_range_render_interval to 5 secs 2018-04-09 18:47:31 -04:00
John Colvin 8388768edc add/implement LaddersSpawnMany to toggles.proto, defaults to false 2018-04-09 18:47:30 -04:00
John Colvin 38e8c9a0c8 LaddersOnThreads, in toggles.proto, defaults to true 2018-04-09 18:47:30 -04:00
Eric Harding 03bf7212a0 Add some checks. Try to remove null checks for non-nullable fields 2018-04-09 16:11:57 -04:00
Eric Harding 84dca4a714 Make Message fields in Immutable protos nullable 1/2 2018-04-09 15:59:04 -04:00
Pedro Pinto c14ffe900a Drop tags 95, 96 if securelogon is on. Changed application to spark 2018-04-09 15:46:42 -04:00
Jake Norbut 4428e74d10 PnL - PnL is now a ISharedWindow 2018-04-09 14:28:45 -05:00
Jake Norbut 386b5f9b6b OrerBook - OrderBook is now an ISharedWindow (ISlowWindow) 2018-04-09 12:33:04 -05:00
Jake Norbut 2394f0abc4 PnL - Summary in status bar now ignores NaN pnl [this should never occur in practice with a waterfall pnl type] 2018-04-09 11:22:25 -05:00
Jake Norbut 4a4a30b0fd Orderbook - don't crash when filtering an OrderBook with orphans 2018-04-09 10:59:08 -05:00
John Colvin d530108854 DIYGrid - HyperlinkColumn and CellAdorner 2018-04-08 21:21:33 -04:00
John Colvin 5836855636 DIYGrid - mouse-drag selection 2018-04-08 18:16:43 -04:00
John Colvin 6ecbe8c951 Ladder Perf - cache/reuse size-FormattedTexts 2018-04-07 20:45:34 -04:00
John Colvin 50a6cc3223 Ladder Perf - in LadderRender, use const top_margin 2018-04-07 18:59:56 -04:00
John Colvin 3caee644ab Ladder Perf - only render grid lines once (nothin' like low hanging fruit) 2018-04-07 18:39:15 -04:00
John Colvin 021f79ff8c Merged master into implementing-diy-grid 2018-04-07 17:24:50 -04:00
John Colvin 419de81457 DIYGrid.ColumnBase - remove unused function 2018-04-07 17:23:21 -04:00
John Colvin fe8cb76d52 MarketView/TimeAndSales default column widths #DIYGrid 2018-04-07 17:14:26 -04:00
John Colvin f52516b49f DIYGrid - _frozen_column_count is 0 by default 2018-04-07 16:35:59 -04:00
John Colvin 70e66927a6 DIYGrid - fix cell clipping, simplify row/cell render 2018-04-07 16:33:01 -04:00
Kevin Darby 18a0ed75c4 sdk refactor, much cleaner now 2018-04-06 18:23:32 -04:00
Pedro Pinto c62490d410 Moved docker ptr 2018-04-06 19:34:40 +00:00
Pedro Pinto 3602fd479b Made secure logon work a bit more portable 2018-04-06 19:29:35 +00:00
Kevin Darby 335d590106 Merge branch 'master' of bitbucket.org:bluetradesys/spark
# Conflicts:
#	src/lib/sparkproto/SparkProtos.proto
#	src/server/sparkd/CMakeLists.txt
#	src/server/sparkd/order_man/order_man_utils.hgit
2018-04-06 15:29:27 -04:00
Kevin Darby a9d8a3929e fix build 2018-04-06 15:16:58 -04:00
Kevin Darby b814a23a57 spark_sdk coming along 2018-04-06 15:02:48 -04:00
Kevin Darby d6aeda8cfd sdk 2018-04-06 13:10:47 -04:00
Kevin Darby c95b402683 fixed help message 2018-04-06 13:10:47 -04:00
Kevin Darby ebecef31f4 spark_sdk md works 2018-04-06 13:10:47 -04:00
Kevin Darby cc815c0778 maybe eric changed this 2018-04-06 13:10:23 -04:00
Eric Harding 6a5cde8a33 advance docker pointer 2018-04-06 11:39:23 -04:00
Jake Norbut d58c1b5fca PnL - right align status bar, visual polishing 2018-04-05 16:33:35 -05:00
Eric Harding dd729f815d SPARK-985 Implement RFQ 2018-04-05 16:03:15 -04:00
Jake Norbut 49f3ad6304 SPARK-941 - added some totals in a new status bar; will ultimately put a swipe sum in here 2018-04-05 14:55:52 -05:00
Eric Harding 7c20402bd5 advance linux docker pointer 2018-04-05 15:16:53 -04:00
Eric Harding 09f96d4cce Backport mdp3main changes from edge 2018-04-05 15:16:53 -04:00
John Colvin 04d0e82ea8 DIYGrid - row selection 2018-04-05 14:48:08 -04:00
John Colvin d855a7025e DIYGrid - invalidate_column_layout in OnRenderSizeChanged 2018-04-05 13:40:33 -04:00
John Colvin bdb7dfb8c6 DIYGrid - handle null columns 2018-04-05 13:39:58 -04:00
Pedro Pinto a64001a333 SPARK-971, Fixed a bug in secure logon implementation. 2018-04-05 13:36:57 -04:00
Pedro Pinto 07a406142a SPARK-971, Secure logon for spark. 2018-04-05 12:34:34 -04:00
Pedro Pinto 7523e812ea Moved container pointer 2018-04-05 12:33:32 -04:00
Eric Harding a008bac794 SPARK-959 SecureLogon - only consider non-expired keys 2018-04-05 10:58:12 -04:00
John Colvin 904cea0389 DIYGrid - row selection color 2018-04-05 10:46:34 -04:00
Eric Harding 005073060a Marketdata.conf should not have 'Regular' (every packet) onixs logging enabled. 2018-04-05 09:13:50 -04:00
John Colvin eafd648a19 DIYGrid.HighPerformance - row_height assert msg typeo 2018-04-04 17:27:14 -04:00
John Colvin 964ad04e94 DIYGrid.RowBase - protect constructor 2018-04-04 17:24:41 -04:00
John Colvin c082f181c6 DIYGrid.RowBase - clear previous Height Binding on rowLoaded 2018-04-04 17:24:21 -04:00
John Colvin 747cc872ea DIYGrid.HighPerformanceRow - row_height is readonly 2018-04-04 17:23:37 -04:00
John Colvin ab00a4dc98 DIYGrid.HighPerformanceRow - initialize row_height 2018-04-04 16:15:58 -04:00
John Colvin 8045be92bf DIYGrid.RowBase - discourage changes disclaimer 2018-04-04 16:10:20 -04:00
John Colvin e6ff41428f DIYGrid.HighPerformanceRow - remove hardcoded row height 2018-04-04 16:09:10 -04:00
John Colvin f6bc4c953a DIYGrid - fix 'ColumnHeight' misnomer 2018-04-04 13:49:48 -04:00
Jake Norbut a449ff23a4 Ladder Badge - the Taming of the Badge; badge borders stay within ladder levels now 2018-04-04 12:20:20 -05:00
John Colvin 8e5db104a2 DIYGrid DataContext can be null 2018-04-04 13:17:45 -04:00
John Colvin dc4d3a9ea3 TimeAndSales - fix 'zero-width' rows on startup bug #DIYGrid 2018-04-04 13:17:45 -04:00
John Colvin a8ffb45529 DIYGrid - add column filters 2018-04-04 13:17:44 -04:00
Jake Norbut 2532c2b2a1 SPARK-925 - updating proto defaults for new Order column size; updating total window width to accomodate for larger order column 2018-04-04 11:20:11 -05:00
Jake Norbut 3897dbc6e5 PnL - no need to update pnl when it was recnetly updated w/i some threshold by a position update 2018-04-04 11:20:11 -05:00
Eric Harding d1daf73086 SPARK-959 Implement functions for CME secure logon. 2018-04-04 11:28:21 -04:00
Pedro Pinto 4a02df82a4 Moved externals pointer and referenced cryptopp 2018-04-04 15:13:05 +00:00
John Colvin 3b73cb6e99 TimeAndSales - set background to 'CommonBackground' 2018-04-04 11:09:02 -04:00
John Colvin c891c3dd30 DIYGrid - mview and time&sales use new HighPerformanceRow 2018-04-04 11:09:02 -04:00
Eric Harding f318dffdeb Revert "SPARK-959 Implement functions for CME "secure" logon."
This reverts commit ac2fa9b7bc.
2018-04-04 11:04:59 -04:00
John Colvin 9bc7edb246 Merged master into implementing-diy-grid 2018-04-04 11:02:42 -04:00
Eric Harding ac2fa9b7bc SPARK-959 Implement functions for CME "secure" logon. 2018-04-04 11:01:12 -04:00
Jake Norbut 9abf59170a PnL - specifying a default sort and minwidth so that window is resized appropriately on launch 2018-04-04 09:45:24 -05:00
Pedro Pinto fec3c68958 Claned up SparkProtos a little. 2018-04-04 10:17:58 -04:00
Pedro Pinto 90fd7bfd1b SPARK-503, Aligned simulator and jf sessions so we don't crash 2018-04-04 08:56:52 -04:00
Jake Norbut 9544b69047 SPARK-925 - at least in part; made order badge wider by 10 pixels 2018-04-03 14:20:41 -05:00
Pedro Pinto 9beb8e0035 SPARK-974, Partial handling of trade correction messages. 2018-04-03 11:42:04 -04:00
Jake Norbut 27558a3dca OrdersModel - Adding .Replay(1).RefCount() to the subscribei; cancel selected wasn't working from the OrderBook prior to this because the subscription appeared to be getting immediately disposed 2018-04-03 09:39:15 -05:00
Eric Harding 8852671151 Touch CMakeLists.txt before server build. 2018-04-03 10:30:37 -04:00
Jake Norbut 19b9eb69f1 SPARK-918 - No longer hiding high/open/etc for synthetics in ladder 2018-04-02 16:13:53 -05:00
Jake Norbut b5869e348c SPARK-689 - adding Settle price to ladder info area 2018-04-02 16:03:27 -05:00
Jake Norbut 2ed0a14c29 SPARK-519 - smarter MinWidth size for OrderBook 2018-04-02 13:27:21 -05:00
Eric Harding be751c0443 Remove .conf reference to 'UseFakeOrderLogic' 2018-04-02 13:54:10 -04:00
Pedro Pinto 557e539fad SPARK-678, 0 sized stream updates were being swallowed by the API. 2018-04-02 13:52:36 -04:00
Pedro Pinto 057e41342b SPARK-923, Qty and price no longer updated in completed orders. 2018-04-02 12:22:33 -04:00
Jake Norbut 96f35493a4 Windows positioning - removing temporary code 2018-04-02 09:11:02 -05:00
Jake Norbut 2f65e3d257 SPARK-686 - window positioning code now moves the window if it overlaps with the taskbar 2018-04-02 09:04:00 -05:00
Pedro Pinto dcffc8b2db SPARK-692, We no longer risk missing order state transitions on boot. 2018-04-02 10:01:58 -04:00
Eric Harding 04873b5847 SPARK-671 toggle for ladders on threads for easier debugging 2018-04-02 09:20:34 -04:00
Eric Harding 076e81c529 SPARK-671 Switch ladder to multi thread mode 2018-04-02 09:07:06 -04:00
Pedro Pinto 1dc4293eb5 Fixed build. 2018-04-02 08:55:11 -04:00
Pedro Pinto 01d1753620 Changed Support to support, for consistency with other subsystems. 2018-04-02 08:52:47 -04:00
Pedro Pinto 669da60852 Added logging for incoming order API calls. 2018-03-29 21:54:56 +00:00
Eric Harding 7e46267e43 fix build 2018-03-29 17:18:34 -04:00
Eric Harding fde007bf30 missing proto changes from previous checkin 2018-03-29 15:21:03 -04:00
Eric Harding 1fdaf6933d Backport some mdp3 changes from edge. 2018-03-29 15:17:12 -04:00
Eric Harding 522ba48c85 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-03-29 13:38:33 -04:00
Eric Harding 70fcc09b02 Readability improvement in WPFPanel 2018-03-29 13:38:05 -04:00
Eric Harding bfd72eb2b2 SPARK-671 make ActivityLogWindow run on its own thread. 2018-03-29 13:37:48 -04:00
Eric Harding 0e1352e99b Probably want to keep this at 300 2018-03-29 11:37:52 -04:00
Eric Harding feeb3348bf SPARK-977 larger ladder recenter control 2018-03-29 11:30:35 -04:00
John Colvin 7c8da47f6e DIYGrid style changes 2018-03-28 14:07:41 -04:00
John Colvin 890143c806 Merged master into implementing-diy-grid 2018-03-28 13:58:38 -04:00
John Colvin 51b8603fbb fix cell total width calculation 2018-03-25 15:10:18 -04:00
John Colvin 2934ed642d start the conversation 2018-03-25 14:59:35 -04:00
665 changed files with 46361 additions and 11169 deletions

No files matched your search

+5
View File
@@ -9,6 +9,8 @@ cmake-build-debug/
.vs
obj/
*.csproj.user
*.vspx
*.psess
msbuild.log
*.orig
vcxproj.user
@@ -16,3 +18,6 @@ src/client/spark/Properties/launchSettings.json
compile_commands.json
.cquery_cached_index/
src/client/sparkcmd/Properties/launchSettings.json
node_modules
paket-files
project.lock.json
+101 -95
View File
@@ -1,96 +1,102 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
],
"defines": [],
"intelliSenseMode": "clang-x64",
"browse": {
"path": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
}
},
{
"name": "Windows",
"intelliSenseMode": "clang-x64",
"includePath": [
"${workspaceRoot}/src",
"${workspaceRoot}/build/src",
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
"${workspaceRoot}/externals/windows/vscode/usr/include"
],
"defines": [
"__linux__",
"__x86_64__"
],
"browse": {
"path": [
"${workspaceRoot}/src",
"${workspaceRoot}/build/src",
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
"${workspaceRoot}/externals/windows/vscode/usr/include/*"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
}
},
{
"name": "WindowsQuick",
"intelliSenseMode": "clang-x64",
"includePath": [
"${workspaceRoot}/src",
"${workspaceRoot}/build/src",
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
"${workspaceRoot}/externals/windows/vscode/usr/include"
],
"defines": [
"__linux__",
"__x86_64__"
],
"browse": {
"path": [
"${workspaceRoot}/src"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": "${workspaceRoot}/.vscode/browse.vc.db"
}
}
],
"version": 3
{
"configurations": [
{
"name": "Linux",
"includePath": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
],
"defines": [],
"intelliSenseMode": "clang-x64",
"browse": {
"path": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"cStandard": "c11",
"cppStandard": "c++17"
},
{
"name": "Windows",
"intelliSenseMode": "clang-x64",
"includePath": [
"${workspaceRoot}/src",
"${workspaceRoot}/build/src",
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
"${workspaceRoot}/externals/windows/vscode/usr/include"
],
"defines": [
"__linux__",
"__x86_64__"
],
"browse": {
"path": [
"${workspaceRoot}/src",
"${workspaceRoot}/build/src",
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
"${workspaceRoot}/externals/windows/vscode/usr/include/*"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"cStandard": "c11",
"cppStandard": "c++17"
},
{
"name": "WindowsQuick",
"intelliSenseMode": "clang-x64",
"includePath": [
"${workspaceRoot}/src",
"${workspaceRoot}/build/src",
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
"${workspaceRoot}/externals/windows/vscode/usr/include"
],
"defines": [
"__linux__",
"__x86_64__"
],
"browse": {
"path": [
"${workspaceRoot}/src"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": "${workspaceRoot}/.vscode/browse.vc.db"
},
"cStandard": "c11",
"cppStandard": "c++17"
}
],
"version": 4
}
+77 -1
View File
@@ -1,6 +1,7 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
@@ -31,6 +32,17 @@
"externalConsole": true,
"requireExactSource": false
},
{
"name": "SDK Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
"args": ["md","GLBX","Future","ES","ESM8"],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Attach",
"type": "clr",
@@ -106,12 +118,76 @@
"/home/bts/spark/": "${workspaceRoot}"
}
},
{
"name": "Admin Service Tests",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test", "[admin_service]"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"preLaunchTask": "",
"logging": {
"engineLogging": false
},
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "enable pretty-printer",
"ignoreFailures": true
}
],
"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": "Limiter Tests",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test", "[limiter]"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"preLaunchTask": "",
"logging": {
"engineLogging": false
},
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "enable pretty-printer",
"ignoreFailures": true
}
],
"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": "MD Tests",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test", "-a", "[synthetic]"],
"args": ["test", "[md]"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
+2 -2
View File
@@ -28,6 +28,7 @@ if (WIN32)
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} /MT")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF /LTCG")
add_subdirectory(src/lib)
else ()
@@ -36,6 +37,5 @@ else ()
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG -O3")
add_subdirectory(src/lib)
add_subdirectory(src/server/sparkd)
add_subdirectory(src/web)
endif()
+63 -38
View File
@@ -1,10 +1,10 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spark_lib", "build\src\lib\spark_lib.vcxproj", "{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spark_lib", "build\src\lib\spark_lib.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
@@ -12,12 +12,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sparkcmd", "src\client\spar
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{2256015B-E7D5-3614-8F39-814CA68A4444}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
ProjectSection(ProjectDependencies) = postProject
{502FD050-679A-3B50-B438-098500700919} = {502FD050-679A-3B50-B438-098500700919}
EndProjectSection
EndProject
Global
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
@@ -53,22 +55,22 @@ Global
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.Build.0 = Release|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Debug|Any CPU.ActiveCfg = Debug|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Debug|x64.ActiveCfg = Debug|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Debug|x64.Build.0 = Debug|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Debug|x86.ActiveCfg = Debug|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Release|Any CPU.ActiveCfg = Release|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Release|x64.ActiveCfg = Release|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Release|x64.Build.0 = Release|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Release|x86.ActiveCfg = Release|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{502FD050-679A-3B50-B438-098500700919}.Debug|Any CPU.ActiveCfg = Debug|x64
{502FD050-679A-3B50-B438-098500700919}.Debug|x64.ActiveCfg = Debug|x64
{502FD050-679A-3B50-B438-098500700919}.Debug|x64.Build.0 = Debug|x64
{502FD050-679A-3B50-B438-098500700919}.Debug|x86.ActiveCfg = Debug|x64
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{502FD050-679A-3B50-B438-098500700919}.Release|Any CPU.ActiveCfg = Release|x64
{502FD050-679A-3B50-B438-098500700919}.Release|x64.ActiveCfg = Release|x64
{502FD050-679A-3B50-B438-098500700919}.Release|x64.Build.0 = Release|x64
{502FD050-679A-3B50-B438-098500700919}.Release|x86.ActiveCfg = Release|x64
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|Any CPU.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.Build.0 = Debug|x64
@@ -129,22 +131,42 @@ Global
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.Build.0 = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|Any CPU.ActiveCfg = Debug|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x64.ActiveCfg = Debug|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x64.Build.0 = Debug|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x86.ActiveCfg = Debug|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|Any CPU.ActiveCfg = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x64.ActiveCfg = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x64.Build.0 = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x86.ActiveCfg = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|Any CPU.ActiveCfg = Debug|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x64.ActiveCfg = Debug|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x64.Build.0 = Debug|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x86.ActiveCfg = Debug|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|Any CPU.ActiveCfg = Release|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x64.ActiveCfg = Release|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x64.Build.0 = Release|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x86.ActiveCfg = Release|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|Any CPU.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.Build.0 = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x86.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|Any CPU.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x64.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x64.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x86.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x86.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|Any CPU.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x64.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x64.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x86.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x64.Build.0 = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -152,4 +174,7 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {422D1620-0166-4C26-9A81-CA8A2EB1AB58}
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal
+30
View File
@@ -0,0 +1,30 @@
using bts.api;
using bts.api.admin;
using bts.api.core;
using bts.spark;
using mbts.spark.utilities;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
namespace mbts.spark {
[Export]
class AdminModel {
readonly AdminService _service;
[ImportingConstructor]
private AdminModel(AdminService service) {
_service = service;
on_admin_data = _service.subscribei_admin_data().Table().Replay(1).RefCount();
}
public IObservable<TableUpdate<AdminDataImmutable, int>> on_admin_data { get; }
}
}
@@ -57,7 +57,8 @@ namespace bts.shell {
}
void _show_modify_order(uint order_key) {
if (_orders.state.TryGetValue(order_key, out OrderRecordImmutable ori) && _md_map.TryGetValue(ori.Def.SecurityKey, out SecurityMD smd))
if (_orders.state.TryGetValue(order_key, out OrderRecordImmutable ori) && _md_map.TryGetValue(ori.Def.SecurityKey, out SecurityMD smd)
&& (ori.HasDef && ori.Def.HasCustomDef && !(ori.Def.CustomDef.HasStop || ori.Def.CustomDef.HasStopLimit)))
_show_order_window(new OrderModificationDialog {
DataContext = new ExistingOrderTicketVM(_model,
Observable.Return(new OrderRecordTuple(ori, smd)),
@@ -11,6 +11,7 @@ using bts.support;
using bts.api.orderman;
using bts.api.synthetic;
using bts.api.diagnostic;
using bts.api.admin;
namespace Spark
{
@@ -34,6 +35,9 @@ namespace Spark
[Export]
public OrderService order_service => context.locate<OrderService>();
[Export]
public AdminService admin_service => context.locate<AdminService>();
[Export]
public SyntheticSecurityService synthetic_security_service => context.locate<SyntheticSecurityService>();
+38
View File
@@ -0,0 +1,38 @@
using mbts.ui.shell;
using System.Windows.Input;
using System.Windows.Media;
namespace mbts.ui {
interface IMenuItemVM {
string name { get; }
string group { get; }
ImageSource icon { get; }
ICommand command { get; }
object parameter { get; }
}
class MenuItemVM : ViewModelBase, IMenuItemVM {
object _parameter;
public MenuItemVM(string name, ICommand command, ImageSource icon = null, string group = null, object parameter = null) {
this.name = name;
this.group = group;
this.command = command;
this.icon = icon;
_parameter = parameter;
}
public string name { get; }
public string group { get; }
public ImageSource icon { get; }
public ICommand command { get; }
public object parameter {
get => _parameter;
set {
_parameter = value;
command.TryRaiseCanExecuteChanged();
RaisePropertyChanged();
}
}
}
}
+4 -2
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -42,7 +42,6 @@ namespace mbts.ui
public static ImageSource DataCopy { get { return load_image("DataCopy.ico"); } }
public static ImageSource Import { get { return load_image("Import.ico"); } }
public static ImageSource Refresh { get { return load_image("Refresh.png"); } }
public static ImageSource FavoriteWindow { get { return load_image("FavoriteWindow.png"); } }
//public static ImageSource Ticket { get { return load_image("Ticket.png"); } } // replaced with AddOrders
@@ -98,14 +97,17 @@ namespace mbts.ui
public static ImageSource Clock { get { return (DrawingImage)_resources["Clock"]; } }
public static ImageSource Defrag { get { return (DrawingImage)_resources["Defrag"]; } }
public static ImageSource Display { get { return (DrawingImage)_resources["Display"]; } }
public static ImageSource Export => (DrawingImage)_resources["Export"];
public static ImageSource Filter { get { return (DrawingImage)_resources["Filter"]; } }
public static ImageSource FilterActive { get { return (DrawingImage)_resources["FilterActive"]; } }
public static ImageSource FilterError { get { return (DrawingImage)_resources["FilterError"]; } }
public static ImageSource Import => (DrawingImage)_resources["Import"];
public static ImageSource Knight { get { return (DrawingImage)_resources["Knight"]; } }
public static ImageSource Payment { get { return (DrawingImage)_resources["Payment"]; } }
public static ImageSource Spawn { get { return (DrawingImage)_resources["Spawn"]; } }
public static ImageSource Target { get { return (DrawingImage)_resources["Target"]; } }
public static ImageSource Tetris { get { return (DrawingImage)_resources["Tetris"]; } }
public static ImageSource ViewSettings => (DrawingImage)_resources["ViewSettings"];
public static ImageSource Wrench { get { return (DrawingImage)_resources["Wrench"]; } }
public static ImageSource Support => (DrawingImage)_resources["SupportIcon"];
+46 -7
View File
@@ -319,7 +319,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="Clock">
<DrawingImage x:Key="Clock" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
@@ -608,7 +608,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="CancelSelected">
<DrawingImage.Drawing>
<DrawingGroup>
@@ -668,7 +668,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage.Drawing>
</DrawingImage-->
<DrawingImage x:Key="Synthetics">
<DrawingImage x:Key="Synthetics" x:Shared="False">
<!-- Spreads icon -->
<DrawingImage.Drawing>
<DrawingGroup>
@@ -681,7 +681,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="AddSynthetic">
<DrawingImage x:Key="AddSynthetic" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
@@ -693,7 +693,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="RemoveSynthetic">
<DrawingImage x:Key="RemoveSynthetic" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
@@ -706,7 +706,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage>
<DrawingImage x:Key="ModifySynthetic">
<DrawingImage x:Key="ModifySynthetic" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
@@ -718,7 +718,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="CloneSynthetic">
<DrawingImage x:Key="CloneSynthetic" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
@@ -730,6 +730,45 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="ViewSettings" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Geometry="M22.889013,19.178117C23.365026,19.178117 23.75202,19.56508 23.75202,20.040055 23.75202,20.516007 23.365026,20.904069 22.889013,20.904069 22.413031,20.904069 22.02503,20.516007 22.02503,20.040055 22.02503,19.56508 22.413031,19.178117 22.889013,19.178117z M22.889013,15.528093C20.40003,15.528092 18.375006,17.552018 18.375006,20.040055 18.375006,22.529069 20.40003,24.554093 22.889013,24.554093 25.37702,24.554093 27.402014,22.529069 27.402014,20.040055 27.402014,17.552018 25.37702,15.528092 22.889013,15.528093z M22.167028,12.426042L23.834021,12.426042 23.834021,13.935075 23.981024,13.958024C24.912024,14.12404,25.772009,14.500016,26.509008,15.03505L26.62702,15.12404 27.757025,13.993058 28.935034,15.171037 27.806005,16.301042 27.940008,16.484025C28.422033,17.167008,28.769018,17.950089,28.943029,18.796037L28.996008,19.093034 30.500006,19.093034 30.500006,20.759049 29.02503,20.759049 28.997015,20.980119C28.848028,21.952043,28.472021,22.85109,27.924017,23.619035L27.805029,23.779069 28.935034,24.909074 27.757025,26.088029 26.62702,24.958024 26.61701,24.966081C25.866034,25.536027,24.981024,25.938004,24.019018,26.117082L23.834021,26.147111 23.834021,27.426042 22.167028,27.426042 22.167028,26.177018 21.949011,26.149064C20.942022,25.995011,20.014013,25.596086,19.228033,25.016007L19.151007,24.957047 18.019018,26.088029 16.841009,24.909074 17.972021,23.779069 17.863013,23.633073C17.294012,22.839005,16.908026,21.905046,16.767034,20.894059L16.75202,20.759049 15.500006,20.759049 15.500006,19.093034 16.783026,19.093034 16.812017,18.91005C16.991034,17.947038,17.393011,17.063004,17.963019,16.312028L17.972021,16.301042 16.841009,15.171037 18.019018,13.993058 19.15003,15.123063 19.310034,15.005021C20.078009,14.458024,20.977026,14.081071,21.949011,13.932023L22.167028,13.905046z M16.000014,6.0000114C14.346014,6.0000114 13.000014,7.3460112 13.000014,9.0000114 13.000014,10.654011 14.346014,12.000011 16.000014,12.000011 17.654014,12.000011 19.000014,10.654011 19.000014,9.0000114 19.000014,7.3460112 17.654014,6.0000114 16.000014,6.0000114z M16.000014,4.0000114C18.757014,4.0000114 21.000014,6.2430115 21.000014,9.0000114 21.000014,11.757011 18.757014,14.000011 16.000014,14.000011 13.243014,14.000011 11.000014,11.757011 11.000014,9.0000114 11.000014,6.2430115 13.243014,4.0000114 16.000014,4.0000114z M16.000123,0C24.651189,0 31.47924,8.0160384 31.765244,8.3580403 32.078245,8.7290418 32.078245,9.2720447 31.765244,9.6440461 31.700243,9.7220466 30.130231,11.580056 27.56321,13.533065L26.352201,11.941058C27.853213,10.798052 29.000222,9.6770465 29.645226,9.0010433 27.881213,7.1610341 22.366171,2.0000095 16.000123,2.0000095 9.6350741,2.0000095 4.1210318,7.1590343 2.3550186,9.0000434 4.013031,10.731052 8.9770689,15.388074 14.846114,15.946077L14.657112,17.936087C6.6410513,17.175083 0.49200439,9.9510479 0.23400211,9.6430461 -0.078000069,9.2720447 -0.078000069,8.7290418 0.23400211,8.3580403 0.52000427,8.0160384 7.3480568,0 16.000123,0z"
/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="Export" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Geometry="M23.471008,4.2230202L32,12.800012 23.471008,20.360005 23.471008,16.322012C23.471008,16.322012 8.7430115,12.294 5.0610046,20.360005 5.0610046,18.343985 6.3990173,8.2560073 23.471008,8.2560073z M0,0L21.334015,0 21.334015,2.1340014 21.334015,4.2690102 19.200012,4.2690102 19.200012,2.1340014 2.1340027,2.1340014 2.1340027,23.466997 19.200012,23.466997 19.200012,21.331989 21.334015,21.331989 21.334015,23.466997 21.334015,25.601 19.200012,25.601 0,25.601 0,23.466997 0,2.1340014z"
/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="Import" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Geometry="M13.594002,4.2219891L13.594002,8.255989C30.661003,8.255989 32,18.345018 32,20.360004 28.319,12.293011 13.594002,16.32399 13.594002,16.32399L13.594002,20.360004 5.0610008,12.798993z M0,0L21.333,0 21.333,2.1329974 21.333,4.2670025 19.201,4.2670025 19.201,2.1329974 2.1330032,2.1329974 2.1330032,23.467003 19.201,23.467003 19.201,21.332998 21.333,21.332998 21.333,23.467003 21.333,25.6 19.201,25.6 0,25.6 0,23.467003 0,2.1329974z"
/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<!-- Pin
<Path Data="F1M3,11L3,7 0,7 0,6 1,6 1,1 1,0 6,0 6,1 6,6 7,6 7,7 4,7 4,11 3,11z M2,1L2,6 4,6 4,1 2,1z" />
Unpin
@@ -1,10 +1,13 @@
using bts.api;
using bts.api.core;
using bts.api.orderman;
using mbts.co2;
using mbts.collections;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -15,105 +18,129 @@ using System.Windows.Input;
namespace mbts.spark {
partial class MarketViewVM {
public class Row : SecurityMDRow, IDisposable, IEquatable<Row> {
public class Row : SecurityMDRow, IDisposable, IEquatable<Row>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
readonly IBus _bus;
readonly OrdersModel _orders_model;
readonly AnimationManager _animation = new AnimationManager();
readonly ObservableCollection<OrderRecordImmutable> _orders = new ObservableCollection<OrderRecordImmutable>();
readonly ObservableFilterCollection<OrderRecordImmutable> _working_orders, _working_bids, _working_asks;
readonly string _security_text, _security_key_text;
readonly Property<double> _bid_price, _ask_price, _last_price, _day_change, _settlement, _session_high, _session_low;
readonly Property<int> _position;
readonly Property<ushort> _bid_size, _ask_size, _last_size;
readonly Property<ulong> _volume;
readonly Property<bool> _inside_bids, _inside_asks;
readonly DelegateProperty<int> _working_bid_volume, _working_ask_volume;
readonly DelegateProperty<double> _working_best_bid, _working_best_ask;
readonly Property<ushort> _bid_size, _ask_size;
readonly CellProperty<int> _position;
readonly CellProperty<ushort> _last_size;
readonly CellProperty<ulong> _volume;
readonly CellProperty<bool> _inside_bids, _inside_asks;
readonly CellProperty<int> _working_bid_volume, _working_ask_volume;
readonly CellProperty<double> _working_best_bid, _working_best_ask;
readonly CellProperty<string> _working_bids_text, _working_asks_text;
readonly MyOrderSummarizer _order_summarizer = new MyOrderSummarizer();
ulong _bbo_ref, _last_ref;
public Row(IBus bus, SecurityMD smd, OrdersModel orders_model) : base(smd) {
_bus = bus;
_orders_model = orders_model;
_position = backing(nameof(position), 0);
_security_text = smd.security.ToString();
_security_key_text = security_key.ToString();
string format_price(double price) => security.ContractSpec.format(price);
_position = new CellProperty<int>(0);
_bid_price = backing(nameof(bid_price), double.NaN);
_bid_size = backing(nameof(bid_size), (ushort)0);
_ask_price = backing(nameof(ask_price), double.NaN);
_ask_size = backing(nameof(ask_size), (ushort)0);
_bid_size = backing<ushort>(nameof(bid_size), 0);
_ask_size = backing<ushort>(nameof(ask_size), 0);
_last_price = backing(nameof(last_price), double.NaN);
_last_size = backing(nameof(last_size), (ushort)0);
_last_size = new CellProperty<ushort>(0);
_day_change = backing(nameof(day_change), double.NaN);
_session_low = backing(nameof(session_low), double.NaN);
_session_high = backing(nameof(session_high), double.NaN);
_settlement = backing(nameof(settlement), double.NaN);
_volume = backing(nameof(volume), 0ul);
_volume = new CellProperty<ulong>(0);
_inside_bids = new CellProperty<bool>();
_inside_asks = new CellProperty<bool>();
_working_bid_volume = new CellProperty<int>();
_working_ask_volume = new CellProperty<int>();
//
_working_orders = new ObservableFilterCollection<OrderRecordImmutable>(_orders, o => o.is_active());
_working_bids = new ObservableFilterCollection<OrderRecordImmutable>(_working_orders, o => o.Def.Side == Side.Buy);
_working_asks = new ObservableFilterCollection<OrderRecordImmutable>(_working_orders, o => o.Def.Side == Side.Sell);
_working_best_bid = backing(nameof(working_best_bid), () => _working_bids.Max(o => o.State.Price, double.NaN));
_working_best_ask = backing(nameof(working_best_ask), () => _working_asks.Min(o => o.State.Price, double.NaN));
_inside_bids = backing(nameof(inside_bids), _bid_price.CombineLatest(_working_best_bid).Select(vals => vals.Item2.newval >= vals.Item1.newval), false);
_inside_asks = backing(nameof(inside_asks), _ask_price.CombineLatest(_working_best_ask).Select(vals => vals.Item2.newval <= vals.Item1.newval), false);
_working_bid_volume = backing(nameof(working_bid_volume), () => _working_bids.Sum(o => o.working_quantity()));
_working_ask_volume = backing(nameof(working_ask_volume), () => _working_asks.Sum(o => o.working_quantity()));
_working_best_bid = new CellProperty<double>(format_price, double.NaN);
_working_best_ask = new CellProperty<double>(format_price, double.NaN);
_working_bids_text = new CellProperty<string>(string.Empty);
_working_asks_text = new CellProperty<string>(string.Empty);
//
_day_change = backing(nameof(day_change), double.NaN);
_session_low = backing(nameof(session_low), double.NaN);
_session_high = backing(nameof(session_high), double.NaN);
_animation.AnimationsChanged += _animation_AnimationsChanged;
//_working_bids_filled_animation = Disposable
default_command = new DelegateCommand(_show_ladder);
bid_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Bid));
bid_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidSize));
working_bids_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingBids));
ask_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Ask));
ask_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskSize));
working_asks_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingAsks));
_animation.AnimationsChanged += on_animations_changed;
void on_animations_changed() => RaisePropertyChanged(nameof(animations));
_dispose = Disposable.Create(
_working_bids.Subscribe(_working_bids_CollectionChanged),
_working_asks.Subscribe(_working_asks_CollectionChanged),
_position.ObserveOnDispatcher().Subscribe(vals => _animation.animate_position(vals.newval > vals.oldval)),
_inside_bids.ObserveOnDispatcher().Subscribe(vals => _animation.animate_working_bids_inside(vals.newval)),
_inside_asks.ObserveOnDispatcher().Subscribe(vals => _animation.animate_working_asks_inside(vals.newval)),
_day_change.ObserveOnDispatcher().Subscribe(vals => _animation.animate_day_change(vals.newval > vals.oldval)),
_orders_model.on_security_orders(security.Id).ObserveOnDispatcher().Subscribe(_on_orders),
Disposable.Create(() => _animation.AnimationsChanged -= _animation_AnimationsChanged)
_position.ObserveOnDispatcher().Subscribe(v => _animation.animate_position(v.newval > v.oldval)),
_inside_bids.ObserveOnDispatcher().Subscribe(v => _animation.animate_working_bids_inside(v.newval)),
_inside_asks.ObserveOnDispatcher().Subscribe(v => _animation.animate_working_asks_inside(v.newval)),
_day_change.ObserveOnDispatcher().Subscribe(v => _animation.animate_day_change(v.newval > v.oldval)),
Disposable.Create(() => _animation.AnimationsChanged -= on_animations_changed)
);
}
public event Action invalidated;
#region Bound Properties
public bool inside_bids => _inside_bids.Value;
public bool inside_asks => _inside_asks.Value;
public int position => _position.Value;
public bool inside_bids => _inside_bids.value;
public bool inside_asks => _inside_asks.value;
public int position => _position.value;
public double last_price => _last_price.Value;
public ushort last_size => _last_size.Value;
public ushort last_size => _last_size.value;
public double bid_price => _bid_price.Value;
public ushort bid_size => _bid_size.Value;
public double ask_price => _ask_price.Value;
public ushort ask_size => _ask_size.Value;
public ulong volume => _volume.Value;
public ulong volume => _volume.value;
public double settlement => _settlement.Value;
public double day_change => _day_change.Value;
public double session_low => _session_low.Value;
public double session_high => _session_high.Value;
public int working_bid_volume => _working_bid_volume.Value;
public int working_ask_volume => _working_ask_volume.Value;
public string working_bids_text => OrderUtils.order_collection_summary(_working_bids, (x, y) => x.CompareTo(y), security.ContractSpec);
public string working_asks_text => OrderUtils.order_collection_summary(_working_asks, (x, y) => y.CompareTo(x), security.ContractSpec);
public double working_best_bid => _working_best_bid.Value;
public double working_best_ask => _working_best_ask.Value;
public IEnumerable<CellAnimation> animations => _animation.get_animations();
public ICommand show_ladder => new DelegateCommand(_show_ladder);
public ICommand show_order_book => new DelegateCommand<MarketViewView.Columns>(_show_order_book);
public ICommand show_order_ticket => new DelegateCommand<MarketViewView.Columns>(_show_order_ticket);
public int working_bid_volume => _working_bid_volume.value;
public int working_ask_volume => _working_ask_volume.value;
public string working_bids_text => OrderUtils.order_collection_summary(_order_summarizer.best_working_bid, _order_summarizer.best_working_bid_qty, _order_summarizer.working_bid_qty, security.ContractSpec);
public string working_asks_text => OrderUtils.order_collection_summary(_order_summarizer.best_working_ask, _order_summarizer.best_working_ask_qty, _order_summarizer.working_ask_qty, security.ContractSpec);
public double working_best_bid => _working_best_bid.value;
public double working_best_ask => _working_best_ask.value;
public IList<CellAnimation> animations => _animation.get_animations().ToList();
public ICommand bid_price_click { get; }
public ICommand bid_size_click { get; }
public ICommand working_bids_click { get; }
public ICommand ask_price_click { get; }
public ICommand ask_size_click { get; }
public ICommand working_asks_click { get; }
public ICommand default_command { get; }
#endregion
public void refresh() {
bool dirty = false;
if (md.md.get_bbo(ref _bbo_ref, out NativePriceSize b, out NativePriceSize a)) {
_bid_price.set(b.price);
_bid_size.set(b.size);
_ask_price.set(a.price);
_ask_size.set(a.size);
_inside_bids.on_next_distinct(_bid_price.Value >= _working_best_bid.value, ref dirty);
_inside_asks.on_next_distinct(_ask_price.Value <= _working_best_ask.value, ref dirty);
}
if (md.md.get_last_trade(ref _last_ref, out NativePriceSize ps, out DateTime ts, out ulong v)) {
_last_price.set(ps.price);
_last_size.set(ps.size);
_volume.set(v);
_last_size.on_next_distinct(ps.size, ref dirty);
_volume.on_next_distinct(v, ref dirty);
}
_settlement.set(md.md.settlement_price);
_session_low.set(md.md.session_low);
_session_high.set(md.md.session_high);
_day_change.set(last_price - session_high);
_day_change.set(last_price - settlement);
if (dirty)
invalidated?.Invoke();
}
public bool Equals(Row other) =>
@@ -130,7 +157,7 @@ namespace mbts.spark {
void _show_ladder() => _bus.post(SparkTags.ShowLadderTag, md);
void _show_order_book(MarketViewView.Columns col) {
switch(col) {
switch (col) {
case MarketViewView.Columns.WorkingBids: _show_order_book(Side.Buy); break;
case MarketViewView.Columns.WorkingAsks: _show_order_book(Side.Sell); break;
}
@@ -175,39 +202,106 @@ namespace mbts.spark {
#endregion
#region Callbacks
void _on_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
uint removed_buys = 0, removed_sells = 0, added_buys = 0, added_sells = 0;
foreach (var o in orders.removed) {
_orders.Remove(o);
switch (o.Def.Side) {
case Side.Buy: removed_buys += o.State.FilledQuantity; break;
case Side.Sell: removed_sells += o.State.FilledQuantity; break;
}
}
foreach (var o in orders.added) {
_orders.Add(o);
switch (o.Def.Side) {
case Side.Buy: added_buys += o.State.FilledQuantity; break;
case Side.Sell: added_sells += o.State.FilledQuantity; break;
}
}
_position.Value += (int)(removed_sells + added_buys - removed_buys - added_sells);
void _on_orders(ITableUpdateTransform<OrderRecordImmutable, uint> orders) {
foreach (var o in orders.removed)
_order_summarizer.remove_order(o);
foreach (var o in orders.added)
_order_summarizer.add_order(o);
bool dirty = false;
_refresh_orders(ref dirty);
if (dirty)
invalidated?.Invoke();
}
void _working_bids_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
_working_best_bid.refresh();
_working_bid_volume.refresh();
RaisePropertyChanged(nameof(working_bids_text));
void _refresh_orders(ref bool dirty) {
_position.on_next_distinct(_order_summarizer.position, ref dirty);
_working_bid_volume.on_next_distinct(_order_summarizer.working_bid_qty, ref dirty);
_working_ask_volume.on_next_distinct(_order_summarizer.working_ask_qty, ref dirty);
_working_best_bid.on_next_distinct(_order_summarizer.best_working_bid, ref dirty);
_working_best_ask.on_next_distinct(_order_summarizer.best_working_ask, ref dirty);
_inside_bids.on_next_distinct(_order_summarizer.best_working_bid >= bid_price, ref dirty);
_inside_asks.on_next_distinct(_order_summarizer.best_working_ask <= ask_price, ref dirty);
RaisePropertyChanged(nameof(working_bids_text), nameof(working_asks_text));
}
void _working_asks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
_working_best_ask.refresh();
_working_ask_volume.refresh();
RaisePropertyChanged(nameof(working_asks_text));
}
void _animation_AnimationsChanged() => RaisePropertyChanged(nameof(animations));
#endregion
}
class MyOrderSummarizer {
SortedSetMap<double, OrderRecordImmutable> _bids, _asks;
int _best_bid_working_qty, _best_ask_working_qty, _position, _working_bid_qty, _working_ask_qty;
public MyOrderSummarizer() {
_bids = new SortedSetMap<double, OrderRecordImmutable>(
OrderDataHelper.CompareUtils.bid_price_comparer,
OrderDataHelper.CompareUtils.order_record_id_eq_comparer);
_asks = new SortedSetMap<double, OrderRecordImmutable>(
OrderDataHelper.CompareUtils.ask_price_comparer,
OrderDataHelper.CompareUtils.order_record_id_eq_comparer);
}
// bid
public double best_working_bid => _bids.Count > 0 ? _bids.at_index(0).Key : double.NaN;
public int best_working_bid_qty => _best_bid_working_qty;
public int working_bid_qty => _working_bid_qty;
// ask
public double best_working_ask => _asks.Count > 0 ? _asks.at_index(0).Key : double.NaN;
public int best_working_ask_qty => _best_ask_working_qty;
public int working_ask_qty => _working_ask_qty;
// pos
public int position => _position;
public void add_order(OrderRecordImmutable o) {
// This is a market data form - not an orders form - so just show data for root/non-child orders.
if (o.has_parent())
return;
switch (o.side()) {
case Side.Buy:
_position += (int)o.filled();
if (o.is_active()) {
_working_bid_qty += o.working_quantity();
_bids.add(o.price(), o);
}
break;
case Side.Sell:
_position -= (int)o.filled();
if (o.is_active()) {
_working_ask_qty += o.working_quantity();
_asks.add(o.price(), o);
}
break;
default: return;
}
_refresh();
}
public void remove_order(OrderRecordImmutable o) {
// This is a market data form - not an orders form - so just show data for root/non-child orders.
if (o.has_parent())
return;
switch (o.side()) {
case Side.Buy:
_position -= (int)o.filled();
if (o.is_active()) {
_working_bid_qty -= o.working_quantity();
_bids.remove(o.price(), o);
}
break;
case Side.Sell:
_position += (int)o.filled();
if (o.is_active()) {
_working_ask_qty -= o.working_quantity();
_asks.remove(o.price(), o);
}
break;
default: return;
}
_refresh();
}
void _refresh() {
_best_bid_working_qty = _bids.Count > 0 ? _bids.at_index(0).Sum(o => o.working_quantity()) : 0;
_best_ask_working_qty = _asks.Count > 0 ? _asks.at_index(0).Sum(o => o.working_quantity()) : 0;
}
}
}
}
}
@@ -1,45 +1,118 @@
using bts.api;
using bts.api;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.Windows.Media;
using System.Reactive.Linq;
using static bts.shell.MarketViewWindowFactory;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.MarketViewView;
namespace mbts.spark {
partial class MarketViewVM : SecurityMDGrid<MarketViewVM.Row, SecurityMD> {
readonly IBus _bus;
readonly OrdersModel _orders_model;
public MarketViewVM(IBus bus, OrdersModel orders_model, SecurityCriteria security_criteria, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
: base(RXCollection.Create(security_criteria.subscribe_md(bus), SecurityDataHelper.EqualityComparer.MDComparer), sorting_criteria) {
public MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state)
: base(RXCollection.Create(state.SelectMany(s => s.security_criteria.subscribe_md(bus)), SecurityDataHelper.EqualityComparer.MDComparer), null) {
_bus = bus;
_orders_model = orders_model;
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(null), nameof(Row.security));
if (filters != null) {
security_filter.load_state(filters.security);
bid_price_filter.load_state(filters.bid_price);
bid_size_filter.load_state(filters.bid_size);
ask_price_filter.load_state(filters.ask_price);
ask_size_filter.load_state(filters.ask_size);
last_price_filter.load_state(filters.last_price);
last_size_filter.load_state(filters.last_size);
day_change_filter.load_state(filters.day_change);
settlement_filter.load_state(filters.settlement);
session_low_filter.load_state(filters.session_low);
session_high_filter.load_state(filters.session_high);
position_filter.load_state(filters.position);
working_bid_volume_filter.load_state(filters.working_bid_volume);
working_ask_volume_filter.load_state(filters.working_ask_volume);
volume_filter.load_state(filters.volume);
security_key_filter.load_state(filters.security_key);
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
// Columns
const string size_col_header = "Qty", working_col_header = "Working";
const double working_orders_col_width = 105;
IMultiValueConverter price_converter = new ui.spark.converters.TickDisplayConverter();
add_col(Columns.Security, "Security", r => r.security.ToString(), nameof(Row.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), 64, volume_filter, DataGridFilterKnownTemplates.ULongMinMax);
add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.LastSize, size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax);
add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true);
add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid_brush);
add_hl_size_col(Columns.BidSize, size_col_header, nameof(Row.bid_size), nameof(Row.bid_size_click), bid_size_filter, BTSColors.MarketData.bid_brush);
add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text_brush, BTSColors.MarketData.bid_brush);
add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text_brush, BTSColors.MarketData.ask_brush);
add_hl_size_col(Columns.AskSize, size_col_header, nameof(Row.ask_size), nameof(Row.ask_size_click), ask_size_filter, BTSColors.MarketData.ask_brush);
add_hl_working_orders_col(Columns.WorkingAsks, working_col_header, nameof(Row.working_asks_text), nameof(Row.working_best_ask), nameof(Row.working_asks_click), working_ask_volume_filter, BTSColors.MarketData.ask_brush);
add_col(Columns.Settlement, "Settlement", r => r.format_price(r.settlement), nameof(Row.settlement), DefaultColumnWidths.Price, settlement_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.DayChange, "Change", r => r.format_price(r.day_change), nameof(Row.day_change), DefaultColumnWidths.Price, day_change_filter, DataGridFilterKnownTemplates.DoubleMinMax, true);
add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.SecurityKey, "Security Key", r => r.security_key.ToString(), nameof(Row.security_key), DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
//
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false) =>
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
Header = header,
sort_member_path = prop,
Width = width,
padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding
});
void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Brush foreground, Brush background) =>
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price, foreground, background, SelectionFill);
void add_hl_size_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Brush foreground) =>
add_hl_col(col_key, header, new Binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, foreground, null, null);
void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Brush foreground) =>
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null);
void add_hl_col(
Columns col_key, string header, BindingBase binding, string prop, string command_prop,
IDataGridColumnFilter filter, string filter_template, double width, Brush foreground, Brush background, Brush background_overlay) =>
columns.Add(new HyperlinkElementColumn<Row, Columns>(
col_key, binding, new Binding(command_prop), filter, filter_template) {
Header = header,
sort_member_path = prop,
foreground = foreground,
background = background,
background_overlay = background_overlay,
Width = DefaultColumnWidths.Price
});
BindingBase price_binding(string prop) {
var mb = new MultiBinding { Converter = price_converter };
mb.Bindings.Add(new Binding(prop) { Mode = BindingMode.OneWay });
mb.Bindings.Add(new Binding($"{nameof(Row.security)}.{nameof(SecurityImmutable.ContractSpec)}") { Mode = BindingMode.OneWay });
return mb;
}
_resources.AddRange(DataGridFilterHelper.hook_filters(rows_view,
//
_resources.AddRange(
state.ObserveOnDispatcher().Subscribe(s => {
using (rows_view.DeferRefresh()) {
rows_view.SetSortingCriteria(s.sorting_criteria);
var filters = s.filters;
if (filters != null) {
security_filter.load_state(filters.security);
bid_price_filter.load_state(filters.bid_price);
bid_size_filter.load_state(filters.bid_size);
ask_price_filter.load_state(filters.ask_price);
ask_size_filter.load_state(filters.ask_size);
last_price_filter.load_state(filters.last_price);
last_size_filter.load_state(filters.last_size);
day_change_filter.load_state(filters.day_change);
settlement_filter.load_state(filters.settlement);
session_low_filter.load_state(filters.session_low);
session_high_filter.load_state(filters.session_high);
position_filter.load_state(filters.position);
working_bid_volume_filter.load_state(filters.working_bid_volume);
working_ask_volume_filter.load_state(filters.working_ask_volume);
volume_filter.load_state(filters.volume);
security_key_filter.load_state(filters.security_key);
}
}
}), DataGridFilterHelper.hook_filters(rows_view,
security_filter, bid_price_filter, bid_size_filter, ask_price_filter, ask_size_filter, last_price_filter, last_size_filter, day_change_filter, settlement_filter,
session_low_filter, session_high_filter, position_filter, working_bid_volume_filter, working_ask_volume_filter, volume_filter, security_key_filter),
WorkLoop.Start(_refresh)
WorkLoop.Start("MarketView", _refresh)
);
}
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
#region Filters
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_price_filter { get; } = new DoubleMinMax<Row>(r => r.bid_price, nameof(Row.bid_price));
@@ -86,6 +159,6 @@ namespace mbts.spark {
void _refresh() {
foreach (var r in _row_vms) r.refresh();
}
}
}
}
}
@@ -1,306 +1,43 @@
<UserControl x:Class="mbts.ui.spark.MarketViewView" x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:mbts.ui"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:usp="clr-namespace:mbts.ui.spark">
<UserControl
x:Class="mbts.ui.spark.MarketViewView"
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:usp="clr-namespace:mbts.ui.spark"
FocusManager.IsFocusScope="True"
Background="{StaticResource CommonBackground}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style x:Key="OrderTicketCell" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_order_ticket}"/>
<Setter Property="u:AttachedCommands.DoubleClickParameter" Value="{Binding Path='Column.(c:DataGridEx.CellKey)', RelativeSource={RelativeSource Self}}"/>
</Style>
<Style x:Key="OrderTicketStaticBackgroundCell" TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellStaticBackgroundStyle}">
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_order_ticket}"/>
<Setter Property="u:AttachedCommands.DoubleClickParameter" Value="{Binding Path='Column.(c:DataGridEx.CellKey)', RelativeSource={RelativeSource Self}}"/>
</Style>
<Style x:Key="PositionCell" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="Foreground" Value="{Binding position, Converter={StaticResource PositionSignBrushConverter}}"/>
<Setter Property="Padding" Value="8,0,4,0"/>
</Style>
<Style x:Key="BidPriceCell" TargetType="DataGridCell" BasedOn="{StaticResource OrderTicketStaticBackgroundCell}">
<Setter Property="c:DataGridEx.CellStaticBackground" Value="{uc:BTSColor MarketData.bid}"/>
<Setter Property="Background" Value="{uc:BTSColor MarketData.bid}"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="AskPriceCell" TargetType="DataGridCell" BasedOn="{StaticResource OrderTicketStaticBackgroundCell}">
<Setter Property="c:DataGridEx.CellStaticBackground" Value="{uc:BTSColor MarketData.ask}"/>
<Setter Property="Background" Value="{uc:BTSColor MarketData.ask}"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="DayChangeCell" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="Foreground" Value="{Binding day_change, Converter={StaticResource DayChangeSignBrushConverter}}"/>
<Setter Property="Padding" Value="8,0,4,0"/>
</Style>
<Style x:Key="LastPriceElement" TargetType="TextBlock">
<Setter Property="Foreground" Value="{uc:BTSColor MarketData.last}"/>
</Style>
<Style x:Key="LastSizeElement" TargetType="TextBlock">
<Setter Property="Foreground" Value="{uc:BTSColor MarketData.last}"/>
</Style>
<Style x:Key="HyperlinkElementBase" TargetType="Hyperlink">
<Setter Property="CommandParameter" Value="{Binding Path='Column.(c:DataGridEx.CellKey)', RelativeSource={RelativeSource AncestorType=DataGridCell}}"/>
</Style>
<Style x:Key="HyperlinkBidFG" TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkElementBase}">
<Setter Property="Foreground" Value="Transparent"/>
<Setter Property="TextDecorations" Value="Underline"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{u:BTSColor MarketData.bid}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="HyperlinkAskFG" TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkElementBase}">
<Setter Property="Foreground" Value="Transparent"/>
<Setter Property="TextDecorations" Value="Underline"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{u:BTSColor MarketData.ask}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="HyperlinkWhiteFG" TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkElementBase}">
<Setter Property="Foreground" Value="Transparent"/>
<Setter Property="TextDecorations" Value="Underline"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White"/>
</Trigger>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<c:DataGridEx x:Name="grid" ItemsSource="{Binding rows_view}" IsReadOnly="True" CanUserSortColumns="True" GridLinesVisibility="None"
CurrentItem="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
u:DataGridBehavior.NotifyColumnsOfSorting="True" usp:GridAnimation.Enableanimation="True" BorderThickness="0">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="usp:GridAnimation.RowAnimation" Value="{Binding animations}"/>
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_ladder}"/>
<Setter Property="u:AttachedCommands.Enter" Value="{Binding show_ladder}"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Security" Binding="{Binding security}"
c:DataGridEx.CellKey="{u:Enum Security, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.security_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
<DataGridTextColumn Header="Volume" Binding="{Binding volume}"
c:DataGridEx.CellKey="{u:Enum Volume, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.volume_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.volume_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.volume_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource ULongMinMaxTemplate}"/>
<DataGridTextColumn Header="Last" ElementStyle="{StaticResource LastPriceElement}" SortMemberPath="last_price"
c:DataGridEx.CellKey="{u:Enum Last, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.last_price_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.last_price_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.last_price_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="last_price"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Qty" ElementStyle="{StaticResource LastSizeElement}" Binding="{Binding last_size}"
c:DataGridEx.CellKey="{u:Enum LastSize, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.last_size_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.last_size_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.last_size_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource UShortMinMaxTemplate}"/>
<DataGridTextColumn Header="Pos" CellStyle="{StaticResource PositionCell}" Binding="{Binding position}"
c:DataGridEx.CellKey="{u:Enum Position, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.position_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.position_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.position_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"/>
<DataGridTemplateColumn Header="Working" SortMemberPath="working_best_bid"
c:DataGridEx.CellKey="{u:Enum WorkingBids, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.working_bid_volume_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.working_bid_volume_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.working_bid_volume_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource UIntMinMaxTemplate}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding show_order_book}" Style="{StaticResource HyperlinkBidFG}">
<TextBlock Text="{Binding working_bids_text}" Foreground="{u:BTSColor MarketData.bid}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Qty"
c:DataGridEx.CellKey="{u:Enum BidSize, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.bid_size_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.bid_size_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.bid_size_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource UShortMinMaxTemplate}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding show_order_ticket}" Style="{StaticResource HyperlinkBidFG}">
<TextBlock Text="{Binding bid_size}" Foreground="{u:BTSColor MarketData.bid}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Bid" CellStyle="{StaticResource BidPriceCell}" SortMemberPath="bid_price"
c:DataGridEx.CellKey="{u:Enum Bid, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.bid_price_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.bid_price_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.bid_price_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding show_order_ticket}" Style="{StaticResource HyperlinkWhiteFG}">
<TextBlock Foreground="White">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="bid_price"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Ask" CellStyle="{StaticResource AskPriceCell}" SortMemberPath="ask_price"
c:DataGridEx.CellKey="{u:Enum Ask, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.ask_price_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.ask_price_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.ask_price_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding show_order_ticket}" Style="{StaticResource HyperlinkWhiteFG}">
<TextBlock Foreground="White">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="ask_price"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Qty"
c:DataGridEx.CellKey="{u:Enum AskSize, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.ask_size_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.ask_size_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.ask_size_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource UShortMinMaxTemplate}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding show_order_ticket}" Style="{StaticResource HyperlinkAskFG}">
<TextBlock Text="{Binding ask_size}" Foreground="{u:BTSColor MarketData.ask}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Working" SortMemberPath="working_best_ask"
c:DataGridEx.CellKey="{u:Enum WorkingAsks, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.working_ask_volume_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.working_ask_volume_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.working_ask_volume_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource UIntMinMaxTemplate}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding show_order_book}" Style="{StaticResource HyperlinkAskFG}">
<TextBlock Text="{Binding working_asks_text}" Foreground="{u:BTSColor MarketData.ask}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Settlement" SortMemberPath="settlement"
c:DataGridEx.CellKey="{u:Enum Settlement, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.settlement_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.settlement_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.settlement_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="settlement"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Change" CellStyle="{StaticResource DayChangeCell}" SortMemberPath="day_change"
c:DataGridEx.CellKey="{u:Enum DayChange, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.day_change_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.day_change_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.day_change_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="day_change"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Low" SortMemberPath="session_low"
c:DataGridEx.CellKey="{u:Enum SessionLow, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.session_low_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.session_low_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.session_low_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="session_low"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="High" SortMemberPath="session_high"
c:DataGridEx.CellKey="{u:Enum SessionHigh, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.session_high_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.session_high_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.session_high_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="session_high"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Security Key" Width="*" Binding="{Binding security_key}"
c:DataGridEx.CellKey="{u:Enum SecurityKey, Type={u:Nested usp:MarketViewView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.security_key_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_key_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_key_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
</DataGrid.Columns>
</c:DataGridEx>
</Grid>
<AdornerDecorator>
<c:DIYGrid
x:Name="_grid"
Columns="{Binding columns}"
CurrentRow="{Binding current_row}"
Rows="{Binding rows_view}"
>
<c:DIYGrid.InputBindings>
<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
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
/>
</c:DIYGrid.CommandBindings>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<usp:MarketViewRowView usp:GridAnimation.RowAnimation="{Binding animations}"/>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
</AdornerDecorator>
</UserControl>
@@ -1,4 +1,6 @@
using mbts.spark.filters;
using mbts.spark;
using mbts.ui.controls;
using System.Linq;
using System.Windows.Controls;
namespace mbts.ui.spark {
@@ -10,7 +12,12 @@ namespace mbts.ui.spark {
public MarketViewView() {
InitializeComponent();
DataGridFilterHelper.set_filter_key_event_hooks(this);
AdornerHelper.attach_continuously_on_loaded(
() => new GridAnimation.DIYGridAnimationAdorner<Columns>(
_grid, _grid.descendants_of_type<DIYGridRowsPresenter>().First()),
_grid);
}
}
class MarketViewRowView : DIYGrid.HighPerformanceRow<MarketViewVM.Row, MarketViewView.Columns> { }
}
@@ -9,79 +9,62 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Media;
namespace mbts.spark
{
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>
{
readonly MarketViewWindowFactory _factory;
namespace mbts.spark {
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>, ISharedWindow {
const string _window_type = "MarketView", _thread_id = "MarketView";
readonly OrdersModel _orders_model;
SingleValuePump<MarketViewVM> _vm = new SingleValuePump<MarketViewVM>();
MarketViewWindowFactory.StartupData _state;
readonly AllowOnce _init_guard = new AllowOnce("MarketView");
MarketViewVM _vm;
MarketViewView _view;
PartialCommand<SecurityCriteria> _criteria_callback_command;
MarketViewWindowFactory.StartupData _state;
IReadOnlyList<SparkCommand> _commands;
IDisposable _subscriptions;
MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, ImageSource icon) : base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
_factory = factory;
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state)
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
_orders_model = orders_model;
ITag<SecurityCriteria> _updated_security_criteria_tag = new SparkTags.SparkTag<SecurityCriteria>("MarketView Callback");
_criteria_callback_command = SparkCommand.create("Update Market View", SparkCommands.ShowMarketView.icon, _updated_security_criteria_tag);
Commands = new SparkCommand[] {
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (_criteria_callback_command, _state.security_criteria)),
SparkCommands.ShowLadder.complete(_vm.observable.SelectMany(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault),
// TODO: ShowLinkedLadder
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.observable.SelectMany(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault),
// TODO: ShowLinkedOrderTicket
SparkCommands.ShowFills.complete(_vm.observable.SelectMany(vm => vm.selected_securities_observable()), _ => true,
_state = state;
Icon.Freeze();
}
event Action<MarketViewWindowFactory.StartupData> _state_changed;
public override string window_type => _window_type;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public override MarketViewWindowFactory.StartupData SaveState => _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states());
public override double MinWidth => 1035 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
public string thread_id => _thread_id;
public void thread_init() {
_init_guard.check_access();
Logger.notnull(_state);
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);
_view = new MarketViewView { DataContext = _vm };
var update_securities_tag = new SparkTags.SparkTag<SecurityCriteria>("MarketView Callback");
var update_securities_cmd = SparkCommand.create("Update Market View", Icon, update_securities_tag);
_commands = new List<SparkCommand> {
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (update_securities_cmd, _state.security_criteria)),
SparkCommands.ShowLadders.complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.ShowFills.complete(_vm.selected_securities_observable(), _ => true,
secs => new FillsWindowFactory.StartupData(new FillsWindowFactory.FilterStates {
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
})
),
SparkCommands.ShowTimeAndSales.complete(_vm.observable.SelectMany(vm => vm.selected_securities_observable()), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
// SpreadMatrix ??
// SyntheticCreate
SparkCommands.ShowSyntheticCreate.complete(_vm.observable.SelectMany(vm => vm.selected_securities_observable()), secs => secs.Count > 0, secs => new SecurityCriteria(secs))
// ModifyOrder - await orders
// CancelOrdersRoot ?
//SparkCommands.CancelBuyOrders,
//SparkCommands.CancelSellOrders,
//SparkCommands.CancelOrders,
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count < 3, secs => new SecurityCriteria(secs))
};
_subscriptions = _bus.subscribe(_updated_security_criteria_tag).ObserveOnDispatcher().Subscribe(update_criteria);
_subscriptions = _bus.subscribe(update_securities_tag).ObserveOnDispatcher().Subscribe(c =>
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states())));
}
public static MarketViewWindow create(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, ImageSource icon) {
var window = new MarketViewWindow(factory, bus, orders_model, icon);
window._initialize(state);
return window;
}
public void update_criteria(SecurityCriteria c) =>
_initialize(new MarketViewWindowFactory.StartupData(c, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states()));
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands { get; }
public override MarketViewWindowFactory.StartupData SaveState => _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
public override double MinWidth => 1035 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
public override void Dispose() {
Disposable.dispose(ref _subscriptions);
Disposable.dispose(ref _vm);
foreach (var c in Commands.OfType<IDisposable>()) c.Dispose();
}
void _initialize(MarketViewWindowFactory.StartupData state) {
Logger.notnull(state);
_state = state;
_vm.value = new MarketViewVM(_bus, _orders_model, state.security_criteria, state.sorting_criteria, state.filters);
if (_view == null) {
_view = new MarketViewView { DataContext = _vm.value };
} else {
_view.DataContext = _vm.value;
}
_vm.Dispose();
Commands.OfType<IDisposable>().DisposeAll();
}
}
}
@@ -1,5 +1,4 @@
using bts.api;
using bts.spark;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
@@ -17,17 +16,16 @@ namespace bts.shell {
public MarketViewWindowFactory(MarketShellModule module, IBus bus, OrdersModel orders_model) : base(module, "MarketView") {
_bus = bus;
_orders_model = orders_model;
_dispose = bus.subscribe(SparkTags.ShowMarketViewTag).ObserveOnDispatcher().Subscribe(show);
_dispose = bus.subscribe(SparkTags.ShowMarketViewTag).ObserveOnDispatcher().Subscribe(_show);
}
public override Task<IShellWindow> load(StartupData state) =>
Task.FromResult(_create(state));
void show(SecurityCriteria criteria) =>
_bus.post(SparkTags.ShowWindow, _create(new StartupData(criteria)));
void _show(SecurityCriteria criteria) =>
_bus.post(SparkTags.ShowWindow, _create(new StartupData(criteria, new List<SortingCriteria> { new SortingCriteria(nameof(MarketViewVM.Row.security), true) }, null)));
IShellWindow _create(StartupData state) =>
MarketViewWindow.create(this, _bus, _orders_model, state, SparkCommands.PickMarketView.icon);
IShellWindow _create(StartupData state) => new MarketViewWindow(this, _bus, _orders_model, state);
[ProtoContract]
public class StartupData {
@@ -2,9 +2,12 @@
using bts.co2;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.spark;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using static bts.shell.TimeAndSalesWindowFactory;
@@ -17,7 +20,7 @@ namespace mbts.spark {
: base(RXCollection.CreateBuffer(model.trades, 1000), sorting_criteria) {
_bus = bus;
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(null), nameof(Row.security));
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
if (filters != null) {
time_filter.load_state(filters.time);
security_filter.load_state(filters.security);
@@ -26,6 +29,16 @@ namespace mbts.spark {
trade_key_filter.load_state(filters.trade_key);
security_key_filter.load_state(filters.security_key);
}
// Columns
add_column(TimeAndSalesView.Columns.Security, "Security", DIYGrid.DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.Time, "Time", DIYGrid.DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter);
add_column(TimeAndSalesView.Columns.Price, "Price", DIYGrid.DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_column(TimeAndSalesView.Columns.Size, "Size", DIYGrid.DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax);
add_column(TimeAndSalesView.Columns.TradeKey, "Trade Key", DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.SecurityKey, "Security Key", DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template) =>
columns.Add(new DIYGrid.RenderTextColumn<Row>(r => r[col_key], filter, filter_template) { Header = header, Width = width });
//
_resources.AddRange(
show_ladder,
DataGridFilterHelper.hook_filters(
@@ -34,6 +47,7 @@ namespace mbts.spark {
);
}
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
public DelegateCommandParamObserver<Row> show_ladder { get; }
#region Filters
@@ -62,27 +76,61 @@ namespace mbts.spark {
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);
}
public class Row : SecurityRow {
public class Row : SecurityRow, DIYGrid.IRowVM {
readonly uint _trade_key;
readonly string
_security_text,
_time_text,
_price_text,
_size_text,
_trade_key_text,
_security_key_text;
public Row(TimeAndSalesVM parent, SecurityImmutable security, TradeMessageImmutable trade) : base(security) {
_trade_key = trade.Key;
time = trade.Time.lldatetime();
price = trade.Mantissa * Math.Pow(10, trade.Exponent);
size = trade.Size;
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();
_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 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.TradeKey: return _trade_key_text;
case TimeAndSalesView.Columns.SecurityKey: return _security_key_text;
default: return null;
}
}
}
void _invalidate() =>
invalidated?.Invoke();
}
}
}
@@ -1,9 +1,13 @@
<UserControl 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:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:usp="clr-namespace:mbts.ui.spark">
<UserControl
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:usp="clr-namespace:mbts.ui.spark"
FocusManager.IsFocusScope="True"
Background="{StaticResource CommonBackground}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
@@ -12,63 +16,26 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<c:DataGridEx x:Name="grid" ItemsSource="{Binding rows_view}" IsReadOnly="True" CanUserSortColumns="True" GridLinesVisibility="None"
CurrentItem="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
u:DataGridBehavior.NotifyColumnsOfSorting="True" BorderThickness="0">
<DataGrid.InputBindings>
<KeyBinding Key="Return" Command="{Binding DataContext.show_ladder, Source={u:DirectReference grid}}"/>
</DataGrid.InputBindings>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_ladder}"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Security" Binding="{Binding security}"
c:DataGridEx.CellKey="{u:Enum Security, Type={u:Nested usp:TimeAndSalesView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.security_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
<DataGridTextColumn Header="Time" Binding="{Binding time, StringFormat='M/d h:mm:ss tt'}"
c:DataGridEx.CellKey="{u:Enum Time, Type={u:Nested usp:TimeAndSalesView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.time_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.time_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.time_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource TimeFilterTemplate}"/>
<DataGridTextColumn Header="Price" SortMemberPath="price"
c:DataGridEx.CellKey="{u:Enum Price, Type={u:Nested usp:TimeAndSalesView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.price_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.price_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.price_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="price"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Qty" Binding="{Binding size}"
c:DataGridEx.CellKey="{u:Enum Size, Type={u:Nested usp:TimeAndSalesView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.size_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.size_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.size_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource UIntMinMaxTemplate}"/>
<DataGridTextColumn Header="Trade Key" Binding="{Binding trade_key}"
c:DataGridEx.CellKey="{u:Enum TradeKey, Type={u:Nested usp:TimeAndSalesView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.trade_key_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.trade_key_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.trade_key_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
<DataGridTextColumn Header="Security Key" Width="*" Binding="{Binding security_key}"
c:DataGridEx.CellKey="{u:Enum SecurityKey, Type={u:Nested usp:TimeAndSalesView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.security_key_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_key_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_key_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
</DataGrid.Columns>
</c:DataGridEx>
</Grid>
<AdornerDecorator>
<Grid>
<c:DIYGrid Columns="{Binding columns}" Rows="{Binding rows_view}" CurrentRow="{Binding current_row}">
<c:DIYGrid.InputBindings>
<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
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
/>
</c:DIYGrid.CommandBindings>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<usp:TimeAndSalesRowView/>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -1,4 +1,5 @@
using mbts.spark.filters;
using mbts.spark;
using mbts.ui.controls;
using System.Windows.Controls;
namespace mbts.ui.spark {
@@ -8,9 +9,9 @@ namespace mbts.ui.spark {
partial class TimeAndSalesView : UserControl {
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey }
public TimeAndSalesView() {
public TimeAndSalesView() =>
InitializeComponent();
DataGridFilterHelper.set_filter_key_event_hooks(this);
}
}
class TimeAndSalesRowView : DIYGrid.HighPerformanceRow<TimeAndSalesVM.Row, TimeAndSalesView.Columns> { }
}
@@ -15,6 +15,7 @@ using static bts.shell.TimeAndSalesWindowFactory;
namespace mbts.spark {
class TimeAndSalesWindow : SparkWindow<StartupData>, ISlowWindow {
const string _window_type = "TimeAndSales";
readonly MDConsumer _consumer;
readonly TimeAndSalesWindowFactory _factory;
SingleValuePump<TimeAndSalesVM> _vm;
@@ -29,8 +30,10 @@ namespace mbts.spark {
_consumer = consumer;
_factory = factory;
_state = state;
Icon.Freeze();
}
public override string window_type => _window_type;
public override IEnumerable<SparkCommand> Commands => _commands;
public override UIElement Content => _view;
public override double MinWidth => 444 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
@@ -42,18 +45,18 @@ namespace mbts.spark {
public void thread_init() {
_dispose_resources();
_vm = new SingleValuePump<TimeAndSalesVM>();
var updated_security_criteria_tag = new SparkTags.SparkTag<SecurityCriteria>("TimeAndSales Callback");
var criteria_callback_command = SparkCommand.create("Update Time and Sales", SparkCommands.ShowTimeAndSales.icon, updated_security_criteria_tag);
var update_securities_tag = new SparkTags.SparkTag<SecurityCriteria>("TimeAndSales Callback");
var update_securities_cmd = SparkCommand.create("Update Time and Sales", Icon, update_securities_tag);
var current_smd_source = _vm.observable.SelectMany(vm => vm.current_security_md_observable(_bus)).ObserveOnDispatcher();
var selected_secs_source = _vm.observable.SelectMany(vm => vm.selected_securities_observable()).ObserveOnDispatcher();
_commands = new SparkCommand[] {
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (criteria_callback_command, _state.security_criteria)),
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (update_securities_cmd, _state.security_criteria)),
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.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)
};
_subscriptions = _bus.subscribe(updated_security_criteria_tag).ObserveOnDispatcher().Subscribe(update_criteria);
_subscriptions = _bus.subscribe(update_securities_tag).ObserveOnDispatcher().Subscribe(update_criteria);
_initialize();
bool always_true<T>(T _) => true;
@@ -16,7 +16,7 @@
<ResourceDictionary Source="/Modules/Spreader/ParameterSectionView.xaml"/>
</ResourceDictionary.MergedDictionaries>
<sys:Double x:Key="GroupLevelIndent">18</sys:Double>
<sys:Double x:Key="RowHeaderWidth">265</sys:Double>
<sys:Double x:Key="RowHeaderWidth">285</sys:Double>
<sys:Double x:Key="RowHeight">16</sys:Double>
<sys:Double x:Key="NegativeRowHeight">-16</sys:Double>
<sys:Double x:Key="ItemsHostCanBeDisabledCollapsedHeight">15</sys:Double>
@@ -104,7 +104,7 @@
</usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
<usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
<DataTemplate>
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}"/>
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}" Increment="{Binding tick_increment, FallbackValue={u:Double 1}}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
<usp:DataGridParameterCellTemplateConverter.IntTemplate>
@@ -299,6 +299,7 @@
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}}"
>
<msp:ContractPickerView
CloseOnOK="False" CloseOnCancel="False"
@@ -317,7 +318,7 @@
Command="{Binding RemoveLegCommand, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}}"
CommandParameter="{Binding}" Style="{StaticResource RoundCloseButton}"
/>
<TextBlock Grid.Column="1" Margin="4,0,0,0" Text="{Binding}"/>
<TextBlock Grid.Column="1" Margin="4,0,0,0" Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
</Grid>
</DataTemplate>
@@ -30,7 +30,7 @@ namespace mbts.ui.spark {
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExecutionStrategyEditor), new FrameworkPropertyMetadata(typeof(ExecutionStrategyEditor)));
public ExecutionStrategyEditor() =>
new ExecutionStrategyErrorAdorner(this);
AdornerHelper.attach_continuously_on_loaded(() => new ExecutionStrategyErrorAdorner(this), this);
#region SyntheticName
public static readonly DependencyProperty SyntheticNameProperty = DependencyProperty.Register("SyntheticName", typeof(string), typeof(ExecutionStrategyEditor),
@@ -52,8 +52,43 @@ namespace mbts.ui.spark {
#endregion
#region IsNew
public static readonly DependencyProperty IsNewProperty = DependencyProperty.Register(nameof(IsNew), typeof(bool), typeof(ExecutionStrategyEditor), new PropertyMetadata(false));
public bool IsNew { get => (bool)GetValue(IsNewProperty); set => SetValue(IsNewProperty, value); }
public static readonly DependencyProperty IsNewProperty =
DependencyProperty.Register(
nameof(IsNew),
typeof(bool),
typeof(ExecutionStrategyEditor),
new PropertyMetadata(false, OnIsNewChanged));
static void OnIsNewChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
d.CoerceValue(CanUserAddNewLegProperty);
public bool IsNew {
get => (bool)GetValue(IsNewProperty);
set => SetValue(IsNewProperty, value);
}
#endregion
#region CanUserAddNewLeg
public static readonly DependencyProperty CanUserAddNewLegProperty =
DependencyProperty.Register(
nameof(CanUserAddNewLeg),
typeof(bool),
typeof(ExecutionStrategyEditor),
new PropertyMetadata(false, null, CoerceCanUserAddNewLeg));
static object CoerceCanUserAddNewLeg(DependencyObject d, object baseValue) {
var value = (bool)baseValue;
if (value) {
if (!((ExecutionStrategyEditor)d).IsNew)
return false;
}
return value;
}
public bool CanUserAddNewLeg {
get => (bool)GetValue(CanUserAddNewLegProperty);
set => SetValue(CanUserAddNewLegProperty, value);
}
#endregion
#region IsNewLegPopupOpen
@@ -162,7 +197,7 @@ namespace mbts.ui.spark {
var legs = LegTuples;
dg.Columns.SetItems(legs.Select(_column_factory));
if (IsNew) dg.Columns.Add((DataGridColumn)FindResource("AddColumnPlaceholder"));
_dg_line_adorner = new ExecutionStrategyGridLineAdorner(dg);
_dg_line_adorner = AdornerHelper.attach_continuously_on_loaded(() => new ExecutionStrategyGridLineAdorner(dg), dg);
_legs_collection_changed_sub = (legs as INotifyCollectionChanged)?.Subscribe((s, e) => dg.Columns.ReflectChange(legs, _column_factory, e));
}
}
@@ -174,8 +209,7 @@ namespace mbts.ui.spark {
return col;
}
ExecutionStrategyGridLineAdorner _dg_line_adorner;
IDisposable _legs_collection_changed_sub;
IDisposable _legs_collection_changed_sub, _dg_line_adorner;
private class DataGridColumnInternal : DataGridDynamicColumn {
readonly IValueConverter _cell_template_converter, _cell_editing_template_converter;
@@ -231,7 +265,7 @@ namespace mbts.ui.spark {
if (_vm != null && _vm.GetErrors(nameof(SyntheticEditorVM.tick_factors)).Cast<string>().Any())
this.draw_border(drawing_context, _tick_combo_box.value, null, new Pen(Brushes.Red, 1));
string error_text = string.Join(", ", _vm.AllErrors);
this.draw_relative_text(RelativePosition.Below, error_text, drawing_context, _name_text_box.value, Brushes.OrangeRed, 0, y_offset);
this.draw_relative_text(RelativePosition.Below, error_text, drawing_context, _name_text_box.value, BTSColors.header_error_text_brush, 0, y_offset);
}
}
}
@@ -127,6 +127,10 @@ namespace bts.shell {
};
public static PostHedgePriceBasedForcedCross PostHedgePriceBasedForcedCross => new PostHedgePriceBasedForcedCross();
public static PostHedgeTimeBasedForcedCross PostHedgeTimeBasedForcedCross => new PostHedgeTimeBasedForcedCross();
public static PostHedgeOppositeMarketVolumeBasedForcedCross PostHedgeOppositeMarketVolumeBasedForcedCross =>
new PostHedgeOppositeMarketVolumeBasedForcedCross {
QuantityThreshold = OrderDataHelper.Defaults.QuantityMeasure
};
#endregion
public static SyntheticContract SyntheticContract(string name, IEnumerable<ContractLeg> legs) {
@@ -25,6 +25,7 @@
TickFactors="{Binding tick_factors}"
AvailableTickFactors="{Binding available_tick_factors_view}"
IsNew="{Binding is_new}"
CanUserAddNewLeg="{Binding can_add_leg}"
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
PriceCalc="{Binding price_calc}"
ParameterRows="{Binding rows}"
@@ -6,9 +6,7 @@ namespace mbts.ui.spark {
/// Interaction logic for SyntheticManagementView.xaml
/// </summary>
internal partial class SyntheticManagementView : UserControl {
public SyntheticManagementView() {
public SyntheticManagementView() =>
InitializeComponent();
DataGridFilterHelper.set_filter_key_event_hooks(this);
}
}
}
@@ -13,6 +13,7 @@ using static bts.shell.SyntheticManagementWindowFactory;
namespace mbts.spark {
class SyntheticManagementWindow : SparkWindow<StartupData> {
const string _window_type = "SyntheticManagement";
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
StartupData _state;
OrdersModel _orders_model;
@@ -60,6 +61,7 @@ namespace mbts.spark {
else _view.DataContext = _vm.value;
}
public override string window_type => _window_type;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands { get; }
public override StartupData SaveState => _state = new StartupData(_vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
@@ -10,6 +10,15 @@ namespace mbts.spark {
public override void set_value(LegSpreaderParamsTuple leg_tup, double value) =>
leg_tup.leg_params.QuoteParams.LeanQuantityRatio = value;
public override ICell create_cell(LegSpreaderParamsTuple leg_tup) => new HedgeRatioCell(this, leg_tup);
class HedgeRatioCell : Cell<double> {
public HedgeRatioCell(RowModelBase<double> model, LegSpreaderParamsTuple leg_tup)
: base(model, leg_tup) { }
public double tick_increment => 0.1;
}
}
}
}
@@ -0,0 +1,39 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public override string display_name { get; } = "Cross Ticks";
public override ParameterSection[] section_path { get; } = new[] { Sections.PostHedge, Subsections.OppositeMarketVolumeBasedCross };
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
_get_omv_cross(leg_tup).CrossTicks = _value_from_store(leg_tup);
public override uint get_value(LegSpreaderParamsTuple leg_tup) =>
_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross ?
omv_cross.CrossTicks :
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
if (_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross)
omv_cross.CrossTicks = value;
}
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
_get_omv_cross(leg_tup) != null;
PostHedgeOppositeMarketVolumeBasedForcedCross _get_omv_cross(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.CrossTicks);
}
}
}
@@ -0,0 +1,36 @@
using bts.shell;
using mbts.ui.spark;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeOppositeMarketVolumeBasedCrossEnabledModel : RowModelBase<bool> {
readonly PostHedgeOppositeMarketVolumeBasedCrossThresholdModel _threshold;
readonly PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel _ticks;
public PostHedgeOppositeMarketVolumeBasedCrossEnabledModel(PostHedgeOppositeMarketVolumeBasedCrossThresholdModel threshold, PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel ticks) {
_threshold = threshold;
_ticks = ticks;
}
public override string display_name { get; } = string.Empty;
public override ParameterSection[] section_path { get; } = new[] { Sections.PostHedge, Subsections.OppositeMarketVolumeBasedCross };
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross != null;
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
if (value) {
if (leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross == null) {
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross = SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross;
_threshold.apply_value(leg_tup);
_ticks.apply_value(leg_tup);
}
} else {
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross = null;
}
_threshold.set_enabled(leg_tup, value);
_ticks.set_enabled(leg_tup, value);
}
}
}
}
@@ -0,0 +1,41 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeOppositeMarketVolumeBasedCrossThresholdModel : RowModelBase<QuantityMeasure> {
IDictionary<uint, QuantityMeasure> _value_store = new Dictionary<uint, QuantityMeasure>();
public override string display_name { get; } = "Threshold";
public override ParameterSection[] section_path { get; } = new[] { Sections.PostHedge, Subsections.OppositeMarketVolumeBasedCross };
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
_get_omv_cross(leg_tup).QuantityThreshold = _value_from_store(leg_tup);
public override QuantityMeasure get_value(LegSpreaderParamsTuple leg_tup) =>
_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross ?
omv_cross.QuantityThreshold :
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, QuantityMeasure value) {
_value_store[leg_tup.SecurityKey] = value;
if (_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross)
omv_cross.QuantityThreshold = value;
}
public override ICell create_cell(LegSpreaderParamsTuple leg_tup) => new QuantityMeasureCell(this, leg_tup);
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
_get_omv_cross(leg_tup) != null;
PostHedgeOppositeMarketVolumeBasedForcedCross _get_omv_cross(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross;
QuantityMeasure _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.QuantityThreshold);
}
}
}
@@ -123,6 +123,12 @@ namespace mbts.spark {
"forces legged hedge orders to automatically cross the market, if they are still " +
"working beyond a certain time period.If a hedge order is legged for 'Time(ms)', " +
"it is re-priced to cross the opposite inside market by 'Cross Ticks.'");
public static ParameterSection OppositeMarketVolumeBasedCross =>
new ParameterSection("Size Based Cross", true,
"forces legged hedging orders to automatically cross the market if the volume " +
"of the opposite inside market drops below the threhold. If opposite inside market volume drops below 'Threshold,' " +
"it is re-priced to cross the opposite inside market by 'Cross Ticks.'");
}
}
}
@@ -27,7 +27,7 @@ namespace mbts.spark {
readonly QuantityModel _quantity_model;
readonly PriceMultiplierModel _price_mult_model;
readonly List<IDisposable> _resources = new List<IDisposable>();
readonly Property<bool> _has_working_orders;
readonly Property<bool> _has_working_orders, _can_add_leg;
Security _synthetic;
bool _set_name_automatically, _set_ticks_automatically, _set_quantities_automatically, _set_price_mults_automatically, _is_setting_quantities, _is_setting_price_mults;
TickFactors _tick_factors;
@@ -173,6 +173,9 @@ namespace mbts.spark {
}
_is_setting_price_mults = false;
};
_can_add_leg = backing(nameof(can_add_leg), leg_tuples.ObserveSelf().Select(_ => leg_count < 2), leg_count < 2);
} else {
_can_add_leg = backing(nameof(can_add_leg), false);
}
var quote_throttle_outside_ticks = new QuoteThrottleOutsideTicksModel();
var quote_throttle_inside_ticks = new QuoteThrottleInsideTicksModel();
@@ -191,6 +194,9 @@ namespace mbts.spark {
var post_hedge_time_based_cross_ticks = new PostHedgeTimeBasedCrossMarketTicksModel();
var post_hedge_time_based_cross_time = new PostHedgeTimeBasedCrossTimeModel();
var post_hedge_opposite_market_based_cross_threshold = new PostHedgeOppositeMarketVolumeBasedCrossThresholdModel();
var post_hedge_opposite_market_based_cross_cross_ticks = new PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel();
rows = new IRowModel[] {
_quantity_model,
_price_mult_model,
@@ -214,7 +220,10 @@ namespace mbts.spark {
post_hedge_price_based_cross_market_ticks,
new PostHedgeTimeBasedCrossEnabledModel(post_hedge_time_based_cross_time, post_hedge_time_based_cross_ticks),
post_hedge_time_based_cross_time,
post_hedge_time_based_cross_ticks
post_hedge_time_based_cross_ticks,
new PostHedgeOppositeMarketVolumeBasedCrossEnabledModel(post_hedge_opposite_market_based_cross_threshold, post_hedge_opposite_market_based_cross_cross_ticks),
post_hedge_opposite_market_based_cross_threshold,
post_hedge_opposite_market_based_cross_cross_ticks
}.Select(m => new RowVM(m, leg_tuples)).ToList();
set_synthetic(synthetic, strategy);
}
@@ -243,6 +252,7 @@ namespace mbts.spark {
public bool has_working_orders => _has_working_orders.Value;
public bool is_new { get; }
public bool is_new_leg_popup_open { get; set; }
public bool can_add_leg => _can_add_leg.Value;
public SyntheticContract.Types.PriceCalc price_calc { get => _synthetic.Synthetic.PriceCalc; set => _synthetic.Synthetic.PriceCalc = value; }
public ObservableCollection<LegSpreaderParamsTuple> leg_tuples { get; } = new ObservableCollection<LegSpreaderParamsTuple>();
public int leg_count => leg_tuples.Count;
@@ -291,7 +301,7 @@ namespace mbts.spark {
}
void _deduce_name() {
_synthetic.Synthetic.Name = SecurityFormatter.FormatSyntheticDesc(leg_tuples.Select(l => l.leg));
_synthetic.Synthetic.Name = SecurityFormatter.format_contract_legs(leg_tuples.Select(l => new ContractLegImmutable(l.leg)));
RaisePropertyChanged(nameof(name));
}
@@ -8,7 +8,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Input;
using static bts.api.SecurityFormatter;
using static bts.shell.SyntheticManagementWindowFactory;
namespace mbts.spark {
@@ -70,7 +69,7 @@ namespace mbts.spark {
).ObserveOnDispatcher());
name = security.Synthetic.Name;
n_legs = security.Synthetic.Legs.Count();
description = FormatSyntheticDesc(security);
description = SecurityFormatter.format_contract_legs(security.Synthetic.Legs);
show_ladder = new DelegateCommand(_show_ladder);
show_order_book = new DelegateCommand(_show_order_book);
}
@@ -1,20 +1,14 @@
using mbts.ui;
using mbts.ui.controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace bts.shell
{
public static class ActivityLogCategory
{
namespace bts.shell {
public static class ActivityLogCategory {
public static ILogViewerCategory order = new LogViewerCategory("Order", BTSColors.Orders.sent_brush);
public static ILogViewerCategory fill = new LogViewerCategory("Fill", BTSColors.Orders.filled_dark);
public static ILogViewerCategory cancel => new LogViewerCategory("Cancel", BTSColors.Orders.working_cancel);
public static ILogViewerCategory modify = new LogViewerCategory("Modify", BTSColors.Orders.modified);
public static ILogViewerCategory fill = new LogViewerCategory("Fill", BTSColors.Orders.filled_dark_brush);
public static ILogViewerCategory cancel = new LogViewerCategory("Cancel", BTSColors.Orders.working_cancel_brush);
public static ILogViewerCategory modify = new LogViewerCategory("Modify", BTSColors.Orders.modified_brush);
public static ILogViewerCategory warn = new LogViewerCategory("Warn", Brushes.Gold);
public static IEnumerable<ILogViewerCategory> all_categories {
@@ -1,6 +1,7 @@
using bts.api;
using bts.spark;
using mbts;
using mbts.ui;
using mbts.ui.controls;
using System;
using System.Collections.Generic;
@@ -13,27 +14,36 @@ using System.Windows.Media;
namespace bts.shell
{
class ActivityLogWindow : SparkWindow<ActivityWindowState> {
class ActivityLogWindow : SparkWindow<ActivityWindowState>, ISharedWindow {
const string _window_type = "ActivityLog";
readonly IObservable<LogViewerMessage> _message_src;
readonly LogViewerMessageCollection _messages = new LogViewerMessageCollection();
IDisposable _dispose;
LogViewerMessageCollection _messages = new LogViewerMessageCollection();
Lazy<UIElement> _content;
LogViewer _content;
public ActivityLogWindow(IObservable<LogViewerMessage> messages, IBus bus, ActivityWindowFactory isw)
: base("Activity Log", bus, isw, SparkCommands.ShowActivityLog.icon) {
_dispose = messages.ObserveOnDispatcher().Subscribe(_messages.AddLog);
_content = new Lazy<UIElement>(() =>
new LogViewer {
ItemsSource = _messages,
Categories = ActivityLogCategory.all_categories }
);
_message_src = messages;
}
public override string window_type => _window_type;
public override ActivityWindowState SaveState => new ActivityWindowState();
public override UIElement Content => _content.Value;
public override UIElement Content => _content;
public string thread_id => nameof(ActivityLogWindow);
public override void Dispose() {
Disposable.dispose(ref _dispose);
}
public void thread_init() {
_dispose = _message_src.ObserveOnDispatcher().Subscribe(_messages.AddLog);
_content =
new LogViewer {
ItemsSource = _messages,
Categories = ActivityLogCategory.all_categories
};
}
}
}
@@ -1,5 +1,4 @@
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.spark;
using mbts.spark.filters;
@@ -9,38 +8,34 @@ using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Data;
using static bts.shell.FillsWindowFactory;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.FillsView;
using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
namespace mbts.spark {
class FillsVM : SecurityGrid<FillsVM.Row, FillTuple> {
readonly OrdersModel _model;
readonly uint[] _orders;
public FillsVM(IBus bus, OrdersModel model, StartupData state)
: this(bus, model, _get_observable(bus, model, state), state.sorting_criteria, state.filters) { }
public FillsVM(IBus bus, OrdersModel model, uint[] orders, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
: this(bus, model, orders != null && orders.Length > 0 ? model.on_order_fill_tuples(orders) : model.on_fill_tuples, sorting_criteria, filters) { }
public FillsVM(IBus bus, OrdersModel model, IObservable<TableUpdate<FillTuple, string>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
FillsVM(IBus bus, OrdersModel model, IObservable<ITableUpdateTransform<FillTuple, string>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
: base(RXCollection.CreateTableUpdate(fills_source), sorting_criteria) {
_model = model;
_orders = orders;
account_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.accounts, r => r.account, nameof(Row.account));
user_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.users, r => r.user, nameof(Row.user));
var securities_source = fills_source
.Select(t => new HashSet<SecurityImmutable>(
t.state.Values
.Where(ft => ft.security.HasValue)
.Select(ft => ft.security.Value))
);
account_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_accounts(), r => r.account, nameof(Row.account));
user_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_users(), r => r.user, nameof(Row.user));
security_filter = DiscreteValuesFilterVM.Create<Row, SecurityImmutable, SecurityCriteria>(
securities_source,
fills_source.with_respect_to(ft => ft.security ?? SecurityDataHelper.Defaults.SecurityImmutable, s => s.Id, ft => ft.key).Select(t => (t.added, t.removed)),
r => r.security,
SecurityCriteria.from_securities,
SecurityCriteria.get_to_securities(bus),
nameof(Row.security),
EqualityComparer.Create<SecurityImmutable, uint>(s => s.Id),
SecurityDataHelper.EqualityComparer.IDComparer,
Comparer<SecurityImmutable>.Default
);
if (filters != null) {
@@ -57,15 +52,45 @@ namespace mbts.spark {
fill_key_filter.load_state(filters.fill_key);
order_key_filter.load_state(filters.order_key);
security_key_filter.load_state(filters.security_key);
} else {
fill_time_filter.load_state(
new TimeFilterTuple(ui.controls.TimeFilterMode.CMEOpen,
TimeTextTuple.FromDateTime(DateTime.Today),
TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1))
)
);
}
// columns
add_col(Columns.Security, DefaultColumnHeaders.Security, r => r.security.ToString(), nameof(Row.security), security_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Security);
add_col(Columns.FillTime, DefaultColumnHeaders.FillTime, r => r.fill_time.ToString("M/d h:mm:ss tt"), nameof(Row.fill_time), fill_time_filter, DataGridFilterKnownTemplates.TimeFilter, DefaultColumnWidths.Time);
add_col(Columns.User, DefaultColumnHeaders.User, r => r.user, nameof(Row.user), user_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.User);
add_col(Columns.Account, DefaultColumnHeaders.Account, r => r.account, nameof(Row.account), account_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Account);
add_col(Columns.Exchange, DefaultColumnHeaders.Exchange, r => r.exchange.ToString(), nameof(Row.exchange), exchange_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Exchange);
add_col(Columns.TimeInForce, DefaultColumnHeaders.TimeInForce, r => r.time_in_force.ToString(), nameof(Row.time_in_force), time_in_force_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.TimeInForce);
columns.Add(new OrderTypeColumn<Columns>(Columns.OrderType, new Binding(nameof(Row.order_type)), new Binding(nameof(Row.order_metadata)), order_type_filter) { sort_member_path = nameof(Row.order_type) });
columns.Add(new SideColumn<Row, Columns>(Columns.Side, r => r.side, side_filter) { sort_member_path = nameof(Row.side) });
add_col(Columns.Size, DefaultColumnHeaders.Size, r => r.size.ToString(), nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.Size);
add_col(Columns.Price, DefaultColumnHeaders.Price, r => r.format_price(r.price), nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price);
add_col(Columns.FillKey, DefaultColumnHeaders.FillKey, r => r.fill_key.ToString(), nameof(Row.fill_key), fill_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.FillKey);
add_col(Columns.OrderKey, DefaultColumnHeaders.OrderKey, r => r.order_key.ToString(), nameof(Row.order_key), order_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.OrderKey);
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
//
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, IDataGridColumnFilter filter, string filter_template, double width) =>
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
Header = header,
sort_member_path = prop,
Width = width
});
//
_resources.Add(DataGridFilterHelper.hook_filters(rows_view,
fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter,
security_filter, side_filter, price_filter, size_filter, fill_key_filter, order_key_filter, security_key_filter
));
}
public uint[] orders => _orders;
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
#region Filters
public IDataGridColumnPersistentFilter<Row, TimeFilterTuple> fill_time_filter { get; } = new TimeFilterVM<Row>(r => r.fill_time, nameof(Row.fill_time));
public IDataGridColumnPersistentFilter<Row, TimeInForce[]> time_in_force_filter { get; } = DiscreteValuesFilterVM.Create<Row, TimeInForce>(EnumHelper.get_values<TimeInForce>().ToLArray(), r => r.time_in_force, nameof(Row.time_in_force));
public IDataGridColumnPersistentFilter<Row, OrderType[]> order_type_filter { get; } = DiscreteValuesFilterVM.Create<Row, OrderType>(EnumHelper.get_values<OrderType>().ToLArray(), r => r.order_type, nameof(Row.order_type));
@@ -96,11 +121,19 @@ namespace mbts.spark {
order_key = order_key_filter.get_state(),
security_key = security_key_filter.get_state()
};
#endregion
protected override Row row_factory(FillTuple row_model) => new Row(row_model, _model);
protected override IEnumerable<SortingCriteria> get_default_sorting() { yield return new SortingCriteria(nameof(Row.fill_time), false); }
public class Row : SecurityRow, IEquatable<Row> {
static IObservable<ITableUpdateTransform<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, StartupData state) {
if (state.orders != null && state.orders.Length == 0) {
var orders_hash = new HashSet<uint>(state.orders);
return model.on_fill_tuples.filter(f => orders_hash.Contains(f.fill.OrderKey));
} else return model.on_fill_tuples;
}
public class Row : SecurityRow, IEquatable<Row>, IRowVM {
readonly OrdersModel _model;
readonly FillImmutable _fill;
readonly OrderRecordImmutable _order;
@@ -116,7 +149,7 @@ namespace mbts.spark {
public OrderType order_type => _order.Def.CustomDef.CustomCase;
public OrderMetadata order_metadata => _model.factory.get_metadata(_order.Def.CustomDef.CustomCase);
public string account => _order.Def.Account;
public string user => _order.Def.Login;
public string user => _order.Def.User;
public Venue exchange => security.Exchange;
public Side side => _fill.Side;
public double price => _fill.LastPrice;
@@ -127,6 +160,9 @@ namespace mbts.spark {
public bool Equals(Row other) => other != null && other._fill.Equals(_fill) && other._order.Equals(_order);
public override bool Equals(object obj) => Equals(obj as Row);
public override int GetHashCode() => _fill.FillKey.GetHashCode() ^ _order.Key.GetHashCode();
public event Action invalidated;
void _raise_invalidated() => invalidated?.Invoke();
}
}
}
}
@@ -1,121 +1,47 @@
<UserControl x:Class="mbts.ui.spark.FillsView" 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:usp="clr-namespace:mbts.ui.spark"
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters">
<UserControl
x:Class="mbts.ui.spark.FillsView"
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:usp="clr-namespace:mbts.ui.spark"
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
FocusManager.IsFocusScope="True"
Background="{StaticResource CommonBackground}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
<Style x:Key="SideColorCodedTextElement" TargetType="TextBlock">
<Setter Property="Foreground" Value="{u:BTSColor MarketData.bid}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding side}" Value="Sell">
<Setter Property="Foreground" Value="{u:BTSColor MarketData.ask}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="OrderTypeElement" TargetType="usp:OrderTypeItem">
<Setter Property="OrderType" Value="{Binding order_type}"/>
<Setter Property="Metadata" Value="{Binding order_metadata}"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<DataGrid x:Name="grid" ItemsSource="{Binding rows_view}" IsReadOnly="True" CanUserSortColumns="True" GridLinesVisibility="None"
CurrentItem="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
u:DataGridBehavior.NotifyColumnsOfSorting="True" BorderThickness="0">
<DataGrid.Columns>
<DataGridTextColumn Header="Security" Binding="{Binding security}"
c:DataGridEx.CellKey="{u:Enum Security, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.security_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
<DataGridTextColumn Header="Fill Time" Binding="{Binding fill_time, StringFormat='M/d h:mm:ss tt'}"
c:DataGridEx.CellKey="{u:Enum FillTime, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.fill_time_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.fill_time_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.fill_time_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource TimeFilterTemplate}"/>
<DataGridTextColumn Header="User" Binding="{Binding user}"
c:DataGridEx.CellKey="{u:Enum User, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.user_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.user_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.user_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
<DataGridTextColumn Header="Account" Binding="{Binding account}"
c:DataGridEx.CellKey="{u:Enum Account, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.account_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.account_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.account_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
<DataGridTextColumn Header="Exchange" Binding="{Binding exchange}"
c:DataGridEx.CellKey="{u:Enum Exchange, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.exchange_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.exchange_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.exchange_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
<DataGridTextColumn Header="TIF" Binding="{Binding time_in_force}"
c:DataGridEx.CellKey="{u:Enum TimeInForce, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding time_in_force_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.time_in_force_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.time_in_force_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
<usp:DataGridOrderTypeColumn Header="Order Type" ElementStyle="{StaticResource OrderTypeElement}" SortMemberPath="order_type"
c:DataGridEx.CellKey="{u:Enum OrderType, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.order_type_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.order_type_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.order_type_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
<DataGridTextColumn Header="B/S" ElementStyle="{StaticResource SideColorCodedTextElement}" Binding="{Binding side}"
c:DataGridEx.CellKey="{u:Enum Side, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.side_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.side_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.side_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
<DataGridTextColumn Header="Qty" Binding="{Binding size}"
c:DataGridEx.CellKey="{u:Enum Size, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.size_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.size_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.size_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource UIntMinMaxTemplate}"/>
<DataGridTextColumn Header="Price" SortMemberPath="price"
c:DataGridEx.CellKey="{u:Enum Price, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.price_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.price_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.price_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="price"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Fill Key" Binding="{Binding fill_key}"
c:DataGridEx.CellKey="{u:Enum FillKey, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.fill_key_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.fill_key_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.fill_key_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
<DataGridTextColumn Header="Order Key" Binding="{Binding order_key}"
c:DataGridEx.CellKey="{u:Enum OrderKey, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.order_key_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.order_key_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.order_key_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
<DataGridTextColumn Header="Security Key" Width="*" Binding="{Binding security_key}"
c:DataGridEx.CellKey="{u:Enum SecurityKey, Type={u:Nested usp:FillsView.Columns}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.security_key_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_key_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_key_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
<AdornerDecorator>
<Grid>
<c:DIYGrid
x:Name="_grid"
Columns="{Binding columns}"
CurrentRow="{Binding current_row}"
Rows="{Binding rows_view}"
>
<c:DIYGrid.InputBindings>
<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
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
/>
</c:DIYGrid.CommandBindings>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<usp:FillsRowView/>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -1,4 +1,5 @@
using mbts.spark.filters;
using mbts.spark;
using mbts.ui.controls;
using System.Windows.Controls;
namespace mbts.ui.spark {
@@ -8,9 +9,9 @@ namespace mbts.ui.spark {
partial class FillsView : UserControl {
public enum Columns { FillTime, User, Account, Exchange, Security, TimeInForce, OrderType, Side, Price, Size, FillKey, OrderKey, SecurityKey, ExchangeOrderID }
public FillsView() {
public FillsView() =>
InitializeComponent();
DataGridFilterHelper.set_filter_key_event_hooks(this);
}
}
class FillsRowView : DIYGrid.HighPerformanceRow<FillsVM.Row, FillsView.Columns> { }
}
@@ -5,35 +5,35 @@ using mbts.ui;
using mbts.ui.spark;
using mbts.utils;
using System.Windows;
using StartupData = bts.shell.FillsWindowFactory.StartupData;
namespace mbts.spark
{
class FillsWindow : SparkWindow<FillsWindowFactory.StartupData> {
namespace mbts.spark {
class FillsWindow : SparkWindow<StartupData>, ISharedWindow {
const string _window_type = "Fills", _thread_id = "Fills";
readonly AllowOnce _init_guard = new AllowOnce("Fills");
readonly OrdersModel _orders_model;
SingleValuePump<FillsVM> _vm = new SingleValuePump<FillsVM>();
FillsWindowFactory.StartupData _state;
FillsVM _vm;
FillsView _view;
StartupData _state;
public static FillsWindow create(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, FillsWindowFactory.StartupData state) {
var window = new FillsWindow(factory, bus, orders_model);
window.initialize(state);
return window;
}
FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model) : base("Fills", bus, factory, SparkCommands.ShowFills.icon) =>
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state)
: base("Fills", bus, factory, SparkCommands.ShowFills.icon) {
_orders_model = orders_model;
public void initialize(FillsWindowFactory.StartupData state) {
Logger.notnull(state);
_state = state;
_vm.value = new FillsVM(_bus,_orders_model, state.orders, state.sorting_criteria, state.filters);
if (_view == null) _view = new FillsView { DataContext = _vm.value };
else _view.DataContext = _vm.value;
}
public override string window_type => _window_type;
public override UIElement Content => _view;
public override FillsWindowFactory.StartupData SaveState => _state = new FillsWindowFactory.StartupData(_vm.value.orders, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filters());
public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.GetSortingCriteria(), _vm.get_filters());
public override double MinWidth => 1050 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
public string thread_id => _thread_id;
public void thread_init() {
_init_guard.check_access();
Logger.notnull(_state);
_vm = new FillsVM(_bus, _orders_model, _state);
_view = new FillsView { DataContext = _vm };
}
public override void Dispose() => Disposable.dispose(ref _vm);
}
@@ -22,7 +22,7 @@ namespace bts.shell {
}
public override Task<IShellWindow> load(StartupData state) =>
Task.FromResult<IShellWindow>(FillsWindow.create(this, _bus, _orders_model, state));
Task.FromResult<IShellWindow>(new FillsWindow(this, _bus, _orders_model, state));
public async void show(StartupData state) =>
_bus.post(SparkTags.ShowWindow, await load(state));
@@ -1,5 +1,7 @@
using bts.api;
using bts.api;
using mbts;
using mbts.config;
using mbts.persist;
using mbts.spark;
using mbts.ui;
using ProtoBuf;
@@ -7,69 +9,112 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using LadderSettings = mbts.spark.LadderSettings;
namespace bts.shell {
class LadderWindowFactory : ShellWindowFactory<LadderSaveState> {
const string _window_type = "ladder";
readonly IBus _bus;
readonly IWindowManager _wm;
readonly UserSettingsHub _settings_hub;
readonly OrdersModel _orders_model;
readonly int _max_ladders_open;
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, OrdersModel orders_model)
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model)
: base(owner, "SparkLadder") {
_bus = bus;
_wm = wm;
_settings_hub = settings_hub;
_orders_model = orders_model;
var window_props = Configuration.get<bts.config.IShellWindowProps>();
_max_ladders_open = (int)(window_props?.ladder?.max_total_open ?? 75);
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
bus.subscribe(SparkTags.ShowLadderTag).ObserveOnDispatcher().Subscribe(show_window),
bus.subscribe(SparkTags.ShowLinkedLadder).ObserveOnDispatcher().Subscribe(show_linked)
bus.subscribe(SparkTags.ShowLadderTag).ObserveOnDispatcher().Subscribe(s => show_windows(new[] { s })),
bus.subscribe(SparkTags.ShowLaddersTag).ObserveOnDispatcher().Subscribe(show_windows)
);
}
public async override Task<IShellWindow> load(LadderSaveState state) {
await Wait.until_true(() => _securities.ContainsKey(state.security_key), TimeSpan.FromSeconds(15));
if (_securities.TryGetValue(state.security_key, out SecurityMD security)) {
return create_ladder_window(security, Observable.Return(state.settings));
return create_ladder_window(security, state.settings);
}
return null;
}
static LadderSaveState _get_state(LadderVM vm) =>
new LadderSaveState { security_key = vm.security.Id, settings = vm.current_settings };
new LadderSaveState { security_key = vm.security.Id, settings = vm.get_save_state() };
IShellWindow create_ladder_window(SecurityMD md, IObservable<mbts.spark.LadderSettings> settings) {
var model = new LadderModel(md, _orders_model);
var vm = new LadderVM(_bus, model, settings);
var view = new LadderView { DataContext = vm };
var state = new LadderSaveState { security_key = md.security.Id, settings = vm.current_settings };
return ShellWindow.create(module, md.security.ToString(), view, vm, s => save(_get_state(vm), s), Icons.Ladder);
int _ladder_count = 0;
IShellWindow create_ladder_window(SecurityMD md, LadderSettings state_settings) {
var id = Interlocked.Increment(ref _ladder_count) % Environment.ProcessorCount;
var view = new Lazy<UIElement>(() => {
var model = new LadderModel(md, _orders_model);
var settings_manager = new WindowSettingsManager<LadderSettings>(
_settings_hub, state_settings,
us => sanitize_ladder_settings(us.ladder),
(us, v) => us.with(sanitize_ladder_settings(v)));
var vm = new LadderVM(_bus, model, settings_manager);
return new LadderView(vm);
});
string thread = Configuration.get<bts.config.toggles.LaddersOnThreads>().enabled ? $"Ladder{id:00}" : "";
return ShellWindow.create(
module, md.security.ToString(), _window_type, view,
(v,s) => save(_get_state(((LadderView)v).vm), s),
icon: Icons.Ladder,
thread_id: thread);
// if there are new props, make sure they are not null, invalid, etc.
LadderSettings sanitize_ladder_settings(LadderSettings s) {
// these MarketBreakLines are new settings props, ensure they are not null
if (s.bid_break_line == null)
s.bid_break_line = LadderSettings.Default.bid_break_line;
if (s.ask_break_line == null)
s.ask_break_line = LadderSettings.Default.ask_break_line;
if (s.mid_break_line == null)
s.mid_break_line = LadderSettings.Default.mid_break_line;
return s;
}
}
void show_window(SecurityMD smd) =>
_wm.Windows.Add(create_ladder_window(smd, Observable.Return(_orders_model.factory.default_ladder_settings(smd.security))));
void show_windows(IReadOnlyCollection<SecurityMD> securities) {
int n_allowed = _max_ladders_open - _wm.list_windows().Where(w => w.window_type == _window_type).Count();
int n_opened = 0;
foreach (var s in securities) {
if (++n_opened > n_allowed) {
BTSMessageBox.Show($"The maximum number of ladders are already opened ({_max_ladders_open}).", "Ladders",
BTSMessageBoxButtons.OK, MessageBoxImage.Exclamation, true, false, true);
break;
} else {
_bus.post(SparkTags.ShowWindow, create_ladder_window(s, null));
}
}
}
// TODO: whenever we re-implement the linked ladder, need to add some field to the LadderSaveState to indicate the owning window
void show_linked(IObservable<SecurityMD> keystream) {
var view = new LadderView();
IDisposable sub = null;
var window =
ShellWindow.create(module, "loading", view,
Disposable.Create(() => {
(view.DataContext as LadderVM)?.Dispose();
sub?.Dispose();
}), s => {
var vm = view.DataContext as LadderVM;
if (vm != null) save(_get_state(vm), s);
}, Icons.Ladder);
sub =
keystream
.Select(smd => new LadderVM(_bus, new LadderModel(smd, _orders_model),
Observable.Return(_orders_model.factory.default_ladder_settings(smd.security)))
).Aggregate((old, vm) => { old.Dispose(); return vm; })
.Subscribe(vm => { view.DataContext = vm; window.Title = vm.security.ToString(); });
}
//void show_linked(IObservable<SecurityMD> keystream) {
// var view = new LadderView();
// IDisposable sub = null;
// var window =
// ShellWindow.create(module, "loading", view,
// Disposable.Create(() => {
// (view.DataContext as LadderVM)?.Dispose();
// sub?.Dispose();
// }), s => {
// var vm = view.DataContext as LadderVM;
// if (vm != null) save(_get_state(vm), s);
// }, Icons.Ladder);
// sub =
// keystream
// .Select(smd => new LadderVM(_bus, new LadderModel(smd, _orders_model),
// Observable.Return(_orders_model.factory.default_ladder_settings(smd.security)))
// ).Aggregate((old, vm) => { old.Dispose(); return vm; })
// .Subscribe(vm => { view.DataContext = vm; window.Title = vm.security.ToString(); });
//}
}
[ProtoContract]
@@ -1,5 +1,6 @@
using bts.api;
using bts.spark;
using mbts.persist;
using mbts.spark;
using mbts.ui;
using System.Collections.Generic;
@@ -9,9 +10,7 @@ using System.Threading.Tasks;
using System.Windows.Media;
using static mbts.ui.shell.ProtoBufSerializationHelper;
namespace bts.shell
{
namespace bts.shell {
[Export(typeof(IShellModule))]
class TradingShellModule : IShellModule {
LadderWindowFactory _lwf;
@@ -20,8 +19,8 @@ namespace bts.shell
PAndLWindowFactory _plwf;
[ImportingConstructor]
public TradingShellModule(IBus bus, IWindowManager wm, OrdersModel orders_model, PAndLModel pandl_model) {
_lwf = new LadderWindowFactory(this, bus, wm, orders_model);
public TradingShellModule(IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model) {
_lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model);
_obwf = new OrderBookWindowFactory(this, bus, orders_model);
_fwf = new FillsWindowFactory(this, bus, orders_model);
_plwf = new PAndLWindowFactory(this, bus, pandl_model);
@@ -34,7 +33,7 @@ namespace bts.shell
public int priority => 20;
public IEnumerable<SparkCommand> actions => new [] {
SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData()),
SparkCommands.ShowOrders.complete(() => OrderBookWindow.default_startup_data),
SparkCommands.ShowFills.complete(() => new FillsWindowFactory.StartupData()),
SparkCommands.ShowPAndL.complete(() => new PAndLWindowFactory.PAndLStartupData()),
SparkCommands.PickLadder,
@@ -57,5 +56,4 @@ namespace bts.shell
if (i != null) i.Dispose();
}
}
}
}
@@ -87,11 +87,8 @@ namespace mbts.spark {
key = ort.order.Key;
if (DoubleHelper.IsNaNOrInfinity(price))
price = ort.order.State.Price;
if (!size_calculator.value.HasValue || size_calculator.value.Value == 0) {
int wq = ort.order.working_quantity();
if (wq > 0)
size_calculator.value = (uint)wq;
}
if (!size_calculator.value.HasValue || !size_calculator.value_setter_called)
size_calculator.bypass_value_setter(ort.order.State.Quantity);
}),
Observable.Merge(_price_dirty, _size_dirty, _account_dirty, _time_in_force_dirty, _order_type_dirty).ObserveOnDispatcher().Subscribe(_ => RaisePropertyChanged(nameof(dirty_items_text))),
_price_dirty.observe_current(true).CombineLatest(_size_dirty.observe_current(true), (pd, sd) => pd || sd).DistinctUntilChanged().ObserveOnDispatcher().Subscribe(modify_order));
+185
View File
@@ -0,0 +1,185 @@
using bts.api;
using bts.api.core;
using bts.api.orderman;
using mbts.collections;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
namespace mbts.spark {
class FillTupleSource : IDisposable {
public static IObservable<ITableUpdateTransform<FillTuple, string>> create_observable(
IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities,
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders,
IObservable<TableUpdate<FillImmutable, string>> fills
) {
FillTupleSource source = null;
int ref_count = 0;
return Observable.Create<TableUpdateTransform<FillTuple, string>>(observer => {
if (Interlocked.Increment(ref ref_count) == 1) {
SparkUtils.lazy_assert(source == null, () => fmt_assert_msg("source should be null, there must be a leak."));
source = new FillTupleSource(securities, orders, fills);
}
IDisposable sub = source.fill_tuples.Subscribe(observer);
return Disposable.Create(() => {
Disposable.dispose_once(ref sub);
if (Interlocked.Decrement(ref ref_count) == 0)
Disposable.dispose_once(ref source);
});
});
string fmt_assert_msg(string msg) => $"{nameof(FillTupleSource)}.{nameof(create_observable)}: {msg}";
}
IDisposable _dispose;
FillTupleSource(
IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities,
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders,
IObservable<TableUpdate<FillImmutable, string>> fills) {
State state = new State();
var src = Observable.FromEvent<Action<TableUpdateTransform<FillTuple, string>>, TableUpdateTransform<FillTuple, string>>(cb => _updated += cb, cb => _updated -= cb);
fill_tuples = Observable.Create<TableUpdateTransform<FillTuple, string>>(observer => {
var init_state = state.fill_tuples.state.ToDictionary();
observer.OnNext(new TableUpdateTransform<FillTuple, string>(init_state, init_state.Values.ToList(), new List<FillTuple>(0)));
return src.Subscribe(observer);
});
_dispose = Disposable.Create(
securities.ObserveOnDispatcher().Subscribe(s => update(state.update_securities(s))),
orders.ObserveOnDispatcher().Subscribe(o => update(state.update_orders(o))),
fills.ObserveOnDispatcher().Subscribe(f => update(state.update_fills(f)))
);
void update(State new_state) {
state = new_state;
_updated?.Invoke(state.fill_tuples);
}
}
event Action<TableUpdateTransform<FillTuple, string>> _updated;
IObservable<TableUpdateTransform<FillTuple, string>> fill_tuples { get; }
public void Dispose() =>
Disposable.dispose(ref _dispose);
class State {
readonly IReadOnlyDictionary<string, FillImmutable> _fills;
readonly IReadOnlyDictionary<uint, OrderRecordImmutable> _orders;
readonly IReadOnlyDictionary<uint, SecurityMD> _securities;
readonly TableUpdateTransform<FillTuple, string> _fill_tuples;
readonly SetMap<uint, string> _order_fills;
readonly SetMap<uint, string> _security_fills;
public State() : this(
new Dictionary<string, FillImmutable>(),
new Dictionary<uint, OrderRecordImmutable>(),
new Dictionary<uint, SecurityMD>(),
new TableUpdateTransform<FillTuple, string>(new Dictionary<string, FillTuple>(), new List<FillTuple>(), new List<FillTuple>()),
new SetMap<uint, string>(),
new SetMap<uint, string>()
) { }
State(
IReadOnlyDictionary<string, FillImmutable> fills,
IReadOnlyDictionary<uint, OrderRecordImmutable> orders,
IReadOnlyDictionary<uint, SecurityMD> securities,
TableUpdateTransform<FillTuple, string> fill_tuples,
SetMap<uint, string> order_fill_tuples,
SetMap<uint, string> security_fill_tuples
) {
_fills = fills;
_orders = orders;
_securities = securities;
_fill_tuples = fill_tuples;
_order_fills = order_fill_tuples;
_security_fills = security_fill_tuples;
}
public TableUpdateTransform<FillTuple, string> fill_tuples => _fill_tuples;
public State update_fills(TableUpdate<FillImmutable, string> fills) {
var removed = fills.removed.Select(get_fill_tuple).ToList();
var added = fills.added.Select(get_fill_tuple).ToList();
var order_fills = _order_fills.clone();
var security_fills = _security_fills.clone();
HashSet<string> added_keys = new HashSet<string>(fills.added.Select(f => f.FillKey)),
removed_keys = new HashSet<string>(fills.removed.Select(f => f.FillKey));
foreach (var ft in removed) {
string k = ft.fill.FillKey;
if (!added_keys.Contains(k)) {
order_fills.remove(ft.fill.OrderKey, k);
security_fills.remove(ft.fill.SecurityKey, k);
}
}
foreach (var ft in added) {
string k = ft.fill.FillKey;
if (!removed_keys.Contains(k)) {
order_fills.add(ft.fill.OrderKey, k);
security_fills.add(ft.fill.SecurityKey, k);
}
}
return new State(fills.state, _orders, _securities, _fill_tuples.update(removed, added, f => f.key), order_fills, security_fills);
//
FillTuple get_fill_tuple(FillImmutable fill) =>
new FillTuple(fill, _orders.TryGetValue(fill.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(),
_securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
}
public State update_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
List<FillTuple> added = new List<FillTuple>(), removed = new List<FillTuple>();
orders.detect_changed_keys(o => o.Key, out HashSet<uint> removed_keys, out HashSet<uint> added_keys, out HashSet<uint> changed_keys);
foreach (var k in removed_keys) {
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(_orders[k], fill_keys));
added.AddRange(get_fill_tuples(null, fill_keys));
}
}
foreach (var k in added_keys) {
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(null, fill_keys));
added.AddRange(get_fill_tuples(orders.get(k), fill_keys));
}
}
foreach (var k in changed_keys) {
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(_orders[k], fill_keys));
added.AddRange(get_fill_tuples(orders.get(k), fill_keys));
}
}
return new State(_fills, orders.state, _securities, _fill_tuples.update(removed, added, ft => ft.key), _order_fills, _security_fills);
//
IEnumerable<FillTuple> get_fill_tuples(OrderRecordImmutable? o, ISet<string> fill_keys) =>
fill_keys.Select(k => {
var fill = _fills[k];
return new FillTuple(fill, o, _securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
});
}
public State update_securities(IReadOnlyDictionary<uint, SecurityMD> securities) {
List<FillTuple> added = new List<FillTuple>(), removed = new List<FillTuple>();
_securities.Keys.difference(securities.Keys, out IEnumerable<uint> added_keys, out IEnumerable<uint> removed_keys);
foreach (var k in removed_keys) {
if (_security_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(_securities[k].security, fill_keys));
added.AddRange(get_fill_tuples(null, fill_keys));
}
}
foreach (var k in added_keys) {
if (_security_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(null, fill_keys));
added.AddRange(get_fill_tuples(securities[k].security, fill_keys));
}
}
return new State(_fills, _orders, securities, _fill_tuples.update(removed, added, f => f.key), _order_fills, _security_fills);
//
IEnumerable<FillTuple> get_fill_tuples(SecurityImmutable? s, ISet<string> fill_keys) =>
fill_keys.Select(k => {
var fill = _fills[k];
return new FillTuple(fill, _orders.TryGetValue(fill.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(), s);
});
}
}
}
}
@@ -23,6 +23,8 @@ namespace bts.spark {
readonly IBus _bus;
IReadOnlyDictionary<uint, SecurityMD> _security = new Dictionary<uint, SecurityMD>();
IDisposable _dispose;
static readonly IEqualityComparer<OrderRecordImmutable> _id_comparer =
mbts.utils.EqualityComparer.Create<OrderRecordImmutable, uint>(a => a.Key);
public OrderActivitySummarizer(IBus bus, IObservable<TableUpdate<OrderRecordImmutable, uint>> orders) {
_bus = bus;
@@ -92,8 +94,8 @@ namespace bts.spark {
void orders_updated(TableUpdate<OrderRecordImmutable, uint> update) {
// Go though added and changed orders
var added = new HashSet<OrderRecordImmutable>(update.added);
var changed = new HashSet<OrderRecordImmutable>(update.added);
var added = new HashSet<OrderRecordImmutable>(update.added, _id_comparer);
var changed = new HashSet<OrderRecordImmutable>(update.added, _id_comparer);
var removed = update.removed.ToDictionary(o => o.Key);
added.ExceptWith(update.removed);
changed.ExceptWith(added);
+2 -2
View File
@@ -19,10 +19,10 @@ namespace mbts.ui.spark
_ask_pen.Freeze();
}
private static readonly Pen _bid_pen = new Pen(BTSColors.MarketData.bid_brush, 4), _ask_pen = new Pen(BTSColors.MarketData.ask_brush, 4);
private static readonly ConcurrentDictionary<uint, Brush> _algo_pallete = new ConcurrentDictionary<uint, Brush>();
private static readonly ConcurrentDictionary<uint, Brush> _algo_palette = new ConcurrentDictionary<uint, Brush>();
public static ImageSource Create(OrderMetadata metadata, Side side) {
Brush brush = _algo_pallete.GetOrAdd(metadata.Color, c => {
Brush brush = _algo_palette.GetOrAdd(metadata.Color, c => {
Brush b = new SolidColorBrush(c.rgb_color());
b.Freeze();
return b;
@@ -174,7 +174,7 @@
<uag:OrderDetailsPopupControl
Grid.Row="2"
usp:OrderModificationControl.IsDirty="{Binding order_type_dirty, FallbackValue={u:False}}"
HasEditableProperties="{Binding selected_order_type.has_editable_properties, FallbackValue={u:False}}"
HasEditableProperties="{Binding properties_vm.selected_order_type.has_editable_properties, FallbackValue={u:False}}"
IsEnabled="{Binding can_change_order_type, FallbackValue={u:True}}"
IsOpen="{Binding properties_vm.selected_order_type.editable_properties_open, FallbackValue={u:False}}"
ItemsSource="{Binding properties_vm.order_types}"
@@ -2,23 +2,22 @@
using bts.api.orderman;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Input;
using System.Reactive.Subjects;
namespace mbts.spark {
namespace mbts.spark {
class InlineOrderProperties {
public readonly InlineUserProperty<string> account;
public readonly InlineUserProperty<TimeInForce> time_in_force;
public readonly InlineUserProperty<CustomOrderDefinition> order_type_def;
public InlineOrderProperties(OrdersModel model, LadderSettings settings) : this(
new InlineUserProperty<string>(model.factory.default_account, true),
new InlineUserProperty<string>(model.factory.default_account, false),
new InlineUserProperty<TimeInForce>(settings.default_time_in_force, true),
new InlineUserProperty<CustomOrderDefinition>(model.factory.custom_order(settings.default_order_type), true)) { }
@@ -39,148 +38,104 @@ namespace mbts.spark {
class OrderPropertiesEditorVM : DisposableViewModelBase {
readonly OrdersModel _model;
readonly ObservableCollection<CustomOrderDefinition.CustomOneofCase> _order_types_model =
new ObservableCollection<CustomOrderDefinition.CustomOneofCase>(EnumHelper.get_values_except(CustomOrderDefinition.CustomOneofCase.None));
Property<string> _selected_account;
Property<TimeInForce> _selected_time_in_force;
IOrderTypeVM _selected_order_type;
readonly Property<SecurityImmutable> _security;
readonly SelectorProperty<string> _accounts;
readonly SelectorProperty<TimeInForce> _time_in_force;
readonly SelectorProperty<IOrderTypeVM> _order_type;
IDisposable _order_type_param_errors_sub;
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityImmutable> security_source, IObservable<InlineOrderProperties> order_properties) {
_model = model;
accounts = model.factory.accounts;
order_types = new ObservableAutoWrapper<IOrderTypeVM, CustomOrderDefinition.CustomOneofCase>(_order_types_model, ot => OrderTypeVMFactory.create(ot, model, security_source));
_selected_account = backing(nameof(selected_account), string.Empty, val => val != null && accounts.Contains(val));
_selected_time_in_force = backing(nameof(selected_time_in_force), TimeInForce.Undefined, time_in_forces.Contains);
InlineOrderProperties props = new InlineOrderProperties();
_dispose = Disposable.Create(
security_source.ObserveOnDispatcher().Subscribe(s => {
security = s;
_order_types_model.sync_with(model.factory.default_order_types(security));
if (props.order_type_def.has_value) {
if (props.order_type_def.force_override || selected_order_type == null || !order_types.Contains(selected_order_type)) {
if (order_types.TryGetFirst(ot => ot.order_type == props.order_type_def.value.CustomCase, out IOrderTypeVM match)) {
match.load_from(props.order_type_def.value);
selected_order_type = match;
}
} else if (selected_order_type == null || !order_types.Contains(selected_order_type))
selected_order_type = order_types.FirstOrDefault();
}
time_in_forces.sync_with(model.factory.default_time_in_forces(security));
auto_select_time_in_force();
security_changed?.Invoke(s);
RaisePropertyChanged(nameof(security));
}),
order_properties.ObserveOnDispatcher().Subscribe(p => {
props = p;
auto_select_account();
auto_select_time_in_force();
auto_select_order_type();
})
);
#region Auto Select
void auto_select_account() =>
auto_select_prop(props.account, _selected_account, accounts, () => model.factory.default_account);
void auto_select_time_in_force() =>
auto_select_prop(props.time_in_force, _selected_time_in_force, time_in_forces, () => model.factory.default_time_in_force(security));
void auto_select_prop<T>(InlineUserProperty<T> inline_prop, Property<T> prop, IReadOnlyList<T> choices, Func<T> get_default) {
if (inline_prop.has_value && choices.Contains(inline_prop.value)) {
if (inline_prop.force_override || has_errors(prop.name)) {
prop.Value = inline_prop.value;
}
} else if (has_errors(prop.name)) {
prop.Value = get_default();
}
_security = backing(nameof(security), security_source.ObserveOnDispatcher(), SecurityDataHelper.Defaults.SecurityImmutable);
// guarentee that "this.security" will always be set before observers are notified
// this is important bc some observers will reference "this.security" in their callbacks
// - namely the [SelectorProperty]order_type's "get_default" function,
// which may be called in response to its items changing, which are driven by the observable security
// (see 'get_default_order_type_vm')
security_source = _security.observe_current(true).Replay(1).RefCount();
_accounts = backing(
nameof(selected_account),
model.factory.subscribe_accounts(),
order_properties.Select(ops => ops.account),
() => string.Empty,
val => (!String.IsNullOrWhiteSpace(val) && accounts.Contains(val), "Please select an account"));
_time_in_force = backing(
nameof(selected_time_in_force),
security_source.Select(s => model.factory.default_time_in_forces(s)),
order_properties.Select(ops => ops.time_in_force),
() => model.factory.default_time_in_force(security),
tif => time_in_forces.Contains(tif));
_order_type = backing(
nameof(selected_order_type),
security_source.Select(s => model.factory.default_order_type_defs(s).Select(create_order_type_vm)),
order_properties.Select(get_order_type_vm_prop),
get_default_order_type_vm,
(a, b) => a.order_type == b.order_type);
_order_type.Changed += on_selected_order_type_changed;
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
// local methods
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) {
var vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_source);
vm.load_from(cd);
return vm;
}
void auto_select_order_type() {
bool invalid = has_errors(nameof(selected_order_type));
if (props.order_type_def.has_value) {
if (invalid) {
if (try_select_type(props.order_type_def.value.CustomCase)) {
selected_order_type.load_from(props.order_type_def.value);
} else {
select_default_value();
}
} else if (props.order_type_def.force_override) {
try_select_type(props.order_type_def.value.CustomCase);
}
} else if (invalid) {
select_default_value();
}
void select_default_value() {
var ot = model.factory.default_order_type(security);
if (!try_select_type(ot))
selected_order_type = order_types.FirstOrDefault();
}
bool try_select_type(CustomOrderDefinition.CustomOneofCase type) {
if (order_types.TryGetFirst(ot => ot.order_type == type, out IOrderTypeVM result)) {
selected_order_type = result;
return true;
}
return false;
}
InlineUserProperty<IOrderTypeVM> get_order_type_vm_prop(InlineOrderProperties p) {
if (!p.order_type_def.has_value)
return new InlineUserProperty<IOrderTypeVM>();
return new InlineUserProperty<IOrderTypeVM>(create_order_type_vm(p.order_type_def.value), p.order_type_def.force_override);
}
#endregion
}
public event Action<SecurityImmutable> security_changed;
event Action<IOrderTypeVM> _selected_order_type_changed;
public SecurityImmutable security { get; private set; } = SecurityDataHelper.Defaults.SecurityImmutable;
public string[] accounts { get; }
public string selected_account { get => _selected_account.Value; set => _selected_account.Value = value; }
public ObservableCollection<TimeInForce> time_in_forces { get; } = new ObservableCollection<TimeInForce>();
public TimeInForce selected_time_in_force { get => _selected_time_in_force.Value; set => _selected_time_in_force.Value = value; }
public ObservableAutoWrapper<IOrderTypeVM, CustomOrderDefinition.CustomOneofCase> order_types { get; }
public IOrderTypeVM selected_order_type {
get => _selected_order_type;
set {
Disposable.dispose(ref _order_type_param_errors_sub);
_selected_order_type = value;
if (value == null) {
IOrderTypeVM get_default_order_type_vm() {
var ot = model.factory.default_order_type(security);
if (order_types.TryGetFirst(vm => vm.order_type == ot, out IOrderTypeVM result))
return result;
return order_types.FirstOrDefault();
}
void on_selected_order_type_changed(IOrderTypeVM old_val, IOrderTypeVM new_val) {
if (new_val == null) {
assert(false);
} else {
assert(order_types.Contains(value));
value.init();
if (value is INotifyDataErrorInfo ndei) {
assert(order_types.Contains(new_val));
new_val.init();
if (new_val is INotifyDataErrorInfo ndei) {
assert_no_param_errors();
_order_type_param_errors_sub = ndei.subscribe_errors_changed((s, e) => assert_no_param_errors());
void assert_no_param_errors() => assert(!ndei.HasErrors, "order type pararmeters invalid.");
}
}
RaisePropertyChanged();
_selected_order_type_changed?.Invoke(_selected_order_type);
}
}
public ICommand set_price_command { get; }
public SecurityImmutable security => _security.Value;
public ReadOnlyObservableCollection<string> accounts => _accounts.items;
public string selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; }
public ReadOnlyObservableCollection<TimeInForce> time_in_forces => _time_in_force.items;
public TimeInForce selected_time_in_force { get => _time_in_force.selected_item; set => _time_in_force.selected_item = value; }
public ReadOnlyObservableCollection<IOrderTypeVM> order_types => _order_type.items;
public IOrderTypeVM selected_order_type { get => _order_type.selected_item; set => _order_type.selected_item = value; }
public IObservable<string> observe_account(bool start_immediately = false) =>
_selected_account.observe_current(start_immediately);
_accounts.observe_current(start_immediately);
public IObservable<TimeInForce> observe_time_in_force(bool start_immediately = false) =>
_selected_time_in_force.observe_current(start_immediately);
_time_in_force.observe_current(start_immediately);
public IObservable<IOrderTypeVM> observe_order_type(bool start_immediately = false) {
var source = Observable.FromEvent<Action<IOrderTypeVM>, IOrderTypeVM>(cb => _selected_order_type_changed += cb, cb => _selected_order_type_changed -= cb);
if (start_immediately)
source = source.StartWith(_selected_order_type);
return source;
}
public IObservable<IOrderTypeVM> observe_order_type(bool start_immediately = false) =>
_order_type.observe_current(start_immediately);
public OrderDefinition create(Side side) {
if (HasErrors) throw new InvalidOperationException("Cannot create order when errors are present.");
var order = _model.factory.order(security);
order.Account = selected_account;
order.AccountId = _model.factory.get_account_id(selected_account);
order.TimeInForce = selected_time_in_force;
order.Side = side;
_selected_order_type.assign_to(order.CustomDef);
selected_order_type.assign_to(order.CustomDef);
return order;
}
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
Disposable.dispose(ref _order_type_param_errors_sub);
}
}
}
+86 -4
View File
@@ -17,7 +17,89 @@
<sys:Double x:Key="HorizontalSpacing">8.0</sys:Double>
<sys:Double x:Key="VerticalSpacing">4.0</sys:Double>
<sys:Double x:Key="ButtonHeight">22.0</sys:Double>
</ResourceDictionary>
<Style x:Key="SellButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="{u:BTSColor MarketData.ask}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Content">
<Setter.Value>
<TextBlock VerticalAlignment="Center" Foreground="{u:BTSColor UI.alternate_text}">Sell</TextBlock>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
x:Name="Border"
CornerRadius="0"
BorderThickness="1"
BorderBrush="{u:BTSColor UI.Button.normal_border}"
Background="{u:BTSColor MarketData.ask}"
Padding="2"
Height="Auto"
SnapsToDevicePixels="True">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"
Name="button_content"
/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="#99c00000" />
<Setter TargetName="Border" Property="BorderBrush" Value="{u:BTSColor UI.Button.mouseover_border}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BuyButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="{u:BTSColor MarketData.bid}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Content">
<Setter.Value>
<TextBlock VerticalAlignment="Center" Foreground="{u:BTSColor UI.alternate_text}">Buy</TextBlock>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
x:Name="Border"
CornerRadius="0"
BorderThickness="1"
BorderBrush="{u:BTSColor UI.Button.normal_border}"
Background="{u:BTSColor MarketData.bid}"
Padding="2"
Height="Auto"
SnapsToDevicePixels="True">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"
Name="button_content"
/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="#99167ed3" />
<Setter TargetName="Border" Property="BorderBrush" Value="{u:BTSColor UI.Button.mouseover_border}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
@@ -31,9 +113,9 @@
<ColumnDefinition MinWidth="120"/>
</Grid.ColumnDefinitions>
<usp:OrderModificationControl Grid.ColumnSpan="3" />
<Button Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="40" Background="{u:BTSColor MarketData.bid}" Foreground="White" FontWeight="Bold" Command="{Binding send_order}" CommandParameter="{u:Enum Buy, Type=msys:Side}">Buy</Button>
<Button Grid.Row="2" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="40" Background="{u:BTSColor MarketData.ask}" Foreground="White" FontWeight="Bold" Command="{Binding send_order}" CommandParameter="{u:Enum Sell, Type=msys:Side}">Sell</Button>
</Grid>
<Button Grid.Row="2" Style="{StaticResource BuyButton}" Command="{Binding send_order}" CommandParameter="{u:Enum Buy, Type=msys:Side}" />
<Button Grid.Row="2" Grid.Column="2" Style="{StaticResource SellButton}" Command="{Binding send_order}" CommandParameter="{u:Enum Sell, Type=msys:Side}" />
</Grid>
<u:WindowStyle.TitleContentTemplate>
<DataTemplate>
<TextBlock>
@@ -18,7 +18,7 @@ namespace mbts.spark {
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
show_quantity = new InlineUserProperty<double>(0, true);
show_quantity = new InlineUserProperty<double>(1, true);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
@@ -27,12 +27,16 @@ namespace mbts.spark {
}
class TimeBoxOrderVM : OrderTypeVMBase<TimeBoxOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
const double _start_immediately_delay_ms = 500;
readonly IObservable<SecurityImmutable> _security_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
InlineTimeBoxOrderProperties _inline_props;
TimeTextTuple _start = TimeTextTuple.FromDateTime(DateTime.Now), _stop = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(5));
TimeTextTuple _absolute_start = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(5)), _absolute_stop = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(10));
TimeSpan? _relative_start = TimeSpan.FromMinutes(5), _relative_stop = TimeSpan.FromMinutes(5);
StartMode _start_mode = StartMode.In;
StopMode _stop_mode = StopMode.After;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
@@ -57,12 +61,12 @@ namespace mbts.spark {
_select_child_order(p.selected_child_order_type.value);
}
if (p.start.force_override) {
start = TimeTextTuple.FromDateTime(p.start.value);
RaisePropertyChanged(nameof(start));
absolute_start = TimeTextTuple.FromDateTime(p.start.value);
RaisePropertyChanged(nameof(absolute_start));
}
if (p.stop.force_override) {
stop = TimeTextTuple.FromDateTime(p.stop.value);
RaisePropertyChanged(nameof(stop));
absolute_stop = TimeTextTuple.FromDateTime(p.stop.value);
RaisePropertyChanged(nameof(absolute_stop));
}
_inline_props = p;
_validate();
@@ -71,6 +75,22 @@ namespace mbts.spark {
_validate();
}
public enum StartMode {
At,
In,
[Description("Immediately")]
Now
}
public enum StopMode {
At,
After,
[Description("When Completed")]
WhenComleted
}
public IEnumerable<StartMode> start_modes => EnumHelper.get_values<StartMode>();
public IEnumerable<StopMode> stop_modes => EnumHelper.get_values<StopMode>();
public ICollection<IOrderTypeVM> child_order_types {
get {
if (_child_order_choices == null) {
@@ -100,30 +120,40 @@ namespace mbts.spark {
}
}
public TimeTextTuple start {
get => _start;
public StartMode start_mode {
get => _start_mode;
set {
_start = value;
_start_mode = value;
_validate();
RaisePropertyChanged(nameof(show_absolute_start), nameof(show_relative_start));
}
}
public TimeTextTuple stop {
get => _stop;
public StopMode stop_mode {
get => _stop_mode;
set {
_stop = value;
_stop_mode = value;
_validate();
RaisePropertyChanged(nameof(show_absolute_stop), nameof(show_relative_stop));
}
}
public TimeTextTuple absolute_start { get => _absolute_start; set { _absolute_start = value; _validate(); } }
public TimeTextTuple absolute_stop { get => _absolute_stop; set { _absolute_stop = value; _validate(); } }
public TimeSpan? relative_start { get => _relative_start; set { _relative_start = value; _validate(); } }
public TimeSpan? relative_stop { get => _relative_stop; set { _relative_stop = value; _validate(); } }
public bool show_absolute_start => _start_mode == StartMode.At;
public bool show_relative_start => _start_mode == StartMode.In;
public bool show_absolute_stop => _stop_mode == StopMode.At;
public bool show_relative_stop => _stop_mode == StopMode.After;
public override void assign_to(CustomOrderDefinition value) {
if (_child_order == null)
_select_child_order(model.factory.default_order_type(_security.Value, true));
_validate();
var (start_time, stop_time) = _validate();
if (!HasErrors) {
DateTime? start_result = start.result, stop_result = stop.result;
var tb = factory.time_box_order();
tb.Start = start.result.Value.ToUniversalTime().ToGoogleTime();
tb.Stop = stop.result.Value.ToUniversalTime().ToGoogleTime();
tb.Start = start_time.Value.ToUniversalTime().ToGoogleTime();
tb.Stop = stop_time.Value.ToUniversalTime().ToGoogleTime();
child_order.assign_to(tb.Child);
value.TimeBox = tb;
}
@@ -155,19 +185,57 @@ namespace mbts.spark {
RaisePropertyChanged(nameof(child_order));
}
void _validate() {
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
set_error(nameof(child_order), string.Empty, true);
else clear_error(nameof(child_order));
if (!start.result.HasValue)
set_error(nameof(start), string.Empty, true);
else clear_error(nameof(start));
if (!stop.result.HasValue)
set_error(nameof(stop), string.Empty, true);
else clear_error(nameof(stop));
if (!HasErrors) {
if (start.result.Value > stop.result.Value)
set_error(nameof(stop), string.Empty, true);
(DateTime? start_time, DateTime? stop_time) _validate() {
assert(_child_order != null && !(_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors), nameof(child_order));
// ti.start_name is null if mode is Now, stop_name if mode is WhenCompleted
var now = DateTime.Now;
var ti = get_time_info();
if (ti.start_name != null) {
if (!ti.start_time.HasValue || ti.start_time.Value < now)
set_error(ti.start_name, string.Empty, false);
else clear_error(ti.start_name);
}
if (ti.stop_name != null) {
if (!ti.stop_time.HasValue || ti.stop_time.Value <= now || ti.start_time.HasValue && ti.start_time.Value >= ti.stop_time.Value)
set_error(ti.stop_name, string.Empty, false);
else clear_error(ti.stop_name);
}
return (ti.start_time, ti.stop_time);
//
(DateTime? start_time, string start_name, DateTime? stop_time, string stop_name) get_time_info() {
DateTime? start_time = null, stop_time = null;
string start_name = null, stop_name = null;
switch (_start_mode) {
case StartMode.At:
start_time = _absolute_start.result;
start_name = nameof(absolute_start);
break;
case StartMode.In:
if (_relative_start.HasValue)
start_time = now + _relative_start.Value;
start_name = nameof(relative_start);
break;
case StartMode.Now:
default:
start_time = now.AddMilliseconds(_start_immediately_delay_ms);
break;
}
switch (_stop_mode) {
case StopMode.At:
stop_time = _absolute_stop.result;
stop_name = nameof(absolute_stop);
break;
case StopMode.After:
if (_relative_stop.HasValue)
stop_time = (start_time ?? now) + _relative_stop;
stop_name = nameof(relative_stop);
break;
case StopMode.WhenComleted:
default:
stop_time = DateTime.MaxValue;
break;
}
return (start_time, start_name, stop_time, stop_name);
}
}
}
+26 -49
View File
@@ -3,7 +3,6 @@ using bts.api.core;
using bts.api.orderman;
using bts.spark;
using mbts.spark.utilities;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
@@ -22,7 +21,7 @@ namespace mbts.spark {
readonly SpreaderModel _spreader;
readonly ErrorReporter _error;
readonly OrderActivitySummarizer _activity;
readonly UpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_demuxer;
readonly TableUpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_demuxer;
IDisposable _dispose;
[ImportingConstructor]
@@ -30,12 +29,18 @@ namespace mbts.spark {
_bus = bus;
_factory = factory;
_service = service;
on_orders = _service.subscribei_orders().Table();
_spreader = spreader;
_error = error;
on_orders = _service.subscribei_orders().Table().Replay(1).RefCount();
IReadOnlyDictionary<uint, OrderRecordImmutable> empty_orders = new Dictionary<uint, OrderRecordImmutable>();
working_orders =
on_orders.Scan(empty_orders, (worders, table) => _update_working_orders(worders, table)).Replay(1).RefCount();
on_fills = _service.subscribei_fills().Table().Replay(1).RefCount();
_activity = new OrderActivitySummarizer(bus, on_orders);
on_fill_tuples = FillTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders, on_fills);
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
_dispose = Disposable.Create(
_security_orders_demuxer = new UpdateDemuxer<OrderRecordImmutable, uint, uint>(service.subscribei_orders(), o => o.Key, o => o.Def.SecurityKey),
on_orders.Subscribe(_security_orders_demuxer),
_bus.subscribe(SparkTags.CreateOrder).Subscribe(a => send_order(a.definition, a.price, a.size).ignore()),
_bus.subscribe(SparkTags.CancelOrders).Subscribe(x => cancel_orders(x).ignore()),
_bus.subscribe(SparkTags.CrackOrders).Subscribe(x => crack_orders(x).ignore()),
@@ -46,18 +51,13 @@ namespace mbts.spark {
#region Output
public OrderFactory factory => _factory;
public SpreaderModel spreader => _spreader;
public IObservable<TableUpdate<OrderRecordImmutable, uint>> on_orders { get; }
public IObservable<TableUpdate<FillImmutable, string>> on_fills { get; }
public IObservable<ITableUpdateTransform<FillTuple, string>> on_fill_tuples { get; }
public IObservable<IReadOnlyDictionary<uint,OrderRecordImmutable>> working_orders { get; }
public IObservable<TableUpdate<OrderRecordImmutable, uint>> on_security_orders(uint security) =>
_security_orders_demuxer.subscribe(security).Table();
public IObservable<TableUpdate<FillTuple, string>> on_fill_tuples =>
_on_fill_tuples();
public IObservable<TableUpdate<FillTuple, string>> on_order_fill_tuples(IReadOnlyCollection<uint> order_keys) =>
_on_order_fill_tuples(_on_key_orders(order_keys));
public IObservable<ITableUpdateTransform<OrderRecordImmutable, uint>> on_security_orders(uint security) =>
_security_orders_demuxer.subscribe(security);
#endregion
#region Input
@@ -118,41 +118,18 @@ namespace mbts.spark {
public void Dispose() =>
Disposable.dispose(ref _dispose);
#region Private Methods
IObservable<Update<OrderRecordImmutable, uint>> _on_key_orders(IReadOnlyCollection<uint> order_keys) =>
_service.subscribei_orders().Select(u => u.Filter(order_keys));
IObservable<TableUpdate<FillTuple, string>> _on_fill_tuples() => _on_fill_tuples(
_service.subscribei_fills()
.CombineLatest(on_orders, _bus.subscribe(SparkTags.SecuritiesTag), (fills, orders, md_map) => (
_get_fill_tuples(fills.removed, orders.state, md_map),
_get_fill_tuples(fills.added, orders.state, md_map)
)));
IObservable<TableUpdate<FillTuple, string>> _on_order_fill_tuples(IObservable<Update<OrderRecordImmutable, uint>> orders_source) => _on_fill_tuples(
_service.subscribei_fills()
.CombineLatest(orders_source.Table(), _bus.subscribe(SparkTags.SecuritiesTag), (fills, orders, md_map) => {
fills = fills.Filter(f => orders.state.ContainsKey(f.OrderKey));
return (
_get_fill_tuples(fills.removed, orders.state, md_map),
_get_fill_tuples(fills.added, orders.state, md_map)
);
}));
IObservable<TableUpdate<FillTuple, string>> _on_fill_tuples(IObservable<(IEnumerable<FillTuple> raw_added, IEnumerable<FillTuple> raw_removes)> source) => source
.PairWithPrevious((Enumerable.Empty<FillTuple>(), Enumerable.Empty<FillTuple>()))
.Select(vals => (
vals.curr.Item1.Except(vals.prev.Item1).ToReadOnlyList(),
vals.curr.Item2.Except(vals.prev.Item2).ToReadOnlyList()
)).Where(vals => vals.Item1.Count > 0 || vals.Item2.Count > 0)
.Table(f => f.key);
static IEnumerable<FillTuple> _get_fill_tuples(IEnumerable<FillImmutable> fills, IReadOnlyDictionary<uint, OrderRecordImmutable> orders, IReadOnlyDictionary<uint, SecurityMD> md_map) =>
fills.Select(f => new FillTuple(f,
orders.TryGetValue(f.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(),
md_map.TryGetValue(f.SecurityKey, out SecurityMD smd) ? smd.security : new SecurityImmutable?()
));
#endregion
static IReadOnlyDictionary<uint, OrderRecordImmutable> _update_working_orders(IReadOnlyDictionary<uint, OrderRecordImmutable> target, TableUpdate<OrderRecordImmutable, uint> table) {
var clone = new Dictionary<uint, OrderRecordImmutable>((IDictionary<uint, OrderRecordImmutable>)target);
foreach (var o in table.removed) {
if (o.is_active())
clone.Remove(o.Key);
}
foreach (var o in table.added) {
if (o.is_active())
clone.Add(o.Key, o);
}
return clone;
}
}
// usinig new OrdersModel extensions to reduce the amount of code change needed to switch uis over to the new OrdersModel,
+4 -3
View File
@@ -15,9 +15,10 @@
<OutputType>WinExe</OutputType>
<StartupObject>mbts.ui.shell.App</StartupObject>
<RootNamespace>bts</RootNamespace>
<LangVersion>7.1</LangVersion>
<LangVersion>7.2</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
<WarningsAsErrors />
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../spark_api/spark_api.csproj" />
@@ -34,7 +35,7 @@
<PackageReference Include="IronPython" Version="2.7.7" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="NUnit" Version="3.8.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="protobuf-net" Version="2.3.2" />
@@ -0,0 +1,109 @@
using mbts.collections;
using NUnit.Framework;
using System.Collections.Generic;
namespace bts.tests {
[TestFixture]
class SortedSetMapTests {
class TestParams {
public readonly IComparer<int> key_comparer = Comparer<int>.Default;
public readonly IEqualityComparer<int> value_eq_comparer = EqualityComparer<int>.Default;
public readonly Dictionary<int, int[]> unsorted_key_vals = new Dictionary<int, int[]> {
{ 3, new[] { 1, 4, 2, 5, 4, 2, 7 } },
{ 5, new[] { 7, 9, 3, 5, 1 } },
{ 4, new[] { 5, 5, 1, 2, 8, 6 } },
{ 1, new[] { 4, 3, 2, 1 } },
{ 2, new[] { 8, 8, 8, 8 } }
};
public readonly List<int> sorted_keys;
public TestParams() {
sorted_keys = new List<int>(unsorted_key_vals.Keys);
sorted_keys.Sort(key_comparer);
}
public SortedSetMap<int, int> get_populated_sorted_set_map() {
var ssm = new SortedSetMap<int, int>(Comparer<int>.Default, EqualityComparer<int>.Default);
foreach (var kv in unsorted_key_vals) {
foreach (int val in kv.Value)
ssm.add(kv.Key, val);
}
return ssm;
}
}
[Test]
public void test_add_values() {
var tps = new TestParams();
var ssm = new SortedSetMap<int, int>(tps.key_comparer, tps.value_eq_comparer);
foreach (var kv in tps.unsorted_key_vals) {
foreach (int val in kv.Value)
ssm.add(kv.Key, val);
}
Assert.That(ssm.Count == tps.unsorted_key_vals.Count, () => $"Count is incorrect: expected({tps.unsorted_key_vals.Count}), actual({ssm.Count}).");
// test key-sorting, validate corresponding value-sets
for (int i = 0; i < tps.unsorted_key_vals.Count; i++) {
int expected_key = tps.sorted_keys[i];
var actual_kv = ssm.at_index(i);
Assert.That(expected_key == actual_kv.Key, "Keys are not sorted correctly.");
var expected_vals = new HashSet<int>(tps.unsorted_key_vals[expected_key]);
Assert.That(actual_kv.SetEquals(expected_vals), () => $"Value set is incorrect: expected({string.Join(", ", expected_vals)}), actual({string.Join(", ", actual_kv)}).");
}
}
[Test]
public void test_remove_value() {
var tps = new TestParams();
var ssm = tps.get_populated_sorted_set_map();
// test remove value
int middle_key = tps.sorted_keys[(int)(0.5 * tps.sorted_keys.Count)];
int[] middle_values = tps.unsorted_key_vals[middle_key];
int middle_middle_value = middle_values[(int)(0.5 * middle_values.Length)];
// this should not remove a key-node, just a value from the value-set
int pre_remove_count = ssm.Count;
var pre_remove_kv = ssm[middle_key];
Assert.That(ssm.remove(middle_key, middle_middle_value), "Failed to remove value.");
Assert.That(ssm.Count == pre_remove_count, "Key-node should not have been removed. ");
var post_remove_kv = ssm[middle_key];
Assert.That(ReferenceEquals(pre_remove_kv, post_remove_kv), "Key-value-set should remain the same object.");
Assert.That(!post_remove_kv.Contains(middle_middle_value), "Value was not removed from set.");
}
[Test]
public void test_remove_key() {
var tps = new TestParams();
var ssm = tps.get_populated_sorted_set_map();
int middle_key = tps.sorted_keys[(int)(0.5 * tps.sorted_keys.Count)];
int pre_remove_count = ssm.Count;
var pre_remove_kv = ssm[middle_key];
Assert.That(ssm.remove(middle_key), "Failed to remove key.");
Assert.That(ssm.Count == pre_remove_count - 1, "Count was not decremented.");
Assert.That(!ssm.contains_key(middle_key), "Still contains key after it was supposedly removed.");
}
[Test]
public void test_resize() {
const int initial_capacity = 4, ultimate_length = 10;
var ssm = new SortedSetMap<int, int>(initial_capacity);
for (int i = 0; i < ultimate_length; i++)
ssm.add(i, 1);
Assert.That(ssm.Count == ultimate_length, $"Count should be {ultimate_length}, actually {ssm.Count}");
}
[Test]
public void test_copy() {
var tps = new TestParams();
var ssm = tps.get_populated_sorted_set_map();
// test CopyTo
var dest_array = new IGroupingSet<int, int>[ssm.Count];
ssm.CopyTo(dest_array, 0);
for (int i = 0; i < dest_array.Length; i++) {
var dkv = dest_array[i];
var kv = ssm.at_index(i);
Assert.That(kv.Key == dkv.Key, () => $"CopyTo destination array key doesn't match: expectd({dkv.Key}), actual({kv.Key}).");
}
}
}
}
@@ -14,6 +14,7 @@ namespace mbts.ui.tests
public event Action TitleChanged = () => { };
public System.Windows.Media.ImageSource Icon { get { return null; } }
public event Action IconChanged = () => { };
public string window_type { get; }
public IShellModule Owner { get; set; }
public void GetState(System.IO.Stream save_here) { }
#endregion
@@ -74,20 +74,19 @@ namespace mbts.ui.tests
Application.Current.Dispatcher.Invoke(() => {
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_windows_disposed");
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
ObservableCollection<IShellWindow> windows = new ObservableCollection<IShellWindow>();
var thread_factory = new ShellThreadFactory();
IWindowManager wm = new WindowManager(windows, thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
Assert.IsNotNull(Application.Current);
Assert.IsNotNull(Application.Current.Windows);
Assert.AreEqual(Application.Current.Windows.Count, 0);
TestShellWindow test_window = new TestShellWindow();
wm.Windows.Add(test_window);
wm.add(test_window);
// Check that no new dispatcher was created for this window
Assert.AreEqual(Dispatcher.CurrentDispatcher, test_window.Dispatcher);
Assert.AreEqual(Application.Current.Windows.Count, 1);
wm.Windows.Clear();
wm.clear();
Assert.AreEqual(Application.Current.Windows.Count, 0);
Assert.IsTrue(test_window.IsDisposed);
thread_factory.emergency_shutdown();
@@ -100,19 +99,18 @@ namespace mbts.ui.tests
Application.Current.Dispatcher.Invoke(() => {
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_slow_windows_disposed");
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
ObservableCollection<IShellWindow> windows = new ObservableCollection<IShellWindow>();
var thread_factory = new ShellThreadFactory();
IWindowManager wm = new WindowManager(windows, thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
Assert.AreEqual(Application.Current.Windows.Count, 0);
// an ISlowWindow gets its own thread
var slow_window = new TestSlowWindow();
Assert.IsTrue(slow_window is ISlowWindow);
wm.Windows.Add(slow_window);
wm.add(slow_window);
Assert.AreNotEqual(Dispatcher.CurrentDispatcher, slow_window.Dispatcher);
Assert.AreEqual(Application.Current.Windows.Count, 1);
wm.Windows.Clear();
wm.clear();
Assert.AreEqual(Application.Current.Windows.Count, 0);
DateTime now = DateTime.UtcNow;
while (!slow_window.IsDisposed && (DateTime.UtcNow - now) < TimeSpan.FromSeconds(120)) { Thread.Sleep(100); }
+3 -2
View File
@@ -15,6 +15,7 @@
<ResourceDictionary Source="/view/Controls/DragDropItemsControl/DraggableContentControl.xaml" />
<ResourceDictionary Source="/view/Controls/DragDropItemsControl/DragDropListBox.xaml" />
<ResourceDictionary Source="/view/Controls/DragDropItemsControl/DraggableListBoxItem.xaml" />
<ResourceDictionary Source="/view/Controls/DialogSkirts/DialogSkirt.xaml"/>
<ResourceDictionary Source="/view/Controls/DialogSkirts/DialogSkirtOKCancel.xaml" />
<ResourceDictionary Source="/view/Controls/DialogSkirts/DialogSkirtOKApplyCancel.xaml" />
<ResourceDictionary Source="/view/Controls/ComboCheckBox/ComboCheckBox.xaml" />
@@ -165,9 +166,9 @@
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup
x:Name="PART_Popup" Grid.ColumnSpan="2" Margin="1" Placement="Bottom" AllowsTransparency="true"
x:Name="PART_Popup" Grid.ColumnSpan="2" Margin="1" Placement="Bottom" AllowsTransparency="false"
IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
PopupAnimation="None"
>
<Border
x:Name="dropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
@@ -33,6 +33,10 @@ namespace bts.spark
public static PartialCommand<SecurityMD> ShowLadder =>
SparkCommand.create("Ladder", Icons.Ladder, SparkTags.ShowLadderTag);
public static PartialCommand<IReadOnlyList<SecurityMD>> ShowLadders =>
SparkCommand.create("Ladder", Icons.Ladder, SparkTags.ShowLaddersTag);
public static PartialCommand<IObservable<SecurityMD>> ShowLinkedLadder =>
SparkCommand.create("Linked Ladder", Icons.Ladder, SparkTags.ShowLinkedLadder);
public static SparkCommand PickLadder =>
+4
View File
@@ -48,7 +48,11 @@ namespace mbts.spark
#region Spark Window
public static ITag<FillsWindowFactory.StartupData> ShowFillsTag { get; } = new SparkTag<FillsWindowFactory.StartupData>(nameof(ShowFillsTag));
// TODO: maybe get rid of the singlular ShowLadderTag, and just use the plural ShowLaddersTag.
// The owners of the various SparkWindows may remove dependencies on the former at their leasure.
// For now, both tags go through the same logic in the LadderWindowFactory anyways.
public static ITag<SecurityMD> ShowLadderTag { get; } = new SparkTag<SecurityMD>("Show Ladder");
public static ITag<IReadOnlyList<SecurityMD>> ShowLaddersTag { get; } = new SparkTag<IReadOnlyList<SecurityMD>>(nameof(ShowLaddersTag));
public static ITag<IObservable<SecurityMD>> ShowLinkedLadder { get; } = new SparkTag<IObservable<SecurityMD>>("Linked Ladder");
public static ITag<SecurityCriteria> ShowMarketViewTag { get; } = new SparkTag<SecurityCriteria>("Show MarketView");
public static ITag<OrderBookWindowFactory.OrderBookStartupData> ShowOrderBookTag { get; } = new SparkTag<OrderBookWindowFactory.OrderBookStartupData>(nameof(ShowOrderBookTag));
@@ -0,0 +1,80 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace mbts.ui.controls {
class ObservableAggregateCollection<T> : ObservableCollection<T> {
readonly List<ObservableCollection<T>> _collections = new List<ObservableCollection<T>>();
public ObservableAggregateCollection(params ObservableCollection<T>[] collections) {
foreach (var c in collections)
add_collection(c);
}
public void add_collection(ObservableCollection<T> collection) {
this.AddRange(collection);
_collections.Add(collection);
collection.CollectionChanged += _on_collection_changed;
}
public void remove_collection(ObservableCollection<T> collection) {
_remove_collection_items(collection, out int _, out int collection_index);
_collections.RemoveAt(collection_index);
collection.CollectionChanged -= _on_collection_changed;
}
public void clear_collections() {
foreach (var c in _collections)
c.CollectionChanged -= _on_collection_changed;
Clear();
}
void _on_collection_changed(object sender, NotifyCollectionChangedEventArgs e) {
int offset = _get_collection_start(sender);
switch (e.Action) {
case NotifyCollectionChangedAction.Add: this.InsertRange(e.NewStartingIndex + offset, e.NewItems); break;
case NotifyCollectionChangedAction.Remove: this.RemoveRange(e.OldStartingIndex + offset, e.OldItems.Count); break;
case NotifyCollectionChangedAction.Move: Move(e.OldStartingIndex + offset, e.NewStartingIndex + offset); break;
case NotifyCollectionChangedAction.Replace:
this.RemoveRange(e.OldStartingIndex + offset, e.OldItems.Count);
this.InsertRange(e.NewStartingIndex + offset, e.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
var c = (ObservableCollection<T>)sender;
_remove_collection_items(c, out int starting_index, out int _);
_add_items(c, starting_index);
break;
}
}
void _add_items(IList items, int index) {
foreach (T item in items)
Insert(index++, item);
}
void _remove_items(IList items, int index) {
foreach (var item in items)
RemoveAt(index);
}
void _remove_collection_items(ObservableCollection<T> collection, out int starting_index, out int collection_index) {
int ex_count = 0;
int i = 0;
for (; i < _collections.Count; i++) {
if (_collections[i] == collection)
break;
else ex_count += _collections[i].Count;
}
starting_index = ex_count;
collection_index = i;
for (++i; i < _collections.Count; i++)
ex_count += _collections[i].Count;
this.RemoveRange(starting_index, Count - ex_count);
}
int _get_collection_start(object sender) =>
_collections.TakeWhile(c => c != sender).Sum(c => c.Count);
}
}
+115
View File
@@ -0,0 +1,115 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace mbts.collections {
interface IGroupingSet<TKey, TElement> : IGrouping<TKey, TElement>, ISet<TElement> { }
class SetMap<TKey, TElement> : IEnumerable<IGroupingSet<TKey, TElement>>, ICollection {
readonly IEqualityComparer<TKey> _key_comparer;
readonly IEqualityComparer<TElement> _element_comparer;
readonly Dictionary<TKey, InternalGroupSet> _dictionary;
public SetMap()
: this(EqualityComparer<TKey>.Default) { }
public SetMap(IEqualityComparer<TKey> key_comparer)
: this(key_comparer, EqualityComparer<TElement>.Default) { }
public SetMap(IEqualityComparer<TKey> key_comparer, IEqualityComparer<TElement> element_comparer)
: this(new Dictionary<TKey, InternalGroupSet>(), key_comparer, element_comparer) { }
SetMap(Dictionary<TKey, InternalGroupSet> dictionary, IEqualityComparer<TKey> key_comparer, IEqualityComparer<TElement> element_comparer) {
_dictionary = dictionary;
_key_comparer = key_comparer;
_element_comparer = element_comparer;
}
public int Count => _dictionary.Count;
public object SyncRoot => null;
public bool IsSynchronized => false;
public ISet<TElement> this[TKey key] => _dictionary[key];
public void CopyTo(Array array, int index) =>
((ICollection)_dictionary).CopyTo(array, index);
public IEnumerator<IGroupingSet<TKey, TElement>> GetEnumerator() =>
_dictionary.Values.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator();
public bool add(TKey key, TElement value) {
if (!_dictionary.TryGetValue(key, out InternalGroupSet values))
_dictionary.Add(key, values = new InternalGroupSet(key, _element_comparer));
return values.Add(value);
}
public bool remove(TKey key, TElement value) {
if (_dictionary.TryGetValue(key, out InternalGroupSet values)) {
bool removed = values.Remove(value);
if (values.Count == 0)
_dictionary.Remove(key);
return removed;
}
return false;
}
public bool remove(TKey key) =>
_dictionary.Remove(key);
public void clear() => _dictionary.Clear();
public bool try_pop(TKey key, out ISet<TElement> value) {
if (try_lookup(key, out value)) {
remove(key);
return true;
} else return false;
}
public bool try_lookup(TKey key, out ISet<TElement> value) {
if (_dictionary.TryGetValue(key, out InternalGroupSet result)) {
value = result;
return true;
}
value = null;
return false;
}
public SetMap<TKey, TElement> clone() =>
new SetMap<TKey, TElement>(
_dictionary.ToDictionary(
kv => kv.Key,
kv => new InternalGroupSet(kv.Key, kv.Value, _element_comparer)
), _key_comparer,
_element_comparer);
class InternalGroupSet : HashSet<TElement>, IGroupingSet<TKey, TElement> {
public InternalGroupSet(TKey key, IEqualityComparer<TElement> comparer)
: base(comparer) => Key = key;
public InternalGroupSet(TKey key, IEnumerable<TElement> values, IEqualityComparer<TElement> comparer)
: base(values, comparer) => Key = key;
public TKey Key { get; }
}
}
static class SetMapExtensions {
public static bool removes_key<TKey, TElement>(this SetMap<TKey, TElement> set_map, TKey key, TElement element) {
int count_before = set_map.Count;
if (set_map.remove(key, element))
return set_map.Count < count_before;
return false;
}
public static bool adds_key<TKey, TElement>(this SetMap<TKey, TElement> set_map, TKey key, TElement element) {
int count_before = set_map.Count;
if (set_map.add(key, element))
return set_map.Count > count_before;
return false;
}
}
}
@@ -0,0 +1,124 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace mbts.collections {
/// <summary>
/// Similar to a SortedList, but supports both key and index read-access, the latter is achieved by the at_index() method.
/// It also allows for multiple values per key, by internally creating a HashSet&lt;TValue&gt; for each key.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
class SortedSetMap<TKey, TValue> : IEnumerable<IGroupingSet<TKey, TValue>>, ICollection {
readonly IComparer<TKey> _key_comparer;
readonly IEqualityComparer<TValue> _value_eq_comparer;
InternalGroupSet[] _key_values;
int _count;
public SortedSetMap(int capacity = 4)
: this(Comparer<TKey>.Default, EqualityComparer<TValue>.Default, capacity) { }
public SortedSetMap(IComparer<TKey> key_comparer, IEqualityComparer<TValue> value_eq_comparer, int capacity = 4) {
_key_comparer = key_comparer;
_value_eq_comparer = value_eq_comparer;
_key_values = new InternalGroupSet[capacity];
}
public int Count => _count;
public bool IsReadOnly => false;
public object SyncRoot => null;
public bool IsSynchronized => false;
public IEnumerator<IGroupingSet<TKey, TValue>> GetEnumerator() {
for (int x = 0; x < _count; ++x)
yield return _key_values[x];
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void CopyTo(Array array, int index) => Array.Copy(_key_values, 0, array, index, _count);
public void add(TKey key, TValue value) {
int pos = _binary_search(key);
if (pos < 0) {
pos = ~pos;
_count++;
if (_count + 1 >= _key_values.Length)
_expand();
Array.Copy(_key_values, pos, _key_values, pos + 1, _count - pos);
_key_values[pos] = new InternalGroupSet(key, _value_eq_comparer) { value };
} else {
_key_values[pos].Add(value);
}
}
public bool remove(TKey key, TValue value) {
int index = _binary_search(key);
if (index >= 0 && index <= _count) {
var set = _key_values[index];
if (set.Remove(value)) {
if (set.Count == 0) {
_count--;
Array.Copy(_key_values, index + 1, _key_values, index, _count - index);
}
return true;
}
}
return false;
}
public bool remove(TKey key) {
int index = _binary_search(key);
if (index >= 0 && index <= _count) {
_count--;
Array.Copy(_key_values, index + 1, _key_values, index, _count - index);
return true;
}
return false;
}
public bool contains_key(TKey key) =>
_binary_search(key) >= 0;
public ISet<TValue> this[TKey key] {
get {
int index = _binary_search(key);
if (index < 0)
throw new KeyNotFoundException();
return _key_values[index];
}
}
public IGroupingSet<TKey, TValue> at_index(int index) =>
_key_values[index];
int _binary_search(TKey key) {
int low = 0;
int high = _count - 1;
while (low <= high) {
int i = low + ((high - low) >> 1);
int c = _key_comparer.Compare(_key_values[i].Key, key);
if (c == 0) {
return i;
} else if (c < 0) {
low = i + 1;
} else high = i - 1;
}
return ~low;
}
void _expand() {
var expanded_values = new InternalGroupSet[_key_values.Length * 2];
Array.Copy(_key_values, expanded_values, _count);
_key_values = expanded_values;
}
class InternalGroupSet : HashSet<TValue>, IGroupingSet<TKey, TValue> {
public InternalGroupSet(TKey key, IEqualityComparer<TValue> comparer)
: base(comparer) => Key = key;
public InternalGroupSet(TKey key, IEnumerable<TValue> values, IEqualityComparer<TValue> comparer)
: base(values, comparer) => Key = key;
public TKey Key { get; }
}
}
}
@@ -6,21 +6,27 @@ namespace mbts.ui {
// Value = "{mbts:BTSColor X.y}"
// Some colos are also available as static resources in colors.xaml
public static class BTSColors {
static Brush _header_brush, _header_text_brush, _header_alternate_brush;
static Brush _header_brush, _header_text_brush, _header_alternate_brush, _header_error_text_brush;
static BTSColors() {
_header_brush = new SolidColorBrush(header);
_header_text_brush = new SolidColorBrush(header_text);
_header_alternate_brush = new SolidColorBrush(header_alternate);
_header_brush.Freeze();
_header_text_brush.Freeze();
_header_alternate_brush.Freeze();
_init_brush(header, out _header_brush);
_init_brush(header_text, out _header_text_brush);
_init_brush(header_alternate, out _header_alternate_brush);
_init_brush(header_error_text, out _header_error_text_brush);
}
// freezing brushes makes them thread-safe
static void _init_brush(BTSColor color, out Brush brush) {
brush = color;
brush.Freeze();
}
[DisplayInfo(name = "Default Header")]
public static BTSColor header { get { return BTSCoreColors.base_blue; } }
public static Brush header_brush { get { return _header_brush; } }
public static BTSColor header_text { get { return BTSCoreColors.white; } }
public static BTSColor header_error_text { get; } = new BTSColor(0xFE, 0x6A, 0x6E);
public static Brush header_error_text_brush => _header_error_text_brush;
public static BTSColor header_border { get { return new BTSColor(0x63,0x8A,0xCC); } }
public static Brush header_text_brush { get { return _header_text_brush; } }
public static BTSColor header_alternate { get { return BTSCoreColors.blue_grey; } }
@@ -143,15 +149,22 @@ namespace mbts.ui {
// 'sent' and 'filled_dark' are used in the ladder log viewer to indicate sent/filled orders (both buys and sells)
// 'default_background' and 'filled' seemed too pale against the log viewer's current style
// as we restyle the ladder, and perhaps the log viewer, maybe this will change, in the meantime, here they are.
static readonly BTSColor _sent = new BTSColor(0x92, 0x5B, 0xBE);
static readonly Brush _sent_brush = _sent, _filled_dark_brush = _sent;
static readonly BTSColor _sent = new BTSColor(0x92, 0x5B, 0xBE), _working_order_background = new BTSColor(0xffedcdu);
static Orders() {
_init_brush(_sent, out sent_brush);
_init_brush(filled_dark, out filled_dark_brush);
_init_brush(working_cancel, out working_cancel_brush);
_init_brush(working_order_background, out working_order_background_brush);
_init_brush(modified, out modified_brush);
}
// Base Colors
[DisplayInfo(name = "Orders")]
public static BTSColor default_background { get { return BTSCoreColors.blue_light_lighter; } }
public static BTSColor default_text { get { return BTSCoreColors.black; } }
public static BTSColor sent => _sent;
public static Brush sent_brush => _sent_brush;
public static readonly Brush sent_brush;
[DisplayInfo(name = "Price")]
public static BTSColor price_background { get { return BTSCoreColors.blue_light_dark; } }
@@ -161,11 +174,16 @@ namespace mbts.ui {
// Alerts
public static BTSColor working_status_summary { get { return BTSCoreColors.green; } }
public static BTSColor working_cancel { get { return BTSCoreColors.red; } }
public static readonly Brush working_cancel_brush;
public static BTSColor working_cancel_text { get { return BTSCoreColors.white; } }
public static BTSColor working_order_background => _working_order_background;
public static readonly Brush working_order_background_brush;
public static BTSColor filled { get { return BTSCoreColors.green_light_light_saturated; } }
public static BTSColor filled_dark => BTSCoreColors.green; // used in ladder log viewer
public static BTSColor modified => orange; // used in the ladder log viewer
public static BTSColor filled_dark => BTSCoreColors.green;
public static readonly Brush filled_dark_brush; // used in ladder log viewer
public static BTSColor modified => orange;
public static readonly Brush modified_brush; // used in the ladder log viewer
[DisplayInfo(name = "Order Buy Text")]
public static BTSColor order_buy_text { get { return BTSCoreColors.black; } }
@@ -179,6 +197,12 @@ namespace mbts.ui {
public static BTSColor straddle_theovsmarket_negative { get { return BTSCoreColors.red_light; } }
public static BTSColor straddle_theovsmarket_positive { get { return BTSCoreColors.green_light; } }
// Order Types
public static BTSColor tick_order { get { return new BTSColor(31, 4, 57);}}
public static BTSColor lean_order { get { return new BTSColor(110, 77, 143);}}
public static BTSColor join_order { get { return new BTSColor(15, 1, 29);}}
public static BTSColor leader_order { get { return new BTSColor(40, 9, 72);}}
}
public static class AW2 {
Loaded 100 of 665 files, more files were not shown because too many files have changed in this diff. Show more