Compare commits

..
Author SHA1 Message Date
John Colvin dfce23a33d test 2 2023-07-07 18:03:34 -04:00
John Colvin 709334a1aa test 2023-07-07 17:47:07 -04:00
John Colvin f9a6e0963c chore: Remove Spark.Admin.WebApp
These files were added by accident (leftover from switching branches
from development -> spark_crypto.prod)
2023-07-06 14:52:57 +00:00
John Colvin b2879837c6 chore: Remove devcontainer.json 2023-07-06 12:26:48 +00:00
John Colvin 0d03ddb009 Merge remote-tracking branch 'origin/spark_crypto.prod' into spark_crypto.tick_hotfix 2023-07-06 12:26:28 +00:00
John Colvin a6dfa4ae80 fix: Don't show decimal BTC Futs/Spd qtys
They tick in multiples of 10, so decimal precision isn't needed
2023-07-05 18:20:53 -04:00
John Colvin e66ec82ba8 hotfix: Setup default cfg for new Deribit price ticks 2023-07-05 18:19:49 -04:00
John Colvin 9ce5e971c6 fix: Fix typos in BTSColor paths in xaml 2023-07-05 18:11:40 -04:00
John Colvin 3e7cc49ebb hotfix: Format text w/ RegionalTickFactors in PriceEntryControl 2023-07-05 18:11:40 -04:00
John Colvin d07e68b6e2 hotfix: Use 1st leg's min price tick rule for Deribit combos 2023-07-05 18:11:40 -04:00
John Colvin 619ece2313 refactor: Consolidate impl for 'round_to_tick' overloads
Also add a 'decimal' overload for RegionalTickFactors. This is more work
for Deribit tick hotfix.
2023-07-05 18:11:40 -04:00
John Colvin 1dda9566f3 hotfix: Use '.' separator if tick unit is multiple of base 2023-07-05 18:11:06 -04:00
John Colvin a524ca02a5 refactor: Change tick arithmetic to use int64
The tick count is "price / tick_size" and tick_size is usually < 1, so
the extra bits may be needed to avoid arithmetic overflow
2023-07-05 12:02:28 -04:00
John Colvin 3c97f2552b fix build: Don't use libfmt or ranges in win cpp
I can't deal with this anymore. My build is suddenly broken on Windows
(for spark_crypto.prod branch). I assume they updated the compiler
in a way that isn't backwards compatible. Thanks Windows.
2023-07-05 11:58:14 -04:00
John Colvin 15b1d55ef1 fix build: Disable Binance md code on win 2023-07-05 11:58:14 -04:00
John Colvin 4e42c9f01b test: Refactor tick tests for RegionalTickFactors
Gives us some peace of mind about the dynamic ticks hotfix for Deribit
2023-07-05 11:58:14 -04:00
John Colvin 76775f821a hotfix: Support dynamic ticks in order ticket 2023-07-05 11:58:14 -04:00
John Colvin e7088f6b1c hotfix: Support dynamic ticks in ladder
Deribit now provides dynamic price ticks (a list of ticks per range).

https://www.deribit.com/kb/4-july-2023
https://www.deribit.com/kb/deribit-on-screen-options-tick-size-reduction
2023-07-05 11:58:14 -04:00
Taha 1fb42ade59 Adding perf logs on the front end 2023-07-05 10:50:13 -04:00
Taha 129650e9d4 Only the skew owner publishes theos to the server 2023-07-05 10:50:13 -04:00
John Colvin 170a44ac04 fix: Check sign to tick in correct direction
Don't assume it's positive and blindly tickup
2023-07-03 22:34:46 -05:00
Taha Afzal 7e04f02327 Merge branch 'spark_crypto.prod.autofit' into spark_crypto.prod 2023-07-04 03:21:28 +00:00
Taha Afzal abda2dbf97 VolSurfaces and achived theo history size is not mantained at runtime 2023-07-04 03:13:46 +00:00
Taha Afzal ad1a45cf49 ArchivedTheos are truncated on a server start 2023-07-04 03:13:46 +00:00
John Colvin e9abdd0ff2 chore: Remove unused fn 'gen_prices' 2023-07-03 12:30:03 -05:00
John Colvin 9254002318 fix: Check HasPrice/QuantityDisplay before accessing prop 2023-07-03 12:24:20 -05:00
John Colvin 596780b7f4 chore: Remove unused 'unit_quantity' prop 2023-07-03 12:22:37 -05:00
John Colvin 7ba061ac20 hotfix: Add cfg to account for new deribit tick format 2023-07-02 16:58:53 -05:00
John Colvin c01593d119 fix (ui colors): Cherry-pick synth editor dark theme from dev 2023-06-26 12:26:38 -04:00
Taha 09e2a1764f Only the user with control over the volsurface can save it 2023-06-15 11:44:34 -04:00
John Colvin a780668bce fix: Ensure tick up/down values are within min/max bounds 2023-06-15 11:43:58 -04:00
John Colvin 5b79aeaa14 ui: Enforce min auto-fit period (30 secs) 2023-06-15 11:37:18 -04:00
John Colvin 049c006b5f ui: Disable driver/atm pts moved auto-fit ui 2023-06-15 11:37:18 -04:00
John Colvin 6fdc344472 feat: Add 'acquire ownership' button to vs context menu 2023-06-15 11:37:18 -04:00
John Colvin cb09b93553 chore: Rename ns bts.reactive -> Spark.Core.Reactive 2023-06-15 11:37:18 -04:00
John Colvin 21447d32e5 fix (vs ui): Only log 'is_dirty' changes for editable cols 2023-06-15 11:37:18 -04:00
Taha 28b03e1cd2 Only the user with control over the volsurface can save it 2023-06-14 22:23:45 -04:00
Taha a7acb89c15 Fixing merge conflict 2023-06-14 15:57:42 -04:00
John Colvin 35289710ba fix: Ensure tick up/down values are within min/max bounds 2023-06-14 14:20:47 -04:00
Taha 4677acfb39 Merge branch 'spark_crypto.prod.autofit' of bitbucket.org:bluetradesys/spark into spark_crypto.prod.autofit 2023-06-14 13:55:03 -04:00
Taha ac05d0d69e AutoFitter reads the settings from the settings now 2023-06-14 13:54:53 -04:00
John Colvin 25a9b6b433 fix: Ensure tick up/down values are within min/max bounds 2023-06-14 13:49:49 -04:00
John Colvin b78713ad0d ui: Enforce min auto-fit period (30 secs) 2023-06-14 12:59:40 -04:00
John Colvin 1f3d0e879c ui: Disable driver/atm pts moved auto-fit ui 2023-06-14 12:59:39 -04:00
John Colvin baad4dc68e feat: Add 'acquire ownership' button to vs context menu 2023-06-14 12:22:57 -04:00
John Colvin ef7c0c7a30 chore: Rename ns bts.reactive -> Spark.Core.Reactive 2023-06-14 11:39:06 -04:00
Taha 3b3c4a2a35 Merge branch 'spark_crypto.prod.autofit' of bitbucket.org:bluetradesys/spark into spark_crypto.prod.autofit 2023-06-14 10:21:55 -04:00
John Colvin ef9fc66a31 fix (vs ui): Only log 'is_dirty' changes for editable cols 2023-06-14 09:44:42 -04:00
John Colvin 212d9c853b fix: Init auto fit vals in vs settings editor 2023-06-14 09:09:34 -04:00
Taha 7815b18b6b Merge branch 'spark_crypto.prod.autofit' of bitbucket.org:bluetradesys/spark into spark_crypto.prod.autofit 2023-06-13 22:34:12 -04:00
Taha 05126074d4 Enabling AutoFit 2023-06-13 22:34:07 -04:00
John Colvin 3611d2c1f3 fix: Enable tick up/down buttons for nullable uint32 ctrl 2023-06-13 15:43:33 -04:00
John Colvin b5ad4b8104 feat: Add auto-fit ui for vs settings 2023-06-13 15:43:33 -04:00
Taha 43d7c0a2b5 Comments 2023-06-12 13:43:42 -04:00
Taha 12e361cec7 AutoFit based on a timer works now 2023-06-11 21:59:46 -04:00
John Colvin 82c126362d fix build: Fix a few path errors in publish target 2023-05-31 22:13:52 -04:00
Taha fd2ba70031 Culling the logs 2023-05-29 11:35:49 -04:00
Taha ab410c339f Order Tickets from the sheets are defaulted correctly now 2023-05-29 10:32:29 -04:00
John Colvin 9e3f16d8bc fix build: Update externals so prod builds in new docker 2023-04-05 11:45:24 -04:00
John Colvin a3f194d708 build (web): Upgrade web build to net7.0 2023-04-05 11:39:37 -04:00
John Colvin bf87c46fcd fix build: Remove inappropriate ThreadStatic attr
This has likely been incorrect for a while. The problem surfaced after
upgrading to net7.0, as the new framework emits a warning (CA2259) when the
attribute is associated with a non-static field.
2023-03-07 16:30:59 -05:00
John Colvin 222d4b266e build: Disable warning cs8981
This is a new warning, introduced in net7.0, that disallows lowercase
class names. Our spark_crypto.prod branch currently uses protoc v3.19.1,
which is incompatible with this new warning, as it generates
classes with lowercase names. We can re-enable the warning when we
upgrade protoc to 3.21.12 (this has been done in the api server branch).
2023-03-07 16:25:21 -05:00
John Colvin d593eaf058 build: Upgrade TargetFramework to net7.0
We've been using several net7.0 preview features for a while now. Now that a
stable net7.0 release is available, it is time upgrade officially. This also
aids the effort to build csharp on Linux, where these preview features didn't
work out of the box. Lastly, trying to make these preview features work would
be a waste of time, as namespaces/interfaces in the official release have
changed in a way that isn't backwards compatible w/ the preview features.
2023-03-07 16:17:39 -05:00
John Colvin 55d021ccb1 fix build (publish): Copy libspark.dll to publish dir
Bugs (fixed):
  * Spark.exe generated by the installer crashes immediately on launch because
    it cannot find the libspark.dll.

  Cause: Sure enough, libspark.dll isn't in the install folder
  (AppData\Roaming\bts\Spark)

* Build libspark explicitly
* Fix libspark.dll copy src path
2023-02-07 16:41:11 -05:00
John Colvin b02f814c65 fix (ui): Remove context menu items that do nothing
They were accidentally brought over from Edge. Their functionality
hasn't been implemented in Spark.
2023-01-23 01:34:55 -05:00
John Colvin 8c42aa93a2 chore: Remove unused code 2023-01-23 01:34:55 -05:00
John Colvin 357bc5ad44 fix (ui): Format perp futs 'Perp' suffix (not bogus exp) 2023-01-23 01:34:55 -05:00
John Colvin 07fb9e8eb7 ui (pnl): Use 'default_text' foreground in popup titles
Previously, the foreground for some popups was a hardcoded dark gray,
which became hard-to-read following the Dark Theme changes.
2023-01-23 01:34:55 -05:00
John Colvin 9f9fc4c8ab fix (analytics-exps): Wait for sym list before loading selection
Bugs (fixed):
   * The selected symbols aren't restored with the workspace

   Cause: The selected symbols were getting loaded once, in the ctr, before the
   list of symbols had been properly initialized. The view automatically
   deselects items that aren't in the ItemsSource.
2023-01-22 12:23:03 -05:00
John Colvin 2aef052398 ui (pnl): Use gray font color when pnl is zero 2023-01-22 12:23:03 -05:00
John Colvin 1c2027506a refactor: Clean up NullableColor/FontWeightProto 2023-01-22 12:23:03 -05:00
John Colvin c3274b2008 refactor: Rename argb_color -> to_uint
This is a more appropriate name
2023-01-22 00:11:28 -05:00
John Colvin 99aef9e86e refactor: Get rid of redundant prop 'NullableColorProto.argb'
It was a relic of the past. Although different fns are called in the props'
getters ('rgb_color' vs 'argb_color'), both ultimately return the same value.
2023-01-22 00:09:38 -05:00
John Colvin 8e9233022e fix (mini-ob): Return root element in CancelColumn.view_factory
Bugs (fixed):
  * Mini Orderbook crashes immediately when opened

  Cause: The (cell) view_factory fn wasn't returning the cell's root
  element, and was instead returning the root's child element.
  As a result, when the row attempted to add this element to its visual
  tree, the following exception was thrown:

    System.ArgumentException: Specified Visual is already a child of
    another Visual or the root of a CompositionTarget.
2023-01-21 13:41:04 -05:00
arhamGIT 4c91538f22 base websockets working 2023-01-04 04:34:44 -05:00
Taha Afzal 320427c9c5 Merge branch 'spark_crypto.dev' of bitbucket.org:bluetradesys/spark into spark_crypto.dev 2022-12-25 16:17:12 -05:00
Taha Afzal 5e8543902e Sheet theos change colors for contracts that are in a task 2022-12-25 16:17:01 -05:00
John Colvin edae3c9ab7 build (refactor): Remove 'zip' opt from publish_spark
The slight convenience afforded by this opt doesn't seem worth the
additional complexity. This script produces a self-contained application
folder. Decisions on what to do with that folder (whether or not to zip
it, etc.) are best left upstream.
2022-12-23 11:40:35 -05:00
John Colvin fd9892c6b6 fix: Prevent raw_pb_callback premature garbage collection
Bugs (fixed):
   * Startup crash w/ error message: "A callback was made on a garbage
     collected delegate of type ApiNativeMethods.raw_pb_callback"

   Cause: The callback is only referenced in unmanaged c++. Without references
   in managed code, the Garbage Collector deems the delegate eligible for
   collection before the callback has finished execution.

   Note: The original solution forced a reference in managed code by storing it
   in a class-member 'keepalive' dictionary, whose life isn't bound to the
   [synchronous] scope of the 'send' method. The callback would then remove
   itself from the dictionary in its last line of code. While this reduced the
   chance of such an error occurring, occassional crashes persisted
   (particularly in low-bandwidth environments). I suspect the frequency at
   which garbage-collector threads run is inversely related to the frequency
   at which data arrives from the server, as the latter triggers execution of
   higher-priority threads.

   Steps to repro:
      1. Insert a Thread.Sleep statement (1000 ms should be sufficient) after
         the call that removes the last reference to the delegate (i.e., make
         the thread sleep for a significant amount of time in the callback
         AFTER it becomes eligible for garbage collection).

      2. Build, run, and observe crash on startup.

   Solution: Wrap the callback in another class, and store the wrapper
   in the dictionary rather than the callback itself. The reason why this
   works isn't totally clear, however its effectiveness has been verified
   with the repro (described above).
2022-12-23 11:25:07 -05:00
Taha Afzal 45668baaf8 Adding code to Main 2022-12-20 12:17:51 -05:00
Taha Afzal 305cc0e01c Adding simple concrete classes 2022-12-20 12:00:46 -05:00
Taha Afzal f0f365538b Initial check in for Websocker skeleton 2022-12-19 23:45:52 -05:00
Taha Afzal f9bba0baed Merge branch 'spark_crypto.dev' of bitbucket.org:bluetradesys/spark into spark_crypto.dev 2022-12-19 17:41:38 -05:00
John Colvin 306c18065a chore: Add csproj bones for websocket api 2022-12-19 14:25:37 -05:00
John Colvin c64c71c98c fix build (sparkd install): Copy plugins to install dir 2022-12-15 12:47:35 -05:00
John Colvin 7de87c1c25 ui (refactor): Consolidate (Repeat)Button colors to one class
The only difference between a RepeatButton and a regular Button is in
the click-behavior (they should look identical).
2022-12-15 12:47:35 -05:00
John Colvin 12716bb6e1 fix: Add a couple null checks 2022-12-15 12:47:35 -05:00
John Colvin 8e68d573ff ui (volsurface): Update oxyplot to get rid of 'dummy' win 2022-12-15 12:47:35 -05:00
Taha Afzal dd66e94bfd Merge branch 'spark_crypto.prod' of bitbucket.org:bluetradesys/spark into spark_crypto.prod 2022-12-09 16:59:50 -05:00
Taha Afzal d399f75648 Decreasing the log rate on the server 2022-12-09 16:59:08 -05:00
John Colvin 937173dd2f ui (volsurface): Update oxyplot to get rid of 'dummy' win 2022-12-09 13:23:44 -05:00
Taha Afzal f1f7a40fba Changing some Sheet UI Defaults 2022-12-09 01:53:08 -05:00
Taha Afzal 5522d364d4 Fixing Sheets and SpreadBox colors 2022-12-09 00:47:47 -05:00
John Colvin 3a218d246e ui: Style ListBoxEx w/ dark theme colors
This control used in grid filters (multi-select)
2022-12-08 13:52:14 -05:00
John Colvin bafc28e3bc ui: Touch up orderbook panel (dark theme) 2022-12-08 13:52:14 -05:00
John Colvin 6d1eb474c4 ui (grid settings): Align column-list cell params
The [vertical] alignment issue was only noticeable in the pnl settings dialog
(its cell params section is taller than those in other settings dialogs), where
the shorter columns list was center aligned with the taller cell params.
2022-12-08 13:52:14 -05:00
John Colvin d172f4ffab ui: Touch up grid settings dialogs (dark theme) 2022-12-08 13:52:13 -05:00
John Colvin c527b6e84f ui: Touch up AlgoStudio (dark theme) 2022-12-08 13:51:55 -05:00
John Colvin b8c7abd6b3 ui: Style TickUpDown ctrls w/ dark theme colors 2022-12-08 13:30:55 -05:00
John Colvin fea8e184c1 style: Sort tag attributes in xaml
* Put attached props before regular (i.e., not attached) props
* Sort alphabetically, with exceptions for props that are logically
  grouped together. For example Width/MinWidth/MaxWidth may be
  adjacent, as they are all 'width' props.
2022-12-08 13:30:55 -05:00
John Colvin 44a7bc0de9 style: Format large xaml files
* Improves readability
* Adds new lines to make navigating through the tags easier
* Mostly just white space changes

fix add empty lines

fix empty lines (reword commit to 'white space changes')

fix white space
2022-12-08 13:30:55 -05:00
arhamGIT 4bad9ef183 ladder lines 2022-12-08 13:30:55 -05:00
arhamGIT 590835c627 ladder changed 2022-12-08 13:30:54 -05:00
arhamGIT 997766df92 partial PNL header fix 2022-12-08 13:30:54 -05:00
arhamGIT cdbf66cd6c grid fixed with new row colors 2022-12-08 13:30:54 -05:00
arhamGIT 4c90eed59e bunch of changes for dark theme 2022-12-08 13:30:54 -05:00
John Colvin d260f8ce2c ui: Hardcode dark theme 2022-12-08 13:30:54 -05:00
Taha Afzal 12d0afb215 Merge branch 'spark_crypto.tasking_eye' of bitbucket.org:bluetradesys/spark into spark_crypto.tasking_eye 2022-12-06 15:21:46 -05:00
Taha Afzal a9040f5f8f For TaskingEye, side is not a part of the algo any more. Therefore selecting the bids and starting an eye, makes it go shoot at the bid, and vice verca for the asks 2022-12-06 15:21:37 -05:00
Taha Afzal 6e6acb5a71 Fix for TaskingEyeAlgo with empty security lists (ie when algo is launched from the ladder) 2022-12-06 15:17:54 -05:00
Taha Afzal 256697d5b0 Side is a parameter of TaskingEyeAlgo, it is inferred from the OrderDefinition (like other algos) 2022-12-06 11:06:44 -05:00
Taha Afzal be9067b22e AlgoWrapper was not publishing the filled fractional qtys, fixed now 2022-12-05 22:59:59 -05:00
Taha Afzal a5874a59f0 AlgoWrapper was not reporting fractional qty fills, this fixes it now via using the get_quantity function in orderman_common.h 2022-12-05 21:56:50 -05:00
Taha Afzal e8405e5638 OrderServiceServer starts calling ServiceBridge's do_work only once it gets the ready signal from ServiceBridge 2022-12-05 14:33:04 -05:00
Taha Afzal ab70c9dfb1 Fixing problem with TaskingEye for fractional shot qtys 2022-12-04 23:21:23 -05:00
Taha Afzal 1e4d8584c7 Merge branch 'spark_crypto.tasking_eye' of bitbucket.org:bluetradesys/spark into spark_crypto.tasking_eye 2022-12-04 20:36:25 -05:00
Taha Afzal 29eef77099 ServiceBridge now periodically publishes the TheoEvent (with a flag indicating such). This helps the algos get the theos without waiting for the next update 2022-12-04 20:36:10 -05:00
Taha Afzal c50e89ae78 Merge branch 'spark_crypto.tasking_eye' of bitbucket.org:bluetradesys/spark into spark_crypto.tasking_eye 2022-12-03 00:28:12 -05:00
Taha Afzal 89917713d6 Tasking eye UI work in the sheets 2022-12-03 00:28:00 -05:00
Taha Afzal 1e0ac46e07 Account name is part of the Tasking Eye Algo now 2022-12-02 23:18:06 -05:00
Taha Afzal 6279b8f5f5 Securities (string of sec_ids unfortunately) is a parameter in Tasking Eye Algo now 2022-12-02 15:44:25 -05:00
Taha Afzal 4007c87e20 Merge branch 'spark_crypto.tasking_eye' of bitbucket.org:bluetradesys/spark into spark_crypto.tasking_eye 2022-12-01 09:50:47 -05:00
Taha Afzal ec0f42e76c Adding timer for the theo publishing time 2022-12-01 09:50:37 -05:00
Taha Afzal 99f63c2a6f Fixed a big with the shot throttle 2022-11-30 21:42:54 -05:00
Taha Afzal 12ac20f756 TaskingEyeAlgo code complete, it is firing orders and correctly (needs more testing and ui integration) 2022-11-30 15:39:00 -05:00
Taha Afzal 9d36d74005 TaskingEyeAlgo correctly subscribes and receives driver books 2022-11-29 13:42:15 -05:00
Taha Afzal da2cc977a0 TaskingEyeAlgo hooked up to theos 2022-11-29 00:11:04 -05:00
Taha Afzal 84e7bdb586 Adding TaskingEyeAlgo 2022-11-28 16:57:12 -05:00
Taha Afzal b062a229c8 Added on_theos to IAlgoOrder, and hooked up the plumbing 2022-11-28 10:54:38 -05:00
Taha Afzal 8680b3d815 Publishing the interp theos to the server is almost done 2022-11-25 22:20:08 -05:00
Taha Afzal 6ab0e4c5a7 Merge branch 'spark_crypto.tasking_eye' of bitbucket.org:bluetradesys/spark into spark_crypto.tasking_eye 2022-11-25 16:36:34 -05:00
Taha Afzal 867bcf97d0 Added InterpTheosServerPublisher, which allows to publish precomputed theos to the server 2022-11-25 16:36:26 -05:00
Taha Afzal 360bf9b37e Added get_driver_sec to ITheoCalculator 2022-11-25 16:22:10 -05:00
Taha Afzal 4d85106fbf Added server method to publish precomputed theos, feed it into a calculator, and publish the calculator as an event 2022-11-23 12:52:01 -05:00
Taha Afzal 64a253b581 Fixed the vol change in day bug (difference of uint64s was being assigned to an int32) 2022-10-26 09:37:25 -04:00
Taha Afzal 5f379c1356 SpreadBox minor button display fixes 2022-10-13 07:32:03 -04:00
Taha Afzal 76c80383ce Merge branch 'spark_crypto.dev' into spark_crypto.prod 2022-10-13 00:55:11 -04:00
Taha Afzal e5a6ea80f0 Making the SpreadBox button click more obvious (ToolStripButtons do not have the button like feel of a click) 2022-10-13 00:54:13 -04:00
Taha Afzal b921e644df Merge branch 'spark_crypto.dev' into spark_crypto.prod 2022-10-13 00:13:42 -04:00
Taha Afzal 1c3fb8c192 Merge branch 'spark_crypto.dev' of bitbucket.org:bluetradesys/spark into spark_crypto.dev 2022-10-13 00:00:40 -04:00
Taha Afzal d530d86a30 Merge branch 'spark_crypto.prod' of bitbucket.org:bluetradesys/spark into spark_crypto.prod 2022-10-12 23:57:08 -04:00
Taha Afzal 9d9229ef56 Merge branch 'spark_crypto.spread_creation' into spark_crypto.dev 2022-10-12 23:43:11 -04:00
Taha Afzal 6f6f3c0800 RFQ qty and sides, sent from the client, are now sent over to Deribit 2022-10-12 23:39:44 -04:00
Taha Afzal 7b234044f6 Added controls in the spreadbox that allow for specifying qty and side for an RFQ 2022-10-12 20:08:24 -04:00
John Colvin d46c82f34c feat (ladder): Add side/qty inputs for rfqs 2022-10-12 15:41:57 -04:00
Taha Afzal 4d5af610f0 Added an 'RFQDialog' tag to SparkTags, that launches athe RFQDialog window 2022-10-12 11:55:19 -04:00
Taha Afzal 1e94466f24 RFQ for new spreads are now routed to emumba's create_combo method 2022-10-11 15:55:55 -04:00
Taha Afzal 3210c0bce4 Combo creation at Deribit is turned on from the UI now (it was disabled with a boolean switch prior to this commit) 2022-10-11 10:53:09 -04:00
Taha Afzal 849f4ac579 Merge branch 'spark_crypto.prod' of bitbucket.org:bluetradesys/spark into spark_crypto.prod 2022-10-10 15:14:40 -04:00
John Colvin 6d56f67697 feat: Add volsurface graph settings dialog
A lot of the vm code was already there, but I cleaned it up a bit. The view was
ported over from edge and also cleaned up/tweaked a bit.
2022-10-10 14:46:34 -04:00
John Colvin c0289719e7 fix (pre-emptive): Add null check in 'DisposeAll' fn
Bugs (fixed):
  * The volsurface graph settings dialog crashes when you click OK/Apply

    Note: This fix is 'pre-emptive' because I saw the bug while working on the
    vs graph settings dialog. That feat will be added shortly in a subsequent
    commit (I wanted to commit the fix first so the bug wouldn't be in the
    official 'feat' revision/commit).

    Cause: A null ref ex is thrown in DisposeAll, because the List of IDisposables
    to dispose contains null items.

    Another way to fix the bug would have been to make sure null items aren't
    added to these Lists in the first place. However, that places the burden (to
    check for null) on the callers' shoulders, and leaves open the potential for
    this bug in every place where 'DisposeAll' is called.
2022-10-10 14:35:29 -04:00
John Colvin a32e969b39 fix (ui): Fix ColorPickerControl/Popup SelectedColor binding
There were a few issues here:
  * The ColorPickerPopup was binding it's own SelectedColor prop to the internal
    ColorPickerControl's SelectedColor prop. The SelectedColor prop is typically also
    bound to some (external) viewmodel's 'color' prop. This is problematic as
    Dependency Properties can only be associated with one 'Write' binding.

  * The code designed to keep various sub-color properties in sync with the
    result color was confusing, and didn't seem to work. For example, the ARGB
    sliders' values didn't match those of the 'result' color.
2022-10-10 14:22:34 -04:00
John Colvin f5899e7781 fix (vs graph): Bind series vm color to correct OxyPlot prop
Bugs (fixed):
  * The VolSurface graph colors don't reflect the settings. They appear to be
    randomly generated.

   Cause: The series vm's color prop was bound to 'TextColor' in the model; it
   should have been bound to 'Color'
2022-10-10 11:17:17 -04:00
John Colvin ed07c956a7 ui: Remove quotes around 'Enter' in col filter prompt
Looks cleaner this way, and isn't clipped given the current width
2022-10-10 11:15:14 -04:00
Taha Afzal 2022ee5a07 Completing Deribit execution test 2022-10-10 11:01:59 -04:00
Taha Afzal a0bc1e8691 Spreader can handle options now 2022-10-09 23:56:38 -04:00
Taha Afzal c1951ac49a Added ability to view md/theo in USD, in the sheets 2022-10-08 21:45:57 -04:00
John Colvin e159e0e77b ui (man fills): Add 'Time Entered' column 2022-10-07 15:04:36 -04:00
John Colvin 6bbd4134ec ui (man fills): Use foreground to indicate buy/sell 2022-10-07 15:04:35 -04:00
John Colvin 8a72b2150a ui (rec rep): Use font weight to indicate price/qty discreps 2022-10-07 15:04:35 -04:00
John Colvin e4d5d1eeb2 ui (rec rep): Use foreground to indicate long/short pos 2022-10-07 15:04:35 -04:00
John Colvin 6bf2726b63 ui (rec rep): Filter out system spread positions
The exchange splits spread positions into the individual legs
2022-10-07 14:50:10 -04:00
Taha Afzal a63af0cb46 Manual tests for Deribit order execution 2022-10-06 22:37:39 -04:00
John Colvin b2a7e1d2ed feat: Add 'Send RFQ' button to ladder 2022-10-05 13:35:00 -04:00
John Colvin 32a38b3b5a refactor: Remove unused code 2022-10-05 13:35:00 -04:00
John Colvin 8533bcf0a7 style: Format LadderView.xaml
It's a big file. Irregular formatting/spacing makes it difficult to navigate.

* Use max line width of 100
* Add empty line after multiline tags
* Trim trailing white space
* Rename terse xml namespaces:
   - 'converters' -> 'cv'
   - 'spark' -> 'sp'
   - 'mbts' -> 'u'

fix fmt ladder
2022-10-05 13:34:39 -04:00
John Colvin e00eae1129 refactor (ui): Enable auto-refresh in column-text filters
feat (mview): Enable auto-refresh in security filter

This behavior is (more) consistent with other modern search uis
2022-10-05 01:05:24 -04:00
John Colvin 95595c4cbf fix: Arrange Adorner by target's RenderSize (not Desired)
Bugs (fixed):
  * The Adorner that is used to render a text prompt on top of text boxes appears
  clipped; it doesn't span the width of the TextBox.
2022-10-05 01:05:24 -04:00
John Colvin aad36a4972 fix (mview): Prevent icon flicker when selection changes
Observe changes to MarketViewVM.current_row' rather than
'MarketViewVM.selected_rows'. The latter (collection) is momentarily empty when
the selection switches from one row to another, during which time the buttons
are disabled, hence the flicker.
2022-10-05 01:05:24 -04:00
Taha Afzal cc03f47d70 SpreadBoxes can be brought up from MV 2022-10-04 22:17:46 -04:00
Taha Afzal 5c5e5c23ef SpreadDisplayForm's start location is not tied to the launching window now 2022-10-04 21:31:30 -04:00
John Colvin 4b531a2a1a feat: Add 'Send RFQ' button to mview 2022-10-04 18:07:30 -04:00
John Colvin 35530bec0b refactor: Remove unused variable 2022-10-04 16:42:50 -04:00
John Colvin 5853bb2436 refactor: Make RFQSend tag data immutable
It's safer this way. The data doesn't need to be mutated.
2022-10-04 16:39:01 -04:00
John Colvin bfdf45d30d fix (ui): Fix typo so spreads' qty metadata comes from 1st leg 2022-10-04 15:33:11 -04:00
Taha Afzal a330bc5020 MV and Ladder use GreekTuple.normalized_theo now 2022-10-03 23:20:48 -04:00
Taha Afzal 66f68ef3ec Fixing the spread filled price (EMNativeOrder needed to ignore spread_leg like the GLBX and ICENativeOrders 2022-10-03 23:19:10 -04:00
Taha Afzal d6e651129b RFQs are fully hooked up 2022-10-03 14:54:09 -04:00
Taha Afzal d4a59b8253 UI sends RFQ requests (and almost combo creations) 2022-10-03 00:04:55 -04:00
Taha Afzal 569f1460a4 Wiring up RFQ sending 2022-10-03 00:04:55 -04:00
Taha Afzal 2f369dbd57 RFQ wiring complete 2022-10-02 22:20:44 -04:00
Taha Afzal 444e486862 Changing RFQ Proto 2022-10-01 22:15:05 -04:00
Taha Afzal b42504d827 RFQ work to identify the spreads 2022-09-30 15:26:22 -05:00
Taha Afzal 18ac85a454 SpreadBox can identify listed spreads, shows the market data as well as allows orders (through ladder) and RFQs 2022-09-30 15:26:22 -05:00
Taha Afzal 788a1a06c5 Greeks for Spreads and Futures are added to bus's Greeks 2022-09-30 15:26:22 -05:00
John Colvin 787b485bce fix (ladder): Init qty input to null (not nan)
Bugs (fixed):
  * The ladder qty box defaults to 'NaN' when opened
  * Cannot change qty input via keyboard until 'NaN' is cleared
2022-09-28 22:57:36 -05:00
John Colvin 788df0e888 fix build (gcc-11): Include optional 2022-09-28 21:22:47 -05:00
Taha Afzal 916532f7c5 Fixing formatting of Deribit spreads 2022-09-28 21:22:47 -05:00
Taha Afzal 3c511bb177 RFQ hooked up on the server side 2022-09-28 21:22:47 -05:00
Taha Afzal 49f13755c6 Adding RFQ to OrderService interface 2022-09-28 21:22:47 -05:00
Taha Afzal dcb4b1ae7e Spread leg fills at Deribit now have the correct prices 2022-09-28 21:22:47 -05:00
Taha Afzal 2152b48fe2 Spread execution is almost done, leg prices are not correct 2022-09-28 21:22:47 -05:00
John Colvin b6f2f99f21 improve: Get spread qty metadata from first leg 2022-09-28 21:22:47 -05:00
John Colvin c44df8ec06 style: Add documentation to ContractLegEnumerable 2022-09-27 17:03:58 -05:00
John Colvin 493f082d6d fix (mview): Apply sec price format to theo & driver 2022-09-27 16:01:49 -05:00
John Colvin bbe76b2709 feat (ladder): Show theo, delta, vega in left panel 2022-09-27 15:53:22 -05:00
John Colvin ce8eda24fc refactor: Make vm prop changed subs more performant 2022-09-27 10:53:39 -05:00
John Colvin 9f9113bf9a refactor: Clean up LadderSettingsVM a bit
* Add readonly modifiers
* Declare 1 member per statement (don't separate members of same type by commas)
2022-09-26 13:34:50 -04:00
John Colvin a199595938 feat (part): Add 'Spread Box' menu item in mview
It posts the selected securities to the 'SpreadBox.ShowSecurities' tag.

TODO: Hook up handler to launch spread boxes in response to the
'SpreadBox.ShowSecurities' tag
2022-09-22 23:51:51 -04:00
Taha Afzal 83de5fac75 Deribit spread market data is now written to shared memory 2022-09-22 23:51:48 -04:00
Taha Afzal c688657d53 Vol surfaces are curtailed to the newest 500 at server startup 2022-09-22 23:51:41 -04:00
John Colvin a5c62c66b1 refactor (ladder): Get theos from Greeks tag
The old tag, KeyedTheos, is redundant and will be removed soon
2022-09-22 22:04:44 -04:00
John Colvin 6d595d18ee fix: Fix typo in requirements.txt ('=' -> '==') 2022-09-21 01:55:28 -04:00
John Colvin 946e8f1c47 Update externals
* Removes no-longer necessary node_modules dir
2022-09-21 00:17:31 -05:00
John Colvin b765c3756d chore: Remove arbitrary [vscode] 'tasks.json'
Vscode json files (settings, tasks, launch, etc.) should be tracked in the
dev/<name> dirs
2022-09-21 00:17:31 -05:00
John Colvin 54dc7bf6c3 fix (webapp): Add method without 'set_' prefix in generated js
We already do this for 'get_' prefixes
2022-09-21 00:17:31 -05:00
John Colvin 6142a08db6 build (client): Copy libspark.dll to 'publish' dir
This dll isn't automatically copied when 'dotnet publish' is executed, because
dotnet core isn't really integrated with cpp
2022-09-21 00:17:31 -05:00
John Colvin ca59976e00 fix build (cpp win): Use explicit void template arg in memcpy 2022-09-21 00:17:31 -05:00
John Colvin 7f7a160916 fix build (cpp win): Disable ccapi code in windows 2022-09-21 00:17:31 -05:00
John Colvin c8eb51c303 build (refactor): Move web build scripts to tools/py
Even though they're specific to the web CMakeLists proj, it's good to have all
build scripts in one place
2022-09-21 00:17:31 -05:00
John Colvin 4160233a58 build: Get rid of redundant setup_builds.sh
The new build_sparkd.py script handles both configuration and actual build
2022-09-21 00:17:31 -05:00
John Colvin c5f79f754b build: Compile sparkd w/ newer gcc v11 2022-09-21 00:17:31 -05:00
John Colvin 5f568d681a Update docker/linux to get gcc v11 2022-09-21 00:17:28 -05:00
John Colvin 83a2147428 build (sparkd): Enable Binance for ccapi 2022-09-21 00:14:38 -05:00
John Colvin 9dd2cee6d9 refactor: Repurpose ccapi ftx-impl for general use 2022-09-21 00:14:38 -05:00
Taha Afzal 57365013bd CCMarketDataListener is mostly complete, error handling and RFQs/state remains 2022-09-21 00:14:38 -05:00
Taha Afzal 3d55ef075b CCMarketDataListener writes its security to shared memory now 2022-09-21 00:14:38 -05:00
Taha Afzal 14da6461b1 Constructor added to CCMarketDataListener 2022-09-21 00:14:38 -05:00
Taha Afzal e84d3df024 Adding skeleton 2022-09-21 00:14:38 -05:00
John Colvin ed38fe0ce9 fix build (web): Install webpack-cli 2022-09-21 00:14:38 -05:00
John Colvin b5daee3bcf build (web): Replace deprecated fn copy_tree w/ copytree 2022-09-21 00:14:38 -05:00
John Colvin f5c8c29f5a build: Migrate web build to net6.0
Clean up the web build process a bit while we're at it
* Add 'custom_commands' to run npm install in CMakeLists
* Generate tsconfig files for CMake preset configurations
* Rename local variables to _snake_case in CMakeLists
2022-09-21 00:14:38 -05:00
John Colvin 89fa2eae50 build: Enable web build in wsl
Not sure why this was disabled in the first place
2022-09-21 00:14:38 -05:00
John Colvin 41b8c9238c build: Remove server/sparkd from libspark include path 2022-09-21 00:14:38 -05:00
John Colvin 28b66306da chore (linux build): Add benign changes to satisfy gcc11 2022-09-21 00:14:38 -05:00
John Colvin 0bdbc1897a chore (linux build): Add benign changes to satisfy clang13 2022-09-21 00:14:38 -05:00
John Colvin c3d9443a89 build: Automate venv activation in server build 2022-09-21 00:14:38 -05:00
John Colvin 400db13a78 build: Update cmake-min-version to 3.12
Several 'add_compile_definitions' calls were added to the root
CMakeLists.txt as part of the ccapi_cpp lib integration (3.12 is the
version in which that call was introduced)
2022-09-21 00:14:38 -05:00
John Colvin 1d7d5858d5 build: Add script to build sparkd (server)
Similar to the py script used to build spark (client), 'build_spark.py'.
2022-09-21 00:14:38 -05:00
John Colvin 8792f24c5d refactor (build): Move '_exit' to common module logging
Also rename 'exit_msg' since 'exit' is already a builtin fn
2022-09-21 00:14:38 -05:00
John Colvin 76d35b16eb refactor (build): Change SparkPaths to return Path objs 2022-09-21 00:14:38 -05:00
John Colvin 81f7fe72ab refactor (build): Move CWD & SparkPaths to common module file_utils 2022-09-21 00:14:38 -05:00
John Colvin 649ba11edb Update docker/linux 2022-09-21 00:14:32 -05:00
John Colvin 2fc6d7c262 Update externals
* Upgrade ccapi to v5.31.2
* Upgrade emumba to v3.0.6
* Update emumba license (good til 2023-08-01)
2022-09-19 17:50:40 -05:00
John Colvin 45421eedce chore: Move 'CME' specific script to 'cme' dir 2022-09-19 17:50:40 -05:00
John Colvin 8ecb3118e4 refactor (script): Use Path type rather than str 2022-09-19 17:50:40 -05:00
John Colvin 3fdb10890d chore: Rename scripts w/ consistent grammar 2022-09-19 17:50:40 -05:00
John Colvin 2a6224c88d chore: Move py scripts/utils into 'py' dir 2022-09-19 17:50:40 -05:00
John Colvin 8d6ddc8250 chore: Remove unused legacy scripts 2022-09-19 17:50:40 -05:00
John Colvin 6cc8590a1c build: Use venv python to execute buildspark.py
Previously, the virtual environment was activated in the Proto3 build target.
Since the proto3 build target is executed once per proto file, and 91 proto
files are processed during the client cs build, this added significant time to
the build. For example, on [John's]my laptop, activating the venv takes ~0.15s,
which amounts to ~9.55 secs per build.
2022-09-19 17:50:40 -05:00
John Colvin 063d908775 build: Remove unused legacy build script 2022-09-19 17:50:40 -05:00
John Colvin 93565f0ebf fix (jenkins): Update jenkins build procedures for spark-crypto 2022-09-19 17:50:40 -05:00
John Colvin 1d7fcf5d18 build: Move ':create_venv' fn to its own cmd script
So jenkins scripts can reference it as well as buildspark.cmd
2022-09-19 17:50:40 -05:00
John Colvin 897d20f12f build: Refactor buildspark.py so 'build' fn can be imported
This way, jenkins build/deploy py scripts can import 'buildspark.build', and thus
don't have to than re-invent the 'build' wheel
2022-09-19 17:50:40 -05:00
John Colvin a928925252 build: Add 'publish' target to build script for jenkins 2022-09-19 17:50:39 -05:00
John Colvin 4088affa15 fix (sparkd clang): Use 'clang' c compiler w/ 'clang++'
Otherwise cmake picks 'cc' by default

Bugs (fixed):
  * Sparkd crashes due to strange runtime error related to multithreaded access
    of std::promise in clang builds
2022-09-19 17:50:39 -05:00
Taha Afzal 7def6aacd2 MergedMDDaemon has a timer that can periodically remove expired contracts (not enabled right now) 2022-09-16 09:51:27 -04:00
Taha Afzal 94ae1780cd Expired contracts are filtered out from the risk report 2022-09-16 09:51:27 -04:00
John Colvin 2c65c4376b fix (orderbook): Invalid row view when qty display changes
Bugs (fixed):
  * Orderbook qty formatting is inconsistent across Orderbook instances. Within
    one Orderbook, the formatting does seem to be consistent.
2022-09-15 16:02:34 -04:00
John Colvin 68bb3dbfa1 refactor: Remove redundant member 'filled_quantity_text'
It is redundant since another prop, 'display_filled_quantity_text', returns the
exact same value. Eventually we should bring this prop back and replace the
'display_' one. The latter is a legacy prop, from the time before we had proper
Security metadata config. Removing the former for now because having both of
them causes needless confusion.
2022-09-15 15:58:23 -04:00
John Colvin 82a73693a6 fix: Post keyed theos to bus
They were already being created, just not published (perhaps due to a bad merge)
2022-09-15 15:39:38 -04:00
John Colvin 612cd4a4c8 fix: Remove refs to invalid 'default' SecurityImmutable
It's most (probably all) cases where a static 'default' SecurityImmutable is
used, a Nullable<SecurityImmutable> would more accurately represent the state,
which could likely (probably always) be described as "waiting for my real
security from an asynchronous source."

Bugs (fixed):
  * [Race] When Fills window opens, an exception is thrown if the sec-filter's
    items are sorted with a 'default' SecurityImmutable.

    Cause: The static default SecurityImmutable doesn't have an underlying sec
    type (i.e., ContractOneofCase == None). The compare fn doesn't support
    this.

    TODO (maybe): It might be a good idea to 'fix' the compare fn as well
    (i.e., make it support 'None' sec types)

TODO (probably): Remove remaining references to the static default
SecurityImmutable
2022-09-15 15:39:38 -04:00
John Colvin b3828dfc53 refactor: Add lazy param factory logic to SparkCommand
Some SparkCommands convert their parameter to another object that is posted to
the bus when the command is executed. The lazy param factory logic allows those
SparkCommands to hold off on converting the parameter until just before execution.
Thus, these SparkCommands naturally avoid passing invalid parameters to the
delegate (assuming the 'can_execute' evaluates to false for invalid parameters).
2022-09-15 15:39:38 -04:00
John Colvin 01f26567ed refactor: Get rid of SecurityGrid base class
It was pretty old code, and needed to be removed due to its dependence on
non-null SecurityImmutables. The 'default' SecurityImmutable object is invalid,
and throws in its property accessors since the underlying Security object is null.
2022-09-15 15:27:35 -04:00
John Colvin 90d21c9c15 refactor (synth man): No need to support vm reassignment
This was over-engineering..it made things complicated for zero benefit
2022-09-15 15:27:35 -04:00
John Colvin 6351c1340e refactor: Rename SelectManyInSeries -> SelectSeries
Shorter is better in this case
2022-09-15 15:27:35 -04:00
John Colvin 18cdddf76d git: Remove phantom submodule
Can't recall why 'deribit-crypto-connector-demo-application' was added as a
submodule in the first place. It has no actual corresponding files in the main
repository, just an entry in the '.gitmodules' file. Removing it now because
it's causing jenkins builds to fail (jenkins attempts to clone all submodules
in the .gitmodules file, and it doesn't have access permission on this one).
2022-09-08 22:07:05 -04:00
John Colvin b14c8bb1ac Update docker/linux
* Get ccapi deps again (after rebasing the 'ccapi deps' commit in
  docker/linux)
* Upgrade dotnet from a mixture of v2 & 3 to v6.0
2022-09-04 14:37:59 -04:00
John Colvin ceff286120 fix build (server w/ ccapi-disbaled): Add ifdef SPARK_ENABLE_CCAPI ppds 2022-09-02 11:48:12 -04:00
John Colvin 59aa4677e8 Update docker/linux
To get rapidjson-dev (ccapi dep)
2022-09-02 11:31:58 -04:00
John Colvin 996cd4c856 fix (ladder settings ui): Init 'use_self_security' properly
When loading settings obj
2022-09-02 10:30:08 -05:00
John Colvin 9287149c26 refactor: Clean up 'bts.api.core.Result<T>'
Impl ToString while we're at it
2022-09-02 10:14:02 -05:00
John Colvin 306b56a999 fix (hopeful): Make sure callback disposed after subscription
Hopefully this fixes the 'Garbage Collected callback' crash on login
2022-09-02 09:45:02 -05:00
John Colvin 812cf0a43f fix (man fills ui): Remove erroneous extra ladder button 2022-09-02 09:42:25 -05:00
John Colvin 2c539fd833 ui (ladder settings): Select self in sec-picker by default
For pre-existing fair value lines (this behavior's already implemented for new
fair value lines)
2022-09-01 11:07:51 -04:00
John Colvin 334295ed67 fix (ladder settings ui): Fix typo in binding
Resolve discrepancy between between xaml/cs

Bugs (fixed):
  * Color palette section is empty in Fair Value Line color picker
2022-09-01 01:25:58 -04:00
John Colvin c157b9cdfd ui (ladder): Add 'Use Self Security' checkbox to settings ui
Incorporate it into the settings protobuf by making the security_key field
nullable and allowing 'null' to mean 'Use Self Security'
2022-09-01 01:21:09 -04:00
Taha Afzal 8280b7a815 Fixing Deribit order minor issues that were causing the spreader to cancel its child orders 2022-08-31 15:17:35 -04:00
Taha Afzal fb7f982c65 Improving the logs 2022-08-31 15:17:35 -04:00
John Colvin 75b50d93e8 feat (orderbook): Add button to launch Ladder 2022-08-31 01:13:42 -04:00
John Colvin 865bdac9df refactor: Enable nullable in LadderWindow.cs 2022-08-31 01:13:42 -04:00
John Colvin ebe5f468d7 feat (open orders): Add button to launch Ladder 2022-08-29 22:54:48 -04:00
John Colvin 1bc8d886f4 fix: Don't reopen window on synchronous shutdown
Even if 'e.Cancel' is true, as the initial 'Closing' event is always canceled
to allow for asynchronous teardown
2022-08-29 22:54:48 -04:00
John Colvin 38e352e0d3 feat (man fills): Add button to launch Ladder 2022-08-29 22:54:48 -04:00
John Colvin f6079e7031 refactor: Clean up ManualFillsWindowFactory
After refactoring the shell window logic into new 'ManualFillsWindow' class
2022-08-29 22:54:48 -04:00
John Colvin 8c51488190 refactor: Make proper ManualFillsWindow class
Instances of this class are generated instead of 'anonymous' shell windows.
This makes for cleaner code, better stack traces, etc.
2022-08-29 22:54:48 -04:00
John Colvin 9aad08ada6 feat (day fills): Add button to launch Ladder 2022-08-29 22:54:48 -04:00
John Colvin 2d3d11ba59 fix (ui): Set disabled animation KeyTime to 0
Bugs (fixed):
  * The disabled button animation in SparkMenu (toolbar) is laggy
2022-08-29 22:54:47 -04:00
John Colvin 6087953185 feat (fills): Add button to launch Ladder 2022-08-29 22:54:47 -04:00
John Colvin 851654c8a5 feat (rec rep): Add button to launch Ladder 2022-08-29 22:54:47 -04:00
John Colvin 57a4745e29 feat (ladder): Add 'Theo' fair value line 2022-08-28 14:13:10 -05:00
John Colvin 32cd00895e fix: Add null check in ContractPickerVM
The ContractPickerPopup's SecurityMap binding source may produce null values
during normal operations (esp when opening/closing the popup, as the binding
source may depend on visuals that are initialized/destroyed when the popup
opens/closes).
2022-08-28 13:51:04 -05:00
John Colvin dee5f2684f fix: Add 'IsEnabledForegroundConverter' to ResourceDictionary
Bugs (fixed):
  * Client crashes when ladder settings dialog is opened
2022-08-28 13:21:27 -05:00
John Colvin f639a526cf refactor: Consolidate Co2 & md dirs into MarketData dir 2022-08-28 11:16:56 -05:00
Taha Afzal d168d8712e Added Keyed Theos that can be used to get theos from security ids 2022-08-25 11:58:54 -05:00
John Colvin 78a9605383 Update docker/linux
To get latest cmake
2022-08-24 01:34:38 -05:00
John Colvin a713b1018e fix (preemptive pnl): Actually use eq_comparer member
To determine when value changes occur (when to raise prop-changed event, etc.)
2022-08-23 16:54:31 -04:00
John Colvin 9f626dd10c ui (launcher): Use consistent margin between buttons 2022-08-23 16:54:31 -04:00
John Colvin 8ec7fdd60a ui (launcher): Remove pointless separator bar 2022-08-23 16:54:17 -04:00
John Colvin ffb2245ae6 refactor: Clean up immutable.pb.cs code-gen template a bit more
So the generated code more closely follows our formatting practices
2022-08-23 16:54:17 -04:00
John Colvin 21051c9a0b fix (pnl): Support Exp group popups filtering 2022-08-23 16:54:17 -04:00
John Colvin 1c8bd34144 ui (launcher): Remove pointless Help button 2022-08-23 02:29:05 -04:00
John Colvin f614fb7e7d ui (pnl): Indicate 'has day trades' w/ check mark
Instead of 'Yes/No'
2022-08-22 17:10:03 -04:00
John Colvin b222cd3e55 ui (pnl): Drop border between group/section headers
It needlessly clutters the ui, since the contrasting backgrounds already
illustrate the distinction
2022-08-22 16:55:18 -04:00
John Colvin 4f2be64813 ui: Rename 'P&L' -> 'P&L Summary', 'Day P&L' -> 'P&L Day Fills' 2022-08-22 16:43:31 -04:00
John Colvin bfb73ffc5a refactor: Add 'Freeze' prop to BTSBrushExtension 2022-08-22 16:08:40 -04:00
John Colvin 9bb7025752 fix: Don't share nonfrozen Brushes across threads
Bugs (fixed):
  * Grid settings dialogs crash when multiple are opened on diff threads

  Cause: Brushes (and all Freezables) that haven't been frozen throw an ex when
  any Dependency Properties are accessed from a thread that isn't associated
  with their Dispatcher (i.e., the thread on which they were created).
2022-08-22 16:08:40 -04:00
John Colvin bbfbd0b691 ui (launcher): Drop 'Reports' from buttons in Analytics module
Also change 'Vol Surfaces' -> 'Vol Surface' to be consistent (dropping
'Reports' from the other buttons made them singular)
2022-08-22 11:41:10 -04:00
John Colvin 5628e96a90 ui: Speed up expand/collapse animation in settings dialogs 2022-08-22 10:03:28 -04:00
John Colvin ef517b7554 ui: Reduce column resize-grip width
It blocks too much of the column filter's clickable area
2022-08-22 01:46:01 -04:00
John Colvin ba06af327d ui (slide, combo): Drop ':' in last trade desc 2022-08-22 01:30:31 -04:00
John Colvin b8b03172bc ui (slide): Display vol save & update times adjacently 2022-08-22 01:30:31 -04:00
John Colvin fb5c3c1e6e fix (orderbook-panel): Stretch visible items to fill dead space
Clean up the OrderBookView's VisualState's a bit in the process. Set each
item's visibility to 'Collapsed' by default, and flip to 'Visible' in the
VisualStates
2022-08-22 01:30:31 -04:00
John Colvin faaa7b11d4 fix (orderbook-ladder): Auto scroll to selected order
This used to work, not sure when it broke
2022-08-22 01:30:31 -04:00
John Colvin 92a0467e1e fix (orderbook-ladder): Detect LadderVM changes w/ default Equals
Bugs (fixed):
  * OrderBook ladder doesn't show orders
  * OrderBook ladder doesn't allow user to scroll beyond initial price range

  Cause: Both bugs occur because the LadderVMContainer doesn't detect LadderVM
  instance changes unless LadderVM.security has changed
2022-08-22 01:30:31 -04:00
John Colvin 3bb2025ea2 fix (ladder): Make sure price-range 'busy' is flipped back to false
Once the async price-range-shift op is completed
Bugs (fixed):
  * Can't scroll beyond initial price range in orderbook ladder

  Cause: The LadderGridHost's price-range-shift 'busy' flag gets stuck to false
2022-08-22 01:30:31 -04:00
John Colvin f2730ff0d7 doc: Add documentation for Task.ignore ext 2022-08-22 01:30:31 -04:00
John Colvin 7fcff9e5b4 refactor: Clean up MarketViewAnimation code a bit 2022-08-22 01:30:31 -04:00
John Colvin 51f83f4880 refactor: Rename 'HashCode.with' -> 'with_values'
Repurpose 'with' to accept a single value
2022-08-22 01:30:31 -04:00
John Colvin 88cb510aea refactor: Remove the ulong 'lldatetime' ext
It's too esoteric. Most ulongs don't represent an 'LLDateTime'
TODO: Consider removing the uint 'argb' extensions for same reason
2022-08-22 01:30:14 -04:00
John Colvin 452c642af0 refactor: Rename TryFirst ext FirstOrNull
Also move to it from APIExtensions to EnumerableExtensions
2022-08-22 00:05:08 -04:00
John Colvin 3fafc5004e refactor: Clean up LazySubscription, move to Spark.Core.Disposables ns 2022-08-22 00:05:07 -04:00
John Colvin fd8e502140 refactor: Lock private obj (not 'this')
'Lock(this)' is generally bad practice. It's better to use an explicitly named
var that is minimally accessible to outside callers to reduce risk of deadlocks.
2022-08-22 00:05:07 -04:00
John Colvin b499a5c38b refactor: Move IDisposable utils to Spark.Core.Disposables ns
* Move Anonymous/FinalizableDisposable to own file
* Rename DisposableCombo -> ImmutableDisposable and move it to its own file
* Clean up IDisposable utils impl, but keep changes to calling code minimial
2022-08-22 00:05:04 -04:00
John Colvin 2320383b16 refactor: Remove DisposableContainer class
It's innately antithetical to the Dispose pattern, as it can never truly be
disposed
2022-08-22 00:03:11 -04:00
John Colvin 59b3e73330 fix: Assign IDisposable to _dispose member (not local var)
So it is properly disposed, and not just eventually collected by GC
2022-08-22 00:03:11 -04:00
John Colvin 9d9a1b86c4 fix: Fix typo in 'init_auto_login'
The 'SubscribeClosed' call should be executed immediately, and the returned
IDisposable disposed when 'login_hooks' is disposed (prior to this commit, it
wasn't calling 'SubscribeClosed' until login_hooks was disposed)
2022-08-22 00:03:11 -04:00
John Colvin 21cc2cefbf refactor: Un-nest SelectorWrapper class, rename SelectorList 2022-08-22 00:03:11 -04:00
John Colvin 904df4260d fix (preemptive): Fix typo in SelectorWrapper list
Bugs (fixed):
  * [preemptive] SelectorWrapper.IsReadOnly is wrong

  Details: The fix is considered 'preemptive' because this prop is almost
  certainly not referenced. Nevertheless, the impl ought to be correct.
2022-08-22 00:03:11 -04:00
John Colvin af785d5d0b fix: Make sure ThreadSafeEvent uses same lock as owner 2022-08-22 00:03:11 -04:00
John Colvin 1d796856ca refactor: Make spark_api depend on spark_core (not other way around) 2022-08-22 00:03:11 -04:00
John Colvin 35f0d5a53f refactor: Split (Equality)Comparer utils up into modular classes 2022-08-22 00:03:10 -04:00
John Colvin 2b2fdd2b94 refactor: Remove borderline useless TaskExtensions
There were few references to these extensions, and many weren't referenced at all
2022-08-22 00:03:10 -04:00
John Colvin 9e4539a902 refactor: Remove unused code 2022-08-22 00:03:05 -04:00
John Colvin a76c8ffd98 fix: Fix typo 'is not null' -> 'is null'
Bugs (fixed):
  * Window size isn't restored if window has multiple tabs

  Cause: The typo flips the bool arg that indicates whether or not a window
  should be measured and fit to size (as happens when a window is opened for
  the first time)
2022-08-18 16:17:22 -04:00
John Colvin d7474cb8da fix (ui): Await settings-save before disposing IShellWindow
Bugs (fixed):
  * Can't reopen Fills windows via undo/redo in the BookMarks module

  Details:
    * Only applies to Fills windows closed via the tab-close 'x' button (not
      the window-close 'x' button)

    * Doesn't cause a crash, nor exhibit any side effects (aside from breaking
      undo/redo)

  Cause:
    * ISlow/SharedWindows (i.e., shell windows that run on bg threads) are
      disposed before the ISettingsCollector retrieves their state via
      'IShellWindow.SaveState'. The FillsWindow sets its _vm member to null
      when it is disposed, leading to a null-ref ex in its SaveState getter.
      This ex is caught (synchronously) and logged as an error, which explains
      the lack of side effects.
2022-08-18 13:57:44 -04:00
John Colvin 890a23a537 refactor: Rename AsyncEvent.invoke -> Invoke
To be syntactically consistent w/ csharp delegates (and the wpf Dispatcher,
which has Invoke/InvokeAsync methods)
2022-08-18 12:39:57 -04:00
John Colvin ab17eee260 refactor: Remove unused code 2022-08-18 12:39:57 -04:00
Taha Afzal acadc3bd7c Fixing merge conflict 2022-08-17 20:26:23 -04:00
Taha Afzal 8701fb7132 Fixing qty in ExchangeOpenOrders for fractional qty orders 2022-08-17 14:12:23 -04:00
Taha Afzal 89fcb3092b Fixing merge conflict 2022-08-17 12:54:27 -04:00
John Colvin c9856b96ae fix build (client): Remove erroneous using statement 2022-08-16 21:12:49 -04:00
John Colvin 04477a4b7c refactor: Don't copy strings 2022-08-16 21:08:53 -04:00
John Colvin 99371a2ead feat: Add Open Orders window 2022-08-16 17:25:03 -04:00
John Colvin 70487a8ef7 ui: Make 'B/S' the standard header for 'Side' cols
Until now, this header was hardcoded in OrderBookSettings
2022-08-16 17:25:03 -04:00
John Colvin 44c419fdaf refactor: Move bool-check converter from rec rep to common place
Will be used in the new Open Orders window
2022-08-16 17:25:03 -04:00
John Colvin 5eb151f9a0 refactor: Move common orderbook col headers to shared place
Specifically, col headers that will be used in the new Open Orders window, for
consistency's sake
2022-08-16 17:25:03 -04:00
John Colvin 855a939c51 refactor: Return obj from Log.not/isnull fns
For convenience, so these calls may be used in a chain like manner, etc.
2022-08-16 17:25:03 -04:00
John Colvin 7071d9b961 refactor: Add Not/MaybeNull attributes to Log.not/isnull fns
So the compiler recognizes these methods guarentee a particular result of
the expression 'obj is null'
2022-08-16 17:25:03 -04:00
John Colvin adf14491bc refactor: Check if obj is of type 'T' in Optional<T>.Equals 2022-08-16 17:25:03 -04:00
John Colvin 7e0e8f2801 refactor: Clean up Optional<T> 2022-08-16 17:25:02 -04:00
John Colvin 4a37e2d6a6 refactor: Implement IKeyedData in generated cs pbuf types
For convenience, so we don't have to pass key-selector lambdas all over the
place when dealing w/ IObservable<TableUpdate>s from the pbuf services
2022-08-16 17:25:02 -04:00
John Colvin 12b2371a69 style: Clean up api code-gen template immutable.pb.cs 2022-08-16 17:25:02 -04:00
John Colvin c19d173983 refactor: Move SecurityStore to spark_core, rename Resolver 2022-08-16 17:25:02 -04:00
John Colvin 203164f372 fix (preemptive): Init default UserSettings.day_fills_grid
This fix is preemptive because the hardcoded Day Fills defaults are automatically
used when the UserSettings.day_fills_grid member is null
2022-08-16 17:25:02 -04:00
John Colvin 6f69f1c2e2 fix (preemptive): Apply Day Fills settings migration rules
This is a preemptive fix bc as of now (8/14/2022), there are no Day Fills
migration rules
2022-08-16 17:25:02 -04:00
John Colvin ad1a585b5c refactor: Move Optional into Spark.Core 2022-08-16 17:25:02 -04:00
John Colvin f29e367ec7 refactor: Clean up ObservableSortedCollection
* Enable nullability
* Run formatter
* Add readonly specifiers where possible
* Improve documentation
2022-08-16 17:25:02 -04:00
John Colvin 18127383f0 refactor: Make IObservableCollection inherit IReadOnly 2022-08-16 17:25:02 -04:00
John Colvin b450c757c9 refactor: Move ObservableSortedCollection to Spark.Core.Collections 2022-08-16 17:25:02 -04:00
John Colvin 836683bcbe refactor: Move IObservableCollection to Spark.Core.Collections 2022-08-16 17:25:02 -04:00
John Colvin c271af30d8 refactor: Get rid of non-generic InsertRange ext 2022-08-16 17:25:02 -04:00
John Colvin 31fab94208 refactor: Get rid of non-generic AddRange ext
Most references to this fn were by generic IList<T>s. This resulted in error
prone, misleading code as such calls subtly bypass the compile-type type
constraints provided of the generic impl.
2022-08-16 17:25:02 -04:00
John Colvin a9b5067d7a refactor: Move fns from GlobalExtensions to less vague classes 2022-08-16 17:25:01 -04:00
John Colvin 7a31b025a2 refactor: Add 'ms' suffix to exch-open-order timestamp fields
Just to make it clear that they are milliseconds (not nanos, micros, etc.)
2022-08-16 17:24:52 -04:00
John Colvin 31897a1b2e refactor: Change exch-open-order record_id to auto-incremented int
To insulate ourselves from potential duplicate-id bugs at the exchange
2022-08-16 16:46:47 -04:00
John Colvin f93bc592cb fix: Set RetrievedExchangeOpenOrder.exchange_order_id
Bugs (fixed):
  * The 'exchange order id' field is empty in open orders data received
    by client
2022-08-16 13:22:38 -04:00
John Colvin 6ca2753951 fix (vs): Use precison in terms of actual data for eq comp
Bugs (fixed):
  * The tick up/down buttons have no effect for vol input cells

  Cause: The cell vm ignores some value updates because the equality
  comparer incorrectly considers the new value equal to the current one
  (because it doesn't use enough decimal places in the comparison)
2022-08-11 01:33:43 -04:00
Taha Afzal 470ca0093e Adding more logging helpers to VolSurfaceTroubleshootingFunctions 2022-08-10 20:54:10 -04:00
John Colvin 7e1715964f fix (vs): Pass the vm's 'current_grid' to the model
Bugs (fixed):
  * Cannot edit volsurface cell values (they are immediately reverted)

  Cause: The vm was passing the model's grid back to itself before, effectively
  undoing manual edits in cells
2022-08-10 18:27:41 -04:00
John Colvin 39e929a614 fix: Actually use eq-comp member in Property vm 2022-08-10 18:17:58 -04:00
John Colvin a9aa06f275 fix (vs): Actually use coulmn comparer in cell vm 2022-08-10 18:11:27 -04:00
John Colvin 54510c9e59 refactor (vs grid): Supress recalc reqs while processing update 2022-08-10 14:26:28 -04:00
John Colvin cb56429f04 refactor: Rename 'execute' -> 'Invoke'
To be consistent and interchangeable w/ System.Action
2022-08-10 14:26:28 -04:00
John Colvin ac8147cc0f refactor: Add 'implicit operator T(Optional<T>)' for convenience 2022-08-10 14:26:28 -04:00
John Colvin 82ac189ac9 refactor: Strip 'Wrapper' from 'DeferrableActionWrapper' name 2022-08-10 14:26:27 -04:00
John Colvin 5721ff78b6 fix (vs): Only request recalc when input changes (not output)
Bugs (fixed):
  * The volsurface grid can get into an endless loop of recalc requests
2022-08-09 15:56:05 -04:00
John Colvin 7afd2947f1 refactor: Clean up vs grid vms a bit
* Split vs-cell-vm classes up into own files
* Rename classes by changing 'VolGrid' prefix to 'VolSurface'
* Remove unused struct VolSurfaceCellState
* Enable nullable
* Run formatter
2022-08-09 15:55:40 -04:00
John Colvin c3e3862c5f refactor: Update vs cell 'is_dirty' synchronously when vals change 2022-08-09 02:57:35 -04:00
John Colvin bf2dc9ecca fix (vs grid): Account for nan & rounding error
Bugs (fixed):
  * The volsurface grid starts flasing is_dirty borders all over the place. The
    repro is inexact, but it seems to happen after inputs are changed a few
    times (perhaps from loading a previous volsurface).

  * The volsurface grid flashes is_dirty borders just from clicking on an input
    cell (even if no change is entered).
2022-08-09 02:57:35 -04:00
John Colvin e0e188a02c refactor: Enable NaN identity equality in tick_equals 2022-08-09 02:57:35 -04:00
John Colvin 79ba1654d3 fix: Fix typo in tick_equals fn 2022-08-09 02:57:35 -04:00
Taha Afzal 2aab0d18a5 Formatting slide exp, to look like PNL's 2022-08-08 23:45:02 -04:00
John Colvin 312c8ded11 fix (ui pnl): Add greek props to group row vm
Bugs (fixed):
  * Greek sums still aren't visible in group rows (though they are visible in
    Selected/Total header rows)
2022-08-08 16:11:25 -04:00
John Colvin 3f86779da6 ui (launcher): Move PNL menu items to Analytics module 2022-08-08 16:11:25 -04:00
John Colvin 716d3b8482 ui (day fills): Make title & icon consistent w/ PNL win 2022-08-08 16:11:25 -04:00
John Colvin 24f6dbc4f0 ui (day fills): Convert fill_time utc to local 2022-08-08 16:11:25 -04:00
John Colvin 3e943fa35e refactor (pnl): Add 'PNL' prop to Icons class
To make it clear which icon belongs to which window
2022-08-08 16:11:25 -04:00
John Colvin 457f878b41 ui (pnl): Change win title to 'P&L' to match launcher menu item 2022-08-08 16:11:25 -04:00
John Colvin ac2b2d042a refactor: Split WorkspaceProtos up into 1 class/file 2022-08-08 16:11:25 -04:00
John Colvin 829d2cdbf6 refactor: Provide ICloneable impl in IProtobuf<T> 2022-08-08 16:11:25 -04:00
John Colvin 3cd9693b36 diag (vs): Log cell vm 'is_dirty' changes 2022-08-08 16:11:25 -04:00
John Colvin 2b52ecf2ef perf (vs): Supress repeated 'is_dirty' updates
Before the updates reach the dispatcher thread
2022-08-08 16:11:25 -04:00
John Colvin faab60cbc1 ui (vs): Convert NaN to 0 when users clicks tick up/down 2022-08-08 16:11:25 -04:00
John Colvin b6be0d649d fix (vs): Don't throw ex if parse_double input is null
Bugs (fixed):
  * Previous vol surfaces may have missing data, which can lead to an assert
    crash in the parse_double fn
2022-08-08 16:11:25 -04:00
Taha Afzal 2a65c45d21 Theos/Greeks are still produced if AW2 window is not loaded 2022-08-08 16:00:48 -04:00
Taha Afzal dc6ae2ac29 Removing P from the names of greek columns in Day PNL 2022-08-08 11:53:56 -04:00
Taha Afzal df4250c33a Hooking AW2 populate empty exps 2022-08-07 22:03:50 -04:00
John Colvin 53504ef6f0 ui (pnl): Improve default settings 2022-08-07 15:18:16 -04:00
John Colvin 10afb1af06 ui (pnl): Show greek sums in group/header rows 2022-08-07 14:59:00 -04:00
John Colvin a9149dfa87 refactor: Clean up PNLNormalizedData
* Use new lang feats: nullable, and 'init' prop setters
* Group 'sod', 'day', and 'working' members in respective groups
* Run formatter
2022-08-07 14:39:44 -04:00
John Colvin 5afa6695e7 fix (pnl view): Enforce min height for 'Selected' header row 2022-08-07 13:51:55 -04:00
John Colvin d2a6d54090 refactor (pnl): Replace dyn resources w/ Bindings in xaml
Traditional Bindings are more performant than DynamicResources, and make for
cleaner code
2022-08-07 13:51:55 -04:00
John Colvin 794ac25c01 log: Change 'Begin/End' tags to 'BEG/END' in log_op ext
Improves readability of log_op logs

* Gives the 'BEG/END' tags a uniform width, so their msg bodies will align
* All caps makes the tags stand out

Improves readability by providing uniform width to the BEG/END tags
2022-08-07 13:51:55 -04:00
John Colvin 37214e9480 refactor: Make msg body param optional in 'fn' logger exts 2022-08-07 13:51:55 -04:00
John Colvin 90ab551778 ui (day fills): Format dates in exp col filter 2022-08-07 13:51:54 -04:00
John Colvin 9a4ec46440 fix: Don't begin shutdown til after workspace saved (async)
Bugs (fixed):
   * Seems like only one window's settings are restored w/ workspace
2022-08-07 13:51:54 -04:00
John Colvin c86d283755 fix: Supress empty TableUpdates in 'distinct_on'
Bugs (fixed):
  * Discrete values column filters (i.e., multi-selector views) that are driven
  by 'distinct_on' IObservable<TableUpdate> deselect items when the main
  grid data changes

  Cause: When processing updates from the input IObseravble, the 'distinct_on'
  ext doesn't check if the new/old results are equal. This in turn causes the
  selector-view to remove, then re-add items that didn't actually change.
  Exhibiting reasonable behavior, the view deselects items when they are removed
  from the ItemsSource.
2022-08-07 13:51:54 -04:00
John Colvin 941956c1b5 fix: Actually supress false changes in 'to_table_checked' ext 2022-08-07 13:51:54 -04:00
John Colvin 67e3d22104 style: Fix typo in param name '_selector' -> 'selector' 2022-08-07 13:51:54 -04:00
John Colvin 7e41c93f9a refactor: Rename PNLFilterStates -> PNLFilterSettings 2022-08-07 13:51:54 -04:00
John Colvin 086ccbb3fa refactor: Weaken T constraint for is_within_range ext
There is no reason T has to implement IFloatingPoint<T>
2022-08-07 13:51:54 -04:00
John Colvin 329ddc576d ui (day fills): Add (groupable) exp column 2022-08-07 13:51:54 -04:00
John Colvin 1faef425ab refactor: Consolidate format_expiration fns into single fn 2022-08-07 13:51:54 -04:00
John Colvin bb068cf6b2 fix (preemptive): Add 'Option' case to exp switch expr 2022-08-07 13:51:54 -04:00
John Colvin f7cade41c6 ui (day fills): Fmt qty cols w/ sec-meta display settings 2022-08-07 13:51:54 -04:00
John Colvin 47afd3a911 ui (day fills): Hide bid/ask cols by default 2022-08-07 13:51:54 -04:00
John Colvin ef80d2a41d ui: Shorten a few default column widths
Namely: Account, Exchange, and Symbol
2022-08-07 13:51:53 -04:00
John Colvin 44ef080e9f ui (day fills): Format theo and driver w/ 4 dec places
With the caveat that future fills' drivers are formatted according to their own
tick factors (i.e., same as the "price" column). This should be good/reasonable
behavior because in rare cases where futures don't drive themselves, they are
most likely driven by a neighboring expiration with the same price display.
2022-08-07 13:51:53 -04:00
John Colvin fef8d2c413 ui (day fills): Format price w/ sec's tick factors 2022-08-07 13:51:53 -04:00
John Colvin 201746e561 fix (day fills): Init column models w/ settings
Bugs (fixed):
  * Day fills column settings are not restored with workspace

  Cause: This is a kind-of re-entrancy bug.  When the SimpleGridSettingsManager
  attempts to load the initial column settings, it assigns props in the column
  views. When the width prop is set, the column view forwards the new width to
  the model, which causes the model to fire a settings_changed event, which
  pipes the model's settings (which have not been fully initialized) back to
  the view.
2022-08-07 13:51:53 -04:00
John Colvin 8e375a0b7f ui (day fills): Use qty fg to indicate fill side
Rather than using the row bg, this gives the win a subtler, more modern look
2022-08-07 13:51:53 -04:00
John Colvin 045f3dc060 chore: Move ValueConverter into own file 2022-08-07 13:51:53 -04:00
John Colvin 6b6012236d ui (day fills): Add pnl, delta foreground triggers 2022-08-07 13:51:53 -04:00
John Colvin 9cee798939 chore: Set ModalStateDefinition ContentProperty
For convenience in xaml (so ModalStateDefinition.Source can be defined without
explicit tag)
2022-08-07 13:51:53 -04:00
John Colvin c79bdb12e0 refactor: Make ActivityLogCategories readonly props 2022-08-07 13:51:53 -04:00
John Colvin bce77d65de fix: Add null check in OptionsView
Bugs (fixed):
  * If an Options View window is launched w/ "incomplete" security criteria
    (i.e., not a tuple of Exch, Type, Sym), a null ref crash happens when the
    window is restored with the workspace.
2022-08-07 13:51:53 -04:00
John Colvin 0db814ef2d fix (rec rept): Use tick-factors to avoid rounding errors
Bugs (fixed):
  * Reconcile Report can show false positive price mismatch

  Cause: The epsilon const (1E-12) used in the price comparison too small.
  Evidently, the Deribit-position prices provided to the Reconcile Report are
  only accurate up to 9 decimal places.
2022-08-07 13:51:53 -04:00
John Colvin 31b5ec4bd3 fix (fills view): Change side col base class row type to the vm
Bugs (fixed):
  * In the Fills window, side column cells are empty
2022-08-07 13:51:52 -04:00
John Colvin e15cdfd76e refactor: Add get bg/fg delegates to RenderColumnBase
These colors are frequently functions of the rows; now such cases don't require
implementation in classes that derive from RenderColumnBase
2022-08-07 13:51:52 -04:00
John Colvin eb150ba82c chore (ui): Hook up background setting in OrderTypeColumn 2022-08-07 13:51:52 -04:00
John Colvin d763275179 fix (ui preemptive): Bind horiz align setting to horiz content align
Bugs (fixed)
  * (preemptive) The OrderTypeColumn root border doesn't span the width of the
    column.  This would happen if the column's background was a
    non-transparent brush, and horizontal alignment wasn't stretched.
2022-08-07 13:51:52 -04:00
John Colvin 2b96a8a5c4 ui: Enable grid background setting in all windows
And remove bool props that were used to toggle this behavior
2022-08-07 13:51:52 -04:00
John Colvin 31866c76b7 chore (vs graph): Add tick_line_color setting 2022-08-07 13:51:52 -04:00
John Colvin a2da946e03 fix (vs graph): Actually use axis_line_color setting in vm
Bugs (fixed):
  * The vs graph axis line color setting is ignored
2022-08-07 13:51:52 -04:00
John Colvin 528bab34e7 chore: Show color name in ColorNotFoundException msg 2022-08-07 13:51:52 -04:00
John Colvin 776b9e4f94 chore: Remove '_color' and '_brush' props from BTSColors
The BTSColor props are implicitly convertible to both System.Windows.Media.Color
and Brush
2022-08-07 13:51:52 -04:00
John Colvin 168d328e97 chore: Clean BTSColorExtension 2022-08-06 23:54:15 -04:00
John Colvin f871dbd230 chore: Add 'Pure' attr to is_null_or_empty ext 2022-08-06 23:54:15 -04:00
John Colvin 1662b56ea0 refactor: Clean up ReflectionHelper a bit
* Rename '(static_)property_delegates_of_type' -> '(static_)property_getters',
  and include name w/ prop getters
* Rename 'FindApplicableConstructor' -> 'get_constructor'
* Remove unused fns
2022-08-06 23:54:15 -04:00
John Colvin f3302dbac0 fix (ui): Validate width/height before updating DragRectAdorner 2022-08-06 23:54:15 -04:00
John Colvin 181072f3e7 chore: Clean up ui settings classes
* Move most settings files into new dir 'Model/Settings'
* Conform code style to our current practices
2022-08-06 23:54:15 -04:00
John Colvin f654312123 chore (refactor): Move synth mgmt settings classes into sep files 2022-08-05 13:41:37 -04:00
John Colvin c46d97646a fix: Gather individual win settings on dispatcher thread
In practice, window-states live on their respective dispatcher threads.
Window settings factories shouldn't be expected to be thread-safe.
2022-08-05 13:41:37 -04:00
John Colvin 41742359e9 fix (vs ui): Don't clip vs time sched ui 2022-08-05 13:41:37 -04:00
John Colvin 024723925e ui (vs grid settings): Constrain window height 2022-08-05 13:41:37 -04:00
John Colvin 76b834768e style: Clean up spark.csproj 2022-08-05 13:41:37 -04:00
John Colvin 89284bea87 build: Upgrade csharp nuget pkgs 2022-08-05 13:41:37 -04:00
John Colvin 532154640b refactor: Replace PNLCellAppearance ctr w/ 'init' props 2022-08-05 13:41:36 -04:00
Taha Afzal ab8468d498 Added function in VolSurfaceDisplayModel to populate empty exps 2022-08-04 18:42:15 -04:00
Taha Afzal 84b0332778 Better logs for the case when Deribit's open orders's security can not be resolved 2022-08-03 23:53:57 -04:00
Taha Afzal 20b601f290 Fixed the crash related to querying Deribit for open orders 2022-08-03 16:57:10 -04:00
Taha Afzal 47002cae4c This PNL fix change was accidentally merged out 2022-08-03 08:47:50 -04:00
John Colvin eaaffe369a Fix typo typeof(HyperlinkCell) -> typeof(TextCell) 2022-08-03 00:07:39 -04:00
John Colvin 7292a31c00 fix (rec rep): Save grid settings w/ workspace 2022-08-03 00:07:39 -04:00
John Colvin 389d762a93 ui (launcher): Drop "2" in "P&L 2" menu item
It was leftover from the transition to the "new" pnl window (which hasn't been
new since 2019)
2022-08-03 00:07:39 -04:00
John Colvin 9704ebf096 style: Move ReconcileReportModel into rec rep dir 2022-08-03 00:07:39 -04:00
John Colvin de320338ca ui (launcher): Group 'Fill' menu items together 2022-08-03 00:07:39 -04:00
John Colvin 668dfd9130 feat (ui): Add Day Fills win 2022-08-03 00:07:39 -04:00
John Colvin a2960b23a8 refactor (ITradeLogRow): Rename trader -> user, add user_id
Use consistent nomenclature throught the sys
2022-08-03 00:01:02 -04:00
John Colvin 55744edc25 refactor: Move SecurityDataHelper.Defaults into Security class 2022-08-02 13:24:48 -04:00
John Colvin 7ece5d18cb refactor: Move sec 'key_fn' into Security class 2022-08-02 12:37:46 -04:00
John Colvin 8005ac8635 refactor: Name top_of_book tuple items bid/ask 2022-08-01 21:56:07 -04:00
John Colvin c00a71fdfa chore: Add fill_kind to ITradeLogRow 2022-08-01 11:14:29 -04:00
John Colvin 197c915968 Remove erroneous font_size binding in xaml
Each cell's font size comes from its associated column object (not the cell vm)
2022-08-01 11:14:29 -04:00
John Colvin 6d77a07bbe fix: Load grid-level settings in pnl window
Font family/size/weight, foreground, background
2022-08-01 11:14:29 -04:00
John Colvin adfb560dd1 chore: Post users-map on the bus 2022-08-01 11:14:29 -04:00
John Colvin 508d20e4e0 refactor: Rename ITradeLogRow.trade_key -> order_key
This is what it actually is; the trade_key misnomer was ported from edge
2022-08-01 11:14:29 -04:00
John Colvin 72457619f7 chore: Move IPositionModel to Spark.Trading ns 2022-08-01 11:14:29 -04:00
John Colvin 18d5b83a95 chore: Replace anon pnl win w/ proper PNLWindow class
The PNL window is too complex/terse to be nested anonymously in the
PNLWindowFactory class.  This makes the code cleaner/easier to read.
2022-08-01 11:14:29 -04:00
John Colvin 653362b947 chore: Make 'Safe' IEnumerable exts accept nullable ref types 2022-08-01 11:14:29 -04:00
John Colvin b7477b8f93 fix (fills ui): Add back 'Fill Status' col
Not sure when/why it went missing
2022-08-01 11:14:29 -04:00
John Colvin 105f1fceaf ui (pnl): Increase sec column default width 2022-08-01 11:14:29 -04:00
John Colvin cb65489ab7 fix (combo vm): Fix typo when assigning swipe_sum net/calls/puts
Bugs (fixed):
  * The swipe sum net/puts are swapped in combo status bar
2022-08-01 11:14:29 -04:00
John Colvin 96772727df fix (exp params): Ensure auto-picked driver has right sym 2022-08-01 11:14:29 -04:00
John Colvin 960a618fe1 fix: Bind pnl account col to AccountTuple
Bugs (fixed):
  * The group-row pnl cell hyperlinks don't open anything when clicked

  Cause: An InvalidCastException is thrown when the code that creates the fills
  vm (for the popup) casts 'PNLRowCollectionViewGroup.Name' to an AccountTuple,
  as said 'Name' member is bound to the 'account_name' (of type string).
2022-08-01 11:14:29 -04:00
John Colvin 505cd37225 chore: Assert that PositionPriceSize.GetHashCode isn't invoked
Impl doesn't guarentee equal objects produce the same hash
2022-08-01 11:14:28 -04:00
John Colvin a5f66b8ac6 feat: Add greeks cols to pnl view (for options) 2022-08-01 11:14:28 -04:00
John Colvin 2021f29f62 feat: Support options in pnl view 2022-08-01 11:14:28 -04:00
John Colvin 9ccf3e0f87 chore: Remove UTF-8 BOM from pnl files 2022-08-01 11:14:28 -04:00
John Colvin 7cfc53eba8 refactor: Make sum_real ext generic (IFloatingPoint<T>)
Also add bool param 'nan_if_none' (to return NaN instead of 0 when values
contains no real numbers)
2022-08-01 11:14:28 -04:00
John Colvin f9d269c750 chore: Add account_id to ITradeLogRow, IPNLUnit 2022-08-01 11:14:28 -04:00
John Colvin 67d5161fd1 chore: Dispose _pnl_calc in ReportDaemon.Dispose
This wasn't causing any problems in practice (this class is instantiated once
on startup and never gets disposed).  However, since it implements IDisposable,
might as well do it correctly.
2022-08-01 11:14:28 -04:00
Taha Afzal d6e9bbce71 Something funky, not sure why it is showing as touched 2022-08-01 11:14:28 -04:00
John Colvin 76ec2c0fa2 fix: Pass key_fn to TableUpdate Operate ext
Bugs (fixed):
  * PNL window crashes immediately when opened

    Cause: The 'Operate' ext fn aggregates an IObservable of TableUpdates w/
    another IObservable of any T.  If the latter fires before the former, an
    'empty' TableUpdate is passed to the aggregate fn.  When this happens, a
    null ref ex is thrown in the 'empty' TableUpdate's ctr because the key
    param is null.
2022-08-01 11:14:28 -04:00
Taha Afzal e82a2c9402 Added Day PNL trades to PNLData 2022-08-01 11:14:28 -04:00
Taha Afzal 60ce106e24 Fixing a PNL crash which was caused by options for a symbol being temporarily null 2022-08-01 11:14:28 -04:00
Taha Afzal 1eb4952422 Added Control Keys to sheets, Ctrl + right click always brings up the context menu, Ctrl + market always brings up the ladder 2022-08-01 11:14:28 -04:00
Taha Afzal 3043582889 Improved some log messages 2022-08-01 11:14:28 -04:00
Taha Afzal f604db6517 Reorganized PNL calculator and associated types 2022-08-01 11:14:28 -04:00
Taha Afzal f95b0c19d7 Added PNL Calculation code 2022-08-01 11:14:28 -04:00
taha afzal 655bf87e92 Fixing open orders callback (was firing intermittent callbacks) 2022-07-22 16:36:52 -04:00
taha afzal 6d06a1920d OrderServiceServer now publishes open orders 2022-07-22 15:16:03 -04:00
taha afzal 70af51e163 ExchangeOpenOrders are now routed all the way to OrderServiceServer 2022-07-21 20:36:17 -04:00
taha afzal 2c17fe334e Added Exchange Open Orders function to IExecutionGateway 2022-07-21 00:38:15 -04:00
taha afzal 5eca2cd3ac Exchange Open orders are routed all the way to EMExecutionGateway 2022-07-20 20:13:56 -04:00
taha afzal 4b5ff8cad4 Adding AsyncRequestHelper to EmMDListener, that allows code to be shared bw positions and open_orders request 2022-07-20 15:29:25 -04:00
John Colvin 258d4a3fc1 import compute_synthetic_book method from spartan 2020-12-08 23:00:18 -06:00
John Colvin c53f6425cd import IntPrice arithmetic operators from spartan
The compute_synthetic_book method (also to be imported
from spartan) depends on a couple of them.
2020-12-08 22:52:28 -06:00
1194 changed files with 58772 additions and 36578 deletions

No files matched your search

+18
View File
@@ -7,6 +7,24 @@
"commands": [
"dotnet-format"
]
},
"fable": {
"version": "4.0.0-theta-018",
"commands": [
"fable"
]
},
"fantomas": {
"version": "5.2.2",
"commands": [
"fantomas"
]
},
"fake-cli": {
"version": "6.0.0",
"commands": [
"fake"
]
}
}
}
+1
View File
@@ -50,3 +50,4 @@ trace_data/
*.tmp/*
/.venv*/*
.cache/
src/web/api/tsconfig.*.g.json
-3
View File
@@ -7,6 +7,3 @@
[submodule "docker/windows"]
path = docker/windows
url = git@bitbucket.org:bluetradesys/windows_docker.git
[submodule "src/deribit-crypto-connector-demo-application"]
path = src/deribit-crypto-connector-demo-application
url = git@gitlab.com:cryptoexchanges1/deribit-crypto-connector-demo-application.git
+9 -2
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.12)
project(spark)
if(WIN32)
@@ -7,7 +7,12 @@ else()
set(platform "linux")
endif()
set(EXTERNALS_ROOT "${CMAKE_SOURCE_DIR}/externals/${platform}")
set(
EXTERNALS_ROOT
"${CMAKE_SOURCE_DIR}/externals/${platform}"
CACHE
STRING
"External dependencies root folder (should contain folders: lib, bin, include")
set(SPARK_PYTHON "${EXTERNALS_ROOT}/python")
set(SPARK_BIN "${EXTERNALS_ROOT}/bin")
set(SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
@@ -52,6 +57,7 @@ else()
add_compile_definitions(SPARK_ENABLE_CCAPI)
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_FTX)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
add_compile_definitions(CCAPI_ENABLE_LOG_WARN)
# flags
@@ -100,6 +106,7 @@ else()
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
install(DIRECTORY "${CMAKE_BINARY_DIR}/server/plugins" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libACE-5.7.5.so" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/src/emumba/sparkd.lic" DESTINATION bin)
+3 -2
View File
@@ -106,8 +106,9 @@ function(Service_Compiler_Generate_Cpp SRCS HDRS)
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.client.pb.h"
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.server.pb.cc"
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.server.pb.h"
COMMAND
"PYTHONPATH=${SPARK_PYTHON}" ${Protobuf_PROTOC_EXECUTABLE} ARGS
COMMAND ${CMAKE_SOURCE_DIR}/tools/venv_exec.sh
ARGS
${Protobuf_PROTOC_EXECUTABLE}
--cpp_out=${CMAKE_BINARY_DIR}/src
--plugin=protoc-gen-service=${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/plugin.py
--service_out=lang=cpp,make_file_name=${ABS_FIL}:${CMAKE_BINARY_DIR}/src
+3 -1
View File
@@ -46,6 +46,7 @@ function setup_build
assert_dir $repo_root
assert_dir $build_root
set compiler "$argv[1]"
set c_compiler (which cc)
set build_type "$argv[2]"
if test \( -z "$build_type" \) -o \( \( "$build_type" != debug \) -a \( "$build_type" != release \) \)
@@ -59,6 +60,7 @@ function setup_build
else
# set compiler_arg "/usr/lib/ccache/clang++"
set compiler_arg "clang++"
set c_compiler (which clang)
end
else if test "$build_type" = release
set build_type_arg Release
@@ -85,7 +87,7 @@ function setup_build
rm -rf "$build_dest"
mkdir -p "$build_dest"
cd "$build_dest"
cmake -G Ninja -DCMAKE_BUILD_TYPE=$build_type_arg -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_CXX_COMPILER=$compiler_arg $repo_root
cmake -G Ninja -DCMAKE_BUILD_TYPE=$build_type_arg -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_C_COMPILER=$c_compiler -DCMAKE_CXX_COMPILER=$compiler_arg $repo_root
end
# The while loop below removes arguments (via shift) as it parses them.
+1
View File
@@ -12,6 +12,7 @@ pycodestyle==2.7.0
pycparser==2.20
pylint==2.10.2
PyMeta3==0.5.1
termcolor==1.1.0
toml==0.10.2
wrapt==1.12.1
yapf==0.31.0
+32 -12
View File
@@ -1,10 +1,10 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
# Visual Studio Version 17
VisualStudioVersion = 17.3.32922.545
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}") = "libspark", "build\src\lib\libspark.vcxproj", "{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
@@ -14,11 +14,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\sp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
ProjectSection(ProjectDependencies) = postProject
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40} = {9FA60215-26AF-3EB7-AB89-D4F4B126EF40}
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25} = {FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{ECD78CC5-138F-4809-A764-2358C2AC2E82}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_websocket", "src\client\spark_websocket\spark_websocket.csproj", "{3201359D-C071-4089-9EDE-B04D759F1508}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api_server", "src\client\spark_api_server\spark_api_server.csproj", "{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -35,14 +39,14 @@ Global
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Release|x64.Build.0 = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Debug|x64.ActiveCfg = Debug|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Debug|x64.Build.0 = Debug|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Release|x64.ActiveCfg = Release|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Release|x64.Build.0 = Release|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Debug|x64.ActiveCfg = Debug|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Debug|x64.Build.0 = Debug|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Release|x64.ActiveCfg = Release|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Release|x64.Build.0 = Release|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.Build.0 = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.MinSizeRel|x64.ActiveCfg = Release|x64
@@ -83,6 +87,22 @@ Global
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Release|x64.Build.0 = Release|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x64.Build.0 = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Debug|x64.ActiveCfg = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Debug|x64.Build.0 = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.MinSizeRel|x64.ActiveCfg = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.MinSizeRel|x64.Build.0 = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Release|x64.ActiveCfg = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Release|x64.Build.0 = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.RelWithDebInfo|x64.Build.0 = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Debug|x64.ActiveCfg = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Debug|x64.Build.0 = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.MinSizeRel|x64.ActiveCfg = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.MinSizeRel|x64.Build.0 = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Release|x64.ActiveCfg = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Release|x64.Build.0 = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.RelWithDebInfo|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+55 -53
View File
@@ -1,53 +1,55 @@
<Project>
<PropertyGroup>
<ComputerSpecificConfig>..\..\tools\override\$(computername).props</ComputerSpecificConfig>
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
<Externals>$(SparkRoot)\externals\windows</Externals>
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
<ErrorReport>None</ErrorReport>
<Prefer32Bit>False</Prefer32Bit>
<Deterministic>true</Deterministic>
<EnableDefaultContentItems>False</EnableDefaultContentItems>
<ShowMissingItemTypes>false</ShowMissingItemTypes>
<Configuration Condition="'$(configuration)'==''">Debug</Configuration>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
</PropertyGroup>
<PropertyGroup Label="Output">
<BaseOutputPath>$(SparkRoot)\build\$(Configuration)</BaseOutputPath>
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
</PropertyGroup>
<Import
Project="$(ComputerSpecificConfig)"
Condition="Exists($(ComputerSpecificConfig))" />
<PropertyGroup Label="ProtoBuf">
<ProtocPluginBase>$(SparkRoot)\src\lib\api\service_compiler</ProtocPluginBase>
<protoc>$(Externals)\bin\protoc.exe</protoc>
<protogen>$(Externals)\bin\protogen.exe</protogen>
</PropertyGroup>
<ItemDefinitionGroup>
<Proto2></Proto2>
<Proto3>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCS>%(OutBase)%(FileName).cs</OutCS>
<OutCSClient>%(OutBase)%(FileName).client.pb.cs</OutCSClient>
<OutCSServer>%(OutBase)%(FileName).server.pb.cs</OutCSServer>
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
<CompileOptions></CompileOptions>
</Proto3>
<Proto3Excel>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCSExcel>%(OutBase)%(FileName).excel.pb.cs</OutCSExcel>
<CompileOptions></CompileOptions>
</Proto3Excel>
</ItemDefinitionGroup>
</Project>
<Project>
<PropertyGroup>
<ComputerSpecificConfig>..\..\tools\override\$(computername).props</ComputerSpecificConfig>
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
<Externals>$(SparkRoot)\externals\windows</Externals>
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
<ErrorReport>None</ErrorReport>
<Prefer32Bit>False</Prefer32Bit>
<Deterministic>true</Deterministic>
<EnableDefaultContentItems>False</EnableDefaultContentItems>
<ShowMissingItemTypes>false</ShowMissingItemTypes>
<Configuration Condition="'$(configuration)'==''">Debug</Configuration>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<LangVersion>11</LangVersion>
</PropertyGroup>
<PropertyGroup Label="Output">
<BaseOutputPath>$(SparkRoot)\build\$(Configuration)</BaseOutputPath>
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
<ProtoOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</ProtoOutputPath>
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
</PropertyGroup>
<Import
Project="$(ComputerSpecificConfig)"
Condition="Exists($(ComputerSpecificConfig))" />
<PropertyGroup Label="ProtoBuf">
<ProtocPluginBase>$(SparkRoot)\src\lib\api\service_compiler</ProtocPluginBase>
<protoc>$(Externals)\bin\protoc.exe</protoc>
<protogen>$(Externals)\bin\protogen.exe</protogen>
</PropertyGroup>
<ItemDefinitionGroup>
<Proto2></Proto2>
<Proto3>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCS>%(OutBase)%(FileName).cs</OutCS>
<OutCSClient>%(OutBase)%(FileName).client.pb.cs</OutCSClient>
<OutCSServer>%(OutBase)%(FileName).server.pb.cs</OutCSServer>
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
<CompileOptions></CompileOptions>
</Proto3>
<Proto3Excel>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCSExcel>%(OutBase)%(FileName).excel.pb.cs</OutCSExcel>
<CompileOptions></CompileOptions>
</Proto3Excel>
</ItemDefinitionGroup>
</Project>
+1 -2
View File
@@ -52,8 +52,7 @@
<Exec Command="set PYTHONPATH=$(SparkRoot)\.venv\Scripts\python" />
<Exec
Command="
$(SparkRoot)\tools\build_protos.cmd ^
%(Proto3.FullPath) ^
$(protoc) %* %(Proto3.FullPath) ^
--error_format=msvs ^
--proto_path=$(SourceRoot) ^
--plugin=&quot;protoc-gen-service=$(ProtocPluginBase)\plugin.cmd&quot; ^
@@ -3,7 +3,6 @@ namespace mbts.spark {
using bts.api;
using bts.api.analytics;
using bts.collections;
using bts.reactive;
using bts.utils;
using mbts;
@@ -15,7 +14,10 @@ namespace mbts.spark {
using mbts.ui.utility;
using mbts.utils;
using Spark.Core;
using Spark.Core.Collections;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using Spark.ViewModel.Commands;
using System;
@@ -33,14 +35,15 @@ namespace mbts.spark {
class AnalyticsExpirationParametersVM : DisposableViewModelBase, IHandleRowSelection {
readonly IBus _bus;
readonly ObservableCollection<AnalyticsExpirationParameters> _parameters = new();
readonly DeferrableActionWrapper _sync_visible_rows;
readonly DeferrableAction _sync_visible_rows;
readonly Property<AnalyticsExpirationParametersRowVM?> _copy_row_source;
readonly Property<bool> _auto_copy_rolls_is_checked;
readonly Property<bool> _auto_copy_rates_is_checked;
readonly DelegateProperty<bool> _is_dirty;
readonly ObservableHashSet<AnalyticsExpirationParametersRowVM> _selected_rows = new();
readonly DeferrableActionWrapper _raise_selected_rows_changed;
readonly DeferrableAction _raise_selected_rows_changed;
readonly IRaiseCanExecuteChangedCommand _set_driver_to_nearest_exp_cmd;
IList<string>? _pending_selected_symbols;
AnalyticsExpirationParametersMap? _parameter_map;
(ICommand cmd, IDisposable can_exe_sub)? _smart_copy_roll_target_to_selection_cmd;
@@ -63,9 +66,7 @@ namespace mbts.spark {
_is_dirty = backing(nameof(is_dirty), () => rows.Any(r => r.is_dirty));
if (startup_data is not null) {
if (!startup_data.selected_symbols.is_null_or_empty()) {
selected_symbols.AddRange(startup_data.selected_symbols);
}
_pending_selected_symbols = startup_data.selected_symbols;
auto_copy_rolls_is_checked = startup_data.auto_copy_rolls_is_checked;
auto_copy_rates_is_checked = startup_data.auto_copy_rates_is_checked;
}
@@ -81,32 +82,32 @@ namespace mbts.spark {
selected_rows_changed += _ => _set_driver_to_nearest_exp_cmd.RaiseCanExecuteChanged();
_dispose = Disposable.Create(
_selected_rows.Subscribe((s, e) => _raise_selected_rows_changed.execute()),
_selected_rows.Subscribe((s, e) => _raise_selected_rows_changed.Invoke()),
ObservableAutoWrapper.connect(_parameters, rows, p => new(this, p), r => r.Dispose()),
ObservableAutoWrapper.connect(
non_options,
explicit_drivers,
sec => new ExpirationDriverImmutable(new() { Security = sec.to_api_sec_exp() })),
rows.Subscribe((s, e) => _sync_visible_rows.execute()),
rows.Subscribe((s, e) => _sync_visible_rows.Invoke()),
_bus.subscribe(SparkTags.ExpirationParametersInputTag).OnDispatcher().Subscribe(
CompositeObserver.create(
Observer.Create<AnalyticsExpirationParametersMap>(_on_parameter_map),
can_commit_observer.wrap<bool, AnalyticsExpirationParametersMap>(m => m is not null))),
ObservableAutoWrapper.connect_item_hooks(selected_symbols, sym => {
if (selected_symbol_ref_count.increment_adds_key(sym)) {
_sync_visible_rows.execute();
_sync_visible_rows.Invoke();
}
return Disposable.Create(() => {
if (selected_symbol_ref_count.decrement_removes_key(sym)) {
_sync_visible_rows.execute();
_sync_visible_rows.Invoke();
}
});
}),
ObservableAutoWrapper.connect_item_hooks(
rows, r => r.observe_is_dirty().Subscribe(_ => _is_dirty.refresh())),
_is_dirty.observe_current(true).OnDispatcher().Subscribe(can_revert_observer),
Disposable.wrap_collection(rows),
Disposable.Create(() => {
rows.DisposeAll();
_smart_copy_roll_target_to_selection_cmd?.can_exe_sub?.Dispose();
_smart_copy_riskless_rate_to_selection_cmd?.can_exe_sub?.Dispose();
}));
@@ -195,10 +196,18 @@ namespace mbts.spark {
};
void _set_driver_to_nearest_exp(IEnumerable<AnalyticsExpirationParametersRowVM> rows) {
Func<ExpirationDriverImmutable, bool> is_unexpired = (d) => {
var st = d.Security.to_sec_exp().security_tuple.to_api_sec_tuple();
var exp = d.Security.Expiration.date_time();
return SecurityExpirationHelper.is_unexpired(st, exp);
};
foreach (var r in rows) {
if (drivers.Where(d => d.HasSecurity).TryGetMinElement(
d => (r.security.expiration - d.Security.Expiration.date_time()).Ticks.abs(),
out var nearest_exp)) {
if (drivers
.Where(d => d.HasSecurity && d.Security.Symbol == r.security.symbol)
.Where(d => is_unexpired(d))
.TryGetMinElement(
d => (r.security.expiration - d.Security.Expiration.date_time()).Ticks.abs(),
out var nearest_exp)) {
r.driver = nearest_exp;
}
}
@@ -285,10 +294,10 @@ namespace mbts.spark {
}
symbols.sync_with(_parameters.Select(p => p.security.symbol).Distinct().Ordered());
using var _ = _sync_visible_rows.defer_execution();
using var _ = _sync_visible_rows.defer();
if (is_first_update) {
// select all symbols on first update
selected_symbols.sync_with(symbols);
selected_symbols.sync_with(_pending_selected_symbols?.Intersect(symbols) ?? symbols);
_pending_selected_symbols = null;
}
}
@@ -9,7 +9,7 @@
xmlns:u="clr-namespace:mbts.ui"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:shell="clr-namespace:mbts.ui.shell"
Background="{StaticResource CommonBackground}">
Background="{u:BTSBrush UI.common_background}">
<UserControl.Resources>
<cv:DoubleTextConverter x:Key="DoubleTextConverter" />
<cv:DoubleTextConverter
@@ -153,7 +153,6 @@
SelectedItems="{Binding selected_symbols}"
HorizontalAlignment="Left"
Margin="5,2,2,2"
TextBlock.Foreground="Black"
HeaderContent="Symbols"
ShowSelectAll="True">
<c:ComboCheckBox.HeaderContentTemplate>
@@ -165,19 +164,13 @@
<WrapPanel>
<TextBlock
Margin="5,5,0,0" Text="Auto copy rolls:" Style="{StaticResource TextBlock.Dark}" />
<CheckBox
Margin="5,5,10,0"
IsChecked="{Binding auto_copy_rolls_is_checked}"
Style="{StaticResource CheckBox.Dark}" />
Margin="5,5,0,0" Foreground="{u:BTSBrush UI.default_text}" Text="Auto copy rolls:" />
<CheckBox Margin="5,5,10,0" IsChecked="{Binding auto_copy_rolls_is_checked}" />
</WrapPanel>
<WrapPanel>
<TextBlock
Margin='0,5,0,0' Text="Auto copy rates:" Style="{StaticResource TextBlock.Dark}" />
<CheckBox
Margin="5,5,0,0"
IsChecked="{Binding auto_copy_rates_is_checked}"
Style="{StaticResource CheckBox.Dark}" />
Margin='0,5,0,0' Foreground="{u:BTSBrush UI.default_text}" Text="Auto copy rates:" />
<CheckBox Margin="5,5,0,0" IsChecked="{Binding auto_copy_rates_is_checked}" />
</WrapPanel>
</StackPanel>
<c:DataGridEx
@@ -422,8 +415,7 @@
<DataTemplate>
<TextBox
Text="{Binding roll_target, Converter={StaticResource DoubleTextConverter}}"
Margin="2"
Style="{StaticResource TextBox.Dark}" />
Margin="2" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -457,8 +449,7 @@
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding riskless_rate, Converter={StaticResource RisklessRateConverter}}"
Style="{StaticResource TextBox.Dark}" />
Text="{Binding riskless_rate, Converter={StaticResource RisklessRateConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -492,8 +483,7 @@
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding settlement_price, Converter={StaticResource DoubleTextConverter}}"
Style="{StaticResource TextBox.Dark}" />
Text="{Binding settlement_price, Converter={StaticResource DoubleTextConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -528,8 +518,7 @@
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding delta_beta, Converter={StaticResource DoubleTextConverter}}"
Style="{StaticResource TextBox.Dark}" />
Text="{Binding delta_beta, Converter={StaticResource DoubleTextConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -563,9 +552,7 @@
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding vega_beta, Converter={StaticResource DoubleTextConverter}}"
Style="{StaticResource TextBox.Dark}" />
<TextBox Text="{Binding vega_beta, Converter={StaticResource DoubleTextConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -7,6 +7,8 @@ namespace mbts.ui.shell {
using mbts.ui.converters;
using mbts.ui.extensions;
using Spark.Core.Disposables;
using System;
using System.Windows;
using System.Windows.Controls;
@@ -5,6 +5,8 @@ namespace mbts.spark {
using mbts.ui;
using mbts.ui.shell;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Windows;
@@ -6,6 +6,8 @@ namespace mbts.spark {
using mbts.analytics;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -40,6 +42,12 @@ namespace mbts.spark {
exp_params_out_src.Subscribe(save_exp_params));
void save_exp_params(IEnumerable<AnalyticsExpirationParameters> exp_params) {
Func<AnalyticsExpirationParameters, bool> is_unexpired = (p) => {
var st = p.driver_security.security_tuple.to_api_sec_tuple();
var exp = p.security.expiration;
return SecurityExpirationHelper.is_unexpired(st, exp);
};
exp_params = exp_params.Where(t => is_unexpired(t));
// listens to the commit tag and saves it
Log.Gen.info($"AnalyticsParametersManager received commit req. count:{exp_params.Count()}");
var to_save = new ExpirationParameterEntryList();
@@ -0,0 +1,120 @@
namespace mbts.spark {
using bts;
using bts.api;
using mbts;
using mbts.analytics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
// For each vs, it sends the vol surface calc request (_do_periodic_auto_fit)
// and then commits the response
public class SingleVolSurfaceAutoFitSource : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly int _vs_id;
private readonly ITag<VolSurfaceInput> _vs_input_tag;
private readonly ITag<VolSurfaceCalculationResponse> _vs_calc_response_tag;
private DateTime _last_req_time = DateTime.MinValue;
private TimeSpan _req_period = TimeSpan.MaxValue;
private int _req_id = 0;
public SingleVolSurfaceAutoFitSource(int vs_id, ThreadedBlackboard bb) {
_bb = bb;
_vs_id = vs_id;
_vs_input_tag = SparkTags.VolSurfaceCommittedInputTag(vs_id);
_vs_calc_response_tag = SparkTags.VolSurfaceAutoCalculationResponseTag(vs_id);
_bb.subscribe(_vs_input_tag);
_bb.subscribe(_vs_calc_response_tag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.data_callback += _on_bb_data;
_bb.timer_callback += _on_bb_timer;
}
private void _on_bb_data(object tag, object data) {
if (tag.Equals(_vs_input_tag))
_possibly_autofit();
else if (tag.Equals(_vs_calc_response_tag)) {
VolSurfaceCalculationResponse calc_resp = data as VolSurfaceCalculationResponse;
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource._on_bb_data vs_id: {_vs_id} " +
$"received calculation response");
_bb.post(SparkTags.VolSurfaceCommitTag(_vs_id),
calc_resp.volsurface_calculation_output.volsurface_input);
}
}
private bool _user_has_vs_control() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
return vs_input != null &&
vs_input.surface_settings.core_settings.vs_manager == current_user;
}
private void _on_bb_timer() {
_possibly_autofit();
}
private void _possibly_autofit() {
var auto_fit_period = _get_auto_fit_periodic();
if (!auto_fit_period.HasValue)
return;
if (DateTime.Now - _last_req_time <= auto_fit_period)
return;
if (!_user_has_vs_control())
return;
// auto fit here
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource vs_id: {_vs_id} requesting fit " +
$"last_req_time: {_last_req_time} period: {auto_fit_period.Value}");
_send_request();
}
private Nullable<TimeSpan> _get_auto_fit_periodic() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
if (vs_input == null)
return null;
var fit_settings =
VolSurfaceDataFunctions.get_market_fit_settings(vs_input.surface_settings);
if (fit_settings == null)
return null;
var auto_fit_setting = fit_settings.vs_auto_fit_settings;
if (auto_fit_setting == null)
return null;
if (auto_fit_setting.auto_fit_region == VolSurfaceEnums.VolCurveFitRegion.None)
return null;
var time_based_fit_setting = auto_fit_setting.time_based_settings;
if (time_based_fit_setting == null)
return null;
if (!time_based_fit_setting.period_seconds.HasValue)
return null;
return TimeSpan.FromSeconds(time_based_fit_setting.period_seconds.Value);
}
private void _send_request() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
if (vs_input == null)
return;
VolCurveMarketFitAction mf_action = new VolCurveMarketFitAction(
VolCurveMarketFitAction.ActionType.FitWholeCurve, null, false);
VolCurveAction vc_action = new VolCurveAction(mf_action);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
vs_input.surface_parameters.curve_parameters_map.ToDictionary(
i => i.Item1,
i => vc_action
);
VolSurfaceCalculationRequest req = new VolSurfaceCalculationRequest(vs_input,
_req_id++, action_map, true, true);
_last_req_time = DateTime.Now;
_bb.post(SparkTags.VolSurfaceAutoCalculationRequestTag(_vs_id), req);
}
#region IDisposable
public void Dispose() {
_bb.data_callback -= _on_bb_data;
_bb.timer_callback -= _on_bb_timer;
}
#endregion
}
}
@@ -63,9 +63,9 @@ namespace mbts.spark {
vs_input, vs_nonvol_data.contracts, aux_data_map);
_timer.stop();
var tag_str = tag == null ? "Null" : tag.ToString();
bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
$"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
$"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
// bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
// $"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
// $"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
_logger.log(_log_id, _log_id, _timer.lastIntervalTime);
_bb.post(_out_bb_computed_vol_tag, vs_output);
}
@@ -4,6 +4,7 @@
using bts.api.analytics;
using mbts.analytics;
using mbts.ui;
using Spark.Core.Protobuf;
@@ -13,6 +14,7 @@
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Windows;
// For a single VolSurface this class
// 1. Listents to commmits and calls the service to persist data
@@ -24,7 +26,6 @@
private readonly ITag<VolSurfaceInput> _in_bb_committ_tag;
private readonly AnalyticsService _analytics_service;
private readonly HighPerformanceTimer _timer = new HighPerformanceTimer();
private readonly string _user;
private readonly List<IDisposable> _disposables = new List<IDisposable>();
private IDictionary<UInt64, VolSurfaceInputSnapshot> _snp_shot_map = new
Dictionary<UInt64, VolSurfaceInputSnapshot>();
@@ -35,10 +36,10 @@
volsurface_id));
_volsurface_id = volsurface_id;
_bb = threaded_bb;
_user = "TEST_USER"; // TODO
_in_bb_committ_tag = SparkTags.VolSurfaceCommitTag(volsurface_id);
_analytics_service = analytics_service;
_bb.subscribe(_in_bb_committ_tag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.data_callback += _on_bb_data;
var vs_sub = _analytics_service.subscribei_volsurfaces()
.ObserveOn(SynchronizationContext.Current)
@@ -54,8 +55,10 @@
private void _on_vss(bts.api.core.Update<VolSurfaceEntryImmutable, UInt64> update) {
IEnumerable<VolSurfaceEntryImmutable> all_vss = update.all;
var this_vs_vss = all_vss.Where(vs => vs.VsId == _volsurface_id);
bts.Log.Gen.info(String.Format("SingleVolSurfaceGateway._on_vss count:{0} vs_count:{1}",
all_vss.Count(), this_vs_vss.Count()));
bts.Log.Gen.info(
$"SingleVolSurfaceGateway._on_vss vs_count: {this_vs_vss.Count()} " +
$"all: {update.all.Count()} added: {update.added.Count()} " +
$"updated: {update.changed.Count()} removed: {update.removed.Count()}");
if (this_vs_vss.Count() == 0) {
return;
}
@@ -83,22 +86,39 @@
static VolSurfaceInput _to_vs_input(VolSurfaceEntryImmutable vs) =>
ProtobufSerializationHelper.deserialize<VolSurfaceInput>(vs.Volsurface.ToByteArray());
// private void _show_save_error(string message) {
// BTSMessageBox.Show(message, "Vol Save Error", BTSMessageBoxButtons.OK);
// }
private void _on_bb_data(object tag, object data) {
if (_in_bb_committ_tag.Equals(tag)) {
try {
_timer.start();
VolSurfaceInput vs_input = data as VolSurfaceInput;
var user_tup = _bb.read_state(SparkTags.CurrentUser);
string current_user = user_tup.name;
string vs_manager = vs_input.surface_settings.core_settings.vs_manager;
if (current_user != vs_manager) {
string msg = $"User {current_user} does not have the permission to save vols. " +
$"Permission can be obtained via the grid context menu item 'Acquire ownership from {vs_manager}'";
BTSMessageBox.Show(msg, "Vol Save Error", BTSMessageBoxButtons.OK);
return;
}
_sanitize_input(ref vs_input);
vs_input = VolSurfaceDataFunctions.unset_driver_overrides(vs_input);
vs_input.saver_login = _user;
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, _user);
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, current_user);
_timer.stop();
Log.Gen.info(String.Format(
"SingleVolSurfaceGateway._on_bb_data Time to commit volsurface {0} {1} msecs",
_volsurface_id, _timer.lastIntervalTime));
var archived_theos = _get_theos();
if (archived_theos != null)
if (archived_theos != null) {
_timer.start();
_analytics_service.archive_theos(archived_theos);
_timer.stop();
Log.Gen.info($"SingleVolSurfaceGateway._on_bb_data archiving theos size: " +
$"{archived_theos.SecTheo.Count} time: {_timer.lastIntervalTime} msecs");
}
} catch (Exception ex) {
Log.Gen.warn(ex.ToString());
_bb.post(SparkTags.MessageDisplayTag,
@@ -36,7 +36,11 @@ namespace mbts.spark {
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
a_syms => new SingleVolSurfaceCommittedOutputRetriever(_vs_id, _bb, a_syms)),
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
a_syms => new SingleVolSurfaceRequestCalculator(_vs_id, a_syms, _bb)),
a_syms => new SingleVolSurfaceRequestCalculator(
SingleVolSurfaceRequestCalculator.RequestType.UI, _vs_id, a_syms, _bb)),
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
a_syms => new SingleVolSurfaceRequestCalculator(
SingleVolSurfaceRequestCalculator.RequestType.AUTO, _vs_id, a_syms, _bb)),
new SingleVolSurfaceMarketImpliedVolSource(_vs_id, _bb),
// new SingleVolSurfaceTheoSource(_vs_id, _bb),
// TODO: suspending heatmaps for now (12/24/21)
@@ -44,6 +48,7 @@ namespace mbts.spark {
// server work that can optionally be done on the client
new SingleVolSurfaceDataSource (SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
a_syms => new SingleVolSurfaceInterpGreeksPublisher (_vs_id, _bb, a_syms)),
new SingleVolSurfaceAutoFitSource(_vs_id, _bb),
};
}
@@ -75,7 +75,7 @@ namespace mbts.spark {
var exp_price_map = OptionDriverPriceMap.get_vs_price_map(
all_sym_price_map, (s) => _option_symbols.Contains(s));
if (sc_data != null && vs_nonvol_input != null && exp_price_map != null) {
bts.Log.Gen.info($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
bts.Log.Gen.diag($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
"publishing VolSurfaceNonVolData");
_bb.post(_out_bb_vs_nonvol_data_tag,
new VolSurfaceNonVolData(
@@ -18,10 +18,16 @@ namespace mbts.spark {
// Pubs :
// 1. Calc Resp
public class SingleVolSurfaceRequestCalculator : IDisposable {
public enum RequestType {
UI = 0,
AUTO
}
private readonly RequestType _request_type = RequestType.UI;
private readonly int _vs_id;
private readonly HashSet<string> _symbols;
private readonly ThreadedBlackboard _bb;
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_request_tag;
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_auto_request_tag;
private readonly ITag<VolSurfaceNonVolData> _in_bb_nonvol_data_tag;
private readonly ITag<VolSurfaceStandardTime> _in_bb_vs_standard_time_tag;
private readonly ITag<VolSurfaceEnums.MarketSide> _in_bb_market_impliedvol_side_tag;
@@ -30,18 +36,22 @@ namespace mbts.spark {
/// data
private VolSurfaceOutput _last_output;
public SingleVolSurfaceRequestCalculator(int vs_id, IEnumerable<string> symbols,
public SingleVolSurfaceRequestCalculator(RequestType request_type,
int vs_id, IEnumerable<string> symbols,
ThreadedBlackboard threaded_bb) {
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id:{vs_id}");
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id: {vs_id} request_type: {request_type}");
_request_type = request_type;
_vs_id = vs_id;
_symbols = new HashSet<string>(symbols);
_bb = threaded_bb;
_in_bb_request_tag = SparkTags.VolSurfaceCalculationRequestTag(vs_id);
_in_bb_auto_request_tag = SparkTags.VolSurfaceAutoCalculationRequestTag(vs_id);
_in_bb_nonvol_data_tag = SparkTags.VolSurfaceNonVolDataTag(vs_id);
_in_bb_vs_standard_time_tag = SparkTags.VolSurfaceStandardTimeTag(vs_id);
_in_bb_market_impliedvol_side_tag = SparkTags.VolSurfaceMarketImpliedVolSide(vs_id);
_in_bb_dirty_tag = SparkTags.VolSurfaceDirtyStateTag(vs_id);
_bb.subscribe(_in_bb_request_tag);
_bb.subscribe(_in_bb_auto_request_tag);
_bb.subscribe(_in_bb_nonvol_data_tag);
_bb.subscribe(_in_bb_vs_standard_time_tag);
_bb.subscribe(_in_bb_vs_standard_time_tag);
@@ -51,11 +61,21 @@ namespace mbts.spark {
}
private void _on_bb_data(object tag, object data) {
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
_recompute_and_publish(true);
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
_recompute_and_publish(false);
switch (_request_type) {
case RequestType.UI: {
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
_recompute_and_publish(true);
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
_recompute_and_publish(false);
break;
}
case RequestType.AUTO: {
if (tag.Equals(_in_bb_auto_request_tag)) {
_serve_auto_request();
}
break;
}
}
}
private void _recompute_and_publish(bool is_new_request) {
@@ -80,7 +100,7 @@ namespace mbts.spark {
nonvol_data.driver_price_map,
vs_standard_time, true);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
_post_process_action(effective_req, aux_data_map);
_post_process_action(_bb, _vs_id, effective_req, aux_data_map);
_last_output = GeneralVolSurfaceCalculator.compute(effective_req.volsurface_input,
nonvol_data.contracts,
aux_data_map,
@@ -89,16 +109,49 @@ namespace mbts.spark {
effective_req.cascade_up,
effective_req.cascade_down);
_timer.stop();
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish time:" +
$"{_timer.lastIntervalTime} vs_output:{_last_output.log_string()}");
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish vs_id: {_vs_id} " +
$"time: {_timer.lastIntervalTime}");
_bb.post(SparkTags.VolSurfaceCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(_last_output, effective_req.request_id));
}
}
}
private void _serve_auto_request() {
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request " +
$"vs_id: {_vs_id} auto request received");
bool is_dirty = _bb.read_state(_in_bb_dirty_tag, false);
VolSurfaceNonVolData nonvol_data = _bb.read_state(_in_bb_nonvol_data_tag);
VolSurfaceStandardTime vs_standard_time = _bb.read_state(_in_bb_vs_standard_time_tag);
VolSurfaceCalculationRequest auto_req = _bb.read_state(_in_bb_auto_request_tag);
if (is_dirty || nonvol_data == null || vs_standard_time == null || auto_req == null) {
return;
}
_timer.start();
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map =
VolSurfaceUtilityFunctions.compute_aux_data(auto_req.volsurface_input,
nonvol_data.input,
nonvol_data.driver_price_map,
vs_standard_time, true);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
_post_process_action(_bb, _vs_id, auto_req, aux_data_map);
var vs_output = GeneralVolSurfaceCalculator.compute(auto_req.volsurface_input,
nonvol_data.contracts,
aux_data_map,
action_map,
null,
auto_req.cascade_up,
auto_req.cascade_down);
_timer.stop();
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request vs_id: {_vs_id} " +
$"time: {_timer.lastIntervalTime}");
_bb.post(SparkTags.VolSurfaceAutoCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(vs_output, auto_req.request_id));
}
/// Computes the impl vol if the action is fit to market
private IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
private static IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
ThreadedBlackboard bb, int vs_id,
VolSurfaceCalculationRequest req,
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map) {
IDictionary<VolSurfaceExpiration, VolCurveAction> rv = req.action_map;
@@ -109,7 +162,7 @@ namespace mbts.spark {
VolCurveAction rep_action = rv.ElementAt(0).Value;
if (rep_action.action_type == VolCurveAction.ActionType.FitToMarket) {
IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> impl_vol_map =
_fill_in_implied_vols(req.volsurface_input, req.action_map, aux_data_map,
_fill_in_implied_vols(bb, vs_id, req.volsurface_input, req.action_map, aux_data_map,
req.cascade_up, req.cascade_down);
rv = impl_vol_map.ToDictionary(kv => kv.Key, kv => new VolCurveAction(
new VolCurveMarketFitAction(rep_action.market_fit_action.fit_action, kv.Value,
@@ -119,7 +172,8 @@ namespace mbts.spark {
return rv;
}
private IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
private static IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
ThreadedBlackboard bb, int vs_id,
VolSurfaceInput vs_inp,
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map,
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map,
@@ -140,15 +194,15 @@ namespace mbts.spark {
applicable_exps = applicable_exps.Where(vs_exp => master_exps.Contains(vs_exp));
VolSurfaceEnums.MarketSide impliedvol_side =
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(_bb, _vs_id);
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(bb, vs_id);
foreach (VolSurfaceExpiration vs_exp in applicable_exps) {
VolSurfaceExpiration md_exp;
VolCurveMarketImpliedVolData exp_impl_data =
VolSurfaceCoreFunctions.compute_implied_vols(vs_inp, vs_exp, aux_data_map,
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(_bb, arg_vs_exp),
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(bb, arg_vs_exp),
impliedvol_side,
SingleVolSurfaceMarketImpliedVolSource._ignore_market_implied_vol_source_settings(
_bb, _vs_id),
bb, vs_id),
out md_exp);
rv[vs_exp] = exp_impl_data;
}
@@ -11,18 +11,22 @@ namespace mbts.analytics {
// tags, into IReadOnlyDictionary of symbol vs data publsihed under one tag
public class SymbolAggregationPublisher<TData> : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly ITag<SystemContractData> _sc_data_tag;
private readonly SecurityType _sec_type;
Func<string, ITag<TData>> _get_tag;
// the tag on which the output will be published
ITag<IReadOnlyDictionary<string, TData>> _out_tag;
private readonly ITag<SystemContractData> _sc_data_tag;
private Dictionary<string, ITag<TData>> _tag_map =
new Dictionary<string, ITag<TData>>();
private HashSet<object> _obj_tags = new HashSet<object>();
private bool _dirty = true;
public SymbolAggregationPublisher(
ThreadedBlackboard bb,
SecurityType sec_type,
Func<string, ITag<TData>> get_tag,
ITag<IReadOnlyDictionary<string, TData>> out_tag) {
_bb = bb;
_sec_type = sec_type;
_get_tag = get_tag;
_out_tag = out_tag;
_sc_data_tag = SparkTags.SystemContractsTag;
@@ -48,15 +52,20 @@ namespace mbts.analytics {
return;
}
foreach (var st in sc_data.sectup_map) {
if (st.security_type != _sec_type) {
continue;
}
// this is not correct since it is assuming symbol/name is
// unique across securty_tuples
string symbol = st.security_name;
if (!_tag_map.ContainsKey(symbol)) {
var symbol_tag = _get_tag(symbol);
_tag_map.Add(symbol, symbol_tag);
_obj_tags.Add(symbol_tag);
_bb.subscribe(symbol_tag);
_dirty = true;
var tag = _get_tag(symbol);
if (tag != null) {
_tag_map.Add(symbol, tag);
_obj_tags.Add(tag);
_bb.subscribe(tag);
_dirty = true;
}
}
}
}
@@ -72,7 +81,7 @@ namespace mbts.analytics {
output.Add(symbol, symbol_data);
}
IReadOnlyDictionary<string, TData> ro_output = output;
bts.Log.Gen.info($"SymbolAggregationPublisher._on_timer publishing " +
bts.Log.Gen.diag($"SymbolAggregationPublisher._on_timer publishing " +
$"tag: {_out_tag} sz: {ro_output.Count}");
_bb.post(_out_tag, ro_output);
}
@@ -16,6 +16,7 @@ using System.Collections;
using mbts.spark;
using System.DirectoryServices.ActiveDirectory;
using Spark.Core.Protobuf;
using mbts.ui;
// using mbts.bb;
namespace mbts.spark {
@@ -76,7 +77,8 @@ namespace mbts.spark {
private HighPerformanceTimer _timer = new HighPerformanceTimer();
public VolSurfaceBusManager(bts.api.IBus bus,
bts.api.analytics.AnalyticsService analytics_service) {
bts.api.analytics.AnalyticsService analytics_service,
bts.api.orderman.OrderService order_service) {
bts.Log.Ui.info("VSBusManager cons tid:{VolSurfaceTroubleshootingFunctions.tid()}");
_bus = bus;
_bb = new ThreadedBlackboard("AW2", bus);
@@ -101,12 +103,16 @@ namespace mbts.spark {
_subs.Add(new AnalyticsParametersManager(bus, _analytics_service));
_subs.Add(new ResourceAllocator(bus, _bb));
_subs.Add(new SymbolAggregationPublisher<OptionDriverPriceMap>(
_bb, (sym) => SparkTags.OptionDriverBBOTag(sym),
_bb, SecurityType.Option, sym => SparkTags.OptionDriverBBOTag(sym),
SparkTags.AllOptionDriverBBOTag));
_subs.Add(new SymbolAggregationPublisher<OptionTheoInterpolationData>(
_bb, (sym) => SparkTags.SymbolInterpGreeksTag(sym),
_bb, SecurityType.Option, sym => SparkTags.SymbolInterpGreeksTag(sym),
SparkTags.AllInterpGreeks));
_subs.Add(new SymbolAggregationPublisher<ExpStrikeKeyedData<TheoPoint>>(
_bb, SecurityType.Option, sym => SparkTags.TC2Tags.LiveTheosTag(sym),
SparkTags.TC2Tags.AllLiveTheosTag));
_subs.Add(new LiveTheoPublisher(_bb));
_subs.Add(new InterpTheosServerPublisher(_bb, order_service));
_subs.Add(new VolSurfaceInterpGreeksMultiplexer(_bb));
// usage
var list_sub = _bus.subscribe(SparkTags.VolSurfaceListTag).OnDispatcher().Subscribe(t => {
@@ -138,14 +144,74 @@ namespace mbts.spark {
var list_imm = new VolSurfaceUsageListImmutable(ulist);
Log.Ui.info(list_imm.log_string());
_bb.post(SparkTags.VolSurfaceUsageTag, list_imm);
_spin_vss(list_imm);
});
_disps.Add(list_sub);
// f(contract keyed theos, securities) -> id keyed theos
// (so the newer UI controls can access theos vis sec keys)
var theo_sub =
_bus.subscribe(SparkTags.TC2Tags.AllLiveTheosTag)
.CombineLatest(bus.subscribe(SparkTags.SystemContractsTag),
(theos, sc_data) => (theos, sc_data)).Subscribe(
tup => {
var keyed_theos = _assemble_keyed_theos(tup.sc_data, tup.theos);
_bus.post(SparkTags.KeyedTheos, keyed_theos);
}
);
_disps.Add(theo_sub);
}
static IReadOnlyDictionary<uint, VolSurfaceTheoPoint> _assemble_keyed_theos(
SystemContractData sc_data,
IReadOnlyDictionary<string, ExpStrikeKeyedData<TheoPoint>> theos) {
if (sc_data is null || theos is null) {
return new Dictionary<uint, VolSurfaceTheoPoint>(0);
}
HighPerformanceTimer timer = new HighPerformanceTimer();
timer.start();
Dictionary<uint, VolSurfaceTheoPoint> rv = new Dictionary<uint, VolSurfaceTheoPoint>();
foreach (var kv in theos) {
var symbol = kv.Key;
ExpStrikeKeyedData<TheoPoint> sym_theos = kv.Value;
if (sym_theos == null)
continue;
foreach (var s_kv in sym_theos.get_data_linearly()) {
DateTime exp = s_kv.first;
double strike = s_kv.second;
TheoPoint tp = s_kv.third;
// TODO: fix exchange later
Tuple<uint?, uint?> keys =
sc_data.get_option_sec_id(Venue.Deribit, symbol, exp, strike);
if (keys != null) {
var c_key = keys.Item1;
var p_key = keys.Item2;
if (c_key.HasValue) {
rv[c_key.Value] = new VolSurfaceTheoPoint(tp, true);
}
if (p_key.HasValue) {
rv[p_key.Value] = new VolSurfaceTheoPoint(tp, false);
}
}
}
}
timer.stop();
Log.Gen.info($"VolSurfaceBusManager keyed theos count: {rv.Count} " +
$"time: {timer.lastIntervalTime}");
return rv;
}
public ThreadedBlackboard bb() {
return _bb;
}
void _spin_vss(VolSurfaceUsageListImmutable ulist) {
foreach (var u in ulist.UsageList) {
var vs_id = u.VsId;
_bb.post(SparkTags.VolSurfaceActiveUITag(vs_id), true);
}
}
void _on_vs_usage_update(Update<VolSurfaceUsageListImmutable, int> update) {
// TODO
return;
@@ -204,6 +270,7 @@ namespace mbts.spark {
new mbts.analytics.VolSurfaceNonVolInput(exp_param_map.exp_map.ToDictionary()));
} else if (_in_bb_req_vol.Equals(tag)) {
ClientArchivedVolRequest client_req = _bb.read_state(_in_bb_req_vol);
bts.Log.Ui.info($"VolSurfaceBusManager._on_bb_update ClientArchivedVolRequest: {client_req.ToString()}");
ArchivedTheoRetrieval ret = client_req.ret;
if (ret == null || ret.SecTup == null) {
bts.Log.Ui.warn($"VolSurfaceBusManager malformed ArchivedTheoRetrieval");
@@ -0,0 +1,20 @@
namespace mbts.analytics {
using mbts.spark;
// TheoPoint (from edge) has both calls and puts in the same structure.
// This shoudl dis-entangle that
public class VolSurfaceTheoPoint {
public readonly double theo = double.NaN;
public readonly double delta = double.NaN;
public readonly double driver = double.NaN;
public VolSurfaceTheoPoint(TheoPoint tp, bool is_call) {
if (tp != null) {
theo = is_call ? tp.fwd_normalized_call : tp.fwd_normalized_put;
delta = is_call ? tp.cdelta : tp.pdelta;
driver = tp.driver;
}
}
}
}
@@ -6,6 +6,7 @@
using mbts.user.settings;
using System;
using System.Collections.Generic;
public class VolSurfaceDisplayGridColumn : IEquatable<VolSurfaceDisplayGridColumn> {
public readonly Func<object, string> format;
@@ -18,16 +19,19 @@
VolSurfaceColumnSettings settings,
Func<object, string> format = null,
Func<string, object> parse = null,
Func<object, bool, object> tick_move = null) {
Func<object, bool, object> tick_move = null,
IEqualityComparer<object> comparer = null) {
this.settings = settings;
this.format = format ?? (v => v?.ToString() ?? string.Empty);
this.parse = parse ?? (v => v);
this.tick_move = tick_move ?? ((v, is_up) => v);
this.comparer = comparer ?? EqualityComparer<object>.Default;
}
public string unique_col_name => settings.id;
public string display_name => settings.display_name;
public string group => settings.group;
public IEqualityComparer<object> comparer { get; }
internal VolSurfaceColumnType type => settings.type;
@@ -7,6 +7,7 @@
using mbts.user.settings;
using System;
using System.Collections.Generic;
class VolSurfaceDisplayUtilities {
public static TickDisplaySettings default_tick_display =>
@@ -18,6 +19,10 @@
default_tick_display.with(display_multiplier: 100);
public static double parse_double(object obj) {
if (obj is null) {
Log.Gen.warn("parse_double received null input (will return nan and continue)");
return double.NaN;
}
Log.assert(obj is double);
obj = obj ?? Double.NaN;
double rv = obj is Double ? (double)obj : (Double.TryParse(obj.ToString(), out rv) ? rv : Double.NaN);
@@ -49,9 +54,31 @@
}
public static Func<object, bool, object> get_double_tickmove(TickFactors tick_factors, int tick_move) =>
(data, is_up) => data is double d_data ?
tick_factors.tick(d_data, is_up ? tick_move : -tick_move) :
data;
(data, is_up) => {
if (data is not double dbl) {
return data;
}
if (dbl.is_nan()) {
return 0D;
}
return tick_factors.tick(dbl, is_up ? tick_move : -tick_move);
};
public static IEqualityComparer<double> get_double_comparer(
TickFactors tick_factors = null, double display_mult = double.NaN) {
if (tick_factors is null) {
return new TickFactors(10^7, 1).tick_equality_comparer();
}
// This equality comparer is used to compare actual values (not multiplied by
// 'display_mult'), while the TickFactors are used to format the display value (multiplied
// by 'display_mult'). Multiply the tick base by 'display_mult' so the equality comparison
// is done with the correct number of decimal places.
if (display_mult > 0 && Math.Log10(display_mult).is_int()) {
tick_factors = tick_factors.with(
tick_base: tick_factors.tick_base * display_mult);
}
return tick_factors.tick_equality_comparer();
}
public static VolSurfaceDisplayGridColumn get_double_output_col(
string group,
@@ -113,7 +140,10 @@
col_config,
format: get_double_tostring(col_config.tick_factors, col_config.tick_display),
parse: get_parse_double(col_config.tick_factors, col_config.tick_display),
tick_move: get_double_tickmove(col_config.tick_factors, col_config.tick_move));
tick_move: get_double_tickmove(col_config.tick_factors, col_config.tick_move),
comparer: get_double_comparer(
col_config.tick_factors, col_config.tick_display.display_multiplier)
.cast<double, object>());
}
}
}
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using mbts.analytics;
using bts.api;
using bts.collections;
using System.Linq;
using bts.api.orderman;
using bts.api.analytics;
using bts;
namespace mbts.spark {
// Listens to the interp theos, decorates the data
// and sends them over to the sparkd server
public class InterpTheosServerPublisher : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly bts.api.orderman.OrderService _order_service;
private HighPerformanceTimer _timer = new HighPerformanceTimer();
public InterpTheosServerPublisher(ThreadedBlackboard bb,
bts.api.orderman.OrderService order_service) {
bts.Log.Ui.info(
$"InterpTheosServerPublisher cons tid:{VolSurfaceTroubleshootingFunctions.tid()}");
_bb = bb;
_order_service = order_service;
_bb.subscribe(SparkTags.AllInterpGreeks);
_bb.subscribe(SparkTags.SystemContractsTag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.subscribe(SparkTags.VolSurfaceUsageTag);
_bb.data_callback += _on_data;
}
private void _on_data(object tag, object data) {
if (tag.Equals(SparkTags.SystemContractsTag) ||
tag.Equals(SparkTags.AllInterpGreeks)) {
_publish_data();
}
}
private static IDictionary<UInt32, DriverMD> _get_drivers(
IReadOnlyDictionary<string, OptionDriverPriceMap> driver_map,
SystemContractData sc_data) {
var rv = new Dictionary<UInt32, DriverMD>();
foreach (var kv in driver_map) {
rv.AddRange(
SecurityNormalizer.normalize_drivers(kv.Value, sc_data).ToArray());
}
return rv;
}
private static IEnumerable<Tuple<SecurityImmutable, TheoPoint>> _get_theos(
IReadOnlyDictionary<string, OptionTheoInterpolationData> theo_data,
SystemContractData sc_data) {
var rv = new List<Tuple<SecurityImmutable, TheoPoint>>();
foreach (var kv in theo_data) {
rv.AddRange(
SecurityNormalizer.normalize_interp_theos(kv.Value, sc_data));
}
return rv;
}
private IReadOnlyDictionary<string, OptionTheoInterpolationData>
_filter_for_publish(IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos) {
HashSet<string> syms = new HashSet<string>();
VolSurfaceUsageListImmutable usage = _bb.read_state(SparkTags.VolSurfaceUsageTag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
foreach (var u in usage.UsageList) {
VolSurfaceInput vs_input =
_bb.read_state(SparkTags.VolSurfaceCommittedInputTag(u.VsId));
if (vs_input != null) {
if (vs_input.surface_settings.core_settings.vs_manager ==
current_user) {
syms.Add(u.SymbolName);
}
}
}
return
all_theos.Where(kv => syms.Contains(kv.Key)).ToDictionary(kv => kv.Key, kv => kv.Value);
}
private void _publish_data() {
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag, null);
IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos =
_bb.read_state(SparkTags.AllInterpGreeks, null);
all_theos = _filter_for_publish(all_theos);
// find the symbol vs vs_id, get the committed vs, see if it is
// owned by the current user, and only then publish it
IReadOnlyDictionary<string, OptionDriverPriceMap> all_drivers =
_bb.read_state(SparkTags.AllOptionDriverBBOTag, null);
if (sc_data == null || all_theos == null || all_drivers == null)
return;
_timer.start();
IDictionary<UInt32, DriverMD> driver_map = _get_drivers(all_drivers, sc_data);
IEnumerable<Tuple<SecurityImmutable, TheoPoint>> theo_map = _get_theos(all_theos, sc_data);
// assemble it together and send it to the server
var ptp_list = new List<bts.api.orderman.PrecomputedTheoPoint>();
foreach (var tup in theo_map) {
SecurityImmutable sec = tup.Item1;
TheoPoint tp = tup.Item2;
bool is_call = sec.Option.CallPut == PutOrCall.Call;
var sec_id = sec.Id;
DriverMD dmd = null;
driver_map.TryGetValue(sec_id, out dmd);
PrecomputedTheoPoint ptp = new PrecomputedTheoPoint();
ptp.SecId = sec_id;
ptp.Driver = tp.driver;
ptp.DriverSecId = dmd != null && dmd.driver_security.HasValue ?
dmd.driver_security.Value.Id : 0;
ptp.Theo = is_call ? tp.call : tp.put;
ptp.Delta = is_call ? tp.cdelta : tp.pdelta;
ptp.Gamma = is_call ? tp.cgamma : tp.pgamma;
ptp.Vega = is_call ? tp.cvega : tp.pvega;
ptp.Theta = is_call ? tp.c_theta : tp.p_theta;
ptp.Rho = Double.NaN;
ptp_list.Add(ptp);
}
bts.api.orderman.PrecomputedTheos server_theos = new PrecomputedTheos();
server_theos.Theos.AddRange(ptp_list);
_timer.stop();
var comp_time = _timer.lastIntervalTime;
_timer.start();
_order_service.set_precomputed_theos(server_theos);
_timer.stop();
var pub_time = _timer.lastIntervalTime;
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data " +
$"size: {server_theos.Theos.Count} comp_time: {comp_time} msecs " +
$"pub_time: {pub_time}");
}
public void Dispose() {
_bb.data_callback -= _on_data;
}
}
}
+42 -11
View File
@@ -5,6 +5,8 @@ using bts.api;
using mbts.ui;
using MathNet.Numerics.Providers.FourierTransform;
using BCG;
using System.Windows.Automation;
using bts.collections;
namespace mbts.spark {
// Publishes theos for all options (on the side, also publishes the theos for TC2)
@@ -68,19 +70,14 @@ namespace mbts.spark {
}
private void _log_theos(SystemContractData sc_data,
IReadOnlyDictionary<uint, GreeksTuple> greeks_map,
DateTime? exp_filt = null) {
IReadOnlyDictionary<uint, GreeksTuple> greeks_map) {
string rv = "LiveTheosPublisher data: ";
foreach (var kv in greeks_map) {
var id = kv.Key;
var g_tup = kv.Value;
if (sc_data.sec_md_map.TryGetValue(id, out var smd)) {
if (exp_filt == null ||
(smd.security.security_type() == SecurityType.Option &&
smd.security.Option.expiration_date().Equals(exp_filt.Value))) {
string sec_str = smd.ToString();
rv += "\n" + $"id: {id} sec: {sec_str} greeks: {g_tup}";
}
string sec_str = smd.ToString();
rv += "\n" + $"id: {id} sec: {sec_str} greeks: {g_tup}";
}
}
bts.Log.Gen.info(rv);
@@ -134,6 +131,7 @@ namespace mbts.spark {
return;
}
var k_theos = new Dictionary<SecurityStrike, TheoPoint>();
// option spreads
foreach (var kv in sc_data.strike_map) {
foreach (SecurityStrike k in kv.Value) {
var (call_id, put_id) = k.call_put_id;
@@ -142,26 +140,59 @@ namespace mbts.spark {
k_theos[k] = tp;
// put it in the output map
if (call_id.HasValue) {
var normalized_theo = VolSurfaceCoreFunctions.normalize_theo(tp.call, tp.driver);
res.Add(call_id.Value,
new GreeksTuple(tp.vol, tp.driver, tp.call, tp.cdelta, tp.cgamma,
tp.cvega, tp.c_theta, Double.NaN));
tp.cvega, tp.c_theta, Double.NaN, normalized_theo));
}
if (put_id.HasValue) {
var normalized_theo = VolSurfaceCoreFunctions.normalize_theo(tp.put, tp.driver);
res.Add(put_id.Value,
new GreeksTuple(tp.vol, tp.driver, tp.put, tp.pdelta, tp.pgamma,
tp.pvega, tp.p_theta, Double.NaN));
tp.pvega, tp.p_theta, Double.NaN, normalized_theo));
}
}
}
// futures
var future_theos = _get_future_theos();
res.AddRange(future_theos);
// spreads, all option and future non tied spreads
foreach (var spr in sc_data.spreads) {
GreeksTuple gtup = StatelessSpreadCalculator.compute_non_tied_spread(spr.security, res);
res.Add(spr.security.Id, gtup);
}
// copy and publish
IReadOnlyDictionary<uint, GreeksTuple> res_ro = res;
bts.Log.Gen.info($"LiveTheoPublisher._interp_and_publish posting " +
$"count: {res_ro.Count}");
$"count: {res_ro.Count}");
_timer.stop();
_logger.log(_log_id, _log_id, _timer.lastIntervalTime);
_bb.post(SparkTags.Greeks, res_ro);
// oh and those TC2 theos for comptability
_publish_tc2_theos(k_theos);
}
// If we do any rolls like SP for futures, it will be here
IDictionary<UInt32, GreeksTuple> _get_future_theos() {
var rv = new Dictionary<UInt32, GreeksTuple>();
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag);
if (sc_data == null)
return rv;
foreach (var kv in sc_data.exp_map) {
var id = kv.Value;
SecurityMD sec_md;
if (sc_data.sec_md_map.TryGetValue(id, out sec_md)) {
if (sec_md.security.HasFuture) {
SecurityExpiration exp = kv.Key;
double pr = sec_md.md.fixing_price;
rv.Add(sec_md.security.Id,
new GreeksTuple(0, pr, pr, 1, 0, 0, 0, 0, pr));
}
}
}
return rv;
}
// recomputes and publishes them periodicially
void _publish_future_theos() {
if (DateTime.Now - _last_future_theos_published < _futures_theos_publish_period)
@@ -1,5 +1,7 @@
#nullable enable
namespace mbts.spark {
using bts.api;
using mbts.analytics;
using System;
@@ -39,5 +41,10 @@ namespace mbts.spark {
.SelectMany(kv => kv.Value.exp_price_map)
.ToDictionaryCrushed(kv => kv.Key.to_vs_exp(), kv => kv.Value);
}
public IReadOnlyDictionary<SecurityExpiration, DriverMD> get_price_map() {
return new Dictionary<SecurityExpiration, DriverMD>(exp_price_map);
}
}
}
@@ -0,0 +1,70 @@
namespace mbts.spark {
using bts.api;
using System;
using System.Collections.Generic;
using System.Linq;
// A lot of options data (migrated from edge) is not normalized
// on the sec_id, but rather on a combination of symbol, exp, strike.
// Additionally the exchange is missing. This class serves as that
// one since point to resolve that.
// (Over time we will normalize all the data and this wont be needed)
public static class SecurityNormalizer {
// f(option_exp:driver_md) -> (sec_id:driver_md)
public static IDictionary<UInt32, DriverMD> normalize_drivers(
OptionDriverPriceMap driver_map, SystemContractData sc_data) {
if (driver_map is null) {
return new Dictionary<uint, DriverMD>(0);
}
Dictionary<UInt32, DriverMD> rv = new Dictionary<uint, DriverMD>();
foreach (var kv in driver_map.get_price_map()) {
var exp = kv.Key;
var dmd = kv.Value;
IEnumerable<SecurityMD> exp_secs = sc_data.get_securities(
(sec_md) => {
var s = sec_md.security;
return s.security_type() == SecurityType.Option &&
s.Symbol == driver_map.option_symbol &&
s.earliest_expiration() == exp.expiration;
}
);
foreach (var sec_md in exp_secs) {
rv.Add(sec_md.security.Id, kv.Value);
}
}
return rv;
}
public static IEnumerable<Tuple<SecurityImmutable, TheoPoint>> normalize_interp_theos(
OptionTheoInterpolationData symbol_theos, SystemContractData sc_data) {
if (symbol_theos is null) {
return Enumerable.Empty<Tuple<SecurityImmutable, TheoPoint>>();
}
var rv = new List<Tuple<SecurityImmutable, TheoPoint>>();
foreach (DateTime exp in symbol_theos.get_expirations()) {
Dictionary<double, Dictionary<double, TheoPoint>> exp_data =
symbol_theos.get_expiration_data(exp);
// exp_data is driver vs strike vs TP
foreach (var exp_kv in exp_data) {
foreach (var k_kv in exp_kv.Value) {
double strike = k_kv.Key;
TheoPoint tp = k_kv.Value;
IEnumerable<SecurityMD> calls = sc_data.get_options(
null, symbol_theos.symbol, exp, strike, PutOrCall.Call);
IEnumerable<SecurityMD> puts = sc_data.get_options(
null, symbol_theos.symbol, exp, strike, PutOrCall.Put);
foreach (SecurityMD sec_md in calls) {
rv.Add(Tuple.Create(sec_md.security, tp));
}
foreach (SecurityMD sec_md in puts) {
rv.Add(Tuple.Create(sec_md.security, tp));
}
}
}
}
return rv;
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -107,6 +108,22 @@ namespace mbts.spark {
}
}
public SecurityImmutable? get_option_contract(
Venue venue, string symbol, DateTime exp, double strike, PutOrCall cp) {
var k_tup = get_option_sec_id(venue, symbol, exp, strike);
if (k_tup != null) {
var id = cp == PutOrCall.Call ? k_tup.Item1 : k_tup.Item2;
if (id.HasValue) {
var id_val = id.Value;
SecurityMD sec_md;
if (sec_md_map.TryGetValue(id_val, out sec_md)) {
return sec_md.security;
}
}
}
return null;
}
public Tuple<uint?, uint?> get_option_sec_id(
Venue venue, string symbol, DateTime exp, double strike) {
var s_exp = new SecurityExpiration(venue, SecurityType.Option, symbol, exp);
@@ -242,5 +259,28 @@ namespace mbts.spark {
kv.Value.Count > 0).Select(kv => kv.Key.expiration);
}
public IEnumerable<SecurityMD> spreads {
get {
return sec_md_map.Where(kv => kv.Value.security.HasSpread)
.Select(kv => kv.Value);
}
}
public IEnumerable<SecurityMD> get_securities(Func<SecurityMD, bool> criteria) {
return sec_md_map.Values.Where(sec_md => criteria(sec_md));
}
public IEnumerable<SecurityMD> get_options(
Venue? exchange, string option_name, DateTime? exp, double? strike, PutOrCall? cp) {
return get_securities(sc => {
var s = sc.security;
return s.security_type() == SecurityType.Option &&
(exchange == null || s.Exchange == exchange) &&
(exp == null || s.earliest_expiration() == exp) &&
(strike == null || s.strike() == strike) &&
(cp == null || s.Option.CallPut == cp);
});
}
}
}
@@ -63,6 +63,27 @@ namespace mbts.spark {
return rv;
}
public static string log_string(this VolSurfaceCalculationRequest vs_req) {
if (vs_req == null) {
return "Null";
}
string action_str = String.Empty;
foreach (var kv in vs_req.action_map) {
action_str += $"exp: {kv.Key.ToString()} action: {kv.Value.ToString()}, ";
}
action_str = action_str.TrimEnd(',', ' ');
return $"vs_inp: {vs_req.volsurface_input.log_string()}\nreq_id: {vs_req.request_id}\n" +
$"action_map: {action_str}\nup: {vs_req.cascade_up} down: {vs_req.cascade_down}";
}
public static string log_string(this VolSurfaceCalculationResponse resp) {
if (resp == null) {
return "Null";
}
return $"req_id: {resp.request_id} vs_output: " +
$"{resp.volsurface_calculation_output.volsurface_input.log_string()}";
}
private static string _trim_leading_char(string s, char c) {
if (!s.is_null_or_empty() && s.EndsWith(c)) {
return s.Remove(s.Length - 1, 1);
@@ -71,11 +92,12 @@ namespace mbts.spark {
}
internal static string log_string(this VolSurfaceDisplayModel.DisplayModelData dm_data) {
string rv = String.Empty;
string global_grid_str = dm_data.global_grid == null ? "Null" : dm_data.global_grid.log_string();
rv += $"global_grid: {global_grid_str} \n";
string exp_grid_str = dm_data.exp_grid == null ? "Null" : dm_data.exp_grid.log_string();
rv += $"exp_grid: {exp_grid_str} \n";
var vs_output = dm_data.vs_output;
var exp_grid = dm_data.exp_grid;
var vs_output_str = vs_output == null ? "NULL" : vs_output.volsurface_input.log_string();
var exp_grid_str = exp_grid == null ? "NULL" : exp_grid.log_string();
var rv = $"DisplayModelData\nvs_output:\n{vs_output_str}\n" +
$"exp_grid\n{exp_grid_str}\n";
return rv;
}
@@ -91,19 +113,19 @@ namespace mbts.spark {
}
internal static string log_string(this VolSurfaceDisplayGrid dgrid) {
Func<IDictionary<string, object>, string> log_exp = (map) => {
Func<IDictionary<string, object>, string> log_exp = (col_val_map) => {
string rv = String.Empty;
foreach (var kv in map) {
foreach (var kv in col_val_map) {
string val_string = kv.Value == null ? "Null" : kv.Value.ToString();
rv += $"{kv.Key}:{val_string},";
rv += $"{kv.Key}: {val_string}, ";
}
return _trim_leading_char(rv, ',');
};
string rv = String.Empty;
foreach (var kv in dgrid.data_map) {
rv += $"Exp:{kv.Key.expiration.ToShortDateString()} Data:{log_exp(kv.Value)},";
rv += $"Exp: {kv.Key.expiration.ToShortDateString()} Data: {log_exp(kv.Value)}\n";
}
return _trim_leading_char(rv, ',');
return rv;
}
internal static string log_string(this OptionDriverPriceMap map) {
@@ -238,6 +260,15 @@ namespace mbts.spark {
return rv;
}
internal static string log_string(this IPNLUnit p) {
string rv = "IPNLUnit:";
rv += $" account: {p.account} sec: {p.security.security_desc()} driver: {p.driver} theo: {p.theo} settle: {p.settle_price}" +
$" start_pos: {p.start_long_pos - p.start_short_pos} day_pos: {p.day_long_pos - p.day_short_pos}" +
$" start_pnl: {p.start_pnl} start_delta: {p.start_delta} start_vega: {p.start_vega}" +
$" day_pnl: {p.day_pnl} day_delta: {p.day_delta} day_vega: {p.day_vega}";
return rv;
}
internal static int tid() {
return System.Threading.Thread.CurrentThread.ManagedThreadId;
}
@@ -12,7 +12,7 @@ namespace mbts.spark {
using System.Collections.Generic;
[ProtoContract]
class VolSurfaceGraphSettings : IEquatable<VolSurfaceGraphSettings>, ICloneable {
class VolSurfaceGraphSettings : IProtobuf<VolSurfaceGraphSettings> {
public static VolSurfaceGraphSettings get_default() {
return new VolSurfaceGraphSettings {
put_control_points = new ScatterSeriesSettings {
@@ -110,7 +110,7 @@ namespace mbts.spark {
// }
//}
public object Clone() =>
public VolSurfaceGraphSettings Clone() =>
ProtobufSerializationHelper.clone(this);
public bool Equals(VolSurfaceGraphSettings other) =>
@@ -6,6 +6,10 @@ using mbts.analytics;
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core;
using Spark.Core.Disposables;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -51,7 +55,7 @@ namespace mbts.spark {
var vs_removes = removed.Select(exp => exp.ToVSExp());
var true_removed = removed.Where(rmv => !vs_adds.Contains(rmv.ToVSExp()));
var true_added = added.Where(add => !vs_removes.Contains(add.ToVSExp()));
var true_added = added.Except(vs_removes.Concat(_hidden_exps), v => v.ToVSExp());
var changed_calc_modes = added.Where(add => vs_removes.Contains(add.ToVSExp()));
foreach (var tr in true_removed) {
@@ -61,16 +65,10 @@ namespace mbts.spark {
}
}
foreach (var ta in true_added) {
if (_hidden_exps.Contains(ta.ToVSExp()) ||
!rows.TryGetFirst(r => r.expiration.expiration == ta.expiration, out var row)) {
continue;
}
visible_rows.InsertSorted(row,
Comparer<IExpirationBasedRow>.Create(
(r1, r2) => r1.expiration.CompareTo(r2.expiration))
.wrap<IExpirationBasedRow, object>());
}
var comp = ComparerWrapper<object>.wrap_cast(
NullCheckComparer.create<IExpirationBasedRow>(
(r1, r2) => r1.expiration.CompareTo(r2.expiration)));
visible_rows.InsertSorted(true_added, comp);
foreach (var ccm in changed_calc_modes) {
int index = visible_rows
@@ -10,6 +10,10 @@ namespace mbts.spark {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Analytics.View;
using Spark.Core.Disposables;
using Spark.Core.Protobuf;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -27,13 +31,13 @@ namespace mbts.spark {
readonly LinearAxisVM _x_axis = new() { position = AxisPosition.BottomLeftToRight };
readonly LinearAxisVM _y_axis = new() { position = AxisPosition.LeftBottomToTop };
readonly Dictionary<SecurityExpiration, ExpGraphBounds> _expiration_graph_bounds;
readonly VolSurfaceGraphSettings _settings;
readonly ObservableCollection<VolSurfaceDisplayGridExpiration> _expirations = new();
readonly Dictionary<VolSurfaceDisplayGridExpiration, SeriesSetVM> _expiration_series_cache = new();
VolSurfaceExpiration _md_exp;
VolSurfaceDisplayGridExpiration _expiration;
VolSurfaceDisplayGridExpiration _earliest_selected_expiration;
VolSurfaceGraphSettings _settings;
object _implied_vol_market_side = ImpliedVolHelper.None;
bool _show_vol_path;
@@ -82,13 +86,17 @@ namespace mbts.spark {
if (_model.get_active_settings() is not VolSurfaceSettings vss) {
return null;
}
// return new VolSurfaceGraphSettingsEditor {
// DataContext = new VolSurfaceGraphSettingsEditorVM(vss.core_settings.vol_surface_name, ProtoBufSerializationHelper.clone_protobuf(_settings), settings => {
// _settings = settings;
// _recreate_series();
// })
// };
return null;
var vm = new VolSurfaceGraphSettingsVM(vss.core_settings.vol_surface_name);
vm.load_from(_settings.Clone());
vm.request_commit += commit;
return new VolSurfaceGraphSettingsWindow {
DataContext = vm
};
void commit(VolSurfaceGraphSettings settings) {
_settings = settings;
_recreate_series();
}
});
graph_vm = new BTSGraphVM {
plot_area_background = Colors.Transparent,
@@ -117,7 +125,7 @@ namespace mbts.spark {
})
.BufferLatest(TimeSpan.FromSeconds(0.1))
.OnDispatcher()
.SelectManyInSeries(series => Observable.Create<SeriesVM>(o => {
.SelectSeries(series => Observable.Create<SeriesVM>(o => {
o.OnNext(null);
return Disposable.Create(series
.Select(svm => svm.points.Subscribe((s, e) => o.OnNext(svm)))
@@ -185,6 +193,20 @@ namespace mbts.spark {
static (double call_wing_start, double put_wing_start) _get_actual_wing_starts(VolCurveDisplayGraph disp_graph) =>
disp_graph.all_points.Select(p => p.Item1).MinMax() ?? (double.NaN, double.NaN);
void _recreate_series() {
/// 'graph_vm.series_set' is bound to '_expirations', so clearing and re-adding expirations
/// in turn regenerates the series. '_expiration_series_cache' must
/// be cleared as well, otherwise cached series vms would just be back to the collection
/// ('graph_vm.series_set').
var visible_exp = _earliest_selected_expiration;
_expiration_series_cache.Clear();
_expirations.SetItems(_model.get_ordered_expirations());
foreach (var svm in graph_vm.series.OfType<SeriesSetVM>()) {
svm.is_visible = svm.args.Equals(visible_exp);
}
_update();
}
void _expirations_changed(IEnumerable<VolSurfaceDisplayGridExpiration> added, IEnumerable<VolSurfaceDisplayGridExpiration> removed) {
_expirations.sync_with(_model.get_ordered_expirations());
_update();
@@ -1,84 +0,0 @@
namespace mbts.spark {
using mbts.ui;
using mbts.ui.shell;
using System;
using System.Reactive.Linq;
using System.Windows.Input;
class VolGridCellVM : DisposableViewModelBase {
readonly Property<object> _candidate_value;
readonly Property<object> _committed_value;
readonly Property<bool> _is_dirty;
public VolGridCellVM(string column_id) {
this.column_id = column_id ?? throw new ArgumentNullException(nameof(column_id));
_committed_value = backing<object>(nameof(committed_value));
_candidate_value = backing<object>(nameof(candidate_value));
_is_dirty = backing(nameof(is_dirty),
_committed_value.observe_current(true)
.CombineLatest(_candidate_value.observe_current(true))
.Select(tup => !Equals(tup.First, tup.Second))
.OnDispatcher());
}
public string column_id { get; }
public object committed_value {
get => _committed_value.Value;
set => _committed_value.set(value);
}
public object candidate_value {
get => _candidate_value.Value;
set => _candidate_value.set(value);
}
public bool is_dirty => _is_dirty.Value;
public IObservable<object> observe_candidate(bool start_immediately = false) =>
_candidate_value.observe_current(start_immediately);
public void revert() =>
candidate_value = committed_value;
public override string ToString() =>
$"column_id: {column_id}, committed: {committed_value}, candidate: {candidate_value}";
}
class VolGridCellVM<T> : VolGridCellVM {
public VolGridCellVM(string column_id) : base(column_id) { }
public VolGridCellVM(string column_id, T committed_value, T candidate_value) : base(column_id) {
this.committed_value = committed_value;
this.candidate_value = candidate_value;
}
public new T committed_value {
get => base.committed_value is T v ? v : default;
set => base.committed_value = value;
}
public new T candidate_value {
get => base.candidate_value is T v ? v : default;
set => base.candidate_value = value;
}
}
class VolGridTickCellVM : VolGridCellVM {
ICommand _tick_up_cmd;
ICommand _tick_down_cmd;
public VolGridTickCellVM(string column_id) : base(column_id) { }
public event Action<bool> request_tick;
public ICommand tick_up_cmd =>
_tick_up_cmd ??= new DelegateCommand(() =>
request_tick?.Invoke(true));
public ICommand tick_down_cmd =>
_tick_down_cmd ??= new DelegateCommand(() =>
request_tick?.Invoke(false));
}
}
@@ -0,0 +1,11 @@
#nullable enable
namespace mbts.spark.VolSurface {
using System.Windows.Media;
readonly struct VolSurfaceCellState {
public bool is_dirty => committed_text is not null;
public FormattedText? text { get; init; }
public FormattedText? committed_text { get; init; }
}
}
@@ -0,0 +1,80 @@
#nullable enable
namespace mbts.spark {
using bts;
using mbts.ui.controls;
using mbts.ui.shell;
using Spark.Core;
using System;
class VolSurfaceCellVM : DisposableViewModelBase {
readonly Property<object?> _candidate_value;
readonly Property<object?> _committed_value;
readonly DelegateProperty<bool> _is_dirty;
readonly DeferrableAction _refresh;
public VolSurfaceCellVM(
VolSurfaceDisplayGridExpiration expiration, VolSurfaceDisplayGridColumn column) {
this.column = column ?? throw new ArgumentNullException(nameof(column));
this.expiration = expiration ?? throw new ArgumentNullException(nameof(expiration));
_committed_value = backing<object?>(nameof(committed_value), column.comparer);
_candidate_value = backing<object?>(nameof(candidate_value), column.comparer);
_is_dirty = backing(
nameof(is_dirty), () => !column.comparer.Equals(candidate_value, committed_value));
_refresh = new(_is_dirty.refresh);
_committed_value.Changed += on_committed_changed;
_candidate_value.Changed += on_candidate_changed;
if (column.type.is_editable()) {
_is_dirty.Changed += (o, n) => {
Log.Ui.info(
$"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
$"commited: {committed_value}; candidate: {candidate_value}");
};
}
void on_committed_changed(object? oldval, object? newval) =>
_is_dirty.refresh();
void on_candidate_changed(object? oldval, object? newval) =>
_is_dirty.refresh();
}
public event PropertyChange<object?>? candidate_value_changed {
add => _candidate_value.Changed += value;
remove => _candidate_value.Changed -= value;
}
public VolSurfaceDisplayGridColumn column { get; }
public string column_id => column.unique_col_name;
public VolSurfaceDisplayGridExpiration expiration { get; }
public object? committed_value {
get => _committed_value.Value;
set => _committed_value.set(value);
}
public object? candidate_value {
get => _candidate_value.Value;
set => _candidate_value.set(value);
}
public bool is_dirty => _is_dirty.Value;
public IObservable<object?> observe_candidate(bool start_immediately = false) =>
_candidate_value.observe_current(start_immediately);
public void reset(object? committed_value, object? candidate_value) {
using var _ = _refresh.defer();
this.committed_value = committed_value;
this.candidate_value = candidate_value;
}
public void revert() =>
candidate_value = committed_value;
public override string ToString() =>
$"column: {column}, committed: {committed_value}, candidate: {candidate_value}";
}
}
@@ -78,7 +78,7 @@ namespace mbts.spark.VolSurface {
view.SetBinding(FrameworkElement.DataContextProperty, new Binding() {
Converter = _data_context_converter
});
view.SetBinding(TextBlock.TextProperty, new Binding(nameof(VolGridCellVM.candidate_value)) {
view.SetBinding(TextBlock.TextProperty, new Binding(nameof(VolSurfaceCellVM.candidate_value)) {
Converter = ValueConverter.create(obj => model.format(obj))
});
return view;
@@ -101,16 +101,16 @@ namespace mbts.spark.VolSurface {
view.SetBinding(FrameworkElement.DataContextProperty, new Binding() {
Converter = _data_context_converter
});
view.SetBinding(VolSurfaceCellView.CommittedValueProperty, new Binding(nameof(VolGridCellVM.committed_value)) {
view.SetBinding(VolSurfaceCellView.CommittedValueProperty, new Binding(nameof(VolSurfaceCellVM.committed_value)) {
Mode = BindingMode.OneWay,
Converter = ValueConverter.create(obj => model.format(obj))
});
view.SetBinding(VolSurfaceCellView.CandidateValueProperty, new Binding(nameof(VolGridCellVM.candidate_value)) {
view.SetBinding(VolSurfaceCellView.CandidateValueProperty, new Binding(nameof(VolSurfaceCellVM.candidate_value)) {
Mode = BindingMode.TwoWay,
Converter = ValueConverter.create<object, string>(obj => model.format(obj), obj => model.parse(obj))
});
view.SetBinding(VolSurfaceCellView.TickDownCommandProperty, new Binding(nameof(VolGridTickCellVM.tick_down_cmd)));
view.SetBinding(VolSurfaceCellView.TickUpCommandProperty, new Binding(nameof(VolGridTickCellVM.tick_up_cmd)));
view.SetBinding(VolSurfaceCellView.TickDownCommandProperty, new Binding(nameof(VolSurfaceTickCellVM.tick_down_cmd)));
view.SetBinding(VolSurfaceCellView.TickUpCommandProperty, new Binding(nameof(VolSurfaceTickCellVM.tick_up_cmd)));
var bd = new Border {
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
@@ -1,3 +1,4 @@
#nullable enable
namespace mbts.spark.VolSurface {
using bts;
using bts.collections;
@@ -11,6 +12,10 @@ namespace mbts.spark.VolSurface {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core;
using Spark.Core.Disposables;
using Spark.Core.Collections;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -24,19 +29,25 @@ namespace mbts.spark.VolSurface {
class VolSurfaceGridVM : DisposableViewModelBase {
readonly VolSurfaceVM _parent;
readonly UserTuple _user;
readonly ObservableCollection<VolSurfaceDisplayGridExpiration> _expirations = new();
readonly DeferrableActionWrapper _invalidate_rows;
readonly DeferrableAction _invalidate_rows;
readonly ColumnsModel _columns_model;
ISet<string> _hidden_columns;
ISet<VolSurfaceExpiration> _hidden_expirations;
IDisposable _shift_editor;
ISet<VolSurfaceExpiration>? _hidden_expirations;
IDisposable? _shift_editor;
public VolSurfaceGridVM(VolSurfaceVM vm, VolSurfaceDisplayModel model,
ISet<string> hidden_columns, ISet<VolSurfaceExpiration> hidden_expirations) {
Log.assert(hidden_columns != null);
Log.assert(hidden_expirations != null);
public VolSurfaceGridVM(
VolSurfaceVM vm,
VolSurfaceDisplayModel model,
ISet<string> hidden_columns,
ISet<VolSurfaceExpiration> hidden_expirations,
UserTuple user) {
Log.notnull(hidden_columns);
Log.notnull(hidden_expirations);
_parent = vm;
_user = user;
this.model = model;
_columns_model = new(create_exp_column_view());
_hidden_columns = hidden_columns;
@@ -63,16 +74,31 @@ namespace mbts.spark.VolSurface {
};
_dispose = Disposable.Create(
ObservableAutoWrapper.connect(_expirations, rows, exp => new VolSurfaceRowVM(this, exp), row => row.Dispose()),
ObservableAutoWrapper.connect(_expirations, rows, row_factory),
ObservableAutoWrapper.connect_item_hooks(rows, connect_row),
sorted_visible_rows.bind(visible_rows),
Disposable.Create(() => {
this.model.on_candidate_grid_change -= _on_grid_updated;
this.model.on_committed_grid_change -= _on_grid_updated;
Disposable.dispose(rows);
rows.DisposeAll(true);
Disposable.dispose(ref _shift_editor);
}),
_columns_model);
VolSurfaceRowVM row_factory(VolSurfaceDisplayGridExpiration exp) =>
new(this, exp, _user);
IDisposable connect_row(VolSurfaceRowVM row) {
row.request_calculate += request_recalc;
return Disposable.Create(() => {
row.request_calculate -= request_recalc;
row.Dispose();
});
void request_recalc() =>
model.set_candidate_grid(current_grid);
}
VolSurfaceColumn create_exp_column_view() {
var filter = new ComboCheckBox();
filter.SetResourceReference(FrameworkElement.StyleProperty, "ExpirationFilterDropDown");
@@ -92,7 +118,7 @@ namespace mbts.spark.VolSurface {
}
}
public event Action<IEnumerable<VolSurfaceDisplayGridExpiration>> on_selection_changed;
public event Action<IEnumerable<VolSurfaceDisplayGridExpiration>>? on_selection_changed;
public VolSurfaceDisplayGrid current_grid { get; private set; }
public VolSurfaceDisplayGrid committed_grid { get; private set; }
@@ -118,6 +144,7 @@ namespace mbts.spark.VolSurface {
.Select(group => new DIYGridSuperHeaderVM(group.Key, group.Count(), collapsed_content: _short_header(group.Key)))
.ToList();
public ICommand populate_empty_exps_cmd => _parent.populate_empty_exps_cmd;
public ICommand main_settings_cmd => _parent.main_settings_cmd;
public ICommand advanced_settings_cmd => _parent.advanced_settings_cmd;
public ICommand graph_settings_cmd => _parent.vol_curve_vm.settings_command;
@@ -141,13 +168,14 @@ namespace mbts.spark.VolSurface {
rows.select_exp().Except(visible_rows.select_exp());
public void invalidate_rows() =>
_invalidate_rows.execute();
_invalidate_rows.Invoke();
public IDisposable defer_invalidate_rows() =>
_invalidate_rows.defer_execution();
_invalidate_rows.defer();
public VolSurfaceColumn get_column(string id) =>
columns.FirstOrDefault(c => c.id == id) as VolSurfaceColumn;
columns.FirstOrDefault(c => c.id == id) as VolSurfaceColumn ??
throw new InvalidOperationException($"Failed to find column '{id}'");
internal void signal_selection_changed() {
var e = on_selection_changed;
@@ -218,7 +246,7 @@ namespace mbts.spark.VolSurface {
!_hidden_columns.Contains(column.unique_col_name);
void _on_grid_updated() {
using var _ = _invalidate_rows.defer_execution();
using var _ = _invalidate_rows.defer();
var grid_new = model.get_active_grid();
// add/remove columns
var cols_new = grid_new.get_ordered_columns()
@@ -243,7 +271,7 @@ namespace mbts.spark.VolSurface {
}
current_grid = grid_new;
committed_grid = is_dirty ? model.get_committed_grid() : current_grid;
_invalidate_rows.execute();
_invalidate_rows.Invoke();
}
void _launch_shift_editor(VolSurfaceDisplayGridColumn column, Action<double> commit_shift) {
@@ -324,7 +352,7 @@ namespace mbts.spark.VolSurface {
model.set_candidate_grid(current_grid);
class ColumnsModel : IDisposable {
IDisposable _dispose;
IDisposable? _dispose;
public ColumnsModel(VolSurfaceColumn row_header_col) {
views_with_header_ro = ObservableAutoWrapper.create_read_only_wrapper(views_with_header);
@@ -1,5 +1,6 @@
#nullable enable
namespace mbts.spark {
using bts;
using bts.collections;
using mbts.spark.VolSurface;
@@ -8,6 +9,9 @@ namespace mbts.spark {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -20,6 +24,10 @@ namespace mbts.spark {
readonly VolSurfaceGridVM _parent;
readonly Property<VolSurfaceColumn?> _column;
readonly Property<DIYGridSuperHeaderVM?> _super_header;
readonly UserTuple _user;
readonly Property<bool> _is_open;
readonly DelegateProperty<string> _vs_manager;
readonly List<IDisposable> _resources = new();
ICommand? _revert_cmd;
ICommand? _shift_selection_cmd;
@@ -32,21 +40,29 @@ namespace mbts.spark {
ICommand? _copy_all_to_clipboard_cmd;
ICommand? _smart_copy_rolls_rates_cmd;
ICommand? _import_csv_cmd;
ObservantCommand? _acquire_ownership_cmd;
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row) {
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row, UserTuple user) {
_parent = parent;
_row = row;
_user = user;
_column = backing<VolSurfaceColumn?>(nameof(column));
_super_header = backing<DIYGridSuperHeaderVM?>(nameof(super_header));
_vs_manager = backing(
nameof(vs_manager),
() => _parent.model.get_active_settings()?.core_settings?.vs_manager ?? string.Empty);
_is_open = backing(nameof(is_open), false);
_dispose = Disposable.Create(
_resources.AddRange(
_column.invalidate_others_on_change(nameof(column_name)),
_super_header.invalidate_others_on_change(nameof(group_name)),
_is_open.Subscribe(_ => _vs_manager.refresh()),
_parent.visible_rows.ObserveSelf()
.BufferLatest(TimeSpan.FromSeconds(1))
.OnDispatcher()
.Subscribe(_ => sync_symbols()));
_dispose = Disposable.Create(() => _resources.DisposeAll(true));
void sync_symbols() {
var visible_exps = _parent.visible_rows.Map(r => r.expiration.symbol);
symbols.sync_with_models(
@@ -79,6 +95,7 @@ namespace mbts.spark {
public bool is_input_cell => column?.type == VolSurfaceColumnType.ReadWriteTick;
public ICommand populate_empty_exps_cmd => _parent.populate_empty_exps_cmd;
public ICommand main_settings_cmd => _parent.main_settings_cmd;
public ICommand advanced_settings_cmd => _parent.advanced_settings_cmd;
public ICommand grid_settings_cmd => _parent.grid_settings_cmd;
@@ -161,6 +178,34 @@ namespace mbts.spark {
_parent.model.csv_importing_enabled,
null);
public ICommand acquire_ownership_cmd {
get {
if (_acquire_ownership_cmd is null) {
_acquire_ownership_cmd = new(
() => {
var settings = _parent.model.get_active_settings();
if (settings is null) {
Log.Ui.error("Unable to acquire volsurface ownership: settings are null.");
return;
}
settings.core_settings.vs_manager = _user.name;
_parent.model.set_candidate_settings(settings);
});
_resources.Add(_vs_manager.observe_current(true)
.Subscribe(SelectorObserver.builder(_acquire_ownership_cmd).wrap<string>(
curr => curr != _user.name)));
}
return _acquire_ownership_cmd;
}
}
public bool is_open {
get => _is_open.Value;
set => _is_open.set(value);
}
public string vs_manager => _vs_manager.Value;
VolSurfaceDisplayGridColumn _assert_model() =>
column?.model ?? throw new InvalidOperationException();
@@ -1,32 +1,34 @@
#nullable enable
namespace mbts.spark.VolSurface {
using bts;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using Spark.Core;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Media;
public struct VolSurfaceCellState {
public bool is_dirty => committed_text is not null;
public FormattedText? text;
public FormattedText? committed_text;
}
class VolSurfaceRowVM : DisposableViewModelBase, ISelectable, IFormatCellsToClipboard, DIYGrid.IRowVM {
readonly VolSurfaceGridVM _parent;
readonly VolSurfaceDisplayGridExpiration _exp;
readonly Dictionary<string, CellData> _cells = new();
readonly UserTuple _user;
readonly DeferrableAction _request_calculate;
VolSurfaceRowContextMenuVM? _context_menu;
bool _is_selected;
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp) {
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp, UserTuple user) {
_parent = parent;
_exp = exp;
_user = user;
_request_calculate = new(() => request_calculate?.Invoke());
_dispose = Disposable.Create(() => {
_cells.Values.DisposeAll();
_cells.Clear();
@@ -36,6 +38,7 @@ namespace mbts.spark.VolSurface {
public event Action<bool>? is_selected_changed;
public event Action? invalidated;
public event Action? request_calculate;
public VolSurfaceDisplayGridExpiration expiration => _exp;
@@ -50,14 +53,16 @@ namespace mbts.spark.VolSurface {
}
public VolSurfaceRowContextMenuVM context_menu =>
_context_menu ??= new(_parent, this);
_context_menu ??= new(_parent, this, _user);
public VolGridCellVM get_cell(VolSurfaceDisplayGridColumn column) =>
public VolSurfaceCellVM get_cell(VolSurfaceDisplayGridColumn column) =>
_cells.GetOrAdd(column.unique_col_name, _ => _create_cell(column)).vm;
public void invalidate() {
foreach (var cell in _cells.Values) {
_sync_cell(cell.vm);
using (_request_calculate.supress()) {
foreach (var cell in _cells.Values) {
_sync_cell(cell.vm);
}
}
invalidated?.Invoke();
}
@@ -79,29 +84,15 @@ namespace mbts.spark.VolSurface {
CellData _create_cell(VolSurfaceDisplayGridColumn column) {
var cell = column.type switch {
VolSurfaceColumnType.ReadWriteTick => create_tick_cell(),
_ => new VolGridCellVM(column.unique_col_name),
_ => new VolSurfaceCellVM(_exp, column)
};
_sync_cell(cell);
var dispose = cell.observe_candidate().DistinctUntilChanged().OnDispatcher()
.Subscribe(value => {
var curr = _parent.current_grid.read(expiration, column);
if (Equals(value, curr)) {
return;
}
var grid = _parent.current_grid;
grid.write(expiration, column, value);
_parent.model.set_candidate_grid(grid);
});
return new(cell, dispose);
return new(cell, _connect_cell(cell));
VolGridTickCellVM create_tick_cell() {
var cell = new VolGridTickCellVM(column.unique_col_name);
VolSurfaceCellVM create_tick_cell() {
var cell = new VolSurfaceTickCellVM(_exp, column);
cell.request_tick += is_up => {
var col = _parent.get_column(column.unique_col_name);
if (col is null) {
throw new InvalidOperationException(
$"Failed to find column with id {column.unique_col_name}");
}
if (col.model?.tick_move is var tm) {
cell.candidate_value = tm?.Invoke(cell.candidate_value, is_up);
return;
@@ -112,23 +103,48 @@ namespace mbts.spark.VolSurface {
}
}
void _sync_cell(VolGridCellVM cell) {
void _sync_cell(VolSurfaceCellVM cell) {
if (cell is null) {
return;
}
cell.committed_value = _parent.committed_grid.read(_exp, cell.column_id);
cell.candidate_value = _parent.current_grid.read(_exp, cell.column_id);
using var _ = _request_calculate.supress();
cell.reset(
committed_value: _parent.committed_grid.read(_exp, cell.column_id),
candidate_value: _parent.current_grid.read(_exp, cell.column_id));
}
IDisposable? _connect_cell(VolSurfaceCellVM cell) {
return cell.column.type switch {
VolSurfaceColumnType.ReadWriteTick => connect_input(),
_ => null
};
IDisposable connect_input() {
cell.candidate_value_changed += on_candidate_value_changed;
return Disposable.Create(() =>
cell.candidate_value_changed -= on_candidate_value_changed);
void on_candidate_value_changed(object? oldval, object? newval) {
var curr = _parent.current_grid.read(expiration, cell.column);
if (cell.column.comparer.Equals(newval, curr)) {
return;
}
var grid = _parent.current_grid;
grid.write(expiration, cell.column, newval);
_request_calculate.Invoke();
}
}
}
class CellData : IDisposable {
IDisposable _dispose;
IDisposable? _dispose;
public CellData(VolGridCellVM vm, IDisposable dispose) {
public CellData(VolSurfaceCellVM vm, IDisposable? dispose) {
this.vm = vm;
_dispose = Disposable.Create(vm, dispose);
}
public VolGridCellVM vm { get; }
public VolSurfaceCellVM vm { get; }
public void Dispose() =>
Disposable.dispose(ref _dispose);
@@ -0,0 +1,27 @@
#nullable enable
namespace mbts.spark {
using mbts.ui;
using mbts.ui.controls;
using System;
using System.Windows.Input;
class VolSurfaceTickCellVM : VolSurfaceCellVM {
ICommand? _tick_up_cmd;
ICommand? _tick_down_cmd;
public VolSurfaceTickCellVM(
VolSurfaceDisplayGridExpiration expiration, VolSurfaceDisplayGridColumn column)
: base(expiration, column) { }
public event Action<bool>? request_tick;
public ICommand tick_up_cmd =>
_tick_up_cmd ??= new DelegateCommand(() =>
request_tick?.Invoke(true));
public ICommand tick_down_cmd =>
_tick_down_cmd ??= new DelegateCommand(() =>
request_tick?.Invoke(false));
}
}
@@ -0,0 +1,89 @@
#nullable enable
namespace mbts.spark {
using bts.api.domain;
using mbts.analytics;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using VolCurveFitRegion = analytics.VolSurfaceEnums.VolCurveFitRegion;
public class VolSurfaceAutoFitSettingsVM : DisposableViewModelBase {
readonly Property<VolCurveFitRegion> _fit_region;
readonly Property<uint?> _period_secs;
readonly Property<double?> _driver_points_moved;
readonly Property<double?> _atm_points_moved;
readonly Property<bool> _is_auto_fit_enabled;
public VolSurfaceAutoFitSettingsVM() {
_fit_region = backing(nameof(fit_region), VolCurveFitRegion.None);
_is_auto_fit_enabled = backing(
nameof(is_auto_fit_enabled),
_fit_region.observe_current(true),
r => r != VolCurveFitRegion.None);
_period_secs = backing<uint?>(nameof(period_secs), 10);
_driver_points_moved = backing<double?>(nameof(driver_points_moved));
_atm_points_moved = backing<double?>(nameof(atm_points_moved));
_fit_region.Changed += (o, n) => RaisePropertyChanged(nameof(is_auto_fit_enabled));
}
public IEnumerable<VolCurveFitRegion> fit_regions =>
EnumHelper.get_values<VolCurveFitRegion>();
public VolCurveFitRegion fit_region {
get => _fit_region.Value;
set => _fit_region.set(value);
}
public bool is_auto_fit_enabled => _is_auto_fit_enabled.Value;
public uint? period_secs {
get => _period_secs.Value;
set => _period_secs.set(value);
}
public double? driver_points_moved {
get => _driver_points_moved.Value;
set => _driver_points_moved.set(value);
}
public TickDisplaySettingsImmutable? driver_points_display =>
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
public double? atm_points_moved {
get => _atm_points_moved.Value;
set => _atm_points_moved.set(value);
}
public TickDisplaySettingsImmutable? atm_points_display =>
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
public void load_from(VolSurfaceAutoFitSettings value) {
fit_region = value?.auto_fit_region ?? VolCurveFitRegion.None;
period_secs = value?.time_based_settings?.period_seconds;
driver_points_moved = value?.driver_move_based_settings?.points_moved;
atm_points_moved = value?.atm_implied_vol_based_settings?.points_moved;
}
public void assign_to(VolSurfaceAutoFitSettings value) {
if (value is null) {
throw new ArgumentNullException(nameof(value));
}
value.auto_fit_region = fit_region;
value.time_based_settings = new() {
period_seconds = period_secs
};
value.driver_move_based_settings = new() {
points_moved = driver_points_moved
};
value.atm_implied_vol_based_settings = new() {
points_moved = atm_points_moved
};
}
}
}
@@ -96,7 +96,7 @@ namespace mbts.spark {
get => _core_settings.font_weight_override;
set {
using var _ = detect_property_change();
_core_settings.with(font_weight_override: value.HasValue ? new NullableFontWeightProto(value) : null);
_core_settings.with(font_weight_override: value.HasValue ? new(value) : null);
}
}
@@ -1,72 +0,0 @@
using mbts.ui;
using mbts.ui.controls.graph;
using System;
using System.Windows.Input;
using System.Windows.Media;
namespace mbts.spark
{
class VolSurfaceGraphSettingsEditorVM
{
public VolSurfaceGraphSettingsEditorVM(string vol_surface_name, VolSurfaceGraphSettings settings, Action<VolSurfaceGraphSettings> commit) {
this.vol_surface_name = vol_surface_name;
_settings = settings;
this.commit = new DelegateCommand(() => commit(_settings));
}
private readonly VolSurfaceGraphSettings _settings;
public string vol_surface_name { get; private set; }
public VolSurfaceScatterSeriesSettingsVM implied_puts { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.implied_puts); } }
public VolSurfaceScatterSeriesSettingsVM implied_atm { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.implied_atm); } }
public VolSurfaceScatterSeriesSettingsVM implied_calls { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.implied_calls); } }
public VolSurfaceScatterSeriesSettingsVM put_control_points { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.put_control_points); } }
public VolSurfaceScatterSeriesSettingsVM atm { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.atm); } }
public VolSurfaceScatterSeriesSettingsVM call_control_points { get { return new VolSurfaceScatterSeriesSettingsVM(_settings.call_control_points); } }
public VolSurfaceLineSeriesSettingsVM put_wing { get { return new VolSurfaceLineSeriesSettingsVM(_settings.put_wing); } }
public VolSurfaceLineSeriesSettingsVM inner_curve { get { return new VolSurfaceLineSeriesSettingsVM(_settings.inner_curve); } }
public VolSurfaceLineSeriesSettingsVM call_wing { get { return new VolSurfaceLineSeriesSettingsVM(_settings.call_wing); } }
public VolSurfaceLineSeriesSettingsVM vol_path { get { return new VolSurfaceLineSeriesSettingsVM(_settings.vol_path); } }
public ICommand commit { get; private set; }
}
class VolSurfaceScatterSeriesSettingsVM
{
public VolSurfaceScatterSeriesSettingsVM(ScatterSeriesSettings settings) {
_settings = settings;
}
private readonly ScatterSeriesSettings _settings;
public MarkerShape shape { get { return _settings.shape; } set { _settings.shape = value; } }
public double size { get { return _settings.size; } set { _settings.size = value; } }
public Color fill {
get => _settings.fill;
set => _settings.fill = value;
}
public Color stroke {
get => _settings.stroke;
set => _settings.stroke = value;
}
public Array shapes { get { return Enum.GetValues(typeof(MarkerShape)); } }
}
class VolSurfaceLineSeriesSettingsVM
{
public VolSurfaceLineSeriesSettingsVM(LineSeriesSettings settings) {
_settings = settings;
}
private readonly LineSeriesSettings _settings;
public Color color { get { return (Color)_settings.color; } set { _settings.color = (BTSColor)value; } }
public double thickness { get { return _settings.stroke_thickness; } set { _settings.stroke_thickness = value; } }
}
}
@@ -9,6 +9,7 @@ namespace mbts.spark {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core.Disposables;
using Spark.Core.Protobuf;
using System;
@@ -39,6 +40,7 @@ namespace mbts.spark {
vol_model_cubic_spline_vm.load_from(settings.spline_model_settings);
vol_model_dual_quadratic_vm.load_from(settings.dualquadratic_model_settings);
vol_path_parabolic_vm.load_from(settings.core_settings.volpath_settings.parabolic_vol_path_settings);
auto_fit_vm.load_from(settings.core_settings.market_fit_settings.vs_auto_fit_settings);
init_rotation_vms(call_rotations, _core_settings.call_rotation.strike_vol_movement_pairs);
init_rotation_vms(put_rotations, _core_settings.put_rotation.strike_vol_movement_pairs);
@@ -104,6 +106,7 @@ namespace mbts.spark {
}).ToList();
_core_settings.strangle.strike_vol_movement_pairs = strangles.SelectMany(r => new[] { Tuple.Create(r.target_put_strike, r.put_move), Tuple.Create(r.target_call_strike, r.call_move) }).ToList();
_core_settings.atm_shift.atm_strike_vol_movement = atm_shifts.Count == 1 ? atm_shifts[0].move : 0;
auto_fit_vm.assign_to(_core_settings.market_fit_settings.vs_auto_fit_settings);
commit(_settings);
}
@@ -232,23 +235,7 @@ namespace mbts.spark {
}
}
public bool enable_auto_fit {
get => _core_settings.market_fit_settings.enable_auto_fit;
set {
using var _ = detect_property_change();
_core_settings.market_fit_settings.enable_auto_fit = value;
}
}
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region {
get => _core_settings.market_fit_settings.auto_fit_region;
set { _core_settings.market_fit_settings.auto_fit_region = value; }
}
public double auto_fit_time_ms {
get => _core_settings.market_fit_settings.auto_fit_time_ms;
set { _core_settings.market_fit_settings.auto_fit_time_ms = value; }
}
public VolSurfaceAutoFitSettingsVM auto_fit_vm { get; } = new();
public bool auto_decay_vol_time {
get => _core_settings.time_settings.time_mode == VolSurfaceEnums.VolSurfaceTimeMode.AutoDecay;
@@ -539,7 +526,7 @@ namespace mbts.spark {
public ObservableCollection<VSSymbol> symbols { get; private set; }
public void Dispose() =>
Disposable.dispose(symbols);
symbols.DisposeAll(true);
}
public class VSSymbolStructureVM : DisposableViewModelBase {
@@ -550,11 +537,11 @@ namespace mbts.spark {
_selected_driver_grid = backing<VSSymbolGridVM>(nameof(selected_driver_grid));
settings.core_settings.master_slave_relationships ??= new List<VolSurfaceSymbolRelationship>();
settings.core_settings.implied_vol_marketdata_relationships ??= new List<VolSurfaceSymbolRelationship>();
_dispose = new DisposableCombo(
_dispose = Disposable.Create(
driver_grids.Subscribe((s, e) =>
selected_driver_grid ??= driver_grids.FirstOrDefault()),
bus.subscribe(SparkTags.ExpirationParametersInputTag).OnDispatcher().Subscribe(on_exp_params_input)) +
new Action(() => Disposable.dispose(driver_grids));
bus.subscribe(SparkTags.ExpirationParametersInputTag).OnDispatcher().Subscribe(on_exp_params_input),
Disposable.Create(() => driver_grids.DisposeAll(true)));
void on_exp_params_input(AnalyticsExpirationParametersMap data) {
var driver_map = data.get_option_driver_map();
+13 -2
View File
@@ -1,5 +1,6 @@
#nullable enable
namespace mbts.spark {
using bts;
using bts.api;
using bts.collections;
@@ -10,6 +11,7 @@ namespace mbts.spark {
using mbts.ui.controls;
using mbts.ui.shell;
using Spark.Core.Disposables;
using Spark.ViewModel.Commands;
using System;
@@ -26,6 +28,7 @@ namespace mbts.spark {
readonly IBus _bus;
readonly string _request_id = Guid.NewGuid().ToString();
readonly VolSurfaceDisplayModel _model;
readonly UserTuple _user;
readonly IRaiseCanExecuteChangedCommand _revert_cmd;
readonly IRaiseCanExecuteChangedCommand _commit_cmd;
readonly IDialogCommand _main_settings_cmd;
@@ -37,10 +40,16 @@ namespace mbts.spark {
bool _ignore_implied_vol_settings;
public VolSurfaceVM(
VolSurfaceDisplayModel model, int id, IBus bus, VolSurfaceWindowSettings startup_data) {
VolSurfaceDisplayModel model,
int id,
IBus bus,
VolSurfaceWindowSettings startup_data,
UserTuple user
) {
_model = model;
_vs_id = id;
_bus = bus;
_user = user;
macro_actions = new VolSurfaceMacroActionsVM(_model);
globals = new VolSurfaceGlobalParameterVM(_model);
@@ -48,7 +57,7 @@ namespace mbts.spark {
_grid_settings = startup_data.macro_grid_settings ?? new VolSurfaceMacroGridSettings();
var hidden_exps_set = startup_data.hidden_exps.empty_if_null().ToHashSet();
grid = new VolSurfaceGridVM(
this, _model, _grid_settings.hidden_cols(), hidden_exps_set);
this, _model, _grid_settings.hidden_cols(), hidden_exps_set, _user);
grid.on_selection_changed += _on_grid_selection_changed;
_model.publish_grid_settings(_grid_settings);
@@ -60,6 +69,7 @@ namespace mbts.spark {
vol_terms_vm = new VolTermsGraphVM(_model, demux);
// command
populate_empty_exps_cmd = new Command(() => { _model.populate_empty_expirations(); });
_revert_cmd = new Command(o => _model.revert(), o => _model.is_dirty);
_commit_cmd = new Command(o => _model.commit(), o => _model.is_dirty);
load_previous_cmd = new Command(
@@ -185,6 +195,7 @@ namespace mbts.spark {
public ICommand revert_cmd => _revert_cmd;
public ICommand commit_cmd => _commit_cmd;
public ICommand populate_empty_exps_cmd { get; }
public ICommand main_settings_cmd => _main_settings_cmd;
public ICommand advanced_settings_cmd => _advanced_settings_cmd;
public ICommand grid_settings_cmd => _grid_settings_cmd;
@@ -8,6 +8,7 @@
using mbts.ui.shell;
using Spark.Core;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
+19 -6
View File
@@ -5,6 +5,7 @@
using mbts.analytics;
using mbts.ui.controls;
using Spark.Core.Disposables;
using Spark.Core.Protobuf;
using System;
@@ -126,6 +127,20 @@
_log_bad_transition(_state, "set candidate grid");
}
public void populate_empty_expirations() {
bts.Log.Ui.info(String.Format("VolSurfaceDisplayModel.populate_expirations called with state: {0}",
_state.ToString()));
if (_state == DisplayModelState.Idle || _state == DisplayModelState.ResponseReady) {
DisplayModelData curr_data = _get_current_data();
VolSurfaceInput vs_inp =
VolSurfaceDataFunctions.populate_empty_expirations(curr_data.vs_output.volsurface_input);
_change_state(DisplayModelState.WaitingForCalcResponse);
_bus.post(SparkTags.VolSurfaceCalculationRequestTag(_vs_id),
new VolSurfaceCalculationRequest(vs_inp, _request_version_no++));
} else
_log_bad_transition(_state, "populate_expirations");
}
#endregion
#region Globals
@@ -145,6 +160,7 @@
return (_committed_data != null && _committed_data.vs_output != null) ? _committed_data.vs_output.volsurface_input.surface_parameters.vol_time : DateTime.MinValue;
}
public void set_candidate_vol_time(DateTime vol_time) {
bts.Log.Ui.info($"VolSurfaceDisplayModel.set_candidate_vol_time vs_id: {_vs_id} state: {_state.ToString()} vol_time: {vol_time}");
if (_state == DisplayModelState.Idle || _state == DisplayModelState.ResponseReady) {
DisplayModelData curr_data = _get_current_data();
if (curr_data != null) {
@@ -568,9 +584,7 @@
private void _change_state(DisplayModelState new_state) {
bool dirty_before = _translate_to_dirty(_state),
dirty_after = _translate_to_dirty(new_state);
// mbts.Logger.log(bts.log.Level.Info, bts.log.Subsystem.ui,
// String.Format("State change {0}->{1}", _state.ToString(), new_state.ToString()));
bts.Log.Ui.info(String.Format("State change {0}->{1}", _state.ToString(), new_state.ToString()));
bts.Log.Ui.info($"VolSurfaceDisplayModel._change_state vs_id: {_vs_id} {_state.ToString()}->{new_state.ToString()}");
_state = new_state;
if (dirty_before != dirty_after && on_dirty_change != null) {
_bus.post(SparkTags.VolSurfaceDirtyStateTag(_vs_id), dirty_after);
@@ -613,9 +627,8 @@
}
}
private static void _log_bad_transition(DisplayModelState state, string action) {
//mbts.Logger.log(bts.log.Level.Error, bts.log.Subsystem.ui, String.Format("{0}:{1}", state, action));
bts.Log.Ui.error(String.Format("{0}:{1}", state, action));
private void _log_bad_transition(DisplayModelState state, string action) {
bts.Log.Ui.error($"VoLSurfaceDisplayModel bad_transaction vs_id: {_vs_id} {state}:{action}");
}
private static bool _translate_to_dirty(DisplayModelState state) {
@@ -172,6 +172,18 @@ namespace mbts.spark {
return new(sec.Exchange, sec.ContractCase, sec.Symbol, exp_date, sec.Id);
}
public static SecurityTuple security_tuple(this Security sec) {
return security_tuple(new SecurityImmutable(sec));
}
public static SecurityTuple security_tuple(this SecurityImmutable sec) {
bts.api.SecurityTuple rv = new SecurityTuple();
rv.Symbol = sec.Symbol;
rv.Type = sec.security_type();
rv.Venue = sec.Exchange;
return rv;
}
public static ExpirationDriver to_exp_driver(this SecurityExpiration sec_exp) =>
new() {
Security = new() {
+12 -7
View File
@@ -5,6 +5,8 @@ using bts.api.core;
using bts.collections;
using bts.utils;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Composition;
@@ -41,7 +43,7 @@ namespace mbts.spark {
on_users = Observable.Return(DictionaryHelper.wrap_value(user.id, user.name));
all_user_tuples = user.permissions == UserTuple.Permissions.Admin ?
service.subscribei_admin_data()
.Select(u => u.all.TryFirst())
.Select(u => u.all.FirstOrNull())
.Where(ad => ad.HasValue)
.Select(ad => ad.Value.Users
.Where(u => user.can_access_user_data(u.Id))
@@ -50,11 +52,14 @@ namespace mbts.spark {
.Table(UserTuple.key_fn)
.Cache() :
Observable.Return(TableUpdate<UserTuple, uint>.mk_empty(UserTuple.key_fn).with_added(new[] { user }));
_dispose = on_accounts.Subscribe(map => {
_account_map = map;
var account_tuple_map = MappedDictionary.create(map, (id, name) => new AccountTuple(id, name));
bus.post(SparkTags.AccountsTag, account_tuple_map);
});
_dispose = Disposable.Create(
all_user_tuples.Subscribe(t => bus.post(SparkTags.Users, t.state)),
on_accounts.Subscribe(map => {
_account_map = map;
var account_tuple_map = MappedDictionary.create(map, (id, name) => new AccountTuple(id, name));
bus.post(SparkTags.AccountsTag, account_tuple_map);
}));
bus.post(SparkTags.CurrentUser, user);
}
public IObservable<IReadOnlyDictionary<uint, string>> on_users { get; }
@@ -76,7 +81,7 @@ namespace mbts.spark {
}));
public AccountTuple? get_default_account() =>
_account_map.Select(kv => new AccountTuple(kv.Key, kv.Value)).TryFirst();
_account_map.Select(kv => new AccountTuple(kv.Key, kv.Value)).FirstOrNull();
public void Dispose() =>
Disposable.dispose(ref _dispose);
@@ -10,7 +10,8 @@ namespace mbts.analytics {
double gamma = double.NaN,
double vega = double.NaN,
double theta = double.NaN,
double rho = double.NaN) {
double rho = double.NaN,
double normalized_theo = double.NaN) {
this.vol = vol;
this.driver = driver;
this.theo = theo;
@@ -19,6 +20,7 @@ namespace mbts.analytics {
this.vega = vega;
this.theta = theta;
this.rho = rho;
this.normalized_theo = normalized_theo;
}
public static GreeksTuple empty => new();
@@ -31,6 +33,7 @@ namespace mbts.analytics {
public double vega { get; }
public double theta { get; }
public double rho { get; }
public double normalized_theo { get; }
public bool Equals(GreeksTuple other) =>
vol.Equals(other.vol) &&
@@ -65,7 +68,8 @@ namespace mbts.analytics {
double? gamma = null,
double? vega = null,
double? theta = null,
double? rho = null) =>
double? rho = null,
double? normalized_theo = null) =>
new(
vol ?? greeks_tuple.vol,
driver ?? greeks_tuple.driver,
@@ -74,12 +78,7 @@ namespace mbts.analytics {
gamma ?? greeks_tuple.gamma,
vega ?? greeks_tuple.vega,
theta ?? greeks_tuple.theta,
rho ?? greeks_tuple.rho);
public static double normalized_theo(this in GreeksTuple greeks_tuple) =>
greeks_tuple.theo / greeks_tuple.driver;
public static double normalized_delta(this in GreeksTuple greeks_tuple) =>
greeks_tuple.delta / greeks_tuple.driver;
rho ?? greeks_tuple.rho,
normalized_theo ?? greeks_tuple.normalized_theo);
}
}
@@ -8,6 +8,7 @@ namespace mbts {
using ProtoBuf;
using Spark.Core;
using Spark.Core.Collections;
using Spark.Core.Protobuf;
@@ -100,17 +101,13 @@ namespace mbts {
public static IComparer<SecurityExpiration> get_comparer(Properties prop) {
return prop switch {
Properties.Type => wrap(s => s.security_type),
Properties.Symbol => wrap(s => s.symbol),
Properties.Expiration => wrap(s => s.expiration),
Properties.Venue => wrap(s => s.exchange),
Properties.Id => wrap(s => s.id).to_safe(),
Properties.Type => ComparerWrapper<SecurityExpiration>.wrap(s => s.security_type),
Properties.Symbol => ComparerWrapper<SecurityExpiration>.wrap(s => s.symbol),
Properties.Expiration => ComparerWrapper<SecurityExpiration>.wrap(s => s.expiration),
Properties.Venue => ComparerWrapper<SecurityExpiration>.wrap(s => s.exchange),
Properties.Id => ComparerWrapper<SecurityExpiration>.wrap(s => s.id),
_ => throw new NotImplementedException()
};
static IComparer<SecurityExpiration> wrap<T>(
Func<SecurityExpiration, T> selector, IComparer<T>? comparer = null) =>
(comparer ?? Comparer<T>.Default).wrap(selector);
}
public static IComparer<SecurityExpiration> get_comparer(params Properties[] hierarchy) =>
@@ -148,7 +145,7 @@ namespace mbts {
!(x == y);
public override string ToString() =>
$"{security_tuple} {expiration.ToShortDateString()}";
$"{security_tuple} {SecurityFormatter.format_expiration(expiration)}";
}
[ProtoContract]
@@ -189,6 +186,6 @@ namespace mbts {
exchange ?? sec_exp.exchange, security_type ?? sec_exp.security_type,
symbol ?? sec_exp.symbol,
expiration ?? sec_exp.expiration,
id.has_value ? id.value : sec_exp.id);
id.has_value ? id : sec_exp.id);
}
}
@@ -4,13 +4,11 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.analytics
{
namespace mbts.analytics {
/// These are the VolSurface settings that are common
/// to any Vol Model
[ProtoBuf.ProtoContract(Name = "VolSurfaceCoreSettings")]
public class VolSurfaceCoreSettings
{
public class VolSurfaceCoreSettings {
[ProtoBuf.ProtoMember(1)]
public string vol_surface_name;
[ProtoBuf.ProtoMember(2)]
@@ -87,5 +85,7 @@ namespace mbts.analytics
get => _time_settings ??= new();
set => _time_settings = value;
}
[ProtoBuf.ProtoMember(240)]
public string vs_manager { get; set; } = string.Empty;
}
}
@@ -75,6 +75,7 @@ namespace mbts.analytics {
if (core_settings.market_fit_settings == null) {
core_settings.market_fit_settings = new VolSurfaceMarketFitSettings();
}
core_settings.market_fit_settings.vs_auto_fit_settings ??= new();
if (core_settings.time_settings == null) {
core_settings.time_settings = new VolSurfaceTimeSettings();
}
@@ -141,6 +141,7 @@ namespace mbts.analytics
public enum VolCurveFitRegion
{
None,
ATM,
All
}
@@ -230,4 +231,4 @@ namespace mbts.analytics
USE_CALENDAR_FOR_BUSINESS
}
}
}
}
@@ -65,7 +65,7 @@ namespace mbts.analytics {
}
public override string ToString() {
return String.Format("{0} {1} {2}",
return String.Format("{0} {1} {2}",
security_type.ToString().ToUpper().First(), symbol, expiration.ToShortDateString());
}
@@ -74,6 +74,11 @@ namespace mbts.analytics {
int rv = expiration.CompareTo(other.expiration);
return rv != 0 ? rv : symbol.CompareTo(other.symbol);
}
public double diff_days(VolSurfaceExpiration exp_other) {
TimeSpan ts = expiration - exp_other.expiration;
return ts.TotalDays;
}
}
public static class VolSurfaceExpirationExtensions {
@@ -4,11 +4,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.analytics
{
namespace mbts.analytics {
[ProtoBuf.ProtoContract(Name = "VolSurfaceMarketFitSettings")]
public class VolSurfaceMarketFitSettings
{
public class VolSurfaceMarketFitSettings {
/// use one sided market or not
[ProtoBuf.ProtoMember(10)]
public bool ignore_one_sided_markets;
@@ -22,19 +20,44 @@ namespace mbts.analytics
public double graph_update_period_ms = 2500;
/// If this is set to true a regression curve is drawn
/// and the vol model adheres to that
[ProtoBuf.ProtoMember(50)]
[ProtoBuf.ProtoMember(50)]
public bool use_weighted_regression_fit = false;
[ProtoBuf.ProtoMember(55)]
public uint regression_order = 4;
/// The system auto fits and saves vols if this is
/// set to true
[ProtoBuf.ProtoMember(60)]
public bool enable_auto_fit = false;
/// If enable_auto_fit = true, this governs
/// how often the system auto-fits
[ProtoBuf.ProtoMember(70)]
public double auto_fit_time_ms = 30000;
[ProtoBuf.ProtoMember(80)]
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region = VolSurfaceEnums.VolCurveFitRegion.ATM;
[ProtoBuf.ProtoMember(90)]
public VolSurfaceAutoFitSettings vs_auto_fit_settings { get; set; }
}
[ProtoBuf.ProtoContract(Name = "VolSurfaceAutoFitSettings")]
public class VolSurfaceAutoFitSettings {
// each of these settings can be enabled independently. if they are enabled
// then any of them being true will result in an autmatic fit
[ProtoBuf.ProtoContract(Name = "TimeBasedSettings")]
public class TimeBasedSettings {
[ProtoBuf.ProtoMember(10)]
public uint? period_seconds { get; set; }
}
[ProtoBuf.ProtoContract(Name = "DriverMoveBasedSettings")]
public class DriverMoveBasedSettings {
[ProtoBuf.ProtoMember(10)]
public double? points_moved { get; set; }
}
[ProtoBuf.ProtoContract(Name = "ATMImpliedVolBasedSettings")]
public class ATMImpliedVolBasedSettings {
[ProtoBuf.ProtoMember(20)]
public double? points_moved { get; set; }
// we can do price terms moved as well
}
[ProtoBuf.ProtoMember(5)]
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region;
[ProtoBuf.ProtoMember(10)]
public TimeBasedSettings time_based_settings = null;
[ProtoBuf.ProtoMember(20)]
public DriverMoveBasedSettings driver_move_based_settings = null;
[ProtoBuf.ProtoMember(30)]
public ATMImpliedVolBasedSettings atm_implied_vol_based_settings = null;
}
}
@@ -943,7 +943,15 @@ namespace mbts.analytics {
}
}
#endregion
#endregion
#region inverse contracts
// Bitcoin contracts, for example, where the strike is in USD, and the theo
// needs to be in terms of the underlying contract
public static double normalize_theo(double theo, double fwd) {
return fwd == 0 ? Double.NaN : (theo / fwd);
}
#endregion
#region Other Pricing Utilities
@@ -19,9 +19,8 @@ namespace mbts.analytics
/// with default inputs
public static VolSurfaceInput sync_input_expirations_to_master_list(VolSurfaceInput vs_input, IEnumerable<VolSurfaceExpiration> system_exp_list)
{
var vs_exp_map = vs_input.surface_parameters.curve_parameters_map
.empty_if_null().Select(t => t.Item1).ToHashSet();
var sys_exp_map = system_exp_list.ToHashSet();
HashSet<VolSurfaceExpiration> vs_exp_map = new HashSet<VolSurfaceExpiration>(vs_input.surface_parameters.curve_parameters_map.Select(t => t.Item1)),
sys_exp_map = new HashSet<VolSurfaceExpiration>(system_exp_list);
sys_exp_map.difference(
vs_exp_map,
out HashSet<VolSurfaceExpiration> deleted_exps,
@@ -681,5 +680,60 @@ namespace mbts.analytics
}
#endregion
#region Data copying
public static bool exp_has_input(VolSurfaceSettings vs_settings, VolCurveParameters exp_params) {
if (exp_params == null || vs_settings == null)
return false;
double atm_vol = get_atm_vol(vs_settings, exp_params);
double ref_fwd = exp_params.core_parameters.ref_forward;
return !Double.IsNaN(atm_vol) && !Double.IsNaN(ref_fwd);
}
public static VolSurfaceExpiration find_nearest_populated_exp(
VolSurfaceInput vs_input, VolSurfaceExpiration vs_exp) {
VolCurveParameters exp_params = get_curve_parameters(vs_input, vs_exp);
if (exp_has_input(vs_input.surface_settings, exp_params))
return vs_exp;
VolSurfaceExpiration rv = null;
double rv_diff_days = Double.MaxValue;
foreach (var kv in vs_input.surface_parameters.curve_parameters_map) {
var this_exp = kv.Item1;
var this_exp_params = kv.Item2;
if (exp_has_input(vs_input.surface_settings, this_exp_params)) {
double this_exp_diff_days = Math.Abs(vs_exp.diff_days(this_exp));
if (this_exp_diff_days < rv_diff_days) {
rv = this_exp;
rv_diff_days = this_exp_diff_days;
}
}
}
return rv;
}
// Copies the empty exps from closest neighbor
public static VolSurfaceInput populate_empty_expirations(VolSurfaceInput vs_input) {
VolSurfaceInput rv = Spark.Core.Protobuf.ProtobufSerializationHelper.clone(vs_input);
rv.surface_parameters.curve_parameters_map.Clear();
foreach (var kv in vs_input.surface_parameters.curve_parameters_map) {
var vs_exp = kv.Item1;
var exp_params = kv.Item2;
if (!exp_has_input(vs_input.surface_settings, exp_params)) {
VolSurfaceExpiration np_exp = find_nearest_populated_exp(vs_input, vs_exp);
if (np_exp != null) {
var np_exp_params = get_curve_parameters(vs_input, np_exp);
if (np_exp_params != null) {
exp_params = Spark.Core.Protobuf.ProtobufSerializationHelper.clone(np_exp_params);
}
}
}
rv.surface_parameters.curve_parameters_map.Add(
new Tuple<VolSurfaceExpiration, VolCurveParameters>(vs_exp, exp_params));
}
return rv;
}
#endregion
}
}
@@ -475,6 +475,7 @@ namespace mbts.analytics {
public static ResultMessage save_volsurface(
VolSurfaceInput vs_input, AnalyticsService service, string user = "") {
vs_input.saver_login = user;
vs_input.surface_settings.core_settings.vs_manager = user;
// convert to VolSurfaceEntry
var to_save = new VolSurfaceEntry();
var serialized_vs = ProtobufSerializationHelper.serialize(vs_input);
@@ -595,7 +596,7 @@ namespace mbts.analytics {
_greek_getter(g)(t, is_call);
public static IDictionary<double, IEnumerable<Theos>> hash_strikes(IEnumerable<Theos> theos) =>
theos.Map(t => t.strike);
theos.Map(t => t.strike);
static Func<Theos, bool, double> _greek_getter(VolSurfaceGreeks g) =>
g switch {
@@ -674,7 +675,7 @@ namespace mbts.analytics {
var vol_time = VolSurfaceCoreFunctions.compute_vol_time(
vs_input, vs_standard_time.server_time_utc);
var exp_inp_data = vs_nonvol_input.get_option_exp_input();
foreach (var (exp, curve_params) in vs_input.surface_parameters.curve_parameters_map.empty_if_null()) {
foreach (var (exp, curve_params) in vs_input.surface_parameters.curve_parameters_map) {
var time_shift = DoubleHelper.CoerceNaN(curve_params.core_parameters.time_shift);
_compute_option_bizt(
vs_input, exp, vol_time, time_shift, out var biz_yrs, out var biz_days_per_yr);
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -88,8 +88,8 @@ namespace mbts.analytics {
public void set_driver_price(
IDictionary<VolSurfaceExpiration, double> driver_price_map) {
Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_driver_price " +
$"vs_id: {_vs_id} exp_count: {driver_price_map.Count}");
// Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_driver_price " +
// $"vs_id: {_vs_id} exp_count: {driver_price_map.Count}");
_driver_price_map = new Dictionary<VolSurfaceExpiration, double>(driver_price_map);
_compute_and_fire(InputType.DriverPrice);
}
+15
View File
@@ -0,0 +1,15 @@
namespace Spark {
using System;
using System.Runtime.InteropServices;
static class ConsoleInterop {
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr h_wnd, int n_cmd_show);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int FreeConsole();
}
}
+2 -1
View File
@@ -1,10 +1,11 @@
using bts.api;
using mbts;
using mbts.config;
using mbts.spark;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Composition;
using System.Diagnostics;
+7 -5
View File
@@ -8,22 +8,22 @@ using mbts.ui;
using mbts.ui.spark;
using mbts.ui.utilities;
using Spark.Core.Disposables;
using System;
using System.Composition;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows;
using bts.api.analytics;
using BCG;
using Disposable = mbts.Disposable;
namespace bts {
[Export(typeof(IShellDaemon))]
class AnalyticsDaemon : IShellDaemon, IDisposable {
private readonly IBus _bus;
private readonly AnalyticsService _analytics_service;
private readonly bts.api.orderman.OrderService _order_service;
private readonly VolSurfaceBusManager _vs_manager;
private readonly SpreadManager _spread_manager;
private readonly SpreadCalculationSynchronizer _spread_calc;
@@ -31,11 +31,13 @@ namespace bts {
IDisposable _dispose;
[ImportingConstructor]
public AnalyticsDaemon(IBus bus, AnalyticsService analytics_service) {
public AnalyticsDaemon(IBus bus, AnalyticsService analytics_service,
bts.api.orderman.OrderService order_service) {
bts.Log.Gen.info("AnalyticsDaemon started");
_bus = bus;
_analytics_service = analytics_service;
_vs_manager = new VolSurfaceBusManager(_bus, _analytics_service);
_order_service = order_service;
_vs_manager = new VolSurfaceBusManager(_bus, _analytics_service, _order_service);
_spread_manager = new SpreadManager(_vs_manager.bb(), "xxx");
_spread_calc = new SpreadCalculationSynchronizer(_vs_manager.bb());
}
@@ -1,10 +1,11 @@
using bts.api;
using bts.spark;
using mbts;
using mbts.spark;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -7,6 +7,8 @@ using mbts;
using mbts.spark;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Composition;
+1
View File
@@ -11,6 +11,7 @@ using System.Reactive.Linq;
using SecurityKey = System.UInt32;
using MDHandle = mbts.co2.FakeMDConsumerHandle;
using Spark.Core.Disposables;
namespace bts {
//[Export(typeof(IShellDaemon))]
@@ -7,6 +7,8 @@ namespace bts.daemons {
using mbts.spark;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -17,7 +19,7 @@ namespace bts.daemons {
[Export(typeof(IShellDaemon))]
public sealed class FakeSymbolDriverDaemon : IShellDaemon, IDisposable {
IDisposable _dispose;
IDisposable? _dispose;
public FakeSymbolDriverDaemon(IBus bus) {
int tid_counter = 1;
@@ -68,6 +70,7 @@ namespace bts.daemons {
double price,
int size = 1,
string account = "ACC A",
uint account_id = 1,
double? strike = null,
PutOrCall? cp = null,
double? theo = null
@@ -81,9 +84,11 @@ namespace bts.daemons {
price,
theo ?? price + 10 * new Random().NextDouble(),
account,
account_id,
true,
DateTime.Now,
"FakeTrader");
"FakeTrader",
1);
return t1;
}
}
+10 -10
View File
@@ -8,15 +8,14 @@ using mbts.ui;
using mbts.ui.spark;
using mbts.ui.utilities;
using Spark.Core.Disposables;
using System;
using System.Composition;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows;
using Disposable = mbts.Disposable;
namespace bts {
[Export(typeof(IShellDaemon))]
class GlobalAlertsDaemon : IShellDaemon, IDisposable {
@@ -39,13 +38,14 @@ namespace bts {
_notification_service = notification_service;
_launcher = new ThreadedWindowLauncher(_window_name);
_dispose = Disposable.Create(
bus.subscribe(SparkTags.ShowGlobalAlertsTag)
.Subscribe(_ => _launcher.show(_view_factory)),
_hub.subscribe_default_settings()
.SubscribeEach(s => new CompositeDisposable(
s.alerts.trade_alerts.Where(t => t.is_enabled).Select(
a => _notification_service.link_to_trades(
a.predicate.security_criteria, a.predicate.create(), a.sound)))));
bus.subscribe(SparkTags.ShowGlobalAlertsTag).Subscribe(_ =>
_launcher.show(_view_factory)),
_hub.subscribe_default_settings().SubscribeEach(s =>
Disposable.Create(s.alerts.trade_alerts
.Where(t => t.is_enabled)
.Select(a => _notification_service.link_to_trades(
a.predicate.security_criteria, a.predicate.create(), a.sound))
.ToList())));
}
Window _view_factory() {
+6 -2
View File
@@ -14,6 +14,7 @@ namespace bts {
using mbts.ui;
using Spark.Core.Data;
using Spark.Core.Disposables;
using System;
using System.Collections.Concurrent;
@@ -134,7 +135,10 @@ namespace bts {
var meta = _security_metadata.get(smd.security);
if (meta.HasValue) {
exch_secs[smd.id] = new(
smd.security, smd.md, meta.Value.PriceDisplay, meta.Value.QuantityDisplay);
smd.security,
smd.md,
meta.Value.HasPriceDisplay ? meta.Value.PriceDisplay : null,
meta.Value.HasQuantityDisplay ? meta.Value.QuantityDisplay : null);
}
}
_bus.post(SparkTags.ServerSecuritiesTag, exch_secs);
@@ -142,6 +146,6 @@ namespace bts {
}
public void Dispose() =>
Disposable.dispose(_resources);
_resources.DisposeAll(true);
}
}
+19 -16
View File
@@ -2,6 +2,7 @@ using BCG;
using bts.api;
using bts.api.synthetic;
using bts.collections;
using bts.md;
using bts.utils;
@@ -10,6 +11,8 @@ using mbts.co2;
using mbts.spark;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -27,16 +30,22 @@ namespace bts {
IDisposable _dispose;
Dictionary<uint, SecurityMD> _server_sec_map = new Dictionary<uint, SecurityMD>();
Dictionary<uint, SecurityMD> _fake_sec_map = new Dictionary<uint, SecurityMD>();
private readonly IList<IDisposable> _subs = new List<IDisposable>();
[ImportingConstructor]
public MergedMDDaemon(IBus bus) {
//TempTester.test_analytics();
bts.Log.Gen.info("MergedMDDaemon started");
_bus = bus;
_bus.subscribe(SparkTags.ServerSecuritiesTag).OnDispatcher().Subscribe(
d => _on_server_sec_map(d));
_bus.subscribe(SparkTags.FakeSecuritiesTag).OnDispatcher().Subscribe(
d => _on_fake_sec_map(d));
_subs.Add(_bus.subscribe(SparkTags.ServerSecuritiesTag).OnDispatcher().Subscribe(
d => _on_server_sec_map(d)));
_subs.Add(_bus.subscribe(SparkTags.FakeSecuritiesTag).OnDispatcher().Subscribe(
d => _on_fake_sec_map(d)));
// This is done periodically to remove expired contracts
// (disabling it right now because we are not sure if deleting securities
// works with the various UI windows)
// _subs.Add(_bus.subscribe(SparkTags.SparkTimer).BufferLatest(TimeSpan.FromMinutes(1))
// .Subscribe(t => _merge_and_post()));
}
private void _log_options(IReadOnlyDictionary<uint, SecurityMD> sec_map) {
@@ -53,34 +62,28 @@ namespace bts {
}
private void _on_server_sec_map(IReadOnlyDictionary<uint, SecurityMD> sec_map) {
_log_options(sec_map);
bts.Log.Gen.info(String.Format("MergedMDDaemon._on_server_sec_map sz:{0}", sec_map.Count()));
//_log_options(sec_map);
bts.Log.Gen.info(String.Format("MergedMDDaemon._on_server_sec_map sz: {0}", sec_map.Count()));
_server_sec_map = new Dictionary<uint, SecurityMD>(sec_map);
_merge_and_post();
}
private void _on_fake_sec_map(IReadOnlyDictionary<uint, SecurityMD> sec_map) {
bts.Log.Gen.info(String.Format("MergedMDDaemon._on_fake_sec_map sz:{0}", sec_map.Count()));
bts.Log.Gen.info(String.Format("MergedMDDaemon._on_fake_sec_map sz: {0}", sec_map.Count()));
_fake_sec_map = new Dictionary<uint, SecurityMD>(sec_map);
_merge_and_post();
}
private void _merge_and_post() {
Func<SecurityMD, bool> is_active = (sec_md) => {
if (sec_md.security.HasFuture || sec_md.security.HasOption) {
var exp = sec_md.security.security_expiration();
if (exp.HasValue) {
return exp.Value.Date >= DateTime.Now.Date.AddDays(-1);
}
return true;
}
return true;
var sec = sec_md.security;
return SecurityExpirationHelper.is_unexpired(sec);
};
var to_pub = _server_sec_map.Where(kv => is_active(kv.Value)).ToDictionary(kv => kv.Key, kv => kv.Value);
foreach (var kv in _fake_sec_map) {
to_pub[kv.Key] = kv.Value;
}
bts.Log.Gen.info(String.Format("MergedMDDaemon._merge_and_post sz:{0}", to_pub.Count()));
bts.Log.Gen.info(String.Format("MergedMDDaemon._merge_and_post sz: {0}", to_pub.Count()));
_bus.post(SparkTags.SecuritiesTag, to_pub);
SystemContractData sc_data = new SystemContractData(to_pub);
_bus.post(SparkTags.SystemContractsTag, sc_data);
@@ -2,10 +2,11 @@
using bts.api.orderman;
using bts.spark;
using mbts;
using mbts.spark;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Composition;
@@ -10,6 +10,8 @@ using mbts.spark;
using mbts.ui;
using mbts.ui.spark;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+42
View File
@@ -0,0 +1,42 @@
using bts.api;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Composition;
using System.Reactive.Linq;
using System.Collections.Generic;
using mbts.spark;
using bts.api.orderman;
using bts.collections;
using System.Linq;
using Microsoft.Scripting.Utils;
namespace bts {
[Export(typeof(IShellDaemon))]
class RFQDaemon : IShellDaemon, IDisposable {
private readonly IBus _bus;
private readonly bts.api.orderman.OrderService _order_service;
private List<IDisposable> _subs = new List<IDisposable>();
[ImportingConstructor]
public RFQDaemon(IBus bus, bts.api.orderman.OrderService order_service) {
bts.Log.Gen.info("RFQDaemon started");
_bus = bus;
_order_service = order_service;
var d = bus.subscribe(SparkTags.RFQSend)
.Subscribe(_handle_rfq);
}
private void _handle_rfq(RFQRequestImmutable rfq) {
bts.Log.Gen.info($"RFQDaemon._handle_rfq");
_order_service.send_rfq(rfq);
}
public void Dispose() {
foreach (var d in _subs) {
d.Dispose();
}
}
}
}
@@ -2,6 +2,8 @@
using mbts.spark;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Composition;
+8 -4
View File
@@ -13,6 +13,7 @@ namespace bts.daemons {
using mbts.ui;
using Spark.Core.Collections;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
@@ -29,10 +30,11 @@ namespace bts.daemons {
readonly OrderService _order_service;
readonly ErrorReporter _error_reporter;
readonly IBus _bus;
readonly mbts.analytics.PNLReportCalculator _pnl_calc;
IReadOnlyDictionary<int, ReportImmutable> _report_parameters_map =
new Dictionary<int, ReportImmutable>();
IDisposable _dispose;
IDisposable? _dispose;
[ImportingConstructor]
public ReportDaemon(
@@ -48,6 +50,7 @@ namespace bts.daemons {
_error_reporter = error_reporter;
_order_service = order_service;
_report_calculators = new(mk_report_calculator);
_pnl_calc = new PNLReportCalculator(bus);
_dispose = Disposable.Create(
bus.subscribe(SparkTags.ReportBlackboardTags.Create).Subscribe(rp => _create_report(rp).ignore()),
@@ -59,7 +62,8 @@ namespace bts.daemons {
_bus.subscribe(SparkTags.SystemContractsTag).CombineLatest(
_order_service.subscribe_fills(), _position_service.subscribe_manual_fills())
.Subscribe(tup => _on_all_trade_data(tup.First, tup.Second, tup.Third)),
_report_calculators);
_report_calculators,
_pnl_calc);
IDisposable mk_report_calculator(int id) {
if (!_report_parameters_map.TryGetValue(id, out var r)) {
@@ -156,8 +160,8 @@ namespace bts.daemons {
var fills_tl = fills_filtered.Select(
f => new TradeLogRow(f, sc_data.sec_md_map[f.SecurityKey].security));
var m_fills_tl = man_fills_filtered.Select(
m => new TradeLogRow(m, sc_data.sec_md_map[m.SecurityKey].security));
var m_fills_tl = man_fills_filtered.Select(m => new TradeLogRow(
m, sc_data.sec_md_map[m.SecurityKey].security, Fill.Types.FillKind.Standard));
var merged_fills = fills_tl.Concat(m_fills_tl).ToList();
Log.Ui.info($"ReportDaemon posting {merged_fills.Count} trades");
_bus.post(SparkTags.TradeLogTag, merged_fills);
@@ -5,6 +5,8 @@ using mbts;
using mbts.spark;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Composition;
using System.Reactive.Linq;
@@ -1,10 +1,11 @@
using bts.api.core;
using bts.metrics;
using mbts;
using mbts.config;
using mbts.ui;
using Spark.Core.Disposables;
using System;
using System.Composition;
using System.Linq;
+20 -3
View File
@@ -52,6 +52,9 @@ namespace mbts.ui {
EditDocumentGeometryKey = nameof(EditDocumentGeometryKey),
VolCurveGeometryKey = nameof(VolCurveGeometryKey),
FinancialGrowthGeometryKey = nameof(FinancialGrowthGeometryKey),
MarketGeometryKey = nameof(MarketGeometryKey),
RefreshGeometryKey = nameof(RefreshGeometryKey),
FavoriteWindowGeometryKey = nameof(FavoriteWindowGeometryKey),
GearGeometryKey = nameof(GearGeometryKey),
HamburgerMenuGeometryKey = nameof(HamburgerMenuGeometryKey),
HornGeometryKey = nameof(HornGeometryKey),
@@ -60,6 +63,7 @@ namespace mbts.ui {
LetterDKey = nameof(LetterDKey),
MenuOrientationGeometryKey = nameof(MenuOrientationGeometryKey),
OrdersGeometryKey = nameof(OrdersGeometryKey),
PaperPlaneGeometryKey = nameof(PaperPlaneGeometryKey),
PauseGeometryKey = nameof(PauseGeometryKey),
PaymentGeometryKey = nameof(PaymentGeometryKey),
PencilGeometryKey = nameof(PencilGeometryKey),
@@ -77,7 +81,9 @@ namespace mbts.ui {
public static Geometry PlayGeometry => Geometry.Parse(get_geometric_path(PlayGeometryKey).FirstOrDefault() ?? string.Empty);
static readonly Pen _copy_boxes_pen = new Pen(Brushes.Black, HighDpiExtensions.RoundLayoutValue(5)).frozen();
static readonly Pen _copy_boxes_pen = new Pen(
BTSColors.UI.icon_foreground, 5.0.RoundLayoutValue())
.frozen();
static readonly string base_uri = "pack://application:,,,/Images/Icons/";
private static ResourceDictionary _resources = (ResourceDictionary)Application.LoadComponent(new Uri("/Images/Vector/Icons.xaml", UriKind.RelativeOrAbsolute));
@@ -116,8 +122,8 @@ namespace mbts.ui {
public static ImageSource DataCopy { get { return load_image("DataCopy.ico"); } }
public static ImageSource Refresh { get { return load_image("Refresh.png"); } }
public static ImageSource FavoriteWindow { get { return load_image("FavoriteWindow.png"); } }
// public static ImageSource Refresh { get { return load_image("Refresh.png"); } }
// public static ImageSource FavoriteWindow { get { return load_image("FavoriteWindow.png"); } }
public static ImageSource Browser => load_resource_and_freeze("Browser");
@@ -153,6 +159,9 @@ namespace mbts.ui {
public static ImageSource Book { get { return load_resource_and_freeze("Book"); } }
public static ImageSource Orders => create_icon(OrdersGeometryKey);
public static ImageSource Refresh => create_icon(RefreshGeometryKey);
public static ImageSource FavoriteWindow => create_icon(FavoriteWindowGeometryKey);
public static ImageSource AddOrders { get { return load_resource_and_freeze("AddOrders"); } }
public static ImageSource CancelOrders { get { return load_resource_and_freeze("CancelOrders"); } }
public static ImageSource CancelBuys { get { return load_resource_and_freeze("CancelBuys"); } }
@@ -203,6 +212,7 @@ namespace mbts.ui {
public static ImageSource CopyBoxes => create_icon(CopyBoxesGeometryKey, null, _copy_boxes_pen);
public static ImageSource CurrencyExchange => create_icon(CurrencyExchangeGeometryKey);
public static ImageSource DataEdit => create_icon(DataEditGeometryKey);
public static ImageSource DayFills => PNL;
public static ImageSource Defrag { get { return load_resource_and_freeze("Defrag"); } }
public static ImageSource DetachAlgos => create_icon_and_rotate(DetachAlgosKey, Brushes.Red);
public static ImageSource Disk => create_icon(DiskGeometryKey);
@@ -229,17 +239,24 @@ namespace mbts.ui {
public static ImageSource Import => load_resource_and_freeze("Import");
public static ImageSource Key => create_icon(KeyGeometryKey);
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
public static ImageSource manual_fills => create_icon(DataEditGeometryKey);
public static ImageSource Market => create_icon(MarketGeometryKey);
public static ImageSource MenuOrientation => create_icon(MenuOrientationGeometryKey);
public static ImageSource open_orders => Book;
public static ImageSource options_view => Sheets;
public static ImageSource paper_plane => create_icon(
PaperPlaneGeometryKey, null, new(BTSColors.UI.icon_foreground, 2));
public static ImageSource Pause => create_icon(PauseGeometryKey);
public static ImageSource Payment => create_icon(PaymentGeometryKey);
public static ImageSource Pencil => create_icon(PencilGeometryKey);
public static ImageSource Play => create_icon(PlayGeometryKey);
public static ImageSource PNL => Accounting;
public static ImageSource Prohibited => create_icon(ProhibitedGeometryKey);
public static ImageSource RecurringSchedule => create_icon(RecurringScheduleGeometryKey);
public static ImageSource RecurringScheduleGray => create_icon(RecurringScheduleGeometryKey, Brushes.Gray);
public static ImageSource RubberStamp => create_icon(RubberStampGeometryKey);
public static ImageSource RedoCircular => create_icon(UndoCircularGeometryKey, Brushes.Black, null, new ScaleTransform(-1, 1));
public static ImageSource rfq => paper_plane;
public static ImageSource Spawn { get { return load_resource_and_freeze("Spawn"); } }
public static ImageSource Speaker => create_icon(SpeakerGeometryKey);
public static ImageSource Star => create_icon(StarGeometryKey);
+30 -3
View File
@@ -325,6 +325,18 @@
M50.14375,33.389126C50.416863,33.394688,50.653496,33.587773,50.705998,33.864617L50.015995,45.114388C49.810295,46.291462,49.130394,45.097489,49.130394,45.097489L47.65659,42.640538C42.747776,49.635197 34.633152,54.221001 25.433924,54.221001 14.884193,54.221001 5.7437552,48.204125 1.2449617,39.419205 0.80087067,38.552021 0.21358696,37.705738 0.052086769,36.736958 -0.49735315,33.490925 3.4507786,32.901036 4.8049124,34.432404 5.3674143,35.067692 6.034036,36.16407 6.5079177,37.026051 10.106778,43.799416 17.2321,48.413819 25.433924,48.413819 32.656544,48.413819 39.026364,44.826695 42.903977,39.351506L40.229367,38.854115C40.229367,38.854115,38.677464,38.647118,39.742466,37.996033L50.024895,33.398526C50.064972,33.391365,50.104733,33.388333,50.14375,33.389126z M23.030291,9.0310004L27.889918,9.0310004 27.889918,13.028299C30.539786,13.205498,32.324932,13.958096,33.464161,14.528294L33.964176,14.775793 32.417435,20.038878 31.706417,19.68068C30.579987,19.11958 28.839241,18.244583 26.149073,18.244583 24.006916,18.244583 22.723183,19.122181 22.723183,20.590876 22.723183,21.894272 23.943314,22.804369 27.551308,24.187165 32.706444,26.124659 34.911,28.702853 34.911,32.817341 34.911,36.892929 32.090527,40.05172 27.673611,40.989117L27.673611,45.540001 22.767485,45.540001 22.767485,41.330414C20.252919,41.155815,17.695551,40.424118,16.101811,39.404721L15.683999,39.137619 17.221739,33.731438 17.969059,34.180634C19.088988,34.851235 21.402748,35.97873 24.340325,35.97873 26.734886,35.97873 28.276728,34.921635 28.276728,33.291336 28.276728,31.986641 27.547409,30.853846 23.986016,29.57255 20.083815,28.144253 16.095511,26.04406 16.095511,21.239174 16.095511,17.280985 18.737379,14.291395 23.030291,13.329098z M25.268765,0C35.819447,2.7347414E-07 44.961449,6.0155495 49.458798,14.804573 49.902695,15.669173 50.490094,16.516972 50.651287,17.48427 51.198281,20.734265 47.255726,21.318962 45.901341,19.789065 45.336346,19.153567 44.669552,18.051968 44.19566,17.19517 40.5957,10.421881 33.473275,5.8072398 25.268765,5.8072393 18.047545,5.8072398 11.678013,9.3918336 7.8003562,14.867073L10.474627,15.367072C10.474627,15.367072,12.026909,15.574172,10.962921,16.223872L0.67933281,20.826762C0.36037083,20.880062,0.057029126,20.669165,0.00099972012,20.354063L0.69093342,9.1118043C0.89552094,7.9296158,1.5738537,9.124924,1.5738539,9.124924L3.0452772,11.583379C7.9566038,4.584462,16.073666,2.7347414E-07,25.268765,0z
</sys:String>
<!-- Paper Plane (the icon is free to use if we include this information in our credit section)
Icon by <a href="https://freeicons.io/profile/2257">www.wishforge.games</a> on <a href="https://freeicons.io">freeicons.io</a>
-->
<x:Array x:Key="{x:Static mbts:Icons.PaperPlaneGeometryKey}" Type="sys:String">
<sys:String>
M26.4,2.9L3.8,15c-0.7,0.4-0.7,1.5,0.1,1.8l20.8,8.7c0.6,0.3,1.3-0.2,1.4-0.8l1.7-20.8 C27.9,3,27.1,2.5,26.4,2.9z
</sys:String>
<sys:String>
M26,4L13,20v7.3c0,0.9,1.2,1.4,1.8,0.7L19,23
</sys:String>
</x:Array>
<sys:String x:Key="{x:Static mbts:Icons.PauseGeometryKey}">
M17.94,0L29.150001,0 29.150001,32 17.94,32z M0,0L11.210002,0 11.210002,32 0,32z
</sys:String>
@@ -659,7 +671,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="m 127.71875,0.03125 c 1.96953,0.68706766 3.77873,1.728511 5.53125,2.84375 5.68444,-0.1664634 11.35972,0.1351305 17.03125,0.46875 -1.73194,-1.1492389 -3.57847,-2.1023993 -5.53125,-2.8125 -5.6651,-0.45913017 -11.34995,-0.58354857 -17.03125,-0.5 z m -4.25,0.125 c -5.70361,0.2336956 -11.37548,0.81394241 -17.03125,1.5625 1.95847,0.6900238 3.78385,1.7005175 5.53125,2.8125 C 117.61899,3.7613446 123.30363,3.2513106 129,3 127.28567,1.8094865 125.39256,0.94326338 123.46875,0.15625 z M 149,0.90625 c 1.96418,0.6694319 3.78573,1.7017145 5.53125,2.8125 5.71276,0.5944911 11.3908,1.4529283 17.03125,2.53125 C 169.85207,5.0455955 167.95676,4.1939604 166.03125,3.40625 160.39919,2.2891664 154.71106,1.4856327 149,0.90625 z m -46.8125,1.4375 C 96.464888,3.3096165 90.767826,4.4520829 85.15625,5.9375 87.106815,6.6380546 88.941831,7.6391195 90.6875,8.75 96.310308,7.3225043 101.99157,6.1139557 107.71875,5.1875 106.00019,4.0048872 104.11138,3.1307925 102.1875,2.34375 z m 32.46875,1.5 c 2.00275,1.4247295 3.75993,3.1494432 5.53125,4.84375 5.68351,-0.080003 11.36274,0.089238 17.03125,0.5 -1.73044,-1.7529327 -3.58348,-3.3699033 -5.53125,-4.875 -5.66508,-0.4641114 -11.35053,-0.55229 -17.03125,-0.46875 z m -4.28125,0.125 C 124.103,4.2953783 119.00738,4.7504024 113.34375,5.5 c 1.99941,1.4415661 3.79164,3.1353901 5.53125,4.875 5.65135,-0.7706117 11.33386,-1.2798945 17.03125,-1.53125 -1.66132,-1.8295873 -3.58341,-3.3698478 -5.53125,-4.875 z m 39.9375,0.34375 c 1.96346,0.6759081 3.78484,1.7011488 5.53125,2.8125 5.74468,1.2638733 11.4064,2.8505892 17.03125,4.5625 -1.72987,-1.168721 -3.60534,-2.0558794 -5.53125,-2.84375 C 181.73627,7.0795573 176.0521,5.5785891 170.3125,4.3125 z m -14.375,0.375 c 1.9717,1.4743322 3.79243,3.136185 5.53125,4.875 5.71797,0.542825 11.39215,1.453188 17.03125,2.53125 -1.6913,-1.79287 -3.58414,-3.3704114 -5.53125,-4.875 C 167.33901,6.0657565 161.63964,5.35834 155.9375,4.6875 z m -46.84375,1.46875 c -5.72932,0.9221197 -11.402883,2.1554082 -17.03125,3.5625 1.99347,1.451281 3.791265,3.135015 5.53125,4.875 5.62016,-1.451631 11.30209,-2.635724 17.03125,-3.5625 -1.69439,-1.7852366 -3.54058,-3.4272419 -5.53125,-4.875 z M 80.90625,7.0625 c -5.768034,1.593637 -11.417957,3.561289 -17.03125,5.625 1.930762,0.74325 3.819326,1.676529 5.53125,2.84375 5.568779,-2.194858 11.293752,-3.937501 17.03125,-5.625 C 84.697913,8.7506893 82.833288,7.8508336 80.90625,7.0625 z m 96.3125,1.03125 c 2.01998,1.5283684 3.9853,3.332564 5.53125,4.875 5.75001,1.241656 11.41327,2.827536 17.03125,4.5625 -1.70386,-1.770251 -3.54256,-3.428678 -5.53125,-4.875 -5.59469,-1.822363 -11.3081,-3.215876 -17.03125,-4.5625 z M 141.5625,10.1875 c 1.98812,2.171216 3.79154,4.502672 5.53125,6.875 5.68335,-0.07635 11.3631,0.08322 17.03125,0.5 -1.70296,-2.41177 -3.58181,-4.690977 -5.53125,-6.90625 -5.82598,-0.440889 -11.98586,-0.537879 -17.03125,-0.46875 z m -4.25,0.125 c -5.69916,0.210199 -11.37949,0.783222 -17.03125,1.53125 1.96864,2.199545 3.78641,4.526916 5.53125,6.90625 5.65033,-0.779208 11.33395,-1.279899 17.03125,-1.53125 -1.7175,-2.400101 -3.58197,-4.691155 -5.53125,-6.90625 z m -49.5,0.5625 c -5.756209,1.627414 -11.443914,3.488666 -17.03125,5.625 1.986252,1.443163 3.797302,3.109802 5.53125,4.84375 C 81.914775,19.260145 87.586406,17.35867 93.34375,15.75 91.661607,13.944308 89.760517,12.380286 87.8125,10.875 z m 75.03125,0.15625 c 1.99219,2.183637 3.78522,4.525295 5.53125,6.90625 6.28774,0.687745 11.37979,1.450824 17.03125,2.53125 -1.68349,-2.427307 -3.58184,-4.691013 -5.53125,-6.90625 -5.63506,-1.107989 -11.31933,-1.943258 -17.03125,-2.53125 z M 116,12.5 c -5.73799,0.878008 -11.39825,2.174659 -17.03125,3.5625 2.03956,2.148921 3.76403,4.56042 5.5625,6.90625 5.61994,-1.453285 11.29946,-2.648737 17.03125,-3.5625 C 119.8176,17.015603 118.00451,14.67882 116,12.5 z m -56.40625,1.84375 c -5.792814,2.278458 -11.419014,4.940887 -17.03125,7.625 1.967004,0.694041 3.777949,1.728013 5.53125,2.84375 5.58415,-2.744879 11.247417,-5.328478 17.03125,-7.625 -1.725645,-1.141384 -3.572016,-2.162277 -5.53125,-2.84375 z M 184.125,14.4375 c 2.02152,2.155746 3.78605,4.526439 5.53125,6.90625 5.75343,1.228401 11.41327,2.827536 17.03125,4.5625 C 205.01516,23.467587 203.10649,21.216187 201.15625,19 195.55578,17.209943 189.86603,15.703904 184.125,14.4375 z m -89.40625,2.78125 c -5.757378,1.612569 -11.421181,3.531225 -17.03125,5.59375 2.013414,2.164294 3.785804,4.526096 5.53125,6.90625 C 88.826875,27.652066 94.492577,25.733692 100.25,24.125 98.572893,21.691803 96.66853,19.434409 94.71875,17.21875 z m -28.1875,0.9375 c -5.809395,2.239258 -11.434051,4.908712 -17.03125,7.625 1.996367,1.448111 3.791108,3.134858 5.53125,4.875 C 60.587618,27.843081 66.305928,25.370353 72.0625,23 70.325483,21.271546 68.515383,19.599256 66.53125,18.15625 z m 81.9375,0.90625 C 150.4643,21.946692 152.27759,24.946629 154,28 c 5.68473,-0.186058 11.35951,0.135118 17.03125,0.46875 -1.70966,-3.061344 -3.58704,-6.02118 -5.53125,-8.9375 -5.66815,-0.423667 -11.34913,-0.552311 -17.0Line truncated
<GeometryDrawing.Pen>
<Pen Brush="White" Thickness="2" />
<Pen Brush="{mbts:BTSColor UI.icon_foreground}" Thickness="2" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -1270,8 +1282,23 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<!-- Credits: https://freeicons.io/icon/download?_token=zMXvmlGgqOxtOIVOq4byxkcsakzIoDKVPValPURT&icon_id=line-chart-icon-6411&type=svg -->
<x:Array x:Key="{x:Static mbts:Icons.MarketGeometryKey}" Type="sys:String">
<sys:String>M33,362.371v78.9c0,4.8,3.9,8.8,8.8,8.8h61c4.8,0,8.8-3.9,8.8-8.8v-138.8l-44.3,44.3C57.9,356.071,45.9,361.471,33,362.371z</sys:String>
<sys:String>M142,301.471v139.8c0,4.8,3.9,8.8,8.8,8.8h61c4.8,0,8.8-3.9,8.8-8.8v-82.3c-13.9-0.3-26.9-5.8-36.7-15.6L142,301.471z</sys:String>
<sys:String>M251,350.271v91c0,4.8,3.9,8.8,8.8,8.8h61c4.8,0,8.8-3.9,8.8-8.8v-167.9l-69.9,69.9C257,345.971,254.1,348.271,251,350.271z</sys:String>
<sys:String>M432.7,170.171l-72.7,72.7v198.4c0,4.8,3.9,8.8,8.8,8.8h61c4.8,0,8.8-3.9,8.8-8.8v-265.6c-2-1.7-3.5-3.2-4.6-4.2L432.7,170.171z</sys:String>
<sys:String>M482.6,41.371c-2.9-3.1-7.3-4.7-12.9-4.7c-0.5,0-1.1,0-1.6,0c-28.4,1.3-56.7,2.7-85.1,4c-3.8,0.2-9,0.4-13.1,4.5c-1.3,1.3-2.3,2.8-3.1,4.6c-4.2,9.1,1.7,15,4.5,17.8l7.1,7.2c4.9,5,9.9,10,14.9,14.9l-171.6,171.7l-77.1-77.1c-4.6-4.6-10.8-7.2-17.4-7.2c-6.6,0-12.7,2.6-17.3,7.2L7.2,286.871c-9.6,9.6-9.6,25.1,0,34.7l4.6,4.6c4.6,4.6,10.8,7.2,17.4,7.2s12.7-2.6,17.3-7.2l80.7-80.7l77.1,77.1c4.6,4.6,10.8,7.2,17.4,7.2c6.6,0,12.7-2.6,17.4-7.2l193.6-193.6l21.9,21.8c2.6,2.6,6.2,6.2,11.7,6.2c2.3,0,4.6-0.6,7-1.9c1.6-0.9,3-1.9,4.2-3.1c4.3-4.3,5.1-9.8,5.3-14.1c0.8-18.4,1.7-36.8,2.6-55.3l1.3-27.7C487,49.071,485.7,44.571,482.6,41.371z</sys:String>
</x:Array>
<!-- Credits: -->
<x:Array x:Key="{x:Static mbts:Icons.RefreshGeometryKey}" Type="sys:String">
<sys:String>M45.633,17.348C46.54,19.792,47,22.367,47,25c0,12.131-9.869,22-22,22c-6.595,0-12.795-2.963-16.958-8H15v-2H5v10h2v-6.126C11.53,46.007,18.068,49,25,49c13.234,0,24-10.767,24-24c0-2.872-0.502-5.68-1.492-8.348L45.633,17.348z</sys:String>
<sys:String>M35,13h10V3h-2v6.125C38.47,3.992,31.934,1,25,1C11.767,1,1,11.767,1,25c0,2.872,0.502,5.68,1.492,8.348l1.875-0.696C3.46,30.208,3,27.633,3,25C3,12.869,12.869,3,25,3c6.598,0,12.795,2.962,16.958,8H35V13z</sys:String>
</x:Array>
<!-- Credits: -->
<x:Array x:Key="{x:Static mbts:Icons.FavoriteWindowGeometryKey}" Type="sys:String">
<sys:String>M4 2h16a1 1 0 0 1 1 1v19.276a.5.5 0 0 1-.704.457L12 19.03l-8.296 3.702A.5.5 0 0 1 3 22.276V3a1 1 0 0 1 1-1zm15 17.965V4H5v15.965l7-3.124 7 3.124zM12 13.5l-2.939 1.545.561-3.272-2.377-2.318 3.286-.478L12 6l1.47 2.977 3.285.478-2.377 2.318.56 3.272L12 13.5z</sys:String>
</x:Array>
<!-- 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
@@ -4,6 +4,7 @@
using mbts.utils;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using System;
@@ -0,0 +1,25 @@
#nullable enable
namespace Spark.Model {
using bts;
using bts.api;
using bts.co2;
using mbts.spark;
using Spark.Api.Reactive;
using Spark.Core;
using System.Composition;
class SecurityResolverExporter : VirtualSingleton<SecurityResolverExporter> {
readonly LazyOnce<SecurityResolver>? _resolver;
[ImportingConstructor]
public SecurityResolverExporter(IBus bus, MarketDataRelay md_relay) =>
_resolver = new(() => new(md_relay, bus.subscribe(SparkTags.SecuritiesTag)));
[Export]
public SecurityResolver resolver =>
Log.notnull(_resolver);
}
}
@@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace mbts.ui.settings {
public interface ISavedWindowFactory {
Task<IShellWindow> restore(WindowState window);
Task<IShellWindow> restore(WindowSettings window);
}
[Export(typeof(ISavedWindowFactory))]
@@ -28,7 +28,7 @@ namespace mbts.ui.settings {
[Import]
public ObservableCollection<IShellModule> modules { get; set; }
public Task<IShellWindow> restore(WindowState window) {
public Task<IShellWindow> restore(WindowSettings window) {
Log.Ui.info("Loading window '{0}'".fmt(window.title));
bool changed;
int count = 0;
@@ -49,7 +49,7 @@ namespace mbts.ui.settings {
/// ex: If change the data format twice from A to B to C then we would write 2 IShellWindowMigration instances.
/// Starting in state A one of the _compat tests changes to state B. Now we need to re-run
/// the migrations because state B will match a different rule and be converted to state C.
private bool apply_compatability_migrations(ref WindowState window) {
private bool apply_compatability_migrations(ref WindowSettings window) {
foreach (var test in _compat) {
try {
if (test.apply(ref window)) {
@@ -14,9 +14,9 @@ using System.Windows.Threading;
namespace mbts.ui.settings {
public interface ISettingsCollector {
// Get the save state for every open window
Task<List<WindowState>> get_window_state();
Task<WindowState> get_window_state(IShellWindow window);
IEnumerable<ModuleState> get_module_state();
Task<List<WindowSettings>> get_window_state();
Task<WindowSettings> get_window_state(IShellWindow window);
IEnumerable<ModuleSettings> get_module_state();
WindowPosition get_launcher_state();
}
@@ -39,9 +39,9 @@ namespace mbts.ui.settings {
IWindowsInterop _interop => _wm;
public async Task<List<WindowState>> get_window_state() {
public async Task<List<WindowSettings>> get_window_state() {
var windows = _wm.list_windows();
var rv = new List<WindowState>(windows.Count);
var rv = new List<WindowSettings>(windows.Count);
foreach (var window in windows) {
// Check that the window is still open since we might be iterating asyncronously
if (_wm.contains(window)) {
@@ -54,12 +54,12 @@ namespace mbts.ui.settings {
return rv;
}
public IEnumerable<ModuleState> get_module_state() {
public IEnumerable<ModuleSettings> get_module_state() {
//Note: I'm assuming that the list of modules is stable and I don't have to protect against it changing here
foreach (var module in modules.OfTypeSafe<IPersistShellModule>()) {
MemoryStream out_stream = new MemoryStream();
module.get_state(out_stream);
yield return new ModuleState() {
yield return new ModuleSettings() {
name = module.key(),
app_data = out_stream.ToArray(),
};
@@ -70,7 +70,7 @@ namespace mbts.ui.settings {
return _launcher.get_position();
}
public async Task<WindowState> get_window_state(IShellWindow window) {
public async Task<WindowSettings> get_window_state(IShellWindow window) {
try {
if (window == null || window.Owner == null) return null;
Dispatcher dispatcher = null;
@@ -89,10 +89,10 @@ namespace mbts.ui.settings {
}
var pos = _interop.GetPosition(window);
var state = new WindowState() {
var state = new WindowSettings() {
owner = window.Owner.key(),
app_data = dispatcher is null ?
(await dispatcher.InvokeAsync(serialize)) : serialize(),
serialize() : (await dispatcher.InvokeAsync(serialize)),
position = pos,
title = string.IsNullOrEmpty(pos.custom_title) ? window.Title : pos.custom_title
};
@@ -1,4 +1,4 @@
using bts.shell.pnl;
using bts.shell.pnl;
using bts.utils;
using mbts.ui.settings;
@@ -21,6 +21,8 @@ namespace bts.settings {
interface IPNLSettingsMigrationRule : ISettingsMigrationRule<PNLSettings> { }
interface ISyntheticManagementMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IReconcileReportMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IDayFillsGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IOpenOrdersGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
static class SettingsMigrationHelper {
public static bool apply<T>(T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, out T new_settings) {
@@ -1,6 +1,8 @@
using mbts.spark;
using mbts.utils;
using Spark.Core.Disposables;
using System;
using System.Linq;
using System.Reactive.Linq;
@@ -87,7 +89,8 @@ namespace mbts.persist {
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
new NonSharedWrapper(_wsm);
public void Dispose() => Disposable.dispose(ref _dispose);
public void Dispose() =>
Disposable.dispose(ref _dispose);
}
public class DisconnectedImpl : IWindowSettingsManager<T> {
@@ -0,0 +1,31 @@
#nullable enable
namespace mbts.ui.shell {
using ProtoBuf;
using Spark.Core.Protobuf;
using System.Collections.Generic;
[ProtoContract]
public class ModuleSettings : IProtobuf<ModuleSettings> {
[ProtoMember(1)]
public string? name { get; set; }
[ProtoMember(2)]
public byte[]? app_data { get; set; }
public bool Equals(ModuleSettings? other) =>
other is not null &&
name == other.name &&
app_data.safe_sequence_equals(other.app_data);
public override bool Equals(object? obj) =>
Equals(obj as ModuleSettings);
public override int GetHashCode() =>
GetType().GetHashCode();
public ModuleSettings Clone() =>
ProtobufSerializationHelper.clone(this);
}
}
@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
namespace mbts.spark {
using bts.shell;
using bts.shell.fills;
@@ -34,6 +34,8 @@ namespace mbts.spark {
_notification = NotificationSettings.Default,
_market_view_grid = MarketViewSettings.DefaultGridSettings,
_fills_grid = FillsSettings.DefaultGridSettings,
_day_fills_grid = DayFillsSettings.default_grid(),
_open_orders_grid = OpenOrdersSettings.default_grid(),
_time_and_sales_grid = TimeAndSalesSettings.DefaultGridSettings,
_synthetic_management = SyntheticManagementSettings.default_grid_settings,
_reconcile_report = ReconcileReportSettings.Default().grid,
@@ -64,6 +66,10 @@ namespace mbts.spark {
GridSettings? _time_and_sales_grid;
[ProtoMember(7)]
GridSettings? _fills_grid;
[ProtoMember(15)]
GridSettings? _day_fills_grid;
[ProtoMember(16)]
GridSettings? _open_orders_grid;
[ProtoMember(8)]
GridSettings? _synthetic_management;
[ProtoMember(14)]
@@ -99,6 +105,12 @@ namespace mbts.spark {
public GridSettings fills_grid =>
_fills_grid ??= FillsSettings.DefaultGridSettings;
public GridSettings day_fills_grid =>
_day_fills_grid ??= DayFillsSettings.default_grid();
public GridSettings open_orders_grid =>
_open_orders_grid ??= OpenOrdersSettings.default_grid();
public GridSettings synthetic_management_grid =>
_synthetic_management ??=
SyntheticManagementSettings.default_grid_settings;
@@ -123,6 +135,8 @@ namespace mbts.spark {
GridSettings? market_view = null,
GridSettings? time_and_sales = null,
GridSettings? fills = null,
GridSettings? day_fills = null,
GridSettings? open_orders = null,
GridSettings? synthetic_management = null,
GridSettings? reconcile_report = null,
PNLSettings? pnl = null,
@@ -135,6 +149,8 @@ namespace mbts.spark {
_market_view_grid = market_view ?? _market_view_grid,
_time_and_sales_grid = time_and_sales ?? _time_and_sales_grid,
_fills_grid = fills ?? _fills_grid,
_day_fills_grid = day_fills ?? _day_fills_grid,
_open_orders_grid = open_orders ?? _open_orders_grid,
_synthetic_management = synthetic_management ?? _synthetic_management,
_reconcile_report = reconcile_report ?? _reconcile_report,
_pnl = pnl ?? _pnl,
@@ -1,4 +1,4 @@
using bts;
using bts;
using bts.settings;
using bts.shell.pnl;
using bts.utils;
@@ -26,6 +26,8 @@ namespace mbts.persist {
static readonly IImmutableProperty<UserSettings, GridSettings>
_fills_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.fills_grid, (us, gs) => us.with(fills: gs)),
_day_fills_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.day_fills_grid, (us, gs) => us.with(day_fills: gs)),
_open_orders_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.open_orders_grid, (us, gs) => us.with(open_orders: gs)),
_mview_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.market_view_grid, (us, gs) => us.with(market_view: gs)),
_synth_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.synthetic_management_grid, (us, gs) => us.with(synthetic_management: gs)),
_reconcile_report_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.reconcile_report_grid, (us, gs) => us.with(reconcile_report: gs));
@@ -36,6 +38,8 @@ namespace mbts.persist {
readonly object _default_settings_lock = new object();
readonly BehaviorSubject<UserSettings> _default_settings_subject = new BehaviorSubject<UserSettings>(UserSettings.factory_defaults);
readonly IEnumerable<IFillsGridSettingsMigrationRule> _fills_migration_rules;
readonly IEnumerable<IDayFillsGridSettingsMigrationRule> _day_fills_migration_rules;
readonly IEnumerable<IOpenOrdersGridSettingsMigrationRule> _open_orders_migration_rules;
readonly IEnumerable<IMarketViewGridSettingsMigrationRule> _mview_migration_rules;
readonly IEnumerable<IPNLSettingsMigrationRule> _pnl_migration_rules;
readonly IEnumerable<ISyntheticManagementMigrationRule> _synth_migration_rules;
@@ -48,6 +52,8 @@ namespace mbts.persist {
public UserSettingsHub(
IUserPersist persist,
[ImportMany] IEnumerable<IFillsGridSettingsMigrationRule> fills_migration_rules,
[ImportMany] IEnumerable<IDayFillsGridSettingsMigrationRule> day_fills_migration_rules,
[ImportMany] IEnumerable<IOpenOrdersGridSettingsMigrationRule> open_orders_migration_rules,
[ImportMany] IEnumerable<IMarketViewGridSettingsMigrationRule> mview_migration_rules,
[ImportMany] IEnumerable<IPAndLGridSettingsMigrationRule> pandl_migration_rules,
[ImportMany] IEnumerable<IPNLSettingsMigrationRule> pnl_migration_rules,
@@ -56,6 +62,8 @@ namespace mbts.persist {
//
_persist = persist;
_fills_migration_rules = fills_migration_rules ?? Enumerable.Empty<IFillsGridSettingsMigrationRule>();
_day_fills_migration_rules = day_fills_migration_rules ?? Enumerable.Empty<IDayFillsGridSettingsMigrationRule>();
_open_orders_migration_rules = open_orders_migration_rules ?? Enumerable.Empty<IOpenOrdersGridSettingsMigrationRule>();
_mview_migration_rules = mview_migration_rules ?? Enumerable.Empty<IMarketViewGridSettingsMigrationRule>();
_pnl_migration_rules = pnl_migration_rules ?? Enumerable.Empty<IPNLSettingsMigrationRule>();
_synth_migration_rules = synth_migration_rules ?? Enumerable.Empty<ISyntheticManagementMigrationRule>();
@@ -68,6 +76,8 @@ namespace mbts.persist {
if (s != null) {
var wrapped_rules = new[] {
_fills_migration_rules.merge().wrap(_fills_propi),
_day_fills_migration_rules.merge().wrap(_day_fills_propi),
_open_orders_migration_rules.merge().wrap(_open_orders_propi),
_mview_migration_rules.merge().wrap(_mview_propi),
_pnl_migration_rules.merge().wrap(_pnl_propi),
_synth_migration_rules.merge().wrap(_synth_propi),
@@ -152,6 +162,20 @@ namespace mbts.persist {
_reconcile_report_migration_rules,
_reconcile_report_propi);
public IWindowSettingsManager<GridSettings> create_day_fills_settings_manager(
GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_day_fills_migration_rules,
_day_fills_propi);
public IWindowSettingsManager<GridSettings> create_open_orders_settings_manager(
GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_day_fills_migration_rules,
_day_fills_propi);
public async Task save_default(UserSettings value) {
// notify observers before persisting so the changes are reflected immediately in the ui
lock (_default_settings_lock) {
@@ -0,0 +1,71 @@
#nullable enable
namespace mbts.ui.shell {
using ProtoBuf;
using Spark.Core.Protobuf;
using System;
using System.ComponentModel;
[ProtoContract]
public class WindowPosition : IProtobuf<WindowPosition> {
[ProtoMember(3)]
public int width { get; set; }
[ProtoMember(4)]
public int height { get; set; }
[ProtoMember(5)]
public int left { get; set; }
[ProtoMember(6)]
public int top { get; set; }
[ProtoMember(7)]
public bool is_maximized { get; set; }
[ProtoMember(8)]
public string? host_id { get; set; }
[ProtoMember(9)]
public int tab_index { get; set; }
[ProtoMember(100)]
public bool horizontal { get; set; }
[ProtoMember(101), DefaultValue(true)]
public bool show_text { get; set; } = true;
[ProtoMember(102), DefaultValue(true)]
public bool always_on_top { get; set; } = true;
[ProtoMember(104)]
public string? custom_title { get; set; }
public bool Equals(WindowPosition? other) =>
other is not null &&
width == other.width &&
height == other.height &&
left == other.left &&
top == other.top &&
is_maximized == other.is_maximized &&
host_id == other.host_id &&
tab_index == other.tab_index &&
horizontal == other.horizontal &&
show_text == other.show_text &&
always_on_top == other.always_on_top &&
custom_title == other.custom_title;
public override bool Equals(object? obj) =>
Equals(obj as WindowPosition);
public override int GetHashCode() =>
GetType().GetHashCode();
public WindowPosition Clone() =>
ProtobufSerializationHelper.clone(this);
object ICloneable.Clone() =>
Clone();
}
}
@@ -0,0 +1,45 @@
#nullable enable
namespace mbts.ui.shell {
using bts.utils;
using ProtoBuf;
using Spark.Core.Protobuf;
using System;
using System.Collections.Generic;
[ProtoContract]
public class WindowSettings : IProtobuf<WindowSettings> {
[ProtoMember(1)]
public string? owner { get; set; }
[ProtoMember(2)]
public byte[]? app_data { get; set; }
[ProtoMember(8)]
public WindowPosition? position { get; set; }
[ProtoMember(9)]
public string? title { get; set; }
public bool Equals(WindowSettings? other) =>
other is not null &&
owner == other.owner &&
app_data.safe_sequence_equals(other.app_data) &&
position.safe_equals(other.position) &&
title == other.title;
public override bool Equals(object? obj) =>
Equals(obj as WindowSettings);
public override int GetHashCode() =>
GetType().GetHashCode();
public WindowSettings Clone() =>
ProtobufSerializationHelper.clone(this);
object ICloneable.Clone() =>
Clone();
}
}
@@ -1,77 +0,0 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui.shell {
public enum DockState {
FLOATING = 1,
TOP = 2,
BOTTOM = 3,
LEFT = 4,
RIGHT = 5,
}
[ProtoContract]
public class WindowPosition {
[ProtoMember(3)]
public int width;
[ProtoMember(4)]
public int height;
[ProtoMember(5)]
public int left;
[ProtoMember(6)]
public int top;
[ProtoMember(7)]
public bool is_maximized;
[ProtoMember(8)]
public string host_id;
[ProtoMember(9)]
public int tab_index;
[ProtoMember(100), DefaultValue(false)]
public bool horizontal = false;
[ProtoMember(101), DefaultValue(true)]
public bool show_text = true;
[ProtoMember(102), DefaultValue(true)]
public bool always_on_top = true;
//[ProtoMember(103)] //used
[ProtoMember(104)]
public string custom_title;
}
[ProtoContract]
public class WindowState {
[ProtoMember(1)]
public string owner;
[ProtoMember(2)]
public byte[] app_data;
//optional bool is_open = 3 [default = true];
//optional bool is_favorite = 4;
//optional string title = 5;
//optional string id = 6;
// optional bool is_loaded = 7 [default = false];
[ProtoMember(8)]
public WindowPosition position;
[ProtoMember(9)]
public string title; // for debugging
}
[ProtoContract]
public class ModuleState {
[ProtoMember(1)]
public string name;
[ProtoMember(2)]
public byte[] app_data;
}
}
@@ -1,13 +1,12 @@
#nullable enable
namespace bts.api.position {
namespace Spark.Trading {
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.position;
using bts.shell.pnl;
using mbts.ui;
using Spark.Trading;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -17,10 +16,10 @@ namespace bts.api.position {
IObservable<TableUpdate<(SecurityImmutable?, PositionUnitImmutable), int>> on_positions { get; }
IObservable<TableUpdate<(SecurityImmutable? sec, RetrievedExchangePositionImmutable exch_pos), AccountSecurityKey>> on_exchange_positions { get; }
IObservable<TableUpdate<ReconcileReportRowData, AccountSecurityKey>> merged_positions { get; }
IObservable<TableUpdate<DayFillsRowData, DayFillsRowKey>> day_fills { get; }
// pnl
IObservable<TableUpdate<(SecurityImmutable?, PNLUnitImmutable, QuantityConversionData<double>), int>> on_pnl { get; }
IObservable<TableUpdate<PNLRowData, AccountSecurityKey>> security_account_pnl { get; }
IObservable<TableUpdate<PNLRowData, AccountSecurityKey>> pnl { get; }
IObservable<TableUpdate<(SecurityImmutable?, PositionUnitImmutable), int>> on_security_positions(
uint security_key);
@@ -10,6 +10,7 @@ namespace Spark.Model.Trading {
using mbts.spark;
using Spark.Api.Reactive;
using Spark.Core.Reactive;
using System;
@@ -25,7 +26,7 @@ namespace Spark.Model.Trading {
public OrderDataSource(
IBus bus,
AdminModel admin_model,
SecurityStore security_store,
SecurityResolver sec_resolver,
OrderService order_service,
ISecurityAuxiliaryDataModel security_auxiliary_model,
ErrorReporter error_reporter
@@ -38,7 +39,7 @@ namespace Spark.Model.Trading {
order_service.subscribei_orders()
.Naggle(interval, scheduler),
o => o.account_id())
.join(security_store, o => o.security_key())
.join(sec_resolver, o => o.security_key())
.Table()
.Cache();
+21 -33
View File
@@ -1,19 +1,23 @@
#nullable enable
namespace bts.api.position {
namespace Spark.Trading {
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.position;
using bts.collections;
using bts.shell.pnl;
using bts.spark;
using mbts;
using mbts.analytics;
using mbts.spark;
using mbts.ui;
using Spark.Api.Reactive;
using Spark.Core;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using Spark.Model.Trading;
using Spark.Trading;
using System;
using System.Collections.Generic;
@@ -29,13 +33,13 @@ namespace bts.api.position {
readonly ErrorReporter _error_reporter;
readonly TableUpdateDemuxer<(SecurityImmutable?, PositionUnitImmutable), int, uint> _security_positions_demuxer;
IDisposable _dispose;
ThreadSafeDisposable? _dispose;
[ImportingConstructor]
public PositionModel(
IBus bus,
AdminModel admin_model,
SecurityStore security_store,
SecurityResolver sec_resolver,
PositionService position_service,
OrderService order_service,
OrderDataSource order_data,
@@ -52,7 +56,7 @@ namespace bts.api.position {
on_exchange_positions = order_service.subscribei_exchange_positions()
.Filter(ep => ep.SecurityKey != 0)
.SelectKeyValue(ep => ep, ep => new AccountSecurityKey(ep.AccountId, ep.SecurityKey))
.join(security_store, ep => ep.SecurityKey)
.join(sec_resolver, ep => ep.SecurityKey)
.Table()
.Cache();
@@ -60,31 +64,15 @@ namespace bts.api.position {
.permission_filter(
_position_service.subscribei_position_data().Naggle(interval, scheduler),
p => p.AccountId)
.join(security_store, pos => pos.SecurityId)
.join(sec_resolver, pos => pos.SecurityId)
.Table()
.Cache();
on_pnl = admin_model
.permission_filter(
_position_service.subscribei_pnl_data().Naggle(interval, scheduler),
p => p.Pos.AccountId)
.join(security_store, p => p.Pos.SecurityId)
.Select(
tup => (tup.sec, tup.val, new QuantityConversionData<double>(
security_auxiliary_model.get_display_quantity_divisor(tup.sec),
QuantityConversionOpType.DivideActual)),
tup => tup.val.RecordId)
.TableCheckedAutoCorrect(
EqualityComparer<(SecurityImmutable?, PNLUnitImmutable, QuantityConversionData<double>)>.Default)
.Where(t => t.has_changes())
.Cache();
day_fills = DayFillsDataSource.create(bus, sec_resolver);
// security account pnl
security_account_pnl = on_pnl
#if DEBUG
.AssertIncremental(p => new AccountSecurityKey(p.Item2.Pos.AccountId, p.Item2.Pos.SecurityId))
#endif
.SelectKeyValue(PNLRowDataFactory.create, PNLRowData.key_fn)
pnl = bus.subscribe(SparkTags.ReportBlackboardTags.PNL.PNLData)
.Select(d => d.position_data.ToDictionary(u => u.id(), PNLRowData.from_pnl_unit))
.Table(PNLRowData.key_fn)
.FullJoin(
order_data.working_account_security_orders,
PNLRowData.key_fn,
@@ -111,18 +99,18 @@ namespace bts.api.position {
.Where(t => t.has_changes())
.Cache();
merged_positions = ReconcileReportDataSource.create(on_exchange_positions, on_positions)
merged_positions = ReconcileReportDataSource.create(
on_exchange_positions, on_positions.Filter(tup => !(tup.Item1?.HasSpread ?? false)))
.Cache();
_dispose = Disposable.Create(
on_positions.Subscribe(_security_positions_demuxer));
_dispose = new(on_positions.Subscribe(_security_positions_demuxer));
}
public IObservable<TableUpdate<(SecurityImmutable? sec, RetrievedExchangePositionImmutable exch_pos), AccountSecurityKey>> on_exchange_positions { get; }
public IObservable<TableUpdate<(SecurityImmutable?, PositionUnitImmutable), int>> on_positions { get; }
public IObservable<TableUpdate<(SecurityImmutable?, PNLUnitImmutable, QuantityConversionData<double>), int>> on_pnl { get; }
public IObservable<TableUpdate<PNLRowData, AccountSecurityKey>> pnl { get; }
public IObservable<TableUpdate<ReconcileReportRowData, AccountSecurityKey>> merged_positions { get; }
public IObservable<TableUpdate<PNLRowData, AccountSecurityKey>> security_account_pnl { get; }
public IObservable<TableUpdate<DayFillsRowData, DayFillsRowKey>> day_fills { get; }
public IObservable<TableUpdate<(SecurityImmutable?, PositionUnitImmutable), int>> on_security_positions(uint security) =>
_security_positions_demuxer.subscribe(security);
@@ -155,6 +143,6 @@ namespace bts.api.position {
"reset positions");
public void Dispose() =>
Disposable.dispose_thread_safe(ref _dispose);
Disposable.dispose(ref _dispose);
}
}
@@ -1,5 +1,5 @@
#nullable enable
namespace bts.api.position {
namespace Spark.Trading {
using bts.api.core;
using Spark.Api.Position;
Loaded 100 of 1194 files, more files were not shown because too many files have changed in this diff. Show more