Compare commits

...
Author SHA1 Message Date
johncolvin-og ea4e599ac6 fix build: Fix a few path errors in publish target 2023-05-31 21:59:51 -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
taha afzal 89d5b25cc7 For EmMDListener, added a limit of 5 pending calls for positions, after which the counter is reset is positions are re-requested 2022-07-12 17:03:19 -04:00
Taha Afzal 3e10d403b4 Combo Report now listens to the sectups in the report configuration 2022-07-12 09:35:42 -04:00
Taha Afzal 1ebf06d49a Slide report filters the securities based on the security tuples passed in the report 2022-07-12 09:35:42 -04:00
Taha Afzal 2be857a851 Forcing qty's to be rounded to 2 2022-07-12 09:35:42 -04:00
John Colvin 1de22a82e8 fix (combo settings ui): Refresh 'result' when sec criteria changes
Bugs (fixed):
   * In Combo settings dialog, security criteria changes aren't committed to
     server if they are the last prop changed before OK click
2022-07-12 01:00:51 -04:00
John Colvin f275308cc8 build (client cpp): Remove ZERO_CHECK.vcxproj from spark_client.sln
The new cmake generates ZERO_CHECK.proj instead, which doesn't need to be
included in the sln. I think this change was introduced in v 3.22.

Devs: This won't break the build for older versions of cmake, although you may
need to manually touch "CMakeLists.txt" when you add/remove build src files
2022-07-12 01:00:44 -04:00
taha afzal 79de0dd4d8 Removed exchange from sub acocunt config 2022-07-11 21:13:05 -04:00
taha afzal 5cedf74406 Merge branch 'spark_crypto.multiple_accounts' into spark_crypto.dev 2022-07-11 17:44:02 -04:00
taha afzal e5de6b4ad7 Changed the relationship of internal account to sub account to simple 1:1 2022-07-11 17:28:53 -04:00
John Colvin e467195de1 chore (combo vm): Let end observer call OnDispatcher
This behavior could arguably be coupled with a Subscribe call, since its whole
purpose is to ensure subscription callbacks occur on the dispatcher thread.
2022-07-11 17:22:44 -04:00
John Colvin 913a5b4a7e ui (combo settings): Show contract picker 2022-07-11 17:22:44 -04:00
John Colvin feb8bd9b79 ui (slide settings): Show contract picker 2022-07-11 16:55:23 -04:00
taha afzal 846ecfa776 Fixed the reconcile issue where if the security existed in multiple accounts it was getting surpressed in one 2022-07-11 13:27:10 -04:00
taha afzal d98fb399a8 Exchange Positions can now be obtained for multiple sub accounts 2022-07-09 01:40:28 -04:00
taha afzal 4a1c1d023a Changes to accomodate the EM API change for mulitple accounts 2022-07-06 16:42:04 -04:00
taha afzal 43cba548f4 Adding some details to the logs to track order flow 2022-06-24 15:05:15 -04:00
taha afzal 772910ffe5 Fixing filled qty's fractional part not being filled on the server 2022-06-23 14:38:57 -05:00
John Colvin 767179caf7 chore (man fills): Format qty w/ sec metadata config 2022-06-23 14:38:57 -05:00
John Colvin ee4a9150b8 refactor (man fills): Rename 'size' members 'quantity' 2022-06-23 14:24:04 -05:00
John Colvin 58914fa703 chore: Replace remaining refs to uint 'filled' fn w/ double impl 2022-06-23 14:24:04 -05:00
John Colvin ba40e7044a fix (ladder, preemptive): Fix typo in _fmt_mkt_depth_item
Bugs (fixed):
   * The _fmt_mkt_depth_item fn potentially will return the result of the
     delegate.ToString(), instead of invoking the delegate and returning the
     result
2022-06-23 14:10:15 -05:00
John Colvin 010d0073fe chore: Set BTC qty display config defaults 2022-06-23 02:05:23 -05:00
John Colvin 40210ce42e chore: Remove unnecessary price_display config default 2022-06-23 01:35:22 -05:00
John Colvin 59cdafba21 fix: Don't cast manual fill qty to uint 2022-06-23 01:22:09 -05:00
John Colvin fe6226baa5 ui (reconcile rep): Match all undefined positions
Now, all positions without real non-zero quantities are considered matches
2022-06-23 01:19:27 -05:00
John Colvin 45987c7652 feat: Show fractional qty in orderbook Filled col 2022-06-22 16:04:24 -05:00
John Colvin a18ce97fb4 feat (ui): Support fractional qtys for order entry 2022-06-22 15:38:38 -05:00
John Colvin 13d5080bbc refactor: Shorten modify/revert cmd prop names
Also consolidate modify_price/qty fns into single modify_order fn
2022-06-22 15:38:38 -05:00
John Colvin 9f07a36d1d refactor: Move order-update-desc fn into OrderUtils for gen use 2022-06-22 15:38:38 -05:00
John Colvin bc2e7f428a chore: Add formal/consistent numeric exceptions/messages 2022-06-22 15:38:38 -05:00
John Colvin 53bfbdd349 fix: Sync 'has_value' prop for qty param
Bugs (fixed):
   * Can't tick qty up/down in the orderbook right panel (clicking the
   button has no discernible effect)
2022-06-22 15:38:38 -05:00
John Colvin 174eac9a59 refactor: Move OrderParamsVM into own file
OrderBookVM.cs was (and frankly still is) massive.  Too massive
2022-06-22 15:38:38 -05:00
John Colvin 3775b86d9e refactor: Rename 'size_' props 'quantity_' to be consistent 2022-06-22 15:38:38 -05:00
John Colvin 5e1c6d6209 fix build (windows): Don't include stdint-uintn.h on windows 2022-06-22 15:38:37 -05:00
taha afzal 0d0fb036ab EmMDListener now get_open_orders and logging the open orders 2022-06-22 15:38:37 -05:00
taha afzal f8668802ab Fixing oder updates on fractional quantity orders (just a few lines) 2022-06-22 15:38:37 -05:00
taha afzal c22a1382a1 Fractional qtys now work on the server (need some client side work) 2022-06-22 15:38:37 -05:00
Taha Afzal 941a0d91cc Enabled Combo report to show fractions (ITradeLow had size of type int) 2022-06-13 20:01:47 -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
1216 changed files with 65330 additions and 42489 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 -22
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
@@ -12,15 +12,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sparkcmd", "src\client\spar
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}"
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
@@ -37,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
@@ -69,14 +71,6 @@ Global
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Release|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Debug|x64.ActiveCfg = Debug|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Debug|x64.Build.0 = Debug|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Release|x64.ActiveCfg = Release|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Release|x64.Build.0 = Release|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.Build.0 = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x64.ActiveCfg = Release|x64
@@ -93,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; ^
@@ -15,7 +15,9 @@ namespace mbts.spark {
using mbts.ui.utility;
using mbts.utils;
using Spark.Core;
using Spark.Core.Collections;
using Spark.Core.Disposables;
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
@@ -30,9 +30,6 @@
<Setter
Property="FontStyle"
Value="Italic" />
<Setter
Property="Foreground"
Value="Gray" />
</Trigger>
</Style.Triggers>
</Style>
@@ -51,9 +48,6 @@
<Setter
Property="FontStyle"
Value="Italic" />
<Setter
Property="Foreground"
Value="Gray" />
</Trigger>
</Style.Triggers>
</Style>
@@ -123,6 +117,20 @@
CommandParameter="{Binding}"
/>
</ContextMenu>
<Style x:Key="TextBlockCellElement" TargetType="TextBlock">
<Setter
Property="Foreground"
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=DataGridCell}}" />
</Style>
<Style x:Key="ComboBoxCellElement" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter
Property="Foreground"
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=DataGridCell}}" />
<Setter Property="Margin" Value="2" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.Resources>
@@ -145,17 +153,23 @@
SelectedItems="{Binding selected_symbols}"
HorizontalAlignment="Left"
Margin="5,2,2,2"
TextBlock.Foreground="Black"
HeaderContent="Symbols"
ShowSelectAll="True"
/>
ShowSelectAll="True">
<c:ComboCheckBox.HeaderContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="{u:BTSColor UI.default_text}" />
</DataTemplate>
</c:ComboCheckBox.HeaderContentTemplate>
</c:ComboCheckBox>
<WrapPanel>
<TextBlock Margin="5,5,0,0" Text="Auto copy rolls:" />
<TextBlock
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:" />
<TextBlock
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>
@@ -163,8 +177,8 @@
x:Name="_grid"
Grid.Row="1"
ItemsSource="{Binding visible_rows}"
AlternatingRowBackground="{u:BTSColor MarketData.driver_background}"
VerticalGridLinesBrush="Gray"
Background="Transparent"
VerticalGridLinesBrush="{u:BTSColor Grid.grid_line}"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
CanUserAddRows="False"
@@ -177,35 +191,10 @@
SelectionUnit="FullRow"
SelectedUnits="{Binding selected_rows}"
PreviewMouseRightButtonUp="_grid_PreviewMouseRightButtonUp">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="BorderThickness" Value="1" />
<Style.Triggers>
<Trigger
Property="IsSelected"
Value="True">
<Setter
Property="Background"
Value="{u:BTSColor Grid.grid_selection_background}" />
<Setter
Property="BorderBrush"
Value="{u:BTSColor Grid.grid_selection_background}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<c:DataGridEx.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay}"/>
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger
Property="IsSelected"
Value="true">
<Setter
Property="Background"
Value="{u:BTSColor Grid.grid_selection_background}" />
</Trigger>
<DataTrigger
Binding="{Binding is_last_row_of_month_group}"
Value="True">
@@ -218,7 +207,7 @@
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</c:DataGridEx.RowStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter
@@ -230,9 +219,6 @@
<Setter
Property="HorizontalContentAlignment"
Value="Center" />
<Setter
Property="BorderThickness"
Value="0,0,1,0" />
<Setter
Property="BorderBrush"
Value="Black" />
@@ -259,9 +245,6 @@
<Setter
Property="Foreground"
Value="{u:BTSColor Name=header_text}" />
<Setter
Property="BorderThickness"
Value="0" />
<Setter
Property="VerticalContentAlignment"
Value="Center" />
@@ -308,9 +291,7 @@
<DataTrigger
Binding="{Binding is_driver_dirty}"
Value="True">
<Setter
Property="BorderBrush"
Value="{u:BTSColor UI.input_dirty_border}" />
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -318,11 +299,10 @@
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource Drivers}}"
ItemTemplate="{StaticResource DriverTemplate}"
SelectedItem="{Binding driver, UpdateSourceTrigger=PropertyChanged}"
Margin="2" />
Style="{StaticResource ComboBoxCellElement}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@@ -340,21 +320,28 @@
<DataTrigger
Binding="{Binding is_deliverable_dirty}"
Value="True">
<Setter
Property="BorderBrush"
Value="Red" />
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding is_deliverable_dirty}" Value="False" />
<Condition Binding="{Binding
deliverable,
Converter={x:Static shell:AnalyticsExpirationParametersView.expiration_is_defined_converter}}"
Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" Value="{u:BTSColor UI.default_foreground_dark}" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource NonOptionsWithEmpty}}"
ItemTemplate="{StaticResource ExpirationTemplate}"
SelectedItem="{Binding deliverable, UpdateSourceTrigger=PropertyChanged}"
Margin="2" />
Style="{StaticResource ComboBoxCellElement}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@@ -372,21 +359,28 @@
<DataTrigger
Binding="{Binding is_roll_schedule_dirty}"
Value="True">
<Setter
Property="BorderBrush"
Value="Red" />
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding is_roll_schedule_dirty}" Value="False" />
<Condition Binding="{Binding
roll_schedule,
Converter={x:Static shell:AnalyticsExpirationParametersView.expiration_is_defined_converter}}"
Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" Value="{u:BTSColor UI.default_foreground_dark}" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource NonOptionsWithEmpty}}"
ItemTemplate="{StaticResource ExpirationTemplate}"
SelectedItem="{Binding roll_schedule, UpdateSourceTrigger=PropertyChanged}"
Margin="2" />
Style="{StaticResource ComboBoxCellElement}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@@ -402,9 +396,7 @@
<DataTrigger
Binding="{Binding is_roll_target_dirty}"
Value="True">
<Setter
Property="BorderBrush"
Value="Red" />
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -415,14 +407,15 @@
Text="{Binding roll_target, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource RollTargetContextMenu}" />
ContextMenu="{StaticResource RollTargetContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding roll_target, Converter={StaticResource DoubleTextConverter}}"
Margin="2" />
Text="{Binding roll_target, Converter={StaticResource DoubleTextConverter}}"
Margin="2" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -438,9 +431,7 @@
<DataTrigger
Binding="{Binding is_riskless_rate_dirty}"
Value="True">
<Setter
Property="BorderBrush"
Value="Red" />
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -451,12 +442,14 @@
Text="{Binding riskless_rate, Converter={StaticResource RisklessRateConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource RisklessRateContextMenu}" />
ContextMenu="{StaticResource RisklessRateContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding riskless_rate, Converter={StaticResource RisklessRateConverter}}" />
<TextBox
Text="{Binding riskless_rate, Converter={StaticResource RisklessRateConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -472,9 +465,7 @@
<DataTrigger
Binding="{Binding is_settlement_price_dirty}"
Value="True">
<Setter
Property="BorderBrush"
Value="Red" />
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -485,12 +476,14 @@
Text="{Binding settlement_price, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource SettlementPriceContextMenu}" />
ContextMenu="{StaticResource SettlementPriceContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding settlement_price, Converter={StaticResource DoubleTextConverter}}" />
<TextBox
Text="{Binding settlement_price, Converter={StaticResource DoubleTextConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -507,9 +500,7 @@
<DataTrigger
Binding="{Binding is_delta_beta_dirty}"
Value="True">
<Setter
Property="BorderBrush"
Value="Red" />
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -520,12 +511,14 @@
Text="{Binding delta_beta, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource DeltaBetaContextMenu}" />
ContextMenu="{StaticResource DeltaBetaContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding delta_beta, Converter={StaticResource DoubleTextConverter}}" />
<TextBox
Text="{Binding delta_beta, Converter={StaticResource DoubleTextConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
@@ -542,9 +535,7 @@
<DataTrigger
Binding="{Binding is_vega_beta_dirty}"
Value="True">
<Setter
Property="BorderBrush"
Value="Red" />
<Setter Property="Foreground" Value="{u:BTSColor UI.input_dirty_foreground}" />
</DataTrigger>
</Style.Triggers>
</Style>
@@ -552,10 +543,11 @@
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding vega_beta, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource VegaBetaContextMenu}" />
Text="{Binding vega_beta, Converter={StaticResource DoubleTextConverter}}"
VerticalAlignment="Center"
Margin="2,0,2,0"
ContextMenu="{StaticResource VegaBetaContextMenu}"
Style="{StaticResource TextBlockCellElement}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
@@ -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;
@@ -51,6 +53,10 @@ namespace mbts.ui.shell {
} :
null);
public static IValueConverter expiration_is_defined_converter =>
ValueConverter.create<SecurityExpiration?, bool>(exp =>
exp is not null && exp.expiration != default);
void _grid_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) {
Disposable.dispose(ref _context_menu_op);
if (e.OriginalSource is not FrameworkElement el || DataContext is not AnalyticsExpirationParametersVM vm) {
@@ -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();
@@ -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);
}
@@ -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(
@@ -32,7 +32,7 @@ namespace mbts.spark {
public SingleVolSurfaceRequestCalculator(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}");
_vs_id = vs_id;
_symbols = new HashSet<string>(symbols);
_bb = threaded_bb;
@@ -89,8 +89,8 @@ 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 time: " +
$"{_timer.lastIntervalTime}");
_bb.post(SparkTags.VolSurfaceCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(_last_output, effective_req.request_id));
}
@@ -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;
@@ -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,108 @@
using System;
using System.Collections.Generic;
using mbts.analytics;
using bts.api;
using bts.collections;
using System.Linq;
using bts.api.orderman;
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.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 void _publish_data() {
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag, null);
IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos =
_bb.read_state(SparkTags.AllInterpGreeks, null);
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;
@@ -46,7 +50,9 @@ namespace mbts.spark {
if (startup_data.vol_curve_x_axis == null) {
var x_axis = BTSOxyGraphAxisDouble.Default;
x_axis.name = "Strike";
x_axis.axis_line_color = BTSColors.Graph.axis_line;
x_axis.major_grid_line_color = BTSColors.Graph.grid_line;
x_axis.tick_line_color = BTSColors.Graph.tick_line;
x_axis.position = AxisPosition.BottomLeftToRight;
x_axis.minimum = 0;
x_axis.maximum = 2000;
@@ -58,7 +64,9 @@ namespace mbts.spark {
if (startup_data.vol_curve_y_axis == null) {
var y_axis = BTSOxyGraphAxisDouble.Default;
y_axis.name = "Volatility";
y_axis.axis_line_color = BTSColors.Graph.axis_line;
y_axis.major_grid_line_color = BTSColors.Graph.grid_line;
y_axis.tick_line_color = BTSColors.Graph.tick_line;
y_axis.position = AxisPosition.LeftBottomToTop;
y_axis.minimum = 0;
y_axis.maximum = 1;
@@ -78,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,
@@ -113,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)))
@@ -181,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;
_is_dirty.Changed += (o, n) => {
Log.assert(o != n);
// TODO: John, this was logged too many times
// 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}";
}
}
@@ -73,12 +73,12 @@ namespace mbts.spark.VolSurface {
var view = new TextBlock {
HorizontalAlignment = HorizontalAlignment.Right,
Margin = _cell_padding,
Foreground = Brushes.Blue,
Foreground = BTSColors.VolSurfaceGrid.readonly_column_foreground
};
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;
@@ -25,17 +30,20 @@ namespace mbts.spark.VolSurface {
class VolSurfaceGridVM : DisposableViewModelBase {
readonly VolSurfaceVM _parent;
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) {
Log.notnull(hidden_columns);
Log.notnull(hidden_expirations);
_parent = vm;
this.model = model;
_columns_model = new(create_exp_column_view());
@@ -63,16 +71,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);
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 +115,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 +141,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 +165,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 +243,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 +268,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 +349,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);
@@ -8,6 +8,8 @@ namespace mbts.spark {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core.Disposables;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -79,6 +81,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;
@@ -4,22 +4,19 @@ namespace mbts.spark.VolSurface {
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 DeferrableAction _request_calculate;
VolSurfaceRowContextMenuVM? _context_menu;
bool _is_selected;
@@ -27,6 +24,7 @@ namespace mbts.spark.VolSurface {
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp) {
_parent = parent;
_exp = exp;
_request_calculate = new(() => request_calculate?.Invoke());
_dispose = Disposable.Create(() => {
_cells.Values.DisposeAll();
_cells.Clear();
@@ -36,6 +34,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;
@@ -52,12 +51,14 @@ namespace mbts.spark.VolSurface {
public VolSurfaceRowContextMenuVM context_menu =>
_context_menu ??= new(_parent, this);
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 +80,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 +99,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));
}
}
@@ -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;
@@ -539,7 +540,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 +551,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();
@@ -77,8 +77,8 @@
public IRaiseCanExecuteChangedCommand vol_shift { get; }
IEnumerable<IRaiseCanExecuteChangedCommand> _commands =>
ReflectionHelper.property_delegates_of_type<VolSurfaceMacroActionsVM, IRaiseCanExecuteChangedCommand>()
.Select(g => g(this));
ReflectionHelper.property_getters<VolSurfaceMacroActionsVM, IRaiseCanExecuteChangedCommand>()
.Select(pg => pg.get(this));
public void selection_changed(IEnumerable<VolSurfaceDisplayGridExpiration> selected) {
_selected_exps = selected.ToList();
@@ -10,6 +10,7 @@ namespace mbts.spark {
using mbts.ui.controls;
using mbts.ui.shell;
using Spark.Core.Disposables;
using Spark.ViewModel.Commands;
using System;
@@ -60,6 +61,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 +187,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);
}
}
@@ -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 {
@@ -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
@@ -680,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
}
}
@@ -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() {
+2 -1
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;
@@ -142,6 +143,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;
+24 -7
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);
@@ -262,9 +279,9 @@ namespace mbts.ui {
public static ImageSource vol_surface => Math;
public static ImageSource Wrench { get { return load_resource_and_freeze("Wrench"); } }
public static ImageSource RightArrow => create_icon(
ArrowGeometryKey, null, new(Brushes.Black, 3));
ArrowGeometryKey, null, new(BTSColors.UI.icon_foreground, 3));
public static ImageSource DownArrow => create_icon(
ArrowGeometryKey, null, new(Brushes.Black, 3), new RotateTransform(90, 0.5, 0.5));
ArrowGeometryKey, null, new(BTSColors.UI.icon_foreground, 3), new RotateTransform(90, 0.5, 0.5));
public static ImageSource Factory { get { return (load_resource_and_freeze("Factory")); } }
public static ImageSource DotsVertical { get { return (load_resource_and_freeze("DotsVertical")); } }
public static ImageSource HandPointingOverDocument => load_resource_and_freeze("HandPointingOverDocument");
@@ -272,10 +289,10 @@ namespace mbts.ui {
public static ImageSource FloppyDisk => create_icon("FloppyDisk");
static Brush _default_chevron_brush => null;
static Pen _default_chevron_pen { get; } = new Pen(Brushes.Black, 3).frozen();
static Pen _default_chevron_pen { get; } = new Pen(BTSColors.UI.default_text, 3).frozen();
public static ImageSource create_icon(string geometric_path_key) =>
create_icon(geometric_path_key, Brushes.Black);
create_icon(geometric_path_key, BTSColors.UI.icon_foreground);
public static ImageSource create_icon(string geometric_path_key, Brush brush, Pen pen = null, Transform transform = null) {
var rv = new DrawingImage(create_drawing(geometric_path_key, brush, pen, transform));
+88 -61
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>
@@ -503,7 +515,7 @@
<DrawingGroup.Children>
<GeometryDrawing Geometry="M1540.22,2082.07L1546.95,2102.78 1568.73,2102.78 1551.11,2115.58 1557.84,2136.29 1540.22,2123.49 1522.6,2136.29 1529.33,2115.58 1511.71,2102.78 1533.49,2102.78 1540.22,2082.07z">
<GeometryDrawing.Pen>
<Pen Thickness="3" Brush="Black" />
<Pen Thickness="3" Brush="{mbts:BTSColor UI.icon_foreground}" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<SolidColorBrush Color="Transparent" />
@@ -525,7 +537,7 @@ M 52.868601,3.8869994 C51.026201,3.8869996 49.534,5.3791947 49.534,7.2190399 49.
M5.2010002,14.812L5.2010002,43.573997 56.098,43.573997 56.098,14.812z
M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
<Pen Thickness="1" Brush="{mbts:BTSColor UI.icon_foreground}" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -537,14 +549,14 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="
M 32.868601,3.8869994 C31.026201,3.8869996 29.534,5.3791947 29.534,7.2190399 29.534,9.0588851 31.026201,10.553 32.868601,10.553 34.7085,10.553 36.202,9.0588851 36.202,7.2190399 36.202,5.3791947 34.7085,3.8869996 32.868601,3.8869994z
M 42.868601,3.8869994 C41.026201,3.8869996 39.534,5.3791947 39.534,7.2190399 39.534,9.0588851 41.026201,10.553 42.868601,10.553 44.7085,10.553 46.202,9.0588851 46.202,7.2190399 46.202,5.3791947 44.7085,3.8869996 42.868601,3.8869994z
M 52.868601,3.8869994 C51.026201,3.8869996 49.534,5.3791947 49.534,7.2190399 49.534,9.0588851 51.026201,10.553 52.868601,10.553 54.7085,10.553 56.202,9.0588851 56.202,7.2190399 56.202,5.3791947 54.7085,3.8869996 52.868601,3.8869994z
M5.2010002,14.812L5.2010002,43.573997 56.098,43.573997 56.098,14.812z
M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
<Pen Thickness="1" Brush="{mbts:BTSColor UI.icon_foreground}" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -556,7 +568,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="F1M-198.67,4280.19C-197.609,4277.78 -197.035,4275.17 -197.035,4272.46 -197.035,4267.33 -199.032,4262.51 -202.665,4258.88 -206.286,4255.25 -211.107,4253.26 -216.235,4253.26 -221.365,4253.26 -226.186,4255.25 -229.811,4258.88 -232.498,4261.57 -234.282,4264.92 -235.032,4268.56L-225.835,4278.86 -252.503,4278.86 -250.373,4251.38 -241.577,4261.22C-240.214,4258.14 -238.303,4255.3 -235.844,4252.85 -230.61,4247.61 -223.644,4244.72 -216.235,4244.72 -208.822,4244.72 -201.861,4247.61 -196.627,4252.85 -191.39,4258.08 -188.503,4265.05 -188.503,4272.46 -188.503,4275.11 -188.894,4277.71 -189.617,4280.19L-198.67,4280.19z">
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="F1M-198.67,4280.19C-197.609,4277.78 -197.035,4275.17 -197.035,4272.46 -197.035,4267.33 -199.032,4262.51 -202.665,4258.88 -206.286,4255.25 -211.107,4253.26 -216.235,4253.26 -221.365,4253.26 -226.186,4255.25 -229.811,4258.88 -232.498,4261.57 -234.282,4264.92 -235.032,4268.56L-225.835,4278.86 -252.503,4278.86 -250.373,4251.38 -241.577,4261.22C-240.214,4258.14 -238.303,4255.3 -235.844,4252.85 -230.61,4247.61 -223.644,4244.72 -216.235,4244.72 -208.822,4244.72 -201.861,4247.61 -196.627,4252.85 -191.39,4258.08 -188.503,4265.05 -188.503,4272.46 -188.503,4275.11 -188.894,4277.71 -189.617,4280.19L-198.67,4280.19z">
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
@@ -567,7 +579,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="m 430.4375 225.625 c -1.92352 0.0276 -3.82501 0.82944 -5.1875 2.1875 L 330.0625 323 288 280.9375 c -1.38915 -1.39371 -3.34472 -2.2056 -5.3125 -2.2056 -1.96778 0 -3.92335 0.81189 -5.3125 2.2056 l -23.9375 23.9375 c -1.3844 1.38804 -2.18997 3.33646 -2.18997 5.29687 0 1.96042 0.80557 3.90884 2.18997 5.29688 l 71.3125 71.3125 c 1.38804 1.3844 3.33646 2.18997 5.29687 2.18997 1.96042 0 3.90884 -0.80557 5.29688 -2.18997 l 4.34375 -4.34375 19.59375 -19.59375 100.5 -100.5 c 1.3844 -1.38804 2.18997 -3.33646 2.18997 -5.29688 0 -1.96041 -0.80557 -3.90883 -2.18997 -5.29687 l -23.9375 -23.9375 c -1.41413 -1.40955 -3.40982 -2.21706 -5.40625 -2.1875 z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="m 430.4375 225.625 c -1.92352 0.0276 -3.82501 0.82944 -5.1875 2.1875 L 330.0625 323 288 280.9375 c -1.38915 -1.39371 -3.34472 -2.2056 -5.3125 -2.2056 -1.96778 0 -3.92335 0.81189 -5.3125 2.2056 l -23.9375 23.9375 c -1.3844 1.38804 -2.18997 3.33646 -2.18997 5.29687 0 1.96042 0.80557 3.90884 2.18997 5.29688 l 71.3125 71.3125 c 1.38804 1.3844 3.33646 2.18997 5.29687 2.18997 1.96042 0 3.90884 -0.80557 5.29688 -2.18997 l 4.34375 -4.34375 19.59375 -19.59375 100.5 -100.5 c 1.3844 -1.38804 2.18997 -3.33646 2.18997 -5.29688 0 -1.96041 -0.80557 -3.90883 -2.18997 -5.29687 l -23.9375 -23.9375 c -1.41413 -1.40955 -3.40982 -2.21706 -5.40625 -2.1875 z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -577,7 +589,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M37.063741,37.405188L33.240929,50.771959 33.345182,50.829412C33.743126,51.061163,34.109612,51.337603,34.437061,51.651468L34.468327,51.682901 44.707291,44.003126 44.641921,43.880777C44.347813,43.295671,44.183001,42.63962,44.183001,41.946699L44.185978,41.833834z M24.591099,31.982382L10.519234,39.24213 10.504738,39.424831C10.479679,39.661278,10.436295,39.892527,10.375957,40.117252L10.337856,40.24584 10.451912,40.307037C10.495525,40.332783,10.538376,40.360497,10.58033,40.390192L26.361021,51.547636 26.448517,51.467699C27.339916,50.69132,28.485014,50.17779,29.750544,50.054647L30.015249,50.035364 34.145618,35.590685 29.192999,32.51112C29.04261,32.417593,28.908485,32.31176,28.79232,32.197899L28.716364,32.117622 28.685419,32.139805C28.132467,32.49791 27.465902,32.706999 26.748449,32.706999 26.030621,32.706999 25.363836,32.49791 24.810755,32.139805z M28.780092,24.040133L28.448082,26.492748 28.685419,26.630915C29.607006,27.227547 30.212999,28.238197 30.212999,29.385599 30.212999,29.614918 30.188759,29.838802 30.142603,30.055031L30.080193,30.287708 30.111809,30.297695C30.268356,30.353413,30.424189,30.42791,30.574446,30.521399L34.837221,33.172018 35.327473,31.457518C35.410911,31.165865,35.525216,30.895145,35.662153,30.652515L35.756067,30.497255 35.710442,30.474535C33.991901,29.570229,32.844,27.933699,32.844,26.065052L32.845716,26.006275z M53.770291,20.119031L44.688253,25.319921 44.720247,25.537884C44.740581,25.711212 44.750998,25.887079 44.750998,26.065052 44.750998,28.912514 42.084275,31.221001 38.797749,31.221001L38.67025,31.218208 38.672958,31.305962C38.671358,31.615515,38.625373,31.944508,38.53014,32.277802L37.755445,34.986586 44.966217,39.470285 44.977812,39.451984C45.814164,38.264851 47.230547,37.483999 48.837201,37.483999 49.078189,37.483999 49.314904,37.501568 49.546028,37.535442L49.656656,37.554392 54.505396,20.597714 54.402527,20.541037C54.290171,20.475618,54.180146,20.40689,54.0726,20.334991z M9.6452522,14.786494L9.5807686,14.857854C9.0475883,15.420187,8.3894768,15.871748,7.6484566,16.172216L7.5149994,16.222663 7.5149994,34.615192 7.5420074,34.625397C8.4930382,35.010883,9.2904701,35.676405,9.8216705,36.51416L9.9140587,36.668142 23.306246,29.758877 23.300888,29.725224C23.289059,29.61356 23.282999,29.500257 23.282999,29.385599 23.282999,27.893976 24.307062,26.633465 25.717823,26.213213L26.001564,26.143343 26.439465,22.908201z M25.594164,6.962996L10.989891,11.757046 10.982574,11.849259C10.963779,12.026639,10.935814,12.201424,10.899147,12.373173L10.896881,12.382581 26.821791,20.083639 28.28179,9.2974239 28.12504,9.2381914C27.074772,8.8124379,26.207533,8.0535914,25.667171,7.0996943z M34.637348,6.8116302L34.599276,6.8923512C33.894737,8.2938632,32.502018,9.3174334,30.841724,9.5606082L30.738474,9.5731842 29.162455,21.21555 33.69524,23.407539 33.705906,23.391473C34.750082,21.903184 36.639784,20.909001 38.797749,20.909001 40.441011,20.909001 41.929324,21.486123 43.006821,22.41919L43.020344,22.431472 51.760321,17.426437 51.737371,17.368303C51.521266,16.762437 51.403998,16.112631 51.403998,15.436536 51.403998,14.921415 51.472072,14.421554 51.600069,13.944759L51.681425,13.670171z M30.077951,0C32.588049,0,34.668222,1.7676992,35.036192,4.0752702L35.064489,4.2886443 52.961684,11.489869 53.021899,11.423252C54.161165,10.222043 55.804663,9.4679997 57.631995,9.4679992 61.070987,9.4680003 63.858998,12.139764 63.858998,15.436536 63.858998,18.733305 61.070987,21.405001 57.631995,21.405001L57.456546,21.40075 52.402265,39.080206 52.428996,39.108406C53.093061,39.879898 53.492,40.868822 53.492,41.946699 53.492,44.410421 51.407745,46.407999 48.837201,46.407999 48.033874,46.407999 47.278115,46.212923 46.618644,45.869505L46.615751,45.867823 35.872695,53.925859 35.950705,54.189234C36.069708,54.632677 36.132998,55.097586 36.132998,55.5767 36.132998,58.641745 33.540598,61.126001 30.342699,61.126001 27.144098,61.126001 24.550999,58.641745 24.550999,55.5767 24.550999,55.193407 24.591516,54.819208 24.668669,54.457815L24.711109,54.281561 8.605793,42.894129C8.5577812,42.860109,8.5120659,42.824305,8.4686737,42.786901L8.4553757,42.77429 8.3729744,42.833338C7.5921974,43.338846 6.6510086,43.634001 5.6380043,43.634001 2.935627,43.634001 0.74499893,41.535135 0.74499893,38.9454 0.74499893,36.841322 2.1911545,35.061296 4.1829567,34.467758L4.2889977,34.438908 4.2889977,16.456114 4.1295319,16.420828C1.7547989,15.835048 0,13.768469 0,11.309781 0,8.3958501 2.4649048,6.0340006 5.5053978,6.0339997 7.5959015,6.0340006 9.4142189,7.1503434 10.346516,8.7949429L10.425873,8.9434187 25.113558,4.1221313 25.119768,4.0752702C25.48749,1.7676992,27.566404,0,30.077951,0z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M37.063741,37.405188L33.240929,50.771959 33.345182,50.829412C33.743126,51.061163,34.109612,51.337603,34.437061,51.651468L34.468327,51.682901 44.707291,44.003126 44.641921,43.880777C44.347813,43.295671,44.183001,42.63962,44.183001,41.946699L44.185978,41.833834z M24.591099,31.982382L10.519234,39.24213 10.504738,39.424831C10.479679,39.661278,10.436295,39.892527,10.375957,40.117252L10.337856,40.24584 10.451912,40.307037C10.495525,40.332783,10.538376,40.360497,10.58033,40.390192L26.361021,51.547636 26.448517,51.467699C27.339916,50.69132,28.485014,50.17779,29.750544,50.054647L30.015249,50.035364 34.145618,35.590685 29.192999,32.51112C29.04261,32.417593,28.908485,32.31176,28.79232,32.197899L28.716364,32.117622 28.685419,32.139805C28.132467,32.49791 27.465902,32.706999 26.748449,32.706999 26.030621,32.706999 25.363836,32.49791 24.810755,32.139805z M28.780092,24.040133L28.448082,26.492748 28.685419,26.630915C29.607006,27.227547 30.212999,28.238197 30.212999,29.385599 30.212999,29.614918 30.188759,29.838802 30.142603,30.055031L30.080193,30.287708 30.111809,30.297695C30.268356,30.353413,30.424189,30.42791,30.574446,30.521399L34.837221,33.172018 35.327473,31.457518C35.410911,31.165865,35.525216,30.895145,35.662153,30.652515L35.756067,30.497255 35.710442,30.474535C33.991901,29.570229,32.844,27.933699,32.844,26.065052L32.845716,26.006275z M53.770291,20.119031L44.688253,25.319921 44.720247,25.537884C44.740581,25.711212 44.750998,25.887079 44.750998,26.065052 44.750998,28.912514 42.084275,31.221001 38.797749,31.221001L38.67025,31.218208 38.672958,31.305962C38.671358,31.615515,38.625373,31.944508,38.53014,32.277802L37.755445,34.986586 44.966217,39.470285 44.977812,39.451984C45.814164,38.264851 47.230547,37.483999 48.837201,37.483999 49.078189,37.483999 49.314904,37.501568 49.546028,37.535442L49.656656,37.554392 54.505396,20.597714 54.402527,20.541037C54.290171,20.475618,54.180146,20.40689,54.0726,20.334991z M9.6452522,14.786494L9.5807686,14.857854C9.0475883,15.420187,8.3894768,15.871748,7.6484566,16.172216L7.5149994,16.222663 7.5149994,34.615192 7.5420074,34.625397C8.4930382,35.010883,9.2904701,35.676405,9.8216705,36.51416L9.9140587,36.668142 23.306246,29.758877 23.300888,29.725224C23.289059,29.61356 23.282999,29.500257 23.282999,29.385599 23.282999,27.893976 24.307062,26.633465 25.717823,26.213213L26.001564,26.143343 26.439465,22.908201z M25.594164,6.962996L10.989891,11.757046 10.982574,11.849259C10.963779,12.026639,10.935814,12.201424,10.899147,12.373173L10.896881,12.382581 26.821791,20.083639 28.28179,9.2974239 28.12504,9.2381914C27.074772,8.8124379,26.207533,8.0535914,25.667171,7.0996943z M34.637348,6.8116302L34.599276,6.8923512C33.894737,8.2938632,32.502018,9.3174334,30.841724,9.5606082L30.738474,9.5731842 29.162455,21.21555 33.69524,23.407539 33.705906,23.391473C34.750082,21.903184 36.639784,20.909001 38.797749,20.909001 40.441011,20.909001 41.929324,21.486123 43.006821,22.41919L43.020344,22.431472 51.760321,17.426437 51.737371,17.368303C51.521266,16.762437 51.403998,16.112631 51.403998,15.436536 51.403998,14.921415 51.472072,14.421554 51.600069,13.944759L51.681425,13.670171z M30.077951,0C32.588049,0,34.668222,1.7676992,35.036192,4.0752702L35.064489,4.2886443 52.961684,11.489869 53.021899,11.423252C54.161165,10.222043 55.804663,9.4679997 57.631995,9.4679992 61.070987,9.4680003 63.858998,12.139764 63.858998,15.436536 63.858998,18.733305 61.070987,21.405001 57.631995,21.405001L57.456546,21.40075 52.402265,39.080206 52.428996,39.108406C53.093061,39.879898 53.492,40.868822 53.492,41.946699 53.492,44.410421 51.407745,46.407999 48.837201,46.407999 48.033874,46.407999 47.278115,46.212923 46.618644,45.869505L46.615751,45.867823 35.872695,53.925859 35.950705,54.189234C36.069708,54.632677 36.132998,55.097586 36.132998,55.5767 36.132998,58.641745 33.540598,61.126001 30.342699,61.126001 27.144098,61.126001 24.550999,58.641745 24.550999,55.5767 24.550999,55.193407 24.591516,54.819208 24.668669,54.457815L24.711109,54.281561 8.605793,42.894129C8.5577812,42.860109,8.5120659,42.824305,8.4686737,42.786901L8.4553757,42.77429 8.3729744,42.833338C7.5921974,43.338846 6.6510086,43.634001 5.6380043,43.634001 2.935627,43.634001 0.74499893,41.535135 0.74499893,38.9454 0.74499893,36.841322 2.1911545,35.061296 4.1829567,34.467758L4.2889977,34.438908 4.2889977,16.456114 4.1295319,16.420828C1.7547989,15.835048 0,13.768469 0,11.309781 0,8.3958501 2.4649048,6.0340006 5.5053978,6.0339997 7.5959015,6.0340006 9.4142189,7.1503434 10.346516,8.7949429L10.425873,8.9434187 25.113558,4.1221313 25.119768,4.0752702C25.48749,1.7676992,27.566404,0,30.077951,0z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -587,7 +599,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M3.3500004,28.921999L16.410999,28.921999 16.410999,29.396999 16.410999,32.959999 16.410999,38.421999 25.514,38.421999 25.514,42.458 25.495,42.458 25.495,47.602999 34.698,47.602999 34.698,51.640999 25.495,51.640999 25.495,51.671 21.458,51.671 21.458,42.458 16.410999,42.458 12.453001,42.458 12.374001,42.458 12.374001,32.959999 3.3500004,32.959999z M54.794001,20.069L59.39695,23.324425 64,26.580851 59.39695,29.837375 54.794001,33.094 54.794001,30.650999 33.583,30.650999 33.583,22.698998 54.794001,22.698998z M14.7162,0.3409996L24.9141,0.3409996C25.97705,0.3409996,26.865667,1.1746159,27.071213,2.1986313L27.086924,2.3030968 27.124624,2.3546333C27.940995,3.4717503 29.881758,6.1571293 30.565413,7.3636971 30.565413,7.3636971 34.096944,13.338465 34.877442,15.061705L34.890623,15.092 40.170837,15.092C41.324416,15.092 42.257998,16.195044 42.257998,17.396999 42.257998,18.600256 41.324416,19.702 40.170837,19.702L33.450961,19.702C32.370606,19.702,31.482553,18.733671,31.375766,17.621177L31.37387,17.581701 29.230907,13.962816 22.261399,18.123934 26.40747,22.377669C27.150276,23.141019,27.230264,24.306093,26.654201,25.155003L26.643522,25.169146 26.636381,25.286112C26.628118,25.36014,26.616044,25.434453,26.600019,25.508835L24.851152,33.574084C24.592043,34.765377 23.418798,35.519273 22.228654,35.261475 21.03861,35.004976 20.280681,33.829283 20.53979,32.64039L22.288656,24.57524 22.309726,24.495231 17.488041,19.548167 17.480572,19.539678 17.253374,19.529043C16.366886,19.445599,15.589428,19.018761,15.078829,18.394262L15.074381,18.388281 12.282196,21.977999 12.216562,21.901865 12.1124,21.994324C11.720974,22.306216,11.1803,22.469,10.5833,22.469L2.1626606,22.469C0.96679688,22.469 0,21.817864 0,20.624998 0,19.428434 0.96679688,18.779999 2.1626606,18.779999L9.5252495,18.779999 9.3850002,18.617311 14.600033,11.548028C14.600033,11.548028,15.127697,11.125361,15.599133,10.798839L15.614553,10.78836 15.604,10.776026 24.149192,5.6036739 23.803184,4.9510002 14.7162,4.9510002C13.500099,4.9510002 12.514999,3.8623199 12.514999,2.6472778 12.514999,1.4298038 13.500099,0.3409996 14.7162,0.3409996z M34.6091,0C36.912666,0 38.780001,1.8671761 38.780001,4.1704998 38.780001,6.4739532 36.912666,8.3409996 34.6091,8.3409996 32.304335,8.3409996 30.437,6.4739532 30.437,4.1704998 30.437,1.8671761 32.304335,0 34.6091,0z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M3.3500004,28.921999L16.410999,28.921999 16.410999,29.396999 16.410999,32.959999 16.410999,38.421999 25.514,38.421999 25.514,42.458 25.495,42.458 25.495,47.602999 34.698,47.602999 34.698,51.640999 25.495,51.640999 25.495,51.671 21.458,51.671 21.458,42.458 16.410999,42.458 12.453001,42.458 12.374001,42.458 12.374001,32.959999 3.3500004,32.959999z M54.794001,20.069L59.39695,23.324425 64,26.580851 59.39695,29.837375 54.794001,33.094 54.794001,30.650999 33.583,30.650999 33.583,22.698998 54.794001,22.698998z M14.7162,0.3409996L24.9141,0.3409996C25.97705,0.3409996,26.865667,1.1746159,27.071213,2.1986313L27.086924,2.3030968 27.124624,2.3546333C27.940995,3.4717503 29.881758,6.1571293 30.565413,7.3636971 30.565413,7.3636971 34.096944,13.338465 34.877442,15.061705L34.890623,15.092 40.170837,15.092C41.324416,15.092 42.257998,16.195044 42.257998,17.396999 42.257998,18.600256 41.324416,19.702 40.170837,19.702L33.450961,19.702C32.370606,19.702,31.482553,18.733671,31.375766,17.621177L31.37387,17.581701 29.230907,13.962816 22.261399,18.123934 26.40747,22.377669C27.150276,23.141019,27.230264,24.306093,26.654201,25.155003L26.643522,25.169146 26.636381,25.286112C26.628118,25.36014,26.616044,25.434453,26.600019,25.508835L24.851152,33.574084C24.592043,34.765377 23.418798,35.519273 22.228654,35.261475 21.03861,35.004976 20.280681,33.829283 20.53979,32.64039L22.288656,24.57524 22.309726,24.495231 17.488041,19.548167 17.480572,19.539678 17.253374,19.529043C16.366886,19.445599,15.589428,19.018761,15.078829,18.394262L15.074381,18.388281 12.282196,21.977999 12.216562,21.901865 12.1124,21.994324C11.720974,22.306216,11.1803,22.469,10.5833,22.469L2.1626606,22.469C0.96679688,22.469 0,21.817864 0,20.624998 0,19.428434 0.96679688,18.779999 2.1626606,18.779999L9.5252495,18.779999 9.3850002,18.617311 14.600033,11.548028C14.600033,11.548028,15.127697,11.125361,15.599133,10.798839L15.614553,10.78836 15.604,10.776026 24.149192,5.6036739 23.803184,4.9510002 14.7162,4.9510002C13.500099,4.9510002 12.514999,3.8623199 12.514999,2.6472778 12.514999,1.4298038 13.500099,0.3409996 14.7162,0.3409996z M34.6091,0C36.912666,0 38.780001,1.8671761 38.780001,4.1704998 38.780001,6.4739532 36.912666,8.3409996 34.6091,8.3409996 32.304335,8.3409996 30.437,6.4739532 30.437,4.1704998 30.437,1.8671761 32.304335,0 34.6091,0z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -597,7 +609,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M55.859353,59.282085C56.00929,59.285538,56.143475,59.392647,56.172002,59.547607L55.789193,65.810493C55.674588,66.466682,55.295579,65.802589,55.295579,65.802589L54.473962,64.431519C51.7422,68.327347 47.221295,70.881996 42.101273,70.881996 36.226228,70.881996 31.13625,67.53186 28.632329,62.638653 28.382323,62.158161 28.059378,61.686871 27.969502,61.146481 27.66084,59.337914 29.858891,59.01112 30.614098,59.864002 30.926646,60.216797 31.299044,60.827484 31.563321,61.306576 33.567227,65.081306 37.533479,67.649055 42.101273,67.649055 46.122369,67.649055 49.669251,65.651596 51.828099,62.602253L50.338567,62.323559C50.338567,62.323559,49.473847,62.210258,50.067658,61.849567L55.794392,59.287113C55.816193,59.283215,55.837933,59.28159,55.859353,59.282085z M40.764077,45.717999L43.468282,45.717999 43.468282,47.945969C44.940877,48.043633,45.939504,48.464172,46.57486,48.781933L46.850839,48.919876 45.9889,51.849625 45.591931,51.649216C44.965676,51.336708 43.994244,50.849598 42.497052,50.849598 41.307038,50.849598 40.589589,51.340611 40.589589,52.155632 40.589589,52.883549 41.269242,53.390064 43.278195,54.159683 46.150391,55.237812 47.377004,56.67535 47.377004,58.964413 47.377004,61.232674 45.806714,62.99192 43.348592,63.516731L43.348592,66.047997 40.616887,66.047997 40.616887,63.706738C39.216088,63.607735,37.791692,63.200325,36.902394,62.633808L36.672002,62.482704 37.5286,59.474827 37.945279,59.724831C38.568932,60.09864 39.856541,60.726257 41.489224,60.726257 42.825127,60.726257 43.684468,60.136341 43.684468,59.230118 43.684468,58.503498 43.274297,57.873283 41.294038,57.157166 39.122395,56.362843 36.898515,55.19091 36.898515,52.516441 36.898515,50.313221 38.372449,48.646427 40.764077,48.112602z M42.010037,40.691002C47.882275,40.691002 52.973134,44.039906 55.477063,48.93552 55.725755,49.413418 56.051247,49.888626 56.139745,50.42767 56.447038,52.237579 54.249299,52.563072 53.493916,51.708893 53.181527,51.354805 52.809136,50.742821 52.546143,50.264954 50.543599,46.492958 46.577512,43.922722 42.010037,43.922722 37.98795,43.922722 34.44249,45.919983 32.28362,48.96946L33.773167,49.248013C33.773167,49.248013,34.636472,49.363979,34.044091,49.72456L28.316394,52.28698C28.141854,52.318275,27.969985,52.199879,27.940001,52.026585L28.322789,45.767639C28.43744,45.104939,28.815036,45.774227,28.815036,45.774227L29.632752,47.140041C32.366937,43.245621,36.88775,40.691002,42.010037,40.691002z M21.804616,0L44.531026,0C48.257512,0,51.280999,3.0222199,51.280999,6.7460904L51.280999,38.274937 50.537671,37.898952C49.215222,37.269638,47.812577,36.778652,46.348977,36.444992L45.879922,36.344151 45.879922,6.7460904C45.879922,6.0039101,45.273122,5.3972201,44.531026,5.3972206L22.158713,5.3972206 21.794215,17.167999C21.794215,17.167999,21.325417,24.9519,12.747249,24.0117L5.3957184,23.8125 5.3957184,57.253899C5.3957189,57.998699,6.0024966,58.602901,6.7473038,58.602901L21.820422,58.602901 21.860354,58.884754C22.115357,60.533066,22.574946,62.114872,23.212727,63.604115L23.393945,64 6.7473038,64C3.0207885,64,2.9307267E-07,60.980499,0,57.253899L0,23.0051 2.6718498,20.1863 2.6743804,20.1094 16.677035,5.3972206 16.637936,5.3972206 12.89565,9.3464394 16.817534,5.1914101 16.872334,5.1914101 16.997233,5.0612788 17.007634,5.0612788z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M55.859353,59.282085C56.00929,59.285538,56.143475,59.392647,56.172002,59.547607L55.789193,65.810493C55.674588,66.466682,55.295579,65.802589,55.295579,65.802589L54.473962,64.431519C51.7422,68.327347 47.221295,70.881996 42.101273,70.881996 36.226228,70.881996 31.13625,67.53186 28.632329,62.638653 28.382323,62.158161 28.059378,61.686871 27.969502,61.146481 27.66084,59.337914 29.858891,59.01112 30.614098,59.864002 30.926646,60.216797 31.299044,60.827484 31.563321,61.306576 33.567227,65.081306 37.533479,67.649055 42.101273,67.649055 46.122369,67.649055 49.669251,65.651596 51.828099,62.602253L50.338567,62.323559C50.338567,62.323559,49.473847,62.210258,50.067658,61.849567L55.794392,59.287113C55.816193,59.283215,55.837933,59.28159,55.859353,59.282085z M40.764077,45.717999L43.468282,45.717999 43.468282,47.945969C44.940877,48.043633,45.939504,48.464172,46.57486,48.781933L46.850839,48.919876 45.9889,51.849625 45.591931,51.649216C44.965676,51.336708 43.994244,50.849598 42.497052,50.849598 41.307038,50.849598 40.589589,51.340611 40.589589,52.155632 40.589589,52.883549 41.269242,53.390064 43.278195,54.159683 46.150391,55.237812 47.377004,56.67535 47.377004,58.964413 47.377004,61.232674 45.806714,62.99192 43.348592,63.516731L43.348592,66.047997 40.616887,66.047997 40.616887,63.706738C39.216088,63.607735,37.791692,63.200325,36.902394,62.633808L36.672002,62.482704 37.5286,59.474827 37.945279,59.724831C38.568932,60.09864 39.856541,60.726257 41.489224,60.726257 42.825127,60.726257 43.684468,60.136341 43.684468,59.230118 43.684468,58.503498 43.274297,57.873283 41.294038,57.157166 39.122395,56.362843 36.898515,55.19091 36.898515,52.516441 36.898515,50.313221 38.372449,48.646427 40.764077,48.112602z M42.010037,40.691002C47.882275,40.691002 52.973134,44.039906 55.477063,48.93552 55.725755,49.413418 56.051247,49.888626 56.139745,50.42767 56.447038,52.237579 54.249299,52.563072 53.493916,51.708893 53.181527,51.354805 52.809136,50.742821 52.546143,50.264954 50.543599,46.492958 46.577512,43.922722 42.010037,43.922722 37.98795,43.922722 34.44249,45.919983 32.28362,48.96946L33.773167,49.248013C33.773167,49.248013,34.636472,49.363979,34.044091,49.72456L28.316394,52.28698C28.141854,52.318275,27.969985,52.199879,27.940001,52.026585L28.322789,45.767639C28.43744,45.104939,28.815036,45.774227,28.815036,45.774227L29.632752,47.140041C32.366937,43.245621,36.88775,40.691002,42.010037,40.691002z M21.804616,0L44.531026,0C48.257512,0,51.280999,3.0222199,51.280999,6.7460904L51.280999,38.274937 50.537671,37.898952C49.215222,37.269638,47.812577,36.778652,46.348977,36.444992L45.879922,36.344151 45.879922,6.7460904C45.879922,6.0039101,45.273122,5.3972201,44.531026,5.3972206L22.158713,5.3972206 21.794215,17.167999C21.794215,17.167999,21.325417,24.9519,12.747249,24.0117L5.3957184,23.8125 5.3957184,57.253899C5.3957189,57.998699,6.0024966,58.602901,6.7473038,58.602901L21.820422,58.602901 21.860354,58.884754C22.115357,60.533066,22.574946,62.114872,23.212727,63.604115L23.393945,64 6.7473038,64C3.0207885,64,2.9307267E-07,60.980499,0,57.253899L0,23.0051 2.6718498,20.1863 2.6743804,20.1094 16.677035,5.3972206 16.637936,5.3972206 12.89565,9.3464394 16.817534,5.1914101 16.872334,5.1914101 16.997233,5.0612788 17.007634,5.0612788z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -607,7 +619,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="F1M70.546875,54.3758316040039L77.484375,54.3758316040039 77.484375,89.0633316040039 70.546875,89.0633316040039 70.546875,54.3758316040039z M48.5625,54.3758316040039L65.90625,71.7195816040039 48.5625,89.0633316040039 48.5625,75.1883316040039 8.109375,75.1883316040039 8.109375,68.2508316040039 48.5625,68.2508316040039 48.5625,54.3758316040039z M37.03125,19.6883316040039L37.03125,33.5633316040039 77.484375,33.5633316040039 77.484375,40.5008316040039 37.03125,40.5008316040039 37.03125,54.3758316040039 19.6875,37.0320816040039 37.03125,19.6883316040039z M8.109375,19.6883316040039L15.046875,19.6883316040039 15.046875,54.3758316040039 8.109375,54.3758316040039 8.109375,19.6883316040039z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="F1M70.546875,54.3758316040039L77.484375,54.3758316040039 77.484375,89.0633316040039 70.546875,89.0633316040039 70.546875,54.3758316040039z M48.5625,54.3758316040039L65.90625,71.7195816040039 48.5625,89.0633316040039 48.5625,75.1883316040039 8.109375,75.1883316040039 8.109375,68.2508316040039 48.5625,68.2508316040039 48.5625,54.3758316040039z M37.03125,19.6883316040039L37.03125,33.5633316040039 77.484375,33.5633316040039 77.484375,40.5008316040039 37.03125,40.5008316040039 37.03125,54.3758316040039 19.6875,37.0320816040039 37.03125,19.6883316040039z M8.109375,19.6883316040039L15.046875,19.6883316040039 15.046875,54.3758316040039 8.109375,54.3758316040039 8.109375,19.6883316040039z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -617,7 +629,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M0,27.006001L37.806999,27.006001 37.806999,31.806 0,31.806z M0,18.003L37.806999,18.003 37.806999,22.806 0,22.806z M0,8.9999999L37.806999,8.9999999 37.806999,13.803 0,13.803z M0,0L37.806999,0 37.806999,4.8030009 0,4.8030009z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M0,27.006001L37.806999,27.006001 37.806999,31.806 0,31.806z M0,18.003L37.806999,18.003 37.806999,22.806 0,22.806z M0,8.9999999L37.806999,8.9999999 37.806999,13.803 0,13.803z M0,0L37.806999,0 37.806999,4.8030009 0,4.8030009z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -627,7 +639,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M -2.7407407 21.999952 V 25 H 11.37037 v -3.000048 z m 0 -4.999952 v 2.999952 H 11.37037 V 17 Z m 0 -5 v 3 H 11.37037 v -3 z m 0 -5 v 3 H 11.37037 V 7 Z m -1 -7 c 0.552 0 1.0810413 0.45297098 1 1 V 5 H 11.37037 V 1 c 0 -0.5529995 0.448 -1 1 -1 0.552 0 1 0.4470005 1 1 0 11.544484 0 19.62158 0 30 0 0.553 -0.448 1 -1 1 -0.552 0 -0.918958 -0.45297 -1 -1 V 27 H -2.7407407 v 4 c 0 0.553 -0.448 1 -1 1 -0.552 0 -1 -0.447 -1 -1 0 -11.54448 0 -19.621581 0 -30 0 -0.5529995 0.448 -1 1 -1 z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M -2.7407407 21.999952 V 25 H 11.37037 v -3.000048 z m 0 -4.999952 v 2.999952 H 11.37037 V 17 Z m 0 -5 v 3 H 11.37037 v -3 z m 0 -5 v 3 H 11.37037 V 7 Z m -1 -7 c 0.552 0 1.0810413 0.45297098 1 1 V 5 H 11.37037 V 1 c 0 -0.5529995 0.448 -1 1 -1 0.552 0 1 0.4470005 1 1 0 11.544484 0 19.62158 0 30 0 0.553 -0.448 1 -1 1 -0.552 0 -0.918958 -0.45297 -1 -1 V 27 H -2.7407407 v 4 c 0 0.553 -0.448 1 -1 1 -0.552 0 -1 -0.447 -1 -1 0 -11.54448 0 -19.621581 0 -30 0 -0.5529995 0.448 -1 1 -1 z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -637,7 +649,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="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" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="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" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -647,7 +659,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M22.1575,13.53399L21.794301,25.304994C21.794301,25.304994,21.3242,33.088695,12.747401,32.148692L5.3958107,31.949492 5.3958107,65.390895C5.3958107,66.135493,6.0026208,66.7391,6.7473808,66.7391L44.5313,66.7391C45.2734,66.7391,45.8802,66.135493,45.8802,65.390895L45.8802,14.883091C45.8802,14.140891,45.2734,13.53399,44.5313,13.53399z M21.804701,8.1369996L44.5313,8.1369996C48.257802,8.1370001,51.279999,11.15899,51.279999,14.883091L51.279999,65.390895C51.279999,69.117496,48.257802,72.136996,44.5313,72.136996L6.7473808,72.136996C3.0208105,72.136996,5.8073994E-07,69.117496,0,65.390895L0,31.142392 2.6718808,28.322993 2.6745006,28.246392 16.6771,13.53399 16.638001,13.53399 12.8945,17.483192 16.816401,13.328391 16.872401,13.328391 16.9961,13.19799 17.0078,13.19799z M30.352,0L55.255002,0 60.581002,0 60.581002,4.747 60.581002,55.207999 55.255002,55.207999 55.255002,4.747 30.352,4.747z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M22.1575,13.53399L21.794301,25.304994C21.794301,25.304994,21.3242,33.088695,12.747401,32.148692L5.3958107,31.949492 5.3958107,65.390895C5.3958107,66.135493,6.0026208,66.7391,6.7473808,66.7391L44.5313,66.7391C45.2734,66.7391,45.8802,66.135493,45.8802,65.390895L45.8802,14.883091C45.8802,14.140891,45.2734,13.53399,44.5313,13.53399z M21.804701,8.1369996L44.5313,8.1369996C48.257802,8.1370001,51.279999,11.15899,51.279999,14.883091L51.279999,65.390895C51.279999,69.117496,48.257802,72.136996,44.5313,72.136996L6.7473808,72.136996C3.0208105,72.136996,5.8073994E-07,69.117496,0,65.390895L0,31.142392 2.6718808,28.322993 2.6745006,28.246392 16.6771,13.53399 16.638001,13.53399 12.8945,17.483192 16.816401,13.328391 16.872401,13.328391 16.9961,13.19799 17.0078,13.19799z M30.352,0L55.255002,0 60.581002,0 60.581002,4.747 60.581002,55.207999 55.255002,55.207999 55.255002,4.747 30.352,4.747z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -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>
@@ -671,7 +683,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M17.9349,52.632801L13.5176,60.285198 22.3529,60.285198z M4.1452103,50.3647L4.1452103,52.622398 59.855499,52.622398 59.855499,50.3647z M17.062819,7.9790001L21.601147,36.012657 25.301687,19.424307 28.011845,37.14275 33.26696,12.41387 36.744106,37.14275 40.357449,27.929331 43.067604,37.14275 46.822746,19.489307 50.89698,37.14275 56.015999,23.676769 56.015999,42.608997 8.1710005,42.608997 8.1710005,23.135174 13.86557,31.999594z M4.1452103,3.8098803L4.1452103,45.6199 59.855499,45.6199 59.855499,3.8098803z M0,0L64,0 64,64 0,64z"/>
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M17.9349,52.632801L13.5176,60.285198 22.3529,60.285198z M4.1452103,50.3647L4.1452103,52.622398 59.855499,52.622398 59.855499,50.3647z M17.062819,7.9790001L21.601147,36.012657 25.301687,19.424307 28.011845,37.14275 33.26696,12.41387 36.744106,37.14275 40.357449,27.929331 43.067604,37.14275 46.822746,19.489307 50.89698,37.14275 56.015999,23.676769 56.015999,42.608997 8.1710005,42.608997 8.1710005,23.135174 13.86557,31.999594z M4.1452103,3.8098803L4.1452103,45.6199 59.855499,45.6199 59.855499,3.8098803z M0,0L64,0 64,64 0,64z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -681,7 +693,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M18.660349,20.393C14.879011,20.393 11.806,23.468643 11.806,27.249899 11.806,31.026154 14.879011,34.103001 18.660349,34.103001 22.444386,34.103001 25.519998,31.026154 25.519998,27.249899 25.519998,23.468643 22.444386,20.393 18.660349,20.393z M50.07317,8.7561953C49.408178,8.7706149 48.735172,8.9156297 48.090648,9.2053407 45.51765,10.359123 44.36155,13.394207 45.51765,15.96858 46.671449,18.541552 49.709146,19.691384 52.283444,18.540251 54.856342,17.378819 56.012442,14.352736 54.856342,11.771862 53.990993,9.8450501 52.068145,8.7129386 50.07317,8.7561953z M15.597473,8.6000007L21.868224,8.6000007 22.20032,14.327933 22.20032,14.531032C23.236712,14.816134,24.218506,15.221134,25.133898,15.736836L25.253695,15.615635 29.675461,11.900828 34.111725,16.338337 30.296556,20.624846 30.165057,20.758946C30.683352,21.674349,31.08955,22.659951,31.378649,23.696451L31.544045,23.696451 37.293999,24.196451 37.293999,30.468565 31.566147,30.803267 31.378649,30.803267C31.08955,31.836972,30.683352,32.821373,30.165057,33.736874L30.280955,33.853974 33.993225,38.274582 29.558261,42.709496 25.271895,38.896988 25.133898,38.758988C24.218506,39.274586,23.236712,39.678185,22.20032,39.963387L22.20032,40.146988 21.868224,45.897 15.597473,45.897 15.093578,40.166485 15.093578,39.959488C14.064985,39.671688,13.084595,39.265385,12.174301,38.749787L12.040102,38.881485 7.7381763,42.709496 3.3033104,38.274582 6.9999428,33.869675 7.1522102,33.721176C6.6379356,32.807072,6.2343292,31.83057,5.9464817,30.803267L5.7499523,30.803267 0,30.468565 0,24.196451 5.7278118,23.696451 5.9464817,23.696451C6.2343292,22.665051,6.6379356,21.687248,7.1522102,20.775946L7.0128822,20.640444 3.1874237,16.338337 7.6209869,11.900828 12.027203,15.600036 12.174301,15.744536C13.084595,15.231535,14.064985,14.825334,15.093578,14.537533L15.093578,14.350033z M52.888157,0L57.259197,1.6601361 56.091211,5.7941664 56.035212,5.937414C56.665401,6.3971274,57.240894,6.9413698,57.753986,7.5533617L57.878984,7.4947021 61.949226,6.0845716 63.865797,10.35405 60.071657,12.331821 59.933659,12.393021C60.053354,13.18731,60.075457,13.977598,60.003952,14.758987L60.12245,14.803286 63.999998,16.682059 62.337223,21.053194 58.253977,19.771912 58.12108,19.721113C57.656389,20.352705,57.112095,20.928097,56.496105,21.441189L56.549604,21.556988 57.962284,25.627228 53.691446,27.541302 51.712375,23.744456 51.653774,23.615557C50.862186,23.736756,50.066597,23.760056,49.285309,23.687157L49.241009,23.800455 47.366036,27.678 42.991099,26.017823 44.277584,21.928082 44.325682,21.799183C43.696789,21.334391,43.121297,20.784899,42.607106,20.171606L42.487208,20.225006 38.469066,21.753483 36.553693,17.486847 40.297138,15.390477 40.441637,15.327978C40.321941,14.53499,40.299839,13.744601,40.372736,12.963413L40.24384,12.915114 36.321998,11.158738 37.984774,6.7851217 42.113513,7.9530752 42.263212,8.009104C42.728206,7.3840535,43.269797,6.8124616,43.879186,6.3033789L43.82189,6.1652506 42.29051,2.1496897 46.562749,0.22918414 48.656518,3.977782 48.722916,4.1289099C49.511906,4.0091622,50.299695,3.9843827,51.075585,4.0534113L51.127785,3.9218233z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M18.660349,20.393C14.879011,20.393 11.806,23.468643 11.806,27.249899 11.806,31.026154 14.879011,34.103001 18.660349,34.103001 22.444386,34.103001 25.519998,31.026154 25.519998,27.249899 25.519998,23.468643 22.444386,20.393 18.660349,20.393z M50.07317,8.7561953C49.408178,8.7706149 48.735172,8.9156297 48.090648,9.2053407 45.51765,10.359123 44.36155,13.394207 45.51765,15.96858 46.671449,18.541552 49.709146,19.691384 52.283444,18.540251 54.856342,17.378819 56.012442,14.352736 54.856342,11.771862 53.990993,9.8450501 52.068145,8.7129386 50.07317,8.7561953z M15.597473,8.6000007L21.868224,8.6000007 22.20032,14.327933 22.20032,14.531032C23.236712,14.816134,24.218506,15.221134,25.133898,15.736836L25.253695,15.615635 29.675461,11.900828 34.111725,16.338337 30.296556,20.624846 30.165057,20.758946C30.683352,21.674349,31.08955,22.659951,31.378649,23.696451L31.544045,23.696451 37.293999,24.196451 37.293999,30.468565 31.566147,30.803267 31.378649,30.803267C31.08955,31.836972,30.683352,32.821373,30.165057,33.736874L30.280955,33.853974 33.993225,38.274582 29.558261,42.709496 25.271895,38.896988 25.133898,38.758988C24.218506,39.274586,23.236712,39.678185,22.20032,39.963387L22.20032,40.146988 21.868224,45.897 15.597473,45.897 15.093578,40.166485 15.093578,39.959488C14.064985,39.671688,13.084595,39.265385,12.174301,38.749787L12.040102,38.881485 7.7381763,42.709496 3.3033104,38.274582 6.9999428,33.869675 7.1522102,33.721176C6.6379356,32.807072,6.2343292,31.83057,5.9464817,30.803267L5.7499523,30.803267 0,30.468565 0,24.196451 5.7278118,23.696451 5.9464817,23.696451C6.2343292,22.665051,6.6379356,21.687248,7.1522102,20.775946L7.0128822,20.640444 3.1874237,16.338337 7.6209869,11.900828 12.027203,15.600036 12.174301,15.744536C13.084595,15.231535,14.064985,14.825334,15.093578,14.537533L15.093578,14.350033z M52.888157,0L57.259197,1.6601361 56.091211,5.7941664 56.035212,5.937414C56.665401,6.3971274,57.240894,6.9413698,57.753986,7.5533617L57.878984,7.4947021 61.949226,6.0845716 63.865797,10.35405 60.071657,12.331821 59.933659,12.393021C60.053354,13.18731,60.075457,13.977598,60.003952,14.758987L60.12245,14.803286 63.999998,16.682059 62.337223,21.053194 58.253977,19.771912 58.12108,19.721113C57.656389,20.352705,57.112095,20.928097,56.496105,21.441189L56.549604,21.556988 57.962284,25.627228 53.691446,27.541302 51.712375,23.744456 51.653774,23.615557C50.862186,23.736756,50.066597,23.760056,49.285309,23.687157L49.241009,23.800455 47.366036,27.678 42.991099,26.017823 44.277584,21.928082 44.325682,21.799183C43.696789,21.334391,43.121297,20.784899,42.607106,20.171606L42.487208,20.225006 38.469066,21.753483 36.553693,17.486847 40.297138,15.390477 40.441637,15.327978C40.321941,14.53499,40.299839,13.744601,40.372736,12.963413L40.24384,12.915114 36.321998,11.158738 37.984774,6.7851217 42.113513,7.9530752 42.263212,8.009104C42.728206,7.3840535,43.269797,6.8124616,43.879186,6.3033789L43.82189,6.1652506 42.29051,2.1496897 46.562749,0.22918414 48.656518,3.977782 48.722916,4.1289099C49.511906,4.0091622,50.299695,3.9843827,51.075585,4.0534113L51.127785,3.9218233z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -691,7 +703,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M33.597977,10.759002C37.946865,10.759002 41.485962,14.285001 41.485962,18.649 41.485962,23 37.946865,26.535 33.597977,26.535 29.23909,26.535 25.709992,23 25.709992,18.649 25.709992,17.784 25.849955,16.953001 26.109888,16.174002 26.779719,16.881001 27.70948,17.327002 28.759213,17.327002 30.778696,17.327002 32.418278,15.691001 32.418278,13.668001 32.418278,12.663001 32.008381,11.748001 31.348551,11.087002 32.058369,10.876001 32.818176,10.759002 33.597977,10.759002z M33.606682,4.3679962C25.92741,4.3679957 19.698065,10.594956 19.698065,18.27293 19.698065,25.953894 25.92741,32.177862 33.606682,32.177862 41.295838,32.177862 47.515175,25.953894 47.515175,18.27293 47.515175,10.594956 41.295838,4.3679957 33.606682,4.3679962z M34.867642,1.546141E-09C36.890393,2.6508449E-05 58.705193,0.41938579 68.893006,18.299923 68.893006,18.299923 57.1442,36.139837 34.44656,34.768854 34.44656,34.768854 14.428583,36.59984 0,18.299923 0,18.299923 9.0791523,0.4590019 34.716553,0.0010111886 34.716553,0.0010114873 34.768162,-1.4442128E-06 34.867642,1.546141E-09z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M33.597977,10.759002C37.946865,10.759002 41.485962,14.285001 41.485962,18.649 41.485962,23 37.946865,26.535 33.597977,26.535 29.23909,26.535 25.709992,23 25.709992,18.649 25.709992,17.784 25.849955,16.953001 26.109888,16.174002 26.779719,16.881001 27.70948,17.327002 28.759213,17.327002 30.778696,17.327002 32.418278,15.691001 32.418278,13.668001 32.418278,12.663001 32.008381,11.748001 31.348551,11.087002 32.058369,10.876001 32.818176,10.759002 33.597977,10.759002z M33.606682,4.3679962C25.92741,4.3679957 19.698065,10.594956 19.698065,18.27293 19.698065,25.953894 25.92741,32.177862 33.606682,32.177862 41.295838,32.177862 47.515175,25.953894 47.515175,18.27293 47.515175,10.594956 41.295838,4.3679957 33.606682,4.3679962z M34.867642,1.546141E-09C36.890393,2.6508449E-05 58.705193,0.41938579 68.893006,18.299923 68.893006,18.299923 57.1442,36.139837 34.44656,34.768854 34.44656,34.768854 14.428583,36.59984 0,18.299923 0,18.299923 9.0791523,0.4590019 34.716553,0.0010111886 34.716553,0.0010114873 34.768162,-1.4442128E-06 34.867642,1.546141E-09z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -701,7 +713,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M3.3390064,47.374004L26.00104,47.374004C26.17504,47.881321 26.361041,48.388939 26.575041,48.889757 27.610043,51.333344 29.086045,53.523722 30.968048,55.406689 31.135048,55.573593 31.309048,55.733997 31.476048,55.894005L3.3390064,55.894005C1.4960022,55.894005,0,54.398552,0,52.555387L0,50.712622C0,48.869556,1.4960022,47.374004,3.3390064,47.374004z M8.6740417,35.422004L25.713049,35.422004C25.220049,37.198282 24.972049,39.03446 24.972049,40.910738 24.972049,41.932125 25.046049,42.947113 25.193049,43.942001L8.6740417,43.942001C6.8310413,43.942001,5.3350391,42.44632,5.3350391,40.60354L5.3350391,38.760464C5.3350391,36.917886,6.8310413,35.422004,8.6740417,35.422004z M44.333998,28.91725L44.333998,31.014374C42.649975,31.25297 41.419993,31.901375 40.634958,32.963784 39.852975,34.026392 39.458993,35.225903 39.458993,36.554114 39.458993,37.565422 39.677987,38.455833 40.118966,39.221838 40.555979,39.98354 41.125986,40.573252 41.829967,40.99555 42.53297,41.425255 43.508984,41.850658 44.758986,42.28466 46.149979,42.776463 47.048965,43.183067 47.451004,43.502871 47.852982,43.819578 48.055986,44.291979 48.055986,44.913282 48.055986,45.51469 47.852982,46.022491 47.451004,46.448399 47.048965,46.874603 46.41597,47.0849 45.560011,47.0849 43.728954,47.0849 42.649975,46.093193 42.325999,44.11248L38.810005,45.030586C39.521005,48.178713,41.360971,49.909225,44.333998,50.214329L44.333998,52.91765 46.607986,52.91765 46.607986,50.202824C48.391985,50.015223 49.755999,49.390217 50.701008,48.33131 51.646992,47.273104 52.118979,45.952789 52.118979,44.36648 52.118979,43.331467 51.888998,42.429262 51.431965,41.65526 50.970966,40.886349 50.360981,40.276442 49.59096,39.839542 48.826006,39.401939 47.763016,38.933239 46.41597,38.440628 45.185011,37.991329 44.384962,37.612823 44.008986,37.307821 43.633984,36.999621 43.446972,36.561912 43.446972,35.991115 43.446972,35.429106 43.641979,34.952402 44.032972,34.558096 44.419996,34.163196 44.935011,33.967896 45.576002,33.967896 46.85701,33.967896 47.797989,34.850602 48.407976,36.612617L51.545002,35.327107C50.795002,32.756988,49.14699,31.319575,46.607986,31.015068L46.607986,28.91725z M3.3390026,23.690004L34.347016,23.690004C33.139015,24.471167 32.010015,25.379241 30.975014,26.414426 29.286014,28.103764 27.917013,30.053523 26.909013,32.203998L3.3390026,32.203998C1.4960022,32.203998,0,30.708376,0,28.865226L0,27.028776C0,25.185925,1.4960022,23.690004,3.3390026,23.690004z M45.470961,22.415C55.686974,22.415 63.96701,30.694868 63.959991,40.924053 63.959991,51.140034 55.680992,59.419998 45.464003,59.419998 41.41096,59.419998 37.659003,58.110893 34.613955,55.900974 31.696961,53.777658 29.412962,50.819633 28.130979,47.380901 27.716977,46.285692 27.408993,45.137482 27.215939,43.948774 27.055966,42.96047 26.96899,41.952459 26.968991,40.917248 26.96899,39.007633 27.255978,37.164823 27.796994,35.428709 28.150998,34.306995 28.597959,33.23199 29.145994,32.210482 31.195984,28.371248 34.553958,25.346428 38.626961,23.723612 40.743967,22.882503 43.053967,22.415 45.470961,22.415z M3.3390045,11.952L38.153052,11.952C39.996056,11.952,41.492057,13.448001,41.492057,15.290001L41.492057,17.133002C41.492057,18.976003,39.996056,20.472003,38.153052,20.472004L3.3390045,20.472004C1.4960022,20.472003,0,18.976003,0,17.133002L0,15.290001C0,13.448001,1.4960022,11.952,3.3390045,11.952z M8.6735134,0L43.487543,0C45.330534,0,46.826027,1.4958782,46.826027,3.3386841L46.826027,5.1814384C46.826027,7.0245428,45.330534,8.5200005,43.487543,8.5200005L8.6735134,8.5200005C6.8304424,8.5200005,5.3350277,7.0245428,5.3350277,5.1814384L5.3350277,3.3386841C5.3350277,1.4958782,6.8304424,0,8.6735134,0z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M3.3390064,47.374004L26.00104,47.374004C26.17504,47.881321 26.361041,48.388939 26.575041,48.889757 27.610043,51.333344 29.086045,53.523722 30.968048,55.406689 31.135048,55.573593 31.309048,55.733997 31.476048,55.894005L3.3390064,55.894005C1.4960022,55.894005,0,54.398552,0,52.555387L0,50.712622C0,48.869556,1.4960022,47.374004,3.3390064,47.374004z M8.6740417,35.422004L25.713049,35.422004C25.220049,37.198282 24.972049,39.03446 24.972049,40.910738 24.972049,41.932125 25.046049,42.947113 25.193049,43.942001L8.6740417,43.942001C6.8310413,43.942001,5.3350391,42.44632,5.3350391,40.60354L5.3350391,38.760464C5.3350391,36.917886,6.8310413,35.422004,8.6740417,35.422004z M44.333998,28.91725L44.333998,31.014374C42.649975,31.25297 41.419993,31.901375 40.634958,32.963784 39.852975,34.026392 39.458993,35.225903 39.458993,36.554114 39.458993,37.565422 39.677987,38.455833 40.118966,39.221838 40.555979,39.98354 41.125986,40.573252 41.829967,40.99555 42.53297,41.425255 43.508984,41.850658 44.758986,42.28466 46.149979,42.776463 47.048965,43.183067 47.451004,43.502871 47.852982,43.819578 48.055986,44.291979 48.055986,44.913282 48.055986,45.51469 47.852982,46.022491 47.451004,46.448399 47.048965,46.874603 46.41597,47.0849 45.560011,47.0849 43.728954,47.0849 42.649975,46.093193 42.325999,44.11248L38.810005,45.030586C39.521005,48.178713,41.360971,49.909225,44.333998,50.214329L44.333998,52.91765 46.607986,52.91765 46.607986,50.202824C48.391985,50.015223 49.755999,49.390217 50.701008,48.33131 51.646992,47.273104 52.118979,45.952789 52.118979,44.36648 52.118979,43.331467 51.888998,42.429262 51.431965,41.65526 50.970966,40.886349 50.360981,40.276442 49.59096,39.839542 48.826006,39.401939 47.763016,38.933239 46.41597,38.440628 45.185011,37.991329 44.384962,37.612823 44.008986,37.307821 43.633984,36.999621 43.446972,36.561912 43.446972,35.991115 43.446972,35.429106 43.641979,34.952402 44.032972,34.558096 44.419996,34.163196 44.935011,33.967896 45.576002,33.967896 46.85701,33.967896 47.797989,34.850602 48.407976,36.612617L51.545002,35.327107C50.795002,32.756988,49.14699,31.319575,46.607986,31.015068L46.607986,28.91725z M3.3390026,23.690004L34.347016,23.690004C33.139015,24.471167 32.010015,25.379241 30.975014,26.414426 29.286014,28.103764 27.917013,30.053523 26.909013,32.203998L3.3390026,32.203998C1.4960022,32.203998,0,30.708376,0,28.865226L0,27.028776C0,25.185925,1.4960022,23.690004,3.3390026,23.690004z M45.470961,22.415C55.686974,22.415 63.96701,30.694868 63.959991,40.924053 63.959991,51.140034 55.680992,59.419998 45.464003,59.419998 41.41096,59.419998 37.659003,58.110893 34.613955,55.900974 31.696961,53.777658 29.412962,50.819633 28.130979,47.380901 27.716977,46.285692 27.408993,45.137482 27.215939,43.948774 27.055966,42.96047 26.96899,41.952459 26.968991,40.917248 26.96899,39.007633 27.255978,37.164823 27.796994,35.428709 28.150998,34.306995 28.597959,33.23199 29.145994,32.210482 31.195984,28.371248 34.553958,25.346428 38.626961,23.723612 40.743967,22.882503 43.053967,22.415 45.470961,22.415z M3.3390045,11.952L38.153052,11.952C39.996056,11.952,41.492057,13.448001,41.492057,15.290001L41.492057,17.133002C41.492057,18.976003,39.996056,20.472003,38.153052,20.472004L3.3390045,20.472004C1.4960022,20.472003,0,18.976003,0,17.133002L0,15.290001C0,13.448001,1.4960022,11.952,3.3390045,11.952z M8.6735134,0L43.487543,0C45.330534,0,46.826027,1.4958782,46.826027,3.3386841L46.826027,5.1814384C46.826027,7.0245428,45.330534,8.5200005,43.487543,8.5200005L8.6735134,8.5200005C6.8304424,8.5200005,5.3350277,7.0245428,5.3350277,5.1814384L5.3350277,3.3386841C5.3350277,1.4958782,6.8304424,0,8.6735134,0z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -711,7 +723,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black"
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M14.200889,0C15.299896,0 16.59991,0.50000046 17.998923,1.4989944 19.597936,2.5990016 20.397944,4.5970043 21.196948,6.4960083 22.495954,9.6950015 23.495961,11.693005 26.093989,11.594006 26.793992,11.594006 27.293996,12.093014 27.293996,12.693005 27.293996,13.393002 26.793992,13.892012 26.193997,13.892012 21.896949,14.092009 20.297938,10.394007 19.097932,7.3960032 18.398921,5.6969966 17.698918,4.0979957 16.699916,3.3979983 15.899907,2.8979978 14.600901,1.9989949 13.700884,2.4989952 12.701883,2.9980039 12.30187,5.0970048 12.20188,6.3960022L13.900898,6.2959961C14.600901,6.2959961,15.100904,6.6960057,15.199904,7.3960032L15.399903,9.6950015C16.799909,10.19401 18.098929,10.894008 19.197938,11.893001 23.995965,15.991012 24.494979,23.287025 20.397944,28.084026 16.099906,32.78203 8.8038569,33.381029 4.0068054,29.184019 -0.79122243,25.086023 -1.3902263,17.79001 2.8067985,12.993009 3.9068136,11.693005 5.405819,10.594005 7.0048309,9.9940128L6.9048391,7.9960098C6.9048391,7.3960032,7.3048363,6.7959966,8.0038471,6.6960057L9.9028645,6.5959992C9.9028645,5.3970083 10.302861,1.6989915 12.701883,0.50000046 13.10188,0.099990942 13.700884,0 14.200889,0z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -722,7 +734,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M10.659519,13.581999C10.787763,13.581999 10.916255,13.630751 11.01375,13.728755 11.207739,13.924762 11.207739,14.241775 11.01175,14.435782L8.5898266,16.850518 11.012901,19.265967C11.20889,19.460974 11.20889,19.776986 11.013901,19.972994 10.916906,20.070997 10.787913,20.119999 10.65992,20.119999 10.531927,20.119999 10.403934,20.070997 10.30694,19.973994L7.8819971,17.556247 5.4570541,19.973994C5.3600597,20.070997 5.2320671,20.119999 5.1040735,20.119999 4.9760809,20.119999 4.8470879,20.070997 4.7500935,19.972994 4.5551033,19.776986 4.5551033,19.460974 4.7510929,19.265967L7.1741672,16.850518 4.752244,14.435782C4.5562544,14.241775 4.5562544,13.924762 4.7502441,13.728755 4.9452333,13.532748 5.2642155,13.533748 5.4572048,13.727755L7.8819966,16.144916 10.306789,13.727755C10.403284,13.630751,10.531276,13.582,10.659519,13.581999z M7.7229891,10.151996C4.0159941,10.151996 0.99999809,13.156997 0.99999809,16.850998 0.99999809,20.544999 4.0159941,23.55 7.7229891,23.55 10.503235,23.55 12.895357,21.859687 13.917652,19.456124L14.034025,19.162648 14.034025,18.772999 14.164453,18.772999 14.191419,18.682897C14.357855,18.100457 14.44698,17.485905 14.44698,16.850998 14.44698,13.156997 11.429984,10.151996 7.7229891,10.151996z M28.477001,10.135L31.999016,13.663 28.424001,17.244999 27.716998,16.538999 29.693872,14.558 21.322995,14.558 21.322995,13.558 30.482195,13.558 27.769998,10.841z M7.4592972,5.1720018L15.45899,8.869 22.849004,5.1720018z M4.6849804,4.1720018L25.465012,4.1720018 25.465012,10.408002 24.465011,10.408002 24.465011,5.4821377 15.476991,9.9780006 5.6849813,5.4540205 5.6849813,9.4246554 5.7953281,9.3948154C6.4117265,9.2363219 7.0576777,9.1519957 7.7229891,9.1519957 11.981983,9.1519957 15.446978,12.605997 15.446978,16.850998 15.446978,17.514279 15.362384,18.158249 15.203383,18.772749L24.465023,18.772999 24.465023,16.665997 25.465026,16.665997 25.465026,19.772997 14.868521,19.772997 14.839026,19.844827C13.664856,22.607125 10.917235,24.550001 7.7229891,24.550001 3.4649954,24.550001 0,21.095999 0,16.850998 0,13.733575 1.8686953,11.042744 4.5478315,9.8331947L4.6849804,9.7732801z M7.8089724,0L29.631012,0 29.631012,7.2790012 28.63101,7.2790012 28.63101,1 8.8089738,1 8.8089738,3.1080008 7.8089724,3.1080008z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M10.659519,13.581999C10.787763,13.581999 10.916255,13.630751 11.01375,13.728755 11.207739,13.924762 11.207739,14.241775 11.01175,14.435782L8.5898266,16.850518 11.012901,19.265967C11.20889,19.460974 11.20889,19.776986 11.013901,19.972994 10.916906,20.070997 10.787913,20.119999 10.65992,20.119999 10.531927,20.119999 10.403934,20.070997 10.30694,19.973994L7.8819971,17.556247 5.4570541,19.973994C5.3600597,20.070997 5.2320671,20.119999 5.1040735,20.119999 4.9760809,20.119999 4.8470879,20.070997 4.7500935,19.972994 4.5551033,19.776986 4.5551033,19.460974 4.7510929,19.265967L7.1741672,16.850518 4.752244,14.435782C4.5562544,14.241775 4.5562544,13.924762 4.7502441,13.728755 4.9452333,13.532748 5.2642155,13.533748 5.4572048,13.727755L7.8819966,16.144916 10.306789,13.727755C10.403284,13.630751,10.531276,13.582,10.659519,13.581999z M7.7229891,10.151996C4.0159941,10.151996 0.99999809,13.156997 0.99999809,16.850998 0.99999809,20.544999 4.0159941,23.55 7.7229891,23.55 10.503235,23.55 12.895357,21.859687 13.917652,19.456124L14.034025,19.162648 14.034025,18.772999 14.164453,18.772999 14.191419,18.682897C14.357855,18.100457 14.44698,17.485905 14.44698,16.850998 14.44698,13.156997 11.429984,10.151996 7.7229891,10.151996z M28.477001,10.135L31.999016,13.663 28.424001,17.244999 27.716998,16.538999 29.693872,14.558 21.322995,14.558 21.322995,13.558 30.482195,13.558 27.769998,10.841z M7.4592972,5.1720018L15.45899,8.869 22.849004,5.1720018z M4.6849804,4.1720018L25.465012,4.1720018 25.465012,10.408002 24.465011,10.408002 24.465011,5.4821377 15.476991,9.9780006 5.6849813,5.4540205 5.6849813,9.4246554 5.7953281,9.3948154C6.4117265,9.2363219 7.0576777,9.1519957 7.7229891,9.1519957 11.981983,9.1519957 15.446978,12.605997 15.446978,16.850998 15.446978,17.514279 15.362384,18.158249 15.203383,18.772749L24.465023,18.772999 24.465023,16.665997 25.465026,16.665997 25.465026,19.772997 14.868521,19.772997 14.839026,19.844827C13.664856,22.607125 10.917235,24.550001 7.7229891,24.550001 3.4649954,24.550001 0,21.095999 0,16.850998 0,13.733575 1.8686953,11.042744 4.5478315,9.8331947L4.6849804,9.7732801z M7.8089724,0L29.631012,0 29.631012,7.2790012 28.63101,7.2790012 28.63101,1 8.8089738,1 8.8089738,3.1080008 7.8089724,3.1080008z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -732,7 +744,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M2,2L2,25 30,25 30,2z M1,0L31,0C31.552002,0,32,0.44702148,32,1L32,26C32,26.552979,31.552002,27,31,27L17,27 17,30 25,30 25,32 7,32 7,30 15,30 15,27 1,27C0.44799805,27,0,26.552979,0,26L0,1C0,0.44702148,0.44799805,0,1,0z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M2,2L2,25 30,25 30,2z M1,0L31,0C31.552002,0,32,0.44702148,32,1L32,26C32,26.552979,31.552002,27,31,27L17,27 17,30 25,30 25,32 7,32 7,30 15,30 15,27 1,27C0.44799805,27,0,26.552979,0,26L0,1C0,0.44702148,0.44799805,0,1,0z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -742,7 +754,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M16.2,11.1C19,11.1 21.2,13.3 21.2,16.1 21.2,18.9 19,21.1 16.2,21.1 13.4,21.1 11.2,18.9 11.2,16.1 11.2,13.4 13.4,11.1 16.2,11.1z M16.2,6C10.6,6 6,10.6 6,16.1 6,21.7 10.5,26.2 16.1,26.2 21.7,26.2 26.2,21.7 26.2,16.1 26.3,10.6 21.7,6 16.2,6z M15.3,0L16.8,0 16.8,5.1999998C22.2,5.5,26.6,9.6999998,27.1,15L32,15 32,16.5 27.1,16.5 27.1,16.7C26.8,22.3,22.3,26.8,16.7,27.1L16.7,32 15.2,32 15.2,27.1 15.5,27.1 16,27.1C10.2,27,5.5,22.5,5.1999998,16.7L5.1999998,16.5 5.1999998,16.4 0,16.4 0,15 5.3000002,15C5.9000001,9.6999998,10.3,5.5,15.7,5.1999998L15.6,5.1999998 15.3,5.1999998z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M16.2,11.1C19,11.1 21.2,13.3 21.2,16.1 21.2,18.9 19,21.1 16.2,21.1 13.4,21.1 11.2,18.9 11.2,16.1 11.2,13.4 13.4,11.1 16.2,11.1z M16.2,6C10.6,6 6,10.6 6,16.1 6,21.7 10.5,26.2 16.1,26.2 21.7,26.2 26.2,21.7 26.2,16.1 26.3,10.6 21.7,6 16.2,6z M15.3,0L16.8,0 16.8,5.1999998C22.2,5.5,26.6,9.6999998,27.1,15L32,15 32,16.5 27.1,16.5 27.1,16.7C26.8,22.3,22.3,26.8,16.7,27.1L16.7,32 15.2,32 15.2,27.1 15.5,27.1 16,27.1C10.2,27,5.5,22.5,5.1999998,16.7L5.1999998,16.5 5.1999998,16.4 0,16.4 0,15 5.3000002,15C5.9000001,9.6999998,10.3,5.5,15.7,5.1999998L15.6,5.1999998 15.3,5.1999998z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -752,7 +764,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M5.5803752,0C7.1813383,0 8.8833761,0.6000061 10.084373,1.8009948 11.785374,3.5029907 12.285377,5.8049927 11.685397,7.9060059L24.094478,20.31601C26.096505,19.714996 28.398524,20.216003 30.099524,21.916992 31.500535,23.317993 32.101552,25.320007 31.901538,27.121002L28.998504,24.118988C27.69753,22.817993 25.496465,22.817993 24.094478,24.118988 22.693467,25.519989 22.693467,27.622009 24.094478,29.02301L26.997514,31.924988C25.195498,32.225006 23.194447,31.625 21.793437,30.123993 20.191436,28.522003 19.591456,26.119995 20.191436,24.118988L7.7823553,11.709015C5.6803512,12.309998 3.3783338,11.80899 1.6773333,10.108002 0.37629925,8.7070007 -0.224718,6.8049927 0.076309212,5.0039978L2.978307,7.9060059C4.2793407,9.2070007 6.4813823,9.2070007 7.8823928,7.9060059 9.2834034,6.605011 9.2834034,4.4030151 7.8823928,3.0020142L4.9803342,0.10000607C5.1803479,0,5.3803615,0,5.5803752,0z" />
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M5.5803752,0C7.1813383,0 8.8833761,0.6000061 10.084373,1.8009948 11.785374,3.5029907 12.285377,5.8049927 11.685397,7.9060059L24.094478,20.31601C26.096505,19.714996 28.398524,20.216003 30.099524,21.916992 31.500535,23.317993 32.101552,25.320007 31.901538,27.121002L28.998504,24.118988C27.69753,22.817993 25.496465,22.817993 24.094478,24.118988 22.693467,25.519989 22.693467,27.622009 24.094478,29.02301L26.997514,31.924988C25.195498,32.225006 23.194447,31.625 21.793437,30.123993 20.191436,28.522003 19.591456,26.119995 20.191436,24.118988L7.7823553,11.709015C5.6803512,12.309998 3.3783338,11.80899 1.6773333,10.108002 0.37629925,8.7070007 -0.224718,6.8049927 0.076309212,5.0039978L2.978307,7.9060059C4.2793407,9.2070007 6.4813823,9.2070007 7.8823928,7.9060059 9.2834034,6.605011 9.2834034,4.4030151 7.8823928,3.0020142L4.9803342,0.10000607C5.1803479,0,5.3803615,0,5.5803752,0z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -762,7 +774,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M25.700012,16.000016L28.800019,16.000016 28.800019,17.099989 25.700012,17.099989z M23.200012,8.599998L17.200012,16.300004C17.200012,16.500016,17.100006,16.800002,17,17.000014L20.100006,22.50001 15.700012,17.699965C15,17.599989 14.5,17.000014 14.5,16.300004 14.5,15.599991 15,15.000017 15.700012,14.89998L15.800019,14.89998z M15.700012,2.9999971C8.7000124,3.2000091,3.0000002,9.0000224,3,16.000016L6.1000063,16.000016 6.1000063,17.099989 3.1000061,17.099989 3.1000061,17.300002C3.7000124,23.699959,9.1000063,28.79999,15.700012,29.000002L15.800019,29.000002 15.800019,26.000006 16.900025,26.000006 16.900025,29.000002 17.100006,29.000002C23.800019,28.500004 29.100006,22.899971 29.100006,16.099991 29.100006,9.3000092 23.800019,3.7000086 17.100006,3.2000089L16.900025,3.2000089 16.900025,6.2000065 15.800019,6.2000065 15.800019,2.9999971z M16,0C24.800019,0 32,7.2000055 32,16.000016 32,24.799994 24.800019,32 16,32 7.2000124,32 2.3106986E-07,24.799994 0,16.000016 2.3106986E-07,7.2000055 7.2000124,0 16,0z"/>
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M25.700012,16.000016L28.800019,16.000016 28.800019,17.099989 25.700012,17.099989z M23.200012,8.599998L17.200012,16.300004C17.200012,16.500016,17.100006,16.800002,17,17.000014L20.100006,22.50001 15.700012,17.699965C15,17.599989 14.5,17.000014 14.5,16.300004 14.5,15.599991 15,15.000017 15.700012,14.89998L15.800019,14.89998z M15.700012,2.9999971C8.7000124,3.2000091,3.0000002,9.0000224,3,16.000016L6.1000063,16.000016 6.1000063,17.099989 3.1000061,17.099989 3.1000061,17.300002C3.7000124,23.699959,9.1000063,28.79999,15.700012,29.000002L15.800019,29.000002 15.800019,26.000006 16.900025,26.000006 16.900025,29.000002 17.100006,29.000002C23.800019,28.500004 29.100006,22.899971 29.100006,16.099991 29.100006,9.3000092 23.800019,3.7000086 17.100006,3.2000089L16.900025,3.2000089 16.900025,6.2000065 15.800019,6.2000065 15.800019,2.9999971z M16,0C24.800019,0 32,7.2000055 32,16.000016 32,24.799994 24.800019,32 16,32 7.2000124,32 2.3106986E-07,24.799994 0,16.000016 2.3106986E-07,7.2000055 7.2000124,0 16,0z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -772,7 +784,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M7,26L19,26 19,28 7,28z M12.732999,14.533003L13.867999,14.533003 13.867999,15.486002C14.643999,15.519002,15.164999,15.671002,15.546999,15.844003L15.210999,17.068001C14.909999,16.949001 14.376999,16.700003 13.543999,16.700003 12.790999,16.700003 12.547999,17.003002 12.547999,17.307001 12.547999,17.664001 12.952999,17.892002 13.936999,18.238001 15.315999,18.693001 15.870998,19.289001 15.870998,20.264 15.870998,21.228001 15.141998,22.052 13.809999,22.268999L13.809999,23.372999 12.674999,23.372999 12.674999,22.344C11.898999,22.310999,11.146,22.116999,10.705999,21.878L11.052999,20.610001C11.539999,20.860001 12.223,21.087999 12.975999,21.087999 13.635999,21.087999 14.087999,20.85 14.087999,20.416 14.087999,20.004 13.716999,19.744001 12.859999,19.474001 11.620999,19.083 10.775999,18.542002 10.775999,17.490002 10.775999,16.537003 11.492999,15.790002 12.732999,15.562002z M2,12L2,30 24,30 24,12z M2,7L2,10 24,10 24,7z M2,2L2,5 24,5 24,2z M0,0L26,0 26,32 0,32z"/>
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M7,26L19,26 19,28 7,28z M12.732999,14.533003L13.867999,14.533003 13.867999,15.486002C14.643999,15.519002,15.164999,15.671002,15.546999,15.844003L15.210999,17.068001C14.909999,16.949001 14.376999,16.700003 13.543999,16.700003 12.790999,16.700003 12.547999,17.003002 12.547999,17.307001 12.547999,17.664001 12.952999,17.892002 13.936999,18.238001 15.315999,18.693001 15.870998,19.289001 15.870998,20.264 15.870998,21.228001 15.141998,22.052 13.809999,22.268999L13.809999,23.372999 12.674999,23.372999 12.674999,22.344C11.898999,22.310999,11.146,22.116999,10.705999,21.878L11.052999,20.610001C11.539999,20.860001 12.223,21.087999 12.975999,21.087999 13.635999,21.087999 14.087999,20.85 14.087999,20.416 14.087999,20.004 13.716999,19.744001 12.859999,19.474001 11.620999,19.083 10.775999,18.542002 10.775999,17.490002 10.775999,16.537003 11.492999,15.790002 12.732999,15.562002z M2,12L2,30 24,30 24,12z M2,7L2,10 24,10 24,7z M2,2L2,5 24,5 24,2z M0,0L26,0 26,32 0,32z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -782,7 +794,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M12.099998,0L19.799995,0 19.799995,12.1 32,12.1 32,19.799996 19.900002,19.799996 19.900002,31.999999 12.199997,31.999999 12.199997,19.900003 0,19.900003 0,12.199999 12.099998,12.199999z"/>
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M12.099998,0L19.799995,0 19.799995,12.1 32,12.1 32,19.799996 19.900002,19.799996 19.900002,31.999999 12.199997,31.999999 12.199997,19.900003 0,19.900003 0,12.199999 12.099998,12.199999z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -794,7 +806,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup.Children>
<GeometryDrawing Brush="Transparent" Geometry="M14.100037,6L14.100037,14.099998 6,14.099998 6,18 14.100037,18 14.100037,26.099998 18,26.099998 18,18 26,18 26,14.099998 17.900024,14.099998 17.900024,6z M16,0C24.800049,0 32,7.1999969 32,16 32,24.800003 24.800049,32 16,32 7.2000122,32 0,24.800003 0,16 0,7.1999969 7.2000122,0 16,0z">
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Black"/>
<Pen Thickness="2" Brush="{mbts:BTSColor UI.icon_foreground}"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -806,7 +818,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M454.352,398.431L331,190.879V87.42c17.459-6.192,30-22.865,30-42.42c0-24.813-20.187-45-45-45H196
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M454.352,398.431L331,190.879V87.42c17.459-6.192,30-22.865,30-42.42c0-24.813-20.187-45-45-45H196
c-24.813,0-45,20.187-45,45c0,19.555,12.541,36.228,30,42.42v103.459L57.659,398.413c-14.085,23.476-14.442,51.715-0.953,75.539
C70.196,497.776,94.594,512,121.972,512h268.055c27.378,0,51.776-14.224,65.266-38.048
C468.779,450.134,468.427,421.903,454.352,398.431z M196,30h120c8.271,0,15,6.729,15,15s-6.729,15-15,15H196
@@ -829,7 +841,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup.Children>
<GeometryDrawing Brush="Transparent" Geometry="M16.378574,2.0941912E-05C17.053625,-0.0029775997,17.669592,0.31702943,17.669592,0.31702964L27.747614,10.181254C30.078613,11.977292,28.301632,12.163302,28.301632,12.163302L14.005584,15.500376C11.924586,18.496447,15.130586,19.519465,15.130586,19.519465L30.870606,16.081391C32.66065,16.035383,31.786623,17.428421,31.786623,17.428421L15.130586,21.214503C12.353603,22.20553,10.869592,20.092476,10.869592,20.092476L0.76355401,8.8182232C-1.4574583,4.6071207,1.8735417,3.616101,1.8735417,3.616101L16.08762,0.023016395C16.184605,0.0080166093,16.282566,2.082104E-05,16.378574,2.0941912E-05z">
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="2"/>
<Pen Brush="{mbts:BTSColor UI.icon_foreground}" Thickness="2"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -841,7 +853,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M26.84801,26.411987L31.841998,26.411987 31.841998,31.915009 26.84801,31.915009z M10.497003,26.411987L18.709003,26.411987 18.709003,31.915009 10.497003,31.915009z M20.052005,18.708984L25.386005,18.708984 25.386005,31.915009 20.052005,31.915009z M10.497003,14.221985L18.709003,14.221985 18.709003,25.057983 10.497003,25.057983z M20.052005,9.5660095L25.386005,9.5660095 25.386005,17.18399 20.052005,17.18399z M26.84801,7.07901L31.841998,7.07901 31.841998,25.057983 26.84801,25.057983z M10.497003,1.9949951L18.709003,1.9949951 18.709003,12.686005 10.497003,12.686005z M0,0L9.2280055,0 9.2280055,32 0,32z"/>
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M26.84801,26.411987L31.841998,26.411987 31.841998,31.915009 26.84801,31.915009z M10.497003,26.411987L18.709003,26.411987 18.709003,31.915009 10.497003,31.915009z M20.052005,18.708984L25.386005,18.708984 25.386005,31.915009 20.052005,31.915009z M10.497003,14.221985L18.709003,14.221985 18.709003,25.057983 10.497003,25.057983z M20.052005,9.5660095L25.386005,9.5660095 25.386005,17.18399 20.052005,17.18399z M26.84801,7.07901L31.841998,7.07901 31.841998,25.057983 26.84801,25.057983z M10.497003,1.9949951L18.709003,1.9949951 18.709003,12.686005 10.497003,12.686005z M0,0L9.2280055,0 9.2280055,32 0,32z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -853,7 +865,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup.Children>
<GeometryDrawing Geometry="M38.642,39.289c-3.895-3.373-2.645-7.564-0.338-13.896c1.898-5.21,4.051-11.118,1.445-16.945 c-1.789-4.001-6.2-8.319-13.215-8.442L25.963,0c-2.518,0-6.289,0.283-9.953,2.734c-0.286,0.191-0.462,0.51-0.472,0.855 c-0.01,0.345,0.148,0.672,0.424,0.879c0.583,0.438,0.94,1.313,1.138,2.049c-2.711,0.486-3.37,1.934-4.01,3.34 c-0.676,1.486-1.443,3.17-5.15,4.794c-1.008,0.442-1.693,1.499-1.789,2.757c-0.092,1.221,0.412,2.334,1.315,2.905 c0.385,0.244,0.778,0.269,1.094,0.289c0.382,0.025,0.657,0.043,1.07,0.409c0.327,0.292,0.593,0.621,0.827,0.912 c0.388,0.481,0.722,0.896,1.268,1.021c0.77,0.181,1.694-0.278,2.761-0.843c0.693-0.368,1.479-0.784,2.073-0.904 c0.754-0.15,1.721-0.323,2.52-0.323c0.684,0,0.909,0.134,0.928,0.114c0.18,1.047-1.972,2.48-3.87,3.745 c-3.333,2.224-7.481,4.988-6.394,9.455c0.17,0.699,0.523,2.123,3.82,3.819C6.66,39.331,1.21,41.742,1.21,43.609 c0,1.244,1.142,2.041,7.165,2.703C12.421,46.757,17.793,47,23.5,47s11.079-0.243,15.125-0.688 c6.022-0.662,7.165-1.459,7.165-2.703C45.79,42.247,42.89,40.596,38.642,39.289z">
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="3"/>
<Pen Brush="{mbts:BTSColor UI.icon_foreground}" Thickness="3"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -865,7 +877,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M27,22.000013C25.346001,22.000013 24,23.345991 24,25.000013 24,26.654005 25.346001,28.000013 27,28.000013 28.653999,28.000013 30,26.654005 30,25.000013 30,23.345991 28.653999,22.000013 27,22.000013z M5,22C3.3460007,22 2,23.346 2,25 2,26.654 3.3460007,28 5,28 6.6540003,28 8,26.654 8,25 8,23.346 6.6540003,22 5,22z M16,2C14.346,2 13,3.3460007 13,5 13,6.6540003 14.346,8 16,8 17.654,8 19,6.6540003 19,5 19,3.3460007 17.654,2 16,2z M16,0C18.757,0 21,2.243 21,5 21,6.2061872 20.570676,7.3139925 19.856814,8.178627L19.749571,8.3023939 26.702677,20.009615 26.74309,20.006532C26.828189,20.002204 26.913844,20.000013 27,20.000013 29.757004,20.000013 32,22.242994 32,25.000013 32,27.757002 29.757004,30.000013 27,30.000013 24.242996,30.000013 22,27.757002 22,25.000013 22,23.104563 23.060166,21.452075 24.618662,20.604373L24.703606,20.560808 18.143555,9.5162544 17.944359,9.6064529C17.346375,9.8598127 16.68925,10 16,10 15.224594,10 14.489846,9.8225756 13.834221,9.5061913L13.749143,9.4625559 7.9581623,20.972196 7.9896793,20.994624C9.2092552,21.907268 10,23.363032 10,25 10,27.757 7.757,30 5,30 2.243,30 0,27.757 0,25 0,22.243 2.243,20 5,20 5.3446255,20 5.6812191,20.035047 6.0064049,20.101764L6.1420031,20.133139 12.151809,8.1885796 12.143186,8.178627C11.429324,7.3139925 11,6.2061872 11,5 11,2.243 13.243,0 16,0z"/>
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M27,22.000013C25.346001,22.000013 24,23.345991 24,25.000013 24,26.654005 25.346001,28.000013 27,28.000013 28.653999,28.000013 30,26.654005 30,25.000013 30,23.345991 28.653999,22.000013 27,22.000013z M5,22C3.3460007,22 2,23.346 2,25 2,26.654 3.3460007,28 5,28 6.6540003,28 8,26.654 8,25 8,23.346 6.6540003,22 5,22z M16,2C14.346,2 13,3.3460007 13,5 13,6.6540003 14.346,8 16,8 17.654,8 19,6.6540003 19,5 19,3.3460007 17.654,2 16,2z M16,0C18.757,0 21,2.243 21,5 21,6.2061872 20.570676,7.3139925 19.856814,8.178627L19.749571,8.3023939 26.702677,20.009615 26.74309,20.006532C26.828189,20.002204 26.913844,20.000013 27,20.000013 29.757004,20.000013 32,22.242994 32,25.000013 32,27.757002 29.757004,30.000013 27,30.000013 24.242996,30.000013 22,27.757002 22,25.000013 22,23.104563 23.060166,21.452075 24.618662,20.604373L24.703606,20.560808 18.143555,9.5162544 17.944359,9.6064529C17.346375,9.8598127 16.68925,10 16,10 15.224594,10 14.489846,9.8225756 13.834221,9.5061913L13.749143,9.4625559 7.9581623,20.972196 7.9896793,20.994624C9.2092552,21.907268 10,23.363032 10,25 10,27.757 7.757,30 5,30 2.243,30 0,27.757 0,25 0,22.243 2.243,20 5,20 5.3446255,20 5.6812191,20.035047 6.0064049,20.101764L6.1420031,20.133139 12.151809,8.1885796 12.143186,8.178627C11.429324,7.3139925 11,6.2061872 11,5 11,2.243 13.243,0 16,0z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -875,8 +887,8 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black" Geometry="M106.965,252.716H262.11c5.522,0,10-4.477,10-10v-77.572c0-5.523-4.478-10-10-10h-67.572V10c0-5.523-4.478-10-10-10 h-77.572c-5.522,0-10,4.477-10,10v232.716C96.965,248.239,101.443,252.716,106.965,252.716z M116.965,20h57.572v145.144 c0,5.523,4.478,10,10,10h67.572v57.572H116.965V20z"/>
<GeometryDrawing Brush="Black" Geometry="M417.253,193.93h-77.571c-5.522,0-10,4.477-10,10v67.572H262.11c-5.522,0-10,4.477-10,10v67.572H29.393 c-5.522,0-10,4.477-10,10v77.572c0,5.523,4.478,10,10,10h387.86c5.522,0,10-4.477,10-10V203.93 C427.253,198.407,422.776,193.93,417.253,193.93z M39.393,369.075h290.289v57.572H39.393V369.075z M407.253,426.646h-57.571 v-67.572c0-5.523-4.478-10-10-10H272.11v-57.572h67.572c5.522,0,10-4.477,10-10V213.93h57.571V426.646z"/>
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M106.965,252.716H262.11c5.522,0,10-4.477,10-10v-77.572c0-5.523-4.478-10-10-10h-67.572V10c0-5.523-4.478-10-10-10 h-77.572c-5.522,0-10,4.477-10,10v232.716C96.965,248.239,101.443,252.716,106.965,252.716z M116.965,20h57.572v145.144 c0,5.523,4.478,10,10,10h67.572v57.572H116.965V20z"/>
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}" Geometry="M417.253,193.93h-77.571c-5.522,0-10,4.477-10,10v67.572H262.11c-5.522,0-10,4.477-10,10v67.572H29.393 c-5.522,0-10,4.477-10,10v77.572c0,5.523,4.478,10,10,10h387.86c5.522,0,10-4.477,10-10V203.93 C427.253,198.407,422.776,193.93,417.253,193.93z M39.393,369.075h290.289v57.572H39.393V369.075z M407.253,426.646h-57.571 v-67.572c0-5.523-4.478-10-10-10H272.11v-57.572h67.572c5.522,0,10-4.477,10-10V213.93h57.571V426.646z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
@@ -916,7 +928,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="1" />
<Pen Brush="{mbts:BTSColor UI.icon_foreground}" Thickness="1" />
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<GeometryGroup>
@@ -933,7 +945,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black"
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M18.51899,0L29.981999,0 29.981999,32 18.51899,32z M0,0L11.464992,0 11.464992,32 0,32z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -945,7 +957,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black"
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M23.679005,26.495C23.99901,26.495 24.265014,26.59 24.475017,26.78 24.685019,26.967 24.79102,27.204 24.79102,27.491 24.79102,27.783 24.68802,28.024 24.481016,28.214 24.271013,28.405 24.00301,28.5 23.679005,28.5 23.354001,28.5 23.085997,28.403 22.870994,28.208 22.655991,28.006 22.54899,27.766 22.54899,27.491 22.54899,27.207 22.655991,26.97 22.870994,26.78 23.085997,26.59 23.354001,26.495 23.679005,26.495z M23.734006,19.5C24.110011,19.5 24.460016,19.541 24.785021,19.625 25.110025,19.708 25.392029,19.839 25.632032,20.017 25.874036,20.195 26.062038,20.422 26.19904,20.7 26.333042,20.978 26.401043,21.311 26.401043,21.7 26.401043,21.947 26.373043,22.171 26.316042,22.371 26.259041,22.572 26.17704,22.762 26.067039,22.94 25.958037,23.118 25.822035,23.289 25.660033,23.453 25.498031,23.617 25.311028,23.786 25.101026,23.961 24.959023,24.078 24.835021,24.187 24.72802,24.289 24.620018,24.39 24.531017,24.49 24.460016,24.589 24.389015,24.689 24.335014,24.794 24.299014,24.905 24.262013,25.017 24.244013,25.144 24.244013,25.285 24.244013,25.383 24.257013,25.482 24.283013,25.583 24.310014,25.685 24.345015,25.77 24.390016,25.838L22.791993,25.838C22.751992,25.733 22.719992,25.615 22.697992,25.483 22.674991,25.351 22.663991,25.226 22.663991,25.109 22.663991,24.922 22.681992,24.752 22.718992,24.599 22.754992,24.445 22.809993,24.3 22.882994,24.164 22.955995,24.029 23.046997,23.898 23.155998,23.775 23.266,23.652 23.393002,23.529 23.539003,23.407 23.694005,23.278 23.828007,23.157 23.943009,23.046 24.059011,22.935 24.157012,22.823 24.238013,22.712 24.320014,22.6 24.380015,22.485 24.420015,22.365 24.460016,22.246 24.481016,22.113 24.481016,21.967 24.481016,21.841 24.459016,21.725 24.414016,21.618 24.369015,21.51 24.306014,21.419 24.222013,21.341 24.140012,21.264 24.03901,21.203 23.918009,21.159 23.799007,21.115 23.667005,21.092 23.521003,21.092 23.204998,21.092 22.876994,21.158 22.536989,21.29 22.195985,21.422 21.87398,21.621 21.569976,21.888L21.569976,20.059C21.88298,19.872 22.222985,19.733 22.59099,19.64 22.959995,19.547 23.341001,19.5 23.734006,19.5z M23.985949,18C20.670016,18 17.972007,20.69101 17.972007,24 17.972007,27.309021 20.670016,30 23.985949,30 27.302003,30 30.000989,27.309021 30.000989,24 30.000989,20.69101 27.302003,18 23.985949,18z M14,2C10.968,2 8.5,4.467 8.5,7.5 8.5,10.438219 10.81616,12.84526 13.717461,12.992831L13.973991,12.99934 13.99997,12.999012 14.027442,12.999304 14.28254,12.992831C17.18384,12.84526 19.5,10.438219 19.5,7.5 19.5,4.467 17.032,2 14,2z M14,0C18.136,0 21.5,3.3640003 21.5,7.5 21.5,9.95575 20.314059,12.13934 18.484733,13.508212L18.265061,13.664531 18.342722,13.68891C19.921902,14.202736,21.408062,14.995103,22.721599,16.037384L22.786062,16.089699 22.966662,16.064397C23.300515,16.021905 23.640717,16 23.985949,16 28.405035,16 32.000998,19.588989 32.000998,24 32.000998,28.411011 28.405035,32 23.985949,32 19.566984,32 15.971998,28.411011 15.971998,24 15.971998,20.96743 17.671191,18.323394 20.169134,16.967021L20.4065,16.845489 20.189965,16.712549C18.448412,15.669154,16.455822,15.076083,14.409823,15.006025L14.033219,14.999578 14,15 13.960834,14.999503 13.690718,15.00291C7.2160797,15.166433,1.9999962,20.449734,1.9999962,26.921989L0,26.928989 0,26.921989C0,20.80439,3.9878531,15.595488,9.51441,13.731782L9.7309766,13.661711 9.5152669,13.508212C7.6859417,12.13934 6.5,9.95575 6.5,7.5 6.5,3.3640003 9.8640003,0 14,0z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -958,7 +970,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M23,14C24.10498,14 25,14.89502 25,16 25,17.10498 24.10498,18 23,18 21.89502,18 21,17.10498 21,16 21,14.89502 21.89502,14 23,14z M16,14C17.105,14 18,14.895 18,16 18,17.105 17.105,18 16,18 14.895,18 14,17.105 14,16 14,14.895 14.895,14 16,14z M9,14C10.105,14 11,14.895 11,16 11,17.105 10.105,18 9,18 7.8950005,18 7,17.105 7,16 7,14.895 7.8950005,14 9,14z M16,2C8.2799997,2 2,8.2800002 2,16 2,23.72 8.2799997,30 16,30 23.72,30 30,23.72 30,16 30,8.2800002 23.72,2 16,2z M16,0C24.822,0 32,7.178 32,16 32,24.822 24.822,32 16,32 7.1780005,32 0,24.822 0,16 0,7.178 7.1780005,0 16,0z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -971,7 +983,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M299.152955025434,0L496.426178902388,0 268.890046089888,280.247772216797 198.190201729536,367.407897949219 199.87503144145,369.495361328125 271.502137154341,457.706451416016 498.999970406294,738 301.717621773481,738 101.234124153852,491.008697509766 99.5304498374462,488.936553955078 98.6409082114697,490.003173828125 0,368.488952636719 98.6409082114697,246.945526123047z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1008,7 +1020,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M 5.3507926 32.045363 H 17.81645 v 2.401501 H 5.3507926 Z m 0 -4.937233 H 24.132995 v 2.401501 H 5.3507926 Z M 18.799228 22.222041 v 2.32929 h -1.567173 v -2.264886 q -2.179014 -0.01073 -4.057475 -0.987533 v -2.973336 q 0.622575 0.504501 1.856993 0.933864 1.234417 0.429362 2.200482 0.493766 v -3.907198 q -2.511771 -0.933863 -3.4349 -2.018003 -0.912395 -1.094874 -0.912395 -2.662047 0 -1.685248 1.191482 -2.8659947 1.19148 -1.1914807 3.155813 -1.3846939 V 4.918734 h 1.567173 v 1.9535992 q 2.264887 0.1073411 3.38123 0.7299162 V 10.500446 Q 20.677689 9.5880506 18.799228 9.3841034 v 4.0682086 q 2.35076 0.847992 3.370495 1.942866 1.019736 1.08414 1.019736 2.629845 0 1.781854 -1.137811 2.876728 -1.13781 1.08414 -3.25242 1.32029 z M 17.232055 12.829737 V 9.4270397 q -1.492034 0.2683516 -1.492034 1.5564393 0 1.105608 1.492034 1.846258 z m 1.567173 3.628113 v 3.25242 q 1.53497 -0.236149 1.53497 -1.53497 0 -1.051939 -1.53497 -1.71745 z m 13.258587 13.65074 h -3.954117 l 0.0014 7.641804 -7.643169 -10e-7 0.0014 3.952751 h 7.640439 l 0.0014 7.641803 h 3.951385 l 0.0014 -7.641802 7.640437 -10e-7 0.0014 -3.952751 -7.643169 10e-7 z M 5.3507926 36.982596 h 5.5381524 v 2.401501 H 5.3507926 Z M 1.3730469 0 A 1.3741374 1.3741374 0 0 0 0 1.3730469 V 45.630859 a 1.3741374 1.3741374 0 0 0 1.3730469 1.373047 H 16.853516 a 17.906029 17.906029 0 0 1 -0.05859 -1.373047 17.906029 17.906029 0 0 1 0.06836 -1.375 H 2.7480469 V 2.7480469 H 33.326172 V 27.783203 a 17.906029 17.906029 0 0 1 1.375 -0.05859 17.906029 17.906029 0 0 1 1.373047 0.06836 V 1.3730469 A 1.3741374 1.3741374 0 0 0 34.701172 0 Z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1020,7 +1032,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="m 26.463403 27.963318 h -2.359042 l -0.683586 -2.137882 h -3.417929 l -0.676884 2.137882 h -2.345638 l 3.498351 -9.610414 h 2.566798 z M 22.92484 24.163384 21.89276 20.933106 q -0.113931 -0.361899 -0.160844 -0.864535 h -0.05361 q -0.03351 0.422214 -0.167545 0.837727 l -1.045485 3.257086 z m 2.065112 7.055953 -1.51223 1.51223 2.923087 2.922042 -2.923087 2.923086 1.51223 1.511185 2.922042 -2.922042 2.923086 2.922042 1.511185 -1.511185 -2.922041 -2.923086 2.922041 -2.922042 -1.511185 -1.51223 -2.923086 2.923087 z m -9.484387 0.416058 h 6.108175 v 1.176737 h -6.108175 z m 0 -2.419244 h 9.203284 v 1.176735 h -9.203284 z m 0 4.83849 h 2.713696 v 1.176736 H 15.505565 Z M 13.556469 15.93316 a 0.67332766 0.67332766 0 0 0 -0.672794 0.672793 v 21.686339 a 0.67332766 0.67332766 0 0 0 0.672794 0.672793 h 7.585433 a 8.7739586 8.7739586 0 0 1 -0.02871 -0.672793 8.7739586 8.7739586 0 0 1 0.0335 -0.673751 H 14.230219 V 17.279703 h 14.983289 v 12.267233 a 8.7739586 8.7739586 0 0 1 0.67375 -0.02871 8.7739586 8.7739586 0 0 1 0.672794 0.0335 V 16.605953 A 0.67332766 0.67332766 0 0 0 29.887258 15.93316 Z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1032,7 +1044,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="m 24.706673 31.196264 -3.086181 3.086182 5.96548 5.963348 -5.96548 5.96548 3.086181 3.084049 5.963348 -5.963348 5.96548 5.963348 3.084049 -3.084049 -5.963347 -5.96548 5.963347 -5.963348 -3.084049 -3.086182 -5.96548 5.965481 z M 5.3507926 32.045363 H 17.81645 v 2.401501 H 5.3507926 Z m 0 -4.937233 H 24.132994 v 2.401501 H 5.3507926 Z M 26.434166 6.0031559 14.993666 17.443656 9.6399578 12.089946 6.0100982 15.719802 c 2.9622322 2.959826 6.0772448 6.092263 8.9969908 8.970151 L 30.064121 9.6329208 Z M 5.3507926 36.982596 h 5.5381524 v 2.401501 H 5.3507926 Z M 1.3730469 0 A 1.3741374 1.3741374 0 0 0 0 1.3730469 V 45.630859 a 1.3741374 1.3741374 0 0 0 1.3730469 1.373047 H 16.853516 a 17.906029 17.906029 0 0 1 -0.05859 -1.373047 17.906029 17.906029 0 0 1 0.06836 -1.375 H 2.7480469 V 2.7480469 H 33.326172 V 27.783203 a 17.906029 17.906029 0 0 1 1.375 -0.05859 17.906029 17.906029 0 0 1 1.373047 0.06836 V 1.3730469 A 1.3741374 1.3741374 0 0 0 34.701172 0 Z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1043,7 +1055,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="Black"
<GeometryDrawing Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="m 18.887708 18.355738 v 1.910329 h -1.285291 v -1.857509 q -1.787082 -0.0088 -3.327671 -0.809909 v -2.438532 q 0.510595 0.413758 1.522982 0.765893 1.012387 0.352134 1.804689 0.404954 V 13.12654 q -2.059987 -0.765892 -2.817076 -1.655032 -0.748286 -0.897943 -0.748286 -2.1832341 0 -1.382128 0.977174 -2.3504979 0.977173 -0.9771733 2.588188 -1.1356339 V 4.1647166 h 1.285291 v 1.6022121 q 1.85751 0.088034 2.77306 0.5986287 v 2.376908 Q 20.428297 7.9941795 18.887708 7.8269156 V 11.16339 q 1.927937 0.695466 2.764256 1.593409 0.83632 0.88914 0.83632 2.156824 0 1.461358 -0.933157 2.359301 -0.933156 0.88914 -2.667419 1.082814 z M 17.602417 10.652795 V 7.8621291 q -1.223667 0.2200841 -1.223667 1.2764876 0 0.9067463 1.223667 1.5141783 z m 1.285291 2.975537 v 2.667419 q 1.258881 -0.193674 1.258881 -1.258881 0 -0.86273 -1.258881 -1.408538 z M 7.8581956 26.412174 H 18.081699 v 1.969551 H 7.8581956 Z m 0 -4.04919 H 23.262109 v 1.969551 H 7.8581956 Z m 23.8237084 -0.610988 -12.064762 12.064763 4.39301 4.393009 12.064761 -12.064762 z m -0.0014 3.48158 0.91143 0.91143 -8.583182 8.583183 -0.91143 -0.91143 z m -12.817392 9.398532 -1.583992 5.915617 5.914283 -1.585326 z M 7.8581956 30.461364 H 12.40022 v 1.969551 H 7.8581956 Z M 4.5959132 0.13069836 A 1.1269761 1.1269761 0 0 0 3.4698314 1.2567801 V 37.554094 a 1.1269761 1.1269761 0 0 0 1.1260818 1.126082 H 15.310509 a 8.0150307 8.0150307 0 0 1 -0.01762 -0.337985 8.0150307 8.0150307 0 0 1 0.240273 -1.915781 H 5.7235967 V 2.3844637 H 30.801741 V 20.486667 a 8.0150307 8.0150307 0 0 1 0.863383 -0.04966 8.0150307 8.0150307 0 0 1 1.390383 0.12334 V 1.2567801 A 1.1269761 1.1269761 0 0 0 31.929425 0.13069836 Z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1079,7 +1091,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M 9.9417804 -0.02293875 1.9154138 8.0034278 17.4301 23.512569 1.9154138 39.027256 9.9417804 47.048077 25.450922 31.538936 40.965607 47.048077 48.986428 39.027256 33.47729 23.512569 48.986428 8.0034278 40.965607 -0.02293875 25.450922 15.491746 Z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1092,7 +1104,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="m 69.641861 87.129943 a 36.813842 36.813842 0 0 0 -36.813776 36.813767 36.813842 36.813842 0 0 0 31.044618 36.31355 l 1.208194 21.35839 1.804543 31.90967 a 41.092964 41.092964 0 0 0 -29.828154 39.50509 41.092964 41.092964 0 0 0 41.09258 41.0931 41.092964 41.092964 0 0 0 41.093104 -41.0931 41.092964 41.092964 0 0 0 -0.10284 -2.9068 41.092964 41.092964 0 0 0 -38.048841 -38.03902 l -2.962092 -52.36321 a 36.813842 36.813842 0 0 0 20.093347 -12.61939 l 33.150946 15.70705 v 5.3e-4 a 27.542416 27.542416 0 0 0 -0.12557 2.49907 27.542416 27.542416 0 0 0 27.54251 27.54249 27.542416 27.542416 0 0 0 27.5425 -27.5425 27.542416 27.542416 0 0 0 -27.5425 -27.5425 27.542416 27.542416 0 0 0 -22.44618 11.61479 l -31.44563 -14.89935 a 36.813842 36.813842 0 0 0 1.55701 -10.53786 36.813842 36.813842 0 0 0 -36.813769 -36.813767 z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1104,7 +1116,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M 36.50044 86.999995 A 36.813842 36.813842 0 0 0 -0.3133356 123.81377 36.813842 36.813842 0 0 0 30.731282 160.12732 l 1.208194 21.35839 1.804543 31.90967 a 41.092964 41.092964 0 0 0 -29.8281546 39.50509 41.092964 41.092964 0 0 0 41.0925806 41.0931 41.092964 41.092964 0 0 0 41.093099 -41.0931 41.092964 41.092964 0 0 0 -0.102836 -2.9068 41.092964 41.092964 0 0 0 -38.04884 -38.03902 l -2.962092 -52.36321 a 36.813842 36.813842 0 0 0 20.093347 -12.61939 l 33.150948 15.70705 v 5.3e-4 a 27.542416 27.542416 0 0 0 -0.125574 2.49907 27.542416 27.542416 0 0 0 27.542513 27.54249 27.542416 27.542416 0 0 0 27.5425 -27.5425 27.542416 27.542416 0 0 0 -27.5425 -27.5425 27.542416 27.542416 0 0 0 -22.44618 11.61479 L 71.757201 134.35163 A 36.813842 36.813842 0 0 0 73.314213 123.81377 36.813842 36.813842 0 0 0 36.50044 86.999995 Z m 116.60981 115.628085 0.007 37.48967 h -37.49716 l 0.007 19.39257 h 37.48344 L 153.11751 297 h 19.38493 l 0.007 -37.48968 h 37.48342 L 210 240.11775 h -37.49727 l 0.007 -37.48967 z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1116,7 +1128,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M 37.03498 86.999995 A 36.813842 36.813842 0 0 0 0.22120379 123.81377 36.813842 36.813842 0 0 0 31.265822 160.12731 l 1.208194 21.35839 1.804543 31.90967 a 41.092964 41.092964 0 0 0 -29.8281552 39.50509 41.092964 41.092964 0 0 0 41.0925812 41.0931 41.092964 41.092964 0 0 0 41.093099 -41.0931 41.092964 41.092964 0 0 0 -0.102836 -2.9068 41.092964 41.092964 0 0 0 -38.04884 -38.03902 l -2.962092 -52.36321 a 36.813842 36.813842 0 0 0 20.093346 -12.61938 l 33.150949 15.70704 v 5.3e-4 a 27.542416 27.542416 0 0 0 -0.125574 2.49907 27.542416 27.542416 0 0 0 27.542503 27.54249 27.542416 27.542416 0 0 0 27.5425 -27.5425 27.542416 27.542416 0 0 0 -27.5425 -27.54249 27.542416 27.542416 0 0 0 -22.44618 11.61479 L 72.29174 134.35163 A 36.813842 36.813842 0 0 0 73.848752 123.81377 36.813842 36.813842 0 0 0 37.03498 86.999995 Z m 94.72671 153.117755 0.008 18.80608 c 0 0 -6.42588 -1.1e-4 78.75576 1.3e-4 l 0.009 -18.80621 c 0 0 4.48208 0 -78.77178 0 z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1129,7 +1141,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M 37.03498 86.999995 A 36.813842 36.813842 0 0 0 0.2212037 123.81377 36.813842 36.813842 0 0 0 31.265822 160.12732 l 1.208194 21.35839 1.804543 31.90967 a 41.092964 41.092964 0 0 0 -29.8281553 39.50509 41.092964 41.092964 0 0 0 41.0925813 41.0931 41.092964 41.092964 0 0 0 41.093099 -41.0931 41.092964 41.092964 0 0 0 -0.102836 -2.9068 41.092964 41.092964 0 0 0 -38.04884 -38.03902 l -2.962092 -52.36321 a 36.813842 36.813842 0 0 0 20.093347 -12.61939 l 33.150948 15.70705 v 5.3e-4 a 27.542416 27.542416 0 0 0 -0.125574 2.49907 27.542416 27.542416 0 0 0 27.542503 27.54249 27.542416 27.542416 0 0 0 27.5425 -27.5425 27.542416 27.542416 0 0 0 -27.5425 -27.5425 27.542416 27.542416 0 0 0 -22.44618 11.61479 L 72.291741 134.35163 A 36.813842 36.813842 0 0 0 73.848753 123.81377 36.813842 36.813842 0 0 0 37.03498 86.999995 Z m 145.03189 90.486735 -76.71431 76.71435 27.93311 27.93293 L 210 205.41986 Z m -0.008 22.13769 5.79522 5.79544 -54.57645 54.57644 -5.7952 -5.79522 z m -81.50016 59.76108 -10.071854 37.61451 37.606034 -10.0803 z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1141,7 +1153,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="m 139.80129 180.78831 a 20.783495 20.783495 0 0 0 -20.78345 20.78346 20.783495 20.783495 0 0 0 17.52643 20.50105 l 0.68209 12.05803 1.01877 18.01482 a 23.1993 23.1993 0 0 0 -16.83968 22.30284 23.1993 23.1993 0 0 0 23.1991 23.19938 23.1993 23.1993 0 0 0 23.19935 -23.19938 23.1993 23.1993 0 0 0 -0.058 -1.64105 23.1993 23.1993 0 0 0 -21.4807 -21.47518 l -1.67225 -29.56198 a 20.783495 20.783495 0 0 0 11.34383 -7.12437 l 18.71558 8.86752 v 3.1e-4 a 15.549251 15.549251 0 0 0 -0.0708 1.41086 15.549251 15.549251 0 0 0 15.5493 15.54927 15.549251 15.549251 0 0 0 15.54931 -15.54927 15.549251 15.549251 0 0 0 -15.54931 -15.54932 15.549251 15.549251 0 0 0 -12.67213 6.5572 l -17.75282 -8.41152 a 20.783495 20.783495 0 0 0 0.87902 -5.94921 20.783495 20.783495 0 0 0 -20.78345 -20.78346 z M 31.983713 88.265691 A 30.676234 30.676234 0 0 0 1.3075339 118.94187 30.676234 30.676234 0 0 0 27.17639 149.20122 l 1.006764 17.79752 1.503689 26.58969 A 34.241941 34.241941 0 0 0 4.8316412 226.50722 34.241941 34.241941 0 0 0 39.073263 260.74928 34.241941 34.241941 0 0 0 73.315316 226.50722 34.241941 34.241941 0 0 0 73.229625 224.08505 34.241941 34.241941 0 0 0 41.524291 192.3879 L 39.05604 148.75469 A 30.676234 30.676234 0 0 0 55.799422 138.2392 l 27.624018 13.08837 v 4.4e-4 a 22.950541 22.950541 0 0 0 -0.104638 2.08242 22.950541 22.950541 0 0 0 22.950618 22.9506 22.950541 22.950541 0 0 0 22.95062 -22.9506 22.950541 22.950541 0 0 0 -22.95062 -22.95061 22.950541 22.950541 0 0 0 -18.703946 9.67837 L 61.362463 127.72286 A 30.676234 30.676234 0 0 0 62.65989 118.94187 30.676234 30.676234 0 0 0 31.983713 88.265691 Z" />
</DrawingGroup.Children>
</DrawingGroup>
@@ -1153,7 +1165,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M22.889013,19.178117C23.365026,19.178117 23.75202,19.56508 23.75202,20.040055 23.75202,20.516007 23.365026,20.904069 22.889013,20.904069 22.413031,20.904069 22.02503,20.516007 22.02503,20.040055 22.02503,19.56508 22.413031,19.178117 22.889013,19.178117z M22.889013,15.528093C20.40003,15.528092 18.375006,17.552018 18.375006,20.040055 18.375006,22.529069 20.40003,24.554093 22.889013,24.554093 25.37702,24.554093 27.402014,22.529069 27.402014,20.040055 27.402014,17.552018 25.37702,15.528092 22.889013,15.528093z M22.167028,12.426042L23.834021,12.426042 23.834021,13.935075 23.981024,13.958024C24.912024,14.12404,25.772009,14.500016,26.509008,15.03505L26.62702,15.12404 27.757025,13.993058 28.935034,15.171037 27.806005,16.301042 27.940008,16.484025C28.422033,17.167008,28.769018,17.950089,28.943029,18.796037L28.996008,19.093034 30.500006,19.093034 30.500006,20.759049 29.02503,20.759049 28.997015,20.980119C28.848028,21.952043,28.472021,22.85109,27.924017,23.619035L27.805029,23.779069 28.935034,24.909074 27.757025,26.088029 26.62702,24.958024 26.61701,24.966081C25.866034,25.536027,24.981024,25.938004,24.019018,26.117082L23.834021,26.147111 23.834021,27.426042 22.167028,27.426042 22.167028,26.177018 21.949011,26.149064C20.942022,25.995011,20.014013,25.596086,19.228033,25.016007L19.151007,24.957047 18.019018,26.088029 16.841009,24.909074 17.972021,23.779069 17.863013,23.633073C17.294012,22.839005,16.908026,21.905046,16.767034,20.894059L16.75202,20.759049 15.500006,20.759049 15.500006,19.093034 16.783026,19.093034 16.812017,18.91005C16.991034,17.947038,17.393011,17.063004,17.963019,16.312028L17.972021,16.301042 16.841009,15.171037 18.019018,13.993058 19.15003,15.123063 19.310034,15.005021C20.078009,14.458024,20.977026,14.081071,21.949011,13.932023L22.167028,13.905046z M16.000014,6.0000114C14.346014,6.0000114 13.000014,7.3460112 13.000014,9.0000114 13.000014,10.654011 14.346014,12.000011 16.000014,12.000011 17.654014,12.000011 19.000014,10.654011 19.000014,9.0000114 19.000014,7.3460112 17.654014,6.0000114 16.000014,6.0000114z M16.000014,4.0000114C18.757014,4.0000114 21.000014,6.2430115 21.000014,9.0000114 21.000014,11.757011 18.757014,14.000011 16.000014,14.000011 13.243014,14.000011 11.000014,11.757011 11.000014,9.0000114 11.000014,6.2430115 13.243014,4.0000114 16.000014,4.0000114z M16.000123,0C24.651189,0 31.47924,8.0160384 31.765244,8.3580403 32.078245,8.7290418 32.078245,9.2720447 31.765244,9.6440461 31.700243,9.7220466 30.130231,11.580056 27.56321,13.533065L26.352201,11.941058C27.853213,10.798052 29.000222,9.6770465 29.645226,9.0010433 27.881213,7.1610341 22.366171,2.0000095 16.000123,2.0000095 9.6350741,2.0000095 4.1210318,7.1590343 2.3550186,9.0000434 4.013031,10.731052 8.9770689,15.388074 14.846114,15.946077L14.657112,17.936087C6.6410513,17.175083 0.49200439,9.9510479 0.23400211,9.6430461 -0.078000069,9.2720447 -0.078000069,8.7290418 0.23400211,8.3580403 0.52000427,8.0160384 7.3480568,0 16.000123,0z"
/>
</DrawingGroup.Children>
@@ -1166,7 +1178,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M23.471008,4.2230202L32,12.800012 23.471008,20.360005 23.471008,16.322012C23.471008,16.322012 8.7430115,12.294 5.0610046,20.360005 5.0610046,18.343985 6.3990173,8.2560073 23.471008,8.2560073z M0,0L21.334015,0 21.334015,2.1340014 21.334015,4.2690102 19.200012,4.2690102 19.200012,2.1340014 2.1340027,2.1340014 2.1340027,23.466997 19.200012,23.466997 19.200012,21.331989 21.334015,21.331989 21.334015,23.466997 21.334015,25.601 19.200012,25.601 0,25.601 0,23.466997 0,2.1340014z"
/>
</DrawingGroup.Children>
@@ -1179,7 +1191,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M13.594002,4.2219891L13.594002,8.255989C30.661003,8.255989 32,18.345018 32,20.360004 28.319,12.293011 13.594002,16.32399 13.594002,16.32399L13.594002,20.360004 5.0610008,12.798993z M0,0L21.333,0 21.333,2.1329974 21.333,4.2670025 19.201,4.2670025 19.201,2.1329974 2.1330032,2.1329974 2.1330032,23.467003 19.201,23.467003 19.201,21.332998 21.333,21.332998 21.333,23.467003 21.333,25.6 19.201,25.6 0,25.6 0,23.467003 0,2.1329974z"
/>
</DrawingGroup.Children>
@@ -1192,7 +1204,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M25.400009,21.400005L29.800003,21.400005C28.100006,24.100017 25.400009,26.200025 22.300003,27.200025 23,26.500013 23.600006,25.600019 24.199997,24.600017 24.699997,23.600017 25.100006,22.500011 25.400009,21.400005z M19,21.400005L24,21.400005C23.699997,22.299998 23.300003,23.200023 22.900009,23.900005 21.800003,26.000013 20.400009,27.3 18.900009,27.700025z M12.300003,21.400005L17.699997,21.400005 17.600006,27.8C16,27.600019 14.5,26.200025 13.300003,23.900005 13,23.100017 12.600006,22.299998 12.300003,21.400005z M26.5,14.700019L32,14.700019C31.900009,16.600013,31.400009,18.400003,30.600006,20.100015L25.900009,20.100015C26.199997,18.400003,26.5,16.600013,26.5,14.700019z M19.100006,14.700019L25.199997,14.700019C25.100006,16.600013,24.900009,18.400003,24.400009,20.100015L19,20.100015z M16.199997,14.700019L17.699997,14.700019 17.699997,20.100015 12,20.100015C11.900009,19.799997,11.900009,19.500009,11.800003,19.200021L15.5,15.500007z M25.900009,7.7999913L30.600006,7.7999913C31.400009,9.500004,31.900009,11.399999,32,13.4L26.5,13.4C26.5,11.399999,26.300003,9.500004,25.900009,7.7999913z M19.100006,7.7999913L24.5,7.7999913C24.900009,9.500004,25.199997,11.399999,25.199997,13.4L19.100006,13.4z M12.100006,7.7999913L17.800003,7.7999913 17.800003,13.4 11.300003,13.4C11.300003,11.299993,11.600006,9.3999979,12.100006,7.7999913z M22.5,0.6000061C25.600006,1.6000066,28.199997,3.6000077,29.900009,6.3999969L25.600006,6.3999969C25.300003,5.2999904 24.900009,4.2000138 24.400009,3.2999892 23.800003,2.2999887 23.199997,1.3999944 22.5,0.6000061z M14.199997,0.6000061C13.5,1.2999882 12.900009,2.2000131 12.300003,3.2000136 10.900009,5.8999965 10,9.8999988 9.9000092,14.000007L9.9000092,14.700019 14.5,14.700019 7.1999969,22.000011 0,14.600013 4.5,14.600013 4.5,13.900001C4.5,7.7000157,8.6000061,2.2999887,14.199997,0.6000061z M19.199997,0.1000061C20.600006,0.50000006 22,1.7999884 23.100006,3.8999953 23.5,4.7000143 23.800003,5.5000026 24.100006,6.3999969L19.100006,6.3999969z M17.900009,0L17.800003,6.3999969 12.400009,6.3999969C12.699997,5.5000026 13.100006,4.6000082 13.5,3.7999892 14.699997,1.5000005 16.300003,0.20001213 17.900009,0z"
/>
</DrawingGroup.Children>
@@ -1206,7 +1218,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup.Children>
<GeometryDrawing Geometry="{Binding Source={StaticResource {x:Static mbts:Icons.ArrowGeometryKey}}}">
<GeometryDrawing.Pen>
<Pen Thickness="3" Brush="Black"/>
<Pen Thickness="3" Brush="{mbts:BTSColor UI.icon_foreground}"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -1220,7 +1232,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup.Children>
<GeometryDrawing Geometry="{Binding Source={StaticResource {x:Static mbts:Icons.ArrowGeometryKey}}}">
<GeometryDrawing.Pen>
<Pen Thickness="3" Brush="Black"/>
<Pen Thickness="3" Brush="{mbts:BTSColor UI.icon_foreground}"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -1236,7 +1248,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M21.832001,24.424L21.832001,27.912998 26.218018,27.912998 26.218018,24.424z M13.757019,24.424L13.757019,27.912998 18.143005,27.912998 18.143005,24.424z M5.7820129,24.424L5.7820129,27.912998 10.167999,27.912998 10.167999,24.424z M1.8950195,6.3799964L3.8880005,6.3799964 4.1870117,17.745001 8.1750183,17.745001 8.1750183,13.956 16.050018,17.745001 16.149994,17.745001 16.149994,13.956 24.024994,17.745001 24.125,17.745001 24.125,13.956 32,17.745001 31.901001,17.745001 31.901001,31.103001 0,31.103001 0,17.745001 1.5950012,17.745001z M8.5740051,1.3959956C8.5740051,1.3959958 9.1719971,3.2899927 6.6800232,3.5889963 6.6800232,3.5889963 5.085022,3.6879953 4.2870178,4.4859993 4.2870178,4.4859993 3.9880066,4.7849952 3.8880005,4.4859993 3.8880005,4.4859993 3.9880066,3.0899958 6.480011,2.6919931 6.5800171,2.7909998 8.3740234,2.6919931 8.5740051,1.3959956z M5.5830078,0C5.5830078,-1.4470834E-07 6.8789978,1.4949948 4.5859985,2.6919931 4.5859985,2.6919931 3.1900024,3.2899927 2.6920166,4.3859931 2.6920166,4.3859931 2.5920105,4.7849952 2.2929993,4.4859993 2.2929993,4.4859993 1.8950195,3.1899942 4.0880127,1.8939967 4.1870117,1.9939953 5.882019,1.2959973 5.5830078,0z"
/>
</DrawingGroup.Children>
@@ -1249,7 +1261,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"
/>
</DrawingGroup.Children>
@@ -1263,15 +1275,30 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Brush="{mbts:BTSColor UI.icon_foreground}"
Geometry="M 0.814453 2e-6 C 0.365092 -7.56e-4 3.21e-4 0.36314 0 0.812502 v 23.945313 c 0.788895 1.126154 1.626953 -0.02112 1.626953 -0.02112 V 1.625002 h 16.241843 c 0 0 0.80538 -0.934678 -0.124651 -1.625002 z M 3.750875 3.43624 C 3.301514 3.435482 2.936743 3.799378 2.936422 4.24874 v 23.945313 c -7.6e-4 0.450124 0.364329 0.815213 0.814453 0.814453 h 7.439453 C 11.042745 28.716852 10.90913 28.418337 10.789937 28.113975 10.699431 27.872971 10.617994 27.628659 10.545796 27.381553 H 4.563375 V 5.06124 h 15.304687 v 7.039063 c 0.173002 -0.01023 0.346234 -0.01609 0.519531 -0.01758 0.343731 -2.6e-4 0.68726 0.01669 1.029297 0.05078 0.02606 0.0038 0.0521 0.0077 0.07813 0.01172 V 4.248738 C 21.494699 3.799376 21.129928 3.43548 20.680567 3.436238 Z m 13.332031 6.884766 c -1.226661 0 -2.230469 1.002456 -2.230469 2.228516 v 9.572265 l -0.824219 -0.8125 v 0.002 c -0.991917 -0.9815 -2.60964 -0.982302 -3.603515 0 -0.48293 0.477155 -0.751953 1.119962 -0.751953 1.796875 0 0.67607 0.269215 1.317958 0.751953 1.794922 l 4.841797 4.78711 0.0078 0.0078 c 2.115591 1.851014 4.144077 2.322266 5.615234 2.322266 0.547268 0 1.017683 -0.06414 1.388672 -0.142578 1.701281 -0.360558 3.108561 -1.372782 3.751954 -2.75586 0.419048 -0.9011 0.978455 -2.062137 0.923828 -3.261718 4.09e-4 -0.01025 0.0011 -0.0075 0.002 -0.02344 0.0015 -0.02655 0.0039 -0.01047 0.0039 -0.07813 v -5.113281 c 0 -1.161114 -0.955476 -2.109375 -2.121094 -2.109375 -0.04803 0 -0.09044 0.0088 -0.136719 0.01172 -0.207784 -1.015547 -1.021531 -1.826172 -2.095703 -1.826172 -0.343265 0 -0.63914 0.145124 -0.93164 0.285156 -0.397916 -0.616533 -1.045784 -1.0625 -1.830078 -1.0625 -0.189487 0 -0.354735 0.07615 -0.53125 0.121094 v -3.515625 c 0 -1.22606 -1.003809 -2.228516 -2.230469 -2.228516 z m 0 1.699219 c 0.148476 0 0.278642 0.05997 0.375 0.15625 0.09685 0.09678 0.15625 0.225142 0.15625 0.373047 v 5.625 1.740234 c 0 0.464846 0.384763 0.849609 0.849609 0.849609 0.464527 0 0.84961 -0.383921 0.84961 -0.849609 v -1.740234 c 0 -0.148598 0.05896 -0.275762 0.15625 -0.373047 0.09729 -0.09729 0.228277 -0.158203 0.375 -0.158203 0.146722 0 0.277709 0.06091 0.375 0.158203 0.09729 0.09729 0.15625 0.224449 0.15625 0.373047 v 0.77539 0.4375 c 0 0.465373 0.384236 0.84961 0.849609 0.84961 0.465373 0 0.849609 -0.384239 0.849609 -0.84961 v -0.425781 l 0.0059 -0.06836 c 0.0286 -0.265146 0.249414 -0.472657 0.52539 -0.472657 0.148238 0 0.278481 0.05996 0.375 0.15625 0.09649 0.09627 0.15625 0.225145 0.15625 0.373047 v 1.935547 c 0 0.465371 0.384237 0.84961 0.84961 0.84961 0.465372 0 0.849609 -0.384237 0.849609 -0.84961 v -0.65039 c 0.0016 -9e-6 2.95e-4 0 0.002 0 0.238806 0 0.421875 0.185008 0.421875 0.410156 l 0.002 5.0625 c -4.64e-4 0.0084 -0.0023 0.0401 -0.0039 0.07227 -0.0015 0.03041 -0.0059 0.02687 -0.002 0.101563 0.03253 0.757506 -0.372777 1.677807 -0.765625 2.521484 h -0.002 c -0.194342 0.418116 -0.532821 0.810028 -0.976563 1.126953 -0.443595 0.316821 -0.990203 0.55925 -1.585937 0.685547 -0.515602 0.10823 -1.298166 0.179402 -2.248047 -0.03125 -0.47546 -0.105441 -0.991926 -0.281044 -1.539063 -0.560547 -0.547306 -0.279588 -1.127683 -0.66381 -1.724609 -1.185547 L 11.620061 23.69601 c -0.162198 -0.160763 -0.248047 -0.365899 -0.248047 -0.587891 0 -0.223284 0.08648 -0.426518 0.248047 -0.585937 0.168295 -0.166151 0.386826 -0.249911 0.607422 -0.25 0.220595 -9e-5 0.437432 0.08144 0.605469 0.248047 l 2.240234 2.207031 0.01367 0.02148 0.04687 0.03516 0.03711 0.0332 0.02539 0.01758 0.01563 0.01367 0.01563 0.0098 c 0.114637 0.07747 0.248043 0.126397 0.388672 0.140625 l -0.04883 -0.0078 0.04102 0.0098 0.08594 0.0039 h 0.0078 c 0.06844 0 0.126002 -0.0085 0.169922 -0.01758 l 0.0059 -0.002 0.01953 -0.0039 h 0.02539 l 0.06641 -0.02734 0.02734 -0.0098 0.0098 -0.0039 c 0.102105 -0.04223 0.198238 -0.105586 0.28125 -0.189454 h 0.002 c 0.163006 -0.165519 0.243191 -0.385955 0.242188 -0.599609 v -0.002 -0.01953 -11.580078 c 0 -0.147904 0.0594 -0.276271 0.15625 -0.373047 0.09636 -0.09628 0.226522 -0.15625 0.375 -0.15625 z"
/>
</DrawingGroup.Children>
</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);
}
}
@@ -1,31 +1,31 @@
using bts.utils;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark {
[ProtoContract]
class GlobalAlertSettings : IEquatable<GlobalAlertSettings> {
[ProtoMember(1)]
TradeAlert[] _trade_alerts;
public GlobalAlertSettings() { }
public GlobalAlertSettings(IEnumerable<TradeAlert> trade_alerts) =>
_trade_alerts = trade_alerts.ToArray();
public IReadOnlyList<TradeAlert> trade_alerts => _trade_alerts ?? Array.Empty<TradeAlert>();
public bool Equals(GlobalAlertSettings other) =>
other != null &&
trade_alerts.SequenceEqual(other.trade_alerts);
public override bool Equals(object obj) =>
Equals(obj as GlobalAlertSettings);
public override int GetHashCode() =>
EqualityComparer.auto_phase(trade_alerts);
}
}
using bts.utils;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark {
[ProtoContract]
class GlobalAlertSettings : IEquatable<GlobalAlertSettings> {
[ProtoMember(1)]
TradeAlert[] _trade_alerts;
public GlobalAlertSettings() { }
public GlobalAlertSettings(IEnumerable<TradeAlert> trade_alerts) =>
_trade_alerts = trade_alerts.ToArray();
public IReadOnlyList<TradeAlert> trade_alerts => _trade_alerts ?? Array.Empty<TradeAlert>();
public bool Equals(GlobalAlertSettings other) =>
other != null &&
trade_alerts.SequenceEqual(other.trade_alerts);
public override bool Equals(object obj) =>
Equals(obj as GlobalAlertSettings);
public override int GetHashCode() =>
EqualityComparer.auto_phase(trade_alerts);
}
}
@@ -1,88 +1,88 @@
using bts;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Composition;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui.settings {
public interface ISavedWindowFactory {
Task<IShellWindow> restore(WindowState window);
}
[Export(typeof(ISavedWindowFactory))]
public class WindowFactory : ISavedWindowFactory {
IShellWindowMigrationRule[] _compat;
[ImportingConstructor]
public WindowFactory([ImportMany] IShellWindowMigrationRule[] migration) {
_compat = migration;
}
[Import]
public ObservableCollection<IShellModule> modules { get; set; }
public Task<IShellWindow> restore(WindowState window) {
Log.Ui.info("Loading window '{0}'".fmt(window.title));
bool changed;
int count = 0;
do {
count++;
changed = apply_compatability_migrations(ref window);
} while (count < 100 && changed);
if (count == 100) {
Log.Persist.error("Error: Window state migration still changing after 100 iterations. Please check IShellWindowMigrationRule logic.");
}
return restore(window.owner, window.app_data, window.position);
}
/// When we apply the migration changes to a window we stop as soon as one "matches" and updates the state
/// Since it's possible that we have multiple levels of migration we run this function until the data stops changing (or 100 iterations)
/// 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) {
foreach (var test in _compat) {
try {
if (test.apply(ref window)) {
return true;
}
} catch (Exception e) {
Log.Persist.error("Exception running compat rule {0}.\n{1}".fmt(test.GetType().Name, e.ToString()));
}
}
return false;
}
private async Task<IShellWindow> restore(string owner_name, byte[] state, WindowPosition windowPosition) {
IShellModule owner = modules.empty_if_null().SingleOrDefault(m => m.key() == owner_name);
if (owner == null) {
Log.Ui.warn("Could not locate module '{0}'".fmt(owner_name));
return null;
}
try {
Task<IShellWindow> task = owner.create(new MemoryStream(state));
if (task != null) {
IShellWindow window = await task;
if (window != null) {
window.set_start_position(windowPosition);
Log.Ui.info($"Restoring window {owner_name} - {window.diagnostic_name()}.");
}
return window;
}
} catch (Exception e) {
Log.Ui.error("Failure to re-create window for module {0}. Error: {1}".fmt(owner_name, e.Message));
}
return null;
}
}
}
using bts;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Composition;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui.settings {
public interface ISavedWindowFactory {
Task<IShellWindow> restore(WindowSettings window);
}
[Export(typeof(ISavedWindowFactory))]
public class WindowFactory : ISavedWindowFactory {
IShellWindowMigrationRule[] _compat;
[ImportingConstructor]
public WindowFactory([ImportMany] IShellWindowMigrationRule[] migration) {
_compat = migration;
}
[Import]
public ObservableCollection<IShellModule> modules { get; set; }
public Task<IShellWindow> restore(WindowSettings window) {
Log.Ui.info("Loading window '{0}'".fmt(window.title));
bool changed;
int count = 0;
do {
count++;
changed = apply_compatability_migrations(ref window);
} while (count < 100 && changed);
if (count == 100) {
Log.Persist.error("Error: Window state migration still changing after 100 iterations. Please check IShellWindowMigrationRule logic.");
}
return restore(window.owner, window.app_data, window.position);
}
/// When we apply the migration changes to a window we stop as soon as one "matches" and updates the state
/// Since it's possible that we have multiple levels of migration we run this function until the data stops changing (or 100 iterations)
/// 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 WindowSettings window) {
foreach (var test in _compat) {
try {
if (test.apply(ref window)) {
return true;
}
} catch (Exception e) {
Log.Persist.error("Exception running compat rule {0}.\n{1}".fmt(test.GetType().Name, e.ToString()));
}
}
return false;
}
private async Task<IShellWindow> restore(string owner_name, byte[] state, WindowPosition windowPosition) {
IShellModule owner = modules.empty_if_null().SingleOrDefault(m => m.key() == owner_name);
if (owner == null) {
Log.Ui.warn("Could not locate module '{0}'".fmt(owner_name));
return null;
}
try {
Task<IShellWindow> task = owner.create(new MemoryStream(state));
if (task != null) {
IShellWindow window = await task;
if (window != null) {
window.set_start_position(windowPosition);
Log.Ui.info($"Restoring window {owner_name} - {window.diagnostic_name()}.");
}
return window;
}
} catch (Exception e) {
Log.Ui.error("Failure to re-create window for module {0}. Error: {1}".fmt(owner_name, e.Message));
}
return null;
}
}
}
@@ -1,86 +1,113 @@
using bts;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Composition;
using System.IO;
using System.Linq;
namespace mbts.ui.settings {
public interface ISettingsCollector {
// Get the save state for every open window
IEnumerable<WindowState> get_window_state();
WindowState get_window_state(IShellWindow window);
IEnumerable<ModuleState> get_module_state();
WindowPosition get_launcher_state();
}
[Export(typeof(ISettingsCollector))]
class SettingsCollector : ISettingsCollector {
WindowManager _wm;
ILauncher _launcher;
[ImportingConstructor]
public SettingsCollector(WindowManager wm, ILauncher launcher) {
_wm = wm;
_launcher = launcher;
}
[Import]
public ObservableCollection<IShellModule> modules { get; set; }
IWindowsInterop _interop => _wm;
public IEnumerable<WindowState> get_window_state() {
foreach (var window in _wm.list_windows()) {
// Check that the window is still open since we might be iterating asyncronously
if (_wm.contains(window)) {
var state = get_window_state(window);
if (state != null) {
yield return state;
}
}
}
}
public IEnumerable<ModuleState> 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() {
name = module.key(),
app_data = out_stream.ToArray(),
};
}
}
public WindowPosition get_launcher_state() {
return _launcher.get_position();
}
public WindowState get_window_state(IShellWindow window) {
try {
if (window == null || window.Owner == null) return null;
MemoryStream out_stream = new MemoryStream();
window.GetState(out_stream);
var pos = _interop.GetPosition(window);
var state = new WindowState() {
owner = window.Owner.key(),
app_data = out_stream.ToArray(),
position = pos,
title = string.IsNullOrEmpty(pos.custom_title) ? window.Title : pos.custom_title
};
return state;
} catch (Exception e) {
Log.Ui.error("Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
}
return null;
}
}
}
using bts;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Composition;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace mbts.ui.settings {
public interface ISettingsCollector {
// Get the save state for every open window
Task<List<WindowSettings>> get_window_state();
Task<WindowSettings> get_window_state(IShellWindow window);
IEnumerable<ModuleSettings> get_module_state();
WindowPosition get_launcher_state();
}
[Export(typeof(ISettingsCollector))]
class SettingsCollector : ISettingsCollector {
WindowManager _wm;
ILauncher _launcher;
[ImportingConstructor]
public SettingsCollector(WindowManager wm, ILauncher launcher) {
_wm = wm;
_launcher = launcher;
}
[Import]
public ObservableCollection<IShellModule> modules { get; set; }
[Import]
public IShellThreadFactory thread_factory { get; set; }
IWindowsInterop _interop => _wm;
public async Task<List<WindowSettings>> get_window_state() {
var windows = _wm.list_windows();
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)) {
var state = await get_window_state(window);
if (state != null) {
rv.Add(state);
}
}
}
return rv;
}
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 ModuleSettings() {
name = module.key(),
app_data = out_stream.ToArray(),
};
}
}
public WindowPosition get_launcher_state() {
return _launcher.get_position();
}
public async Task<WindowSettings> get_window_state(IShellWindow window) {
try {
if (window == null || window.Owner == null) return null;
Dispatcher dispatcher = null;
if (window is ISlowWindow) {
var tf = thread_factory;
if (tf is not null) {
var thread_id = (window as ISharedWindow)?.thread_id;
dispatcher = tf.get_dispatcher(thread_id);
if (dispatcher is null) {
Log.Gen.error(
$"Unable to get window state for '{window.Title}.' " +
$"Didn't find dispatcher for thread {thread_id}.");
return null;
}
}
}
var pos = _interop.GetPosition(window);
var state = new WindowSettings() {
owner = window.Owner.key(),
app_data = dispatcher is null ?
serialize() : (await dispatcher.InvokeAsync(serialize)),
position = pos,
title = string.IsNullOrEmpty(pos.custom_title) ? window.Title : pos.custom_title
};
return state;
byte[] serialize() {
using var ms = new MemoryStream();
window.GetState(ms);
return ms.ToArray();
}
} catch (Exception e) {
Log.Ui.error("Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
}
return null;
}
}
}
@@ -1,95 +1,97 @@
using bts.shell.pnl;
using bts.utils;
using mbts.ui.settings;
using System;
using System.Collections.Generic;
namespace bts.settings {
interface ISettingsMigrationRule<T> {
bool apply(T settings, out T new_settings);
}
// Migration rules of type ISettingsMigrationRule<T>, where 'T' is a common type, such as GridSettings,
// should be exposed through explicitly named interfaces that inherit ISettingsMigrationRule<T>.
// This avoids magic const names in the MEF import/export contracts, which would otherwise be needed to
// determine the various windows to which such rules apply.
interface IFillsGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IMarketViewGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IPAndLGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IPNLSettingsMigrationRule : ISettingsMigrationRule<PNLSettings> { }
interface ISyntheticManagementMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IReconcileReportMigrationRule : ISettingsMigrationRule<GridSettings> { }
static class SettingsMigrationHelper {
public static bool apply<T>(T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, out T new_settings) {
bool applied = false;
foreach (var r in migration_rules) {
if (r.apply(settings, out var migrated)) {
settings = migrated;
applied = true;
}
}
new_settings = applied ? settings : default;
return applied;
}
public static void apply_quiet<T>(ref T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules) {
if (apply(settings, migration_rules, out var new_settings))
settings = new_settings;
}
public static ISettingsMigrationRule<T> merge<T>(this IEnumerable<ISettingsMigrationRule<T>> rules) =>
new MergeWrapper<T>(rules);
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, IImmutableProperty<TOuter, TInner> propi) =>
new SelectorWrapper<TInner, TOuter>(rule, propi.get, propi.set);
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) =>
new SelectorWrapper<TInner, TOuter>(rule, get, set);
class MergeWrapper<T> : ISettingsMigrationRule<T> {
readonly IEnumerable<ISettingsMigrationRule<T>> _rules;
public MergeWrapper(IEnumerable<ISettingsMigrationRule<T>> rules) =>
_rules = rules;
public bool apply(T settings, out T new_settings) {
bool applied = false;
foreach (var r in _rules) {
if (r.apply(settings, out var migrated)) {
settings = migrated;
applied = true;
}
}
new_settings = applied ? settings : default;
return applied;
}
}
class SelectorWrapper<TInner, TOuter> : ISettingsMigrationRule<TOuter> {
readonly ISettingsMigrationRule<TInner> _rule;
readonly Func<TOuter, TInner> _get;
readonly Func<TOuter, TInner, TOuter> _set;
public SelectorWrapper(ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) {
_rule = rule;
_get = get;
_set = set;
}
public bool apply(TOuter settings, out TOuter new_settings) {
if (settings != null) {
var ival = _get(settings);
if (_rule.apply(ival, out var new_ival)) {
new_settings = _set(settings, new_ival);
return true;
}
}
new_settings = default;
return false;
}
}
}
}
using bts.shell.pnl;
using bts.utils;
using mbts.ui.settings;
using System;
using System.Collections.Generic;
namespace bts.settings {
interface ISettingsMigrationRule<T> {
bool apply(T settings, out T new_settings);
}
// Migration rules of type ISettingsMigrationRule<T>, where 'T' is a common type, such as GridSettings,
// should be exposed through explicitly named interfaces that inherit ISettingsMigrationRule<T>.
// This avoids magic const names in the MEF import/export contracts, which would otherwise be needed to
// determine the various windows to which such rules apply.
interface IFillsGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IMarketViewGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IPAndLGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
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) {
bool applied = false;
foreach (var r in migration_rules) {
if (r.apply(settings, out var migrated)) {
settings = migrated;
applied = true;
}
}
new_settings = applied ? settings : default;
return applied;
}
public static void apply_quiet<T>(ref T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules) {
if (apply(settings, migration_rules, out var new_settings))
settings = new_settings;
}
public static ISettingsMigrationRule<T> merge<T>(this IEnumerable<ISettingsMigrationRule<T>> rules) =>
new MergeWrapper<T>(rules);
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, IImmutableProperty<TOuter, TInner> propi) =>
new SelectorWrapper<TInner, TOuter>(rule, propi.get, propi.set);
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) =>
new SelectorWrapper<TInner, TOuter>(rule, get, set);
class MergeWrapper<T> : ISettingsMigrationRule<T> {
readonly IEnumerable<ISettingsMigrationRule<T>> _rules;
public MergeWrapper(IEnumerable<ISettingsMigrationRule<T>> rules) =>
_rules = rules;
public bool apply(T settings, out T new_settings) {
bool applied = false;
foreach (var r in _rules) {
if (r.apply(settings, out var migrated)) {
settings = migrated;
applied = true;
}
}
new_settings = applied ? settings : default;
return applied;
}
}
class SelectorWrapper<TInner, TOuter> : ISettingsMigrationRule<TOuter> {
readonly ISettingsMigrationRule<TInner> _rule;
readonly Func<TOuter, TInner> _get;
readonly Func<TOuter, TInner, TOuter> _set;
public SelectorWrapper(ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) {
_rule = rule;
_get = get;
_set = set;
}
public bool apply(TOuter settings, out TOuter new_settings) {
if (settings != null) {
var ival = _get(settings);
if (_rule.apply(ival, out var new_ival)) {
new_settings = _set(settings, new_ival);
return true;
}
}
new_settings = default;
return false;
}
}
}
}
@@ -1,171 +1,155 @@
using mbts.spark;
using mbts.utils;
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
namespace mbts.persist {
interface IWindowSettingsManager<T> where T : class {
T instance_settings { get; set; }
T default_settings { get; }
event Action<T> instance_settings_changed;
IObservable<T> subscribe_default();
Task save_default_settings(T value);
IWindowSettingsManagerNonShared<T> as_non_shared();
}
/// <summary>
/// An implementation of IWindowSettingsManager that uses a clone of the actual default settings;
/// provides a convenient way to avoid problems that arise from sharing a single default settings object among multiple window instances.
/// </summary>
interface IWindowSettingsManagerNonShared<T> : IWindowSettingsManager<T>, IDisposable where T : class { }
class WindowSettingsManager<T> : IWindowSettingsManager<T> where T : class {
readonly UserSettingsHub _hub;
readonly Func<UserSettings, T> _get;
readonly Func<UserSettings, T, UserSettings> _set;
T _instance_settings;
public WindowSettingsManager(UserSettingsHub hub, T instance_settings, Func<UserSettings, T> get, Func<UserSettings, T, UserSettings> set) {
_hub = hub;
_instance_settings = instance_settings;
_get = get;
_set = set;
}
public T instance_settings {
get => _instance_settings;
set {
_instance_settings = value;
instance_settings_changed?.Invoke(value);
}
}
public T default_settings => _get(_hub.current);
public event Action<T> instance_settings_changed;
public async Task save_default_settings(T value) =>
await _hub.save_default(_set(_hub.current, value));
public IObservable<T> subscribe_default() =>
_hub.subscribe_default_settings().Select(_get).DistinctUntilChanged();
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
new NonSharedWrapper(this);
class NonSharedWrapper : IWindowSettingsManagerNonShared<T> {
readonly IWindowSettingsManager<T> _wsm;
readonly BehaviorSubject<T> _default_subject;
IDisposable _dispose;
public NonSharedWrapper(IWindowSettingsManager<T> wsm) {
_wsm = wsm;
_default_subject = new BehaviorSubject<T>(_wsm.default_settings);
_dispose = wsm.subscribe_default().Subscribe(s =>
_default_subject.OnNext(default_settings = MiscUtils.clone_proto(s)));
}
public T instance_settings {
get => _wsm.instance_settings;
set => _wsm.instance_settings = value;
}
public T default_settings { get; private set; }
public event Action<T> instance_settings_changed {
add => _wsm.instance_settings_changed += value;
remove => _wsm.instance_settings_changed -= value;
}
public async Task save_default_settings(T value) =>
await _wsm.save_default_settings(value);
public IObservable<T> subscribe_default() =>
_default_subject.AsObservable();
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
new NonSharedWrapper(_wsm);
public void Dispose() => Disposable.dispose(ref _dispose);
}
public class DisconnectedImpl : IWindowSettingsManager<T> {
readonly BehaviorSubject<T> _default_settings_subject;
T _instance_settings, _default_settings;
public DisconnectedImpl(T settings) : this(settings, settings) { }
public DisconnectedImpl(T instance_settings, T default_settings) {
_instance_settings = instance_settings;
_default_settings = default_settings;
_default_settings_subject = new BehaviorSubject<T>(default_settings);
}
public T instance_settings {
get => _instance_settings;
set {
if (!Equals(_instance_settings, value)) {
_instance_settings = value;
instance_settings_changed?.Invoke(value);
}
}
}
public T default_settings {
get => _default_settings;
set {
if (!Equals(_default_settings, _default_settings)) {
_default_settings = value;
_default_settings_subject.OnNext(value);
}
}
}
public event Action<T> instance_settings_changed;
public IWindowSettingsManagerNonShared<T> as_non_shared() => new NonSharedWrapper(this);
public Task save_default_settings(T value) {
default_settings = value;
return Task.CompletedTask;
}
public IObservable<T> subscribe_default() => _default_settings_subject;
}
}
static class WindowSettingsManagerExtensions {
/// <summary>
/// Returns an observable sequence of the settings object used by a particular window.
/// <para/>Note: 'Instance' settings take priority over 'default' settings. While the former is present, any changes to the latter will be ignored.
/// </summary>
public static IObservable<T> subscribe_current<T>(this IWindowSettingsManager<T> wsm) where T : class =>
Observable.CombineLatest(
Observable.FromEvent<Action<T>, T>(cb => wsm.instance_settings_changed += cb, cb => wsm.instance_settings_changed -= cb)
.StartWith(wsm.instance_settings),
wsm.subscribe_default(),
(i, d) => i ?? d
).Where(v => v != null)
.DistinctUntilChanged();
public static IObservable<T> subscribe_current<T>(this IWindowSettingsManager<T> wsm, Func<(T defaults, T instance), T> merge_settings) where T : class, IDefaultableSettings {
return Observable.Create<T>(o => {
IDisposable defaults_sub = wsm.subscribe_default().Subscribe(_ => o.OnNext(get_current()));
wsm.instance_settings_changed += on_instance_settings_changed;
return Disposable.Create(() => {
defaults_sub.Dispose();
wsm.instance_settings_changed -= on_instance_settings_changed;
});
void on_instance_settings_changed(T _) =>
o.OnNext(get_current());
});
T get_current() {
var inst = wsm.instance_settings;
if (inst == null) return wsm.default_settings;
if (inst.use_defaults) return merge_settings((wsm.default_settings, inst));
return inst;
}
}
public static T current_settings<T>(this IWindowSettingsManager<T> wsm) where T : class =>
wsm.instance_settings ?? wsm.default_settings;
}
}
using mbts.spark;
using mbts.utils;
using Spark.Core.Disposables;
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
namespace mbts.persist {
interface IWindowSettingsManager<T> where T : class {
T instance_settings { get; set; }
T default_settings { get; }
event Action<T> instance_settings_changed;
IObservable<T> subscribe_default();
Task save_default_settings(T value);
IWindowSettingsManagerNonShared<T> as_non_shared();
}
/// <summary>
/// An implementation of IWindowSettingsManager that uses a clone of the actual default settings;
/// provides a convenient way to avoid problems that arise from sharing a single default settings object among multiple window instances.
/// </summary>
interface IWindowSettingsManagerNonShared<T> : IWindowSettingsManager<T>, IDisposable where T : class { }
class WindowSettingsManager<T> : IWindowSettingsManager<T> where T : class {
readonly UserSettingsHub _hub;
readonly Func<UserSettings, T> _get;
readonly Func<UserSettings, T, UserSettings> _set;
T _instance_settings;
public WindowSettingsManager(UserSettingsHub hub, T instance_settings, Func<UserSettings, T> get, Func<UserSettings, T, UserSettings> set) {
_hub = hub;
_instance_settings = instance_settings;
_get = get;
_set = set;
}
public T instance_settings {
get => _instance_settings;
set {
_instance_settings = value;
instance_settings_changed?.Invoke(value);
}
}
public T default_settings => _get(_hub.current);
public event Action<T> instance_settings_changed;
public async Task save_default_settings(T value) =>
await _hub.save_default(_set(_hub.current, value));
public IObservable<T> subscribe_default() =>
_hub.subscribe_default_settings().Select(_get).DistinctUntilChanged();
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
new NonSharedWrapper(this);
class NonSharedWrapper : IWindowSettingsManagerNonShared<T> {
readonly IWindowSettingsManager<T> _wsm;
readonly BehaviorSubject<T> _default_subject;
IDisposable _dispose;
public NonSharedWrapper(IWindowSettingsManager<T> wsm) {
_wsm = wsm;
_default_subject = new BehaviorSubject<T>(_wsm.default_settings);
_dispose = wsm.subscribe_default().Subscribe(s =>
_default_subject.OnNext(default_settings = MiscUtils.clone_proto(s)));
}
public T instance_settings {
get => _wsm.instance_settings;
set => _wsm.instance_settings = value;
}
public T default_settings { get; private set; }
public event Action<T> instance_settings_changed {
add => _wsm.instance_settings_changed += value;
remove => _wsm.instance_settings_changed -= value;
}
public async Task save_default_settings(T value) =>
await _wsm.save_default_settings(value);
public IObservable<T> subscribe_default() =>
_default_subject.AsObservable();
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
new NonSharedWrapper(_wsm);
public void Dispose() =>
Disposable.dispose(ref _dispose);
}
public class DisconnectedImpl : IWindowSettingsManager<T> {
readonly BehaviorSubject<T> _default_settings_subject;
T _instance_settings, _default_settings;
public DisconnectedImpl(T settings) : this(settings, settings) { }
public DisconnectedImpl(T instance_settings, T default_settings) {
_instance_settings = instance_settings;
_default_settings = default_settings;
_default_settings_subject = new BehaviorSubject<T>(default_settings);
}
public T instance_settings {
get => _instance_settings;
set {
if (!Equals(_instance_settings, value)) {
_instance_settings = value;
instance_settings_changed?.Invoke(value);
}
}
}
public T default_settings {
get => _default_settings;
set {
if (!Equals(_default_settings, _default_settings)) {
_default_settings = value;
_default_settings_subject.OnNext(value);
}
}
}
public event Action<T> instance_settings_changed;
public IWindowSettingsManagerNonShared<T> as_non_shared() => new NonSharedWrapper(this);
public Task save_default_settings(T value) {
default_settings = value;
return Task.CompletedTask;
}
public IObservable<T> subscribe_default() => _default_settings_subject;
}
}
static class WindowSettingsManagerExtensions {
/// <summary>
/// Returns an observable sequence of the settings object used by a particular window.
/// <para/>Note: 'Instance' settings take priority over 'default' settings. While the former is present, any changes to the latter will be ignored.
/// </summary>
public static IObservable<T> subscribe_current<T>(this IWindowSettingsManager<T> wsm) where T : class =>
Observable.CombineLatest(
Observable.FromEvent<Action<T>, T>(cb => wsm.instance_settings_changed += cb, cb => wsm.instance_settings_changed -= cb)
.StartWith(wsm.instance_settings),
wsm.subscribe_default(),
(i, d) => i ?? d
).Where(v => v != null)
.DistinctUntilChanged();
public static T current_settings<T>(this IWindowSettingsManager<T> wsm) where T : class =>
wsm.instance_settings ?? wsm.default_settings;
}
}
@@ -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);
}
}
@@ -0,0 +1,172 @@
#nullable enable
namespace mbts.persist {
using bts;
using bts.api.core;
using bts.api.persistence;
using bts.spark;
using Spark.Core.Protobuf;
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
public class SettingsPersistenceModel : IPersist, IUserPersist {
readonly PersistenceService _persist;
readonly ErrorReporter _errors;
readonly string _user;
public SettingsPersistenceModel(
string user, PersistenceService persist, ErrorReporter errors) {
_user = user;
_persist = persist;
_errors = errors;
}
public Task set<T>(string ns, string key, T value) {
return _persist.store_async(new StoreRequest() {
Ns = _user_ns(ns),
Record = {
new Record() {
Key = key,
Value = ProtobufSerializationHelper.serialize_to_byte_string(value)
}
}
});
}
public Task clear(string ns, string key) {
return _persist.remove_async(new RemoveRequest() {
Ns = _user_ns(ns),
Key = { key }
});
}
public async Task<T?> get<T>(string ns, string key) {
var ret = await _persist.retrieve_async(new RetrieveRequest() {
Ns = _user_ns(ns),
Key = { key }
});
_check_error(ret, ns, key, out var empty);
if (empty) {
return default;
}
return ProtobufSerializationHelper.deserialize<T>(ret.value.Record.Single().Value);
}
public async Task<T[]> get_many<T>(string ns, string key) {
var ret = await _persist.retrieve_async(new RetrieveRequest() {
Ns = _user_ns(ns),
Key = { key }
});
_check_error(ret, ns, key, out bool empty);
if (empty) {
return Array.Empty<T>();
}
return ProtobufSerializationHelper.deserialize_many<T>(
ret.value.Record.Single().Value.ToByteArray());
}
public async Task<IEnumerable<PersistResult<T>>> query<T>(string ns, string pattern) {
var regex = new Regex(pattern);
var results = await _persist.retrieve_all_async(
new RetrieveAllRequest() {
Ns = _user_ns(ns)
});
_check_error(results, ns, string.Empty, out bool empty);
if (empty) {
return Enumerable.Empty<PersistResult<T>>();
}
return results.value.Record
.Where(r => regex.IsMatch(r.Key))
.Select(r => new PersistResult<T>() {
user = _user,
ns = ns,
key = r.Key,
value = ProtobufSerializationHelper.deserialize<T>(r.Value)
}).ToList();
}
public IDisposable subscribe<T>(
string ns, string pattern, Action<IEnumerable<PersistResult<T>>> callback) =>
subscribe<T>(ns, pattern).Subscribe(callback);
public IObservable<IEnumerable<PersistResult<T>>> subscribe<T>(string ns, string pattern) {
var ctx = SynchronizationContext.Current ??
throw new InvalidOperationException("Current SynchronizationContext is null");
var re = new Regex(pattern);
return _persist
.subscribe_ns(_user_ns(ns))
.Select(update =>
update.added
.Where(item => re.IsMatch(item.Key))
.Select(item => new PersistResult<T>() {
user = _user,
ns = ns,
key = item.Key,
value = ProtobufSerializationHelper.deserialize<T>(item.Value)
}))
.SubscribeOn(ctx);
}
public void Dispose() =>
GC.SuppressFinalize(this);
string _user_ns(string ns) =>
$"{_user}#{ns}";
void _check_error(Result<RetrieveResponse> result, string ns, string key, out bool empty) {
if (result.success) {
switch (result.value.Result) {
case RetrieveResponse.Types.Result.EmptyNs:
case RetrieveResponse.Types.Result.NoSuchNs:
case RetrieveResponse.Types.Result.NoSuchKey:
empty = true;
return;
case RetrieveResponse.Types.Result.EmptyKey:
case RetrieveResponse.Types.Result.DuplicateKey:
_errors?.report_error(
$"Invalid Key {result} '{key}'",
"Requested Invalid Settings Key",
MessageBoxImage.Information);
empty = true;
return;
default:
case RetrieveResponse.Types.Result.Ok:
empty = false;
return;
}
} else {
_errors?.report_error(
$"There was an error communicating with the settings server: {result.error_message}",
"Failed to retrieve settings from the server");
empty = true;
}
}
}
[Export(typeof(IPersist))]
public class UISettings : SettingsPersistenceModel {
[ImportingConstructor]
public UISettings([Import("settings")] PersistenceService persist, ErrorReporter err)
: base("all_users", persist, err) { }
}
[Export(typeof(IUserPersist))]
public class UserUISettings : SettingsPersistenceModel {
public UserUISettings(
uint user_id, [Import("settings")] PersistenceService persist, ErrorReporter err)
: base($"User {user_id}", persist, err) { }
[ImportingConstructor]
public UserUISettings(
Lazy<UserTuple> user, [Import("settings")] PersistenceService persist, ErrorReporter err)
: this(user.Value.id, persist, err) { }
}
}
@@ -0,0 +1,44 @@
#nullable enable
namespace mbts.spark {
using bts.utils;
using System;
interface ISettingsProfile<T> {
uint id { get; }
string? name { get; }
T? value { get; }
SecurityCriteria? security_criteria { get; }
}
readonly struct SettingsProfile<T> : IEquatable<SettingsProfile<T>> where T : class, IEquatable<T> {
public static readonly Func<SettingsProfile<T>, uint> key_fn = ns => ns.id;
public readonly uint id;
public readonly string? name;
public readonly T? value;
public readonly SecurityCriteria? security_criteria;
public SettingsProfile(uint id, string? name, T? value, SecurityCriteria? security_criteria = null) {
this.id = id;
this.name = name;
this.value = value;
this.security_criteria = security_criteria;
}
public SettingsProfile<T> with(uint? id = null, string? name = null, T? value = null, SecurityCriteria? security_criteria = null) =>
new(id ?? this.id, name ?? this.name, value ?? this.value, security_criteria ?? this.security_criteria);
public bool Equals(SettingsProfile<T> other) =>
id == other.id &&
name == other.name &&
value.safe_equals(other.value) &&
security_criteria.safe_equals(other.security_criteria);
public override bool Equals(object? obj) =>
obj is SettingsProfile<T> other && Equals(other);
public override int GetHashCode() =>
id.GetHashCode();
}
}
@@ -1,73 +1,73 @@
using mbts.persist;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui.tests {
// Stub persistance store for shell unit tests
public class TestPersist : IPersist, IUserPersist {
public Dictionary<string, Dictionary<string, object>> Values = new Dictionary<string, Dictionary<string, object>>();
public Task set<T>(string ns, string key, T value) {
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
try {
Values[ns][key] = value;
source.TrySetResult(null);
} catch (Exception e) {
source.TrySetException(e);
}
return source.Task;
}
public Task clear(string ns, string key) {
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
try {
Values[ns].Remove(key);
source.TrySetResult(null);
} catch (Exception e) {
source.TrySetException(e);
}
return source.Task;
}
public Task<T> get<T>(string ns, string key) {
TaskCompletionSource<T> source = new TaskCompletionSource<T>();
try {
T result = (T)Values[ns][key];
source.TrySetResult(result);
} catch (Exception e) {
source.TrySetException(e);
}
return source.Task;
}
public Task<T[]> get_many<T>(string ns, string key) {
var source = new TaskCompletionSource<T[]>();
try {
source.TrySetResult((T[])Values[ns][key]);
} catch (Exception e) {
source.TrySetException(e);
}
return source.Task;
}
public async Task<IEnumerable<Tuple<T, DateTime>>> get_history<T>(string ns, string key) {
var res = await get<T>(ns, key);
return new Tuple<T, DateTime>[] { Tuple.Create(res, DateTime.Today) };
}
public Task<IEnumerable<PersistResult<T>>> query<T>(string ns, string pattern) {
throw new NotImplementedException();
}
public IDisposable subscribe<T>(string ns, string pattern, Action<IEnumerable<PersistResult<T>>> callback) {
throw new NotImplementedException();
}
public void Dispose() { }
}
}
using mbts.persist;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui.tests {
// Stub persistance store for shell unit tests
public class TestPersist : IPersist, IUserPersist {
public Dictionary<string, Dictionary<string, object>> Values = new Dictionary<string, Dictionary<string, object>>();
public Task set<T>(string ns, string key, T value) {
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
try {
Values[ns][key] = value;
source.TrySetResult(null);
} catch (Exception e) {
source.TrySetException(e);
}
return source.Task;
}
public Task clear(string ns, string key) {
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
try {
Values[ns].Remove(key);
source.TrySetResult(null);
} catch (Exception e) {
source.TrySetException(e);
}
return source.Task;
}
public Task<T> get<T>(string ns, string key) {
TaskCompletionSource<T> source = new TaskCompletionSource<T>();
try {
T result = (T)Values[ns][key];
source.TrySetResult(result);
} catch (Exception e) {
source.TrySetException(e);
}
return source.Task;
}
public Task<T[]> get_many<T>(string ns, string key) {
var source = new TaskCompletionSource<T[]>();
try {
source.TrySetResult((T[])Values[ns][key]);
} catch (Exception e) {
source.TrySetException(e);
}
return source.Task;
}
public async Task<IEnumerable<Tuple<T, DateTime>>> get_history<T>(string ns, string key) {
var res = await get<T>(ns, key);
return new Tuple<T, DateTime>[] { Tuple.Create(res, DateTime.Today) };
}
public Task<IEnumerable<PersistResult<T>>> query<T>(string ns, string pattern) {
throw new NotImplementedException();
}
public IDisposable subscribe<T>(string ns, string pattern, Action<IEnumerable<PersistResult<T>>> callback) {
throw new NotImplementedException();
}
public void Dispose() { }
}
}
@@ -1,195 +1,248 @@
using bts.shell.pnl;
using Google.Protobuf.WellKnownTypes;
using mbts.ui.settings;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark {
interface IDefaultableSettings {
bool use_defaults { get; set; }
}
/// <summary>
/// The settings Mothership. All window - and any other future user based - settings should have a corresponding default member here.
/// </summary>
[ProtoContract]
class UserSettings {
public static UserSettings factory_defaults =>
new UserSettings {
_ladder = LadderSettings.Default,
_notification = NotificationSettings.Default,
_market_view_grid = bts.shell.market_view.MarketViewSettings.DefaultGridSettings,
_fills_grid = bts.shell.fills.FillsSettings.DefaultGridSettings,
_time_and_sales_grid = bts.shell.time_and_sales.TimeAndSalesSettings.DefaultGridSettings,
_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings,
_reconcile_report_grid = Spark.Trading.ReconcileReportSettings.Default().grid,
_order_book_grid = bts.shell.orderbook.OrderBookStartupData.DefaultGrid
};
[ProtoMember(1)]
LadderSettings _ladder;
[ProtoMember(11)]
readonly LadderSettingsProfile[] _ladder_profiles;
/// When Profiles Are Deleted, ladders that were using that profile shall copy the profile's settings to a local profile.
/// Therefor:
/// Deleted profile ids must never be reused.
/// If they were reused, consider an unloaded workspace with ladders that were using the deleted profile.
/// If such a workspace was reloaded after a new profile was saved with the deleted one's id, the ladders using that deleted profile would suddenly be using the new profile.
/// To Prevent IDs From Being Recycled:
/// The last profile id is stored in the protobuf (<see cref="_last_ladder_profile_id"/>).
/// Anytime a profile is added, it shall use <see cref="next_ladder_profile_id"/>.
[ProtoMember(12)]
uint _last_ladder_profile_id;
// removing NotificationSettings these from proto-serialization, since they are disabled by default for now.
// This way, when they are enabled, there won't be any backwards compatability issues to worry about, as no clients will have persisted them yet.
NotificationSettings _notification;
[ProtoMember(5)]
GridSettings _market_view_grid;
[ProtoMember(6)]
GridSettings _time_and_sales_grid;
[ProtoMember(7)]
GridSettings _fills_grid;
[ProtoMember(8)]
GridSettings _synthetic_management_grid;
[ProtoMember(14)]
GridSettings _reconcile_report_grid;
[ProtoMember(9)]
bts.shell.orderbook.OrderBookGridSettings _order_book_grid;
[ProtoMember(10)]
PNLSettings _pnl;
[ProtoMember(13)]
GlobalAlertSettings _alerts;
public UserSettings() { }
public UserSettings(LadderSettingsProfile[] ladder_profiles) =>
_ladder_profiles = ladder_profiles;
public LadderSettings ladder => _ladder ?? (_ladder = LadderSettings.Default);
public IReadOnlyList<LadderSettingsProfile> ladder_profiles => _ladder_profiles ?? Array.Empty<LadderSettingsProfile>();
public uint next_ladder_profile_id => _last_ladder_profile_id + 1;
public NotificationSettings notification => _notification ?? (_notification = NotificationSettings.Default);
public GridSettings market_view_grid => _market_view_grid ?? (_market_view_grid = bts.shell.market_view.MarketViewSettings.DefaultGridSettings);
public GridSettings time_and_sales_grid => _time_and_sales_grid ?? (_time_and_sales_grid = GridSettings.Default.with(bts.shell.time_and_sales.TimeAndSalesSettings.DefaultColumns.all));
public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.fills.FillsSettings.DefaultGridSettings);
public GridSettings synthetic_management_grid => _synthetic_management_grid ?? (_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings);
public GridSettings reconcile_report_grid => _reconcile_report_grid ??= Spark.Trading.ReconcileReportSettings.Default().grid;
public PNLSettings pnl => _pnl ?? (_pnl = PNLSettings.Default);
public bts.shell.orderbook.OrderBookGridSettings order_book_grid => _order_book_grid ?? (_order_book_grid = bts.shell.orderbook.OrderBookStartupData.Default.grid_settings_wrapper());
public GlobalAlertSettings alerts => _alerts ?? (_alerts = new GlobalAlertSettings());
public UserSettings with(LadderSettings ladder = null,
LadderSettingsProfile[] ladder_profiles = null,
NotificationSettings notification = null,
GridSettings provided_market_view_gridsettings = null,
GridSettings time_and_sales = null,
GridSettings provided_fills_gridsettings = null,
GridSettings provided_synthetic_management_gridsettings = null,
GridSettings reconcile_report_grid = null,
PNLSettings pnl = null,
bts.shell.orderbook.OrderBookGridSettings order_book_grid = null,
GlobalAlertSettings alerts = null) {
var us = new UserSettings(ladder_profiles ?? _ladder_profiles) {
_ladder = ladder ?? _ladder,
_notification = notification ?? _notification,
_market_view_grid = provided_market_view_gridsettings ?? _market_view_grid,
_time_and_sales_grid = time_and_sales ?? _time_and_sales_grid,
_fills_grid = provided_fills_gridsettings ?? _fills_grid,
_synthetic_management_grid = provided_synthetic_management_gridsettings ?? _synthetic_management_grid,
_reconcile_report_grid = reconcile_report_grid ?? _reconcile_report_grid,
_pnl = pnl ?? _pnl,
_order_book_grid = order_book_grid ?? _order_book_grid,
_alerts = alerts ?? _alerts
};
us._last_ladder_profile_id = Math.Max(_last_ladder_profile_id, us.ladder_profiles.Select(p => p.id).MaxOrDefault());
return us;
}
public static class FileExtensions {
public const string
GRID = "grid",
LADDER = "ladder",
PNL = "pnl";
}
}
// - currently all notification-triggers are in GridSettings
// - the SoundNotificationSettings below are used to disable/enable SoundNotifications
// - the ToastNotificationSettings below are used to disable/enable ToastNotifications, and adjust the flash-duration (if enabled).
// - since this is a new feature, the ability to quickly enable/disable both types of notifications may save us the trouble of having to redeploy,
// in the unfortunate event that a bug slipped through the cracks, or the performance impact is more significant than expected.
// TODO: maybe globalize notification settings?
[ProtoContract]
class NotificationSettings {
[ProtoMember(1)]
readonly SoundNotificationSettings _sound_notification;
[ProtoMember(2)]
readonly ToastNotificationSettings _toast_notification;
public NotificationSettings() { }
public NotificationSettings(SoundNotificationSettings sound_notification, ToastNotificationSettings toast_notification) {
_sound_notification = sound_notification;
_toast_notification = toast_notification;
}
public static NotificationSettings Default => new NotificationSettings(SoundNotificationSettings.Default, ToastNotificationSettings.Default);
public SoundNotificationSettings sound_notification => _sound_notification;
public ToastNotificationSettings toast_notification => _toast_notification;
}
[ProtoContract]
class SoundNotificationSettings {
[ProtoMember(1)]
readonly bool _enabled;
public SoundNotificationSettings() { }
public SoundNotificationSettings(bool enabled) =>
_enabled = enabled;
public static SoundNotificationSettings Default => new SoundNotificationSettings(true);
public bool enabled => _enabled;
}
[ProtoContract]
class ToastNotificationSettings {
// though it is possible to infer such Modes from Duration values (null = Disabled, max = ManualClose, otherwise Flash)
// this Mode enum makes the behavior very clear, and less likely to be misinterpretted down the road.
// (without this enum, the meaning of "duration = null," and "duration = 0 seconds," is debatable)
public enum Mode {
Disabled,
ManualClose,
Flash
}
public static ToastNotificationSettings Default =>
new ToastNotificationSettings(Mode.Flash, new Duration { Seconds = 5 }, 320);
[ProtoMember(1)]
readonly Mode _mode;
[ProtoMember(2)]
readonly Duration _duration;
[ProtoMember(3)]
readonly double _max_height;
public ToastNotificationSettings() { }
public ToastNotificationSettings(Mode mode, Duration duration, double max_height) {
_mode = mode;
_duration = duration;
_max_height = max_height;
}
public Mode mode => _mode;
public TimeSpan? duration => _mode == Mode.Flash ? _duration?.ToTimeSpan() ?? TimeSpan.FromSeconds(5) : new TimeSpan?();
public double max_height => _max_height;
}
}
#nullable enable
namespace mbts.spark {
using bts.shell;
using bts.shell.fills;
using bts.shell.market_view;
using bts.shell.orderbook;
using bts.shell.pnl;
using bts.shell.time_and_sales;
using Google.Protobuf.WellKnownTypes;
using mbts.ui.settings;
using ProtoBuf;
using Spark.Trading;
using System;
using System.Collections.Generic;
using System.Linq;
interface IDefaultableSettings {
bool use_defaults { get; set; }
}
/// <summary>
/// The settings Mothership. All window - and any other future user based - settings should have a corresponding default member here.
/// </summary>
[ProtoContract]
class UserSettings {
public static UserSettings factory_defaults =>
new() {
_ladder = LadderSettings.Default,
_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,
_order_book = OrderBookStartupData.DefaultGrid
};
[ProtoMember(1)]
LadderSettings? _ladder;
[ProtoMember(11)]
LadderSettingsProfile[]? _ladder_profiles;
/// When Profiles Are Deleted, ladders that were using that profile shall copy the profile's settings to a local profile.
/// Therefor:
/// Deleted profile ids must never be reused.
/// If they were reused, consider an unloaded workspace with ladders that were using the deleted profile.
/// If such a workspace was reloaded after a new profile was saved with the deleted one's id, the ladders using that deleted profile would suddenly be using the new profile.
/// To Prevent IDs From Being Recycled:
/// The last profile id is stored in the protobuf (<see cref="_last_ladder_profile_id"/>).
/// Anytime a profile is added, it shall use <see cref="next_ladder_profile_id"/>.
[ProtoMember(12)]
uint _last_ladder_profile_id;
// removing NotificationSettings these from proto-serialization, since they are disabled by default for now.
// This way, when they are enabled, there won't be any backwards compatability issues to worry about, as no clients will have persisted them yet.
NotificationSettings? _notification;
[ProtoMember(5)]
GridSettings? _market_view_grid;
[ProtoMember(6)]
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)]
GridSettings? _reconcile_report;
[ProtoMember(9)]
OrderBookGridSettings? _order_book;
[ProtoMember(10)]
PNLSettings? _pnl;
[ProtoMember(13)]
GlobalAlertSettings? _alerts;
public UserSettings() { }
public LadderSettings ladder =>
_ladder ??= LadderSettings.Default;
public IReadOnlyList<LadderSettingsProfile> ladder_profiles =>
_ladder_profiles ?? Array.Empty<LadderSettingsProfile>();
public uint next_ladder_profile_id =>
_last_ladder_profile_id + 1;
public NotificationSettings notification =>
_notification ??= NotificationSettings.Default;
public GridSettings market_view_grid =>
_market_view_grid ??= MarketViewSettings.DefaultGridSettings;
public GridSettings time_and_sales_grid =>
_time_and_sales_grid ??= GridSettings.Default.with(
TimeAndSalesSettings.DefaultColumns.all);
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;
public GridSettings reconcile_report_grid =>
_reconcile_report ??= ReconcileReportSettings.Default().grid ??
throw new InvalidOperationException();
public PNLSettings pnl =>
_pnl ??= PNLSettings.Default;
public OrderBookGridSettings order_book_grid =>
_order_book ??= OrderBookStartupData.Default.grid_settings_wrapper();
public GlobalAlertSettings alerts =>
_alerts ??= new GlobalAlertSettings();
public UserSettings with(
LadderSettings? ladder = null,
LadderSettingsProfile[]? ladder_profiles = null,
NotificationSettings? notification = null,
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,
OrderBookGridSettings? order_book_grid = null,
GlobalAlertSettings? alerts = null) {
var us = new UserSettings() {
_ladder_profiles = ladder_profiles ?? _ladder_profiles,
_ladder = ladder ?? _ladder,
_notification = notification ?? _notification,
_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,
_order_book = order_book_grid ?? _order_book,
_alerts = alerts ?? _alerts
};
us._last_ladder_profile_id = Math.Max(_last_ladder_profile_id, us.ladder_profiles.Select(p => p.id).MaxOrDefault());
return us;
}
public static class FileExtensions {
public const string
GRID = "grid",
LADDER = "ladder",
PNL = "pnl";
}
}
// - currently all notification-triggers are in GridSettings
// - the SoundNotificationSettings below are used to disable/enable SoundNotifications
// - the ToastNotificationSettings below are used to disable/enable ToastNotifications, and adjust the flash-duration (if enabled).
// - since this is a new feature, the ability to quickly enable/disable both types of notifications may save us the trouble of having to redeploy,
// in the unfortunate event that a bug slipped through the cracks, or the performance impact is more significant than expected.
// TODO: maybe globalize notification settings?
[ProtoContract]
class NotificationSettings {
[ProtoMember(1)]
readonly SoundNotificationSettings? _sound_notification;
[ProtoMember(2)]
readonly ToastNotificationSettings? _toast_notification;
public NotificationSettings() { }
public NotificationSettings(SoundNotificationSettings sound_notification, ToastNotificationSettings toast_notification) {
_sound_notification = sound_notification;
_toast_notification = toast_notification;
}
public static NotificationSettings Default => new NotificationSettings(SoundNotificationSettings.Default, ToastNotificationSettings.Default);
public SoundNotificationSettings? sound_notification => _sound_notification;
public ToastNotificationSettings? toast_notification => _toast_notification;
}
[ProtoContract]
class SoundNotificationSettings {
[ProtoMember(1)]
readonly bool _enabled;
public SoundNotificationSettings() { }
public SoundNotificationSettings(bool enabled) =>
_enabled = enabled;
public static SoundNotificationSettings Default => new(true);
public bool enabled => _enabled;
}
[ProtoContract]
class ToastNotificationSettings {
// though it is possible to infer such Modes from Duration values (null = Disabled, max = ManualClose, otherwise Flash)
// this Mode enum makes the behavior very clear, and less likely to be misinterpretted down the road.
// (without this enum, the meaning of "duration = null," and "duration = 0 seconds," is debatable)
public enum Mode {
Disabled,
ManualClose,
Flash
}
public static ToastNotificationSettings Default =>
new(Mode.Flash, new Duration { Seconds = 5 }, 320);
[ProtoMember(1)]
readonly Mode _mode;
[ProtoMember(2)]
readonly Duration? _duration;
[ProtoMember(3)]
readonly double _max_height;
public ToastNotificationSettings() { }
public ToastNotificationSettings(Mode mode, Duration duration, double max_height) {
_mode = mode;
_duration = duration;
_max_height = max_height;
}
public Mode mode => _mode;
public TimeSpan duration =>
_mode switch {
Mode.Flash => _duration?.ToTimeSpan() ?? TimeSpan.FromSeconds(5),
_ => new()
};
public double max_height => _max_height;
}
}
@@ -1,177 +1,201 @@
using bts;
using bts.settings;
using bts.shell.pnl;
using bts.utils;
using mbts.spark;
using mbts.ui.settings;
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
namespace mbts.persist {
/// <summary>
/// Represents the top level of the persisted settings hierarchy.
/// One UserSettings object, which contains the defaults for every SparkWindow, can be read/written on the server by UISettingsHub.
/// </summary>
[Export(typeof(UserSettingsHub))]
class UserSettingsHub {
// do NOT CHANGE these consts, they represent a file path on the server
const string NAME_SPACE = "settings", DEFAULT_KEY = "default";
static readonly IImmutableProperty<UserSettings, GridSettings>
_fills_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.fills_grid, (us, gs) => us.with(provided_fills_gridsettings: gs)),
_mview_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.market_view_grid, (us, gs) => us.with(provided_market_view_gridsettings: gs)),
_synth_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.synthetic_management_grid, (us, gs) => us.with(provided_synthetic_management_gridsettings: gs)),
_reconcile_report_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.reconcile_report_grid, (us, gs) => us.with(reconcile_report_grid: gs));
static readonly IImmutableProperty<UserSettings, PNLSettings>
_pnl_propi = ImmutableProperty.create<UserSettings, PNLSettings>(us => us.pnl, (us, pnl) => us.with(pnl: pnl));
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<IMarketViewGridSettingsMigrationRule> _mview_migration_rules;
readonly IEnumerable<IPNLSettingsMigrationRule> _pnl_migration_rules;
readonly IEnumerable<ISyntheticManagementMigrationRule> _synth_migration_rules;
readonly IEnumerable<IReconcileReportMigrationRule> _reconcile_report_migration_rules;
readonly IUserPersist _persist;
UserSettings _default_settings;
[ImportingConstructor]
public UserSettingsHub(
IUserPersist persist,
[ImportMany] IEnumerable<IFillsGridSettingsMigrationRule> fills_migration_rules,
[ImportMany] IEnumerable<IMarketViewGridSettingsMigrationRule> mview_migration_rules,
[ImportMany] IEnumerable<IPAndLGridSettingsMigrationRule> pandl_migration_rules,
[ImportMany] IEnumerable<IPNLSettingsMigrationRule> pnl_migration_rules,
[ImportMany] IEnumerable<ISyntheticManagementMigrationRule> synth_migration_rules,
[ImportMany] IEnumerable<IReconcileReportMigrationRule> reconcile_report_migration_rules) {
//
_persist = persist;
_fills_migration_rules = fills_migration_rules ?? Enumerable.Empty<IFillsGridSettingsMigrationRule>();
_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>();
_reconcile_report_migration_rules = reconcile_report_migration_rules.empty_if_null();
init();
async void init() {
UserSettings s;
try {
s = await _persist.get<UserSettings>(NAME_SPACE, DEFAULT_KEY);
if (s != null) {
var wrapped_rules = new[] {
_fills_migration_rules.merge().wrap(_fills_propi),
_mview_migration_rules.merge().wrap(_mview_propi),
_pnl_migration_rules.merge().wrap(_pnl_propi),
_synth_migration_rules.merge().wrap(_synth_propi),
_reconcile_report_migration_rules.merge().wrap(_reconcile_report_propi)
};
SettingsMigrationHelper.apply_quiet(ref s, wrapped_rules);
// TODO: move ladder sanitization logic into ISettingsMigrationRule
s.ladder.sanitize();
// ensure valid named ladder set (w/ distinct, non-zero ids)
var ladder_profile_map = new Dictionary<uint, LadderSettingsProfile>();
var unidentified_lps = new List<LadderSettingsProfile>();
foreach (var lp in s.ladder_profiles) {
lp.value.sanitize();
if (lp.id == 0) {
unidentified_lps.Add(lp);
} else ladder_profile_map[lp.id] = lp;
}
uint id = s.next_ladder_profile_id;
foreach (var lp in unidentified_lps) {
ladder_profile_map[id] = lp.with(id);
id++;
}
var sorted_lps = ladder_profile_map.Values.ToArray();
Array.Sort(sorted_lps, Comparer<uint>.Default.wrap<uint, LadderSettingsProfile>(lp => lp.id));
if (!sorted_lps.SequenceEqual(s.ladder_profiles))
await _persist.set(NAME_SPACE, DEFAULT_KEY, s = s.with(ladder_profiles: sorted_lps));
}
} catch (Exception ex) {
Log.Persist.warn($"Error loading {typeof(UserSettings)} from namespace '{NAME_SPACE}', will restore factory defaults and continue as usual: {ex}");
await save_default(s = UserSettings.factory_defaults);
}
lock (_default_settings_lock) {
initialized = true;
if (s != null)
_default_settings_subject.OnNext(_default_settings = s);
}
}
}
// indicates whether or not the settings have been loaded from the server (this happens once, on startup - see the constructor for details)
public bool initialized { get; private set; }
internal UserSettings current {
get {
lock (_default_settings_lock)
return _default_settings ?? UserSettings.factory_defaults;
}
}
/// Subscribing via _persist.subscribe may be cleaner in theory, however using a local event allows observers to be notified immediately.
/// Assuming there aren't any rogue agents saving UserSettings by themselves, there should never be any discrepancies.
public IObservable<UserSettings> subscribe_default_settings() => _default_settings_subject.AsObservable();
public IWindowSettingsManager<GridSettings> create_fills_settings_manager(GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_fills_migration_rules,
_fills_propi);
public IWindowSettingsManager<GridSettings> create_mview_settings_manager(GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_mview_migration_rules,
_mview_propi);
public IWindowSettingsManager<PNLSettings> create_pnl_settings_manager(PNLSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_pnl_migration_rules,
_pnl_propi);
public IWindowSettingsManager<GridSettings> create_synth_settings_manager(GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_synth_migration_rules,
_synth_propi);
public IWindowSettingsManager<GridSettings> create_reconcile_report_settings_manager(
GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_reconcile_report_migration_rules,
_reconcile_report_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) {
_default_settings_subject.OnNext(_default_settings = value);
}
await _persist_async();
}
IWindowSettingsManager<T> _create_settings_manager<T>(T instance_settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, IImmutableProperty<UserSettings, T> propi) where T : class {
if (instance_settings != null && migration_rules.merge().apply(instance_settings, out var new_settings))
instance_settings = new_settings;
return new WindowSettingsManager<T>(this, instance_settings, propi.get, propi.set);
}
async Task _persist_async() {
try {
await _persist.set(NAME_SPACE, DEFAULT_KEY, _default_settings);
} catch (Exception ex) {
Log.Persist.warn($"Error persisting {typeof(UserSettings)} in namespace '{NAME_SPACE}': {ex}");
}
}
}
}
using bts;
using bts.settings;
using bts.shell.pnl;
using bts.utils;
using mbts.spark;
using mbts.ui.settings;
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
namespace mbts.persist {
/// <summary>
/// Represents the top level of the persisted settings hierarchy.
/// One UserSettings object, which contains the defaults for every SparkWindow, can be read/written on the server by UISettingsHub.
/// </summary>
[Export(typeof(UserSettingsHub))]
class UserSettingsHub {
// do NOT CHANGE these consts, they represent a file path on the server
const string NAME_SPACE = "settings", DEFAULT_KEY = "default";
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));
static readonly IImmutableProperty<UserSettings, PNLSettings>
_pnl_propi = ImmutableProperty.create<UserSettings, PNLSettings>(us => us.pnl, (us, pnl) => us.with(pnl: pnl));
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;
readonly IEnumerable<IReconcileReportMigrationRule> _reconcile_report_migration_rules;
readonly IUserPersist _persist;
UserSettings _default_settings;
[ImportingConstructor]
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,
[ImportMany] IEnumerable<ISyntheticManagementMigrationRule> synth_migration_rules,
[ImportMany] IEnumerable<IReconcileReportMigrationRule> reconcile_report_migration_rules) {
//
_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>();
_reconcile_report_migration_rules = reconcile_report_migration_rules.empty_if_null();
init();
async void init() {
UserSettings s;
try {
s = await _persist.get<UserSettings>(NAME_SPACE, DEFAULT_KEY);
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),
_reconcile_report_migration_rules.merge().wrap(_reconcile_report_propi)
};
SettingsMigrationHelper.apply_quiet(ref s, wrapped_rules);
// TODO: move ladder sanitization logic into ISettingsMigrationRule
s.ladder.sanitize();
// ensure valid named ladder set (w/ distinct, non-zero ids)
var ladder_profile_map = new Dictionary<uint, LadderSettingsProfile>();
var unidentified_lps = new List<LadderSettingsProfile>();
foreach (var lp in s.ladder_profiles) {
lp.value.sanitize();
if (lp.id == 0) {
unidentified_lps.Add(lp);
} else ladder_profile_map[lp.id] = lp;
}
uint id = s.next_ladder_profile_id;
foreach (var lp in unidentified_lps) {
ladder_profile_map[id] = lp.with(id);
id++;
}
var sorted_lps = ladder_profile_map.Values.ToArray();
Array.Sort(sorted_lps, Comparer<uint>.Default.wrap<uint, LadderSettingsProfile>(lp => lp.id));
if (!sorted_lps.SequenceEqual(s.ladder_profiles))
await _persist.set(NAME_SPACE, DEFAULT_KEY, s = s.with(ladder_profiles: sorted_lps));
}
} catch (Exception ex) {
Log.Persist.warn($"Error loading {typeof(UserSettings)} from namespace '{NAME_SPACE}', will restore factory defaults and continue as usual: {ex}");
await save_default(s = UserSettings.factory_defaults);
}
lock (_default_settings_lock) {
initialized = true;
if (s != null)
_default_settings_subject.OnNext(_default_settings = s);
}
}
}
// indicates whether or not the settings have been loaded from the server (this happens once, on startup - see the constructor for details)
public bool initialized { get; private set; }
internal UserSettings current {
get {
lock (_default_settings_lock)
return _default_settings ?? UserSettings.factory_defaults;
}
}
/// Subscribing via _persist.subscribe may be cleaner in theory, however using a local event allows observers to be notified immediately.
/// Assuming there aren't any rogue agents saving UserSettings by themselves, there should never be any discrepancies.
public IObservable<UserSettings> subscribe_default_settings() => _default_settings_subject.AsObservable();
public IWindowSettingsManager<GridSettings> create_fills_settings_manager(GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_fills_migration_rules,
_fills_propi);
public IWindowSettingsManager<GridSettings> create_mview_settings_manager(GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_mview_migration_rules,
_mview_propi);
public IWindowSettingsManager<PNLSettings> create_pnl_settings_manager(PNLSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_pnl_migration_rules,
_pnl_propi);
public IWindowSettingsManager<GridSettings> create_synth_settings_manager(GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_synth_migration_rules,
_synth_propi);
public IWindowSettingsManager<GridSettings> create_reconcile_report_settings_manager(
GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_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) {
_default_settings_subject.OnNext(_default_settings = value);
}
await _persist_async();
}
IWindowSettingsManager<T> _create_settings_manager<T>(T instance_settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, IImmutableProperty<UserSettings, T> propi) where T : class {
if (instance_settings != null && migration_rules.merge().apply(instance_settings, out var new_settings))
instance_settings = new_settings;
return new WindowSettingsManager<T>(this, instance_settings, propi.get, propi.set);
}
async Task _persist_async() {
try {
await _persist.set(NAME_SPACE, DEFAULT_KEY, _default_settings);
} catch (Exception ex) {
Log.Persist.warn($"Error persisting {typeof(UserSettings)} in namespace '{NAME_SPACE}': {ex}");
}
}
}
}
@@ -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,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;
@@ -1,9 +1,11 @@
namespace mbts.spark {
using bts;
using bts.api;
using bts.api.analytics;
using bts.api.core;
using bts.collections;
using bts.shell;
using bts.shell.pnl;
using bts.spark;
using mbts.analytics;
@@ -12,7 +14,10 @@ namespace mbts.spark {
using mbts.ui.shell;
using mbts.ui.utility;
using Spark.Core.Data;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using Spark.Trading;
using Spark.ViewModel;
using System;
@@ -35,7 +40,13 @@ namespace mbts.spark {
[ImportingConstructor]
public AnalyticsShellModule(
IBus bus, WindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model) {
IBus bus,
WindowManager wm,
UserSettingsHub settings_hub,
OrdersModel orders_model,
IPositionModel position_model,
ISecurityMetadataStore sec_meta_store,
Lazy<UserTuple> user) {
_wm = wm;
_bus = bus;
var reports_demuxer = new TableUpdateDemuxer<ReportImmutable?, int>(default, false);
@@ -49,15 +60,21 @@ namespace mbts.spark {
new AnalyticsExpirationParametersWindowFactory(this, bus),
new SlideReportWindowFactory(this, bus, reports_demuxer.subscribe),
new ComboReportWindowFactory(this, bus, reports_demuxer.subscribe),
new PNLWindowFactory(
this, bus, settings_hub, orders_model, position_model, sec_meta_store),
new DayFillsWindowFactory(
this, bus, settings_hub, position_model, sec_meta_store, user.Value)
};
var vol_surfaces_src = _bus.subscribe(SparkTags.VolSurfaceListTag).OnDispatcher();
var reports_src = _bus.subscribe(SparkTags.ReportBlackboardTags.ListTag).OnDispatcher();
_dispose = new DisposableCombo(
_dispose = Disposable.Create(
vol_surfaces_src.Subscribe(_on_vol_surfaces),
reports_src.Subscribe(_on_reports),
nullable_reports_src.Subscribe(reports_demuxer),
reports_demuxer,
vm.launch_exp_params_view_cmd.subscribe_executed(_launch_exp_params_view),
vm.launch_pnl_cmd.subscribe_executed(_launch_pnl),
vm.launch_day_fills_cmd.subscribe_executed(_launch_day_fills),
// vol surface
vm.vol_surface_launcher_vm.launch_cmd.subscribe_executed(_launch_vol_surface),
vm.vol_surface_launcher_vm.create_cmd.subscribe_executed(_create_vol_surface),
@@ -114,6 +131,12 @@ namespace mbts.spark {
_bus.post(SparkTags.show_analytics_exp_params, settings);
}
void _launch_pnl() =>
_bus.post(SparkTags.ShowPNLTag, null);
void _launch_day_fills() =>
_bus.post(SparkTags.show_day_fills, DayFillsSettings.Default());
void _launch_vol_surface(int id) {
var windows = _wm.list_windows();
if (windows.OfType<VolSurfaceWindow>().TryGetFirst(w => w.vs_id == id, out var win)) {
@@ -180,7 +203,8 @@ namespace mbts.spark {
_bus.post(SparkTags.ShowSlideReportTag, SlideReportSettings.Default(id));
void _create_combo_report(string _) {
var vm = new ComboReportSettingsVM(_bus.subscribe(SparkTags.AccountsTag));
var vm = new ComboReportSettingsVM(
_bus.subscribe(SparkTags.AccountsTag), _bus.subscribe(SparkTags.SecuritiesTag));
vm.load_from(new(new Report { ReportType = Report.Types.ReportType.Slide }));
var win = new ComboReportCreationDialog() {
DataContext = vm
@@ -13,6 +13,10 @@ namespace mbts.spark {
public KeyValueLauncherVM<int, string> slide_report_launcher_vm { get; } = new() { type_name = "Slide Report" };
public KeyValueLauncherVM<int, string> combo_report_launcher_vm { get; } = new() { type_name = "Combo Report" };
// pnl
public EventCommand launch_pnl_cmd { get; } = new();
public EventCommand launch_day_fills_cmd { get; } = new();
public string? selected_window_type {
get => _selected_window_type;
set => Set(ref _selected_window_type, value);
Loaded 100 of 1216 files, more files were not shown because too many files have changed in this diff. Show more