Compare commits

...
Author SHA1 Message Date
John Colvin c5b79b3dae chore: Define common IUniqueID member impls in interface
Taking advantage of nifty new language feats
2023-08-22 11:35:41 -04:00
John Colvin 5180d13fb0 build (refactor): Organize py classes a bit 2023-08-22 11:35:41 -04:00
John Colvin 4065b8b4c4 dev: Simplify sparkd build instructions
Restructure in style similar to Spark and Api Server build instructions
2023-08-22 11:35:41 -04:00
John Colvin a28070713e dev: Add instructions to build spark in README 2023-08-22 11:35:41 -04:00
John Colvin 83cf96d254 build: Replace build_api_server.py w/ cmake/dotnet clis
Add instructions to build the Api Server in README
2023-08-22 11:35:41 -04:00
John Colvin 56b3eb60d5 build: Remove 'jobs' arg from CMake build presets
Modern CMake uses an appropriate number of jobs by default (I believe
the number of cores on the cpu)
2023-08-22 01:29:45 -04:00
John Colvin c30e09a6eb build: Init SPARK_ENABLE_API_SERVER reguardless of platform 2023-08-22 01:29:45 -04:00
John Colvin be96cbb57d build: Only build Admin Web App on Linux 2023-08-22 01:29:45 -04:00
John Colvin d26b818939 build: Copy libspark to dotnet bin dirs in CMakeLists 2023-08-22 01:29:45 -04:00
John Colvin 6f0e0e5172 build: Add Spark.Api.Server to CMakePresets 2023-08-22 01:29:45 -04:00
John Colvin b2d5ab5e03 chore: Add SyntheticSecurityClient to WebsocketDataSync
TODO (Arham): Integrate SyntheticSecurityClient w/ the websocket api
2023-08-21 15:35:03 -04:00
John Colvin c3e098e27d feat: Add SyntheticSecurityClient (for websocket api) 2023-08-21 15:05:37 -04:00
John Colvin a930d2165a refactor: Move SyntheticTuple to spark_api proj
Also clean struct up a bit and rename 'SyntheticSecurityTuple'
2023-08-21 15:05:37 -04:00
John Colvin 05cdfce9ce fix: Add null checks to account for bad analytics exp params
The Spark client was throwing null ref exs after merging prod to
developement, likely because the param protos stored in db were invalid.
2023-08-11 11:42:29 -04:00
John Colvin 61c9d6b2d6 fix build: Sand rough edges after merge 2023-08-05 00:09:16 -04:00
John Colvin 7545d3fd5c Merge remote-tracking branch 'origin/spark_crypto.prod' into development 2023-08-04 13:40:04 -04:00
John Colvin 04e0297e86 refactor: Simplify 'render_piq' by using common code
No need to re-invent the 'render_cells' wheel
2023-07-31 14:50:44 -04:00
John Colvin 562706b968 fix: Don't assume ladder row-data is sorted by price
Bugs (fixed):
   * Ladder assert-crashes when rendering volume-at-price

   Cause: The render routine expected items to be sorted by price, and
   asserted as much (indirectly). In hindsight, this expectation placed
   an unnecessary burden on the caller.
2023-07-31 14:20:08 -04:00
John Colvin 4195207a71 fix: Replace non-existent StaticResource w/ BTSColor
Bugs (fixed):
   * The Activity Log window crashes immediately opened.

   Cause: WPF throws an Exception because it fails to resolve the
   StaticResources 'TextBox.BackgroundBrush', and
   'TextBox.ForegroundBrush', as they are defined in Colors.xaml, which
   is not imported by LogViewer.xaml.

   I replaced the StaticResources w/ BTSColor extension (rather than
   include Colors.xaml), because it is less error prone, and allows us
   to keep colors organized in 1 place: BTSColors.cs.

   I'm not sure how long the bug was around. It likely slipped through
   the cracks because the ActivityLog window isn't used often.
2023-07-28 14:16:46 -04:00
John Colvin 71eb89b6a1 fix: Check 'HasQuantityDisplay' before accessing prop
Bugs (fixed):
   * The PNL Summary window throws a null ref ex when the first row is
     populated.

     Cause: The SecurityMetadataImmutable's internal QuantityDisplay is
     null. In other words, the mutable SecurityMetadata obj around which
     it wraps has a null QuantityDisplay, which is not allowed since
     Immutable protos (such as 'QuantityDisplayImmutable') must be
     constructed w/ a non-null instance of their mutable counterpart.

* Implement same fix (pre-emptively) in ManualFillRow, and
  OrderCompletedEventPredicate.
2023-07-28 14:01:57 -04:00
John Colvin 94e4a0b725 fix: Change bus data type to nullable immutable
Bugs (fixed):
   * The InterpTheosServerPublisher throws a null ref during startup when it
     accesses 'UsageList' from the VolSurfaceUsageTag data.

     Cause: Immutable protos instantiated w/ the default ctr have an
     invalid state, because the mutable (reference type) instance around
     which they wrap is null. Since an immutable type's props are trivial
     wrappers for this mutable instance, they throw a null ref if the
     mutable instance is null.
2023-07-28 12:02:50 -04:00
John Colvin 40d1fe20e3 diag: Add detailed error logs to notification serv order handler
Maintain a circular buffer of the last N updates. Then if/when an
exception occurs, log the update sequence that led up to it.
2023-07-26 15:30:55 -04:00
John Colvin 2ed23159ca style: Clean up jenkins build script
* Get rid of double backslashes in quoted paths to improve readability
* Call 'activate.bat' instead of 'activate' (official python docs say to
  use the former)
* Add docs
2023-07-26 15:26:30 -04:00
John Colvin faa8438adf build: Make jenkins build script independent of working dir
This alleviates an unneccessary burden on callers (previously, it had to
be run from the repo root dir).
2023-07-26 11:59:03 -04:00
John Colvin 0b35c2ccde fix: Add lock, try/catch in notification serv order handler
Brandon has been experiencing a crash on startup. The stack trace shows
this handler is throwing a duplicate-key ex on 'dictionary.Add' (unclear
why).
2023-07-26 00:55:50 -04:00
John Colvin 98eea0ef09 chore: Remove unused iface member 2023-07-26 00:13:22 -04:00
John Colvin 4bf237aebe fix (pre-emptive): Guard against null/empty prices in LadderRender 2023-07-25 14:27:13 -04:00
John Colvin da927adef4 fix: Check price is within ladder's range before rendering
Bugs (fixed):
  The render routine was indirectly asserting that each item's price was
  >= the ladder's min price. That seems like an inappropriate burden to
  put on the caller. Now we filter these items out in the routine.
2023-07-25 14:24:16 -04:00
John Colvin cf587e26d5 fix: Remove bad assert abt tickdiff in LadderRender
* The test condition was wrong in the first place
* Instead of assert/crashing, log a warning
2023-07-25 14:22:15 -04:00
John Colvin 9e4354fb55 fix buid: Don't goto ERROR just bc venv already exists
This seems like a typo
2023-07-18 23:40:18 -04:00
John Colvin e6bb4c73a8 fix build (jenkins): Upgrade pip via 'python -m pip'
For some reason 'pip install --upgrade pip' works on my laptop, but fails on
the Jenkins box.
2023-07-18 22:53:32 -04:00
John Colvin e43a2a4818 build: Add build script for new Jenkins (on aws) 2023-07-18 22:25:56 -04:00
John Colvin cde0f44b7d build: Default to Release cfg for 'publish' target 2023-07-18 22:25:06 -04:00
Taha b07a1e2c74 Fixing the issues, where the theos are only published out by the vol surface owner 2023-07-18 20:40:59 -04:00
Taha Afzal 8376fce5fe Merge branch 'spark_crypto.prod.vol_order' into spark_crypto.prod 2023-07-14 17:01:29 +00:00
Taha Afzal d9e5c846c2 Adding more logs to VolAlgo 2023-07-14 16:57:55 +00:00
Taha Afzal 65736c13ef VolAlgo completed 2023-07-14 03:54:14 +00:00
Taha Afzal 071c28e925 VolAlgo reflects its state correctly now 2023-07-10 03:20:05 +00:00
Taha Afzal 8355e3e1be VolAlgo code complete - need to test 2023-07-09 04:21:42 +00:00
John Colvin f9a6e0963c chore: Remove Spark.Admin.WebApp
These files were added by accident (leftover from switching branches
from development -> spark_crypto.prod)
2023-07-06 14:52:57 +00:00
John Colvin b2879837c6 chore: Remove devcontainer.json 2023-07-06 12:26:48 +00:00
John Colvin 0d03ddb009 Merge remote-tracking branch 'origin/spark_crypto.prod' into spark_crypto.tick_hotfix 2023-07-06 12:26:28 +00:00
John Colvin a6dfa4ae80 fix: Don't show decimal BTC Futs/Spd qtys
They tick in multiples of 10, so decimal precision isn't needed
2023-07-05 18:20:53 -04:00
John Colvin e66ec82ba8 hotfix: Setup default cfg for new Deribit price ticks 2023-07-05 18:19:49 -04:00
John Colvin 9ce5e971c6 fix: Fix typos in BTSColor paths in xaml 2023-07-05 18:11:40 -04:00
John Colvin 3e7cc49ebb hotfix: Format text w/ RegionalTickFactors in PriceEntryControl 2023-07-05 18:11:40 -04:00
John Colvin d07e68b6e2 hotfix: Use 1st leg's min price tick rule for Deribit combos 2023-07-05 18:11:40 -04:00
John Colvin 619ece2313 refactor: Consolidate impl for 'round_to_tick' overloads
Also add a 'decimal' overload for RegionalTickFactors. This is more work
for Deribit tick hotfix.
2023-07-05 18:11:40 -04:00
John Colvin 1dda9566f3 hotfix: Use '.' separator if tick unit is multiple of base 2023-07-05 18:11:06 -04:00
Taha 3704d1ed16 InterptheosServerPublisher is now opulated the newly added vol field in the PrecomputedTheoPoint proto 2023-07-05 14:41:31 -04:00
John Colvin a524ca02a5 refactor: Change tick arithmetic to use int64
The tick count is "price / tick_size" and tick_size is usually < 1, so
the extra bits may be needed to avoid arithmetic overflow
2023-07-05 12:02:28 -04:00
John Colvin 3c97f2552b fix build: Don't use libfmt or ranges in win cpp
I can't deal with this anymore. My build is suddenly broken on Windows
(for spark_crypto.prod branch). I assume they updated the compiler
in a way that isn't backwards compatible. Thanks Windows.
2023-07-05 11:58:14 -04:00
John Colvin 15b1d55ef1 fix build: Disable Binance md code on win 2023-07-05 11:58:14 -04:00
John Colvin 4e42c9f01b test: Refactor tick tests for RegionalTickFactors
Gives us some peace of mind about the dynamic ticks hotfix for Deribit
2023-07-05 11:58:14 -04:00
John Colvin 76775f821a hotfix: Support dynamic ticks in order ticket 2023-07-05 11:58:14 -04:00
John Colvin e7088f6b1c hotfix: Support dynamic ticks in ladder
Deribit now provides dynamic price ticks (a list of ticks per range).

https://www.deribit.com/kb/4-july-2023
https://www.deribit.com/kb/deribit-on-screen-options-tick-size-reduction
2023-07-05 11:58:14 -04:00
Taha Afzal f2c3ff4be6 Adding vol to PrecomputedTheoPoint 2023-07-05 15:16:45 +00:00
Taha 1fb42ade59 Adding perf logs on the front end 2023-07-05 10:50:13 -04:00
Taha 129650e9d4 Only the skew owner publishes theos to the server 2023-07-05 10:50:13 -04:00
John Colvin 170a44ac04 fix: Check sign to tick in correct direction
Don't assume it's positive and blindly tickup
2023-07-03 22:34:46 -05:00
Taha Afzal 7e04f02327 Merge branch 'spark_crypto.prod.autofit' into spark_crypto.prod 2023-07-04 03:21:28 +00:00
Taha Afzal abda2dbf97 VolSurfaces and achived theo history size is not mantained at runtime 2023-07-04 03:13:46 +00:00
Taha Afzal ad1a45cf49 ArchivedTheos are truncated on a server start 2023-07-04 03:13:46 +00:00
John Colvin e9abdd0ff2 chore: Remove unused fn 'gen_prices' 2023-07-03 12:30:03 -05:00
John Colvin 9254002318 fix: Check HasPrice/QuantityDisplay before accessing prop 2023-07-03 12:24:20 -05:00
John Colvin 596780b7f4 chore: Remove unused 'unit_quantity' prop 2023-07-03 12:22:37 -05:00
John Colvin 7ba061ac20 hotfix: Add cfg to account for new deribit tick format 2023-07-02 16:58:53 -05:00
John Colvin f8e6b73714 fix: Add missing venues to switch statement 2023-06-26 17:34:52 +00:00
John Colvin c01593d119 fix (ui colors): Cherry-pick synth editor dark theme from dev 2023-06-26 12:26:38 -04:00
John Colvin dd53c149c0 fix: Fix typo in default cfg 'SPOT' -> 'SECURITY_TYPE_SPOT' 2023-06-26 15:39:47 +00:00
John Colvin 04b4efe1e1 Update externals
Addresses more trivial build errors after upgrading to C++ 20
2023-06-26 15:24:46 +00:00
John Colvin 81bafbe68d fix build: Address trivial werrors after upgrading to C++ 20
All werrors were about implicit 'this' captures in lambdas
2023-06-26 15:24:46 +00:00
John Colvin 7ee95c55ab build: Upgrade CLI11 to v2.3.2 2023-06-23 15:35:06 +00:00
John Colvin 87d67feff4 build: Upgrade (Linux) c++ to stdlib 20 2023-06-23 15:33:40 +00:00
John Colvin d21ceddbb6 build: Upgrade (Windows) c++ to stdlib 20 2023-06-23 15:33:40 +00:00
John Colvin 4434df3d09 fix: Account for duplicate sec keys in 'normalize_drivers' 2023-06-23 15:33:40 +00:00
John Colvin 35916881c8 fix: Invalidate 'My Windows' module content on open (hover)
Bugs (fixed):
  * My Windows module popup is always empty

    Cause: The list of favorite windows is only evaluated on startup
    (before the list has been properly initialized). Furthermore, even
    if the list was initialized, it must be re-evaluated to account for
    newly added/removed windows.
2023-06-23 15:33:40 +00:00
John Colvin 5230d6bab5 build: Clean up build_spark.py 2023-06-23 15:33:40 +00:00
John Colvin 68c3418c04 build (refactor): Upgrade libfmt from ~8.0.1 to 10.0.0 2023-06-23 15:33:40 +00:00
John Colvin 7bce9f8a7f fix: Fix typo 'groups' -> 'currencies' 2023-06-21 11:07:47 -04:00
John Colvin a5b988738f chore: Add 'requests' module to py venv
This module is used to download files. It is required for the new
'install_fmt.py' script in externals.
2023-06-21 11:07:47 -04:00
John Colvin 53a4b61884 chore: Remove unused cmake var 2023-06-21 11:07:47 -04:00
John Colvin 7be111dc36 refactor: Use upper case http method name in ccapi params 2023-06-21 11:07:47 -04:00
John Colvin 0c5bd66cca refactor: Improve logging sec parsing logs (binance futs) 2023-06-21 11:07:47 -04:00
John Colvin b699c5a310 refactor: Simplify json impl w/ macros 2023-06-21 11:07:46 -04:00
John Colvin af8850f4c0 fix: Gracefully handle REQUEST_STATUS msgs (from ccapi) 2023-06-21 11:07:46 -04:00
John Colvin 10e359d6b5 refactor: Remove unnecessary template param 2023-06-21 11:07:46 -04:00
John Colvin b360881b8b chore: Impl 'descriptor' switch case for all exchs 2023-06-21 11:07:46 -04:00
John Colvin b250dc31d3 chore: Subscribe to BTC/USDC currencies in deribit md cfg 2023-06-21 11:07:46 -04:00
John Colvin 995df189ae chore: Add OKX to symbols.conf 2023-06-21 11:07:46 -04:00
John Colvin 93739e7010 improvement: Show Venue/SecType names in logs (not numeric val) 2023-06-21 11:07:46 -04:00
John Colvin bc42498d61 fix build: Disable ccapi/ftx
It appears that ccapi's ftx execution management code is broken (not that
it really matters, bc it's ftx). We prob should have already disabled this
disgraced exchange anyways.
2023-06-21 11:07:46 -04:00
John Colvin 2b95507c49 build: Better organize CMakeLists per-platform/project 2023-06-21 11:07:46 -04:00
John Colvin ca8c3a18d7 build: Generate only necessary cs files for protos
Prior to this commit, proto files were added to csprojs via the Proto3
target. This target generated 4 files per proto:
  .cs, .immutable.cs, .client.pb.cs, and server.pb.cs
For the (many) proto files that don't define services (just normal messages),
the .client/server.pb.cs files were unnecessary.

This commit splits proto-generation into 2 targets:
  * ProtoService - same as the old 'Proto3' target (generates 4 files per
    proto as described above).
  * Proto - a new target that generates only .cs, and .immutable.cs files
2023-06-21 11:07:46 -04:00
John Colvin e49066ffa7 fix build: Fix cacheVariables in CMakePresets 2023-06-21 11:07:45 -04:00
John Colvin e3cd910acd build: Use CMakePresets in build_spark.py 2023-06-21 11:07:45 -04:00
John Colvin cb47177b8e build: Remove unused 'link_build_dir' arg 2023-06-21 11:07:45 -04:00
John Colvin fba00c9d51 fix build: Remove unused fn 'notnulld'
This fn broke the Spark release build due to an '#if DEBUG' ppd
2023-06-21 11:07:45 -04:00
John Colvin af0b7d9146 fix build: Set cmake policy CMP0076 before target_sources
* Removes warning when configuring build on Windows
* Moves 'vs_filters.cmake' to 'cmake' dir (w/ other cmake scripts)
2023-06-21 11:07:45 -04:00
John Colvin e0a8c476f6 build: Increase sparkd parallel jobs to 8 2023-06-21 11:07:45 -04:00
John Colvin 5acb8100de devcontainer: Define env var 'XDG_CONFIG_HOME' 2023-06-21 11:07:45 -04:00
John Colvin 1c133e87e7 Update externals to get ccapi v5.45.2 2023-06-21 11:07:45 -04:00
John Colvin 8a8419576d build: Add 'Debug (ccapi diag)' configuration preset
Simply a clang debug build w/ ccapi log level set to 'Trace' (instead of
'Debug')
2023-06-21 11:07:45 -04:00
John Colvin 4a28723adb chore: Organize ccapi compile defs in CMakeLists 2023-06-21 11:07:45 -04:00
John Colvin e482408f29 fix: Add 'notional' filter class to model binance md json 2023-06-21 11:07:45 -04:00
John Colvin 4dcc98ab1f improvement: Check for empty str when parsing numbers (json) 2023-06-21 11:07:45 -04:00
John Colvin ace0fc35a2 refactor: Don't log error in Price::from_json
These logs get noisy fast
2023-06-21 11:07:45 -04:00
John Colvin c7d1eb00dc refactor: Add TimeSpan, TimeStamp arithmetic 2023-06-21 11:07:45 -04:00
John Colvin 949c8e0a32 build: Enable boost-beast for ccapi 2023-06-21 11:07:45 -04:00
John Colvin c2419b491a fix: Ensure ccapi log filenames aren't deallocated
Logs are committed asynchronously, and since ccapi log filenames are
passed by value, we must store them somewhere
2023-06-21 11:07:45 -04:00
John Colvin 786322f397 feat: Add Okx md (ccapi) 2023-06-21 11:07:45 -04:00
Taha 09e2a1764f Only the user with control over the volsurface can save it 2023-06-15 11:44:34 -04:00
John Colvin a780668bce fix: Ensure tick up/down values are within min/max bounds 2023-06-15 11:43:58 -04:00
John Colvin 5b79aeaa14 ui: Enforce min auto-fit period (30 secs) 2023-06-15 11:37:18 -04:00
John Colvin 049c006b5f ui: Disable driver/atm pts moved auto-fit ui 2023-06-15 11:37:18 -04:00
John Colvin 6fdc344472 feat: Add 'acquire ownership' button to vs context menu 2023-06-15 11:37:18 -04:00
John Colvin cb09b93553 chore: Rename ns bts.reactive -> Spark.Core.Reactive 2023-06-15 11:37:18 -04:00
John Colvin 21447d32e5 fix (vs ui): Only log 'is_dirty' changes for editable cols 2023-06-15 11:37:18 -04:00
Taha 28b03e1cd2 Only the user with control over the volsurface can save it 2023-06-14 22:23:45 -04:00
Taha a7acb89c15 Fixing merge conflict 2023-06-14 15:57:42 -04:00
John Colvin 35289710ba fix: Ensure tick up/down values are within min/max bounds 2023-06-14 14:20:47 -04:00
Taha 4677acfb39 Merge branch 'spark_crypto.prod.autofit' of bitbucket.org:bluetradesys/spark into spark_crypto.prod.autofit 2023-06-14 13:55:03 -04:00
Taha ac05d0d69e AutoFitter reads the settings from the settings now 2023-06-14 13:54:53 -04:00
John Colvin 25a9b6b433 fix: Ensure tick up/down values are within min/max bounds 2023-06-14 13:49:49 -04:00
John Colvin b78713ad0d ui: Enforce min auto-fit period (30 secs) 2023-06-14 12:59:40 -04:00
John Colvin 1f3d0e879c ui: Disable driver/atm pts moved auto-fit ui 2023-06-14 12:59:39 -04:00
John Colvin baad4dc68e feat: Add 'acquire ownership' button to vs context menu 2023-06-14 12:22:57 -04:00
John Colvin ef7c0c7a30 chore: Rename ns bts.reactive -> Spark.Core.Reactive 2023-06-14 11:39:06 -04:00
Taha 3b3c4a2a35 Merge branch 'spark_crypto.prod.autofit' of bitbucket.org:bluetradesys/spark into spark_crypto.prod.autofit 2023-06-14 10:21:55 -04:00
John Colvin ef9fc66a31 fix (vs ui): Only log 'is_dirty' changes for editable cols 2023-06-14 09:44:42 -04:00
John Colvin 212d9c853b fix: Init auto fit vals in vs settings editor 2023-06-14 09:09:34 -04:00
Taha 7815b18b6b Merge branch 'spark_crypto.prod.autofit' of bitbucket.org:bluetradesys/spark into spark_crypto.prod.autofit 2023-06-13 22:34:12 -04:00
Taha 05126074d4 Enabling AutoFit 2023-06-13 22:34:07 -04:00
John Colvin 3611d2c1f3 fix: Enable tick up/down buttons for nullable uint32 ctrl 2023-06-13 15:43:33 -04:00
John Colvin b5ad4b8104 feat: Add auto-fit ui for vs settings 2023-06-13 15:43:33 -04:00
Taha 43d7c0a2b5 Comments 2023-06-12 13:43:42 -04:00
Taha 12e361cec7 AutoFit based on a timer works now 2023-06-11 21:59:46 -04:00
John Colvin a4b0f026f4 style: Don't use implicit ptr -> bool conversion
Check 'ptr == nullptr' instead.
2023-06-07 13:16:22 -04:00
John Colvin 59f65c32d8 refactor: Change if/else blocks to switch expression 2023-06-07 13:16:22 -04:00
John Colvin ccec77f7d1 refactor: Fix typo 'uitilities' -> 'utilities' 2023-06-07 13:16:22 -04:00
John Colvin 1deeb85d22 improvement: Filter Deribit instrument reqs by regex cfg 2023-06-07 13:16:21 -04:00
John Colvin 648a32147a refactor: Eliminate SecurityID dependency on JsonReader
It's a fundamental type, included by many source files. The fewer
dependencies the better.
2023-06-07 13:16:21 -04:00
John Colvin 806ddfc156 Update externals so ccapi Deribit sends heartbeats 2023-06-07 13:16:21 -04:00
John Colvin 05b5f22204 fix: Capture cfg by value in thread lambda
Bugs (fixed):
  * A race crash occurs during startup, seems related to starting emumba
2023-06-07 13:16:21 -04:00
John Colvin 4f09d2cff1 refactor: Move Emumba deribit cfg to own proto 2023-06-07 13:16:21 -04:00
John Colvin e1234fe583 fix: Fix typo in log helper script 'show_last_run.sh'
So it actually applies the 'num_lines' override arg
2023-06-07 13:16:21 -04:00
John Colvin aa64dacd65 fix: Abort '_add_spread' if any leg secs missing
Bugs (fixed):
  * Sparkd crashes occassionally during startup. The last logs suggest
    it is related to adding Deribit spreads to shared mem.
2023-06-07 13:16:21 -04:00
John Colvin 10b062ebce fix build: Fix typo in CMakePresets cfg profiles 2023-06-07 13:16:21 -04:00
John Colvin e057057173 chore: Add replay/recording cfg to md_dev/prod profiles 2023-06-07 13:16:21 -04:00
John Colvin 82cec2672a fix build: Fix typo in Venue enums in default confs 2023-06-07 13:16:21 -04:00
John Colvin 50184cd6ff docker: Add hostRequirements, shm to devcontainer
The shm-size should be more than sufficient for general market data (the
default size of 64MB wasn't cutting it)
2023-06-07 13:16:21 -04:00
John Colvin e64dc49a64 fix build: Respect cfg path order in gen'd defaults
The path order represents the value-inheritance hierarchy. This way,
values from files earlier in the path take precedence over those from
later files.
2023-06-07 13:16:21 -04:00
John Colvin 010bd53723 fix: Include 'id' member in equals impl 2023-06-07 13:16:20 -04:00
John Colvin 50ae7ca7f9 feat: Add Deribit md (ccapi) 2023-06-07 13:16:20 -04:00
John Colvin 995bbef878 refactor: Add currencies to SecurityCriteria proto 2023-06-07 13:16:20 -04:00
John Colvin 8c325e30be improvement: Validate SecurityCriteriaClause proto
To ensure 'SecurityCriteriaClause::include' calls are reasonably cheap
2023-06-07 13:16:20 -04:00
John Colvin 6bb9b24210 feat: Add 'name_regexps' to SecurityCriteriaClause
This will allow us to filter out securities (instruments) at Deribit
(and likely other venues) BEFORE requesting their metadata. This
will be particularly useful when reacting to instrument state changes
(i.e., when a new instrument is added), which occur one instrument at
a time, and only provide the instrument's name.
2023-06-07 13:16:20 -04:00
John Colvin df1c558c33 refactor: Shorten SecurityCriteriaClause var names
* Drop 'security_' prefix (considering the class name, it is redundant)
* Change 'descs' -> 'names'
2023-06-07 13:16:20 -04:00
John Colvin 357d245a4c fix: Use JsonReader::read rather than nlohmann::get
The former is a more robust solution (logs exceptions, primitive string
conversion, etc.)
2023-06-07 13:16:20 -04:00
John Colvin d43863ab59 refactor: Change BoolName cstrings to strings 2023-06-07 13:16:20 -04:00
John Colvin 89188acc7d refactor: Define 'fmt' fns for a few md types 2023-06-07 13:16:20 -04:00
John Colvin 86a9fc0c83 chore: Add 'fmt' impl for domain::Security
This allows us to pass domain::Security to the LOG macro.
2023-06-07 13:16:20 -04:00
John Colvin 15579acd2a fix build (web): Add missing Venue switch cases 2023-06-07 13:16:19 -04:00
John Colvin 411009c996 Update externals to get ccapi v5.45.1 2023-06-07 13:16:19 -04:00
John Colvin 17a4cce68b refactor: Change 'Currency' from enum to string
It is an unnecessary burden to ensure the enum represents the union set
of currencies available at all suppported venues. The benefits of using
an enum are slim to none (it is practically unreferenced in code), so
the burden isn't justified.
2023-06-07 13:16:19 -04:00
John Colvin adb36795a8 chore: Rename a few .conf files to snake case 2023-06-07 13:16:19 -04:00
John Colvin 691545b71a refactor: Remove redundant sec type field
The Security class already has a 'type' fn that treats the type as a
function of the security variant. This is better than the 'type' field
in SecurityCommon, because it's naturally in sync with the variant.
2023-06-07 13:16:19 -04:00
John Colvin 31c7fa4f2c fix (pre-emptive): Return 'UNSPECIFIED' if sec-type variant empty 2023-06-07 13:16:19 -04:00
John Colvin 86009c3cdb refactor: Reduce Binance US dependency on Binance
There is some common code, but it seems like a good idea to keep
exchange-specific md impls modular/independent
2023-06-07 13:16:19 -04:00
Taha Afzal c9392dd306 Adding multiple accounts to CCAPIExecutionGateway 2023-06-06 18:30:38 +00:00
Taha Afzal ac8a6094fb Cleaning up corrid generation 2023-06-06 18:30:38 +00:00
Taha Afzal 6dbe497013 Bug fixes 2023-06-06 18:30:38 +00:00
Taha Afzal 274aa9b17d Error on order entry handled 2023-06-06 18:30:38 +00:00
Taha Afzal d47e61ca57 CCAPISingleOrderManager state machine fixed 2023-06-06 18:30:38 +00:00
Taha Afzal 3078b918d2 CLeaning up 2023-06-06 18:30:38 +00:00
Taha Afzal 95e9a6e8aa Sending RFQs now (combo creation not done) 2023-06-06 18:30:38 +00:00
Taha Afzal 1a3cf1dbea OpenOrders are reported bck now 2023-06-06 18:30:38 +00:00
Taha Afzal 0d705eed1f PositionRequest cleaning up and consolidating multiple messages (corresponding to different currencies) into one 2023-06-06 18:30:38 +00:00
Taha Afzal 1d28526792 Querying Positions and passing them to the client now 2023-06-06 18:30:38 +00:00
Taha Afzal 3ecf3dd048 OpenPositions and WorkingOrders queried from Deribit now 2023-06-06 18:30:38 +00:00
Taha Afzal 64ff79a38d Passing config to connection config to CCAPIDeribitOrderMux 2023-06-06 18:30:38 +00:00
Taha Afzal aba478f3dc Adding meaningful correlation ids 2023-06-06 18:30:38 +00:00
Taha Afzal 1025881ec8 Stable CCAPI based Deribit execution using per instrument subscription - improvements needed 2023-06-06 18:30:38 +00:00
Taha Afzal 6d95a645e3 Deribit's option position request hooked up 2023-06-06 18:30:38 +00:00
Taha Afzal 4769f66eea Adding some logs to SharedMemory and SharedList 2023-06-06 18:30:38 +00:00
John Colvin 82c126362d fix build: Fix a few path errors in publish target 2023-05-31 22:13:52 -04:00
Taha fd2ba70031 Culling the logs 2023-05-29 11:35:49 -04:00
Taha ab410c339f Order Tickets from the sheets are defaulted correctly now 2023-05-29 10:32:29 -04:00
Taha Afzal 9093c72646 Merge branch 'temp_arham' into spark_crypto.websocket-api.ccapi 2023-05-09 03:56:35 +00:00
arhamGIT 3a220b8320 Merge branch 'temp_arham' of bitbucket.org:bluetradesys/spark into temp_arham 2023-05-08 23:40:44 -04:00
arhamGIT ac20f7c9dd front end changes to make coinbase work 2023-05-08 23:40:28 -04:00
Taha Afzal 578146905e coinbase working 2023-05-09 03:31:20 +00:00
arhamGIT 7f07f5a69b Merge branch 'temp_arham' of bitbucket.org:bluetradesys/spark into temp_arham 2023-04-26 12:33:50 -04:00
arhamGIT 3dd7d0999e merge 2023-04-26 12:33:32 -04:00
Taha Afzal 5076770d15 coinbase sec names showing in shrd mem 2023-04-26 16:27:34 +00:00
Taha Afzal 791446c514 changed to merge proto updates 2023-04-25 15:38:37 +00:00
Taha Afzal 66370bd541 Merge branch 'spark_crypto.websocket-api.ccapi' of bitbucket.org:bluetradesys/spark into spark_crypto.websocket-api.ccapi 2023-04-24 23:24:25 +00:00
Taha Afzal cce8973e87 temp changes to make binance work 2023-04-24 23:19:15 +00:00
John Colvin 37dcd32c6b fix (cli): Fix config --query output indentation
There are 2 spaces per indent level
2023-04-20 00:46:23 -04:00
John Colvin d772958272 diag: Show file/line in Config logs 2023-04-20 00:46:23 -04:00
John Colvin c85e221e9f build: Automatically run build_spark.py in venv 2023-04-20 00:46:23 -04:00
John Colvin 28e95df5c6 refactor: Rename 'STOCK' -> 'SPOT' 2023-04-20 00:46:23 -04:00
John Colvin 8d84355d57 chore: Add Binance BTC qty formatting to default conf 2023-04-20 00:46:23 -04:00
John Colvin 5c25f9a3e5 refactor: Rename Venue/SecType enums according to conventions
https://protobuf.dev/programming-guides/style/#enums
2023-04-20 00:46:23 -04:00
John Colvin c0980809c5 chore: Format .conf files
* Indent with 2 spaces
2023-04-20 00:46:23 -04:00
John Colvin e946c6427e chore: Rename Ports.conf -> ports.conf
This is consistent with proto file naming conventions recommended by
Google (snake_case)
2023-04-20 00:46:23 -04:00
John Colvin 58ccbe2b19 chore: Consolidate default md conf into one file 2023-04-20 00:46:22 -04:00
John Colvin 7519c0b63a build: Update dotnet fable to 4.0.6 2023-04-20 00:46:22 -04:00
John Colvin 947c071cee build: Remove unused cmake def 2023-04-20 00:46:22 -04:00
John Colvin 54018fcce7 refactor: Clean up 'PlatformLoader'
* Rename 'SymbolLoader'
* Consolidate complicated filter logic into one 'load' overload

refactor: Get rid of unnecessary PlatformConfig
2023-04-20 00:46:22 -04:00
John Colvin 4b85eb33f8 chore: Remove redundant Symbols.conf file 2023-04-20 00:40:56 -04:00
John Colvin 592cf54e94 chore: Remove irrelevant test for config fallback url
The 'fallback url' was removed from config a few months ago (this test should
have been removed with it)
2023-04-20 00:40:56 -04:00
John Colvin b8368bf9fa chore: Remove unused args/code
* Removes several 'if' blocks whose predicates were effectively compile-time
  constants (i.e., unnecessary).
2023-04-20 00:40:50 -04:00
John Colvin 7d19ea84ab chore: Remove redundant fn 'get_thread_name' 2023-04-18 23:57:24 -04:00
John Colvin 14651341c4 fix: Fix 'Replace' fn so it ONLY replaces val if key exists
Bugs (fixed):
  * Spark (client) crashes anytime an order is canceled. Other changes
    to existing orders can cause the crash as well, but cancelling does
    so every time.

  * Cause: The IObservable<Update<T, TKey>> 'Naggle' ext fn was broken.
    Specifically, the KeyedValueCollection.Replace fn that 'Naggle' uses
    was broken. Instead of only replacing values with existing keys, it
    would add new keys as well.
2023-04-18 10:36:46 -04:00
John Colvin 681fbc6915 fix: Add 'spot' to sec type filter for preset algos 2023-04-18 00:12:46 -04:00
John Colvin 37d7f51d19 fix: Fix color-name typo in xaml 2023-04-17 23:53:51 -04:00
John Colvin 48eb0a2c9b fix: Use qty display settings in order ticket bbo header 2023-04-16 12:53:04 -04:00
John Colvin 6d2cd54deb fix: Init config w/ (optional) server arg in cli 2023-04-16 00:29:31 -04:00
John Colvin 2ec956b430 fix: Normalize cfg url path sep to '/' on Windows
Bugs (fixed):
  * All client config overrides are ignored (even the '-configserver'
  cli arg). Config overrides on the server appear to work, but they
  aren't forwaded to the client.

  Cause: cURL doesn't like the Window's path separator '\'
2023-04-16 00:13:42 -04:00
John Colvin 4f22ae04fe diag: Add verbose logs during config init 2023-04-16 00:13:42 -04:00
John Colvin 7da82d8444 cli (spark): Add 'sparkd' opt, integrate w/ CommandLineParser
This replaces the old '-configserver' arg, which will continue to be
supported for backwards compatabilty.
2023-04-14 15:16:32 -04:00
John Colvin d8611536ee fix build script: Fix venv path 2023-04-14 15:16:32 -04:00
John Colvin c94ffd1a56 cli: Add spark.console.cmd (alias for Spark.Console.exe) 2023-04-14 15:16:32 -04:00
John Colvin cddf081e19 fix (cli): Read %~dp0 before SHIFT
I just discovered that, when SHIFT is run (the first time), the value of
%~dp0 changes from the script's parent dir to the current working dir
2023-04-14 13:18:28 -04:00
John Colvin 94071b4889 refactor: Move Spark cmd opts to own file 2023-04-14 13:18:28 -04:00
John Colvin d7518cffe1 fix: Don't round ccapi book qtys to int 2023-04-14 17:07:25 +00:00
John Colvin fa26ecaf67 fix: Fix typo 'BINANCE' -> 'BINANCE_US' 2023-04-14 17:07:25 +00:00
John Colvin c2eaf00aaa improvement (cli): Add spot & option to sec type choices 2023-04-14 17:07:25 +00:00
John Colvin 74be3ec9b9 improvement (cli): Dump md json via nlohmann/json
This is much easier to read than previous json format, which we
generated ourselves, though a bit more verbose.
2023-04-14 16:55:29 +00:00
John Colvin 967e3100d2 fix (cli): Fix shared mem dump path 2023-04-14 16:55:29 +00:00
John Colvin b1d1243eae refactor: Change lib::domain::Book qty type to float 2023-04-14 16:55:29 +00:00
John Colvin 72edd99cb0 chore: Get rid of old/unused code from Spartan 2023-04-14 16:55:29 +00:00
John Colvin a512460396 diag: Log when quickbus starts/stops
Something is broken wrt port config on sparkd, and possibly spark
(client) as well. This might help track down the bug.
2023-04-14 16:54:17 +00:00
John Colvin 48ff593c2e chore: Remove unused fns
Also stop exporting PlatformLoader type since it isn't used outside of cpp
2023-04-13 13:59:14 +00:00
John Colvin d8fc99bca4 chore: Remove CMakeDeps that were accidentally added back 2023-04-13 13:59:13 +00:00
Taha Afzal 6a04759fe8 Merge branch 'spark_crypto.websocket-api.ccapi' of bitbucket.org:bluetradesys/spark into spark_crypto.websocket-api.ccapi 2023-04-12 10:28:44 -05:00
Taha Afzal 2cc07b71bd Added some documentation to order_flow.txt 2023-04-12 14:21:27 +00:00
Taha Afzal 40bf117f6c Execution config fixed 2023-04-12 02:34:37 +00:00
Taha Afzal 20209ef429 Merge branch 'spark_crypto.websocket-api.ccapi' of bitbucket.org:bluetradesys/spark into spark_crypto.websocket-api.ccapi 2023-04-11 16:38:47 +00:00
Taha Afzal c30a7a29fd Merging sim_execution 2023-04-11 15:34:20 +00:00
Taha Afzal c6ae2ea038 temp changes to make ws work 2023-04-11 14:16:40 +00:00
Taha Afzal f95715beec Merge branch 'spark_crypto.websocket-api.ccapi' of bitbucket.org:bluetradesys/spark into spark_crypto.websocket-api.ccapi 2023-04-10 15:57:38 +00:00
Taha Afzal 0e85f4b9d5 Merge branch for 'spark_crypto.websocket-api.linux' into spark_crypto.websocket-api.ccapi 2023-04-10 15:55:46 +00:00
arhamGIT c0bb26e56f Merge branch 'spark_crypto.websocket-api.linux' of bitbucket.org:bluetradesys/spark into spark_crypto.websocket-api.linux 2023-04-06 08:36:12 -04:00
arhamGIT 1e3e57b990 misc color changes 2023-04-06 08:35:46 -04:00
arhamGIT 0b50d5b5f0 dark theme in create synthetic 2023-04-05 13:52:45 -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
Taha Afzal eaa6493ce1 Deribit production markets can work with SimExecution 2023-02-27 20:54:43 -05:00
Taha Afzal 9fd0df2a13 SimExecution can be turned on from config now 2023-02-27 15:44:58 -05:00
Taha Afzal d272612c6c Removed SimExecutionGateway's dependency on MDClient 2023-02-26 23:04:08 -05:00
Taha Afzal 77408c8c5e Fixed Self matching 2023-02-26 22:29:10 -05:00
Taha Afzal 09958eb333 SimExecutionGateway can not reflect the trades permenantly in the books 2023-02-24 11:06:26 -05:00
Taha Afzal fdb7bbdac2 SimExecutionGateway can now deal with books with 0 qty levels 2023-02-23 15:51:36 -05:00
Taha Afzal a7f24e84e4 Fills are now generated for SimExecutionGateway 2023-02-23 14:21:07 -05:00
Taha Afzal 7e5c71f337 SimExecutionGateway almost working 2023-02-23 13:43:09 -05:00
Taha Afzal bed6feae3f Simplified SimExecutionGateway by combining order actions and matching into one 2023-02-19 00:49:30 -05:00
Taha Afzal d508d34a3a Completed books/order matching tests for SimExecutionGateway 2023-02-14 23:17:49 -05:00
Taha Afzal 5904bab03e Fixing bug with order crossing market in SimExecutionGateway 2023-02-14 16:21:57 -05:00
Taha Afzal fc9519cde3 Tests completed for working orders in the book (for SimExecution) 2023-02-14 10:06:53 -05:00
Taha Afzal 2dc823a6ac Added all the stateless book/order matching functions, and a few test cases 2023-02-13 20:09:47 -05:00
Taha Afzal ae9366f44a Adding comments and cleaning SimExecutionGateway 2023-02-12 21:27:54 -05:00
Taha Afzal db344656e5 Adding matching logic to SimExecutionGateway 2023-02-09 22:45:58 -05:00
Taha Afzal ac31deed5b Fixed Jetfix conf, and added some logs to SimExecutionGateway 2023-02-08 14:08:38 -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
Taha Afzal 60cde0712f Streamlining MarketDataGateway further - all sources are started independently of each other now, with their own GeneralMarketDataListener 2023-02-07 11:59:11 -05:00
Taha Afzal c20f017069 Simplified MarketDataGateway to deal with multiple market data producers as start-and-forget 2023-02-06 21:27:41 -05:00
Taha Afzal 5122038d21 Fixing windows side build 2023-02-06 15:21:51 -05:00
Taha Afzal 3039df56a0 Hooking up SimExecutionGateway to GeneralMarketDataListener 2023-02-06 14:04:57 -05:00
Taha Afzal 4dc675eb9c Getting rid of Jetfix's platform, using Security.proto's exchange 2023-02-05 20:24:29 -05:00
Taha Afzal cc8c181728 Establishign the connection bw listener and sim execution 2023-01-27 17:13:06 -05:00
Taha Afzal dde8524ecb Renaming some general classes and moving them out of cc 2023-01-26 23:22:36 -05:00
1176 changed files with 20657 additions and 505600 deletions

No files matched your search

+2 -2
View File
@@ -9,7 +9,7 @@
]
},
"fable": {
"version": "4.0.0-theta-018",
"version": "4.0.6",
"commands": [
"fable"
]
@@ -27,4 +27,4 @@
]
}
}
}
}
+29 -10
View File
@@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.12)
project(spark)
set(CMAKE_CXX_STANDARD 20)
if(WIN32)
set(platform "windows")
@@ -19,16 +20,21 @@ set(SPARK_LIB "${EXTERNALS_ROOT}/lib/$<CONFIG>")
set(BUILD_API_SERVER OFF CACHE BOOL "Build the api server")
set(BUILD_DOCS OFF CACHE BOOL "Build documentation")
set(BUILD_SDK OFF CACHE BOOL "Build the sdk")
set(BUILD_ADMIN_WEB_APP ON CACHE BOOL "Build the Admin WebApp")
if (WIN32)
set(BUILD_SPARKD OFF CACHE BOOL "Build sparkd")
set(BUILD_ADMIN_WEB_APP OFF CACHE BOOL "Build the Admin WebApp")
else()
set(BUILD_SPARKD ON CACHE BOOL "Build sparkd")
set(BUILD_ADMIN_WEB_APP ON CACHE BOOL "Build the Admin WebApp")
endif()
include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
link_directories(${SPARK_LIB})
if(BUILD_API_SERVER)
add_compile_definitions(SPARK_ENABLE_API_SERVER)
endif()
if(WIN32)
# flags
set(
@@ -41,7 +47,7 @@ if(WIN32)
/D_WINSOCK_DEPRECATED_NO_WARNINGS \
/D__TBB_NO_IMPLICIT_LINKAGE \
/DBOOST_ALL_NO_LIB \
/std:c++17 \
/std:c++20 \
/D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} /MT")
@@ -65,7 +71,6 @@ else()
set(SPARK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/$<CONFIG>")
if(BUILD_API_SERVER)
add_compile_definitions(SPARK_ENABLE_API_SERVER)
add_compile_definitions(APP_DATA_DIR="\$\{HOME\}/spark_api/")
add_compile_definitions(SHARED_MEMORY_PREFIX="spark_api")
elseif(BUILD_SPARKD)
@@ -80,17 +85,17 @@ else()
# config (i.e., resolved at runtime rather than compile-time)
add_compile_definitions(CCAPI_EXPOSE_INTERNAL)
# Enable ccapi exchanges/services
# Enable ccapi services
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_DERIBIT)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_FTX)
add_compile_definitions(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT)
# Enable ccapi exchanges
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_COIN_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_US)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_BINANCE_COIN_FUTURES)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_HUOBI)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_COINBASE)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_DERIBIT)
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_OKX)
set(CCAPI_LOG_LEVEL
"Warn"
@@ -112,6 +117,20 @@ else()
add_compile_definitions(BINANCE_USDS_FUTURES_INSTRUMENTS_PATH="/dapi/v1/exchangeInfo")
add_compile_definitions(BINANCE_USDS_FUTURES_PRICE_STATS_PATH="/dapi/v1/ticker/24hr")
# ** Deribit **
add_compile_definitions(DERIBIT_INSTRUMENT_PATH="/api/v2/public/get_instrument")
add_compile_definitions(DERIBIT_INSTRUMENTS_PATH="/api/v2/public/get_instruments")
add_compile_definitions(DERIBIT_COMBOS_PATH="/api/v2/public/get_combos")
add_compile_definitions(DERIBIT_TICKER_PATH="/api/v2/public/ticker")
add_compile_definitions(DERIBIT_CURRENCIES_METHOD="public/get_currencies")
add_compile_definitions(DERIBIT_INSTRUMENT_METHOD="public/get_instrument")
add_compile_definitions(DERIBIT_INSTRUMENTS_METHOD="public/get_instruments")
add_compile_definitions(DERIBIT_COMBO_DETAILS_METHOD="public/get_combo_details")
add_compile_definitions(DERIBIT_COMBOS_METHOD="public/get_combos")
# ** Okx **
add_compile_definitions(OKX_INSTRUMENTS_PATH="/api/v5/public/instruments")
add_compile_definitions(OKX_UNDERLYING_PATH="/api/v5/public/underlying")
endif()
# subdirs
+133 -17
View File
@@ -17,14 +17,17 @@
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build",
"environment": {
"SPARK_CMAKE_CXX_FLAGS": "/MP /WX /wd4250 /wd4251 /wd4244 /wd4267 /wd4355 /wd4275 /wd4345 /bigobj /EHsc /DCURL_STATICLIB /D_WINSOCK_DEPRECATED_NO_WARNINGS /D__TBB_NO_IMPLICIT_LINKAGE /DBOOST_ALL_NO_LIB /std:c++17 /D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600"
"SPARK_CMAKE_CXX_FLAGS": "/MP /WX /wd4250 /wd4251 /wd4244 /wd4267 /wd4355 /wd4275 /wd4345 /bigobj /EHsc /DCURL_STATICLIB /D_WINSOCK_DEPRECATED_NO_WARNINGS /D__TBB_NO_IMPLICIT_LINKAGE /DBOOST_ALL_NO_LIB /std:c++20 /D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600"
},
"cacheVariables": {
"CMAKE_CXX_FLAGS": "$env{SPARK_CMAKE_CXX_FLAGS}",
"CMAKE_CXX_FLAGS_DEBUG": "$env{SPARK_CMAKE_CXX_FLAGS} /MTd",
"CMAKE_CXX_FLAGS_RELEASE": "$env{SPARK_CMAKE_CXX_FLAGS} /MT",
"CMAKE_SHARED_LINKER_FLAGS_RELEASE": "/DEBUG /OPT:REF /OPT:ICF /LTCG",
"EXTERNALS_ROOT": "${sourceDir}/externals/windows"
"CMAKE_CXX_STANDARD": "20",
"CMAKE_SHARED_LINKER_FLAGS_DEBUG": "/DEBUG",
"CMAKE_SHARED_LINKER_FLAGS_RELEASE": "/OPT:REF /OPT:ICF /LTCG",
"EXTERNALS_ROOT": "${sourceDir}/externals/windows",
"CONFIG_DEFAULTS_PATH": "config/profiles/default/*.conf"
}
},
@@ -44,11 +47,11 @@
"type": "BOOL",
"value": "ON"
},
"CMAKE_CXX_STANDARD": "20",
"CMAKE_INSTALL_RPATH": ".",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}/sparkd",
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
"CMAKE_CXX_STANDARD": "17",
"BUILD_SDK": {
"type": "BOOL",
"value": "OFF"
@@ -70,7 +73,8 @@
"type": "BOOL",
"value": "ON"
},
"CCAPI_LOG_LEVEL": "Debug"
"CCAPI_LOG_LEVEL": "Info",
"CONFIG_DEFAULTS_PATH": "config/profiles/md_dev/*.conf;config/profiles/default/*.conf"
}
},
@@ -80,7 +84,8 @@
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CCAPI_LOG_LEVEL": "Warn"
"CCAPI_LOG_LEVEL": "Warn",
"CONFIG_DEFAULTS_PATH": "config/profiles/md_prod/*.conf;config/profiles/default/*.conf"
}
},
@@ -93,7 +98,7 @@
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++17 -fdiagnostics-color -DDEBUG",
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -DDEBUG",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/clang/debug/sparkd"
}
},
@@ -107,7 +112,7 @@
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++17 -fdiagnostics-color -Wno-stringop-overread -DDEBUG",
"CMAKE_CXX_FLAGS_DEBUG": "-g -fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -Wno-stringop-overread -DDEBUG",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/gcc/debug/sparkd"
}
},
@@ -121,7 +126,7 @@
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++17 -fdiagnostics-color -fno-omit-frame-pointer -DNDEBUG -O3",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -fno-omit-frame-pointer -DNDEBUG -O3",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/clang/release/sparkd"
}
},
@@ -135,16 +140,72 @@
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++17 -fdiagnostics-color -Wno-stringop-overread -Wno-maybe-uninitialized -fno-omit-frame-pointer -DNDEBUG -O3",
"CMAKE_CXX_FLAGS_RELEASE": "-fPIC -Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++20 -fdiagnostics-color -Wno-stringop-overread -Wno-maybe-uninitialized -fno-omit-frame-pointer -DNDEBUG -O3",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/gcc/release/sparkd"
}
},
{
"name": "spark-api-server-base",
"inherits": "spark",
"hidden": true,
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"BUILD_API_SERVER": {
"type": "bool",
"value": "ON"
},
"DOTNET_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0",
"DOTNET_WINDOWS_BINARY_DIR": "${sourceDir}/build/$<CONFIG>/bin/net7.0-windows"
}
},
{
"name": "spark-api-server-windows",
"inherits": "spark-api-server-base",
"displayName": "Spark Api Server",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"generator": "Visual Studio 17 2022"
},
{
"name": "spark-api-server-linux",
"inherits": "spark-api-server-base",
"displayName": "Spark Api Server",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"generator": "Ninja Multi-Config",
"cacheVariables": {
"BUILD_ADMIN_WEB_APP": {
"type": "BOOL",
"value": "OFF"
}
}
},
{
"name": "sparkd-publish",
"inherits": "sparkd-release-gcc",
"displayName": "Publish",
"description": "Production build compiled w/ gcc and ready to ship"
},
{
"name": "sparkd-ccapi-diag",
"inherits": "sparkd-debug-clang",
"displayName": "Debug (ccapi diag)",
"description": "Debug build w/ ccapi log level 'Trace'",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CCAPI_LOG_LEVEL": "Trace"
}
}
],
@@ -156,21 +217,20 @@
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"hidden": true,
"jobs": 4
"hidden": true
},
{
"name": "spark-dev",
"displayName": "Development",
"name": "spark-debug",
"displayName": "Debug",
"inherits": "spark",
"configurePreset": "spark",
"configuration": "Debug"
},
{
"name": "spark-prod",
"displayName": "Production",
"name": "spark-release",
"displayName": "Release",
"inherits": "spark",
"configurePreset": "spark",
"configuration": "Release"
@@ -184,7 +244,6 @@
"rhs": "Linux"
},
"hidden": true,
"jobs": 4,
"targets": [
"data_files_dir",
"admin_web_app_bundle",
@@ -224,12 +283,69 @@
"inherits": "sparkd"
},
{
"name": "spark-api-server-windows",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"hidden": true,
"configurePreset": "spark-api-server-windows"
},
{
"name": "spark-api-server-windows-debug",
"displayName": "Spark Api Server (Debug)",
"inherits": "spark-api-server-windows",
"configuration": "Debug"
},
{
"name": "spark-api-server-windows-release",
"displayName": "Spark Api Server (Debug)",
"inherits": "spark-api-server-windows",
"configuration": "Release"
},
{
"name": "spark-api-server-linux",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"hidden": true,
"configurePreset": "spark-api-server-linux"
},
{
"name": "spark-api-server-linux-debug",
"inherits": "spark-api-server-linux",
"displayName": "Spark Api Server (Debug)",
"configuration": "Debug"
},
{
"name": "spark-api-server-linux-release",
"inherits": "spark-api-server-linux",
"displayName": "Spark Api Server (Release)",
"configuration": "Release"
},
{
"name": "sparkd-publish",
"configurePreset": "sparkd-publish",
"displayName": "Publish",
"inherits": "sparkd",
"targets": "install"
},
{
"name": "sparkd-ccapi-diag",
"configurePreset": "sparkd-ccapi-diag",
"displayName": "Debug (ccapi diag)",
"inherits": "sparkd"
}
]
}
+12
View File
@@ -0,0 +1,12 @@
function(print_list var)
set(CMAKE_MESSAGE_INDENT "")
set(_var_str "")
foreach(item IN LISTS ${var})
set(_var_str "${_var_str}\n ${item}")
endforeach()
if ("${ARGN}" STREQUAL "VERBOSE")
message(VERBOSE "***${var}:${_var_str}")
else()
message("***${var}:${_var_str}")
endif()
endfunction()
@@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.5)
# add protos to the library to facilitate source_group
target_sources(libspark PUBLIC ${Config_Protos} ${NonConfig_Protos} ${Service_Protos})
# Explicitly set policy to convert relative paths to absolute (although this is a default policy,
# setting it explicitly prevents a compile-warning)
cmake_policy(SET CMP0076 NEW)
# add protos to the library to facilitate source_group
target_sources(libspark PUBLIC ${Config_Protos} ${NonConfig_Protos} ${Service_Protos})
function(assign_source_group)
foreach(_source IN ITEMS ${ARGN})
if(IS_ABSOLUTE "${_source}")
+188 -41
View File
@@ -1,8 +1,15 @@
[cmake]: https://cmake.org/
[cmake ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools
[cmake presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
[cpp ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
[clangd ext]: https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd
[dotnet]: https://dotnet.microsoft.com/en-us/download/dotnet
[ninja build]: https://ninja-build.org/
[ninja multi-config generator]: https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html
[python]: https://www.python.org/
[py venv]: https://docs.python.org/3/library/venv.html
[visual studio]: https://visualstudio.microsoft.com/
[visual studio generator]: https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2017%202022.html
# C++ Intellisense for Spark on Windows (VS Code)
**Steps**
@@ -30,47 +37,187 @@
* *The **Clang** presets may work best with the **Clangd** extension*
* *Intellisense won't work for generated files/types until you build libspark*
# Build Sparkd on Linux
# Build Spark on Windows
### Dependencies
* **[CMake][cmake]** (version >= 3.23)
* **[Python][python]** (version >= 3.9)
* **[Visual Studio][visual studio]** (2022)
* **[Dotnet][dotnet]** (sdk version >= 7.0.0)
**Notes**
* Sparkd is built using **[CMakePresets][cmake presets]**
### References
* **[CMakePresets][cmake presets]**
* **[Visual Studio 17 2022 (CMake generator)][visual studio generator]**
* **[Python Virtual Environment][py venv]**
### Notes
* *Run commands from the repo root dir unless otherwise noted*
**Steps**
1. Set up the **[Python virtual environment][py venv]**<br>
* *Run commands from the repo root dir unless otherwise noted*
1. Create the virtual environment
```
python3 -m venv .venv
```
2. Activate the virtual environment
```
. .venv/bin/activate
```
3. Verify `python` in the venv is first in the path<br>
* ***Expected:** output shows the absolute path to `.venv/bin/python`*
```
which python
```
4. Update `pip`<br>
* *The venv module initializes virtual environments with its own version
of pip, rather than the system's version. Even if the latter is up-to-date, the
former may not be.*
```
python -m pip install --upgrade pip
```
5. Install the packages listed `requirements.txt`
```
pip install -r requirements.txt
```
2. Build **Sparkd**<br>
* From the terminal
```
tools/build_sparkd.sh
```
* *Run with `--help` to see available options*
* From VS Code
1. Install the **[CMake extension][cmake ext]**
2. In the VS Code Command Palette, run **CMake: Select Configure
Preset**, then select the **[preset][cmake presets]** you wish to
build from the dropdown
3. Run **CMake: Build** in the command palette.
```
# Create the virtual environment
python3 -m venv .venv
# Activate the virtual environment
.venv\bin\activate.bat
# Update pip
python -m pip install --upgrade pip
# Install packages
pip install -r requirements.txt
```
2. Build
* *Run commands in python virtual environment*
```
# Configure
cmake --preset spark
# Build Debug
cmake --build --preset spark-debug
dotnet build spark_client.sln --configuration Debug
# Build Release
cmake --build --preset spark-release
dotnet build spark_client.sln --configuration Release
```
# Build Sparkd on Linux
### **Dependencies**
* **[CMake][cmake]** (version >= 3.23)
* **[Ninja Build][ninja build]**
* **[Python][python]** (version >= 3.9)
* **[Docker][docker]**
### **References**
* **[CMakePresets][cmake presets]**
* **[Ninja (CMake generator)][ninja generator]**
* **[Python Virtual Environment][py venv]**
* **[Devcontainer](devcontainer)**
### Prerequisites
* Open repository in a VS Code [Devcontainer](../docker/sparkd/dev/README.md), or
create your own [docker container](../docker/sparkd/dev/README.md) in which to build
### Notes
* *Run commands from the repo root dir unless otherwise noted*<br>
1. Set up the **[Python virtual environment][py venv]**
```
# Create the virtual environment
python3 -m venv .venv
# Activate the virtual environment
. .venv/bin/activate
# Update pip
python -m pip install --upgrade pip
# Install packages
pip install -r requirements.txt
```
2. Build
* *Run commands in python virtual environment*
```
# Configure clang debug
cmake --preset sparkd-clang-debug
# Build clang debug
cmake --build --preset spark-clang-debug
# Configure gcc release
cmake --build --preset sparkd-gcc-release
# Build gcc release
cmake --build --preset sparkd-gcc-release
```
# Build Spark Api Server on Windows
### **Dependencies**
* **[CMake][cmake]** (version >= 3.23)
* **[Python][python]** (version >= 3.9)
* **[Visual Studio][visual studio]** (version)
* **[Dotnet][dotnet]** (sdk version >= 7.0.0)
### **References**
* **[CMakePresets][cmake presets]**
* **[Visual Studio 17 2022 (CMake generator)][visual studio generator]**
* **[Python Virtual Environment][py venv]**
### Notes
* *Run commands from the repo root dir unless otherwise noted*
1. Set up the **[Python Virtual Environment][py venv]**
```
# Create the virtual environment
python3 -m venv .venv
# Activate the virtual environment
.venv\bin\activate.bat
# Update pip
python -m pip install --upgrade pip
# Install packages
pip install -r requirements.txt
```
2. Build
* *Run in python virtual environment*
```
# Configure
cmake --preset spark-api-server-windows
# Build debug
cmake --build --preset spark-api-server-windows-debug
dotnet build Spark.Api.Server.sln --configuration Debug
# Build release
cmake --build --preset spark-api-server-windows-release
dotnet build Spark.Api.Server.sln --configuration Release
```
# Build Spark Api Server on Linux
### **Dependencies**
* **[CMake][cmake]** (version >= 3.23)
* **[Python][python]** (version >= 3.9)
* **[Dotnet][dotnet]** (sdk version >= 7.0.0)
### **References**
* **[CMakePresets][cmake presets]**
* **[Ninja Multi-Config (CMake generator)][ninja multi-config generator]**
* **[Python Virtual Environment][py venv]**
### Prerequisites
* Open repository in a VS Code [Devcontainer](../docker/sparkd/dev/README.md), or
create your own [docker container](../docker/sparkd/dev/README.md) in which to build
### Notes
* *Run commands from the repo root dir unless otherwise noted*
1. Set up the **[Python Virtual Environment][py venv]**
```
# Create the virtual environment
python3 -m venv .venv
# Activate the virtual environment
. .venv/bin/activate
# Update pip
python -m pip install --upgrade pip
# Install packages
pip install -r requirements.txt
```
2. Build
* *Run in python virtual environment*
```
# Configure
cmake --preset spark-api-server-linux
# Build debug
cmake --build --preset spark-api-server-linux-debug
dotnet build Spark.Api.Server.sln --configuration Debug
# Build release
cmake --build --preset spark-api-server-linux-release
dotnet build Spark.Api.Server.sln --configuration Release
```
+4
View File
@@ -116,4 +116,8 @@ if ! shopt -oq posix; then
fi
fi
if [[ ! -v XDG_CONFIG_HOME ]]; then
export XDG_CONFIG_HOME="$HOME/.config"
fi
export PATH="$HOME/.local/bin:$PATH"
+3
View File
@@ -1,3 +1,6 @@
if ! set -q XDG_CONFIG_HOME
set -g XDG_CONFIG_HOME "$HOME/.config"
end
set fish_config_home $XDG_CONFIG_HOME/fish
if test -f "$fish_config_home/aliases.fish"
source "$fish_config_home/aliases.fish"
+6
View File
@@ -1,6 +1,10 @@
{
"image": "bluetradingsystems/sparkd-dev:latest",
"initializeCommand": "${localWorkspaceFolder}/docker/sparkd/dev/devcontainer_initialize.sh",
"hostRequirements": {
"memory": "4gb",
"storage": "6gb"
},
"postCreateCommand": "/home/bts/spark/docker/sparkd/dev/devcontainer_post_create.sh",
"containerUser": "bts",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/bts/spark,type=bind",
@@ -11,6 +15,8 @@
// unique set of ports. Usually we just add 1000 to each port as needed. For
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
"runArgs": [
"--shm-size",
"2gb",
"-p",
"8000:8000",
"-p",
+5 -1
View File
@@ -1,7 +1,9 @@
astroid==2.7.3
autopep8==1.5.7
cffi==1.14.6
certifi==2023.5.7
charset-normalizer==3.1.0
colorama==0.4.4
idna==3.4
isort==5.9.3
lazy-object-proxy==1.6.0
mccabe==0.6.1
@@ -12,7 +14,9 @@ pycodestyle==2.7.0
pycparser==2.20
pylint==2.10.2
PyMeta3==0.5.1
requests==2.31.0
termcolor==1.1.0
toml==0.10.2
urllib3==2.0.3
wrapt==1.12.1
yapf==0.31.0
+4 -1
View File
@@ -793,7 +793,10 @@ let venueToString (venue:bts.api.Venue) =
| bts.api.Venue.GLBX -> "GLBX"
| bts.api.Venue.DERIBIT -> "DERIBIT"
| bts.api.Venue.ICE -> "ICE"
// | bts.api.Venue.DERIBIT -> "DERIBIT"
| bts.api.Venue.BINANCE -> "BINANCE"
| bts.api.Venue.BinanceUs -> "BinanceUs"
| bts.api.Venue.BinanceUsdsFutures -> "BinanceUsdsFutures"
| bts.api.Venue.FTX -> "FTX"
let exchange_display expirationNameList =
let s = sprintf "%O" expirationNameList.product.exchange
+16 -9
View File
@@ -60,23 +60,30 @@
<ItemDefinitionGroup>
<!--
Each .proto input file produces 4 output files. For example,
'foo_bar.proto' produces:
* FooBar.cs
* foo_bar.client.pb.cs
* foo_bar.server.pb.cs
* foo_bar.immutable.pb.cs
Google automatically converts .proto filenames to TitleCase when
generating .cs files. Unless the .proto file is already in TitleCase
(against Google's recommended practice to use snake_case .proto files),
the 'CSName' must be overridden with the TitleCase formatting.
For example:
<Proto3 Include="foo_bar.proto" CSName="FooBar" />
<ProtoService Include="foo_bar.proto" CSName="FooBar" />
-->
<Proto3>
<Proto>
<OutDir>$(ProtoOutputPath)/</OutDir>
<CSName>%(FileName)</CSName>
</Proto3>
</Proto>
<!--
Each .proto (service) input file produces 4 output files. For example,
'foo_bar.proto' produces:
* FooBar.cs
* foo_bar.client.pb.cs
* foo_bar.server.pb.cs
* foo_bar.immutable.pb.cs
-->
<ProtoService>
<OutDir>$(ProtoOutputPath)/</OutDir>
<CSName>%(FileName)</CSName>
</ProtoService>
</ItemDefinitionGroup>
</Project>
+64 -22
View File
@@ -6,39 +6,76 @@
passes wipes out the additional compile includes that we add.
-->
<Target
Name="CompileProto3"
Name="CompileProtos"
AfterTargets="GetAssemblyVersion"
Condition="'@(Proto3)'!=''"
Condition="'@(Proto)'!=''"
Inputs="$(protoc);
%(Proto3.Identity);$(ProtocPluginBase)/plugin.py;
%(Proto.Identity);$(ProtocPluginBase)/plugin.py;
$(ProtocPluginBase)/templates/c#/immutable.pb.cs;"
Outputs="
%(Proto.OutDir)/%(Proto.CSName).cs;
%(Proto.OutDir)/%(Proto.FileName).immutable.pb.cs">
<MakeDir Directories="%(Proto.OutDir)" />
<Message
Importance="High"
Text="Generating Proto %(Proto.FileName)" />
<PropertyGroup>
<GenProtosCmd>$(protoc) %(Proto.FullPath)</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --error_format=msvs</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --proto_path=$(SourceRoot)</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --plugin=&quot;protoc-gen-spark-csharp=$(ProtocPlugin)&quot;</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --spark-csharp_out=lang=c#,templates=immutable.pb.cs,strip_out_path=1:%(Proto.OutDir)</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --csharp_out=%(Proto.OutDir)</GenProtosCmd>
</PropertyGroup>
<Exec Command="$(GenProtosCmd)" WorkingDirectory="%(Proto.RelativeDir)" />
<ItemGroup>
<Compile Include="%(Proto.OutDir)/%(Proto.CSName).cs" />
<Compile Include="%(Proto.OutDir)/%(Proto.FileName).immutable.pb.cs" />
</ItemGroup>
</Target>
<!--
Run this after GetAssemblyVersion because it happens after the Xaml compiler
and exists in both xaml and non-xaml projects. One of the (many) xaml compile
passes wipes out the additional compile includes that we add.
-->
<Target
Name="CompileProtoServices"
AfterTargets="GetAssemblyVersion"
Condition="'@(ProtoService)'!=''"
Inputs="$(protoc);
%(ProtoService.Identity);$(ProtocPluginBase)/plugin.py;
$(ProtocPluginBase)/templates/c#/client.pb.cs;
$(ProtocPluginBase)/templates/c#/server.pb.cs;
$(ProtocPluginBase)/templates/c#/immutable.pb.cs;"
Outputs="
%(Proto3.OutDir)/%(Proto3.CSName).cs;
%(Proto3.OutDir)/%(Proto3.FileName).client.pb.cs;
%(Proto3.OutDir)/%(Proto3.FileName).server.pb.cs;
%(Proto3.OutDir)/%(Proto3.FileName).immutable.pb.cs">
<MakeDir Directories="%(Proto3.OutDir)" />
%(ProtoService.OutDir)/%(ProtoService.CSName).cs;
%(ProtoService.OutDir)/%(ProtoService.FileName).client.pb.cs;
%(ProtoService.OutDir)/%(ProtoService.FileName).server.pb.cs;
%(ProtoService.OutDir)/%(ProtoService.FileName).immutable.pb.cs">
<MakeDir Directories="%(ProtoService.OutDir)" />
<Message
Importance="High"
Text="Generating service %(Proto3.FileName)" />
Text="Generating ProtoService %(ProtoService.FileName)" />
<PropertyGroup>
<GenProtosCmd>$(protoc) %(Proto3.FullPath)</GenProtosCmd>
<GenProtosCmd>$(protoc) %(ProtoService.FullPath)</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --error_format=msvs</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --proto_path=$(SourceRoot)</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --plugin=&quot;protoc-gen-service=$(ProtocPlugin)&quot;</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --service_out=lang=c#,strip_out_path=1:%(Proto3.OutDir)</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --csharp_out=%(Proto3.OutDir)</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --service_out=lang=c#,strip_out_path=1:%(ProtoService.OutDir)</GenProtosCmd>
<GenProtosCmd>$(GenProtosCmd) --csharp_out=%(ProtoService.OutDir)</GenProtosCmd>
</PropertyGroup>
<Exec Command="$(GenProtosCmd)" WorkingDirectory="%(Proto3.RelativeDir)" />
<Exec Command="$(GenProtosCmd)" WorkingDirectory="%(ProtoService.RelativeDir)" />
<ItemGroup>
<Compile Include="%(Proto3.OutDir)/%(Proto3.CSName).cs" />
<Compile Include="%(Proto3.OutDir)/%(Proto3.FileName).client.pb.cs" />
<Compile Include="%(Proto3.OutDir)/%(Proto3.FileName).server.pb.cs" />
<Compile Include="%(Proto3.OutDir)/%(Proto3.FileName).immutable.pb.cs" />
<Compile Include="%(ProtoService.OutDir)/%(ProtoService.CSName).cs" />
<Compile Include="%(ProtoService.OutDir)/%(ProtoService.FileName).client.pb.cs" />
<Compile Include="%(ProtoService.OutDir)/%(ProtoService.FileName).server.pb.cs" />
<Compile Include="%(ProtoService.OutDir)/%(ProtoService.FileName).immutable.pb.cs" />
</ItemGroup>
</Target>
@@ -53,12 +90,17 @@
</Target>
<Target
Name="AfterClean"
Name="CleanProtos"
AfterTargets="Clean">
<Delete Files="%(Proto3.OutDir)/%(Proto3.CSName).cs" />
<Delete Files="%(Proto3.OutDir)/%(Proto3.FileName).client.pb.cs" />
<Delete Files="%(Proto3.OutDir)/%(Proto3.FileName).server.pb.cs" />
<Delete Files="%(Proto3.OutDir)/%(Proto3.FileName).immutable.pb.cs" />
<Delete Files="%(Proto.OutDir)/%(Proto.CSName).cs" />
<Delete Files="%(Proto.OutDir)/%(Proto.FileName).client.pb.cs" />
<Delete Files="%(Proto.OutDir)/%(Proto.FileName).server.pb.cs" />
<Delete Files="%(Proto.OutDir)/%(Proto.FileName).immutable.pb.cs" />
<Delete Files="%(ProtoService.OutDir)/%(ProtoService.CSName).cs" />
<Delete Files="%(ProtoService.OutDir)/%(ProtoService.FileName).client.pb.cs" />
<Delete Files="%(ProtoService.OutDir)/%(ProtoService.FileName).server.pb.cs" />
<Delete Files="%(ProtoService.OutDir)/%(ProtoService.FileName).immutable.pb.cs" />
</Target>
<Target
@@ -8,10 +8,7 @@ namespace Spark.Console {
class ConfigCommand {
public static int run(ConfigCommandOptions opts) {
if (opts.sparkd is not null) {
var args = Environment.GetCommandLineArgs();
BTS.set_config_arg(args, opts.sparkd);
}
Configuration.initialize(opts.sparkd);
var keys = opts.keys?.ToArray() ?? Array.Empty<string>();
var excluded_keys = opts.excluded_keys?.ToArray() ?? Array.Empty<string>();
return Configuration.run_config_query(
@@ -19,7 +19,9 @@ namespace Spark.Console {
if (o.type == TestType.Unit) {
// Skip loading any local config
Configuration.initialize_for_test();
} else Configuration.initialize();
} else {
Configuration.initialize(o.sparkd);
}
ConsoleEx.WriteLine(JsonConvert.SerializeObject(o), ConsoleColor.DarkGray);
@@ -21,5 +21,8 @@ namespace Spark.Console {
[Value(1, MetaName = "Tests", HelpText = "Test list")]
public IEnumerable<string>? tests { get; set; }
[Option("sparkd", MetaValue = "URL", HelpText = "The sparkd server")]
public string? sparkd { get; set; }
}
}
@@ -45,7 +45,7 @@ namespace Spark.Console {
var security = new Security {
Symbol = "ES",
Exchange = Venue.Bts,
Currency = Currency.Usd,
Currency = "USD",
Synthetic = new SyntheticContract {
Name = "Synthetic1",
PriceCalc = SyntheticContract.Types.PriceCalc.Standard,
@@ -3,7 +3,6 @@ namespace mbts.spark {
using bts.api;
using bts.api.analytics;
using bts.collections;
using bts.reactive;
using bts.utils;
using mbts;
@@ -18,6 +17,7 @@ namespace mbts.spark {
using Spark.Core;
using Spark.Core.Collections;
using Spark.Core.Disposables;
using Spark.Core.Reactive;
using Spark.ViewModel.Commands;
using System;
@@ -148,7 +148,7 @@ namespace mbts.spark {
private static bool _direct_valid_driver(SecurityExpiration driver_exp) {
return driver_exp != null && /*driver_exp.expiration >= DateTime.Now.Date &&*/
(driver_exp.security_tuple.security_type == SecurityType.Future ||
driver_exp.security_tuple.security_type == SecurityType.Stock);
driver_exp.security_tuple.security_type == SecurityType.Spot);
}
// returns the driver sectup (it assumes all expirations of this option sectup
// are being driven by the same sectup)
@@ -202,4 +202,4 @@ namespace mbts.spark {
}
#endregion
}
}
}
@@ -0,0 +1,120 @@
namespace mbts.spark {
using bts;
using bts.api;
using mbts;
using mbts.analytics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
// For each vs, it sends the vol surface calc request (_do_periodic_auto_fit)
// and then commits the response
public class SingleVolSurfaceAutoFitSource : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly int _vs_id;
private readonly ITag<VolSurfaceInput> _vs_input_tag;
private readonly ITag<VolSurfaceCalculationResponse> _vs_calc_response_tag;
private DateTime _last_req_time = DateTime.MinValue;
private TimeSpan _req_period = TimeSpan.MaxValue;
private int _req_id = 0;
public SingleVolSurfaceAutoFitSource(int vs_id, ThreadedBlackboard bb) {
_bb = bb;
_vs_id = vs_id;
_vs_input_tag = SparkTags.VolSurfaceCommittedInputTag(vs_id);
_vs_calc_response_tag = SparkTags.VolSurfaceAutoCalculationResponseTag(vs_id);
_bb.subscribe(_vs_input_tag);
_bb.subscribe(_vs_calc_response_tag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.data_callback += _on_bb_data;
_bb.timer_callback += _on_bb_timer;
}
private void _on_bb_data(object tag, object data) {
if (tag.Equals(_vs_input_tag))
_possibly_autofit();
else if (tag.Equals(_vs_calc_response_tag)) {
VolSurfaceCalculationResponse calc_resp = data as VolSurfaceCalculationResponse;
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource._on_bb_data vs_id: {_vs_id} " +
$"received calculation response");
_bb.post(SparkTags.VolSurfaceCommitTag(_vs_id),
calc_resp.volsurface_calculation_output.volsurface_input);
}
}
private bool _user_has_vs_control() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
return vs_input != null &&
vs_input.surface_settings.core_settings.vs_manager == current_user;
}
private void _on_bb_timer() {
_possibly_autofit();
}
private void _possibly_autofit() {
var auto_fit_period = _get_auto_fit_periodic();
if (!auto_fit_period.HasValue)
return;
if (DateTime.Now - _last_req_time <= auto_fit_period)
return;
if (!_user_has_vs_control())
return;
// auto fit here
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource vs_id: {_vs_id} requesting fit " +
$"last_req_time: {_last_req_time} period: {auto_fit_period.Value}");
_send_request();
}
private Nullable<TimeSpan> _get_auto_fit_periodic() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
if (vs_input == null)
return null;
var fit_settings =
VolSurfaceDataFunctions.get_market_fit_settings(vs_input.surface_settings);
if (fit_settings == null)
return null;
var auto_fit_setting = fit_settings.vs_auto_fit_settings;
if (auto_fit_setting == null)
return null;
if (auto_fit_setting.auto_fit_region == VolSurfaceEnums.VolCurveFitRegion.None)
return null;
var time_based_fit_setting = auto_fit_setting.time_based_settings;
if (time_based_fit_setting == null)
return null;
if (!time_based_fit_setting.period_seconds.HasValue)
return null;
return TimeSpan.FromSeconds(time_based_fit_setting.period_seconds.Value);
}
private void _send_request() {
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
if (vs_input == null)
return;
VolCurveMarketFitAction mf_action = new VolCurveMarketFitAction(
VolCurveMarketFitAction.ActionType.FitWholeCurve, null, false);
VolCurveAction vc_action = new VolCurveAction(mf_action);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
vs_input.surface_parameters.curve_parameters_map.ToDictionary(
i => i.Item1,
i => vc_action
);
VolSurfaceCalculationRequest req = new VolSurfaceCalculationRequest(vs_input,
_req_id++, action_map, true, true);
_last_req_time = DateTime.Now;
_bb.post(SparkTags.VolSurfaceAutoCalculationRequestTag(_vs_id), req);
}
#region IDisposable
public void Dispose() {
_bb.data_callback -= _on_bb_data;
_bb.timer_callback -= _on_bb_timer;
}
#endregion
}
}
@@ -63,9 +63,9 @@ namespace mbts.spark {
vs_input, vs_nonvol_data.contracts, aux_data_map);
_timer.stop();
var tag_str = tag == null ? "Null" : tag.ToString();
bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
$"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
$"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
// bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
// $"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
// $"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
_logger.log(_log_id, _log_id, _timer.lastIntervalTime);
_bb.post(_out_bb_computed_vol_tag, vs_output);
}
@@ -4,6 +4,7 @@
using bts.api.analytics;
using mbts.analytics;
using mbts.ui;
using Spark.Core.Protobuf;
@@ -13,6 +14,7 @@
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Windows;
// For a single VolSurface this class
// 1. Listents to commmits and calls the service to persist data
@@ -24,7 +26,6 @@
private readonly ITag<VolSurfaceInput> _in_bb_committ_tag;
private readonly AnalyticsService _analytics_service;
private readonly HighPerformanceTimer _timer = new HighPerformanceTimer();
private readonly string _user;
private readonly List<IDisposable> _disposables = new List<IDisposable>();
private IDictionary<UInt64, VolSurfaceInputSnapshot> _snp_shot_map = new
Dictionary<UInt64, VolSurfaceInputSnapshot>();
@@ -35,10 +36,10 @@
volsurface_id));
_volsurface_id = volsurface_id;
_bb = threaded_bb;
_user = "TEST_USER"; // TODO
_in_bb_committ_tag = SparkTags.VolSurfaceCommitTag(volsurface_id);
_analytics_service = analytics_service;
_bb.subscribe(_in_bb_committ_tag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.data_callback += _on_bb_data;
var vs_sub = _analytics_service.subscribei_volsurfaces()
.ObserveOn(SynchronizationContext.Current)
@@ -54,8 +55,10 @@
private void _on_vss(bts.api.core.Update<VolSurfaceEntryImmutable, UInt64> update) {
IEnumerable<VolSurfaceEntryImmutable> all_vss = update.all;
var this_vs_vss = all_vss.Where(vs => vs.VsId == _volsurface_id);
bts.Log.Gen.info(String.Format("SingleVolSurfaceGateway._on_vss count:{0} vs_count:{1}",
all_vss.Count(), this_vs_vss.Count()));
bts.Log.Gen.info(
$"SingleVolSurfaceGateway._on_vss vs_count: {this_vs_vss.Count()} " +
$"all: {update.all.Count()} added: {update.added.Count()} " +
$"updated: {update.changed.Count()} removed: {update.removed.Count()}");
if (this_vs_vss.Count() == 0) {
return;
}
@@ -83,22 +86,39 @@
static VolSurfaceInput _to_vs_input(VolSurfaceEntryImmutable vs) =>
ProtobufSerializationHelper.deserialize<VolSurfaceInput>(vs.Volsurface.ToByteArray());
// private void _show_save_error(string message) {
// BTSMessageBox.Show(message, "Vol Save Error", BTSMessageBoxButtons.OK);
// }
private void _on_bb_data(object tag, object data) {
if (_in_bb_committ_tag.Equals(tag)) {
try {
_timer.start();
VolSurfaceInput vs_input = data as VolSurfaceInput;
var user_tup = _bb.read_state(SparkTags.CurrentUser);
string current_user = user_tup.name;
string vs_manager = vs_input.surface_settings.core_settings.vs_manager;
if (current_user != vs_manager) {
string msg = $"User {current_user} does not have the permission to save vols. " +
$"Permission can be obtained via the grid context menu item 'Acquire ownership from {vs_manager}'";
BTSMessageBox.Show(msg, "Vol Save Error", BTSMessageBoxButtons.OK);
return;
}
_sanitize_input(ref vs_input);
vs_input = VolSurfaceDataFunctions.unset_driver_overrides(vs_input);
vs_input.saver_login = _user;
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, _user);
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, current_user);
_timer.stop();
Log.Gen.info(String.Format(
"SingleVolSurfaceGateway._on_bb_data Time to commit volsurface {0} {1} msecs",
_volsurface_id, _timer.lastIntervalTime));
var archived_theos = _get_theos();
if (archived_theos != null)
if (archived_theos != null) {
_timer.start();
_analytics_service.archive_theos(archived_theos);
_timer.stop();
Log.Gen.info($"SingleVolSurfaceGateway._on_bb_data archiving theos vs_id: {_volsurface_id} size: " +
$"{archived_theos.SecTheo.Count} time: {_timer.lastIntervalTime} msecs");
}
} catch (Exception ex) {
Log.Gen.warn(ex.ToString());
_bb.post(SparkTags.MessageDisplayTag,
@@ -36,7 +36,11 @@ namespace mbts.spark {
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
a_syms => new SingleVolSurfaceCommittedOutputRetriever(_vs_id, _bb, a_syms)),
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
a_syms => new SingleVolSurfaceRequestCalculator(_vs_id, a_syms, _bb)),
a_syms => new SingleVolSurfaceRequestCalculator(
SingleVolSurfaceRequestCalculator.RequestType.UI, _vs_id, a_syms, _bb)),
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
a_syms => new SingleVolSurfaceRequestCalculator(
SingleVolSurfaceRequestCalculator.RequestType.AUTO, _vs_id, a_syms, _bb)),
new SingleVolSurfaceMarketImpliedVolSource(_vs_id, _bb),
// new SingleVolSurfaceTheoSource(_vs_id, _bb),
// TODO: suspending heatmaps for now (12/24/21)
@@ -44,6 +48,7 @@ namespace mbts.spark {
// server work that can optionally be done on the client
new SingleVolSurfaceDataSource (SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
a_syms => new SingleVolSurfaceInterpGreeksPublisher (_vs_id, _bb, a_syms)),
new SingleVolSurfaceAutoFitSource(_vs_id, _bb),
};
}
@@ -75,7 +75,7 @@ namespace mbts.spark {
var exp_price_map = OptionDriverPriceMap.get_vs_price_map(
all_sym_price_map, (s) => _option_symbols.Contains(s));
if (sc_data != null && vs_nonvol_input != null && exp_price_map != null) {
bts.Log.Gen.info($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
bts.Log.Gen.diag($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
"publishing VolSurfaceNonVolData");
_bb.post(_out_bb_vs_nonvol_data_tag,
new VolSurfaceNonVolData(
@@ -18,10 +18,16 @@ namespace mbts.spark {
// Pubs :
// 1. Calc Resp
public class SingleVolSurfaceRequestCalculator : IDisposable {
public enum RequestType {
UI = 0,
AUTO
}
private readonly RequestType _request_type = RequestType.UI;
private readonly int _vs_id;
private readonly HashSet<string> _symbols;
private readonly ThreadedBlackboard _bb;
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_request_tag;
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_auto_request_tag;
private readonly ITag<VolSurfaceNonVolData> _in_bb_nonvol_data_tag;
private readonly ITag<VolSurfaceStandardTime> _in_bb_vs_standard_time_tag;
private readonly ITag<VolSurfaceEnums.MarketSide> _in_bb_market_impliedvol_side_tag;
@@ -30,18 +36,22 @@ namespace mbts.spark {
/// data
private VolSurfaceOutput _last_output;
public SingleVolSurfaceRequestCalculator(int vs_id, IEnumerable<string> symbols,
public SingleVolSurfaceRequestCalculator(RequestType request_type,
int vs_id, IEnumerable<string> symbols,
ThreadedBlackboard threaded_bb) {
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id: {vs_id}");
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id: {vs_id} request_type: {request_type}");
_request_type = request_type;
_vs_id = vs_id;
_symbols = new HashSet<string>(symbols);
_bb = threaded_bb;
_in_bb_request_tag = SparkTags.VolSurfaceCalculationRequestTag(vs_id);
_in_bb_auto_request_tag = SparkTags.VolSurfaceAutoCalculationRequestTag(vs_id);
_in_bb_nonvol_data_tag = SparkTags.VolSurfaceNonVolDataTag(vs_id);
_in_bb_vs_standard_time_tag = SparkTags.VolSurfaceStandardTimeTag(vs_id);
_in_bb_market_impliedvol_side_tag = SparkTags.VolSurfaceMarketImpliedVolSide(vs_id);
_in_bb_dirty_tag = SparkTags.VolSurfaceDirtyStateTag(vs_id);
_bb.subscribe(_in_bb_request_tag);
_bb.subscribe(_in_bb_auto_request_tag);
_bb.subscribe(_in_bb_nonvol_data_tag);
_bb.subscribe(_in_bb_vs_standard_time_tag);
_bb.subscribe(_in_bb_vs_standard_time_tag);
@@ -51,11 +61,21 @@ namespace mbts.spark {
}
private void _on_bb_data(object tag, object data) {
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
_recompute_and_publish(true);
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
_recompute_and_publish(false);
switch (_request_type) {
case RequestType.UI: {
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
_recompute_and_publish(true);
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
_recompute_and_publish(false);
break;
}
case RequestType.AUTO: {
if (tag.Equals(_in_bb_auto_request_tag)) {
_serve_auto_request();
}
break;
}
}
}
private void _recompute_and_publish(bool is_new_request) {
@@ -80,7 +100,7 @@ namespace mbts.spark {
nonvol_data.driver_price_map,
vs_standard_time, true);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
_post_process_action(effective_req, aux_data_map);
_post_process_action(_bb, _vs_id, effective_req, aux_data_map);
_last_output = GeneralVolSurfaceCalculator.compute(effective_req.volsurface_input,
nonvol_data.contracts,
aux_data_map,
@@ -89,16 +109,49 @@ namespace mbts.spark {
effective_req.cascade_up,
effective_req.cascade_down);
_timer.stop();
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish time: " +
$"{_timer.lastIntervalTime}");
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish vs_id: {_vs_id} " +
$"time: {_timer.lastIntervalTime}");
_bb.post(SparkTags.VolSurfaceCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(_last_output, effective_req.request_id));
}
}
}
private void _serve_auto_request() {
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request " +
$"vs_id: {_vs_id} auto request received");
bool is_dirty = _bb.read_state(_in_bb_dirty_tag, false);
VolSurfaceNonVolData nonvol_data = _bb.read_state(_in_bb_nonvol_data_tag);
VolSurfaceStandardTime vs_standard_time = _bb.read_state(_in_bb_vs_standard_time_tag);
VolSurfaceCalculationRequest auto_req = _bb.read_state(_in_bb_auto_request_tag);
if (is_dirty || nonvol_data == null || vs_standard_time == null || auto_req == null) {
return;
}
_timer.start();
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map =
VolSurfaceUtilityFunctions.compute_aux_data(auto_req.volsurface_input,
nonvol_data.input,
nonvol_data.driver_price_map,
vs_standard_time, true);
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
_post_process_action(_bb, _vs_id, auto_req, aux_data_map);
var vs_output = GeneralVolSurfaceCalculator.compute(auto_req.volsurface_input,
nonvol_data.contracts,
aux_data_map,
action_map,
null,
auto_req.cascade_up,
auto_req.cascade_down);
_timer.stop();
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request vs_id: {_vs_id} " +
$"time: {_timer.lastIntervalTime}");
_bb.post(SparkTags.VolSurfaceAutoCalculationResponseTag(_vs_id),
new VolSurfaceCalculationResponse(vs_output, auto_req.request_id));
}
/// Computes the impl vol if the action is fit to market
private IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
private static IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
ThreadedBlackboard bb, int vs_id,
VolSurfaceCalculationRequest req,
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map) {
IDictionary<VolSurfaceExpiration, VolCurveAction> rv = req.action_map;
@@ -109,7 +162,7 @@ namespace mbts.spark {
VolCurveAction rep_action = rv.ElementAt(0).Value;
if (rep_action.action_type == VolCurveAction.ActionType.FitToMarket) {
IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> impl_vol_map =
_fill_in_implied_vols(req.volsurface_input, req.action_map, aux_data_map,
_fill_in_implied_vols(bb, vs_id, req.volsurface_input, req.action_map, aux_data_map,
req.cascade_up, req.cascade_down);
rv = impl_vol_map.ToDictionary(kv => kv.Key, kv => new VolCurveAction(
new VolCurveMarketFitAction(rep_action.market_fit_action.fit_action, kv.Value,
@@ -119,7 +172,8 @@ namespace mbts.spark {
return rv;
}
private IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
private static IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
ThreadedBlackboard bb, int vs_id,
VolSurfaceInput vs_inp,
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map,
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map,
@@ -140,15 +194,15 @@ namespace mbts.spark {
applicable_exps = applicable_exps.Where(vs_exp => master_exps.Contains(vs_exp));
VolSurfaceEnums.MarketSide impliedvol_side =
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(_bb, _vs_id);
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(bb, vs_id);
foreach (VolSurfaceExpiration vs_exp in applicable_exps) {
VolSurfaceExpiration md_exp;
VolCurveMarketImpliedVolData exp_impl_data =
VolSurfaceCoreFunctions.compute_implied_vols(vs_inp, vs_exp, aux_data_map,
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(_bb, arg_vs_exp),
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(bb, arg_vs_exp),
impliedvol_side,
SingleVolSurfaceMarketImpliedVolSource._ignore_market_implied_vol_source_settings(
_bb, _vs_id),
bb, vs_id),
out md_exp);
rv[vs_exp] = exp_impl_data;
}
@@ -160,8 +160,8 @@ namespace mbts.spark {
public static IEnumerable<bts.api.SecurityTuple> get_vs_sectups(
int vs_id, ThreadedBlackboard bb) {
VolSurfaceUsageListImmutable vs_usage_list = bb.read_state(SparkTags.VolSurfaceUsageTag);
return vs_usage_list.get_sectups(vs_id);
var vs_usage_list = bb.read_state(SparkTags.VolSurfaceUsageTag);
return (vs_usage_list?.get_sectups(vs_id)).empty_if_null();
}
@@ -242,4 +242,4 @@ namespace mbts.spark {
}
#endregion
}
}
}
@@ -81,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);
}
@@ -270,6 +270,7 @@ namespace mbts.spark {
new mbts.analytics.VolSurfaceNonVolInput(exp_param_map.exp_map.ToDictionary()));
} else if (_in_bb_req_vol.Equals(tag)) {
ClientArchivedVolRequest client_req = _bb.read_state(_in_bb_req_vol);
bts.Log.Ui.info($"VolSurfaceBusManager._on_bb_update ClientArchivedVolRequest: {client_req.ToString()}");
ArchivedTheoRetrieval ret = client_req.ret;
if (ret == null || ret.SecTup == null) {
bts.Log.Ui.warn($"VolSurfaceBusManager malformed ArchivedTheoRetrieval");
@@ -5,6 +5,7 @@ using bts.api;
using bts.collections;
using System.Linq;
using bts.api.orderman;
using bts.api.analytics;
using bts;
namespace mbts.spark {
@@ -23,6 +24,8 @@ namespace mbts.spark {
_order_service = order_service;
_bb.subscribe(SparkTags.AllInterpGreeks);
_bb.subscribe(SparkTags.SystemContractsTag);
_bb.subscribe(SparkTags.CurrentUser);
_bb.subscribe(SparkTags.VolSurfaceUsageTag);
_bb.data_callback += _on_data;
}
@@ -55,6 +58,27 @@ namespace mbts.spark {
return rv;
}
private IReadOnlyDictionary<string, OptionTheoInterpolationData>
_filter_for_publish(IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos) {
// find the symbol vs vs_id, get the committed vs, see if it is
// owned by the current user, and only then publish it
HashSet<string> syms = new HashSet<string>();
var usage = _bb.read_state(SparkTags.VolSurfaceUsageTag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
foreach (var u in (usage?.UsageList).empty_if_null()) {
VolSurfaceInput vs_input =
_bb.read_state(SparkTags.VolSurfaceCommittedInputTag(u.VsId));
if (vs_input != null) {
if (vs_input.surface_settings.core_settings.vs_manager ==
current_user) {
syms.Add(u.SecTup.Symbol);
}
}
}
return
all_theos.Where(kv => syms.Contains(kv.Key)).ToDictionary(kv => kv.Key, kv => kv.Value);
}
private void _publish_data() {
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag, null);
IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos =
@@ -64,6 +88,11 @@ namespace mbts.spark {
if (sc_data == null || all_theos == null || all_drivers == null)
return;
_timer.start();
all_theos = _filter_for_publish(all_theos);
if (all_theos.Count == 0) {
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data all_theos.Count == 0");
return;
}
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
@@ -86,6 +115,7 @@ namespace mbts.spark {
ptp.Vega = is_call ? tp.cvega : tp.pvega;
ptp.Theta = is_call ? tp.c_theta : tp.p_theta;
ptp.Rho = Double.NaN;
ptp.Vol = tp.vol;
ptp_list.Add(ptp);
}
bts.api.orderman.PrecomputedTheos server_theos = new PrecomputedTheos();
@@ -96,7 +126,7 @@ namespace mbts.spark {
_order_service.set_precomputed_theos(server_theos);
_timer.stop();
var pub_time = _timer.lastIntervalTime;
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data " +
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data symbols: {all_theos.Count} " +
$"size: {server_theos.Theos.Count} comp_time: {comp_time} msecs " +
$"pub_time: {pub_time}");
}
@@ -105,4 +135,4 @@ namespace mbts.spark {
_bb.data_callback -= _on_data;
}
}
}
}
@@ -32,7 +32,7 @@ namespace mbts.spark {
}
);
foreach (var sec_md in exp_secs) {
rv.Add(sec_md.security.Id, kv.Value);
rv[sec_md.security.Id] = kv.Value;
}
}
return rv;
@@ -26,12 +26,13 @@ namespace mbts.spark {
_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);
Log.Ui.info(
$"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
$"commited: {committed_value}; candidate: {candidate_value}");
};
if (column.type.is_editable()) {
_is_dirty.Changed += (o, n) => {
Log.Ui.info(
$"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
$"commited: {committed_value}; candidate: {candidate_value}");
};
}
void on_committed_changed(object? oldval, object? newval) =>
_is_dirty.refresh();
@@ -29,6 +29,7 @@ namespace mbts.spark.VolSurface {
class VolSurfaceGridVM : DisposableViewModelBase {
readonly VolSurfaceVM _parent;
readonly UserTuple _user;
readonly ObservableCollection<VolSurfaceDisplayGridExpiration> _expirations = new();
readonly DeferrableAction _invalidate_rows;
readonly ColumnsModel _columns_model;
@@ -41,10 +42,12 @@ namespace mbts.spark.VolSurface {
VolSurfaceVM vm,
VolSurfaceDisplayModel model,
ISet<string> hidden_columns,
ISet<VolSurfaceExpiration> hidden_expirations) {
ISet<VolSurfaceExpiration> hidden_expirations,
UserTuple user) {
Log.notnull(hidden_columns);
Log.notnull(hidden_expirations);
_parent = vm;
_user = user;
this.model = model;
_columns_model = new(create_exp_column_view());
_hidden_columns = hidden_columns;
@@ -83,7 +86,7 @@ namespace mbts.spark.VolSurface {
_columns_model);
VolSurfaceRowVM row_factory(VolSurfaceDisplayGridExpiration exp) =>
new(this, exp);
new(this, exp, _user);
IDisposable connect_row(VolSurfaceRowVM row) {
row.request_calculate += request_recalc;
@@ -1,5 +1,6 @@
#nullable enable
namespace mbts.spark {
using bts;
using bts.collections;
using mbts.spark.VolSurface;
@@ -23,6 +24,10 @@ namespace mbts.spark {
readonly VolSurfaceGridVM _parent;
readonly Property<VolSurfaceColumn?> _column;
readonly Property<DIYGridSuperHeaderVM?> _super_header;
readonly UserTuple _user;
readonly Property<bool> _is_open;
readonly DelegateProperty<string> _vs_manager;
readonly List<IDisposable> _resources = new();
ICommand? _revert_cmd;
ICommand? _shift_selection_cmd;
@@ -35,21 +40,29 @@ namespace mbts.spark {
ICommand? _copy_all_to_clipboard_cmd;
ICommand? _smart_copy_rolls_rates_cmd;
ICommand? _import_csv_cmd;
ObservantCommand? _acquire_ownership_cmd;
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row) {
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row, UserTuple user) {
_parent = parent;
_row = row;
_user = user;
_column = backing<VolSurfaceColumn?>(nameof(column));
_super_header = backing<DIYGridSuperHeaderVM?>(nameof(super_header));
_vs_manager = backing(
nameof(vs_manager),
() => _parent.model.get_active_settings()?.core_settings?.vs_manager ?? string.Empty);
_is_open = backing(nameof(is_open), false);
_dispose = Disposable.Create(
_resources.AddRange(
_column.invalidate_others_on_change(nameof(column_name)),
_super_header.invalidate_others_on_change(nameof(group_name)),
_is_open.Subscribe(_ => _vs_manager.refresh()),
_parent.visible_rows.ObserveSelf()
.BufferLatest(TimeSpan.FromSeconds(1))
.OnDispatcher()
.Subscribe(_ => sync_symbols()));
_dispose = Disposable.Create(() => _resources.DisposeAll(true));
void sync_symbols() {
var visible_exps = _parent.visible_rows.Map(r => r.expiration.symbol);
symbols.sync_with_models(
@@ -165,6 +178,34 @@ namespace mbts.spark {
_parent.model.csv_importing_enabled,
null);
public ICommand acquire_ownership_cmd {
get {
if (_acquire_ownership_cmd is null) {
_acquire_ownership_cmd = new(
() => {
var settings = _parent.model.get_active_settings();
if (settings is null) {
Log.Ui.error("Unable to acquire volsurface ownership: settings are null.");
return;
}
settings.core_settings.vs_manager = _user.name;
_parent.model.set_candidate_settings(settings);
});
_resources.Add(_vs_manager.observe_current(true)
.Subscribe(SelectorObserver.builder(_acquire_ownership_cmd).wrap<string>(
curr => curr != _user.name)));
}
return _acquire_ownership_cmd;
}
}
public bool is_open {
get => _is_open.Value;
set => _is_open.set(value);
}
public string vs_manager => _vs_manager.Value;
VolSurfaceDisplayGridColumn _assert_model() =>
column?.model ?? throw new InvalidOperationException();
@@ -1,5 +1,7 @@
#nullable enable
namespace mbts.spark.VolSurface {
using bts;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
@@ -16,14 +18,16 @@ namespace mbts.spark.VolSurface {
readonly VolSurfaceGridVM _parent;
readonly VolSurfaceDisplayGridExpiration _exp;
readonly Dictionary<string, CellData> _cells = new();
readonly UserTuple _user;
readonly DeferrableAction _request_calculate;
VolSurfaceRowContextMenuVM? _context_menu;
bool _is_selected;
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp) {
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp, UserTuple user) {
_parent = parent;
_exp = exp;
_user = user;
_request_calculate = new(() => request_calculate?.Invoke());
_dispose = Disposable.Create(() => {
_cells.Values.DisposeAll();
@@ -49,7 +53,7 @@ namespace mbts.spark.VolSurface {
}
public VolSurfaceRowContextMenuVM context_menu =>
_context_menu ??= new(_parent, this);
_context_menu ??= new(_parent, this, _user);
public VolSurfaceCellVM get_cell(VolSurfaceDisplayGridColumn column) =>
_cells.GetOrAdd(column.unique_col_name, _ => _create_cell(column)).vm;
@@ -0,0 +1,89 @@
#nullable enable
namespace mbts.spark {
using bts.api.domain;
using mbts.analytics;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using VolCurveFitRegion = analytics.VolSurfaceEnums.VolCurveFitRegion;
public class VolSurfaceAutoFitSettingsVM : DisposableViewModelBase {
readonly Property<VolCurveFitRegion> _fit_region;
readonly Property<uint?> _period_secs;
readonly Property<double?> _driver_points_moved;
readonly Property<double?> _atm_points_moved;
readonly Property<bool> _is_auto_fit_enabled;
public VolSurfaceAutoFitSettingsVM() {
_fit_region = backing(nameof(fit_region), VolCurveFitRegion.None);
_is_auto_fit_enabled = backing(
nameof(is_auto_fit_enabled),
_fit_region.observe_current(true),
r => r != VolCurveFitRegion.None);
_period_secs = backing<uint?>(nameof(period_secs), 10);
_driver_points_moved = backing<double?>(nameof(driver_points_moved));
_atm_points_moved = backing<double?>(nameof(atm_points_moved));
_fit_region.Changed += (o, n) => RaisePropertyChanged(nameof(is_auto_fit_enabled));
}
public IEnumerable<VolCurveFitRegion> fit_regions =>
EnumHelper.get_values<VolCurveFitRegion>();
public VolCurveFitRegion fit_region {
get => _fit_region.Value;
set => _fit_region.set(value);
}
public bool is_auto_fit_enabled => _is_auto_fit_enabled.Value;
public uint? period_secs {
get => _period_secs.Value;
set => _period_secs.set(value);
}
public double? driver_points_moved {
get => _driver_points_moved.Value;
set => _driver_points_moved.set(value);
}
public TickDisplaySettingsImmutable? driver_points_display =>
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
public double? atm_points_moved {
get => _atm_points_moved.Value;
set => _atm_points_moved.set(value);
}
public TickDisplaySettingsImmutable? atm_points_display =>
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
public void load_from(VolSurfaceAutoFitSettings value) {
fit_region = value?.auto_fit_region ?? VolCurveFitRegion.None;
period_secs = value?.time_based_settings?.period_seconds;
driver_points_moved = value?.driver_move_based_settings?.points_moved;
atm_points_moved = value?.atm_implied_vol_based_settings?.points_moved;
}
public void assign_to(VolSurfaceAutoFitSettings value) {
if (value is null) {
throw new ArgumentNullException(nameof(value));
}
value.auto_fit_region = fit_region;
value.time_based_settings = new() {
period_seconds = period_secs
};
value.driver_move_based_settings = new() {
points_moved = driver_points_moved
};
value.atm_implied_vol_based_settings = new() {
points_moved = atm_points_moved
};
}
}
}
@@ -40,6 +40,7 @@ namespace mbts.spark {
vol_model_cubic_spline_vm.load_from(settings.spline_model_settings);
vol_model_dual_quadratic_vm.load_from(settings.dualquadratic_model_settings);
vol_path_parabolic_vm.load_from(settings.core_settings.volpath_settings.parabolic_vol_path_settings);
auto_fit_vm.load_from(settings.core_settings.market_fit_settings.vs_auto_fit_settings);
init_rotation_vms(call_rotations, _core_settings.call_rotation.strike_vol_movement_pairs);
init_rotation_vms(put_rotations, _core_settings.put_rotation.strike_vol_movement_pairs);
@@ -105,6 +106,7 @@ namespace mbts.spark {
}).ToList();
_core_settings.strangle.strike_vol_movement_pairs = strangles.SelectMany(r => new[] { Tuple.Create(r.target_put_strike, r.put_move), Tuple.Create(r.target_call_strike, r.call_move) }).ToList();
_core_settings.atm_shift.atm_strike_vol_movement = atm_shifts.Count == 1 ? atm_shifts[0].move : 0;
auto_fit_vm.assign_to(_core_settings.market_fit_settings.vs_auto_fit_settings);
commit(_settings);
}
@@ -233,23 +235,7 @@ namespace mbts.spark {
}
}
public bool enable_auto_fit {
get => _core_settings.market_fit_settings.enable_auto_fit;
set {
using var _ = detect_property_change();
_core_settings.market_fit_settings.enable_auto_fit = value;
}
}
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region {
get => _core_settings.market_fit_settings.auto_fit_region;
set { _core_settings.market_fit_settings.auto_fit_region = value; }
}
public double auto_fit_time_ms {
get => _core_settings.market_fit_settings.auto_fit_time_ms;
set { _core_settings.market_fit_settings.auto_fit_time_ms = value; }
}
public VolSurfaceAutoFitSettingsVM auto_fit_vm { get; } = new();
public bool auto_decay_vol_time {
get => _core_settings.time_settings.time_mode == VolSurfaceEnums.VolSurfaceTimeMode.AutoDecay;
+10 -2
View File
@@ -1,5 +1,6 @@
#nullable enable
namespace mbts.spark {
using bts;
using bts.api;
using bts.collections;
@@ -27,6 +28,7 @@ namespace mbts.spark {
readonly IBus _bus;
readonly string _request_id = Guid.NewGuid().ToString();
readonly VolSurfaceDisplayModel _model;
readonly UserTuple _user;
readonly IRaiseCanExecuteChangedCommand _revert_cmd;
readonly IRaiseCanExecuteChangedCommand _commit_cmd;
readonly IDialogCommand _main_settings_cmd;
@@ -38,10 +40,16 @@ namespace mbts.spark {
bool _ignore_implied_vol_settings;
public VolSurfaceVM(
VolSurfaceDisplayModel model, int id, IBus bus, VolSurfaceWindowSettings startup_data) {
VolSurfaceDisplayModel model,
int id,
IBus bus,
VolSurfaceWindowSettings startup_data,
UserTuple user
) {
_model = model;
_vs_id = id;
_bus = bus;
_user = user;
macro_actions = new VolSurfaceMacroActionsVM(_model);
globals = new VolSurfaceGlobalParameterVM(_model);
@@ -49,7 +57,7 @@ namespace mbts.spark {
_grid_settings = startup_data.macro_grid_settings ?? new VolSurfaceMacroGridSettings();
var hidden_exps_set = startup_data.hidden_exps.empty_if_null().ToHashSet();
grid = new VolSurfaceGridVM(
this, _model, _grid_settings.hidden_cols(), hidden_exps_set);
this, _model, _grid_settings.hidden_cols(), hidden_exps_set, _user);
grid.on_selection_changed += _on_grid_selection_changed;
_model.publish_grid_settings(_grid_settings);
+2 -1
View File
@@ -162,7 +162,8 @@ namespace mbts.spark {
api_sec_exp.Exchange.to_venue(),
api_sec_exp.SecurityType.contract_case(),
api_sec_exp.Symbol,
api_sec_exp.Expiration.date_time());
api_sec_exp.HasExpiration ?
api_sec_exp.Expiration.date_time() : SecurityExpiration.empty.expiration);
public static SecurityExpiration to_sec_exp(this Security sec) =>
to_sec_exp(new SecurityImmutable(sec));
@@ -22,7 +22,7 @@ namespace mbts.spark {
// ii. IntepGreeks for each vs
public class VolSurfaceInterpGreeksMultiplexer : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly ITag<VolSurfaceUsageListImmutable> _in_bb_vs_usage_tag;
private readonly ITag<VolSurfaceUsageListImmutable?> _in_bb_vs_usage_tag;
private IDictionary<int, ITag<VSTheos>> _vs_theo_tags =
new Dictionary<int, ITag<VSTheos>>();
private VolSurfaceUsageListImmutable _vs_usage_map =
@@ -48,8 +48,8 @@ namespace mbts.spark {
}
private void _analyze_usages() {
_vs_usage_map = _bb.read_state(_in_bb_vs_usage_tag,
new VolSurfaceUsageListImmutable(new VolSurfaceUsageList()));
_vs_usage_map = _bb.read_state(_in_bb_vs_usage_tag) ??
new VolSurfaceUsageList().immutable();
bts.Log.Gen.info($"VolSurfaceInterpGreeksMultiplexer._analyze_usages " +
$"count: {_vs_usage_map.UsageList.Count()}");
foreach (int vs_id in _vs_usage_map.vss()) {
@@ -56,7 +56,12 @@ namespace mbts.spark {
new SecurityAlgoTypeSetImmutable(
new SecurityCriteriaClause(
exchanges: new[] { Venue.Glbx },
security_types: new[] { SecurityType.Future, SecurityType.Spread, SecurityType.Option }),
security_types: new[] {
SecurityType.Spot,
SecurityType.Future,
SecurityType.Spread,
SecurityType.Option
}),
new AlgoTypeSetImmutable(
full_algo_set.factory_types.Except(new[] { AlgoType.Spreader }),
full_algo_set.dynamic_types)),
@@ -64,7 +69,12 @@ namespace mbts.spark {
new SecurityAlgoTypeSetImmutable(
new SecurityCriteriaClause(
exchanges: EnumHelper.get_values_except(Venue.Glbx, Venue.Unset).ToArray(),
security_types: new[] { SecurityType.Future, SecurityType.Spread, SecurityType.Option }),
security_types: new[] {
SecurityType.Spot,
SecurityType.Future,
SecurityType.Spread,
SecurityType.Option
}),
new AlgoTypeSetImmutable(
full_algo_set.factory_types.Except(new[] { AlgoType.Spreader, AlgoType.GlbxIceberg }),
full_algo_set.dynamic_types)),
@@ -3,6 +3,7 @@ namespace mbts {
using bts.api;
using bts.utils;
using mbts.spark;
using mbts.ui.utility;
using mbts.utils;
@@ -25,6 +26,9 @@ namespace mbts {
SecurityTuple.empty.security_name,
DateTime.MinValue);
public static bts.api.domain.SecurityExpirationImmutable empty_api_sec_expi =>
empty.to_api_sec_expi();
// for deserialization
SecurityExpiration() =>
security_tuple = SecurityTuple.empty;
@@ -127,13 +131,14 @@ namespace mbts {
public bool Equals(SecurityExpiration? other) =>
other is not null &&
security_tuple.safe_equals(other.security_tuple) &&
expiration == other.expiration;
expiration == other.expiration &&
id == other.id;
public override bool Equals(object? obj) =>
Equals(obj as SecurityExpiration);
public override int GetHashCode() =>
HashCode.Combine(security_tuple, expiration);
HashCode.Combine(security_tuple, expiration, id ?? 0);
object ICloneable.Clone() =>
Clone();
@@ -157,7 +162,7 @@ namespace mbts {
public double expiration_oa_date { get; set; }
public SecurityExpirationProto Clone() =>
new() { security = security, expiration_oa_date = expiration_oa_date};
new() { security = security, expiration_oa_date = expiration_oa_date };
public bool Equals(SecurityExpirationProto? other) =>
other is not null &&
@@ -4,13 +4,11 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.analytics
{
namespace mbts.analytics {
/// These are the VolSurface settings that are common
/// to any Vol Model
[ProtoBuf.ProtoContract(Name = "VolSurfaceCoreSettings")]
public class VolSurfaceCoreSettings
{
public class VolSurfaceCoreSettings {
[ProtoBuf.ProtoMember(1)]
public string vol_surface_name;
[ProtoBuf.ProtoMember(2)]
@@ -87,5 +85,7 @@ namespace mbts.analytics
get => _time_settings ??= new();
set => _time_settings = value;
}
[ProtoBuf.ProtoMember(240)]
public string vs_manager { get; set; } = string.Empty;
}
}
@@ -75,6 +75,7 @@ namespace mbts.analytics {
if (core_settings.market_fit_settings == null) {
core_settings.market_fit_settings = new VolSurfaceMarketFitSettings();
}
core_settings.market_fit_settings.vs_auto_fit_settings ??= new();
if (core_settings.time_settings == null) {
core_settings.time_settings = new VolSurfaceTimeSettings();
}
@@ -141,6 +141,7 @@ namespace mbts.analytics
public enum VolCurveFitRegion
{
None,
ATM,
All
}
@@ -230,4 +231,4 @@ namespace mbts.analytics
USE_CALENDAR_FOR_BUSINESS
}
}
}
}
@@ -4,11 +4,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.analytics
{
namespace mbts.analytics {
[ProtoBuf.ProtoContract(Name = "VolSurfaceMarketFitSettings")]
public class VolSurfaceMarketFitSettings
{
public class VolSurfaceMarketFitSettings {
/// use one sided market or not
[ProtoBuf.ProtoMember(10)]
public bool ignore_one_sided_markets;
@@ -22,19 +20,44 @@ namespace mbts.analytics
public double graph_update_period_ms = 2500;
/// If this is set to true a regression curve is drawn
/// and the vol model adheres to that
[ProtoBuf.ProtoMember(50)]
[ProtoBuf.ProtoMember(50)]
public bool use_weighted_regression_fit = false;
[ProtoBuf.ProtoMember(55)]
public uint regression_order = 4;
/// The system auto fits and saves vols if this is
/// set to true
[ProtoBuf.ProtoMember(60)]
public bool enable_auto_fit = false;
/// If enable_auto_fit = true, this governs
/// how often the system auto-fits
[ProtoBuf.ProtoMember(70)]
public double auto_fit_time_ms = 30000;
[ProtoBuf.ProtoMember(80)]
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region = VolSurfaceEnums.VolCurveFitRegion.ATM;
[ProtoBuf.ProtoMember(90)]
public VolSurfaceAutoFitSettings vs_auto_fit_settings { get; set; }
}
[ProtoBuf.ProtoContract(Name = "VolSurfaceAutoFitSettings")]
public class VolSurfaceAutoFitSettings {
// each of these settings can be enabled independently. if they are enabled
// then any of them being true will result in an autmatic fit
[ProtoBuf.ProtoContract(Name = "TimeBasedSettings")]
public class TimeBasedSettings {
[ProtoBuf.ProtoMember(10)]
public uint? period_seconds { get; set; }
}
[ProtoBuf.ProtoContract(Name = "DriverMoveBasedSettings")]
public class DriverMoveBasedSettings {
[ProtoBuf.ProtoMember(10)]
public double? points_moved { get; set; }
}
[ProtoBuf.ProtoContract(Name = "ATMImpliedVolBasedSettings")]
public class ATMImpliedVolBasedSettings {
[ProtoBuf.ProtoMember(20)]
public double? points_moved { get; set; }
// we can do price terms moved as well
}
[ProtoBuf.ProtoMember(5)]
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region;
[ProtoBuf.ProtoMember(10)]
public TimeBasedSettings time_based_settings = null;
[ProtoBuf.ProtoMember(20)]
public DriverMoveBasedSettings driver_move_based_settings = null;
[ProtoBuf.ProtoMember(30)]
public ATMImpliedVolBasedSettings atm_implied_vol_based_settings = null;
}
}
@@ -475,6 +475,7 @@ namespace mbts.analytics {
public static ResultMessage save_volsurface(
VolSurfaceInput vs_input, AnalyticsService service, string user = "") {
vs_input.saver_login = user;
vs_input.surface_settings.core_settings.vs_manager = user;
// convert to VolSurfaceEntry
var to_save = new VolSurfaceEntry();
var serialized_vs = ProtobufSerializationHelper.serialize(vs_input);
@@ -595,7 +596,7 @@ namespace mbts.analytics {
_greek_getter(g)(t, is_call);
public static IDictionary<double, IEnumerable<Theos>> hash_strikes(IEnumerable<Theos> theos) =>
theos.Map(t => t.strike);
theos.Map(t => t.strike);
static Func<Theos, bool, double> _greek_getter(VolSurfaceGreeks g) =>
g switch {
@@ -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);
}
@@ -50,9 +50,7 @@ namespace mbts.analytics {
deliverable_security ??= SecurityExpiration.empty;
roll_schedule_expiration ??= SecurityExpiration.empty;
this.driver = driver ?? new(new() {
TimeWeightedInterpolated = new()
});
this.driver = driver ?? _default_driver;
_deliverable = deliverable_security.to_api_sec_expi();
_roll_schedule = roll_schedule_expiration.to_api_sec_expi();
this.roll_target = roll_target;
@@ -63,10 +61,10 @@ namespace mbts.analytics {
}
public AnalyticsExpirationParameters(ExpirationParameterEntryImmutable db_i) {
_security = db_i.SecurityExpiration;
_deliverable = db_i.Deliverable;
_roll_schedule = db_i.RollSchedule;
driver = db_i.Driver;
_security = db_i.HasSecurityExpiration ? db_i.SecurityExpiration : _default_api_sec_expi;
_deliverable = db_i.HasDeliverable ? db_i.Deliverable : _default_api_sec_expi;
_roll_schedule = db_i.HasRollSchedule ? db_i.RollSchedule : _default_api_sec_expi;
driver = db_i.HasDriver ? db_i.Driver : _default_driver;
roll_target = db_i.RollTarget;
riskless_rate = db_i.RisklessRate;
settlement_price = db_i.SettlementPrice;
@@ -74,6 +72,12 @@ namespace mbts.analytics {
vega_beta = db_i.VegaBeta;
}
static ExpirationDriverImmutable _default_driver =>
new(new() { TimeWeightedInterpolated = new() });
static bts.api.domain.SecurityExpirationImmutable _default_api_sec_expi =>
SecurityExpiration.empty_api_sec_expi;
public SecurityExpiration security => _security.to_sec_exp();
public SecurityExpiration driver_security => driver.HasSecurity ?
driver.Security.to_sec_exp() : SecurityExpiration.empty;
+8 -47
View File
@@ -5,9 +5,10 @@ namespace mbts.ui.shell {
using CommandLine;
using mbts.ui.native;
using Spark.Cli;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
@@ -21,7 +22,7 @@ namespace mbts.ui.shell {
static readonly Mutex _mutex;
ShellBootstrapper _runtime;
CLIOptions _opts;
SparkCmdOpts _opts;
Process _stunnel_process;
static App() {
@@ -43,25 +44,13 @@ namespace mbts.ui.shell {
#endif
public new int Run() {
var argv = Environment.GetCommandLineArgs().ToList();
// TODO: Add configserver arg to CLIOptions (for now, just strip it
// out before parsing)
for (int i = 0; i < argv.Count; i++) {
if (argv[i].ToLower() == "-configserver") {
argv.RemoveAt(i);
if (i < argv.Count) {
argv.RemoveAt(i);
}
break;
}
}
var argv = SparkCmdOpts.conform_opts(Environment.GetCommandLineArgs());
int exit_code = 1;
Parser.Default.ParseArguments<CLIOptions>(argv).WithParsed(opts => {
Parser.Default.ParseArguments<SparkCmdOpts>(argv).WithParsed(opts => {
_opts = opts;
var opt_desc = TableFormatter.fmt_table(
typeof(CLIOptions).GetProperties()
typeof(SparkCmdOpts).GetProperties()
.Select(p => (p.Name, p.GetValue(opts))), field_separator: "\n\t");
Log.Ui.info($"Running spark_client with options:\n\t{opt_desc}");
try {
@@ -94,7 +83,8 @@ namespace mbts.ui.shell {
CultureInfo.DefaultThreadCurrentUICulture = us_culture;
base.OnStartup(e);
_runtime = new ShellBootstrapper(_opts?.user, _opts?.password, _opts?.restore_workspace);
_runtime = new ShellBootstrapper(
_opts?.user, _opts?.password, _opts?.sparkd, _opts?.restore_workspace);
_runtime.Run();
// Select text on tab in
@@ -159,34 +149,5 @@ namespace mbts.ui.shell {
stunnel_job_error = "Error starting stunnel: " + ex.Message;
}
}
class CLIOptions {
[Option("multiple", HelpText = "Allow multple shells to run simultaneously")]
public bool allow_multiple_shells { get; set; }
[Option("stunnel", HelpText = "Use stunnel")]
public bool stunnel { get; set; }
[Option("no-stunnel", HelpText = "Don't use stunnel")]
public bool no_stunnel {
get => !stunnel;
set => stunnel = !value;
}
[Option("user", HelpText = "The user with which to login")]
public string user { get; set; }
[Option("password", HelpText = "The password with which to login")]
public string password { get; set; }
[Option("restore-workspace", HelpText = "Whether or not to restore the user's last workspace", Default = true)]
public bool restore_workspace { get; set; }
[Option("no-restore-workspace", HelpText = "Whether or not to restore the user's last workspace")]
public bool no_restore_workspace {
get => !restore_workspace;
set => restore_workspace = !value;
}
}
}
}
+1 -1
View File
@@ -104,7 +104,7 @@ namespace bts {
rv.ContractSpec.CashPerPoint = 50;
rv.ContractSpec.PriceMultiplier = 1.0;
// Currency
rv.Currency = Currency.Usd;
rv.Currency = "USD";
}
public static void fill_security_future(ref Security rv, DateTime exp) {
+4 -1
View File
@@ -136,7 +136,10 @@ namespace bts {
var meta = _security_metadata.get(smd.security);
if (meta.HasValue) {
exch_secs[smd.id] = new(
smd.security, smd.md, meta.Value.PriceDisplay, meta.Value.QuantityDisplay);
smd.security,
smd.md,
meta.Value.HasPriceDisplay ? meta.Value.PriceDisplay : null,
meta.Value.HasQuantityDisplay ? meta.Value.QuantityDisplay : null);
}
}
_bus.post(SparkTags.ServerSecuritiesTag, exch_secs);
+1 -1
View File
@@ -68,7 +68,7 @@ namespace bts {
}
private void _on_server_sec_map(IReadOnlyDictionary<uint, SecurityMD> sec_map) {
_log_options(sec_map);
//_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();
+4 -1
View File
@@ -16,8 +16,11 @@ using System.Composition;
namespace Spark {
class ShellServices {
public ShellServices([Import("config_source")]string config_source) =>
context = new(config_source);
[Export]
public ServiceContext context { get; } = new ServiceContext();
public ServiceContext context { get; }
[Export("settings")]
public PersistenceService settings_service => context.locate<PersistenceService>("settings");
@@ -56,7 +56,7 @@ namespace mbts.spark {
r => new ReportImmutable?(r),
r => r?.Id ?? throw new ArgumentNullException(nameof(r)));
_window_factories = new() {
new VolSurfaceWindowFactory(this, bus),
new VolSurfaceWindowFactory(this, bus, user.Value),
new AnalyticsExpirationParametersWindowFactory(this, bus),
new SlideReportWindowFactory(this, bus, reports_demuxer.subscribe),
new ComboReportWindowFactory(this, bus, reports_demuxer.subscribe),
@@ -793,70 +793,106 @@
Grid.Row="5"
Grid.Column="1"
Grid.ColumnSpan="4"
DataContext="{Binding auto_fit_vm}"
Header="Auto Fit"
Margin="0,5,0,0"
Padding="5"
IsEnabled="False">
Padding="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox
Grid.ColumnSpan="2" Content="Enabled" IsChecked="{Binding enable_auto_fit}" />
<TextBlock Grid.Row="1" Text="Region" Margin="16,5,0,0">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger
Binding="{Binding enable_auto_fit}"
Value="False">
<Setter
Property="Foreground"
Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Grid.Row="0" Text="Fit Region" />
<ComboBox
Grid.Row="0"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="8,0,0,0"
VerticalAlignment="Center"
ItemsSource="{Binding fit_regions}"
SelectedItem="{Binding fit_region}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={x:Static shell:VolSurfaceSettingsEditor.AutoFitRegionDisplayTextConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
Text="Every" />
<c:TickUpDownNullableUInt32
Grid.Row="1"
Grid.Column="1"
Margin="12,0,0,0"
HorizontalAlignment="Stretch"
ItemsSource="{Binding auto_fit_region_choices}"
SelectedValue="{Binding auto_fit_region}"
IsEnabled="{Binding enable_auto_fit}" />
HorizontalAlignment="Left"
VerticalAlignment="Center"
Minimum="30"
Margin="8,0,0,0"
Width="60"
Value="{Binding period_secs}"
IsEnabled="{Binding is_auto_fit_enabled}" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="3,5,0,0"
Text="secs" />
<TextBlock Grid.Row="2" Text="Fit Time" Margin="16,5,0,0">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding enable_auto_fit}" Value="False">
<Setter Property="Foreground" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<c:TextBoxDouble
<TextBlock
Grid.Row="2"
Margin="0,5,0,0"
Text="When Driver moves" />
<c:TickUpDownNullableDouble
Grid.Row="2"
Grid.Column="1"
c:TextBoxEx.Suffix="ms"
ToolTipService.ShowOnDisabled="True"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="8,0,0,0"
Width="60"
Margin="12,5,0,0"
IsEnabled="{Binding enable_auto_fit}"
Value="{Binding auto_fit_time_ms}"
Template="{StaticResource SuffixTemplate}" />
Value="{Binding driver_points_moved}"
Increment="0.01"
TickDisplaySettings="{Binding driver_points_display}"
IsEnabled="False"
ToolTip="Not yet supported (coming soon)" />
<TextBlock
Grid.Row="2"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="3,5,0,0"
Text="%" />
<TextBlock
Grid.Row="3"
Margin="0,5,0,0"
Text="When ATM moves" />
<c:TickUpDownNullableDouble
Grid.Row="3"
Grid.Column="1"
ToolTipService.ShowOnDisabled="True"
HorizontalAlignment="Left"
Margin="8,0,0,0"
Width="60"
Value="{Binding atm_points_moved}"
Increment="0.01"
TickDisplaySettings="{Binding atm_points_display}"
IsEnabled="False"
ToolTip="Not yet supported (coming soon)" />
<TextBlock
Grid.Row="3"
Grid.Column="2"
HorizontalAlignment="Left"
Margin="3,5,0,0"
Text="%" />
</Grid>
</GroupBox>
</Grid>
@@ -1,9 +1,20 @@
#nullable enable
namespace mbts.ui.shell {
using mbts.analytics;
using mbts.ui.converters;
using System.Windows;
using System.Windows.Data;
partial class VolSurfaceSettingsEditor : Window {
public VolSurfaceSettingsEditor() =>
InitializeComponent();
public static IValueConverter AutoFitRegionDisplayTextConverter =>
ValueConverter.create<VolSurfaceEnums.VolCurveFitRegion, string>(fr =>
fr switch {
VolSurfaceEnums.VolCurveFitRegion.None => "Disabled",
_ => fr.ToString()
});
}
}
@@ -262,7 +262,7 @@
IsDragAdornerEnabled="False"
ContextMenuOpening="_on_context_menu_opening">
<c:DIYGrid.ContextMenu>
<ContextMenu>
<ContextMenu IsOpen="{Binding is_open}">
<MenuItem
Header="Populate Empty Expirations"
Command="{Binding populate_empty_exps_cmd}" />
@@ -413,6 +413,11 @@
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="Import CSV..." Command="{Binding import_csv_cmd}" />
<MenuItem Command="{Binding acquire_ownership_cmd}">
<MenuItem.Header>
<TextBlock Text="{Binding vs_manager, StringFormat='Acquire ownership from \'{0}\''}" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</c:DIYGrid.ContextMenu>
<c:DIYGrid.RowStyle>
@@ -122,7 +122,7 @@
}
var mouse_pos = Mouse.GetPosition(this);
if (!this.ChildAtPoint<VolSurfaceRowView>(mouse_pos, out var row)) {
cm.IsOpen = false;
cm.SetCurrentValue(ContextMenu.IsOpenProperty, false);
return;
}
var cgs = g.GetColumnGeometry().ToList();
@@ -1,4 +1,5 @@
namespace mbts.spark {
using bts;
using bts.api;
using bts.spark;
@@ -14,14 +15,20 @@
public const string WINDOW_TYPE = "VolSurface";
readonly VolSurfaceWindowSettings _startup_data;
readonly UserTuple _user;
VolSurfaceVM _vm;
VolSurfaceView _view;
public VolSurfaceWindow(
VolSurfaceWindowFactory factory, IBus bus, string vs_name, VolSurfaceWindowSettings startup_data) :
base(vs_name, bus, factory, Icons.vol_surface) {
VolSurfaceWindowFactory factory,
IBus bus,
string vs_name,
VolSurfaceWindowSettings startup_data,
UserTuple user
) : base(vs_name, bus, factory, Icons.vol_surface) {
_startup_data = startup_data ?? throw new ArgumentNullException(nameof(startup_data));
_user = user;
this.vs_name = vs_name;
_dispose = Disposable.Create(() => Disposable.dispose(ref _vm));
}
@@ -37,7 +44,7 @@
public override UIElement Content => _view;
public void thread_init() {
_vm = new VolSurfaceVM(new VolSurfaceDisplayModel(vs_id, _bus, true), vs_id, _bus, _startup_data);
_vm = new VolSurfaceVM(new (vs_id, _bus, true), vs_id, _bus, _startup_data, _user);
_view = new VolSurfaceView {
DataContext = _vm
};
@@ -1,4 +1,5 @@
namespace mbts.spark {
using bts;
using bts.api;
using bts.shell;
@@ -12,10 +13,12 @@ namespace mbts.spark {
class VolSurfaceWindowFactory : ShellWindowFactory<VolSurfaceWindowSettings> {
readonly IBus _bus;
readonly UserTuple _user;
public VolSurfaceWindowFactory(IShellModule module, IBus bus)
public VolSurfaceWindowFactory(IShellModule module, IBus bus, UserTuple user)
: base(module, "VolSurface") {
_bus = bus;
_user = user;
_dispose = bus.subscribe(SparkTags.ShowVolSurface).OnDispatcher().Subscribe(
async state => _bus.post(SparkTags.ShowWindow, await _create(state)));
}
@@ -24,7 +27,7 @@ namespace mbts.spark {
_create(state);
async Task<IShellWindow> _create(VolSurfaceWindowSettings state) =>
new VolSurfaceWindow(this, _bus, await _lookup_vs_name(state.id), state);
new VolSurfaceWindow(this, _bus, await _lookup_vs_name(state.id), state, _user);
async Task<string> _lookup_vs_name(int vs_id) {
var (vol_surfaces, success) = await _bus.subscribe(SparkTags.VolSurfaceListTag).FirstAsyncTask(TimeSpan.FromSeconds(2));
@@ -176,10 +176,11 @@
</usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
</usp:DataGridParameterCellTemplateConverter>
<Style x:Key="ExpanderToggleButton" TargetType="ToggleButton">
<Setter Property="Foreground" Value="{u:BTSColor ExecutionStrategy.expander_foreground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid Background="Transparent">
<Grid Background="{u:BTSColor ExecutionStrategy.expander_background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="12" />
<ColumnDefinition/>
@@ -190,7 +191,7 @@
HorizontalAlignment="Center"
Margin="0,5,0,0"
SnapsToDevicePixels="False"
Stroke="#77333333"
Stroke="{u:BTSColor ExecutionStrategy.arrow_background}"
StrokeThickness="2"
VerticalAlignment="Top"
/>
@@ -199,6 +200,7 @@
ContentSource="{TemplateBinding Content}"
Margin="3,0,0,0"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
@@ -211,7 +213,7 @@
<Setter Property="Stroke" TargetName="arrow" Value="Black"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Stroke" TargetName="arrow" Value="#FF707070"/>
<Setter Property="Stroke" TargetName="arrow" Value="{u:BTSColor ExecutionStrategy.arrow_background_selected}"/>
<Setter Property="Foreground" Value="Gray"/>
</Trigger>
</ControlTemplate.Triggers>
@@ -278,7 +280,8 @@
<ColumnDefinition/>
</SelectiveScrollingGrid.ColumnDefinitions>
<DataGridRowHeader
Background="Transparent"
Foreground="{u:BTSColor ExecutionStrategy.expander_foreground}"
Background="{u:BTSColor ExecutionStrategy.data_grid_row_header_background}"
Padding="{Binding section_count, Converter={StaticResource RowHeaderGroupIndentConverter}}"
FontWeight="{Binding font_weight, FallbackValue={u:FontWeight Normal}}"
SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
@@ -317,9 +320,17 @@
<Style x:Key="DataGridRowStyle" TargetType="DataGridRow">
<Setter Property="BorderThickness" Value="1,0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Background" Value="{u:BTSColor ExecutionStrategy.alternate_column_background}"/>
<Setter Property="Template" Value="{StaticResource DataGridRowTemplate}"/>
<Setter Property="usp:ExecutionStrategyEditor.RowHasErrors" Value="{Binding has_errors}"/>
<Style.Triggers>
<Trigger Property="ListBox.AlternationIndex" Value="1">
<Setter Property="Background" Value="{u:BTSColor ExecutionStrategy.data_grid_background}"/>
</Trigger>
<!-- <Trigger Property="ListBox.AlternationIndex" Value="2">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="Navy"/>
</Trigger> -->
<DataTrigger Binding="{Binding read_only}" Value="True">
<Setter Property="TextElement.Foreground" Value="{u:BTSColor UI.input_disabled_text_dark}"/>
<Setter Property="ToolTip">
@@ -57,7 +57,7 @@ namespace mbts.spark {
main_vm.rows.AddRange(parsed_synths.Select(ps => {
var sec = ps.result;
var r = new SyntheticImportRowVM(orders_model.admin_model.user.id, used_names.Contains) {
tick_factors = TickUtils.get_regional_ticks(sec.ContractSpec).low_tick,
tick_factors = TickUtils.get_regional_ticks(sec.immutable()).low_tick,
name = sec.Synthetic.Name,
parsed_synth = ps
};
@@ -45,17 +45,17 @@ namespace mbts.spark {
SecurityMD.key_fn,
UserTuple.key_fn,
smd => smd.security.Synthetic.UserId,
(smd, sid, ut, uid) => new SyntheticTuple(smd.security, ut.name, null),
(smd, sid, uid) => new SyntheticTuple(smd.security, null, null),
SyntheticTuple.key_fn);
(smd, sid, ut, uid) => new SyntheticSecurityTuple(smd.security, ut.name, null),
(smd, sid, uid) => new SyntheticSecurityTuple(smd.security, null, null),
SyntheticSecurityTuple.key_fn);
synthetic_tuples = synthetic_user_join.LeftJoin(
strategy_source,
SyntheticTuple.key_fn,
SyntheticSecurityTuple.key_fn,
tup => tup.security_id,
(synth_tup, id, strat_tup) => new SyntheticTuple(synth_tup.security, synth_tup.user_name, strat_tup.strategy),
(synth_tup, id, strat_tup) => new SyntheticSecurityTuple(synth_tup.security, synth_tup.user_name, strat_tup.strategy),
(synth_tup, id) => synth_tup,
SyntheticTuple.key_fn);
SyntheticSecurityTuple.key_fn);
}
public IObservable<IReadOnlyDictionary<uint, ExecutionStrategyImmutable>> strategy => _bus.subscribe(SparkTags.SyntheticstrategiesTag);
@@ -69,7 +69,7 @@ namespace mbts.spark {
.LeftJoin(strategies, (security, strategy) => (new ExecutionStrategyImmutable?(strategy), security), security => (new ExecutionStrategyImmutable?(), security))
);
public IObservable<TableUpdate<SyntheticTuple, uint>> synthetic_tuples { get; }
public IObservable<TableUpdate<SyntheticSecurityTuple, uint>> synthetic_tuples { get; }
public IObservable<ExecutionStrategyImmutable> security_strategy(uint security_key) =>
strategy.TrySelect(s => {
@@ -56,7 +56,7 @@ namespace mbts.spark {
public SyntheticEditorVM(SpreaderModel model, SecurityImmutable synthetic, ExecutionStrategyImmutable strategy, IObservable<bool> security_has_working_orders)
: this(synthetic, strategy, false) {
ExceptionAssert.InvalidOperation.That(synthetic.Id != 0, "This constructor may only be used for already-existing synthetic securities (id must be non-zero).");
_tick_factors = TickUtils.get_regional_ticks(synthetic.ContractSpec).low_tick;
_tick_factors = TickUtils.get_regional_ticks(synthetic).low_tick;
if (TickUtils.is_integer_tick(synthetic.ContractSpec)) {
_use_fractional_ticks = false;
_integer_tick_size = DoubleHelper.to_optional_int32(synthetic.ContractSpec.LowTick) ?? 0;
@@ -112,7 +112,7 @@ namespace mbts.spark {
_set_ticks_automatically = false;
_set_quantities_automatically = false;
_set_price_mults_automatically = false;
_tick_factors = TickUtils.get_regional_ticks(_synthetic.ContractSpec).low_tick;
_tick_factors = TickUtils.get_regional_ticks(_synthetic.immutable()).low_tick;
} else {
_deduce_name();
_deduce_ticks();
@@ -411,7 +411,7 @@ namespace mbts.spark {
}
void _deduce_ticks() {
_tick_factors = TickUtils.deduce_ticks(leg_tuples.Select(l => l.leg.Security.ContractSpec));
_tick_factors = TickUtils.deduce_ticks(leg_tuples.Select(l => l.leg.Security.immutable()));
RaisePropertyChanged(nameof(tick_factors));
}
@@ -148,12 +148,12 @@ namespace mbts.spark {
};
public class Row : DisposableViewModelBase, IEquatable<Row>, IRowVM, IActionableRowVM {
public readonly SyntheticTuple synthetic_tuple;
public readonly SyntheticSecurityTuple synthetic_tuple;
readonly IBus _bus;
readonly Property<uint> _n_working_orders;
public Row(IBus bus, SyntheticTuple synthetic_tuple, OrdersModel orders_model) {
public Row(IBus bus, SyntheticSecurityTuple synthetic_tuple, OrdersModel orders_model) {
_bus = bus;
this.synthetic_tuple = synthetic_tuple;
_n_working_orders = backing(nameof(n_working_orders),
@@ -371,6 +371,7 @@
<usp:AlgoEditorView
DataContext="{Binding selected_definition}"
Background="Transparent"
Foreground="{u:BTSColor UI.default_text}"
BorderBrush="{Binding algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
Padding="12,8" />
</ScrollViewer>
@@ -79,7 +79,7 @@ namespace Spark.Trading {
meta.Value.Value.PriceDisplay : Security.Defaults.price_display_settingsi;
if (security.HasValue) {
rv = rv.with(
tick_factors: security.spec_or_default().tick_factors().to_api_tick_factors());
tick_factors: security.Value.tick_factors().to_api_tick_factors());
}
return rv;
}
@@ -242,7 +242,7 @@ namespace mbts.spark {
}
public OrderBookVM? create_orderbook_vm() {
var ticks = _owner.spec.full_ticks_from_zero(real_price);
var ticks = _owner.spec.count_ticks(real_price);
if (!ticks.HasValue) {
return null;
}
@@ -194,7 +194,7 @@ namespace mbts.spark {
void increment_ticks_per_row() {
var tpr = ticks_per_row;
for (uint i = 1; i < 100; i++) {
var new_tpr = TickUtils.fit_ticks_per_row_to_dollars(security.ContractSpec, ticks_per_row + i);
var new_tpr = TickUtils.fit_ticks_per_row_to_dollars(security, ticks_per_row + i);
if (new_tpr != tpr) {
ticks_per_row = new_tpr;
break;
@@ -204,7 +204,7 @@ namespace mbts.spark {
void decrement_ticks_per_row() {
var tpr = ticks_per_row;
for (uint i = 1; i < tpr; i++) {
var new_tpr = TickUtils.fit_ticks_per_row_to_dollars(security.ContractSpec, tpr - i);
var new_tpr = TickUtils.fit_ticks_per_row_to_dollars(security, tpr - i);
if (new_tpr != tpr) {
ticks_per_row = new_tpr;
break;
@@ -242,7 +242,8 @@ namespace mbts.spark {
// since settings profiles may be shared among ladders
// of different securities/tick sizes, the ticks per row setting
// may need to be rounded to a number that is a factor of $1.00
public uint? rounded_ticks_per_row => TickUtils.fit_ticks_per_row_to_dollars(_security.ContractSpec, ticks_per_row);
public uint? rounded_ticks_per_row =>
TickUtils.fit_ticks_per_row_to_dollars(_security, ticks_per_row);
public MarketSizeDisplayVM size_display { get; } = new MarketSizeDisplayVM();
public SimpleGridSettingsVM mini_ob { get; } = new SimpleGridSettingsVM(OrderBookGridSettingsManager.hard_column_flags);
@@ -101,7 +101,7 @@ namespace mbts.spark {
IReadOnlyDictionary<uint, OrderRecordTuple> _cache = DictionaryHelper.empty<uint, OrderRecordTuple>();
ImmutableDictionary<AlgoKey, LadderFavoriteAlgoMetadata> _favorite_algo_metadata_map =
ImmutableDictionary<AlgoKey, LadderFavoriteAlgoMetadata>.Empty;
ContractSpecificationImmutable? _spec;
RegionalTickFactors? _spec;
LadderVM(
IBus bus,
@@ -245,10 +245,9 @@ namespace mbts.spark {
var orders_source = settings.Select(s => s.ticks_per_row).DistinctUntilChanged()
.SelectSeries(tpr => {
var spec = TickUtils.multiply_tick_size(security.ContractSpec, tpr);
var spec = TickUtils.multiply_tick_size(security.regional_ticks(), tpr);
var price_comp = Comparer<double>.Create((a, b) =>
Comparer<int?>.Default.Compare(
spec.full_ticks_from_zero(a), spec.full_ticks_from_zero(b)));
Comparer<long?>.Default.Compare(spec.count_ticks(a), spec.count_ticks(b)));
return _order_price_source(model.orders, price_comp, p => TickUtils.normalize_price(spec, p));
});
_day_change.Changed += (o, n) => RaisePropertyChanged(
@@ -397,9 +396,10 @@ namespace mbts.spark {
}
public SecurityImmutable security => _model.security;
public ContractSpecificationImmutable spec => _spec ?? (
public RegionalTickFactors spec => _spec ?? (
_spec = TickUtils.multiply_tick_size(
security.ContractSpec, current_settings?.ticks_per_row ?? 1)).Value;
security.regional_ticks(), current_settings?.ticks_per_row ?? 1))
?? RegionalTickFactors.Default;
public double display_quantity_divisor { get; }
public LadderSettings current_settings { get; private set; }
@@ -829,7 +829,7 @@ namespace mbts.spark {
if (!price.is_real()) {
msg = "not a real number";
} else if (settlement.is_real()) {
var ticks_from_settle = security.ContractSpec.tickdiff(price, settlement);
var ticks_from_settle = security.tickdiff(price, settlement);
if (ticks_from_settle.HasValue) {
var max_cross = (current_settings?.ladder_order_limits?.max_settlement_cross_ticks)
.as_nullable();
@@ -861,7 +861,7 @@ namespace mbts.spark {
} else {
ulong _ = 0;
_model.md.get_bbo(ref _, out var bid, out var ask);
var mid = TickUtils.mid_tick(security.ContractSpec, bid.price, ask.price);
var mid = TickUtils.mid_tick(security.regional_ticks(), bid.price, ask.price);
_day_change.set(mid - md.settlement_price);
}
_volume.set(volume);
@@ -32,6 +32,8 @@ using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
partial class LadderGridHost : Control {
static readonly int _initial_row_count;
static readonly bool _show_scrollbar;
@@ -83,7 +85,6 @@ namespace mbts.spark {
bool _price_dirty = false, _piq_dirty = false;
LadderPriceList _price_list = new() {
length = _initial_row_count,
spec = Security.Defaults.ContractSpecificationImmutable
};
LadderPriceList _pending_price_list = null;
IComparer<double> _price_comparer;
@@ -386,16 +387,16 @@ namespace mbts.spark {
Column[] _columns { get; set; } = Array.Empty<Column>();
IEnumerable<Column> _visible_columns => _columns.Where(c => c.settings.show);
ContractSpecificationImmutable? _spec_backing;
ContractSpecificationImmutable _spec {
RegionalTickFactors _spec_backing;
RegionalTickFactors _spec {
get {
if (_spec_backing.HasValue) {
return _spec_backing.Value;
if (_spec_backing is not null) {
return _spec_backing;
} else if (_vm != null) {
_spec_backing = TickUtils.multiply_tick_size(security.ContractSpec, _settings?.ticks_per_row ?? 1);
return _spec_backing.Value;
_spec_backing = TickUtils.multiply_tick_size(security.regional_ticks(), _settings?.ticks_per_row ?? 1);
return _spec_backing;
}
return Security.Defaults.ContractSpecificationImmutable;
return RegionalTickFactors.Default;
}
}
@@ -449,6 +450,15 @@ namespace mbts.spark {
return _price_list.high_tick + tick_diff;
}
public bool scroll_to_tick(long tick) {
if (tick > int.MaxValue) {
Log.Ui.error(
$"Unable to scroll to tick {tick} because it exceeds int.MaxValue");
return false;
}
return scroll_to_tick((int)tick);
}
public bool scroll_to_tick(int tick) {
if (_vm is null || _settings is null || _scroll_viewer is null) {
return false;
@@ -462,21 +472,23 @@ namespace mbts.spark {
// The scrollbar's vertical offset won't change. Find the 'top tick' that will put the
// target `tick` in the center of the viewport.
var top_tick = (int)_get_target_top_tick(tick);
if (_settings.animate_recenter) {
var anim = new Int32Animation {
From = _price_list.high_tick,
To = top_tick,
Duration = TimeSpan.FromSeconds(0.15)
};
var sb = new Storyboard();
sb.Children.Add(anim);
Storyboard.SetTarget(anim, this);
Storyboard.SetTargetProperty(anim, new PropertyPath(TopTickProperty));
sb.Begin();
} else {
_set_scrollbar_range(high_tick: top_tick);
_refresh_prices();
checked {
var top_tick = (int)_get_target_top_tick(tick);
if (_settings.animate_recenter) {
var anim = new Int32Animation {
From = (int)_price_list.high_tick,
To = top_tick,
Duration = TimeSpan.FromSeconds(0.15)
};
var sb = new Storyboard();
sb.Children.Add(anim);
Storyboard.SetTarget(anim, this);
Storyboard.SetTargetProperty(anim, new PropertyPath(TopTickProperty));
sb.Begin();
} else {
_set_scrollbar_range(high_tick: top_tick);
_refresh_prices();
}
}
return true;
}
@@ -747,15 +759,11 @@ namespace mbts.spark {
_price_for_row(_row_for_point(point));
static double _size_from_row(
int row,
IList<double> price_list,
NativePriceSize[] depth,
ContractSpecificationImmutable spec) =>
int row, IList<double> price_list, NativePriceSize[] depth, RegionalTickFactors spec) =>
price_list is null || depth is null || row >= price_list.Count ?
double.NaN : _size_from_price(price_list[row], depth, spec);
static double _size_from_price(
double price, NativePriceSize[] depth, ContractSpecificationImmutable spec) {
static double _size_from_price(double price, NativePriceSize[] depth, RegionalTickFactors spec) {
if (!double.IsNaN(price) && depth.TryGetFirst(nps => spec.tick_equals(price, nps.price), out NativePriceSize result))
return result.size;
return double.NaN;
@@ -1105,10 +1113,16 @@ namespace mbts.spark {
Log.assert(_price_dirty);
_price_list = new LadderPriceList {
length = _initial_row_count,
spec = security.ContractSpec
security = security
};
var mid_tick = security.ContractSpec.tickdiff(_mkt_break_lines.origin, 0) ?? 0;
_set_scrollbar_range(high_tick: mid_tick + (_price_list.Count << 1));
var mid_tick = security.tickdiff(_mkt_break_lines.origin, 0) ?? 0;
if (mid_tick > int.MaxValue) {
Log.Ui.error(
$"Unable to set scrollbar range about mid_tick {mid_tick}: " +
$"tick count exceeds int.MaxValue");
} else {
_set_scrollbar_range(high_tick: (int)mid_tick + (_price_list.Count << 1));
}
_price_dirty = true;
}
if (_price_dirty) {
@@ -1187,12 +1201,12 @@ namespace mbts.spark {
_price_list, _vm.md.settlement_price,
_settings.net_change == LadderSettings.NetChangeTerms.Ticks,
_settings.row_height, _settings.price_column,
security.ContractSpec);
_spec);
} else {
LadderRender.render_prices(
_price_vis_tup.visual, _price_vis_tup.glyph_context,
_price_list, _settings.row_height,
_settings.price_column, security.ContractSpec,
_settings.price_column, _spec,
_settings.price_display_format);
}
}
@@ -1269,7 +1283,7 @@ namespace mbts.spark {
if (!_settings.piq_column.show)
return;
int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer);
LadderRender.render_piqs(_piq_vis_tup.visual, _last_vis_tup.glyph_context, origin_index, _price_list, _vm.price_vs_piq, _settings.row_height, _spec, _settings.piq_column, _fmt_mkt_quantity);
LadderRender.render_piqs(_piq_vis_tup.visual, _last_vis_tup.glyph_context, _price_list, _vm.price_vs_piq, _settings.row_height, _spec, _settings.piq_column, _fmt_mkt_quantity);
}
void _refresh_sizes() {
@@ -1347,7 +1361,7 @@ namespace mbts.spark {
return;
if (_price_list is null) {
_price_list = new LadderPriceList {
spec = security.ContractSpec
security = security
};
}
_price_list.reset(null, null, _initial_row_count);
@@ -1431,7 +1445,7 @@ namespace mbts.spark {
_pending_scroll_to_origin |= _settings == null;
_invalidate_spec();
if (_price_list != null) {
_price_list.spec = _spec;
_price_list.security = security;
}
_bid_vis_tup.glyph_context = glyph_context(s.bid_column);
_ask_vis_tup.glyph_context = glyph_context(s.ask_column);
@@ -1,9 +1,11 @@
using bts.api;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
class LadderPriceLineViewAdapter {
public static double bid_line(
double bid, double ask, double settlement, ContractSpecificationImmutable? spec) {
double bid, double ask, double settlement, RegionalTickFactors spec) {
return TickUtils.normalize_price(spec, impl());
double impl() {
if (!double.IsNaN(bid)) {
@@ -17,7 +19,7 @@ namespace mbts.spark {
}
public static double ask_line(
double bid, double ask, double settlement, ContractSpecificationImmutable? spec) {
double bid, double ask, double settlement, RegionalTickFactors spec) {
return TickUtils.normalize_price(spec, impl());
double impl() {
if (!double.IsNaN(ask)) {
@@ -31,7 +33,7 @@ namespace mbts.spark {
}
public static double mid_line(
double bid, double ask, double settlement, ContractSpecificationImmutable? spec) {
double bid, double ask, double settlement, RegionalTickFactors spec) {
return TickUtils.normalize_price(spec, impl());
double impl() {
if (double.IsNaN(bid)) {
@@ -43,16 +45,16 @@ namespace mbts.spark {
if (double.IsNaN(ask)) {
return bid;
}
if (spec.HasValue) {
return TickUtils.mid_tick(spec.Value, bid, ask);
if (spec is not null) {
return TickUtils.mid_tick(spec, bid, ask);
}
return bid;
}
}
static double _try_tick_down(ContractSpecificationImmutable? spec, double value) {
if (spec.HasValue) {
return spec.Value.tickdown(value);
static double _try_tick_down(RegionalTickFactors spec, double value) {
if (spec is not null) {
return spec.tickdown(value);
}
return value;
}
@@ -1,4 +1,5 @@
using bts.api;
using bts.utils;
using mbts.utils;
@@ -8,52 +9,78 @@ using System.Collections.Generic;
using System.Collections.Specialized;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
public class LadderPriceList : IReadOnlyList<double>, IList<double>, INotifyCollectionChanged, IEquatable<LadderPriceList> {
int _length;
int _high_tick;
const long _MAX_LENGTH = int.MaxValue;
long _length;
long _high_tick;
SecurityImmutable? _security;
RegionalTickFactors _regional_tick_backing;
RegionalTickFactors _regional_tick =>
_regional_tick_backing ??= TickUtils.get_regional_ticks(_security);
public LadderPriceList() { }
public LadderPriceList(LadderPriceList other) {
_length = other._length;
_high_tick = other._high_tick;
spec = other.spec;
_security = other._security;
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
public SecurityImmutable? security {
get => _security;
set {
if (value == _security) {
return;
}
_security = value;
_regional_tick_backing = null;
}
}
public ContractSpecificationImmutable spec { get; set; }
public int high_tick {
public long high_tick {
get => _high_tick;
set => _high_tick = value;
}
public int low_tick {
public long low_tick {
get => high_tick - length;
set => reset(null, value, null);
}
public double start {
get => spec.tickup(0, high_tick);
set => high_tick = spec.tickdiff(value, 0) ?? high_tick;
get => _regional_tick.tickup<double>(0, (uint)high_tick);
set => high_tick = _regional_tick.tickdiff(value, 0) ?? high_tick;
}
public int length {
public long length {
get => _length;
set {
if (value < 0) {
throw new ArgumentException($"Value must be >= 0 (actually {value})");
if (value < 0 || value > _MAX_LENGTH) {
throw new ArgumentException(
$"Value 'v' must be >= 0 and < {_MAX_LENGTH} (actually {value})");
}
_length = value;
}
}
public int Count => (int)length;
public int Count {
get {
checked {
return (int)length;
}
}
}
public bool IsReadOnly => false;
public double this[int idx] {
get => spec.tickup(0, high_tick - idx);
get => _regional_tick.tick<double>(0, high_tick - idx);
set => throw new NotSupportedException();
}
@@ -67,11 +94,13 @@ namespace mbts.spark {
GetEnumerator();
public int IndexOf(double item) {
var idx = spec.tickdiff(start, item) ?? -1;
var idx = _regional_tick.tickdiff(start, item) ?? -1;
if (idx < 0 || idx >= length) {
return -1;
}
return idx;
checked {
return (int)idx;
}
}
public void Insert(int index, double item) =>
@@ -138,15 +167,15 @@ namespace mbts.spark {
}
}
public void reset(int? high_tick = null, int? low_tick = null, int? length = null) {
public void reset(long? high_tick = null, long? low_tick = null, long? length = null) {
if (!high_tick.HasValue && !low_tick.HasValue && !length.HasValue) {
return;
}
if (length.HasValue && length.Value < 0) {
throw new ArgumentException("Must be >= 0", nameof(length));
}
int new_high_tick = 0;
int new_length = 0;
long new_high_tick = 0;
long new_length = 0;
if (high_tick.HasValue) {
new_high_tick = high_tick.Value;
if (low_tick.HasValue) {
@@ -180,8 +209,7 @@ namespace mbts.spark {
other != null &&
_high_tick == other._high_tick &&
_length == other._length &&
spec.LowTick.Equals(other.spec.LowTick) &&
spec.PriceBreak.Equals(other.spec.PriceBreak);
_regional_tick.safe_equals(other._regional_tick);
public override bool Equals(object obj) =>
Equals(obj as LadderPriceList);
@@ -189,13 +217,10 @@ namespace mbts.spark {
public override int GetHashCode() =>
HashCode.Combine(_high_tick, _length);
double _tick_to_price(int tick) =>
spec.tickup(0, tick);
double _tick_to_price(long tick) =>
_regional_tick.tick<double>(0, tick);
int? _price_to_tick(double price) =>
spec.tickdiff(price, 0);
void _add_tick(int tick) {
void _add_tick(long tick) {
int idx;
if (length == 0) {
_high_tick = tick;
@@ -204,7 +229,9 @@ namespace mbts.spark {
_high_tick = tick;
idx = 0;
} else if (tick == low_tick - 1) {
idx = length;
checked {
idx = (int)length;
}
} else {
throw new ArgumentException($"Must be == {nameof(high_tick)} ({high_tick}) + 1, or == {nameof(low_tick)} ({low_tick}) - 1)");
}
@@ -212,7 +239,7 @@ namespace mbts.spark {
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _tick_to_price(tick), idx));
}
void _remove_tick(int tick) {
void _remove_tick(long tick) {
if (length == 0) {
throw new InvalidOperationException("Cannot remove tick because length is 0");
}
@@ -221,7 +248,9 @@ namespace mbts.spark {
_high_tick--;
idx = 0;
} else if (tick == low_tick) {
idx = length - 1;
checked {
idx = (int)(length - 1);
}
} else {
throw new ArgumentException(
$"Can only remove {nameof(low_tick)} ({low_tick}) or " +
@@ -10,6 +10,7 @@ using mbts.ui.controls;
using mbts.ui.utility.text;
using Spark.Api.Orders;
using Spark.Core;
using Spark.Core.Disposables;
using System;
@@ -20,6 +21,8 @@ using System.Windows;
using System.Windows.Media;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
static class BadgeRender {
// IMPORTANT: the return values for this method MUST BE IN SYNC with the "format" method, else "measure_height" and "LadderRender.measure_row_height" will be broken.
static int _line_count_for_style(LadderSettings.BadgeStyle badge_style) => badge_style == LadderSettings.BadgeStyle.Alternate ? 2 : 1;
@@ -74,6 +77,16 @@ namespace mbts.spark {
}
public static int index_of(IList<double> prices, double price, IComparer<double> comparer, bool relative_idx) {
for (int i = 1; i < prices.Count; i++) {
Log.assert(prices[i - 1] > prices[i]);
var c = comparer.Compare(prices[i - 1], prices[i]);
if (c >= 0) {
c = comparer.Compare(prices[i - 1], prices[i]);
} else {
c = comparer.Compare(prices[i - 1], prices[i]);
}
Log.assert(c < 0);
}
var idx = CollectionHelper.binary_search(prices, price, comparer);
if (relative_idx && idx < 0) {
idx = ~idx;
@@ -96,30 +109,13 @@ namespace mbts.spark {
settings.font_size, settings.width, row_height, HorizontalAlignment.Right,
-4, VerticalAlignment.Center, row * row_height, true);
public static IList<double> gen_prices(double mid, int row_count, ContractSpecificationImmutable spec) {
bool is_odd = row_count % 2 == 1;
var result = new double[row_count];
int range = row_count / 2;
int middle = row_count / 2;
result[middle] = mid;
double up = mid, down = mid;
// Starting from the middle iterate out to the edges
for (int x = 1; x <= range; ++x) {
up = spec.tickup(up);
down = spec.tickdown(down);
result[middle - x] = up;
result[middle + x] = down;
}
return result;
}
public static GlyphRun[] format_prices(IList<double> prices, double reference_price, bool format_in_tick_terms, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, ContractSpecificationImmutable spec) {
public static GlyphRun[] format_prices(IList<double> prices, double reference_price, bool format_in_tick_terms, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, RegionalTickFactors spec) {
var result = new GlyphRun[prices.Count];
Parallel.For(0, prices.Count, i =>
result[i] = format_text(spec.format(prices[i] - reference_price, format_in_tick_terms), i, row_height, settings, glyph_context));
return result;
}
public static GlyphRun[] format_prices(IList<double> prices, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, ContractSpecificationImmutable spec, LadderSettings.PriceDisplayFormat price_display_format) {
public static GlyphRun[] format_prices(IList<double> prices, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, RegionalTickFactors spec, LadderSettings.PriceDisplayFormat price_display_format) {
var result = new GlyphRun[prices.Count];
Parallel.For(0, prices.Count, i =>
result[i] = format_text(spec.format(prices[i]), i, row_height, settings, glyph_context, price_display_format));
@@ -144,12 +140,12 @@ namespace mbts.spark {
bool format_in_tick_terms,
double row_height,
LadderColumnSettings settings,
ContractSpecificationImmutable spec) {
RegionalTickFactors tick) {
if (price_list.IsNullOrEmpty()) {
return;
}
render_prices(
price, glyph_context, price_list, row_height, settings, spec,
price, glyph_context, price_list, row_height, settings, tick,
wrt_price, format_in_tick_terms);
}
@@ -159,12 +155,12 @@ namespace mbts.spark {
IList<double> dprices,
double row_height,
LadderColumnSettings settings,
ContractSpecificationImmutable spec,
RegionalTickFactors tick,
double wrt_price,
bool format_in_tick_terms) {
var foreground = settings.foreground.rgb_brush();
//var background = new SolidColorBrush(settings.background.rgb_color());
var fprices = format_prices(dprices, wrt_price, format_in_tick_terms, row_height, settings, glyph_context, spec);
var fprices = format_prices(dprices, wrt_price, format_in_tick_terms, row_height, settings, glyph_context, tick);
using (var dc = price.RenderOpen())
using (_draw_with_clip(dc, settings, row_height, dprices.Count)) {
//dc.DrawRectangle(background, null, new Rect(0, 0, settings.width, row_count * row_height));
@@ -179,11 +175,11 @@ namespace mbts.spark {
IList<double> dprices,
double row_height,
LadderColumnSettings settings,
ContractSpecificationImmutable spec,
RegionalTickFactors tick,
LadderSettings.PriceDisplayFormat price_display_format) {
var foreground = settings.foreground.rgb_brush();
//var background = new SolidColorBrush(settings.background.rgb_color());
var fprices = format_prices(dprices, row_height, settings, glyph_context, spec, price_display_format);
var fprices = format_prices(dprices, row_height, settings, glyph_context, tick, price_display_format);
using (var dc = price.RenderOpen())
using (_draw_with_clip(dc, settings, row_height, dprices.Count)) {
//dc.DrawRectangle(background, null, new Rect(0, 0, settings.width, row_count * row_height));
@@ -216,7 +212,7 @@ namespace mbts.spark {
IList<double> prices,
NativePriceSize[] rungs,
double row_height,
ContractSpecificationImmutable spec,
RegionalTickFactors spec,
LadderColumnSettings settings,
Func<NativePriceSize, string> fmt,
bool lighten_market_cells,
@@ -249,17 +245,18 @@ namespace mbts.spark {
direct_size: aggr.direct_size + rung.direct_size,
implied_size: aggr.implied_size + rung.implied_size),
rung => {
//spec.round_to_tick(rung.price);
var ticks = spec.full_ticks_from_zero(rung.price);
var ticks = spec.count_ticks(rung.price);
return ticks.HasValue ? spec.tickup(0, ticks.Value) : rung.price;
});
foreach (var rung in row_tick_groups) {
//double price = spec.round_to_tick(rung.price);
var ticks = spec.full_ticks_from_zero(rung.price);
var ticks = spec.count_ticks(rung.price);
var price = ticks.HasValue ? spec.tickup(0, ticks.Value) : rung.price;
// prices are in descending order, so get diff from curr_price - price
int tick_diff = spec.tickdiff_unchecked(curr_price, price);
curr_index += tick_diff;
var tick_diff = spec.tickdiff(curr_price, price).assert_value();
checked {
curr_index += (int)tick_diff;
}
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Count) {
if (market_cell_background_brush != null)
@@ -279,7 +276,7 @@ namespace mbts.spark {
IList<double> prices,
IEnumerable<VolumeAtPriceLevelImmutable> data,
double row_height,
ContractSpecificationImmutable spec,
RegionalTickFactors spec,
VolumeColumnSettings settings,
int last_index,
string last_size_and_direction,
@@ -371,23 +368,27 @@ namespace mbts.spark {
public delegate void RenderCell<T>(DrawingContext dc, int row, T data_item);
static void _render_cells<T>(DrawingVisual dv, double row_height, IEnumerable<(double price, T value)> data, LadderColumnSettings settings, IList<double> prices, ContractSpecificationImmutable spec, RenderCell<T> render_cell) {
static void _render_cells<T>(DrawingVisual dv, double row_height, IEnumerable<(double price, T value)> cells, LadderColumnSettings settings, IList<double> prices, RegionalTickFactors spec, RenderCell<T> render_cell) {
using (var dc = dv.RenderOpen())
using (_draw_with_clip(dc, settings, row_height, prices.Count)) {
if (data.is_null_or_empty())
if (prices.is_null_or_empty() || cells.is_null_or_empty()) {
return;
int curr_index = 0;
double curr_price = prices[curr_index];
foreach (var (price, value) in data) {
if (double.IsNaN(price))
continue;
// prices are in descending order, so get diff from curr_price - price
int tick_diff = spec.tickdiff(curr_price, price) ?? 0;
curr_index += tick_diff;
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Count) {
render_cell(dc, curr_index, value);
} else break;
}
var min_price = prices[prices.Count - 1];
var max_price = prices[0];
Log.assert(min_price.is_real() && max_price.is_real() && min_price <= max_price);
var indexed_cells = cells
.Where(item => item.price.is_real() && item.price.is_within_range(min_price, max_price))
.Select(row => {
checked {
return (row.price, row.value, (int)spec.tickdiff(max_price, row.price));
}
});
foreach (var (price, value, idx) in indexed_cells) {
var check_price = spec.tickdown(max_price, idx);
Log.assert(spec.tick_equals(price, check_price));
Log.assert(idx >= 0 && idx < prices.Count);
render_cell(dc, idx, value);
}
}
}
@@ -411,35 +412,18 @@ namespace mbts.spark {
internal static void render_piqs(
DrawingVisual dv,
GlyphContext glyph_context,
int origin_index,
IList<double> prices,
IEnumerable<(double price, uint piq)> pvp,
double row_height,
ContractSpecificationImmutable spec,
RegionalTickFactors spec,
LadderColumnSettings settings,
Func<double, string> fmt_value) {
//
using (var dc = dv.RenderOpen()) {
using (_draw_with_clip(dc, settings, row_height, prices.Count)) {
if (!prices.within_range(origin_index) || pvp.is_null_or_empty())
return;
fmt_value ??= FormattingDelegates.default_string<double>();
var foreground = settings.foreground.rgb_brush();
int curr_index = origin_index;
double curr_price = prices[curr_index];
foreach (var entry in pvp) {
double price = entry.price;
// prices are in descending order, so get diff from curr_price - price
int tick_diff = spec.tickdiff_unchecked(curr_price, price);
curr_index += tick_diff;
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Count) {
var gr = format_text(fmt_value(entry.piq), curr_index, row_height, settings, glyph_context);
dc.DrawGlyphRun(foreground, gr);
} else break;
}
}
}
Func<double, string> fmt_value
) {
fmt_value ??= FormattingDelegates.default_string<double>();
var fg = settings.foreground.rgb_brush();
_render_cells(dv, row_height, pvp, settings, prices, spec, (dc, row, piq) =>
dc.DrawGlyphRun(
fg, format_text(fmt_value(piq), row, row_height, settings, glyph_context)));
}
public static void render_mkt_break(DrawingVisual dv, int index, double row_height, uint color) =>
@@ -59,7 +59,9 @@ namespace bts.shell.manual_fills {
nameof(quantity_display),
() => {
var sec_meta = security.HasValue ? _sec_meta_store.get(security.Value) : null;
return sec_meta?.QuantityDisplay ?? Security.Defaults.quantity_display_settingsi;
return sec_meta?.HasQuantityDisplay ?? false ?
sec_meta.Value.QuantityDisplay :
Security.Defaults.quantity_display_settingsi;
});
// TODO (prob): remove conversion_data. Replace get_qty_display_divisor delegates w/
// TickDisplaySettingsImmutable for qty, obtained via ISecurityMetadataStore.
@@ -31,10 +31,12 @@
ItemsSource="{Binding rows_view}"
MinRowHeight="17"
RowHeaderWidth="22"
Background="{u:BTSColor ManualFills.background}"
>
<!--Row Header Style-->
<DataGrid.RowHeaderStyle>
<Style TargetType="DataGridRowHeader">
<Setter Property="Background" Value="transparent"/>
<Setter Property="Content" Value="{Binding}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
@@ -123,8 +125,12 @@
<!--Row Style-->
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{u:BTSColor Grid.default_row_background}"/>
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<Trigger Property="DataGridRow.AlternationIndex" Value="1">
<Setter Property="Background" Value="{u:BTSColor Grid.alternate_row_background}"/>
</Trigger>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource SelectedRowBackground}"/>
</DataTrigger>
@@ -73,7 +73,7 @@ namespace Spark.Trading {
meta.Value.Value.PriceDisplay : Security.Defaults.price_display_settingsi;
if (security.HasValue) {
rv = rv.with(
tick_factors: security.spec_or_default().tick_factors().to_api_tick_factors());
tick_factors: security.Value.regional_ticks().low_tick.to_api_tick_factors());
}
return rv;
}
@@ -779,7 +779,7 @@ namespace mbts.spark {
return;
}
var price_display = row.contract_spec?.tick_factors()?.to_api_tick_display_settingsi();
var price_display = TickUtils.get_tick(row.security, price ?? 0).to_api_tick_display_settingsi();
var update_desc = OrderUtils.update_description(
row.order, price, quantity, price_display, row.quantity_display_settings);
Log.Ui.info(
@@ -185,8 +185,7 @@ namespace mbts.spark {
using var _ = detect_property_changes(
nameof(spec),
nameof(display_quantity_divisor),
nameof(quantity_display),
nameof(unit_quantity));
nameof(quantity_display));
_security_md = value;
bbo.security_md = value;
_payup_bid_cmd.RaiseCanExecuteChanged();
@@ -216,9 +215,6 @@ namespace mbts.spark {
}
}
public double unit_quantity =>
(security?.ContractSpec)?.tick_factors()?.tick_unit_frac() ?? 1;
public AlgoDefinitionVM algo_vm { get; }
public IReadOnlyObservableCollection<AlgoParamRowVM> param_rows { get; }
public BBOVM bbo { get; }
@@ -507,7 +507,7 @@ namespace bts.shell.pnl {
static class PNLDataExtensions {
public static string pnl_symbol(this SecurityImmutable security) {
return bts.api.ExchangeSpecificUitlities.get_pnl_symbol(security);
return bts.api.ExchangeSpecificUtilities.get_pnl_symbol(security);
}
// while override_cpp could use double.NaN to indicate the lack of an override,
@@ -519,7 +519,7 @@ namespace bts.shell.pnl {
public static (double default_cpp, double? override_cpp) pnl_cpp_tuple(this PNLUnitImmutable pnl_unit, SecurityImmutable security) {
// TODO: actual impl
double calced_pnl = bts.api.ExchangeSpecificUitlities.get_cpp(pnl_unit, security);
double calced_pnl = bts.api.ExchangeSpecificUtilities.get_cpp(pnl_unit, security);
return (calced_pnl, pnl_unit.HasCppOverride ? pnl_unit.CppOverride.Value : (double?)null);
}
}
@@ -233,9 +233,12 @@ namespace BCG {
private IDictionary<DateTime, ATMData> _get_atm_data(ExpStrikeKeyedData<mbts.spark.TheoPoint> theo_data) {
NMSheetSettings sheet_settings = _threaded_bb.read_state(_in_bb_sheet_settings_tag) ?? new NMSheetSettings();
VolSurfaceNonVolInput vs_nvinp = _threaded_bb.read_state(_in_exp_param_tag);
IDictionary<DateTime, double> option_exp_roll_data = vs_nvinp != null ?
vs_nvinp.get_roll_map((exp) => exp.symbol == _nmsheet_symbol.option_name && exp.security_type == SecurityType.Option).ToDictionary(kv => kv.Key.expiration, kv => kv.Value) :
new Dictionary<DateTime, double>();
var option_exp_roll_data = vs_nvinp
?.get_roll_map(exp =>
exp.symbol == _nmsheet_symbol.option_name &&
exp.security_type == SecurityType.Option)
?.ToDictionary(kv => kv.Key.expiration, kv => kv.Value)
?? new();
IDictionary<DateTime, ATMData> atm_data = OptionATMDataSource.compute_atm_data(_nmsheet_symbol.option_name, _get_onefive(), theo_data, option_exp_roll_data);
if (sheet_settings.setting_atm.select_atm_nearest_strike_only)
foreach (ATMData exp_atm_data in atm_data.Values)
@@ -306,4 +309,4 @@ namespace BCG {
}
#endregion
}
}
}
@@ -41,5 +41,10 @@ namespace BCG {
public static Size get_ask_size(BBOPacket md) {
return md != null ? md._ask_size : (Size)0;
}
public static Tuple<double, Size> get_md(BBOPacket md, bool is_bid) {
double price = is_bid ? BBOPacket.get_bid_price(md) : BBOPacket.get_ask_price(md);
Size sz = is_bid ? BBOPacket.get_bid_size(md) : BBOPacket.get_ask_size(md);
return Tuple.Create(price, sz);
}
}
}
@@ -86,7 +86,7 @@ namespace BCG {
// return _fut_map.Select(e => Tuple.Create(e.Key.exchange, e.Key.symbol, e.Key.expiration, e.Key));
// }
public IEnumerable<Tuple<Venue, string, uint>> get_stocks() { return new List<Tuple<Venue, string, uint>>(); }
public IEnumerable<Tuple<Venue, string, uint>> get_spots() { return new List<Tuple<Venue, string, uint>>(); }
public Security get_o2_security(uint sec_key) {
Security rv = _key_sec_map.TryGetValue(sec_key, out rv) ? rv : null;
@@ -123,6 +123,15 @@ namespace BCG {
#endregion
#region Orders
[System.ComponentModel.Category("Orders / Algo")]
[System.ComponentModel.DisplayName("Default Account")]
public string Account { get; set; }
[System.ComponentModel.Category("Orders / Algo")]
[System.ComponentModel.DisplayName("Default Qty")]
public Nullable<double> DefaultQty { get; set; }
#endregion
public NMSheetSettingLayout() {
show_settings_header = false;
show_exp_navigator = false;
@@ -18,6 +18,7 @@ using mbts.spark.filters;
using System.Collections;
using System.Reactive.Linq;
using System.Threading;
using bts.api.orderman;
namespace BCG {
public partial class NMSheetControl : UserControl {
@@ -555,6 +556,17 @@ namespace BCG {
}
private void _launch_order_entry_window(NMSheetTheoGrid.GridCell cell,
OrderEntryWindow window_type) {
Func<string, bts.AccountTuple> get_account = (string default_acc) => {
string account = _settings.setting_layout.Account;
if (String.IsNullOrEmpty(account)) {
account = default_acc;
}
Nullable<uint> id = _get_account_id(account);
if (!id.HasValue) {
id = 0;
}
return new bts.AccountTuple(id.Value, account);
};
uint key;
var found = _sheet_data.get_security_key(cell.expiration,
cell.strike, cell.call_selected, out key);
@@ -566,7 +578,19 @@ namespace BCG {
if (sc_data.sec_md_map.TryGetValue(key, out var smd)) {
switch (window_type) {
case OrderEntryWindow.Ticket:
_threaded_bb.post(SparkTags.ShowOrderTicketFromSecurityTag, smd);
bool is_bid = NMSheetSettingColumnOrganization.get_column_info(cell.col).is_bid;
bts.AccountTuple acc_tup = get_account("SMALL");
Nullable<double> qty = _settings.setting_layout.DefaultQty;
OrderDefinition od = new OrderDefinition() {
SecurityKey = smd.id,
TimeInForce = TimeInForce.Gtc,
Account = acc_tup.name,
AccountId = acc_tup.id
};
BBOPacket bbo = _sheet_data.get_bbo(smd.id);
var pr_sz = BBOPacket.get_md(bbo, is_bid);
_threaded_bb.post(SparkTags.ShowOrderTicketTag,
new(new(od), pr_sz.Item1, qty ?? pr_sz.Item2));
break;
case OrderEntryWindow.Ladder:
_threaded_bb.post(SparkTags.ShowLadderTag, smd);
@@ -133,7 +133,7 @@ namespace bts.shell {
void _on_orders(TableUpdate<OrderRecordTuple, uint> update) {
// must update notification_service_data_cache first, some of the predicate/triggers (esp. ToastNotifications) will use the notification_service_data_cache to lookup order-history
_data_cache.update(update.as_iincremental());
_data_cache.update(update);
foreach (var o in update.added.Concat(update.changed))
_order_notifications.process_item(o);
}
@@ -1,4 +1,5 @@
using bts.api;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using mbts.spark;
@@ -8,6 +9,7 @@ using Spark.Api.Orders;
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.shell {
// Any predicate-sound/notifications that need to reference previous data (i.e. notifications based on change/time, or state changes) should use the INotificationServiceDataCache.
@@ -17,7 +19,6 @@ namespace bts.shell {
// --------------------------------------------------------------------------------------------------------------------------------------------
// IMPOARTANT!!! The current implementation of this interface is NOT INTENDED FOR GENERAL USE - order_history is NOT THREAD-SAFE
interface INotificationServiceDataCache {
DateTime last_order_reset_time { get; }
bool is_first_order_update { get; }
IReadOnlyDictionary<uint, List<TimeStampedData<OrderRecordTuple>>> order_history { get; }
}
@@ -45,45 +46,113 @@ namespace bts.shell {
partial class NotificationService : INotificationService, IDisposable {
class DataCache : INotificationServiceDataCache {
const int _buffer_size = 10;
const int _ORDER_HISTORY_BUFFER_SIZE = 10;
const int _TABLE_HISTORY_BUFFER_SIZE = 5;
readonly Dictionary<uint, OrderRecordTuple> _order_cache = new();
readonly Dictionary<uint, List<TimeStampedData<OrderRecordTuple>>> _order_history = new();
readonly List<(TableUpdate<OrderRecordTuple, uint>, DateTime)> _table_history = new(_TABLE_HISTORY_BUFFER_SIZE);
readonly object _lock = new();
public IReadOnlyDictionary<uint, List<TimeStampedData<OrderRecordTuple>>> order_history => _order_history;
public DateTime last_order_reset_time { get; private set; }
public bool is_first_order_update { get; private set; }
internal void update(IIncrementalUpdate<OrderRecordTuple, uint> data) {
internal void update(TableUpdate<OrderRecordTuple, uint> table) {
// First, try processing the update per usual (incrementally). If it throws,
// then log the exception and reset the state.
DateTime now = DateTime.Now;
if (data.removed.Count == _order_cache.Count) {
last_order_reset_time = now;
_order_cache.Clear();
lock (_lock) {
_circular_buffer_add(_table_history, (table, now), _TABLE_HISTORY_BUFFER_SIZE);
try {
impl();
return;
} catch (Exception ex) {
var table_history_desc = _fmt_order_table_history_safe();
Log.Ui.error(
$"Error processing order update in NotificationService: {ex}.\n" +
$"***Last table updates below***\n" +
table_history_desc);
}
// This is the 'escape' path. It really shouldn't throw.
reset();
}
is_first_order_update = _order_cache.Count == 0 && data.added.Count > 0;
foreach (var kv in data.removed) {
if (data.added.ContainsKey(kv.Key)) {
_order_cache[kv.Key] = kv.Value;
_order_history[kv.Key].Add(new TimeStampedData<OrderRecordTuple>(kv.Value, now));
} else {
_order_cache.Remove(kv.Key);
// TODO: maybe these shouldn't be removed?
// if we add delta/time predicates that need to refer to order history, we might need to keep them around
// although, if the server is telling us to remove them, perhaps we shouldn't consider such data.
_order_history.Remove(kv.Key);
void impl() {
if (table.state.Count == 0) {
_order_cache.Clear();
}
is_first_order_update = _order_cache.Count == 0 && table.state.Count > 0;
foreach (var order in table.removed) {
var k = table.key(order);
_order_cache.Remove(k);
_order_history.Remove(k);
}
foreach (var ch in table.changes) {
var k = table.key(ch.New);
_order_cache[k] = ch.New;
_order_history[k].Add(new(ch.New, now));
}
foreach (var order in table.added) {
var k = table.key(order);
_order_cache[k] = order;
var hist = _order_history.GetOrAdd(k, _ => new());
_circular_buffer_add(hist, new(order, now), _ORDER_HISTORY_BUFFER_SIZE);
}
}
foreach (var kv in data.added) {
if (data.removed.ContainsKey(kv.Key))
continue;
_order_cache.Add(kv.Key, kv.Value);
_order_history.AddOrUpdate(kv.Key,
k => new List<TimeStampedData<OrderRecordTuple>> { new(kv.Value, now) },
(k, l) => {
l.Add(new TimeStampedData<OrderRecordTuple>(kv.Value, now));
if (l.Count >= 10)
l.RemoveAt(0);
});
void reset() {
_order_cache.Clear();
_order_history.Clear();
_table_history.Clear();
foreach (var (k, order) in table.state) {
_order_cache[k] = order;
_order_history.GetOrAdd(k, _ => new()).Add(new(order, now));
}
is_first_order_update = true;
}
}
static void _circular_buffer_add<T>(List<T> list, T item, int buffer_size) {
Log.assert(buffer_size > 0);
if (list.Count > buffer_size - 1) {
// Remove oldest items (trim front)
list.RemoveRange(
0, list.Count - (buffer_size - 1));
}
list.Add(item);
}
string _fmt_order_table_history_safe() {
return _fmt_table_history_safe(_table_history, fmt_order_tup);
static string fmt_order_tup(OrderRecordTuple order_tup) {
var sec = order_tup.security;
var order = order_tup.order;
var order_desc = OrderUtils.short_description(order, sec.spec_or_default());
var sec_desc = sec?.ToString() ?? ("sid=" + order_tup.security_id.ToString());
return $"{sec_desc} {order_desc}";
}
}
static string _fmt_table_history_safe<T, TKey>(
List<(TableUpdate<T, TKey>, DateTime)> table_history, Func<T, string> fmt_value) where TKey : IEquatable<TKey> {
var table_history_desc = new System.Text.StringBuilder();
for (int i = 0; i < table_history.Count; i++) {
var (table, time_stamp) = table_history[i];
table_history_desc.AppendLine(
$"Update - {table_history.Count - i}, at {time_stamp:yyyy-MM-dd hh:mm:ss.ffffff}");
table_history_desc.AppendLine(_fmt_table_safe(table, fmt_value));
}
return table_history_desc.ToString();
}
static string _fmt_table_safe<T, TKey>(
TableUpdate<T, TKey> table, Func<T, string> fmt_value) where TKey : IEquatable<TKey> {
try {
return table.description(fmt_value);
} catch (Exception ex) {
return $"Exception while formatting TableUpdate: ${ex}";
}
}
}
@@ -14,6 +14,8 @@ using System.Reactive.Linq;
using System.Windows.Input;
namespace mbts.spark {
using RegionalTickFactors = user.settings.RegionalTickFactors;
class InlineOrderTicketProperties {
public readonly InlineUserProperty<double> price;
public readonly InlineUserProperty<double> size;
@@ -128,6 +130,7 @@ namespace mbts.spark {
public SecurityImmutable security => _security.Value;
public double price { get; set; }
public RegionalTickFactors price_tick => security.regional_ticks();
public CalculatorLogic<double> size_calculator { get; }
public TickDisplaySettingsImmutable qty_display {
@@ -225,10 +225,7 @@ namespace bts.spark {
var security = try_get_security(order.Def.SecurityKey);
var change_descs = new List<string>(1);
TickDisplaySettingsImmutable? price_display =
security.HasValue && security.Value.HasContractSpec ?
security.Value.ContractSpec.tick_factors().to_api_tick_display_settingsi() : null;
var price_display = TickUtils.get_tick(security, price).to_api_tick_display_settingsi();
var price_qty_change_desc = OrdersExtensions.update_description(
order, price, size, price_display);
@@ -74,7 +74,7 @@
<usp:PriceEntryControl
Grid.ColumnSpan="2"
usp:OrderModificationControl.IsDirty="{Binding price_dirty, FallbackValue={x:Null}}"
ContractSpec="{Binding security.ContractSpec}"
RegionalTick="{Binding price_tick}"
Focusable="false"
TickDefaultValue="{Binding current_price, Converter={StaticResource TickDefaultPriceConverter}, FallbackValue={u:Double 0}}"
Value="{Binding price}"
+4 -4
View File
@@ -127,9 +127,9 @@
<TextBlock.Inlines>
<Run FontWeight="Bold" Text="{Binding security, Mode=OneWay}" />
<Run Foreground="{u:BTSColor MarketData.bid}" FontSize="10">
<MultiBinding Converter="{x:Static cv:DynamicDoubleDivisorConverter.Instance}" Mode="OneWay" StringFormat="({0})">
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}" StringFormat="({0})">
<Binding Path="bid_size" Mode="OneWay"/>
<Binding Path="DataContext.display_quantity_divisor" RelativeSource="{RelativeSource AncestorType=usp:OrderTicket}"/>
<Binding Path="DataContext.qty_display" RelativeSource="{RelativeSource AncestorType=usp:OrderTicket}"/>
</MultiBinding>
</Run>
<Run Foreground="{u:BTSColor MarketData.bid}" FontWeight="Bold">
@@ -149,9 +149,9 @@
</Run.Text>
</Run>
<Run Foreground="{u:BTSColor MarketData.ask}" FontSize="10">
<MultiBinding Converter="{x:Static cv:DynamicDoubleDivisorConverter.Instance}" Mode="OneWay" StringFormat="({0})">
<MultiBinding Converter="{x:Static cv:TickDisplayConverter.Instance}" StringFormat="({0})">
<Binding Path="ask_size" Mode="OneWay"/>
<Binding Path="DataContext.display_quantity_divisor" RelativeSource="{RelativeSource AncestorType=usp:OrderTicket}"/>
<Binding Path="DataContext.qty_display" RelativeSource="{RelativeSource AncestorType=usp:OrderTicket}"/>
</MultiBinding>
</Run>
</TextBlock.Inlines>
@@ -91,7 +91,8 @@ namespace mbts {
}
var sec = order.security;
var sec_meta = sec.HasValue ? _sec_meta_store.get(sec.Value) : null;
var qty_display = sec_meta?.QuantityDisplay ??
var qty_display = sec_meta?.HasQuantityDisplay ?? false ?
sec_meta.Value.QuantityDisplay :
Security.Defaults.quantity_display_settingsi;
return ToastNotifications.order_completed(order, qty_display);
}
@@ -122,7 +122,7 @@ namespace mbts.analytics {
timer.stop();
Log.Ui.info($"PNLReportCalculator n_pos: {output.position_data.Count()} " +
$"n_day_trades: {output.day_trades.Count()} time(msecs): {timer.lastIntervalTime}");
_log_before_posting(output.position_data);
//_log_before_posting(output.position_data);
_bus.post(SparkTags.ReportBlackboardTags.PNL.PNLData, output);
});
}
@@ -116,21 +116,21 @@ namespace mbts.analytics {
}
private void _handle_nonvol_data(VolSurfaceNonVolData vs_nonvol_data) {
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_nonvol_data report {0}",
_report.ReportName));
// bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_nonvol_data report {0}",
// _report.ReportName));
_vs_nonvol_data = vs_nonvol_data;
}
private void _handle_future_prices(
IReadOnlyDictionary<SecurityExpiration, FutureTheoStructure> future_prices_map) {
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_future_prices report {0}",
_report.ReportName));
// bts.Log.Gen.Diag(String.Format("SlideReportCalculator._handle_future_prices report {0}",
// _report.ReportName));
_future_prices_map = future_prices_map.ToDictionary(kv => kv.Key, kv => kv.Value.theo);
}
private void _handle_trades(IEnumerable<ITradeLogRow> trades) {
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_trades report {0}",
_report.ReportName));
// bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_trades report {0}",
// _report.ReportName));
_all_trades = trades.Where(t => t.is_active);
if (_report.CalcMode == Report.Types.ReportCalculationMode.PositionOrTheoChange) {
_compute_dirty = true;
@@ -555,7 +555,7 @@ namespace mbts.analytics {
var rv = new HashSet<bts.api.SecurityTuple>();
foreach (var c in sc.Clauses)
foreach (var s in c.Symbols)
foreach (var e in c.Exchanges)
foreach (var e in c.Venues)
foreach (var st in c.SecurityTypes) {
var st_i = new bts.api.SecurityTuple();
st_i.Symbol = s;
@@ -568,4 +568,4 @@ namespace mbts.analytics {
#endregion
}
}
}
@@ -55,7 +55,7 @@ namespace mbts {
if (!is_unexpired(l.Security))
return false;
return true;
case bts.api.SecurityType.Stock:
case bts.api.SecurityType.Spot:
return true;
case bts.api.SecurityType.Synthetic:
foreach (var l in sec.Synthetic.Legs)
@@ -68,4 +68,4 @@ namespace mbts {
return true;
}
}
}
}
+7 -2
View File
@@ -19,9 +19,14 @@
class ShellBootstrapper : BTSApplicationBootstrapper {
Window _login;
public ShellBootstrapper(string user = null, string password = null, bool? restore_workspace = null) {
public ShellBootstrapper(
string user = null,
string password = null,
string config_source = null,
bool? restore_workspace = null
) : base(config_source) {
// Get the LoginModel instance on this thread to make sure it performs syncronous config initialization.
var login_model = LoginModel.instance;
var login_model = LoginModel.init(config_source);
// Show the dialog on a background thread so animations aren't blocked during the main application startup
var thread = new Thread(thread_start) {
IsBackground = true,
@@ -0,0 +1,59 @@
#nullable enable
namespace Spark.Cli {
using CommandLine;
using System.Collections.Generic;
class SparkCmdOpts {
const string _SPARKD_OPT_NAME = "sparkd";
[Option("multiple", HelpText = "Allow multple shells to run simultaneously")]
public bool allow_multiple_shells { get; set; }
[Option("stunnel", HelpText = "Use stunnel")]
public bool stunnel { get; set; }
[Option("no-stunnel", HelpText = "Don't use stunnel")]
public bool no_stunnel {
get => !stunnel;
set => stunnel = !value;
}
[Option("user", MetaValue = "USER", HelpText = "The user to login with")]
public string? user { get; set; }
[Option("password", MetaValue = "PASS", HelpText = "The password to login with")]
public string? password { get; set; }
[Option(
"restore-workspace",
HelpText = "Whether or not to restore the user's last workspace",
Default = true)]
public bool restore_workspace { get; set; }
[Option(
"no-restore-workspace",
HelpText = "Whether or not to restore the user's last workspace")]
public bool no_restore_workspace {
get => !restore_workspace;
set => restore_workspace = !value;
}
[Option(_SPARKD_OPT_NAME, MetaValue = "URL", HelpText = "The config server url")]
public string? sparkd { get; set; }
/// <summary>
/// Conform opts to modern cli for backwards compatibility
/// </summary>
public static IEnumerable<string> conform_opts(IEnumerable<string> opts) {
using var en = opts.GetEnumerator();
while (en.MoveNext()) {
if (en.Current.ToLower() == "-configserver") {
yield return $"--{_SPARKD_OPT_NAME}";
continue;
}
yield return en.Current;
}
}
}
}
@@ -9,7 +9,7 @@ using MathNet.Numerics.Distributions;
namespace BCG {
public class ContractUtilities {
public static bool is_strock(string sec_type) {
return String.Compare(sec_type, SecurityType.Stock.ToString(), true) == 0;
return String.Compare(sec_type, SecurityType.Spot.ToString(), true) == 0;
// return String.Compare(sec_type, SecurityType.Stock.ToString(), true) == 0 ||
// String.Compare(sec_type, SecurityType.Equity.ToString(), true) == 0 ||
// String.Compare(sec_type, SecurityType.ETF.ToString(), true) == 0 ||
@@ -18,8 +18,8 @@ namespace BCG {
}
//also see SecurityType.cs extension method
public static SecurityType default_aw_stock_type {
get { return SecurityType.Stock; }
public static SecurityType default_aw_spot_type {
get { return SecurityType.Spot; }
}
public static string case_correct_symbol(string sym_type, SecurityType default_type = SecurityType.Future) {
Loaded 100 of 1176 files, more files were not shown because too many files have changed in this diff. Show more