Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb9a897dcf | ||
|
|
5bc1ed9a56 | ||
|
|
91adb1df9f |
No files matched your search
@@ -1 +1,717 @@
|
||||
+++
title = "Release notes"
weight = 3
sort_by = "weight"
insert_anchor_links = "right"
+++
## 2.16.2
### User Visible Change
- Synthetic creation dialog has a "fractional" checkbox next to the tick settings. Users may uncheck this box to enter integer tick sizes, so the synthetic security's prices will be displayed without decimals, or fractional units.
## 2.16.1
### Bug Fixes
- OrderBook's algo state parameters (in the panel on the right side) are sorted alphabetically
## 2.16.0
### Bug Fixes
- Solved an issue that could result in incorrect ladder levels
- Solved an issue that could lead to incorrect routing of orders in the event of certain MSGW changes
### API Changes
- Add C# client side API samples for order entry, position monitoring and market data observation
## 2.15.10
### Bug Fixes
- Solved an issue that could cause the user interface to fail when playing some alert sounds
### API Changes
- Added settlement and position information to the API
- Added the ability for user algos to report custom state to the user interface
- Added the ability for algos to discover all active securities
## 2.15.3
### Bug Fixes
- Fixed a bug where some CME currency spreads exhibited incorrect tick values
## 2.15.2
### User Visible Changes
- Excel RTD plugin can now receive ICE market data
## 2.15.1
### Bug Fixes
- Fixed a FIX log parsing issue that resulted in exchange gateway logon problems
## 2.15.0
### User Visible Changes
- Added ability to hide Ladder status messages via Ladder Settings -> Left Panel tab -> Show/Hide groupbox -> Status Message
- Synthetic cloning now allows for any parameter to be changed, including tick size and leg contracts
- Added ability to import synthetics from a CSV file; see our manual for further details on this process
- Reduced Iceberg reloading latency
### Bug Fixes
- Fixed a bug where highlighted fills could lose their highlight status
- Fixed a bug where the (Mini) OrderBook PIQ could be displaying an old value
## 2.14.3
### Bug Fixes
- Fixed a bug regarding ICE logon messaging
## 2.14.2
### Bug Fixes
- Fixed a bug regarding ICE messaging
## 2.14.1
### Bug Fixes
- Fixed a bug regarding ICE logon authentication
## 2.14.0
### User Visible Changes
- All ladders can now be recentered by (shift or alt) + double clicking over the Price column in a ladder
- Added a Max Order Quantity setting to the Safety Checks tab on the Ladder settings. This setting allows for a user-defined maximum order quantity.
- Added a Price Display Format setting to the Market Price and Size Display tab on the Ladder settings. This setting allows for price levels in the ladder to be displayed without a decimal separator.
- Synthetic instrument creation with 3 legs no longer defaults to butterfly quantites
### Bug Fixes
- Fixed an issue where market transition were not observed by IAlgoOrder
- Fixed a bug in error reporting
- Fixed a bug in the PnL window where specifying a certain grouping order would lead to a UI failure
## 2.13.0
### User Visible Changes
- Added the name of the spreader to the parent algo badge (for spreader child orders), in the fills, orderbook and floating orderbook windows
- Added support for sounds for new orders
- Added a new quoting throttle parameter for the spreader, that throttles quotes that are moving toward the inside market but are still a configurable number of ticks away
- Enabled the ladder to remember the left panel scroll position across front end restarts
### Bug Fixes
- Fixed the local fills window so that it does not show the admin fills
- Removed the deleted accounts from the ladder accounts dropdown
- Fixed the orderbook price for iceberg of spreaders
## 2.12.0
### User Visible Changes
- Added support for ICE market data and order execution
### Internal Changes
- Lots of permission / admin changes to facilitate ICE
## 2.11.0
### User Visible Changes
- Fixed fills form time filter to also apply to SOD fills
- Addressed an issue that could prevent the web Admin UI from displaying over slow HTTP connections
- Removed the option to enter manual spread fills
- Fixed an issue that could cause erroneous fill account attribution in the event of a crash
### Internal Changes
- Added support for tag 1031. Currently defaults to Y, can it be override through config.
- To facilitate support, allowed the bts user to view and edit algos from all other users
- Improved system tests
- Improved log messages in UI to include dispatcher thread name
- Added support for Windows core dumps in the UI
## 2.10.0
### User Visible Changes
- Fills now include information about the algo that originated the fill
- A new, more powerful PnL form is now available
### API Changes
- Added security state information (pre open, halted, normal, closed, etc)
### Bug Fixes
- Brought back the "select all" check box in the mini order book
- Fixed an issue in the market data subsyLine truncated
|
||||
+++
|
||||
title = "Release notes"
|
||||
weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 2.15.10
|
||||
### Bug Fixes
|
||||
- Solved an issue that could cause the user interface to fail when playing some alert sounds
|
||||
### API Changes
|
||||
- Added settlement and position information to the API
|
||||
- Added the ability for user algos to report custom state to the user interface
|
||||
- Added the ability for algos to discover all active securities
|
||||
## 2.15.3
|
||||
### Bug Fixes
|
||||
- Fixed a bug where some CME currency spreads exhibited incorrect tick values
|
||||
## 2.15.2
|
||||
### User Visible Changes
|
||||
- Excel RTD plugin can now receive ICE market data
|
||||
## 2.15.1
|
||||
### Bug Fixes
|
||||
- Fixed a FIX log parsing issue that resulted in exchange gateway logon problems
|
||||
## 2.15.0
|
||||
### User Visible Changes
|
||||
- Added ability to hide Ladder status messages via Ladder Settings -> Left Panel tab -> Show/Hide groupbox -> Status Message
|
||||
- Synthetic cloning now allows for any parameter to be changed, including tick size and leg contracts
|
||||
- Added ability to import synthetics from a CSV file; see our manual for further details on this process
|
||||
- Reduced Iceberg reloading latency
|
||||
### Bug Fixes
|
||||
- Fixed a bug where highlighted fills could lose their highlight status
|
||||
- Fixed a bug where the (Mini) OrderBook PIQ could be displaying an old value
|
||||
|
||||
## 2.14.3
|
||||
### Bug Fixes
|
||||
- Fixed a bug regarding ICE logon messaging
|
||||
|
||||
## 2.14.2
|
||||
### Bug Fixes
|
||||
- Fixed a bug regarding ICE messaging
|
||||
|
||||
## 2.14.1
|
||||
### Bug Fixes
|
||||
- Fixed a bug regarding ICE logon authentication
|
||||
|
||||
## 2.14.0
|
||||
### User Visible Changes
|
||||
- All ladders can now be recentered by (shift or alt) + double clicking over the Price column in a ladder
|
||||
- Added a Max Order Quantity setting to the Safety Checks tab on the Ladder settings. This setting allows for a user-defined maximum order quantity.
|
||||
- Added a Price Display Format setting to the Market Price and Size Display tab on the Ladder settings. This setting allows for price levels in the ladder to be displayed without a decimal separator.
|
||||
- Synthetic instrument creation with 3 legs no longer defaults to butterfly quantites
|
||||
### Bug Fixes
|
||||
- Fixed an issue where market transition were not observed by IAlgoOrder
|
||||
- Fixed a bug in error reporting
|
||||
- Fixed a bug in the PnL window where specifying a certain grouping order would lead to a UI failure
|
||||
|
||||
## 2.13.0
|
||||
### User Visible Changes
|
||||
- Added the name of the spreader to the parent algo badge (for spreader child orders), in the fills, orderbook and floating orderbook windows
|
||||
- Added support for sounds for new orders
|
||||
- Added a new quoting throttle parameter for the spreader, that throttles quotes that are moving toward the inside market but are still a configurable number of ticks away
|
||||
- Enabled the ladder to remember the left panel scroll position across front end restarts
|
||||
### Bug Fixes
|
||||
- Fixed the local fills window so that it does not show the admin fills
|
||||
- Removed the deleted accounts from the ladder accounts dropdown
|
||||
- Fixed the orderbook price for iceberg of spreaders
|
||||
|
||||
## 2.12.0
|
||||
### User Visible Changes
|
||||
- Added support for ICE market data and order execution
|
||||
### Internal Changes
|
||||
- Lots of permission / admin changes to facilitate ICE
|
||||
|
||||
## 2.11.0
|
||||
### User Visible Changes
|
||||
- Fixed fills form time filter to also apply to SOD fills
|
||||
- Addressed an issue that could prevent the web Admin UI from displaying over slow HTTP connections
|
||||
- Removed the option to enter manual spread fills
|
||||
- Fixed an issue that could cause erroneous fill account attribution in the event of a crash
|
||||
### Internal Changes
|
||||
- Added support for tag 1031. Currently defaults to Y, can it be override through config.
|
||||
- To facilitate support, allowed the bts user to view and edit algos from all other users
|
||||
- Improved system tests
|
||||
- Improved log messages in UI to include dispatcher thread name
|
||||
- Added support for Windows core dumps in the UI
|
||||
|
||||
## 2.10.0
|
||||
### User Visible Changes
|
||||
- Fills now include information about the algo that originated the fill
|
||||
- A new, more powerful PnL form is now available
|
||||
### API Changes
|
||||
- Added security state information (pre open, halted, normal, closed, etc)
|
||||
### Bug Fixes
|
||||
- Brought back the "select all" check box in the mini order book
|
||||
- Fixed an issue in the market data subsystem that on rare occasions could result in a startup crash
|
||||
### Internal Changes
|
||||
- Fixed an issue that was stopping us from interpreting SOD times as local rather than GMT
|
||||
## 2.9.3
|
||||
### Internal Changes
|
||||
- Fixed system test scripts
|
||||
## 2.9.2
|
||||
### Bug Fixes
|
||||
- Fixed an issue with the exchange simulator that could lead to crossed markets
|
||||
## 2.9.1
|
||||
### Bug Fixes
|
||||
- Fixed an issue with trade sound alerts, where in some cases the use of a "In Between" condition could lead to a UI failure.
|
||||
- Fixed an issue with the display of the new raw position column in the market view window.
|
||||
- Fixed the popup label that describes the raw position information in the ladder
|
||||
### Internal Changes
|
||||
- Avoided shipping production configuration defaults that lead to trying to download a position file for the development sftp server.
|
||||
### Api
|
||||
- C# API sample now uses synchronous calls.
|
||||
|
||||
## 2.9.0
|
||||
### User Visible Changes
|
||||
- Improved Workspace management User Interface
|
||||
- Added the ability to change grid background colors
|
||||
- The Spark UI Window group now has a title in the Windows task bar
|
||||
- Added sound alerts for trade events, see Launcher -> Settings -> Alerts
|
||||
- Allowed composite algos that are attached to existing orders to identify fills that were already present prior to attachment
|
||||
- Added import facilities for GMI position files
|
||||
- Added the ability for risk managers to enter manual fills
|
||||
- Added "raw" P&L columns, that is columns that *do not* include spread legs in their accounting
|
||||
### Bug Fixes
|
||||
- Fixed the settings action menu (Load/Save/Reset to Factory)
|
||||
- Fixed the method used to determine if a security is expired to take into account the start of day time
|
||||
### API Changes
|
||||
- Fixed an issue in the Client SDK that could lead to occasional failures to connect to the spark server as well as excessive memory usage.
|
||||
- Added the ability for algo orders to distinguish fills existing prior to launch
|
||||
### Internal Changes
|
||||
- Changed how we packaged certain third-party components
|
||||
- Added logging of entitlement status
|
||||
- Tagged outgoing orders with sequence number of marketdata or FIX event that triggered them
|
||||
## 2.8.3
|
||||
### Bug Fixes
|
||||
- Fixed a bug that could cause an OrderBook's Security column filter to be blank
|
||||
- Fixed a bug that could cause a UI failure when clearing a filter selection on a filtering widget
|
||||
## 2.8.2
|
||||
### Bug Fixes
|
||||
- Added proper display factors for SOM spreads
|
||||
- Fixed a bug that could cause a UI failure when trying to change settings in a synthetic ladder
|
||||
## 2.8.1
|
||||
### Bug fixes
|
||||
- Fixed an error in the UI grids filtering widget
|
||||
## 2.8.0
|
||||
### User Visible Changes
|
||||
- Added ProRata and Scratch algos
|
||||
- Ladder badge now grows/shrinks to fill the containing cell
|
||||
- Attached algo parameters are now displayed in the update panel
|
||||
- Fixed a bug where the update panel would show '-' values when only
|
||||
1 order was selected
|
||||
- Fixed a bug that cause cause a failure on startup if the user
|
||||
loaded a workspace with a Ladder that had a dynamic order type
|
||||
selected by default
|
||||
- Increased width of login panel to better show login error messages
|
||||
- Added optional 'Maker/Taker' pricing to Spreader algo
|
||||
- Algo buttons in ladder can now be made to deactivate after launching an algo or remain 'sticky'
|
||||
### Internal changes
|
||||
- Made it easier to generate simple UI's for new order types
|
||||
### API Changes
|
||||
- Dynamic orders can now use QuantityMeasure parameter types
|
||||
|
||||
## 2.7.2
|
||||
### User Visible Changes
|
||||
- Changed the login dialog to avoid leaking workspace names from other users.
|
||||
- Added new Splitter algo. From the manual: The splitter is useful
|
||||
for pro-rata markets where you want to vary your exposure
|
||||
independent of the order quantity that you want to fill. The
|
||||
splitter will complete when the number of fills reaches or exceeds
|
||||
the order quantity but it will put exposed_qty total size on the
|
||||
market. Splitter favors order size over queue position.
|
||||
### Internal Changes
|
||||
- Made spark more resilient to lack of connectivity to the CME web site for downloading of templates.
|
||||
|
||||
## 2.7.1
|
||||
### User Visible Changes
|
||||
- Fixed some issues with the Cover algo, where in some circumstances
|
||||
it would erroneously cancel launched hedges on completion.
|
||||
|
||||
## 2.7.0
|
||||
### User Visible Changes
|
||||
- Allowed algos to process "in-flight" fills while shutting down
|
||||
- Changed the cover algo to continue to hedge while shutting down
|
||||
### API Changes
|
||||
- Changed the API so that algos do not fully terminate until all
|
||||
their children are terminated. Algos that are in the process of
|
||||
shutting down in response to a complete or cancel call will still
|
||||
receive child updates, which they can use to calculate and report
|
||||
an accurate fill count (during this shutdown stage algos cannot
|
||||
create, cancel or update children). A new IAlgoServices method,
|
||||
is_shutting_down(), can be used to determine if an algo is
|
||||
shutting down.
|
||||
|
||||
## 2.6.2
|
||||
### User Visible Changes
|
||||
- Fixed an issue in the ladder where in some cases the mini order book could not be brought up from a badge
|
||||
- Fixed a rare race condition that could lead to erroneous limit enforcement
|
||||
- Fixed an issue with the margin limits where an error could occur if an account name with more than 12 chars was used
|
||||
|
||||
## 2.6.1
|
||||
### User Visible Changes
|
||||
- Fixed an issue in the ladder where an error could occur when trying to change ladder settings for the first time
|
||||
|
||||
## 2.6.0
|
||||
### User Visible Changes
|
||||
- In the ladder made last trade more distinctive when overlayed on the volumn column
|
||||
- In the ladder, corrected display of volume tool tip
|
||||
- In the ladder, changed the consolidation of last trade to consider only the price ignoring aggressor changes
|
||||
- Added an option for the Top Algo to delete itself if within N ticks from top of book
|
||||
- Address a rare issue that could lead to an error report on UI exit
|
||||
- Added margin risk limits
|
||||
- Added the "Offset" algo. Offset prices a child algo at a parameterizable distance from the top of the book
|
||||
- Avoided switching the current account and order type on open ladders when limits are changed
|
||||
### Internal Changes
|
||||
- Ensure that admin logins are always allowed to login, regardless of keycode status.
|
||||
|
||||
## 2.5.0
|
||||
### User Visible Changes
|
||||
- Added loss limit risk check
|
||||
- Made algos, synthetics and user names private so that multiple users can share a single system privately.
|
||||
- Allowed the mini order book defaults for column widths to be changed from the mini order book
|
||||
- Allowed the mini order book to shrink to the minimum size that will be needed to display the selected columns
|
||||
- Improved accuracy of the directional arrow when displaying last trades in the volumn column
|
||||
## 2.4.0
|
||||
### User Visible Settings
|
||||
- In the ladder, changed the last trade column to accumulate successive trades at the same price
|
||||
- Fixed a bug in the order book where the right side ladder would display the top leve order's security rather then current row's
|
||||
- Added OK, Cancel buttons to the security picker for ladder profiles or sounds
|
||||
- Added ladder settings for the mini order book
|
||||
- Added a safety check to guard against unintentional market crosses
|
||||
- Improved the Queue Holder mode in the spreader:
|
||||
- Added a Show Quantity, allowing the spreader to keep queue position of the trailing orders as Fills (of size Show Qty) happen
|
||||
- Improved spreader performance when in Queue Holder mode
|
||||
### Internal changes
|
||||
- Suppressed order callbacks for completed/canceled orders. This should eliminate some bogus error logs by spreader orders.
|
||||
- Addressed a potential concurrency issues with service initialization.
|
||||
|
||||
|
||||
## 2.3.0
|
||||
### User Visible Changes
|
||||
- Introduced multiple settings profiles for the ladder
|
||||
- This allows multiple standard configuration for the ladder, optionally with security specific variations
|
||||
- Made Cover order more independent from its hedging chidren:
|
||||
- Cover continues to operate even if an hedging child is canceled
|
||||
- When a cover is canceled it will detach from any running hedging children, instead of canceling them
|
||||
- Changed Lean, Join and Cover algos to hide themselves in the ladder once they have completed their work
|
||||
- Changed manual fills to appear in day PnL rather than in the SOD records
|
||||
- Add buy/sell coloring to fill rows in fills window
|
||||
- Streamlined the mini order book:
|
||||
- Display of the mini order book popup is quicker
|
||||
- Only exchange orders are displayed
|
||||
- Update buttons are only displayed for orders that are under control of an algo with the update applying to the entire algo, not just the exchange order
|
||||
- We can now select multiple rows to attach, detach, update or cancel multiple orders in a batch
|
||||
- A checkbox column has been added to make clearer which rows are selected
|
||||
### API Changes
|
||||
- Enhanced the API to allow algos to implement custom cancel handling
|
||||
- Allowed algos to control their visibility in the ladder through the API
|
||||
### Internal Changes
|
||||
- Added system requirements to Spark documentation
|
||||
- Implemented keycodes
|
||||
- Temporarily rejected stop and stop limit orders, pending bug fixes
|
||||
|
||||
## 2.2.0
|
||||
### User Visible Changes
|
||||
- Made our orders easier to spot in MBO display
|
||||
- Changed middle-click on center column to recenter ladder and introduced Alt middle-click on center to bring up the MBO popup
|
||||
- Fixed Net/Absolute radio button in risk admin UI
|
||||
- Fixed double counting of spread legs in limits
|
||||
- Removed expired fills from the system
|
||||
- Improved handling of orders with undiscovered securities
|
||||
- Added feedback to the ladder when the user attempts to middle click on bid/ask columns using an order that does not have parameters
|
||||
- Fixed display of algo studio algo types in order book
|
||||
- Hid net change setting in synthetic ladders
|
||||
- Fixed a number of problems with filtering on the Fills form
|
||||
- On the Lean algo, OnToward and Away are now calculated with respect to the opposite inside market at time of the order launch
|
||||
- Iceberg Spreaders with queue holders are not erroneously cancelled after the first fill
|
||||
- Added security symbol column to fills window
|
||||
- Re-organized ladder settings dialog to make it clearer and easier to use
|
||||
|
||||
## 2.1.4
|
||||
### User Visible Changes
|
||||
- Fixed handling of cancel button in security picker
|
||||
|
||||
## 2.1.3
|
||||
### User Visible Changes
|
||||
- Fixed a simulator bug in the handling of 0 sized orders
|
||||
|
||||
## 2.2.1
|
||||
### User Visible Changes
|
||||
- Fixed order book form exception when display a spreader order for whichs the legs were not present yet
|
||||
|
||||
## 2.1.1
|
||||
### User Visible Changes
|
||||
- Added PIQ to GLBX Iceberg orders
|
||||
- Corrected handling of stop limit orders
|
||||
|
||||
## 2.1.0
|
||||
### User Visible Changes
|
||||
- Added support for the GLBX Iceberg order type
|
||||
- Allowed quick algo param edits by middle-clicking on either the bid/ask columns or a "favorite" button
|
||||
- Added support for manual fills, either entered manually or imported from file. Manual fills are used only for P&L purposes, they have no impact on limits
|
||||
- Allowed the LTQ and VAP values to be overlayed on the same ladder column
|
||||
- Added day position information to the ladder
|
||||
- Added PIQ to the mini order book
|
||||
- Fixed a rare bug that could lead to server failure when requesting market data
|
||||
- Fixed a bug that could cause incorrect price displays for certain types of synthetic securities
|
||||
|
||||
## 2.0.5
|
||||
### User Visible Changes
|
||||
- Fixed a bug in the handling of rejected QHO orders
|
||||
- Implemented stricter position limit enforcement
|
||||
- Fixed a bug that could cause server failure when trying to attach to already attached orders
|
||||
|
||||
## 2.0.4
|
||||
### User Visible Changes
|
||||
- Fixed performance regression in order manager
|
||||
|
||||
### Internal changes
|
||||
- Improved market data diagnostics for bad configurations
|
||||
- Changed market data default buffer size
|
||||
|
||||
## 2.0.3
|
||||
### Internal changes
|
||||
- Improved backwards compatibility in UI settings
|
||||
|
||||
|
||||
## 2.0.2
|
||||
### User Visible Changes
|
||||
- Middle click the ladder price column to show the orders for that price level
|
||||
- Middle click the bid or ask column in the ladder no longer sends an order
|
||||
|
||||
### Internal changes
|
||||
- Improved market by order performance under heavy load
|
||||
- Fixed a concurrency bug in SIM
|
||||
|
||||
## 2.0.0
|
||||
### User Visible Changes
|
||||
- Support for attaching and detaching algos to existing orders
|
||||
- Support for display and update of all parameters in running algos
|
||||
- Streamlining of order badge decorations in ladder
|
||||
- New order search/filtering algorithm for order book
|
||||
- Simpler representation of complex algo chains in order book and mini order book
|
||||
- Visual and usability improvements to mini order book
|
||||
- Streamlining of algo parameter dialog in ladder
|
||||
- New spreader queue holder behavior
|
||||
- Visual improvements to the order book
|
||||
- New server side API
|
||||
- Improved admin UI performance
|
||||
- Optimized PIQ rendering
|
||||
### Internal changes
|
||||
- Exchange simulator no longer requires daily sequence number reset
|
||||
- Improved MD resilience to corrupted shared memory
|
||||
- Native orders can now buffer updates/cancels when disconnected or pending first acknowledgment
|
||||
- Improved settings manager tool output
|
||||
- Added diagnostics in the event MD memory corruption is detected
|
||||
- Added systems tests for join,lean, iceberg and spreader orders
|
||||
- Made exchange order recovery more resilient in the presence of database loss
|
||||
|
||||
## 1.6.2
|
||||
### User Visible Changes
|
||||
### Exchange simulator
|
||||
- Exchange simulator now now produce position in queue data
|
||||
|
||||
## 1.6.0
|
||||
### User Visible Changes
|
||||
#### New Features
|
||||
- You can now view your order's position in queue in the ladder
|
||||
#### Bug Fixes
|
||||
- Fix a bug in the order popup in the ladder which could lead to a crash.
|
||||
|
||||
## 1.5.9
|
||||
### User Visible Changes
|
||||
- Fix for stunnel connections on Windows 7
|
||||
|
||||
## 1.5.8
|
||||
### User Visible Changes
|
||||
- Fixed a bug where it was possible to crash the UI by entering an order through the order ticket if Enter was used to dismiss the dialog.
|
||||
- Fixed a spreader reporting bug which could cause the server to crash if an algo was used to control certain types of synthetics
|
||||
- Improved user message when the UI fails to login
|
||||
- Integrated stunnel into UI to make VPN setup unnecessary in unsecured environments.
|
||||
## 1.5.7
|
||||
### User Visible Changes
|
||||
- New videos have been added to the manual describing the [Algo Studio](./bts_spark/Trading/_index.md#algo-studio) and [Spreader](./bts_spark/Spreader/_index.md)
|
||||
- Fixed a bug that occasionally would cause windows to be position outside the visible screen on startup
|
||||
- Fixed a bug in the order book form that would occasionally cause the Recreate Order option to be unavailable
|
||||
- Added a confirmation step to Cancel All, Cancel Buys and Cancel Sells commands
|
||||
- Fixed Aggressor coloring and ladder high/low coloring in the SIM execution gateway
|
||||
- Added initial support for ICE market data
|
||||
- In an effort to streamline the PnL report we've made the following changes:
|
||||
- The PnL report will by default filter for Futures contracts only
|
||||
- If you wish to see Spread contracts again you may change Security Type column filter
|
||||
- The PnL report will now only show rows in which there is a position (net and/or day)
|
||||
- The Exchange, Start PnL, Total Net Pos, and Total Net PnL columns are now hidden by default
|
||||
- These columns can be restored by toggling their visibility within the PnL's Settings form
|
||||
- A new Traded Today column has been added to the PnL
|
||||
- Filtering on this column will allow a user to restrict the report to only show today's position
|
||||
### Internal changes
|
||||
- Improved organization of the shell notification architecture
|
||||
- Added logging to help troubleshoot shell exit hang
|
||||
|
||||
## 1.5.6
|
||||
### User Visible Changes
|
||||
- Fixed a bug that could cause limits to be erroneously enforced
|
||||
- Improved algo documentation
|
||||
- Changed how the spreader uses net change
|
||||
- Simplified trigger algo user interface
|
||||
### Internal changes
|
||||
- Defaulted to not recording sim market data
|
||||
- Improved documentation build
|
||||
- Added additional performance tracing
|
||||
|
||||
## 1.5.5
|
||||
- Fixed Market View 'stale working orders' bug
|
||||
|
||||
## 1.5.4
|
||||
- Avoided logging when warming up cache
|
||||
|
||||
## 1.5.3
|
||||
- Fixed sounds startup issue
|
||||
|
||||
## 1.5.2
|
||||
- Added better logging to AlgoDefinitionService
|
||||
|
||||
## 1.5.1
|
||||
- Fixed missing securities in the Fills and P&L windows.
|
||||
|
||||
## 1.5.0
|
||||
### User Visible Changes
|
||||
#### New Features
|
||||
- This release introduces a major new feature, the Algo Studio, a
|
||||
set of tools allowing traders to create their own algos by
|
||||
composing existing algos to express unique strategies.
|
||||
#### User Interface Improvements
|
||||
- Removed security key field from most grids
|
||||
- Removed trade key field from time and sales grid
|
||||
- Fixed the "Since Open" time filter to use the exchange open time rather than 8:30 AM
|
||||
- Fixed the floating order book so that rows are no longer
|
||||
highlighted after an order is modified
|
||||
- Relocated quantity and price columns in floating order book immediately after the cancel column; users now can move the mouse a much shorter distance when modifying their orders
|
||||
- The escape key now closes the floating order book
|
||||
#### Bug Fixes
|
||||
- Fixed the "Since Open" time filter to use the exchange open time rather than 8:30 AM
|
||||
- Fixed a bug in the order book where sometimes parent orders would not update their last message column.
|
||||
|
||||
#### Algos
|
||||
- Added OCO Algo
|
||||
- An OCO order creates a relationship between a child order and a 'stop' or 'other' order such that when one of these is cancelled, both cancel, or when one is filled, the other order's size is debited in the amount of the fill. Composing this order with Cover simulates scalping functionality
|
||||
#### SDK
|
||||
- Improved SDK documentation and usability
|
||||
- Dynamic orders are now properly identified in the order book
|
||||
#### Documentation
|
||||
- Information on the Spark API has been added to the Spark manual
|
||||
- The documentation has been updated and reorganized to make information easier to find
|
||||
### Internal Changes
|
||||
- Improved robustness of limit system test
|
||||
- Fixed a bug in handling of built-in bts to user that could lead to failure to login as bts in old systems
|
||||
- Improved market data subsystem to support security definition updates
|
||||
- Fixed a startup race in market data that could lead to data corruption on startup
|
||||
- Increased default order price precision to 8 digis as required by some CME products
|
||||
|
||||
## 1.4.11
|
||||
### New Features
|
||||
- Floating Order Book
|
||||
- The floating order book provides a quick view of orders occupying a price level in the ladder
|
||||
- Launch the Floating Order Book by middle-clicking a ladder badge
|
||||
- Cancel orders by clicking on the red X in the Cancel column
|
||||
- Quantity and Price modification
|
||||
- Clicking on a row will select it, providing the option for changing your price and quantity
|
||||
- Enter in a new value or use the up/down buttons to set a desired value
|
||||
- Press the green checkbox to finalize your change and update the order
|
||||
### SDK
|
||||
- Improved SDK documentation and usability
|
||||
### User Interface Improvements
|
||||
- The following spreader quoting parameters have been deprecated:
|
||||
- Hidden parameter
|
||||
- Price Type setting of the Conditional parameter
|
||||
### Bug Fixes
|
||||
- Fixed a limit enforcement problem that could lead to 0 sized orders being sent to the exchange
|
||||
|
||||
## 1.4.10
|
||||
### Bug Fixes
|
||||
- Removed duplicate entries in performance log
|
||||
|
||||
## 1.4.9
|
||||
### SDK
|
||||
- Added ticker events to the order API
|
||||
### Performance Improvements
|
||||
- Significantly improved hedging speed
|
||||
### Bug Fixes
|
||||
- Fixed a visual oddity in the order book UI when using High DPI displays
|
||||
|
||||
## 1.4.8
|
||||
### Marketdata
|
||||
- Additional trade details for CME ticker
|
||||
|
||||
## 1.4.7
|
||||
### Bug Fixes
|
||||
- Corrected FIX tag 1028, ManualOrderIndicator, when orders are triggered by algos
|
||||
|
||||
## 1.4.6
|
||||
### Ladder Usability Improvements
|
||||
- The ladder can now be zoomed in/out according to a user's preference
|
||||
- The zoom slider can be enabled from Ladder Form -> Settings button -> Left Pane tab -> check Zoom Slider checkbox
|
||||
- The zoom functionality can also be performed via hotkey and mouse wheel combinations
|
||||
- Zoom In = CTRL + Mouse wheel up
|
||||
- Zoom Out = CTRL + Mouse wheel down
|
||||
|
||||
- Reset zoom = CTRL + Mouse wheel click or double click on the zoom slider
|
||||
### Exchange simulator
|
||||
- Improved the exchange simulator to track real market data more closely
|
||||
### Bug Fixes
|
||||
- Fixed a UI crash in some cases when attempting to sort the order book by time
|
||||
|
||||
## 1.4.5
|
||||
- Fixed entitlement validation for ICS spreads
|
||||
- Tuned performance logging
|
||||
|
||||
## 1.4.4
|
||||
|
||||
### Performance Improvements
|
||||
- Reduced performance logging activity
|
||||
|
||||
## 1.4.3
|
||||
|
||||
### Ladder Usability Improvements
|
||||
- Controls found in Left Pane of the ladder can now be hidden via the checkboxes found under Ladder Form -> Settings button -> Left Pane tab
|
||||
- The Left Pane is now scrollable with the mouse wheel when there isn't enough vertical space to accomodate the entire form
|
||||
- The Left Pane can now grown and shrunk horizontally via the splitter between the left pane and the main ladder area
|
||||
### Performance Improvements
|
||||
- Requoting and hedging latency has been reduced by up to 30%
|
||||
### Software Development Kit (SDK) Improvements
|
||||
- The API now enforces authentication
|
||||
### Bug Fixes
|
||||
- The Market View form now honors filters when new securities are added
|
||||
|
||||
## 1.4.2
|
||||
|
||||
### User Interface Improvements
|
||||
- When creating a synthetic from the Market View window, securities are now added to the Create Synthetic dialog in the same sort order as they appear in grid
|
||||
### Bug Fixes
|
||||
- Shell no longer crashes when removing a security from the synthetics window
|
||||
- It is now possible to create a new synthetic using the same name as a previously deleted synthetic
|
||||
### SDK Improvements
|
||||
- Improved Client and Server SDK clarity and documentation
|
||||
### Performance Improvements
|
||||
- Synthetic calculation speed was improved by 400%.
|
||||
### Risk / Entitlement System
|
||||
- Risk Admin UI
|
||||
- Spark users now have separate Trading, RiskAdmin, and/or MDAdmin permissions
|
||||
- Spark users can now only add other users or accounts if permissioned to do so.
|
||||
- Market Data Entitlement
|
||||
- Automated market data reporting
|
||||
|
||||
|
||||
## 1.4.1
|
||||
|
||||
### Tabs
|
||||
- Tabs now shrink and expand to make better use of horizontal space
|
||||
### Bug Fixes
|
||||
- Fixed a bug that could cause excessive CPU usage when a connection broke
|
||||
- Fixed a layout bug in hedging/requoting performance reports
|
||||
- Fixed account name mismatching bug for old fills
|
||||
### Absolute Position limits
|
||||
- Positions can now be netted or absolute for purposes of limit enforcing
|
||||
|
||||
## 1.4.0
|
||||
|
||||
### Speed Enhancements
|
||||
- Improved requoting and hedging performance
|
||||
- Improved order book form performance
|
||||
### Server Side SDK
|
||||
- Added a beta version of the server side SDK
|
||||
### Front End
|
||||
- Added status bars to all UI grids
|
||||
### Reporting
|
||||
- Added reporting of hedging and requoting performance
|
||||
|
||||
## 1.3.8
|
||||
|
||||
### Speed Enhancements
|
||||
- Further speed enhancements
|
||||
### Conditional (new quoting parameter)
|
||||
- Reduces re-quoting updates by only quoting if N ticks away from the opposite inside market.
|
||||
- Quotes are canceled when it gets further away.
|
||||
- "Price Type" dictates if this logic applies to the quoting market or to the synthetic spread market.
|
||||
|
||||
## 1.3.7
|
||||
|
||||
### Net Change pricing
|
||||
- The ability to price synthetics based off of net change
|
||||
|
||||
## 1.3.5
|
||||
|
||||
### Speed Enhancements
|
||||
- Further speed enhancements
|
||||
### Separation of implied and direct market data
|
||||
- Viewing within a market view will now show 3 columns
|
||||
- Direct
|
||||
- Implied
|
||||
- Merged
|
||||
- Ability to configure the market data size in a ladder as the following
|
||||
- Direct
|
||||
- Implied
|
||||
- Merged
|
||||
### Synthetic creation option to choose Merged or Direct
|
||||
- The ability to choose Merged or Direct per leg when creating a synthetic
|
||||
### Volume at price
|
||||
- Within a ladder, you can now see a volume at price graph
|
||||
- This is configurable from a ladder's settings as well
|
||||
### Customized settings in all windows
|
||||
- All windows now contain a settings page where grid settings (font color, size, etc) are applied
|
||||
- These settings can be saved on a per-window basis or as a Default for newly-launched windows
|
||||
|
||||
|
||||
## 1.3.1 Through 1.3.4
|
||||
|
||||
### Speed Enhancements
|
||||
- Further speed enhancements
|
||||
|
||||
## 1.2
|
||||
|
||||
### Market View
|
||||
- Synthetic future leg working orders are now shown
|
||||
|
||||
### Order Book
|
||||
- Many visual facets of the Order Book grid can now be customized
|
||||
- The Order Book settings page can be accessed from the gears icon in the Order Book toolbar or from the right-click menu
|
||||
- The Expand/Collapse All button has been separated into two different buttons on the toolbar
|
||||
|
||||
### Sound Alerts
|
||||
- Sounds alerts can now be configured for various order events
|
||||
- Configure sounds under the Sound/Notification Triggers on the Order Book settings page
|
||||
|
||||
### Fills
|
||||
- Newly launched fill windows will filter out start-of-day records by default
|
||||
|
||||
### PnL
|
||||
- Prices are now formatted with penny precision
|
||||
- Day PnL has been added to the Totals footer at the bottom of the window
|
||||
|
||||
### Ladder
|
||||
- Order badges now have been improved to communicate a variety of order state
|
||||
- Background color now indicates order side
|
||||
- Child orders will display a one-character abbreviation to their parent order enclosed in a rectangle; the rectangle will have a fill color matching the parent order
|
||||
- Border color around badge will match the associated algo color
|
||||
- Exchange orders will present with a solid dot in the badge's top-right corner
|
||||
- When multiple orders reside at the same level, a solid triangle will be present in badge's bottom-right corner; all other badge decorations will not be visible in this state
|
||||
- Hedge orders will present with a vertical line inside the badge's left border
|
||||
- Trigger orders, once triggered, will no longer be shown in the ladder
|
||||
- Session High and Low visual indicators have been added to the Price column
|
||||
- Session range is now indicated in the Price column
|
||||
- A button reflecting position has been added above the quantity field
|
||||
- This button reflects the position for the currently selected account
|
||||
- Pressing the button will copy the position size to the quantity field
|
||||
- Order quantity button size values are now customizable
|
||||
- Default size preset values can now be changed, as well as added or removed
|
||||
- This can be configured per ladder instance (i.e. per security) by making use of the Custom tab
|
||||
- Synthetic ladders that were saved as part of a workspace are now restored on startup
|
||||
|
||||
### Synthetics
|
||||
- Hedge Ratio parameter has been renamed to Lean Qty Factor
|
||||
- Spreader now includes a Hidden mode parameter
|
||||
- A quoting leg can be hidden, ie an order will not be sent out until the market crosses the computed quoting price. When that happens, the quote is sent out as an IOC order.
|
||||
|
||||
### Algos
|
||||
- Trigger orders now can contain a Trigger Exit parameter based on time or price; once the exit condition has been met the Trigger order will cancel
|
||||
- IOC/FAK order type added
|
||||
- Cover order type added
|
||||
- Canceling a synthetic order no longer cancels any working hedge orders
|
||||
|
||||
### General
|
||||
- A manual page has been added to the launcher Help menu
|
||||
- Various help buttons and tooltips were added throughout the application
|
||||
- Various cash per point values updated
|
||||
- Time-based grid filters now have tick up/down buttons
|
||||
- Numerous performance improvements and stability enhancements
|
||||
- Automated position and Order Book maintenance during daily recycle
|
||||
- Individual fills will be compacted into the Start of Day position records
|
||||
- Orders from the day before the recycle will be deleted
|
||||
|
||||
|
||||
## 1.1
|
||||
|
||||
### 1.1.22
|
||||
- Order Book: Orders in Error status can now be recreated
|
||||
- Order Book: Orders containing child orders will now be collapsed by default.
|
||||
- Order Book: Expand / Collapse All button added to the right-click menu and window action pane, denoted by ▲ icon
|
||||
- Order Book: Symbol column added to allow for filtering based on group symbol
|
||||
- Time filters now have a quick Set Last Hour hyperlink which will set the Period Begin filter to (Now - 1 hour)
|
||||
|
||||
### 1.1.20
|
||||
- Update CME market data handler for price precision extension.
|
||||
https://www.cmegroup.com/confluence/display/EPICSANDBOX/Price+Precision+Extension
|
||||
|
||||
### 1.1.8
|
||||
- Enabled wild cards in risk management UI
|
||||
- Fixed spreader order updates
|
||||
|
||||
### 1.1.7
|
||||
- Fixed spread leg price fill
|
||||
- Fixed HO cash per point value
|
||||
|
||||
### 1.1.6
|
||||
- Fixed occasional crash when filtering fills
|
||||
- Fixed several cash per point multipliers
|
||||
- Colored time of sales row with aggressor side
|
||||
- Iceberg orders now cancel (rather than an error) when a child is canceled
|
||||
|
||||
### 1.1.0
|
||||
- Added per user self match protection, location and self match protection FIX tag settings
|
||||
- Implemented a new "Trigger Order" algo
|
||||
- Corrected tick formatting for a number of products
|
||||
- Added the ability to recreate canceled orders
|
||||
- Added leg fills to facilitate futures based PnL
|
||||
- PnL report now has PnL columns calculated based on your Start of Day, current day, and total position
|
||||
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
|
||||
- Corrected cash per point values, used in pnl calculations, for a number of products
|
||||
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
|
||||
+1
-1
Submodule docker/linux updated: 0afd5e92de...caa02902ba.
+1
-1
Submodule externals updated: 38945c5f49...9a3879ce2b.
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "2.1.508",
|
||||
"rollForward": "latestMajor"
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,6 @@
|
||||
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(VCTargetsPath)'==''">
|
||||
<VCTargetsPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets</VCTargetsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(ComputerSpecificConfig)" Condition="Exists($(ComputerSpecificConfig))" />
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -549,48 +549,24 @@
|
||||
</u:BindingUpdateGroupCollection>
|
||||
</u:Behavior.BindingUpdateGroups>
|
||||
</TextBox>
|
||||
<StackPanel
|
||||
<TextBlock
|
||||
x:Name="PART_TickFactors_Label"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
Text="Ticks"
|
||||
IsEnabled="True"
|
||||
Orientation="Horizontal"
|
||||
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
|
||||
>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
Text="Ticks ("
|
||||
/>
|
||||
<CheckBox
|
||||
Margin="1,0,0,0"
|
||||
IsChecked="{Binding UseFractionalTicks, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Padding="0"
|
||||
>
|
||||
fractional
|
||||
</CheckBox>
|
||||
<TextBlock VerticalAlignment="Center" Margin="1,0,0,0">)</TextBlock>
|
||||
</StackPanel>
|
||||
<Grid
|
||||
/>
|
||||
<c:TickFactorsPicker
|
||||
x:Name="PART_TickFactors_Picker"
|
||||
Grid.Column="3"
|
||||
IsEnabled="True"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="True"
|
||||
SelectedTickFactors="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
|
||||
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
|
||||
>
|
||||
<c:TickFactorsPicker
|
||||
HorizontalAlignment="Stretch"
|
||||
SelectedTickFactors="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
|
||||
Visibility="{Binding UseFractionalTicks, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static cv:BooleanToVisibilityConverter.HiddenInstance}}"
|
||||
/>
|
||||
<c:TickUpDownInt
|
||||
HorizontalAlignment="Stretch"
|
||||
Minimum="1"
|
||||
Value="{Binding IntegerTickSize, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Visibility="{Binding UseFractionalTicks, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource FalseVisibilityConverter}}"
|
||||
/>
|
||||
</Grid>
|
||||
/>
|
||||
<TextBlock
|
||||
x:Name="PART_Pricing_Label"
|
||||
Grid.Column="4"
|
||||
|
||||
@@ -52,26 +52,6 @@ namespace mbts.ui.spark {
|
||||
public TickFactors TickFactors { get => GetValue(TickFactorsProperty) as TickFactors; set => SetValue(TickFactorsProperty, value); }
|
||||
#endregion
|
||||
|
||||
#region UseFractionalTicks
|
||||
public static readonly DependencyProperty UseFractionalTicksProperty = WPFHelper.create_dp<bool, ExecutionStrategyEditor>(
|
||||
nameof(UseFractionalTicks), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, true);
|
||||
|
||||
public bool UseFractionalTicks {
|
||||
get => (bool)GetValue(UseFractionalTicksProperty);
|
||||
set => SetValue(UseFractionalTicksProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IntegerTickSize
|
||||
public static readonly DependencyProperty IntegerTickSizeProperty = WPFHelper.create_dp<int, ExecutionStrategyEditor>(
|
||||
nameof(IntegerTickSize), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 1);
|
||||
|
||||
public int IntegerTickSize {
|
||||
get => (int)GetValue(IntegerTickSizeProperty);
|
||||
set => SetValue(IntegerTickSizeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IsNew
|
||||
public static readonly DependencyProperty IsNewProperty =
|
||||
DependencyProperty.Register(
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
ContractPickerModel="{Binding contract_picker_model}"
|
||||
HedgePricing="{Binding hedge_pricing}"
|
||||
HedgePricingItemsSource="{Binding hedge_pricing_options}"
|
||||
UseFractionalTicks="{Binding use_fractional_ticks}"
|
||||
IntegerTickSize="{Binding integer_tick_size}"
|
||||
/>
|
||||
<c:DialogSkirtOKCancel Grid.Row="1" OKCommand="{Binding commit}" CloseOnOK="False"/>
|
||||
</Grid>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using bts.collections;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.spark;
|
||||
@@ -29,16 +30,8 @@ namespace mbts.spark {
|
||||
readonly Property<bool> _has_working_orders, _can_add_leg;
|
||||
|
||||
Security _synthetic;
|
||||
bool
|
||||
_set_name_automatically,
|
||||
_set_ticks_automatically,
|
||||
_set_quantities_automatically,
|
||||
_set_price_mults_automatically,
|
||||
_is_setting_quantities,
|
||||
_is_setting_price_mults,
|
||||
_use_fractional_ticks = true;
|
||||
bool _set_name_automatically, _set_ticks_automatically, _set_quantities_automatically, _set_price_mults_automatically, _is_setting_quantities, _is_setting_price_mults;
|
||||
TickFactors _tick_factors;
|
||||
int _integer_tick_size = 1;
|
||||
ExecutionStrategyImmutable _strategy;
|
||||
HedgePricing _hedge_pricing;
|
||||
|
||||
@@ -51,10 +44,6 @@ namespace mbts.spark {
|
||||
: 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;
|
||||
if (TickUtils.is_integer_tick(synthetic.ContractSpec)) {
|
||||
_use_fractional_ticks = false;
|
||||
_integer_tick_size = DoubleHelper.to_optional_int32(synthetic.ContractSpec.LowTick) ?? 0;
|
||||
}
|
||||
title = $"Edit '{synthetic.Synthetic.Name}'";
|
||||
icon = Icons.ModifySynthetic;
|
||||
_has_working_orders = backing(nameof(has_working_orders), security_has_working_orders.OnDispatcher());
|
||||
@@ -142,14 +131,14 @@ namespace mbts.spark {
|
||||
flash_error_msg("Name already taken", SparkUtils.UI.MessageFlashDuration, nameof(name)).ignore();
|
||||
valid = false;
|
||||
}
|
||||
if (use_fractional_ticks ? (_tick_factors == null || !_tick_factors.is_valid()) : integer_tick_size == 0) {
|
||||
flash_error_msg("Please select ticks", SparkUtils.UI.MessageFlashDuration, use_fractional_ticks ? nameof(tick_factors) : nameof(integer_tick_size)).ignore();
|
||||
if (_tick_factors == null || !_tick_factors.is_valid()) {
|
||||
flash_error_msg("Please select ticks", SparkUtils.UI.MessageFlashDuration, nameof(tick_factors)).ignore();
|
||||
valid = false;
|
||||
}
|
||||
if (valid) {
|
||||
_synthetic.Synthetic.Legs.SetItems(leg_tuples.Select(l => l.leg));
|
||||
_synthetic.Symbol = _deduce_symbol(_synthetic);
|
||||
_synthetic.ContractSpec = use_fractional_ticks ? _tick_factors.to_spec() : TickUtils.create_integer_tick_spec(integer_tick_size);
|
||||
_synthetic.ContractSpec = _tick_factors.to_spec();
|
||||
uint? id = await model.create_synthetic(new SecurityImmutable(_synthetic));
|
||||
if (id.HasValue && id != 0) {
|
||||
if (await model.save_strategy(id.Value, get_strategy())) {
|
||||
@@ -306,20 +295,6 @@ namespace mbts.spark {
|
||||
public bool has_working_orders => _has_working_orders.Value;
|
||||
public bool is_new { get; }
|
||||
public bool is_new_leg_popup_open { get; set; }
|
||||
public bool use_fractional_ticks {
|
||||
get => _use_fractional_ticks;
|
||||
set {
|
||||
if (Set(ref _use_fractional_ticks, value))
|
||||
_set_ticks_automatically = false;
|
||||
}
|
||||
}
|
||||
public int integer_tick_size {
|
||||
get => _integer_tick_size;
|
||||
set {
|
||||
if (Set(ref _integer_tick_size, value))
|
||||
_set_ticks_automatically = false;
|
||||
}
|
||||
}
|
||||
public bool can_add_leg => _can_add_leg.Value;
|
||||
public SyntheticContract.Types.PriceCalc price_calc { get => _synthetic.Synthetic.PriceCalc; set => _synthetic.Synthetic.PriceCalc = value; }
|
||||
public ObservableCollection<LegSpreaderParamsTuple> leg_tuples { get; } = new ObservableCollection<LegSpreaderParamsTuple>();
|
||||
@@ -348,16 +323,6 @@ namespace mbts.spark {
|
||||
_strategy = strategy;
|
||||
hedge_pricing = strategy.HedgePricing;
|
||||
leg_tuples.SetItems(_get_legs(_synthetic, strategy));
|
||||
if (synthetic.HasContractSpec) {
|
||||
/// assign the fields <see cref="_use_fractional_ticks"/> and <see cref="_integer_tick_size"/>,
|
||||
/// rather than the properties <see cref="use_fractional_ticks"/> and <see cref="integer_tick_size"/>
|
||||
/// because the setters of the former flip <see cref="_set_ticks_automatically"/> to false if the value changes.
|
||||
/// This behavior causes the ticks to be automatically deduced as legs are added/removed,
|
||||
/// until the user explicitly enters a different value (as the properties are bound to tick input controls in the view).
|
||||
Set(ref _use_fractional_ticks, !TickUtils.is_integer_tick(synthetic.ContractSpec), nameof(use_fractional_ticks));
|
||||
if (!use_fractional_ticks)
|
||||
Set(ref _integer_tick_size, DoubleHelper.to_optional_int32(synthetic.ContractSpec.LowTick) ?? 0, nameof(integer_tick_size));
|
||||
}
|
||||
RaisePropertyChanged(nameof(name), nameof(tick_factors));
|
||||
}
|
||||
|
||||
|
||||
@@ -534,44 +534,13 @@ namespace mbts.spark {
|
||||
_size_from_row(row, _price_list, _asks, _vm.security.ContractSpec);
|
||||
|
||||
uint _volume_from_row(int row) {
|
||||
const int max_tries = 3;
|
||||
var errors = new List<string>(0);
|
||||
int num_tries = 0;
|
||||
for (; num_tries < max_tries; num_tries++) {
|
||||
try {
|
||||
var rv = impl();
|
||||
if (num_tries > 0) {
|
||||
log_errors(true);
|
||||
}
|
||||
return rv;
|
||||
} catch (Exception ex) {
|
||||
errors.Add(ex.ToString());
|
||||
}
|
||||
}
|
||||
log_errors(false);
|
||||
return 0;
|
||||
|
||||
void log_errors(bool succeeded) {
|
||||
// Only log distinct errors to avoid useless noise.
|
||||
// It's likely that the exception will always be the same (IndexOutOfRange),
|
||||
// however, this bug is not fully understood, so assume nothing.
|
||||
var error_msg = string.Join("; ", errors.Distinct());
|
||||
if (succeeded) {
|
||||
Log.Ui.error($"Ladder '{security}' got volume for row at index '{row}' after {num_tries + 1} attempts (price_list array length is {_price_list.Length}). The following exceptions were observed: {error_msg}");
|
||||
} else {
|
||||
Log.Ui.error($"Ladder '{security}' failed to get volume for row at index '{row}' after {max_tries} attempts (price_list array length is {_price_list.Length}): {error_msg}");
|
||||
}
|
||||
}
|
||||
|
||||
uint impl() {
|
||||
if (_price_list.SafeTryGetAt(row, out var price)) {
|
||||
var vapls = _volume_container.read_new_value();
|
||||
if (vapls != null) {
|
||||
return vapls.SelectFirstOrDefault(v => MathUtils.epsilon_equals(v.Price, price), v => v.Volume);
|
||||
}
|
||||
}
|
||||
return 0u;
|
||||
if (_price_list.SafeTryGetAt(row, out var price)) {
|
||||
var vapls = _volume_container.read_new_value();
|
||||
if (vapls != null) {
|
||||
return vapls.SelectFirstOrDefault(v => MathUtils.epsilon_equals(v.Price, price), v => v.Volume);
|
||||
}
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace mbts.spark {
|
||||
readonly OrderParamsAggregator _selected_params_mon;
|
||||
readonly CommonValueMonitor<SecurityImmutable?> _selected_security_mon = new CommonValueMonitor<SecurityImmutable?>();
|
||||
readonly RightPanelModel _right_panel = new RightPanelModel();
|
||||
readonly CustomStateSynchronizer _custom_state_sync;
|
||||
OrderBookFillsGridSettings _cached_fills_settings = OrderBookStartupData.DefaultFillsGrid;
|
||||
double
|
||||
_raw_right_panel_width = OrderBookStartupData.Default.right_panel_width,
|
||||
@@ -125,8 +124,6 @@ namespace mbts.spark {
|
||||
_row_collection.CollectionChanged += _row_collection_CollectionChanged;
|
||||
_current_row = backing<OrderBookRowVM>(nameof(current_row));
|
||||
_columns.filter_changed += _on_filter_changed;
|
||||
_custom_state_sync = new CustomStateSynchronizer(this);
|
||||
// commands
|
||||
collapse_all_rows_command = new DelegateCommand(_row_collection.collapse_all);
|
||||
expand_all_rows_command = new DelegateCommand(_row_collection.expand_all);
|
||||
var modify_selected_orders_async_cmd = new AsyncCommand(_modify_selected_orders_async, () => _selected_params_mon.all_active && order_mod_vm.is_dirty);
|
||||
@@ -191,7 +188,56 @@ namespace mbts.spark {
|
||||
security_plurality = p;
|
||||
RaisePropertyChanged(nameof(security_plurality));
|
||||
}),
|
||||
_current_row.Subscribe(_ => _custom_state_sync.sync()),
|
||||
Observable.Create<OrderBookRowVM>(o => {
|
||||
OrderBookRowVM last_fired = null;
|
||||
selected_order_changed += on_selected_order_changed;
|
||||
var sub = _current_row.Subscribe(ch => notify());
|
||||
return Disposable.Create(dispose);
|
||||
// local methods
|
||||
void on_selected_order_changed(OrderRecordImmutable? _) =>
|
||||
notify();
|
||||
|
||||
void notify() {
|
||||
var cr = current_row_if_distinct_root_selected();
|
||||
if (!cr.safe_equals(last_fired))
|
||||
o.OnNext(last_fired = cr);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
Disposable.dispose(ref sub);
|
||||
selected_order_changed -= on_selected_order_changed;
|
||||
}
|
||||
})
|
||||
.SubscribeEach(r => {
|
||||
if (r == null) {
|
||||
current_algo_custom_state.Clear();
|
||||
return Disposable.empty;
|
||||
}
|
||||
r.updated += on_updated;
|
||||
refresh_custom_state();
|
||||
return Disposable.Create(() => r.updated -= on_updated);
|
||||
void on_updated((OrderSecurityRootPair oldval, OrderSecurityRootPair newval) _) =>
|
||||
refresh_custom_state();
|
||||
|
||||
void refresh_custom_state() {
|
||||
current_algo_custom_state.Clear();
|
||||
var dcs = r.order.dynamic_custom_state();
|
||||
// while dcs is not expected to be null, best to play it safe and double check.
|
||||
if (dcs.is_null_or_empty()) {
|
||||
var child_rows = _row_collection.get_descendent_rows(r.order_key);
|
||||
if (_row_collection.filter != null) {
|
||||
child_rows = child_rows.Where(cr => _row_collection.filter(cr.order_tuple));
|
||||
}
|
||||
foreach (var dr in child_rows) {
|
||||
dcs = dr.order.dynamic_custom_state();
|
||||
if (!dcs.is_null_or_empty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (var p in dcs.empty_if_null().OrderBy(kv => kv.Key))
|
||||
current_algo_custom_state.Add(new NamedValueVM(p.Key, DynamicOrderHelper.fmt_param(p.Value)));
|
||||
}
|
||||
}),
|
||||
orders_source.OnDispatcher().Subscribe(t => {
|
||||
if (_selected_rows.Count == 0) {
|
||||
_cached_orders = t.state;
|
||||
@@ -211,8 +257,6 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
}
|
||||
_custom_state_sync.sync(true);
|
||||
orders_changed?.Invoke();
|
||||
}),
|
||||
model.on_orders_can_attach.OnDispatcher().Subscribe(table => {
|
||||
_cached_attachable = new MappedDictionary<uint, KeyValuePair<uint, bool>, bool>(table.state, kv => kv.Value);
|
||||
@@ -358,6 +402,15 @@ namespace mbts.spark {
|
||||
}
|
||||
void on_end_attaching() => Disposable.dispose(ref attach_vm_hooks);
|
||||
|
||||
// the output grid (custom state) in the right panel
|
||||
// will only display info for the 'current_row'
|
||||
// if all selected rows are of the same root order.
|
||||
OrderBookRowVM current_row_if_distinct_root_selected() =>
|
||||
current_row != null &&
|
||||
selected_rows.Select(r => r.order_tuple.root.order.Key).Distinct().TryGetSingle(out uint droot) &&
|
||||
current_row.order_tuple.root.order.Key == droot ?
|
||||
current_row : null;
|
||||
|
||||
IDisposable connect_order_mod_vm() =>
|
||||
Disposable.Create(
|
||||
_selected_params_mon.subscribe_algos().OnDispatcher().Subscribe(order_mod_vm.algo_vm.load_many),
|
||||
@@ -381,7 +434,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public event Action<OrderRecordImmutable?> selected_order_changed;
|
||||
public event Action begin_attaching, end_attaching, orders_changed;
|
||||
public event Action begin_attaching, end_attaching;
|
||||
public event Action<(IEnumerable, bool)> request_selection;
|
||||
|
||||
public ObservableAppendCollection<DIYGrid.ColumnBase> columns { get; }
|
||||
@@ -481,28 +534,6 @@ namespace mbts.spark {
|
||||
public IObservable<bool> subscribe_show_fills() => _show_fills.observe_current(true);
|
||||
public IObservable<bool> subscribe_show_ladder() => _show_ladder.observe_current(true);
|
||||
public IObservable<bool> subscribe_show_order_params() => _show_order_params.observe_current(true);
|
||||
public IObservable<OrderSecurityAlgo?> subscribe_single_selected_order() =>
|
||||
Observable.Create<OrderSecurityAlgo?>(o => {
|
||||
OrderSecurityAlgo? last_fired = null;
|
||||
orders_changed += notify;
|
||||
_current_row.Changed += on_current_row_changed;
|
||||
notify();
|
||||
return Disposable.Create(dispose);
|
||||
// local methods
|
||||
void notify() {
|
||||
var next = current_row == null ? (OrderSecurityAlgo?)null : current_row.order_tuple.order;
|
||||
if (next != last_fired)
|
||||
o.OnNext(last_fired = next);
|
||||
}
|
||||
|
||||
void on_current_row_changed(OrderBookRowVM oldval, OrderBookRowVM newval) =>
|
||||
notify();
|
||||
|
||||
void dispose() {
|
||||
orders_changed -= notify;
|
||||
_current_row.Changed -= on_current_row_changed;
|
||||
}
|
||||
});
|
||||
|
||||
public void row_selection_changed(SelectionChangedEventArgs e) {
|
||||
try {
|
||||
@@ -532,7 +563,6 @@ namespace mbts.spark {
|
||||
_can_attach_selected_orders.refresh();
|
||||
_can_detach_selected_orders.refresh();
|
||||
selected_order_changed?.Invoke(current_order);
|
||||
_custom_state_sync.sync();
|
||||
} catch (Exception ex) {
|
||||
// [John]I observed a strange null-reference exception in this callback when the launcher was shutting down (see https://bluetradesys.atlassian.net/browse/SPARK-2089)
|
||||
Log.Ui.error($"[{nameof(OrderBookVM)}] Error handling selection change: {ex}");
|
||||
@@ -753,45 +783,6 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
class CustomStateSynchronizer {
|
||||
readonly OrderBookVM _vm;
|
||||
OrderBookRowVM _synced_row;
|
||||
|
||||
public CustomStateSynchronizer(OrderBookVM vm) =>
|
||||
_vm = vm;
|
||||
|
||||
public void sync(bool force_refresh = false) {
|
||||
var row = _vm.current_row != null &&
|
||||
_vm.selected_rows.Select(r => r.order_tuple.root.order.Key).Distinct().TryGetSingle(out uint droot) &&
|
||||
_vm.current_row.order_tuple.root.order.Key == droot ?
|
||||
_vm.current_row : null;
|
||||
if (force_refresh || row != _synced_row) {
|
||||
_synced_row = row;
|
||||
_refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void _refresh() {
|
||||
_vm.current_algo_custom_state.Clear();
|
||||
if (_synced_row == null)
|
||||
return;
|
||||
var dcs = _synced_row.order.dynamic_custom_state();
|
||||
// while dcs is not expected to be null, best to play it safe and double check.
|
||||
if (dcs.is_null_or_empty()) {
|
||||
var descendent_rows = _vm._row_collection.get_descendent_rows(_synced_row.order_key);
|
||||
if (_vm._row_collection.filter != null)
|
||||
descendent_rows = descendent_rows.Where(child => _vm._row_collection.filter(child.order_tuple));
|
||||
foreach (var dr in descendent_rows) {
|
||||
dcs = dr.order.dynamic_custom_state();
|
||||
if (!dcs.is_null_or_empty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (var p in dcs.empty_if_null().OrderBy(kv => kv.Key))
|
||||
_vm.current_algo_custom_state.Add(new NamedValueVM(p.Key, DynamicOrderHelper.fmt_param(p.Value)));
|
||||
}
|
||||
}
|
||||
|
||||
public class OrderParamsVM : DisposableViewModelBase {
|
||||
readonly SelectorProperty<AccountTuple, AccountTuple?> _accounts;
|
||||
readonly OrderBookVM _owner;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using bts.shell.fills;
|
||||
@@ -7,11 +6,9 @@ using bts.shell.orderbook;
|
||||
using bts.spark;
|
||||
using bts.view;
|
||||
using mbts.persist;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utilities;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
@@ -27,10 +24,7 @@ namespace mbts.spark {
|
||||
const string _window_type = "OrderBook", _default_title = "Order Book";
|
||||
readonly INotificationService _notification_service;
|
||||
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
|
||||
readonly bool _can_edit_specific_algo_params;
|
||||
IDisposable _attachment_exit_sub;
|
||||
// initialized in thread_init
|
||||
List<IDisposable> _thread_init_resources;
|
||||
OrderBookStartupData _state;
|
||||
OrdersModel _model;
|
||||
OrderBookVM _vm;
|
||||
@@ -40,14 +34,13 @@ namespace mbts.spark {
|
||||
SparkCommand[] _commands;
|
||||
|
||||
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service,
|
||||
OrderBookStartupData state, ImageSource icon, IWindowSettingsManager<OrderBookGridSettings> settings_manager, bool can_edit_specific_algo_params)
|
||||
OrderBookStartupData state, ImageSource icon, IWindowSettingsManager<OrderBookGridSettings> settings_manager)
|
||||
: base(_default_title, bus, factory, SparkCommands.ShowOrders.icon) {
|
||||
_model = model;
|
||||
_factory = factory;
|
||||
_notification_service = notification_service;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
_can_edit_specific_algo_params = can_edit_specific_algo_params;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().order_book ? "OrderBook" : null;
|
||||
}
|
||||
|
||||
@@ -71,14 +64,12 @@ namespace mbts.spark {
|
||||
public string thread_id { get; }
|
||||
|
||||
public override void Dispose() {
|
||||
Disposable.dispose(_thread_init_resources);
|
||||
_commands.OfType<IDisposable>().DisposeAll();
|
||||
Disposable.dispose(ref _attachment_exit_sub);
|
||||
Disposable.dispose(ref _vm);
|
||||
}
|
||||
|
||||
public void thread_init() {
|
||||
Log.assert(_thread_init_resources == null, $"{nameof(_thread_init_resources)} must be initialized in thread_init.");
|
||||
_thread_init_resources = new List<IDisposable>();
|
||||
_menu_display_vm = new SparkMenuDisplayVM();
|
||||
if (_state != null)
|
||||
_menu_display_vm.load_from(_state.menu_display);
|
||||
@@ -156,15 +147,6 @@ namespace mbts.spark {
|
||||
can_execute: () => _vm.selected_orders.Any(x => passes_recreate_search_criteria(x))
|
||||
);
|
||||
recreate_selected_orders_command.group = CommandGroups.Modify;
|
||||
ICommand edit_algo_params_cmd;
|
||||
if (_can_edit_specific_algo_params) {
|
||||
_thread_init_resources.Add(CommandHelper.bind_param_source(
|
||||
new DialogCommand<OrderSecurityAlgo?, Window>(mk_algo_params_dialog, has_algo_params),
|
||||
_vm.subscribe_single_selected_order(),
|
||||
out edit_algo_params_cmd, true));
|
||||
} else {
|
||||
edit_algo_params_cmd = DelegateCommand.empty;
|
||||
}
|
||||
|
||||
var attach_selected_orders_command = new SparkCommandCommand(
|
||||
"Attach To Selected",
|
||||
@@ -212,22 +194,14 @@ namespace mbts.spark {
|
||||
show_grid_settings_command.group = CommandGroups.Auxiliary;
|
||||
|
||||
_commands = new SparkCommand[] {
|
||||
// modify commands
|
||||
toggle_show_right_panel_command,
|
||||
attach_selected_orders_command,
|
||||
detach_selected_orders_command,
|
||||
recreate_selected_orders_command,
|
||||
new SparkCommandCommand("Algo Params...", Icons.Algo, edit_algo_params_cmd) {
|
||||
group = CommandGroups.Modify,
|
||||
show_in_toolbar = false,
|
||||
show_in_context_menu = _can_edit_specific_algo_params
|
||||
},
|
||||
// cancel commands
|
||||
cancel_selected_orders_command,
|
||||
cancel_buy_orders_command,
|
||||
cancel_sell_orders_command,
|
||||
cancel_all_orders_command,
|
||||
|
||||
toggle_collapse_all_command,
|
||||
toggle_expand_all_command,
|
||||
show_grid_settings_command,
|
||||
@@ -236,12 +210,9 @@ namespace mbts.spark {
|
||||
hidden_cmd(new SparkCommandCommand(OrderBookView.CommandDescriptions.RevertOrders, null, _vm.revert_selected_orders_cmd, OrderBookView.KeyboardShortcuts.RevertOrders)),
|
||||
_menu_display_vm.spark_cmd
|
||||
};
|
||||
_thread_init_resources.AddRange(_commands.OfType<IDisposable>());
|
||||
_thread_init_resources.Add(_vm.as_demuxer()
|
||||
.subscribe<bool>(nameof(OrderBookVM.show_right_panel))
|
||||
.Subscribe(_ => toggle_show_right_panel_command.title = get_toggle_show_right_panel_cmd_title()));
|
||||
_vm.as_demuxer().subscribe<bool>(nameof(OrderBookVM.show_right_panel))
|
||||
.Subscribe(_ => toggle_show_right_panel_command.title = get_toggle_show_right_panel_cmd_title());
|
||||
_vm.selected_order_changed += vm_selected_order_changed;
|
||||
_thread_init_resources.Add(Disposable.Create(() => _vm.selected_order_changed -= vm_selected_order_changed));
|
||||
// local methods
|
||||
string get_toggle_show_right_panel_cmd_title() =>
|
||||
_vm.show_right_panel ? OrderBookView.CommandDescriptions.HideRightPanel : OrderBookView.CommandDescriptions.ShowRightPanel;
|
||||
@@ -263,36 +234,6 @@ namespace mbts.spark {
|
||||
attach_selected_orders_command.raiseCanExecuteChanged();
|
||||
detach_selected_orders_command.raiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
bool has_algo_params(OrderSecurityAlgo? order) =>
|
||||
order.HasValue && !OrderDataHelper.get_metadata(order.Value.algo.value.CustomCase).NoProps;
|
||||
|
||||
Window mk_algo_params_dialog(OrderSecurityAlgo? order) {
|
||||
if (!has_algo_params(order))
|
||||
return null;
|
||||
var algo_def = AlgoDefinitionVM.create(_model, order.Value.algo, editing_permissions: AlgoEditingPermissions.AllowEditValues);
|
||||
var algo_params_vm = new OrderBookAlgoParamsVM(algo_def, order.Value.order.is_non_terminal());
|
||||
var dialog = new OrderBookAlgoParamsDialog {
|
||||
Content = algo_params_vm
|
||||
};
|
||||
ui.WindowStyle.SetTitleContent(dialog, algo_def);
|
||||
dialog.Closed += (s, e) => {
|
||||
Disposable.dispose(ref algo_params_vm);
|
||||
dialog = null;
|
||||
};
|
||||
algo_params_vm.RequestCommit += commit;
|
||||
algo_params_vm.RequestClose += (s, e) => close_dialog();
|
||||
return dialog;
|
||||
|
||||
void close_dialog() {
|
||||
dialog?.Close();
|
||||
dialog = null;
|
||||
}
|
||||
void commit(CustomOrderDefinitionImmutable value) {
|
||||
_model.update_order(order.Value.order, new_algo: value).ignore();
|
||||
close_dialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParentViewCreated(SparkWindowView parent_view) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using bts.api;
|
||||
using bts.shell.orderbook;
|
||||
using bts.spark;
|
||||
using mbts.config;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
@@ -16,11 +15,8 @@ namespace bts.shell {
|
||||
readonly INotificationServiceDataCache _notification_service_data_cache;
|
||||
readonly OrdersModel _model;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
readonly OrderBookConfig _config;
|
||||
|
||||
public OrderBookWindowFactory(IShellModule owner, IBus bus, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, OrdersModel model, UserSettingsHub settings_hub) : base(owner, "SparkOrderBookWindow") {
|
||||
var raw_config = Configuration.get<bts.config.OrderBookOptions>();
|
||||
_config = new OrderBookConfig(raw_config.can_edit_specific_algo_params);
|
||||
_bus = bus;
|
||||
_notification_service = notification_service;
|
||||
_notification_service_data_cache = notification_service_data_cache;
|
||||
@@ -33,8 +29,7 @@ namespace bts.shell {
|
||||
var settings_manager = new WindowSettingsManager<OrderBookGridSettings>(_settings_hub, state.grid_settings_wrapper(),
|
||||
us => us.order_book_grid ?? OrderBookStartupData.DefaultGrid,
|
||||
(us, s) => us.with(order_book_grid: s));
|
||||
var w = new OrderBookWindow(_model, this, _bus, _notification_service, state,
|
||||
SparkCommands.ShowOrders.icon, settings_manager.as_non_shared(), _config.can_edit_specific_algo_params);
|
||||
var w = new OrderBookWindow(_model, this, _bus, _notification_service, state, SparkCommands.ShowOrders.icon, settings_manager.as_non_shared());
|
||||
return Task.FromResult<IShellWindow>(w);
|
||||
}
|
||||
|
||||
@@ -42,12 +37,5 @@ namespace bts.shell {
|
||||
var w = await load(startup_data);
|
||||
_bus.post(SparkTags.ShowWindow, w);
|
||||
}
|
||||
|
||||
readonly struct OrderBookConfig {
|
||||
public readonly bool can_edit_specific_algo_params;
|
||||
|
||||
public OrderBookConfig(bool can_edit_specific_algo_params) =>
|
||||
this.can_edit_specific_algo_params = can_edit_specific_algo_params;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<Window
|
||||
x:Class="bts.shell.orderbook.OrderBookAlgoParamsDialog"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:ob="clr-namespace:bts.shell.orderbook"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
u:WindowStyle.SizeToContent="WidthAndHeight"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
Style="{StaticResource BTSWindowTitleTemplate}"
|
||||
>
|
||||
<u:WindowStyle.TitleContentTemplate>
|
||||
<DataTemplate DataType="{x:Type sp:AlgoDefinitionVM}">
|
||||
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
|
||||
<usp:OrderTypeItem Margin="4,0" DisplayName="{Binding name, StringFormat='{}{0} Params'}" BadgeFill="{Binding badge_fill}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</u:WindowStyle.TitleContentTemplate>
|
||||
<Window.ContentTemplate>
|
||||
<DataTemplate DataType="{x:Type ob:OrderBookAlgoParamsVM}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<usp:AlgoEditorGridView
|
||||
Width="390"
|
||||
Margin="5"
|
||||
AlgoParamRows="{Binding param_rows}"
|
||||
CanUserChangeAlgoRoot="False"
|
||||
/>
|
||||
<c:DialogSkirt Grid.Row="1" RightItems="{Binding dialog_skirt}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Window.ContentTemplate>
|
||||
</Window>
|
||||
@@ -1,49 +0,0 @@
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace bts.shell.orderbook {
|
||||
/// <summary>
|
||||
/// Interaction logic for OrderBookAlgoParamsDialog.xaml
|
||||
/// </summary>
|
||||
partial class OrderBookAlgoParamsDialog : Window {
|
||||
public OrderBookAlgoParamsDialog() =>
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
class OrderBookAlgoParamsVM : DisposableViewModelBase, IRequestViewClose {
|
||||
readonly ObservableAggregateCollection<AlgoParamRowVM> _param_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
|
||||
readonly DialogSkirtItem _commit, _cancel;
|
||||
|
||||
public OrderBookAlgoParamsVM(AlgoDefinitionVM algo_definition, bool can_user_edit) {
|
||||
this.algo_definition = algo_definition;
|
||||
_dispose = AlgoParamRowsHelper.connect(algo_definition, _param_rows, false);
|
||||
_commit = new DialogSkirtItem("OK", new DelegateCommand(commit, () => can_user_edit)) { is_default = true };
|
||||
_cancel = new DialogSkirtItem("Cancel", new DelegateCommand(close)) { is_cancel = true };
|
||||
// local methods
|
||||
void commit() {
|
||||
RequestCommit?.Invoke(new CustomOrderDefinitionImmutable(algo_definition.algo_root.created_assigned()));
|
||||
close();
|
||||
}
|
||||
void close() => RequestClose?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler RequestClose;
|
||||
public event Action<CustomOrderDefinitionImmutable> RequestCommit;
|
||||
|
||||
public AlgoDefinitionVM algo_definition { get; }
|
||||
public IReadOnlyObservableCollection<AlgoParamRowVM> param_rows => _param_rows;
|
||||
public IEnumerable<DialogSkirtItem> dialog_skirt {
|
||||
get {
|
||||
yield return _commit;
|
||||
yield return _cancel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace mbts.spark
|
||||
{
|
||||
return;
|
||||
}
|
||||
var ev = fd.EnumType.Values.FirstOrDefault(x => x.Name == value)?.Number;
|
||||
var ev = fd.EnumType.Values.First(x => x.Name == value)?.Number;
|
||||
if(ev.HasValue)
|
||||
fd.Accessor.SetValue(message, ev.Value);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Reflection;
|
||||
using mbts.collections;
|
||||
@@ -14,19 +15,21 @@ using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reflection;
|
||||
using AlgoType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
internal class GenericOrderFactory {
|
||||
static public IAlgoVM create_vm(AlgoKey key, AlgoType algo_type, string display_name, AlgoEditingPermissions editing_permissions) {
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
internal class GenericOrderFactory
|
||||
{
|
||||
static public IAlgoVM create_vm(AlgoKey key, AlgoType algo_type, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
{
|
||||
var prop = typeof(CustomOrderDefinition).GetProperty(algo_type.ToString(),
|
||||
BindingFlags.Instance | BindingFlags.Public);
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||
var rt = prop.PropertyType;
|
||||
|
||||
var propi = typeof(CustomOrderDefinitionImmutable).GetProperty(algo_type.ToString(),
|
||||
BindingFlags.Instance | BindingFlags.Public);
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||
var rti = propi.PropertyType;
|
||||
|
||||
var vmt = typeof(GenericOrderVM<,>);
|
||||
@@ -35,33 +38,38 @@ namespace mbts.spark {
|
||||
object vm = Activator.CreateInstance(makevm, key, algo_type, display_name, editing_permissions);
|
||||
return (IAlgoVM)vm;
|
||||
}
|
||||
static public void setup_generic_custom_order(CustomOrderDefinition custom, CustomOrderDefinition.CustomOneofCase type) {
|
||||
static public void setup_generic_custom_order(CustomOrderDefinition custom, CustomOrderDefinition.CustomOneofCase type)
|
||||
{
|
||||
var prop = typeof(CustomOrderDefinition).GetProperty(type.ToString(),
|
||||
BindingFlags.Instance | BindingFlags.Public);
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||
var rt = prop.PropertyType;
|
||||
var rtt = Activator.CreateInstance(rt);
|
||||
|
||||
|
||||
var ci = rt.GetProperties().Where(x => x.PropertyType == typeof(CustomOrderDefinition));
|
||||
foreach (var cid in ci)
|
||||
foreach(var cid in ci)
|
||||
cid.SetValue(rtt, new CustomOrderDefinition { Limit = new LimitOrderDefinition() });
|
||||
prop.SetValue(custom, rtt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GenericOrderVM<TMutable, TImmutable> : OrderTypeVMBase<TMutable, TImmutable>
|
||||
where TMutable : class, IMessage, new() where TImmutable : struct, IImmutableMessage {
|
||||
where TMutable : class, IMessage, new() where TImmutable : struct, IImmutableMessage
|
||||
{
|
||||
readonly ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string> _parameters =
|
||||
new ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string>(vm => vm.descriptor.Name);
|
||||
readonly AlgoType _algo_type;
|
||||
|
||||
public GenericOrderVM(AlgoKey key, AlgoType algo_type, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
: base(key, display_name, editing_permissions) {
|
||||
: base(key, display_name, editing_permissions)
|
||||
{
|
||||
_algo_type = algo_type;
|
||||
_parameters = new ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string>(vm => vm.descriptor.Name);
|
||||
_resources.AddRange(
|
||||
ObservableAutoWrapper.connect_item_hooks(_parameters, p => {
|
||||
ObservableAutoWrapper.connect_item_hooks(_parameters, p =>
|
||||
{
|
||||
p.PropertyChanged += on_param_prop_changed;
|
||||
return Disposable.Create(() => {
|
||||
return Disposable.Create(() =>
|
||||
{
|
||||
p.PropertyChanged -= on_param_prop_changed;
|
||||
remove_property(p.descriptor.Name);
|
||||
p.Dispose();
|
||||
@@ -72,74 +80,84 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public string algo_type => _algo_type.ToString();
|
||||
public override string abbreviation =>
|
||||
public override string abbreviation =>
|
||||
model == null ? display_name : model.factory.abbreviate_algo(new OneOf<CustomOrderDefinition.CustomOneofCase, string>(_algo_type));
|
||||
public override AlgoDisplayGroup group => AlgoDisplayGroup.API;
|
||||
public ObservableCollection<GenericOrderParamVM<TMutable>> parameters => _parameters;
|
||||
|
||||
protected override void on_model_internal() =>
|
||||
protected override void on_model_internal()
|
||||
{
|
||||
_sync_generic_params();
|
||||
}
|
||||
|
||||
void _sync_generic_params() {
|
||||
if (model != null) {
|
||||
MessageDescriptor desc = new TMutable().Descriptor;
|
||||
void _sync_generic_params()
|
||||
{
|
||||
if (model != null)
|
||||
{
|
||||
Google.Protobuf.Reflection.MessageDescriptor desc = new TMutable().Descriptor;
|
||||
_parameters.SyncWithDictionary(
|
||||
desc.Fields.InDeclarationOrder().Where(_is_value_param_desc).ToDictionary(p => p.Name), _mk_param_vm);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _is_value_param_desc(FieldDescriptor desc) =>
|
||||
(desc.FieldType != FieldType.Message ||
|
||||
static bool _is_value_param_desc(FieldDescriptor desc)
|
||||
{
|
||||
return (desc.FieldType != Google.Protobuf.Reflection.FieldType.Message ||
|
||||
_is_quantity_measure_desc(desc));
|
||||
|
||||
static bool _is_quantity_measure_desc(FieldDescriptor desc) =>
|
||||
desc.FieldType == FieldType.Message &&
|
||||
desc.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName;
|
||||
|
||||
protected override IEnumerable<ChildAlgoData> get_selector_data() =>
|
||||
OrderDataHelper.get_child_algo_data(_algo_type);
|
||||
|
||||
GenericOrderParamVM<TMutable> _mk_param_vm(FieldDescriptor descriptor) {
|
||||
ExceptionAssert.Argument.NotNull(descriptor, nameof(descriptor));
|
||||
if (descriptor.FieldType == FieldType.Message &&
|
||||
descriptor.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName) {
|
||||
var qm = new GenericOrderQuantityVM<TMutable>(descriptor);
|
||||
return attached(qm, GenericOrderHelper.get_quantity_value, GenericOrderHelper.set_quantity_value);
|
||||
} else if (descriptor.FieldType == FieldType.Enum) {
|
||||
return EnumParamFactory.create(this, descriptor);
|
||||
} else if (descriptor.FieldType == FieldType.Bool) {
|
||||
return mk<bool?, bool>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
} else if (descriptor.FieldType == FieldType.Double || descriptor.FieldType == FieldType.Float) {
|
||||
return mk<double, double>(descriptor, v => v, v => v, double.NaN);
|
||||
} else if (descriptor.FieldType == FieldType.Int32) {
|
||||
return mk<int?, int>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
} else if (descriptor.FieldType == FieldType.UInt32) {
|
||||
return mk<uint?, uint>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
} else if (descriptor.FieldType == FieldType.String) {
|
||||
return mk<string, string>(descriptor, v => v, v => v);
|
||||
} else return null;
|
||||
}
|
||||
|
||||
GenericOrderParamVM<TMutable, TView, TModel> mk<TView, TModel>(
|
||||
FieldDescriptor fdesc,
|
||||
Func<TModel, TView> convert,
|
||||
Func<TView, TModel> convert_back,
|
||||
TView default_value = default) {
|
||||
//
|
||||
static bool _is_quantity_measure_desc(FieldDescriptor desc)
|
||||
{
|
||||
return desc.FieldType == Google.Protobuf.Reflection.FieldType.Message &&
|
||||
desc.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName;
|
||||
}
|
||||
|
||||
protected override IEnumerable<ChildAlgoData> get_selector_data()
|
||||
{
|
||||
var v = OrderDataHelper.get_child_algo_data(_algo_type);
|
||||
return v;
|
||||
}
|
||||
|
||||
GenericOrderParamVM<TMutable> _mk_param_vm(FieldDescriptor descriptor)
|
||||
{
|
||||
ExceptionAssert.Argument.NotNull(descriptor, nameof(descriptor));
|
||||
|
||||
if (descriptor.FieldType == FieldType.Message &&
|
||||
descriptor.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName)
|
||||
{
|
||||
var qm = new GenericOrderQuantityVM<TMutable>(descriptor);
|
||||
return attached(qm, GenericOrderHelper.get_quantity_value, GenericOrderHelper.set_quantity_value);
|
||||
}
|
||||
else if(descriptor.FieldType == FieldType.Enum)
|
||||
{
|
||||
var pvm = new GenericOrderEnumeratedVM<TMutable>(descriptor);
|
||||
attached(pvm, GenericOrderHelper.get_enumerated_value, GenericOrderHelper.set_enumerated_value);
|
||||
return pvm;
|
||||
}
|
||||
else if (descriptor.FieldType == FieldType.Bool)
|
||||
return mk<bool?, bool>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
else if (descriptor.FieldType == FieldType.Double || descriptor.FieldType == FieldType.Float)
|
||||
return mk<double, double>(descriptor, v => v, v => v, double.NaN);
|
||||
else if (descriptor.FieldType == FieldType.Int32)
|
||||
return mk<int?, int>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
else if (descriptor.FieldType == FieldType.UInt32)
|
||||
return mk<uint?, uint>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
else if (descriptor.FieldType == FieldType.String)
|
||||
return mk<string, string>(descriptor, v => v, v => v);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
GenericOrderParamVM<TMutable, TView, TModel> mk<TView, TModel>(FieldDescriptor fdesc, Func<TModel, TView> convert, Func<TView, TModel> convert_back, TView default_value = default)
|
||||
{
|
||||
var pvm = new GenericOrderParamVM<TMutable, TView, TModel>(fdesc, convert, convert_back);
|
||||
return attached(pvm, get_from_param, assign_to_param, default_value);
|
||||
return attached<TView, GenericOrderParamVM<TMutable, TView, TModel>>(pvm, get_from_param, assign_to_param, default_value);
|
||||
|
||||
TView get_from_param(IMessage param, FieldDescriptor fd) => convert(ConvertUtils.try_convert<TModel>(fdesc.Accessor.GetValue(param)));
|
||||
void assign_to_param(IMessage param, FieldDescriptor fd, TView value) => fdesc.Accessor.SetValue(param, convert_back(value));
|
||||
}
|
||||
|
||||
TParam attached<T, TParam>(
|
||||
TParam pvm,
|
||||
Func<IMessage, FieldDescriptor, T>
|
||||
selector, Action<IMessage, FieldDescriptor, T> assign_to_param,
|
||||
T default_value = default)
|
||||
where TParam : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<T> {
|
||||
//
|
||||
add_property(
|
||||
TParam attached<T, TParam>(TParam pvm, Func<IMessage, FieldDescriptor, T> selector, Action<IMessage, FieldDescriptor, T> assign_to_param, T default_value = default) where TParam : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<T>
|
||||
{
|
||||
add_property<T>(
|
||||
pvm.descriptor.Name,
|
||||
mdef => mdef != null ? selector(mdef, pvm.descriptor) : default_value,
|
||||
(mdef, v) => assign_to_param(mdef, pvm.descriptor, v),
|
||||
@@ -148,37 +166,7 @@ namespace mbts.spark {
|
||||
pvm.subscribe_value(),
|
||||
default_value);
|
||||
return pvm;
|
||||
}
|
||||
|
||||
static class EnumParamFactory {
|
||||
[ThreadStatic]
|
||||
static Dictionary<Type, IFactory> _factories_backing;
|
||||
static Dictionary<Type, IFactory> _factories =>
|
||||
_factories_backing ?? (_factories_backing = new Dictionary<Type, IFactory>());
|
||||
|
||||
public static GenericOrderParamVM<TMutable> create(GenericOrderVM<TMutable, TImmutable> owner, FieldDescriptor descriptor) {
|
||||
if (descriptor.FieldType != FieldType.Enum)
|
||||
throw ExceptionBuilder.Argument.unexpected_case(descriptor.FieldType, $"{nameof(descriptor)}.{nameof(FieldDescriptor.FieldType)}");
|
||||
if (!_factories.TryGetValue(descriptor.EnumType.ClrType, out var f)) {
|
||||
// Impl<T> is a nested type, so parent class's generic type args must be included.
|
||||
_factories[descriptor.EnumType.ClrType] = f = (IFactory)Activator.CreateInstance(typeof(Impl<>)
|
||||
.MakeGenericType(typeof(TMutable), typeof(TImmutable), descriptor.EnumType.ClrType));
|
||||
}
|
||||
return f.create(owner, descriptor);
|
||||
}
|
||||
|
||||
interface IFactory {
|
||||
GenericOrderParamVM<TMutable> create(GenericOrderVM<TMutable, TImmutable> owner, FieldDescriptor descriptor);
|
||||
}
|
||||
|
||||
class Impl<T> : IFactory where T : struct {
|
||||
static readonly Func<T, T?> _convert = v => v;
|
||||
static readonly Func<T?, T> _convert_back = v => v ?? default;
|
||||
|
||||
public GenericOrderParamVM<TMutable> create(GenericOrderVM<TMutable, TImmutable> owner, FieldDescriptor descriptor) =>
|
||||
owner.mk(descriptor, _convert, _convert_back, default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@
|
||||
<RuntimeIdentifiers>win7-x64</RuntimeIdentifiers>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<LanguageTargets>$(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets</LanguageTargets>
|
||||
<LanguageTargets Condition="'$(VisualStudioVersion)'=='16.0'">$(MSBuildToolsPath)\Microsoft.CSharp.targets</LanguageTargets>
|
||||
<ApplicationIcon>logo.ico</ApplicationIcon>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<StartupObject>mbts.ui.shell.App</StartupObject>
|
||||
|
||||
@@ -63,20 +63,5 @@ namespace mbts {
|
||||
|
||||
public static double? nan_nullable(double value) =>
|
||||
double.IsNaN(value) ? (double?)null : value;
|
||||
|
||||
public static int? to_optional_int32(double value) {
|
||||
if (double.IsNaN(value) || double.IsInfinity(value))
|
||||
return null;
|
||||
if (value > 0) {
|
||||
value = Math.Floor(value);
|
||||
if (value <= int.MaxValue)
|
||||
return (int)value;
|
||||
} else if (value < 0) {
|
||||
value = Math.Ceiling(value);
|
||||
if (value >= int.MinValue)
|
||||
return (int)value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1005,8 +1005,6 @@
|
||||
ContractPickerModel="{Binding contract_picker_model}"
|
||||
HedgePricing="{Binding hedge_pricing}"
|
||||
HedgePricingItemsSource="{Binding hedge_pricing_options}"
|
||||
UseFractionalTicks="{Binding use_fractional_ticks}"
|
||||
IntegerTickSize="{Binding integer_tick_size}"
|
||||
/>
|
||||
<c:DialogSkirt Grid.Row="1" Margin="-4" VerticalAlignment="Top" RightItems="{Binding skirt_items}"/>
|
||||
</Grid>
|
||||
|
||||
@@ -12,9 +12,6 @@ namespace mbts.ui.converters
|
||||
public Visibility VisibilityForFalse { get { return _visfalse; } set { _visfalse = value; } }
|
||||
|
||||
public static BooleanToVisibilityConverter Instance => new BooleanToVisibilityConverter();
|
||||
public static BooleanToVisibilityConverter HiddenInstance => new BooleanToVisibilityConverter {
|
||||
VisibilityForFalse = Visibility.Hidden
|
||||
};
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
|
||||
bool flag = false;
|
||||
|
||||
@@ -49,28 +49,6 @@ namespace mbts.spark {
|
||||
public static double parse(this ContractSpecificationImmutable? spec, string text, double display_multiplier, string nan_text) =>
|
||||
spec.spec_or_default().parse(text, display_multiplier, nan_text);
|
||||
|
||||
public static bool is_integer_tick(ContractSpecificationImmutable spec) =>
|
||||
spec.MainFraction == 0 &&
|
||||
spec.PriceDisplayFormat == 0 &&
|
||||
spec.LowTick > 0 &&
|
||||
MathUtils.is_int(spec.LowTick) &&
|
||||
spec.HighTick > 0 &&
|
||||
MathUtils.is_int(spec.HighTick);
|
||||
|
||||
public static ContractSpecification create_integer_tick_spec(int low_tick) =>
|
||||
create_integer_tick_spec(low_tick, low_tick);
|
||||
|
||||
public static ContractSpecification create_integer_tick_spec(int low_tick, int high_tick) {
|
||||
var rv = SecurityDataHelper.Defaults.ContractSpecification;
|
||||
rv.MainFraction = 0;
|
||||
rv.SubFraction = 0;
|
||||
rv.PriceDisplayFormat = 0;
|
||||
rv.PriceBreak = 0;
|
||||
rv.LowTick = low_tick;
|
||||
rv.HighTick = high_tick;
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static double low_tick(NativePriceSize[] bids, NativePriceSize[] asks) {
|
||||
double low = bids[bids.Length - 1].price;
|
||||
if (double.IsNaN(low))
|
||||
@@ -348,7 +326,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
static bool _check_tick_size(double tick_size) =>
|
||||
tick_size > 0 && (tick_size <= 0.5 || MathUtils.is_int(tick_size));
|
||||
(tick_size > 0 && tick_size <= 0.5) || tick_size == 1;
|
||||
|
||||
struct SpecKey : IEquatable<SpecKey> {
|
||||
public readonly uint denominator, display_digits;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using mbts.ui.converters;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
@@ -14,31 +12,6 @@ namespace mbts.ui.utilities {
|
||||
public static string fmt(string title, KeyGesture key_gesture) =>
|
||||
key_gesture == null ? title : $"{title} ({key_gesture.GetDisplayStringForCulture(CultureInfo.CurrentCulture)})";
|
||||
|
||||
public static IDisposable bind_param_source<T>(ICommand command, IObservable<T> param_source, out ICommand bound_cmd, bool dispose_command_on_dispose = false) {
|
||||
bool initialized = false;
|
||||
bool can_execute = false;
|
||||
T param = default;
|
||||
var bcmd = new DelegateCommand(() => command.Execute(param), () => can_execute);
|
||||
bound_cmd = bcmd;
|
||||
var sub = param_source.Subscribe(p => {
|
||||
param = p;
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
can_execute = command.CanExecute(p);
|
||||
bcmd.RaiseCanExecuteChanged();
|
||||
} else if (can_execute != command.CanExecute(p)) {
|
||||
can_execute = !can_execute;
|
||||
bcmd.RaiseCanExecuteChanged();
|
||||
}
|
||||
});
|
||||
return dispose_command_on_dispose ?
|
||||
Disposable.Create(() => {
|
||||
Disposable.dispose(ref sub);
|
||||
(command as IDisposable)?.Dispose();
|
||||
command = null;
|
||||
}) : sub;
|
||||
}
|
||||
|
||||
#region Paraphrased from https://referencesource.microsoft.com/#PresentationFramework/src/Framework/MS/Internal/Commands/CommandHelpers.cs
|
||||
|
||||
public static bool CanExecuteCommandSource(ICommandSource commandSource) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/Product.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityMapper.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/service/MarketDataServiceServer.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;md/vendor/OnixsUtils.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metricsLine truncated
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/Product.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityMapper.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/service/MarketDataServiceServer.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;md/vendor/OnixsUtils.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metricsLine truncated
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python2
|
||||
import os, sys
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
@@ -104,7 +104,6 @@ message CustomOrderDefinition
|
||||
SplitterOrderDefinition splitter = 29;
|
||||
ScratchOrderDefinition scratch = 31;
|
||||
ProRataOrderDefinition prorata = 32;
|
||||
DeleteAndCoverDefinition delete_and_cover = 34;
|
||||
|
||||
DynamicOrderDefinition dynamic = 1000;
|
||||
}
|
||||
@@ -145,13 +144,6 @@ message TickOrderDefinition {
|
||||
bool detach_on_toward = 5;
|
||||
CustomOrderDefinition child = 6;
|
||||
}
|
||||
enum LeanOnToward
|
||||
{
|
||||
NO_CHANGE = 0; //continue to lean on initial inside market
|
||||
CANCEL = 1; //cancel algo and order
|
||||
DETACH = 2; //cancel algo but leave order working
|
||||
REPRICE = 3; //lean on new inside market
|
||||
}
|
||||
message LeanOrderDefinition {
|
||||
spark.QuantityMeasure cross_threshold = 1;
|
||||
uint32 max_chase_ticks = 2;
|
||||
@@ -187,21 +179,6 @@ message CoverOrderDefinition {
|
||||
CustomOrderDefinition child = 3;
|
||||
bool cancel_on_external_hedge_cancel = 4;
|
||||
bool cancel_hedges_on_cancel = 5;
|
||||
bool detach_hedges = 6;
|
||||
}
|
||||
message DeleteAndCoverDefinition{
|
||||
// cover
|
||||
uint32 hedge_ticks = 1;
|
||||
bool cancel_on_external_hedge_cancel = 2;
|
||||
bool cancel_hedges_on_cancel = 3;
|
||||
bool detach_hedges = 4;
|
||||
// main child = top
|
||||
spark.QuantityMeasure top_threshold = 10;
|
||||
uint32 ticks_off = 11;
|
||||
// hedge child = Lean
|
||||
spark.QuantityMeasure cross_threshold = 20;
|
||||
uint32 max_chase_ticks = 21;
|
||||
LeanOnToward trade_toward_behavior = 22;
|
||||
}
|
||||
message OffsetOrderDefinition {
|
||||
uint32 tick_offset_on_deploy = 1;
|
||||
|
||||
@@ -76,7 +76,6 @@ message LadderKnobs {
|
||||
message OrderBookOptions {
|
||||
optional bool animate_child_order_rows = 1 [default = false];
|
||||
optional double status_color_opacity_when_selected = 2 [default = 0.35];
|
||||
optional bool can_edit_specific_algo_params = 3 [default = false];
|
||||
}
|
||||
|
||||
message OrderTicketOptions {
|
||||
|
||||
@@ -68,5 +68,4 @@ message DynamicAlgos{
|
||||
optional bool enable_pyramid = 2 [default = false];
|
||||
optional bool enable_tas = 3 [default = false];
|
||||
optional bool enable_tascounter = 4 [default = false];
|
||||
optional bool enable_limit_cover = 5 [default = false];
|
||||
}
|
||||
@@ -390,7 +390,7 @@ TEST_CASE("Configuration Tests","[config]") {
|
||||
" \xA0 }"
|
||||
"} ", false);
|
||||
}
|
||||
catch(const bts::config::ConfigurationException)
|
||||
catch(const bts::config::ConfigurationException &)
|
||||
{
|
||||
parse_error = true;
|
||||
}
|
||||
|
||||
@@ -41,16 +41,16 @@ inline api::Side other_side(api::Side side) {
|
||||
}
|
||||
}
|
||||
|
||||
inline double market_epsilon() { return 1e-10; }
|
||||
inline double market_epislon() { return 1e-10; }
|
||||
|
||||
// better == p1 more aggressive than p1 ie for a buy order p1 is higher
|
||||
// than p2, and for a sell order p1 is lower than p2
|
||||
inline bool is_better(api::Side side, Price p1, Price p2) {
|
||||
switch (side) {
|
||||
case api::SIDE_BUY:
|
||||
return p1.value() - p2.value() > market_epsilon();
|
||||
return p1.value() - p2.value() > market_epislon();
|
||||
case api::SIDE_SELL:
|
||||
return p2.value() - p1.value() > market_epsilon();
|
||||
return p2.value() - p1.value() > market_epislon();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ inline Price worsen_price(api::Side side, Price p, Price offset) {
|
||||
}
|
||||
|
||||
inline bool is_same(Price p1, Price p2) {
|
||||
return std::abs(p1.value() - p2.value()) < market_epsilon();
|
||||
return std::abs(p1.value() - p2.value()) < market_epislon();
|
||||
}
|
||||
|
||||
inline bool is_equal_or_better(api::Side side, Price p1, Price p2) {
|
||||
@@ -212,5 +212,5 @@ inline bool gr_than(double p1, double p2, double tick_size) {
|
||||
!test_price_equality(p1, p2, tick_size);
|
||||
}
|
||||
|
||||
} // namespace securities
|
||||
} // namespace bts
|
||||
|
||||
}}
|
||||
@@ -37,7 +37,12 @@ class IntPrice {
|
||||
constexpr IntPrice() : _mantissa(0), _exponent(127) {/*nan*/}
|
||||
constexpr IntPrice(int64_t mantissa, int8_t exponent) : _mantissa(mantissa), _exponent(exponent) {}
|
||||
constexpr IntPrice(const IntPrice& other) : _mantissa(other._mantissa), _exponent(other._exponent) {}
|
||||
|
||||
IntPrice& operator=(IntPrice const&other) {
|
||||
_mantissa = other._mantissa;
|
||||
_exponent = other._exponent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const IntPrice& other) const {
|
||||
if(_exponent == other._exponent) {
|
||||
return _mantissa == other._mantissa;
|
||||
@@ -183,4 +188,4 @@ inline std::ostream& operator<<(std::ostream& os, const IntPrice& p) {
|
||||
return os;
|
||||
}
|
||||
|
||||
}}
|
||||
}}
|
||||
@@ -12,13 +12,11 @@ using small_vector = boost::container::small_vector<T,4>;
|
||||
namespace bts {
|
||||
namespace md {
|
||||
|
||||
MDConsumerHandle::MDConsumerHandle() {
|
||||
memset(this, 0, sizeof(MDConsumerHandle));
|
||||
_type = HANDLETYPE_VOID;
|
||||
MDConsumerHandle::MDConsumerHandle() : _type(HANDLETYPE_VOID), book() {
|
||||
// value initializing 'book' zeroes the union
|
||||
static_assert(sizeof(MDConsumerHandle) <= 40, "");
|
||||
}
|
||||
MDConsumerHandle::MDConsumerHandle(const std::shared_ptr<const SyntheticStrategy>& def) {
|
||||
memset(this, 0, sizeof(MDConsumerHandle));
|
||||
MDConsumerHandle::MDConsumerHandle(const std::shared_ptr<const SyntheticStrategy>& def) : _type(HANDLETYPE_VOID), book() {
|
||||
synthetic.def = def;
|
||||
_type = HANDLETYPE_SYNTHETIC;
|
||||
for(size_t x = 0; x < def->legs.size(); ++x) {
|
||||
@@ -26,9 +24,7 @@ MDConsumerHandle::MDConsumerHandle(const std::shared_ptr<const SyntheticStrategy
|
||||
}
|
||||
}
|
||||
|
||||
MDConsumerHandle::MDConsumerHandle(const MDConsumerHandle& other) {
|
||||
memset(this, 0, sizeof(MDConsumerHandle));
|
||||
_type = HANDLETYPE_VOID;
|
||||
MDConsumerHandle::MDConsumerHandle(const MDConsumerHandle& other): _type(HANDLETYPE_VOID), book() {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
|
||||
@@ -390,6 +390,8 @@ private:
|
||||
std::shared_ptr<const SyntheticStrategy> def;
|
||||
};
|
||||
HandleType _type;
|
||||
// NOTE: If we ever have to add to this union
|
||||
// it would be best to convert it to an std::variant()
|
||||
union {
|
||||
DepthHandle book;
|
||||
BBOHandle bbo;
|
||||
|
||||
@@ -50,7 +50,12 @@ public:
|
||||
|
||||
// Calculating the size for the variable sized entries is tricky
|
||||
ColdEntry* c_entry = new (_cold.add(ColdEntry::fixed_size() + security.size)) ColdEntry(security.size);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
// Security here is a variable sized structure (may be FixedFuture/FixedOption)
|
||||
// So we have to memcpy to keep the extra fields
|
||||
memcpy(&c_entry->security, &security, security.size);
|
||||
#pragma GCC diagnostic pop
|
||||
smapper.apply(c_entry->security);
|
||||
|
||||
MDProducerHandle handle(_cold.header(), _hot.header(), *c_entry, h_entry, MDProducerChannel(*this));
|
||||
|
||||
@@ -19,7 +19,7 @@ class BTS_EXPORT MDProducerHandle
|
||||
public:
|
||||
MDProducerHandle(ColdHeader&, HotHeader&, ColdEntry&, void* hot, MDProducerChannel channel);
|
||||
~MDProducerHandle();
|
||||
|
||||
MDProducerHandle(const MDProducerHandle &src) {operator=(src);}
|
||||
MDProducerHandle& operator=(MDProducerHandle const& other);
|
||||
|
||||
const FixedSecurity& security() const { return cdata().security; }
|
||||
|
||||
@@ -10,7 +10,13 @@ struct UserSymbol {
|
||||
_symbol(s), _exchange(e), _type(t) {}
|
||||
UserSymbol(const UserSymbol& other) :
|
||||
_symbol(other._symbol), _exchange(other._exchange), _type(other._type) {}
|
||||
|
||||
UserSymbol& operator=(UserSymbol const&other) {
|
||||
_symbol = other._symbol;
|
||||
_exchange = other._exchange;
|
||||
_type = other._type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::string& symbol() const { return _symbol; }
|
||||
api::Venue exchange() const { return _exchange; }
|
||||
SecurityType type() const { return _type; }
|
||||
|
||||
@@ -17,7 +17,12 @@ Co2TestInstrument::Co2TestInstrument(const std::string& symbol, SecurityType typ
|
||||
fsec.exchange = api::VENUE_BTS;
|
||||
_cold_buffer = std::shared_ptr<uint8_t>(new uint8_t[ColdEntry::fixed_size() + fsec.size], std::default_delete<uint8_t[]>());
|
||||
_cold = new (_cold_buffer.get()) ColdEntry(fsec.size);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
// Security here is a variable sized structure (may be FixedFuture/FixedOption)
|
||||
// So we have to memcpy to keep the extra fields
|
||||
memcpy(&_cold->security, &fsec, fsec.size);
|
||||
#pragma GCC diagnostic pop
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,7 +38,12 @@ Co2TestInstrument::Co2TestInstrument(const Security& security, std::shared_ptr<C
|
||||
convert_security(security, fsec, false);
|
||||
_cold_buffer = std::shared_ptr<uint8_t>(new uint8_t[ColdEntry::fixed_size() + fsec.size], std::default_delete<uint8_t[]>());
|
||||
_cold = new (_cold_buffer.get()) ColdEntry(fsec.size);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
// Security here is a variable sized structure (may be FixedFuture/FixedOption)
|
||||
// So we have to memcpy to keep the extra fields
|
||||
memcpy(&_cold->security, &fsec, fsec.size);
|
||||
#pragma GCC diagnostic pop
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ public:
|
||||
REQUIRE(db);
|
||||
|
||||
SecurityIDRecorder idsvc { {db, [&](ExchangeKey k) -> SecurityKey {
|
||||
for (const auto fut : { f1handle->producer(), f2handle->producer() }) {
|
||||
for (const auto &fut : { f1handle->producer(), f2handle->producer() }) {
|
||||
if (k.value() == fut.security().numeric_id) return SecurityKey(fut.key());
|
||||
}
|
||||
return SecurityKey(invalid_security_key);
|
||||
@@ -2392,4 +2392,4 @@ TEST_CASE("IntPrice", "[md]") {
|
||||
|
||||
|
||||
}}}
|
||||
#endif
|
||||
#endif
|
||||
@@ -17,6 +17,10 @@ class MDRequestEvent : public mercury::Event<MDRequestEvent> {
|
||||
MDRequestEvent(SecurityKey k, MDRequestType md_type)
|
||||
: security_key(k), md_type(md_type) {}
|
||||
|
||||
MDRequestEvent(MDRequestEvent const&src) {
|
||||
operator=(src);
|
||||
}
|
||||
|
||||
MDRequestEvent &operator=(MDRequestEvent const &rhs) {
|
||||
security_key = rhs.security_key;
|
||||
md_type = rhs.md_type;
|
||||
|
||||
@@ -12,6 +12,9 @@ class SettlementRequestEvent : public mercury::Event<SettlementRequestEvent> {
|
||||
public:
|
||||
using SynopsisPolicy = mercury::CacheAll<SettlementRequestEvent>;
|
||||
SettlementRequestEvent(SecurityKey k) : security_key(k) {}
|
||||
SettlementRequestEvent(SettlementRequestEvent const&src) {
|
||||
operator=(src);
|
||||
}
|
||||
SettlementRequestEvent &operator=(SettlementRequestEvent const&rhs)
|
||||
{
|
||||
security_key = rhs.security_key;
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/OffsetAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/ScratchAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/ProRataAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/DeleteAndCoverAlgo.h"
|
||||
#include "lib/orderman/orders/book_algos/OCOOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/PricerOrder.h"
|
||||
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
|
||||
@@ -243,10 +242,6 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
order_ptr.reset(
|
||||
new orderman::AlgoWrapperOrder(std::make_unique<splitter::SplitterAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::kDeleteAndCover:
|
||||
order_ptr.reset(
|
||||
new orderman::AlgoWrapperOrder(std::make_unique<DeleteAndCoverAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
bts_assert_msg(false, "Cannot create order with unknown type");
|
||||
break;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "BookUtils.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
using Size = bts::md::Size;
|
||||
using Side = bts::api::Side;
|
||||
|
||||
Size BookUtils::volume_at_or_better(const Book& book, Side side, Price price) {
|
||||
return volume_at_or_better(book, side, price.value());
|
||||
}
|
||||
Size BookUtils::volume_at_or_better(const Book& book, Side side, double price) {
|
||||
Size volume = 0;
|
||||
SidePriceComp spc(side);
|
||||
for (const auto& plevel : book.side(side)) {
|
||||
if (spc(price, plevel.price()))
|
||||
break;
|
||||
volume += plevel.size();
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
SidePriceComp BookUtils::price_comp(Side side) {
|
||||
return SidePriceComp(side);
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/config/protos/MarketData.pb.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/domain_model/common.h"
|
||||
#include "lib/orderman/orderman_utils/Book.h"
|
||||
#include "lib/orderman/orderman_utils/PriceComp.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
struct BookUtils {
|
||||
static md::Size volume_at_or_better(const Book& book, api::Side side, Price price);
|
||||
static md::Size volume_at_or_better(const Book& book, api::Side side, double price);
|
||||
static SidePriceComp price_comp(api::Side side);
|
||||
};
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,283 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/config/protos/MarketData.pb.h"
|
||||
#include "lib/domain_model/common.h"
|
||||
#include "lib/md/PriceSize.h"
|
||||
#include "lib/orderman/orderman_utils/Book.h"
|
||||
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
struct Formatter {
|
||||
using Side = bts::api::Side;
|
||||
// this is a placeholder for other book formats down the road.
|
||||
enum class BookFormat { BBO = 1 };
|
||||
enum class Tense {
|
||||
Present,
|
||||
Preterite
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline static void concat(std::ostream& ostr, const T& v) {
|
||||
ostr << v;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline static void concat(std::ostream& ostr, const T& first, Args... args) {
|
||||
ostr << first;
|
||||
(concat(ostr, args), ...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static std::string fmt(const T& curr) {
|
||||
std::stringstream str;
|
||||
str << curr;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline static std::string fmt(const T& curr, Args... next) {
|
||||
std::stringstream str;
|
||||
concat(str, curr, next...);
|
||||
return str.str();
|
||||
}
|
||||
|
||||
template <class TKey, class TValue>
|
||||
static std::string fmt_values(const std::map<TKey, TValue>& map) {
|
||||
if (map.empty())
|
||||
return "None";
|
||||
std::stringstream str;
|
||||
auto it = map.begin();
|
||||
str << it->second;
|
||||
while (++it != map.end()) {
|
||||
str << ", " << it->second;
|
||||
}
|
||||
return str.str();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static std::string fmt_values(const std::vector<T>& values) {
|
||||
if (values.empty())
|
||||
return "None";
|
||||
std::stringstream str;
|
||||
auto it = values.begin();
|
||||
str << *it;
|
||||
while (++it != values.end()) {
|
||||
str << ", " << *it;
|
||||
}
|
||||
return str.str();
|
||||
}
|
||||
|
||||
static std::string fmt_fill(Side side, uint32_t qty, double price) {
|
||||
std::stringstream ostr;
|
||||
ostr << to_string(side, Tense::Preterite) << " " << qty << " @ " << price;
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
static std::string fmt_order(Side side, uint32_t qty, double price) {
|
||||
std::stringstream ostr;
|
||||
ostr << to_string(side) << " " << qty << " @ " << price;
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
static std::string fmt_order(OrderKey key, uint32_t qty, double price, const api::orderman::OrderDefinition& def) {
|
||||
std::stringstream ostr;
|
||||
ostr << key << " " << to_string(def.side()) << " " << qty << " @ "
|
||||
<< price << std::endl << "[Def]" << utils::pb::to_string(def);
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
static std::string fmt_order(OrderKey key, Side side, uint32_t qty, double price, ChildState state) {
|
||||
std::stringstream ostr;
|
||||
ostr << key << " " << to_string(side) << " " << qty << " @ " << price
|
||||
<< " " << to_string(state);
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T, typename... Args>
|
||||
inline static void join_next(std::stringstream&, const std::string&) { }
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline static void join_next(std::stringstream&, std::function<std::string()>) { }
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline static void join_next(std::stringstream& str, const std::string& separator, const T& curr) {
|
||||
str << separator << curr;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline static void join_next(std::stringstream& str, std::function<std::string()> get_separator, const T& curr) {
|
||||
str << get_separator() << curr;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline static void join_next(std::stringstream& str, const std::string& separator, const T& curr, Args... next) {
|
||||
str << separator << curr;
|
||||
(join_next(str, separator, next), ...);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline static void join_next(std::stringstream& str, std::function<std::string()> get_separator, const T& curr, Args... next) {
|
||||
str << get_separator() << curr;
|
||||
(join_next(str, get_separator, next), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename T, typename... Next>
|
||||
inline static std::string join(const std::string&, const T& curr) {
|
||||
std::stringstream str;
|
||||
str << curr;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
template<typename T, typename... Next>
|
||||
inline static std::string join(const std::string& separator, const T& curr, Next... next) {
|
||||
std::stringstream str;
|
||||
str << curr;
|
||||
join_next(str, separator, next...);
|
||||
return str.str();
|
||||
}
|
||||
|
||||
template<typename T, typename... Next>
|
||||
inline static std::string join(std::function<std::string()> get_separator, const T& curr, Next... next) {
|
||||
std::stringstream str;
|
||||
str << curr;
|
||||
join_next(str, get_separator, next...);
|
||||
return str.str();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static std::string to_string(const T& value) {
|
||||
std::stringstream str;
|
||||
str << value;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
static std::string to_string(bool value) {
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
static std::string to_string(Side side, Tense tense = Tense::Present) {
|
||||
switch (side) {
|
||||
case bts::api::SIDE_BUY:
|
||||
switch (tense) {
|
||||
case Tense::Preterite:
|
||||
return "Bought";
|
||||
case Tense::Present:
|
||||
default:
|
||||
return "Buy";
|
||||
}
|
||||
case bts::api::SIDE_SELL:
|
||||
switch (tense) {
|
||||
case Tense::Preterite:
|
||||
return "Sold";
|
||||
case Tense::Present:
|
||||
default:
|
||||
return "Sell";
|
||||
}
|
||||
default:
|
||||
return fmt("Side_", static_cast<int>(side));
|
||||
}
|
||||
}
|
||||
|
||||
static std::string to_string(ChildStatus value) {
|
||||
switch (value) {
|
||||
case ChildStatus::WORKING: return "Working";
|
||||
case ChildStatus::COMPLETED: return "Completed";
|
||||
case ChildStatus::CANCELED: return "Canceled";
|
||||
default: return fmt("ChildStatus_", static_cast<int>(value));
|
||||
}
|
||||
}
|
||||
|
||||
static std::string to_string(ChildState value) {
|
||||
std::stringstream ostr;
|
||||
ostr << "[" << to_string(value.status) << "] "
|
||||
<< value.quantity.value() << " @ " << value.price.value()
|
||||
<< "(total filled " << value.total_fills.value() << " @[avg] "
|
||||
<< value.avg_price.value() << ", last filled "
|
||||
<< value.last_fills.value() << " @ "
|
||||
<< value.last_price.value() << ")";
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
static std::string to_string(const Book &book) {
|
||||
return to_string(book, BookFormat::BBO);
|
||||
}
|
||||
|
||||
static std::string to_string(const Book &book, BookFormat format) {
|
||||
// add other impls as needed
|
||||
std::stringstream ostr;
|
||||
switch (format) {
|
||||
case BookFormat::BBO:
|
||||
if (book.bid().size() > 0 && !book.bid().at(0).is_nan()) {
|
||||
auto best_bid = book.bid().at(0);
|
||||
ostr << best_bid.size() << "@" << best_bid.price();
|
||||
} else {
|
||||
ostr << "N/A";
|
||||
}
|
||||
ostr << " <> ";
|
||||
if (book.ask().size() > 0 && !book.ask().at(0).is_nan()) {
|
||||
auto best_ask = book.ask().at(0);
|
||||
ostr << best_ask.size() << "@" << best_ask.price();
|
||||
} else {
|
||||
ostr << "N/A";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
static std::string to_string(api::orderman::OrderError error) {
|
||||
return api::orderman::OrderError_Name(error);
|
||||
}
|
||||
|
||||
static std::string to_string(api::orderman::CustomOrderDefinition::CustomCase algo_type) {
|
||||
switch (algo_type) {
|
||||
case api::orderman::CustomOrderDefinition::CustomCase::kLimit: return "Limit";
|
||||
case api::orderman::CustomOrderDefinition::CustomCase::kOffset: return "Offset";
|
||||
case api::orderman::CustomOrderDefinition::CustomCase::kCover: return "Cover";
|
||||
case api::orderman::CustomOrderDefinition::CustomCase::kIceberg: return "Iceberg";
|
||||
default: return fmt("AlgoType_", static_cast<int>(algo_type));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// << operators
|
||||
inline std::ostream &operator<<(std::ostream &ostr, const api::Side &side) {
|
||||
ostr << Formatter::to_string(side);
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &ostr, const ChildStatus &child_status) {
|
||||
ostr << Formatter::to_string(child_status);
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &ostr, const ChildState &child_state) {
|
||||
ostr << "[" << child_state.status << "] " << child_state.quantity.value()
|
||||
<< " @ " << child_state.price.value() << "(filled "
|
||||
<< child_state.total_fills.value() << " @avg "
|
||||
<< child_state.avg_price.value() << ")";
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& ostr, const bts::Price& price) {
|
||||
ostr << price.value();
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& ostr, const bts::Quantity& quantity) {
|
||||
ostr << quantity.value();
|
||||
return ostr;
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/domain_model/Security.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
struct PriceComp {
|
||||
bool operator()(const Price& lhs, const Price& rhs) { return lhs.value() > rhs.value(); }
|
||||
bool operator()(const double& lhs, const double& rhs) { return lhs > rhs; }
|
||||
};
|
||||
|
||||
struct ReversePriceComp {
|
||||
bool operator()(const Price& lhs, const Price& rhs) { return lhs.value() < rhs.value(); }
|
||||
bool operator()(const double& lhs, const double& rhs) { return lhs < rhs; }
|
||||
};
|
||||
|
||||
struct SidePriceComp {
|
||||
SidePriceComp(api::Side side) : _side(side) {}
|
||||
|
||||
api::Side side() const { return _side; }
|
||||
|
||||
bool operator()(const Price& lhs, const Price& rhs) {
|
||||
return _side == api::SIDE_SELL ? lhs.value() < rhs.value() : lhs.value() > rhs.value();
|
||||
}
|
||||
|
||||
bool operator()(const double& lhs, const double& rhs) {
|
||||
return _side == api::SIDE_SELL ? lhs < rhs : lhs > rhs;
|
||||
}
|
||||
private:
|
||||
api::Side _side;
|
||||
};
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -16,13 +16,13 @@ class Security;
|
||||
|
||||
namespace orderman {
|
||||
|
||||
constexpr int clordid_sz() { return 20; }
|
||||
constexpr int price_sz() { return 17; }
|
||||
constexpr int quantity_sz() { return 6; }
|
||||
constexpr int max_show_sz() { return 6; }
|
||||
constexpr int memo_sz() {return 12;}
|
||||
constexpr int ice_orderid_sz() {return 12;}
|
||||
constexpr uint32_t corid_sz() {
|
||||
constexpr size_t clordid_sz() { return 20; }
|
||||
constexpr size_t price_sz() { return 17; }
|
||||
constexpr size_t quantity_sz() { return 6; }
|
||||
constexpr size_t max_show_sz() { return 6; }
|
||||
constexpr size_t memo_sz() {return 12;}
|
||||
constexpr size_t ice_orderid_sz() {return 12;}
|
||||
constexpr size_t corid_sz() {
|
||||
return 16;
|
||||
} // 5 for channel, 1 separator, 10 sn
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
@@ -190,18 +190,21 @@ void NativeOrder::on_init_recovered(OrderRecord const &rec,
|
||||
return;
|
||||
}
|
||||
|
||||
auto rq = serv.order_router.get_routing_record(
|
||||
UserID(def.user_id()), AccountID(def.account_id()), *me->sec);
|
||||
if (!rq.has_route) {
|
||||
auto msg = fmt::format("Could not route order: {}", rq.error_msg);
|
||||
LOG(OrderMan, Error, "Unable to route order {} recovered from disk. Message: {}",
|
||||
rec.key(), msg);
|
||||
// bail if the session we used for the order is no longer valid
|
||||
auto const &session = native_state.routing_record().session();
|
||||
if (!serv.order_router.is_valid_session(session)) {
|
||||
LOG(OrderMan, Error,
|
||||
"Unknown JetFIX session {} referenced by recovered "
|
||||
"order {}. This is a serious "
|
||||
"problem which has to be corrected manually. ",
|
||||
session, rec.key());
|
||||
transition_to_error(NativeOrderState::SUB_ERROR_UNKNOWN_FIX_SESSION,
|
||||
"Could not route order");
|
||||
fmt::format("Unknown FIX session: {}", session));
|
||||
return;
|
||||
}
|
||||
|
||||
// if we made it this far we are good.
|
||||
me->routing_record = rq.routing_record;
|
||||
me->routing_record = native_state.routing_record();
|
||||
if (me->state.status() != ORDER_STATUS_PENDING_WORKING)
|
||||
me->exchange_order_id = native_state.exchange_order_id();
|
||||
init_fix();
|
||||
|
||||
@@ -18,7 +18,6 @@ struct CoverAlgo::PImpl {
|
||||
Price price_improvement_delta;
|
||||
bool cancel_on_external_hedge_cancel = false;
|
||||
bool cancel_hedges_on_cancel = false;
|
||||
bool detach_hedges = false;
|
||||
ChildTemplate *child_template = nullptr;
|
||||
ChildTemplate *hedge_template = nullptr;
|
||||
Quantity hedged_fills = Quantity(0);
|
||||
@@ -102,12 +101,8 @@ void CoverAlgo::on_covered_change(ChildState const &st) {
|
||||
me->price_improvement_delta);
|
||||
|
||||
auto hedge =
|
||||
algo().launch_child(*me->hedge_template, hedge_price, hedge_qty,
|
||||
!me->detach_hedges);
|
||||
if (me->detach_hedges)
|
||||
algo().detach_child(hedge);
|
||||
else
|
||||
me->hedges.push_back(hedge);
|
||||
algo().launch_child(*me->hedge_template, hedge_price, hedge_qty);
|
||||
me->hedges.push_back(hedge);
|
||||
|
||||
me->hedged_fills =
|
||||
Quantity(hedge_qty.value() + me->hedged_fills.value());
|
||||
@@ -252,7 +247,6 @@ void CoverAlgo::update_definition(
|
||||
me->price_improvement_delta = Price(
|
||||
securities::tick_size(serv.security()).value() * cover.hedge_ticks());
|
||||
me->cancel_hedges_on_cancel = cover.cancel_hedges_on_cancel();
|
||||
me->detach_hedges = cover.detach_hedges();
|
||||
me->cancel_on_external_hedge_cancel =
|
||||
cover.cancel_on_external_hedge_cancel();
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/DeleteAndCoverAlgo.h"
|
||||
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/orderman/orderman_utils/Book.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_utils.h"
|
||||
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct DeleteAndCoverAlgo::PImpl{
|
||||
IAlgoServices* algo_services = nullptr;
|
||||
OrderDefinition order_def;
|
||||
ChildTemplate* child_template = nullptr;
|
||||
OrderKey child_key;
|
||||
};
|
||||
|
||||
void copy_lean (LeanOrderDefinition& lean,
|
||||
const DeleteAndCoverDefinition& dc){
|
||||
lean.mutable_cross_threshold()->CopyFrom(dc.cross_threshold());
|
||||
lean.set_max_chase_ticks(dc.max_chase_ticks());
|
||||
switch(dc.trade_toward_behavior()){
|
||||
case LeanOnToward::NO_CHANGE:
|
||||
lean.set_trade_toward_behavior(LeanOrderDefinition::NO_CHANGE);
|
||||
break;
|
||||
case LeanOnToward::CANCEL:
|
||||
lean.set_trade_toward_behavior(LeanOrderDefinition::CANCEL);
|
||||
break;
|
||||
case LeanOnToward::DETACH:
|
||||
lean.set_trade_toward_behavior(LeanOrderDefinition::DETACH);
|
||||
break;
|
||||
case LeanOnToward::REPRICE:
|
||||
lean.set_trade_toward_behavior(LeanOrderDefinition::REPRICE);
|
||||
break;
|
||||
default:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void copy_top(TopOrderDefinition& top,
|
||||
const DeleteAndCoverDefinition& dc){
|
||||
top.mutable_top_threshold()->CopyFrom(dc.top_threshold());
|
||||
top.set_ticks_off(dc.ticks_off());
|
||||
}
|
||||
|
||||
void copy_cover(CoverOrderDefinition& cover,
|
||||
const DeleteAndCoverDefinition& dc){
|
||||
cover.set_detach_hedges(true);
|
||||
cover.set_hedge_ticks(dc.hedge_ticks());
|
||||
cover.set_cancel_on_external_hedge_cancel(dc.cancel_on_external_hedge_cancel());
|
||||
cover.set_cancel_hedges_on_cancel(dc.cancel_hedges_on_cancel());
|
||||
}
|
||||
|
||||
DeleteAndCoverAlgo::DeleteAndCoverAlgo() : me(new PImpl()) {}
|
||||
DeleteAndCoverAlgo::~DeleteAndCoverAlgo() {}
|
||||
IAlgoServices &DeleteAndCoverAlgo::algo() const { return *me->algo_services; }
|
||||
|
||||
// initialization /////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
OrderOpResult DeleteAndCoverAlgo::do_before_launch(IBeforeLaunchAlgoServices& serv) {
|
||||
auto &def = serv.definition();
|
||||
if(def.has_custom_def() && def.custom_def().has_delete_and_cover()){
|
||||
const auto& dc = def.custom_def().delete_and_cover();
|
||||
LOG(OrderMan, Info, "DeleteAndCoverAlgo do_before_launch args: {}",
|
||||
utils::pb::to_string(dc));
|
||||
// init child temp
|
||||
me->order_def = serv.definition();
|
||||
me->order_def.set_attach_target_order_key(null_order_key().value());
|
||||
// Lean
|
||||
LeanOrderDefinition lean;
|
||||
copy_lean(lean,dc);
|
||||
lean.mutable_child()->mutable_limit()->MergeFrom (
|
||||
LimitOrderDefinition());
|
||||
// Top
|
||||
TopOrderDefinition top;
|
||||
copy_top(top,dc);
|
||||
top.mutable_child()->mutable_limit()->MergeFrom (
|
||||
LimitOrderDefinition());
|
||||
// Cover
|
||||
CoverOrderDefinition cover;
|
||||
copy_cover(cover,dc);
|
||||
cover.mutable_child()->mutable_top()->MergeFrom (top);
|
||||
cover.mutable_hedge()->mutable_lean()->CopyFrom(lean);
|
||||
// put it all together
|
||||
me->order_def.mutable_custom_def()->mutable_cover()->MergeFrom (cover);
|
||||
me->child_template = &(serv.make_child_template(me->order_def));
|
||||
return {};
|
||||
}
|
||||
else
|
||||
return {OrderError::ORDER_ERROR_INVALID_PARAMETER,
|
||||
std::string("Parameters are not well formed")};
|
||||
}
|
||||
void DeleteAndCoverAlgo::do_launch(IAlgoServices& algo) {
|
||||
me->algo_services = &algo;
|
||||
bts_assert(me->child_template);
|
||||
me->child_key = algo.launch_child(*me->child_template, algo.price(), algo.quantity());
|
||||
}
|
||||
void DeleteAndCoverAlgo::do_launch_attached(IAlgoServices& algo,
|
||||
OrderKey k,
|
||||
ChildState st) {
|
||||
me->algo_services = &algo;
|
||||
me->child_key = k;
|
||||
on_child_change(k, st);
|
||||
}
|
||||
// updates ////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void DeleteAndCoverAlgo::do_update(const api::orderman::Update& u) {
|
||||
LOG(OrderMan, Info, "DeleteAndCoverAlgo order {} receiving update: {}",
|
||||
algo().order_key().value(), utils::pb::to_string(u));
|
||||
if (u.has_custom() && u.custom().has_delete_and_cover()){
|
||||
const auto& dc = u.custom().delete_and_cover();
|
||||
copy_cover(*me->order_def.mutable_custom_def()->mutable_cover(),dc);
|
||||
copy_lean(*me->order_def.mutable_custom_def()->mutable_cover()->mutable_hedge()->mutable_lean(),dc);
|
||||
copy_top(*me->order_def.mutable_custom_def()->mutable_cover()->mutable_child()->mutable_top(),dc);
|
||||
Update child_u = u;
|
||||
child_u.mutable_custom()->CopyFrom(me->order_def.custom_def());
|
||||
child_u.set_order_key (me->child_key.value());// is this needed?
|
||||
algo().update_child(me->child_key,child_u);
|
||||
}
|
||||
else
|
||||
LOG(OrderMan, Error, "DeleteAndCoverAlgo order {} Invalid update: {}",
|
||||
algo().order_key().value(), utils::pb::to_string(u));
|
||||
}
|
||||
// callbacks //////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DeleteAndCoverAlgo::on_child_change(OrderKey, const ChildState &st) {
|
||||
algo().update_fill_state(st.total_fills,st.last_price);
|
||||
switch (st.status){
|
||||
case ChildStatus::WORKING:
|
||||
break;
|
||||
case ChildStatus::CANCELED:
|
||||
algo().cancel();
|
||||
break;
|
||||
case ChildStatus::COMPLETED:
|
||||
algo().complete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class ChildTemplate;
|
||||
|
||||
class DeleteAndCoverAlgo : public IAlgoOrder {
|
||||
public:
|
||||
DeleteAndCoverAlgo();
|
||||
~DeleteAndCoverAlgo();
|
||||
public:
|
||||
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
|
||||
void do_launch(IAlgoServices &) override;
|
||||
void do_launch_attached(IAlgoServices&, OrderKey, ChildState) override;
|
||||
void do_update(api::orderman::Update const &) override;
|
||||
void on_child_change(OrderKey, ChildState const &) override;
|
||||
private:
|
||||
IAlgoServices &algo() const ;
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,401 +0,0 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/LimitCoverAlgo.h"
|
||||
#include "lib/sparkproto/SparkProtos.pb.h"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_utils.h"
|
||||
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include "lib/orderman/orders/algo/utils/DynamicOrderAdapter.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverDefinition.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverAdapter.h"
|
||||
#include <map>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#define DEFAULT_LOG_LEVEL Info
|
||||
|
||||
#define SUMMARIZE_TRANSACTION_BEGIN(id, type, ...) Summary __LOG_TRANS(this, id, Formatter::fmt(__VA_ARGS__), type, DEFAULT_LOG_LEVEL)
|
||||
|
||||
#define SUMMARIZE_TRANSACTION(id, type) Summary __LOG_TRANS(this, id, "", type, DEFAULT_LOG_LEVEL)
|
||||
#define SUMMARIZE SUMMARIZE_TRANSACTION(__FUNCTION__, SummaryType::StatusChange)
|
||||
|
||||
// define 'PRINT_SUMMARIES' to print all logs to the console
|
||||
// (assuming logs are made with the MSG/MSG_LEVEL macros)
|
||||
#ifdef PRINT_SUMMARIES
|
||||
#define _PRINT_MSG(x) std::cout << x << std::endl;
|
||||
#else
|
||||
#define _PRINT_MSG(x)
|
||||
#endif
|
||||
|
||||
#define _MSG(level, msg) \
|
||||
_PRINT_MSG(msg); \
|
||||
LOG(OrderMan, level, "{}", msg)
|
||||
|
||||
#define MSG_LEVEL(level, ...) _MSG(level, (Formatter::fmt(__VA_ARGS__)))
|
||||
// these automatically prepend log_header() to the args
|
||||
#define MSG(...) MSG_LEVEL(DEFAULT_LOG_LEVEL, log_header(), " ", __VA_ARGS__)
|
||||
#define DIAG(...) MSG_LEVEL(Diag, log_header(), " ", __VA_ARGS__)
|
||||
#define INFO(...) MSG_LEVEL(Info, log_header(), " ", __VA_ARGS__)
|
||||
#define WARN(...) MSG_LEVEL(Warn, log_header(), " ", __VA_ARGS__)
|
||||
#define ERROR(...) MSG_LEVEL(Error, log_header(), " ", __VA_ARGS__)
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
using namespace api::orderman;
|
||||
using namespace bts::orderman::limit_cover::utils;
|
||||
using Adapter = bts::orderman::LimitCoverAdapter;
|
||||
|
||||
namespace limit_cover {
|
||||
namespace utils {
|
||||
void init_delete_and_cover(OrderDefinition& target, const DeleteAndCoverDefinition& def) {
|
||||
if (!target.has_custom_def())
|
||||
*target.mutable_custom_def() = CustomOrderDefinition();
|
||||
init_delete_and_cover(*target.mutable_custom_def(), def);
|
||||
}
|
||||
|
||||
void init_delete_and_cover(CustomOrderDefinition& target, const DeleteAndCoverDefinition& def) {
|
||||
if (!target.has_delete_and_cover()) {
|
||||
*target.mutable_delete_and_cover() = DeleteAndCoverDefinition(def);
|
||||
} else {
|
||||
target.mutable_delete_and_cover()->CopyFrom(def);
|
||||
}
|
||||
}
|
||||
|
||||
void init_child_update(Update& target, uint32_t qty, double price, const DeleteAndCoverDefinition& def) {
|
||||
target.set_quantity(qty);
|
||||
target.set_price(price);
|
||||
init_delete_and_cover(*target.mutable_custom(), def);
|
||||
}
|
||||
|
||||
#define INVALID_DEF(x) OrderOpResult(api::orderman::ORDER_ERROR_INVALID_ORDER_DEFINITON, x)
|
||||
|
||||
OrderOpResult validate_custom_def(const CustomOrderDefinition& custom_def) {
|
||||
if (!custom_def.has_dynamic())
|
||||
return INVALID_DEF("DynamicOrderDefinition is undefined");
|
||||
return Adapter::validate_proto(custom_def.dynamic());
|
||||
}
|
||||
OrderOpResult validate_def(const OrderDefinition& def) {
|
||||
if (def.attach_target_order_key() != 0) {
|
||||
return INVALID_DEF("Attach not supported.");
|
||||
} else if (def.has_custom_def()) {
|
||||
return validate_custom_def(def.custom_def());
|
||||
} else {
|
||||
return INVALID_DEF("CustomOrderDefinition is undefined.");
|
||||
}
|
||||
}
|
||||
|
||||
OrderOpResult validate_update(const Update& upd) {
|
||||
return upd.has_custom() ?
|
||||
validate_custom_def(upd.custom()) : OrderOpResult();
|
||||
}
|
||||
} // namespace utils
|
||||
} // namespace limit_cover
|
||||
|
||||
struct LimitCoverAlgo::PImpl {
|
||||
IAlgoServices *algo = nullptr;
|
||||
Status status = Status::WAITING_TO_DEPLOY;
|
||||
OrderKey child_key = null_order_key();
|
||||
double child_price = os::nan();
|
||||
uint32_t child_qty = 0;
|
||||
Book book;
|
||||
uint32_t limit_volume = 0;
|
||||
std::map<uint32_t, LimitCoverGate> gates;
|
||||
double settlement = os::nan();
|
||||
double price_offset_from_settle = 0;
|
||||
double limit_price = os::nan();
|
||||
};
|
||||
|
||||
struct LimitCoverAlgo::Summary {
|
||||
Summary(LimitCoverAlgo* owner, std::string transaction_id, std::string begin_tag = {}, SummaryType type = SummaryType::StatusChange, log::Level level = DEFAULT_LOG_LEVEL)
|
||||
: _owner(owner), _transaction_id(transaction_id), _original_status(owner->me->status), _type(type), _level(level) {
|
||||
if (_owner != nullptr && !_transaction_id.empty()) {
|
||||
std::stringstream str;
|
||||
str << owner->log_header() << " begin " << transaction_id << ".";
|
||||
if (!begin_tag.empty()) {
|
||||
str << " " << begin_tag << ".";
|
||||
}
|
||||
if (type == SummaryType::FullState) {
|
||||
str << " Algo State: " << _owner->fmt_state() << ".";
|
||||
}
|
||||
MSG_LEVEL(level, str.str());
|
||||
}
|
||||
}
|
||||
|
||||
~Summary() {
|
||||
std::stringstream msg_str;
|
||||
if (!_transaction_id.empty()) {
|
||||
msg_str << " end " << _transaction_id << ".";
|
||||
}
|
||||
switch (_type) {
|
||||
case SummaryType::StatusChange:
|
||||
if (_owner->me->status != _original_status)
|
||||
msg_str << " Status changed from " << _original_status << " to " << _owner->me->status << ".";
|
||||
break;
|
||||
case SummaryType::FullState:
|
||||
msg_str << " Algo State: " << _owner->fmt_state() << ".";
|
||||
break;
|
||||
}
|
||||
auto msg = msg_str.str();
|
||||
if (!msg.empty()) {
|
||||
MSG_LEVEL(_level, _owner->log_header(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
LimitCoverAlgo *_owner;
|
||||
std::string _transaction_id;
|
||||
Status _original_status;
|
||||
SummaryType _type;
|
||||
log::Level _level;
|
||||
};
|
||||
|
||||
LimitCoverAlgo::LimitCoverAlgo() : me(new PImpl()) {}
|
||||
LimitCoverAlgo::~LimitCoverAlgo() {}
|
||||
|
||||
OrderOpResult
|
||||
LimitCoverAlgo::do_before_launch(IBeforeLaunchAlgoServices &balgo) {
|
||||
SUMMARIZE;
|
||||
bts_assert(me->status == Status::WAITING_TO_DEPLOY);
|
||||
bts_assert(me->gates.empty());
|
||||
auto vd = validate_def(balgo.definition());
|
||||
if (vd.ok()) {
|
||||
auto lcdef = Adapter::from_proto(balgo.definition());
|
||||
me->price_offset_from_settle = lcdef.price_offset();
|
||||
me->gates = lcdef.normal_gates();
|
||||
}
|
||||
return vd;
|
||||
}
|
||||
|
||||
void LimitCoverAlgo::do_launch(IAlgoServices& algo) {
|
||||
SUMMARIZE;
|
||||
bts_assert(me->status == Status::WAITING_TO_DEPLOY);
|
||||
me->algo = &algo;
|
||||
me->child_qty = algo.quantity().value();
|
||||
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||
algo.subscribe_settlement(SecurityKey(algo.security().id()));
|
||||
}
|
||||
|
||||
void LimitCoverAlgo::do_update(Update const &upd) {
|
||||
SUMMARIZE_TRANSACTION_BEGIN(__FUNCTION__, SummaryType::StatusChange, utils::pb::to_string(upd));
|
||||
auto vup = validate_update(upd);
|
||||
if (!vup.ok()) {
|
||||
me->status = Status::DONE;
|
||||
algo().terminate_error(Formatter::fmt("Invalid update: ", vup.error_msg, "."));
|
||||
return;
|
||||
}
|
||||
if (upd.has_custom()) {
|
||||
auto lcdef = Adapter::from_proto(upd.custom());
|
||||
if (me->status == Status::WAITING_TO_DEPLOY) {
|
||||
me->price_offset_from_settle = lcdef.price_offset();
|
||||
recompute_limit_price();
|
||||
recompute_limit_volume();
|
||||
}
|
||||
me->gates = lcdef.normal_gates();
|
||||
if (me->status == Status::WORKING)
|
||||
recompute_gate();
|
||||
}
|
||||
switch (me->status) {
|
||||
case Status::WAITING_TO_DEPLOY:
|
||||
try_deploy();
|
||||
break;
|
||||
case Status::WORKING: {
|
||||
// don't change the child price; the price param is irrelevant to this algo.
|
||||
if (upd.has_custom() || me->child_qty != upd.quantity())
|
||||
update_child(upd.quantity());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LimitCoverAlgo::on_book(SecurityKey, Book const &b) {
|
||||
SUMMARIZE_TRANSACTION_BEGIN(__FUNCTION__, SummaryType::StatusChange, "BBO: ", Formatter::to_string(b));
|
||||
me->book = b;
|
||||
recompute_limit_volume();
|
||||
switch (me->status) {
|
||||
case Status::WAITING_TO_DEPLOY:
|
||||
try_deploy();
|
||||
break;
|
||||
case Status::WORKING: {
|
||||
if (recompute_gate())
|
||||
update_child(me->child_qty);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LimitCoverAlgo::update_child(uint32_t qty) {
|
||||
bts_assert(me->algo != nullptr);
|
||||
bts_assert(me->status == Status::WORKING);
|
||||
me->child_qty = qty;
|
||||
Update upd;
|
||||
init_child_update(upd, qty, me->child_price, curr_gate().definition());
|
||||
MSG("updating algo child: ", utils::pb::to_string(upd));
|
||||
me->algo->update_child(me->child_key, upd);
|
||||
}
|
||||
|
||||
void LimitCoverAlgo::on_child_change(OrderKey key, ChildState const& state) {
|
||||
bts_assert(key == me->child_key);
|
||||
SUMMARIZE_TRANSACTION_BEGIN(__FUNCTION__, SummaryType::FullState, "Child State: ", Formatter::to_string(state));
|
||||
algo().update_fill_state(state.total_fills, state.last_price);
|
||||
me->child_price = state.price.value();
|
||||
switch (me->status) {
|
||||
case Status::WORKING:
|
||||
switch (state.status) {
|
||||
case ChildStatus::WORKING:
|
||||
break;
|
||||
case ChildStatus::CANCELED: {
|
||||
auto msg = Formatter::fmt("main child (", key, ") canceled unexpectedly.");
|
||||
MSG(msg, ". ", fmt_state());
|
||||
me->status = Status::DONE;
|
||||
algo().terminate_error(msg);
|
||||
break;
|
||||
}
|
||||
case ChildStatus::COMPLETED:
|
||||
me->status = Status::DONE;
|
||||
algo().complete();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Status::DONE:
|
||||
break;
|
||||
default:
|
||||
bts_assert_msg(false, Formatter::fmt(log_header(), " unexpected status: ", me->status));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LimitCoverAlgo::on_settlement(SecurityKey, Price settlement) {
|
||||
if (me->status == Status::WAITING_TO_DEPLOY) {
|
||||
MSG("updating settlement from ", me->settlement, " to ", settlement);
|
||||
me->settlement = settlement.value();
|
||||
DIAG("recomputing limit price...");
|
||||
recompute_limit_price();
|
||||
DIAG("limit price is ", me->limit_price);
|
||||
DIAG("recomputing limit volume...");
|
||||
recompute_limit_volume();
|
||||
DIAG("limit volume is ", me->limit_volume);
|
||||
try_deploy();
|
||||
} else {
|
||||
MSG("ignored settlement update to ", settlement,
|
||||
": settlement updates are only processed while the algo is ", Status::WAITING_TO_DEPLOY,
|
||||
". Settlement at time of deploy was ", me->settlement);
|
||||
}
|
||||
}
|
||||
|
||||
void LimitCoverAlgo::try_deploy() {
|
||||
MSG("trying to deploy. ", fmt_state());
|
||||
bts_assert(me->status == Status::WAITING_TO_DEPLOY);
|
||||
bts_assert(me->algo != nullptr);
|
||||
// this is probably redundant (better safe than sorry tho).
|
||||
bts_assert(me->child_key == null_order_key());
|
||||
if (os::is_nan(me->settlement)) {
|
||||
MSG("still waiting for settle.");
|
||||
} else if (me->limit_volume < activation_threshold()) {
|
||||
MSG("still waiting for limit volume (", me->limit_volume, ") to breech activation threshold (", activation_threshold(), ").");
|
||||
} else {
|
||||
bts_assert(!os::is_nan(me->limit_price));
|
||||
recompute_gate();
|
||||
OrderDefinition child_def;
|
||||
bts_assert(!me->gates.empty());
|
||||
child_def.CopyFrom(algo().definition());
|
||||
init_delete_and_cover(child_def, curr_gate().definition());
|
||||
bts_assert(child_def.custom_def().has_delete_and_cover());
|
||||
MSG("deploying child order: ", utils::pb::to_string(child_def));
|
||||
me->child_key = algo().launch_child(child_def, Price(me->limit_price), algo().quantity());
|
||||
me->status = Status::WORKING;
|
||||
algo().set_ladder_visibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool did_change(T& backing, T new_value) {
|
||||
if (backing == new_value)
|
||||
return false;
|
||||
backing = new_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LimitCoverAlgo::recompute_gate() {
|
||||
if (me->gates.empty())
|
||||
return false;
|
||||
auto orig_sz = me->gates.size();
|
||||
auto it = me->gates.lower_bound(me->limit_volume);
|
||||
if (it == me->gates.end() || (it != me->gates.begin() && it->first > me->limit_volume))
|
||||
it--;
|
||||
me->gates.erase(me->gates.begin(), it);
|
||||
return me->gates.size() < orig_sz;
|
||||
}
|
||||
|
||||
// limit price
|
||||
double LimitCoverAlgo::compute_limit_price() const {
|
||||
return os::is_nan(me->settlement) ? os::nan() :
|
||||
securities::improve_price(side(), Price(me->settlement), Price(me->price_offset_from_settle))
|
||||
.value();
|
||||
}
|
||||
bool LimitCoverAlgo::recompute_limit_price() {
|
||||
return did_change(me->limit_price, compute_limit_price());
|
||||
}
|
||||
|
||||
// limit volume
|
||||
uint32_t LimitCoverAlgo::compute_limit_volume() const {
|
||||
return me->algo == nullptr ? 0 :
|
||||
BookUtils::volume_at_or_better(me->book, side(), me->limit_price);
|
||||
}
|
||||
bool LimitCoverAlgo::recompute_limit_volume() {
|
||||
return did_change(me->limit_volume, compute_limit_volume());
|
||||
}
|
||||
|
||||
// pseudo properties (const state accessors)
|
||||
IAlgoServices &LimitCoverAlgo::algo() const { return *me->algo; }
|
||||
|
||||
api::Side LimitCoverAlgo::side() const {
|
||||
return me->algo == nullptr ? api::SIDE_UNDEFINED : me->algo->definition().side();
|
||||
}
|
||||
|
||||
const LimitCoverGate& LimitCoverAlgo::curr_gate() const {
|
||||
bts_assert(!me->gates.empty());
|
||||
return me->gates.begin()->second;
|
||||
}
|
||||
|
||||
uint32_t LimitCoverAlgo::activation_threshold() const {
|
||||
return me->gates.empty() ? std::numeric_limits<uint32_t>::max() : me->gates.begin()->first;
|
||||
}
|
||||
|
||||
std::string LimitCoverAlgo::log_header() const {
|
||||
std::stringstream ostr;
|
||||
ostr << "LimitCoverAlgo";
|
||||
if (me->algo != nullptr) {
|
||||
ostr << "(" << me->algo->order_key().value() << ")";
|
||||
}
|
||||
ostr << "[" << fmt_status(me->status) << "]";
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
std::string LimitCoverAlgo::fmt_state() const {
|
||||
std::stringstream ostr;
|
||||
if (me->algo != nullptr) {
|
||||
ostr << " [" << fmt_status(me->status) << "]"
|
||||
<< Formatter::to_string(side()) << " " << me->algo->quantity().value()
|
||||
<< " @ " << me->algo->price().value()
|
||||
<< " (" << me->price_offset_from_settle
|
||||
<< " away from settle).";
|
||||
if (me->child_key != null_order_key()) {
|
||||
ostr << " Main child key: " << me->child_key << ", ";
|
||||
}
|
||||
ostr << " Settle: " << me->settlement
|
||||
<< ", Limit Price: " << me->limit_price
|
||||
<< ", BBO: " << Formatter::to_string(me->book);
|
||||
} else {
|
||||
ostr << "(initializing)";
|
||||
}
|
||||
return ostr.str();
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,94 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/orderman/orderman_utils/BookUtils.h"
|
||||
#include "lib/orderman/orderman_utils/Formatter.h"
|
||||
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverDefinition.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverGate.h"
|
||||
#include <map>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
class LimitCoverAlgo : public IAlgoOrder {
|
||||
public:
|
||||
LimitCoverAlgo();
|
||||
~LimitCoverAlgo();
|
||||
|
||||
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
|
||||
void do_launch(IAlgoServices &) override;
|
||||
void do_update(api::orderman::Update const &) override;
|
||||
void on_book(SecurityKey, Book const &) override;
|
||||
void on_settlement(SecurityKey, Price settlement) override;
|
||||
void on_child_change(OrderKey, ChildState const &) override;
|
||||
|
||||
enum class Status {
|
||||
WAITING_TO_DEPLOY,
|
||||
WORKING,
|
||||
DONE
|
||||
};
|
||||
using Status = bts::orderman::LimitCoverAlgo::Status;
|
||||
|
||||
enum SummaryType {
|
||||
StatusChange,
|
||||
FullState
|
||||
};
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
struct Summary;
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
IAlgoServices &algo() const;
|
||||
api::Side side() const;
|
||||
std::string log_header() const;
|
||||
std::string fmt_state() const;
|
||||
// returns the first gate in gates (asserts that gates isn't empty).
|
||||
// Note: after the child is deployed, gates with lower thresholds are
|
||||
// removed when the limit volume passes the next gates' thresh.
|
||||
const LimitCoverGate& curr_gate() const;
|
||||
/// On the compute/recompute methods:
|
||||
/// compute_blah just returns the result (doesn't mutate any state),
|
||||
/// while recompute_blah assigns the result to the appropriate field,
|
||||
/// and returns a bool indicating whether or not the value changed.
|
||||
// limit price
|
||||
double compute_limit_price() const;
|
||||
bool recompute_limit_price();
|
||||
// limit volume
|
||||
uint32_t compute_limit_volume() const;
|
||||
bool recompute_limit_volume();
|
||||
// recomputes the current gate (i.e., the one with the
|
||||
// highest activation thresh that is still less than
|
||||
// the limit volume) then removes all gates with lower threshs.
|
||||
bool recompute_gate();
|
||||
uint32_t activation_threshold() const;
|
||||
//
|
||||
void try_deploy();
|
||||
void update_child(uint32_t qty);
|
||||
};
|
||||
|
||||
namespace limit_cover {
|
||||
namespace utils {
|
||||
inline std::string fmt_status(LimitCoverAlgo::Status status) {
|
||||
switch (status) {
|
||||
case LimitCoverAlgo::Status::WAITING_TO_DEPLOY: return "WAITING_TO_DEPLOY";
|
||||
case LimitCoverAlgo::Status::WORKING: return "WORKING";
|
||||
case LimitCoverAlgo::Status::DONE: return "DONE";
|
||||
default: return Formatter::fmt("STATUS_", static_cast<int>(status));
|
||||
}
|
||||
}
|
||||
void init_delete_and_cover(api::orderman::OrderDefinition& target, const api::orderman::DeleteAndCoverDefinition& def);
|
||||
void init_delete_and_cover(api::orderman::CustomOrderDefinition& target, const api::orderman::DeleteAndCoverDefinition& def);
|
||||
void init_child_update(api::orderman::Update& target, uint32_t qty, double price, const api::orderman::DeleteAndCoverDefinition& def);
|
||||
OrderOpResult validate_def(const api::orderman::OrderDefinition&);
|
||||
OrderOpResult validate_custom_def(const api::orderman::CustomOrderDefinition&);
|
||||
OrderOpResult validate_update(const api::orderman::Update&);
|
||||
} // namespace utils
|
||||
} // namespace limit_cover
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& ostr, const LimitCoverAlgo::Status& status) {
|
||||
ostr << limit_cover::utils::fmt_status(status);
|
||||
return ostr;
|
||||
}
|
||||
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -3,8 +3,6 @@
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_utils.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverAdapter.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/LimitCoverAlgo.h"
|
||||
#include "lib/config/protos/toggles.pb.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "DormanAlgo.h"
|
||||
@@ -205,13 +203,6 @@ extern "C" void init_tas_plugin(bts::SparkServices& srv) {
|
||||
srv.register_dynamic_algo_order(tas_desc, []() { return std::make_unique<orderman::TASAlgo>(); });
|
||||
}
|
||||
|
||||
extern "C" void init_limit_cover(bts::SparkServices & srv) {
|
||||
DynamicOrderDescriptor lc_desc;
|
||||
lc_desc.set_dynamic_order_type("LimitCover");
|
||||
LimitCoverAdapter::define_proto(lc_desc);
|
||||
srv.register_dynamic_algo_order(lc_desc, []() { return std::make_unique<orderman::LimitCoverAlgo>(); });
|
||||
}
|
||||
|
||||
extern "C" void init_spark_plugin(bts::SparkServices &srv) {
|
||||
auto& dyn_algo_config = config::get<config::toggles::DynamicAlgos>();
|
||||
if(dyn_algo_config.enable_dorman())
|
||||
@@ -222,8 +213,6 @@ extern "C" void init_spark_plugin(bts::SparkServices &srv) {
|
||||
init_tas_plugin(srv);
|
||||
if(dyn_algo_config.enable_tascounter())
|
||||
init_tascounter_plugin(srv);
|
||||
if (dyn_algo_config.enable_limit_cover())
|
||||
init_limit_cover(srv);
|
||||
}
|
||||
|
||||
}}}
|
||||
}}}
|
||||
@@ -1,4 +1,16 @@
|
||||
// TODO
|
||||
// put settle time data in the input (X)
|
||||
// better names (X)
|
||||
// at end of settle, order should dettach and complete (X)
|
||||
// order going out logic (X)
|
||||
// settlement compute logic (X)
|
||||
// actual publish (X)
|
||||
// no PTime
|
||||
// check settle calc (X)
|
||||
// check order shooting logic
|
||||
|
||||
#include "lib/orderman/orders/algo/algo_orders/TASCounterAlgo.h"
|
||||
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/orderman/orderman_utils/Book.h"
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverAdapter.h"
|
||||
#include "lib/orderman/orders/OrderOpResult.h"
|
||||
#include "lib/orderman/orders/algo/utils/DynamicOrderAdapter.h"
|
||||
#include <utility>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
template<typename T>
|
||||
using Param = bts::orderman::dynamic_adapter::ParamKey<bts::orderman::LimitCoverDefinition, T>;
|
||||
using DynamicOrderEnumerated = bts::api::orderman::DynamicOrderEnumerated;
|
||||
using OnToward = bts::api::orderman::LeanOnToward;
|
||||
using LimitCoverDefinition = bts::orderman::LimitCoverDefinition;
|
||||
using namespace bts::api::orderman;
|
||||
#define ON_TOWARD_NAME_PAIR(x) std::make_pair(x, #x)
|
||||
|
||||
template<uint32_t gate_num>
|
||||
std::string gate_param_name(std::string name) {
|
||||
return Formatter::fmt("Gate ", gate_num, " ", name);
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string gate_param_name<0>(std::string name) {
|
||||
return Formatter::fmt("Initial ", name);
|
||||
}
|
||||
|
||||
struct PriceOffset : public Param<double> {
|
||||
PriceOffset() : Param<double>(std::string("Limit"),
|
||||
[](const LimitCoverDefinition& def) { return def.price_offset(); },
|
||||
[](LimitCoverDefinition& def, double v) { def.price_offset(v); }) {}
|
||||
};
|
||||
|
||||
template<uint32_t gate_num>
|
||||
struct ActivationThreshold : public Param<uint32_t> {
|
||||
ActivationThreshold() : Param<uint32_t>(gate_param_name<gate_num>("Activation Threshold"),
|
||||
[](const LimitCoverDefinition& def) { return def.gate(gate_num).activation_threshold(); },
|
||||
[](LimitCoverDefinition& def, uint32_t v) { def.mgate(gate_num).activation_threshold(v); }) { }
|
||||
};
|
||||
|
||||
template<uint32_t gate_num>
|
||||
struct CoverThreshold : public Param<uint32_t> {
|
||||
CoverThreshold() : Param<uint32_t>(gate_param_name<gate_num>("Cover Threshold"),
|
||||
[](const LimitCoverDefinition& def) { return def.gate(gate_num).cover_threshold(); },
|
||||
[](LimitCoverDefinition& def, uint32_t v) { def.mgate(gate_num).cover_threshold(v); }) { }
|
||||
};
|
||||
|
||||
template<uint32_t gate_num>
|
||||
struct DeleteThreshold : public Param<uint32_t> {
|
||||
DeleteThreshold() : Param<uint32_t>(gate_param_name<gate_num>("Delete Threshold"),
|
||||
[](const LimitCoverDefinition& def) { return def.gate(gate_num).delete_threshold(); },
|
||||
[](LimitCoverDefinition& def, uint32_t v) { def.mgate(gate_num).delete_threshold(v); }) { }
|
||||
};
|
||||
|
||||
template<uint32_t gate_num>
|
||||
struct HedgeTicks : public Param<uint32_t> {
|
||||
HedgeTicks() : Param<uint32_t>(gate_param_name<gate_num>("Hedge Ticks"),
|
||||
[](const LimitCoverDefinition& def) { return def.gate(gate_num).hedge_ticks(); },
|
||||
[](LimitCoverDefinition& def, uint32_t v) { def.mgate(gate_num).hedge_ticks(v); }) { }
|
||||
};
|
||||
|
||||
template<uint32_t gate_num>
|
||||
struct TopTicksOff : public Param<uint32_t> {
|
||||
TopTicksOff() : Param<uint32_t>(gate_param_name<gate_num>("Top Ticks Offset"),
|
||||
[](const LimitCoverDefinition& def) { return def.gate(gate_num).top_ticks_offset(); },
|
||||
[](LimitCoverDefinition& def, uint32_t v) { def.mgate(gate_num).top_ticks_offset(v); }) { }
|
||||
};
|
||||
|
||||
template<uint32_t gate_num>
|
||||
struct MaxChaseTicks : public Param<uint32_t> {
|
||||
MaxChaseTicks() : Param<uint32_t>(gate_param_name<gate_num>("Max Chase Ticks"),
|
||||
[](const LimitCoverDefinition& def) { return def.gate(gate_num).max_chase_ticks(); },
|
||||
[](LimitCoverDefinition& def, uint32_t v) { def.mgate(gate_num).max_chase_ticks(v); }) { }
|
||||
};
|
||||
|
||||
template<uint32_t gate_num>
|
||||
struct TradeTowardBehavior : public dynamic_adapter::EnumParamKey<LimitCoverDefinition, OnToward> {
|
||||
TradeTowardBehavior() : EnumParamKey<LimitCoverDefinition, OnToward>(
|
||||
gate_param_name<gate_num>("Trade Toward Behavior"), {
|
||||
ON_TOWARD_NAME_PAIR(NO_CHANGE),
|
||||
ON_TOWARD_NAME_PAIR(CANCEL),
|
||||
ON_TOWARD_NAME_PAIR(DETACH),
|
||||
ON_TOWARD_NAME_PAIR(REPRICE)
|
||||
},
|
||||
[](const LimitCoverDefinition& def) { return def.gate(gate_num).on_toward(); },
|
||||
[](LimitCoverDefinition& def, OnToward v) { def.mgate(gate_num).on_toward(v); })
|
||||
{}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ActivationThreshold<0> : public Param<uint32_t> {
|
||||
ActivationThreshold() : Param<uint32_t>(std::string("Entry Threshold"),
|
||||
[](const LimitCoverDefinition& def) { return def.gate(0).activation_threshold(); },
|
||||
[](LimitCoverDefinition& def, uint32_t v) {
|
||||
auto& g = def.mgate(0);
|
||||
g.activation_threshold(v);
|
||||
g.cover_threshold(v);
|
||||
g.delete_threshold(v);
|
||||
}) {}
|
||||
};
|
||||
|
||||
using Adapter = dynamic_adapter::DynamicOrderAdapter<LimitCoverDefinition,
|
||||
PriceOffset,
|
||||
// [gate 0]
|
||||
ActivationThreshold<0>,
|
||||
HedgeTicks<0>,
|
||||
TopTicksOff<0>,
|
||||
MaxChaseTicks<0>,
|
||||
TradeTowardBehavior<0>,
|
||||
// gate 1
|
||||
ActivationThreshold<1>,
|
||||
CoverThreshold<1>,
|
||||
DeleteThreshold<1>,
|
||||
HedgeTicks<1>,
|
||||
TopTicksOff<1>,
|
||||
MaxChaseTicks<1>,
|
||||
TradeTowardBehavior<1>,
|
||||
// gate 2
|
||||
ActivationThreshold<2>,
|
||||
CoverThreshold<2>,
|
||||
DeleteThreshold<2>,
|
||||
HedgeTicks<2>,
|
||||
TopTicksOff<2>,
|
||||
MaxChaseTicks<2>,
|
||||
TradeTowardBehavior<2>>;
|
||||
|
||||
LimitCoverDefinition LimitCoverAdapter::from_proto(const api::orderman::OrderDefinition& order) {
|
||||
if (order.has_custom_def())
|
||||
return from_proto(order.custom_def());
|
||||
return LimitCoverDefinition();
|
||||
}
|
||||
LimitCoverDefinition LimitCoverAdapter::from_proto(const api::orderman::CustomOrderDefinition& custom_def) {
|
||||
LimitCoverDefinition rv;
|
||||
if (custom_def.has_dynamic())
|
||||
Adapter().from_proto(custom_def.dynamic(), rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
#define INVALID_DEF(x) OrderOpResult(api::orderman::ORDER_ERROR_INVALID_ORDER_DEFINITON, x)
|
||||
|
||||
OrderOpResult LimitCoverAdapter::validate_proto(const api::orderman::DynamicOrderDefinition& dyn) {
|
||||
return Adapter().validate_proto(dyn) ?
|
||||
OrderOpResult() :
|
||||
INVALID_DEF("One or more dynamic parameters are missing or invalid.");
|
||||
}
|
||||
void LimitCoverAdapter::init_proto(const LimitCoverDefinition& def, api::orderman::DynamicOrderDefinition& dyn) {
|
||||
Adapter().to_proto(dyn, def);
|
||||
}
|
||||
|
||||
void LimitCoverAdapter::define_proto(api::orderman::DynamicOrderDescriptor& desc) {
|
||||
Adapter().define_proto(desc);
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverDefinition.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
struct LimitCoverAdapter {
|
||||
static LimitCoverDefinition from_proto(const api::orderman::OrderDefinition& order);
|
||||
static LimitCoverDefinition from_proto(const api::orderman::CustomOrderDefinition& custom_def);
|
||||
static OrderOpResult validate_proto(const api::orderman::DynamicOrderDefinition& order);
|
||||
static void init_proto(const LimitCoverDefinition& def, api::orderman::DynamicOrderDefinition& dyn);
|
||||
static void define_proto(api::orderman::DynamicOrderDescriptor& desc);
|
||||
};
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverGate.h"
|
||||
#include "lib/orderman/orderman_utils/Formatter.h"
|
||||
#include <ostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace limit_cover {
|
||||
// The exclusive maximum number of gates.
|
||||
// Note: the limit cover spec called for 3 gates (2 gates explicitly,
|
||||
// plus an initial activation threshold, which is treated as another gate).
|
||||
const uint32_t max_num_gates = 4;
|
||||
}
|
||||
struct LimitCoverDefinition {
|
||||
// price offset
|
||||
double price_offset() const {
|
||||
return _price_offset;
|
||||
}
|
||||
void price_offset(double value) {
|
||||
_price_offset = value;
|
||||
}
|
||||
|
||||
LimitCoverGate gate(uint32_t index) const {
|
||||
return _gate_or_default(index);
|
||||
}
|
||||
LimitCoverGate& mgate(uint32_t index) {
|
||||
return _ensure_gate(index);
|
||||
}
|
||||
void gate(uint32_t index, LimitCoverGate value) {
|
||||
_ensure_gate(index) = value;
|
||||
}
|
||||
|
||||
// The original LimitAlgo spec called for an initial activation thresh, and 2 gates
|
||||
// (fyi a gate has 3 members: activation thresh, cover thresh, and delete thresh).
|
||||
// The initial activation thresh serves as the initial cover, and delete threshes as well.
|
||||
// In this sense, the activation threshold can be considered as an 'initial gate' itself.
|
||||
// 'normal_gates' does just that: returns a map of both gates plus the 'initial gate.'
|
||||
std::map<uint32_t, LimitCoverGate> normal_gates() const {
|
||||
std::map<uint32_t, LimitCoverGate> m;
|
||||
for (auto g : _gates)
|
||||
m.insert(std::make_pair(g.activation_threshold(), g));
|
||||
return m;
|
||||
}
|
||||
private:
|
||||
double _price_offset;
|
||||
std::vector<LimitCoverGate> _gates;
|
||||
|
||||
LimitCoverGate& _ensure_gate(uint32_t index) {
|
||||
bts_assert(index < limit_cover::max_num_gates);
|
||||
while (_gates.size() <= index)
|
||||
_gates.push_back(LimitCoverGate());
|
||||
return _gates[index];
|
||||
}
|
||||
|
||||
LimitCoverGate _gate_or_default(uint32_t index) const {
|
||||
return _gates.size() > index ? _gates[index] : LimitCoverGate();
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& ostr, const LimitCoverDefinition& lcd) {
|
||||
auto ngates = lcd.normal_gates();
|
||||
ostr << "Price Offset: " << lcd.price_offset()
|
||||
<< ", Activation Thresh: " << lcd.gate(0).activation_threshold()
|
||||
<< ", Gates: " << Formatter::fmt_values(ngates);
|
||||
return ostr;
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/sparkproto/SparkProtos.pb.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <ostream>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
struct LimitCoverGate {
|
||||
LimitCoverGate() : LimitCoverGate(0, 0, 0) {}
|
||||
|
||||
LimitCoverGate(const LimitCoverGate& other)
|
||||
: _activation_threshold(other._activation_threshold), _definition(other._definition) {}
|
||||
|
||||
LimitCoverGate(uint32_t activation_thresh, uint32_t cover_thresh, uint32_t delete_thresh)
|
||||
: _activation_threshold(activation_thresh), _definition() {
|
||||
cover_threshold(cover_thresh);
|
||||
delete_threshold(delete_thresh);
|
||||
}
|
||||
|
||||
// activation threshold
|
||||
uint32_t activation_threshold() const { return _activation_threshold; }
|
||||
void activation_threshold(uint32_t value) { _activation_threshold = value; }
|
||||
|
||||
// cover threshold
|
||||
uint32_t cover_threshold() const {
|
||||
return _definition.has_cross_threshold() ? _get_absolute(_definition.cross_threshold()) : 0;
|
||||
}
|
||||
void cover_threshold(uint32_t value) {
|
||||
if (!_definition.has_cross_threshold()) {
|
||||
*_definition.mutable_cross_threshold() = bts::spark::QuantityMeasure();
|
||||
}
|
||||
_definition.mutable_cross_threshold()->set_absolute(value);
|
||||
}
|
||||
|
||||
// delete threshold
|
||||
uint32_t delete_threshold() const {
|
||||
return _definition.has_top_threshold() ? _get_absolute(_definition.top_threshold()) : 0;
|
||||
}
|
||||
void delete_threshold(uint32_t value) {
|
||||
if (!_definition.has_top_threshold()) {
|
||||
*_definition.mutable_top_threshold() = bts::spark::QuantityMeasure();
|
||||
}
|
||||
_definition.mutable_top_threshold()->set_absolute(value);
|
||||
}
|
||||
|
||||
// hedge ticks
|
||||
uint32_t hedge_ticks() const { return _definition.hedge_ticks(); }
|
||||
void hedge_ticks(uint32_t value) { _definition.set_hedge_ticks(value); }
|
||||
// top ticks offset
|
||||
uint32_t top_ticks_offset() const { return _definition.ticks_off(); }
|
||||
void top_ticks_offset(uint32_t value) { _definition.set_ticks_off(value); }
|
||||
// max chase ticks
|
||||
uint32_t max_chase_ticks() const { return _definition.max_chase_ticks(); }
|
||||
void max_chase_ticks(uint32_t value) { _definition.set_max_chase_ticks(value); }
|
||||
// on toward
|
||||
api::orderman::LeanOnToward on_toward() const { return _definition.trade_toward_behavior(); }
|
||||
void on_toward(api::orderman::LeanOnToward value) { _definition.set_trade_toward_behavior(value); }
|
||||
// definition
|
||||
const api::orderman::DeleteAndCoverDefinition& definition() const { return _definition; }
|
||||
api::orderman::DeleteAndCoverDefinition& mdefinition() { return _definition; }
|
||||
void definition(api::orderman::DeleteAndCoverDefinition value) { _definition = value; }
|
||||
|
||||
api::orderman::CustomOrderDefinition custom_def() const {
|
||||
api::orderman::CustomOrderDefinition cod;
|
||||
*(cod.mutable_delete_and_cover()) = definition();
|
||||
return cod;
|
||||
}
|
||||
void custom_def(const api::orderman::CustomOrderDefinition &cod) {
|
||||
if (cod.has_delete_and_cover())
|
||||
definition(cod.delete_and_cover());
|
||||
}
|
||||
private:
|
||||
uint32_t _activation_threshold;
|
||||
api::orderman::DeleteAndCoverDefinition _definition;
|
||||
|
||||
static inline uint32_t _get_absolute(spark::QuantityMeasure qty_measure, uint32_t fallback_value = 0) {
|
||||
return qty_measure.quantity_case() == spark::QuantityMeasure::kAbsolute ? qty_measure.absolute() : fallback_value;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& ostr, const LimitCoverGate& g) {
|
||||
ostr << "Activation Thresh: " << g.activation_threshold() << ", Definition: " << utils::pb::to_string(g.custom_def());
|
||||
return ostr;
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -71,11 +71,9 @@ struct AlgoTestDriverBase::Impl : public RecursionCounter {
|
||||
bool sub_position = false;
|
||||
|
||||
// Callbacks
|
||||
std::vector<std::function<void(OrderKey, Quantity, Price)>> on_child_launching;
|
||||
std::vector<std::function<void(OrderKey)>> on_child_launching;
|
||||
std::vector<std::function<void(const ChildOrder&)>> on_child_changing;
|
||||
std::vector<std::function<void(const ChildOrder&, const ChildOrder&)>> on_child_changed;
|
||||
// runs after the child change has been forwarded to the underlying algo.
|
||||
std::vector<std::function<void(const ChildOrder&, const ChildOrder&)>> after_child_changed;
|
||||
bool allow_overfill = false;
|
||||
|
||||
Impl(IAlgoOrder& o) : algo(o) {}
|
||||
@@ -208,20 +206,6 @@ const std::vector<ChildOrder>& AlgoTestDriverBase::child_orders() const {
|
||||
return me->orders;
|
||||
}
|
||||
|
||||
// This method asserts that the child exists. If the need arises for an
|
||||
// optional return, leave this method as is, and add a 'try_find_child' method.
|
||||
// These are tests, so it's reasonable to assume the caller is certain about
|
||||
// the existence of the child it seeks.
|
||||
const ChildOrder& AlgoTestDriverBase::find_child(OrderKey key) const {
|
||||
for (auto& c : me->orders) {
|
||||
if (c.key == key)
|
||||
return c;
|
||||
}
|
||||
REQUIRE(false);
|
||||
bts_assert(false);
|
||||
return me->orders.front();
|
||||
}
|
||||
|
||||
const std::vector<ChildOrder>& AlgoTestDriverBase::detached_orders() const {
|
||||
return me->detached_orders;
|
||||
}
|
||||
@@ -253,7 +237,6 @@ void AlgoTestDriverBase::fill(OrderKey k, Price price, Quantity qty) {
|
||||
for (const auto& cb : me->on_child_changed) cb(old, child);
|
||||
// No defer because this is called from outside
|
||||
if (child.subscribed) me->algo.on_child_change(k, child.cstate);
|
||||
for (const auto& cb : me->after_child_changed) cb(old, child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -290,7 +273,7 @@ void AlgoTestDriverBase::fill(Price p, Quantity q) {
|
||||
}
|
||||
}
|
||||
|
||||
void AlgoTestDriverBase::on_child_launching(std::function<void(OrderKey, Quantity, Price)>&& cb) {
|
||||
void AlgoTestDriverBase::on_child_launching(std::function<void(OrderKey)>&& cb) {
|
||||
me->on_child_launching.push_back(cb);
|
||||
}
|
||||
void AlgoTestDriverBase::on_child_changing(std::function<void(const ChildOrder&)>&& cb) {
|
||||
@@ -299,21 +282,18 @@ void AlgoTestDriverBase::on_child_changing(std::function<void(const ChildOrder&)
|
||||
void AlgoTestDriverBase::on_child_changed(std::function<void(const ChildOrder&, const ChildOrder&)>&& cb) {
|
||||
me->on_child_changed.push_back(cb);
|
||||
}
|
||||
void AlgoTestDriverBase::after_child_changed(std::function<void(const ChildOrder&, const ChildOrder&)>&& cb) {
|
||||
me->after_child_changed.push_back(cb);
|
||||
}
|
||||
|
||||
bool AlgoTestDriverBase::allow_overfill() const { return me->allow_overfill; }
|
||||
void AlgoTestDriverBase::allow_overfill(bool value) { me->allow_overfill = value; }
|
||||
|
||||
//
|
||||
// Utility
|
||||
|
||||
ChildOrder mkchild(OrderKey key, Price price, Quantity qty, bool sub, ChildStatus status = ChildStatus::WORKING) {
|
||||
ChildOrder mkchild(OrderKey key, Price price, Quantity qty, bool sub) {
|
||||
ChildOrder result = {};
|
||||
result.key = key;
|
||||
result.cstate.price = price;
|
||||
result.cstate.quantity = qty;
|
||||
result.cstate.status = status;
|
||||
result.subscribed = sub;
|
||||
return result;
|
||||
}
|
||||
@@ -427,7 +407,7 @@ OrderKey AlgoTestDriverBase::launch_child(ChildTemplate&, Price price, Quantity
|
||||
OrderKey key = me->next_order();
|
||||
me->orders.push_back(
|
||||
mkchild(key, price, qty, sub));
|
||||
for(const auto& cb : me->on_child_launching) cb(key, qty, price);
|
||||
for(const auto& cb : me->on_child_launching) cb(key);
|
||||
return key;
|
||||
}
|
||||
OrderKey AlgoTestDriverBase::launch_child(api::orderman::OrderDefinition const & def, Price price, Quantity qty, bool sub) {
|
||||
@@ -435,7 +415,7 @@ OrderKey AlgoTestDriverBase::launch_child(api::orderman::OrderDefinition const &
|
||||
me->orders.push_back(
|
||||
mkchild(key, price, qty, sub));
|
||||
me->orders.back().def = {def};
|
||||
for(const auto& cb : me->on_child_launching) cb(key, qty, price);
|
||||
for(const auto& cb : me->on_child_launching) cb(key);
|
||||
return key;
|
||||
}
|
||||
void AlgoTestDriverBase::update_child(OrderKey key, api::orderman::Update const & update) {
|
||||
|
||||
@@ -61,8 +61,6 @@ class AlgoTestDriverBase : private IAlgoServices {
|
||||
Quantity total_fills() const override;
|
||||
bool is_shutting_down() const override;
|
||||
std::vector<PositionUpdate> const & current_positions() const override;
|
||||
void update_child(OrderKey , api::orderman::Update const &) override;
|
||||
void cancel_child(OrderKey) override;
|
||||
//
|
||||
// Change state
|
||||
|
||||
@@ -81,7 +79,6 @@ class AlgoTestDriverBase : private IAlgoServices {
|
||||
//
|
||||
// Inspection methods
|
||||
const std::vector<ChildOrder>& child_orders() const;
|
||||
const ChildOrder& find_child(OrderKey) const;
|
||||
const std::vector<ChildOrder>& detached_orders() const;
|
||||
AlgoState state() const;
|
||||
|
||||
@@ -90,11 +87,10 @@ class AlgoTestDriverBase : private IAlgoServices {
|
||||
// Fill this quantity, start with the largest child order
|
||||
void fill(Price, Quantity);
|
||||
|
||||
void on_child_launching(std::function<void(OrderKey, Quantity, Price)>&&);
|
||||
void on_child_launching(std::function<void(OrderKey)>&&);
|
||||
void on_child_changing(std::function<void(const ChildOrder&)>&&);
|
||||
void on_child_changed(std::function<void(const ChildOrder&, const ChildOrder&)>&&);
|
||||
void after_child_changed(std::function<void(const ChildOrder&, const ChildOrder&)>&&);
|
||||
|
||||
|
||||
//
|
||||
// Tweak internal REQUIRE
|
||||
bool allow_overfill() const;
|
||||
@@ -134,6 +130,8 @@ private:
|
||||
OrderKey launch_child(ChildTemplate &, Price, Quantity, bool) override;
|
||||
OrderKey launch_child(api::orderman::OrderDefinition const &, Price,
|
||||
Quantity, bool) override;
|
||||
void update_child(OrderKey , api::orderman::Update const &) override;
|
||||
void cancel_child(OrderKey) override;
|
||||
void detach_child(OrderKey) override;
|
||||
void detach_all_children() override;
|
||||
|
||||
@@ -159,17 +157,4 @@ struct AlgoTestDriver : public AlgoTestDriverBase
|
||||
const Algo& algo() const { return _algo; }
|
||||
};
|
||||
|
||||
inline std::ostream& operator <<(std::ostream& ostr, const AlgoState& state) {
|
||||
switch (state) {
|
||||
case AlgoState::CREATED: ostr << "CREATED"; break;
|
||||
case AlgoState::READY: ostr << "READY"; break;
|
||||
case AlgoState::RUNNING: ostr << "RUNNING"; break;
|
||||
case AlgoState::CANCELED: ostr << "CANCELED"; break;
|
||||
case AlgoState::TERMINATED: ostr << "TERMINATED"; break;
|
||||
case AlgoState::COMPLETED: ostr << "COMPLETED"; break;
|
||||
default: ostr << "AlgoState_" << static_cast<int>(state); break;
|
||||
}
|
||||
return ostr;
|
||||
}
|
||||
|
||||
}}}//namespace
|
||||
@@ -1,196 +0,0 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/LimitCoverAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/AlgoTestDriver.h"
|
||||
#include "catch/catch.hpp"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/orderman/orderman_utils/Book.h"
|
||||
#include "lib/orderman/orderman_utils/BookUtils.h"
|
||||
#include "lib/orderman/orderman_utils/Formatter.h"
|
||||
#include "lib/orderman/orderman_utils/PriceComp.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_utils.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverAdapter.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/limit_cover/LimitCoverState.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/limit_cover/LimitCoverTestUtils.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/limit_cover/TestSteps.h"
|
||||
#include "lib/sparkproto/SparkProtos.pb.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include <variant>
|
||||
#include <cmath>
|
||||
|
||||
// for some reason, intellisense poops its pants without 'using namespace std.'
|
||||
// std::optional<T>, and std::unordered_map<T>, in particular, break intellisense for the whole document.
|
||||
using namespace std;
|
||||
using namespace bts::api::orderman;
|
||||
using namespace bts::orderman::limit_cover::utils;
|
||||
//#define PRINT_SUMMARIES
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace test {
|
||||
namespace limit_cover {
|
||||
// Glossary:
|
||||
// * limit-price:
|
||||
// The price at which the main child is deployed (computed as a tick offset from the settlement).
|
||||
// * limit-volume:
|
||||
// The same-side market volume at or better than the limit price.
|
||||
// This is recomputed whenever the book changes (obviously).
|
||||
// * volume-gate:
|
||||
// * volume-gate: a simple struct with 3 fields:
|
||||
// 1) activation_threshold (uint)
|
||||
// 2) cover_threshold (uint) must be <= activation_thresh
|
||||
// 3) delete_threshold (uint) must be <= activation_thresh
|
||||
// * current-volume-gate:
|
||||
// The volume gate with the highest activation threshold that is still
|
||||
// <= the limit-volume. This is recomputed whenever the book changes
|
||||
// (obviously).
|
||||
// * volume-gates:
|
||||
// A series of volume gates, sorted by 'activation_threshold.'
|
||||
// Initially, the current-volume-gate is one with the lowest activation threshold.
|
||||
// The main child is not deployed until the limit-volume reaches that threshold.
|
||||
// After that, the algo continues to observe the limit-volume, removing gates with lower
|
||||
// thresholds when the volume breeches the next gate's thresh.
|
||||
|
||||
// Test Assertions:
|
||||
// * child is not launched until the limit-volume reaches the initial activation threshold.
|
||||
// * limit-volume is always correct (verify whenever the book changes).
|
||||
// * current-volume-gate is always correct (verify whenever the book changes).
|
||||
|
||||
// Test Design:
|
||||
// * expectations of the limit cover algo's state are stored in an instance of 'LimitCoverState.'
|
||||
// The actual LimitCoverAlgo does not use this class; the tests just use it as a means
|
||||
// to validate the actual LimitCoverAlgo's state, and to wrap calls to the LimitCoverAlgo
|
||||
// with validation checks.
|
||||
|
||||
// * each test implementation should be executed 6 times:
|
||||
// 2 x 3 = test both sides, with each of the following termination steps:
|
||||
// 1) Fill to completion
|
||||
// 2) Cancel the algo
|
||||
// 3) Cancel the child
|
||||
|
||||
// * Restrictions on the parameters:
|
||||
// - the spec explicitly called for 3 volume gates, and specified that the 3 thresholds
|
||||
// (activation, cover, and delete) be the same for the first gate
|
||||
// (technically, the spec described 2 gates and an 'entry threshold').
|
||||
// - the algo should immediately cancel itself if these parameters are not the case.
|
||||
// while it would most likely not cause a problem, these assumptions allow the tests to focus
|
||||
// on a narrow range of inputs (and of course, such restrictions naturally decrease the
|
||||
// chance of a bug slipping through).
|
||||
|
||||
LimitCoverState init_state(
|
||||
OrderDefinition& mdef,
|
||||
Side side,
|
||||
uint32_t quantity,
|
||||
double price,
|
||||
double price_offset,
|
||||
std::vector<LimitCoverGate> gates,
|
||||
bool cancel_hedges_on_cancel = true,
|
||||
bool cancel_on_external_hedge_cancel = false) {
|
||||
//
|
||||
REQUIRE(!gates.empty());
|
||||
for (auto& g : gates) {
|
||||
g.mdefinition().set_cancel_hedges_on_cancel(cancel_hedges_on_cancel);
|
||||
g.mdefinition().set_cancel_on_external_hedge_cancel(cancel_on_external_hedge_cancel);
|
||||
}
|
||||
LimitCoverDefinition lc_def;
|
||||
lc_def.price_offset(price_offset);
|
||||
uint32_t gate_pos = 0;
|
||||
for (auto g : gates) {
|
||||
lc_def.gate(gate_pos++, g);
|
||||
}
|
||||
DynamicOrderDefinition dyn_def;
|
||||
LimitCoverAdapter::init_proto(lc_def, dyn_def);
|
||||
*(mdef.mutable_custom_def()->mutable_dynamic()) = dyn_def;
|
||||
mdef.set_side(side);
|
||||
return LimitCoverState(mdef, quantity, price);
|
||||
}
|
||||
|
||||
template<typename... Steps>
|
||||
inline void test_impl(Side side, uint32_t qty, double price_offset, double settle, std::vector<LimitCoverGate> gates, Steps... steps) {
|
||||
std::cout << std::endl << std::endl;
|
||||
std::cout << "*************************************************************************************************************************" << std::endl;
|
||||
std::cout << "=========================================================================================================================" << std::endl;
|
||||
int step_count = 1;
|
||||
std::cout << "Test " << "[" << Formatter::to_string(side) << " Order]" << std::endl << "\t1. "
|
||||
<< Formatter::join([&]() {
|
||||
return Formatter::fmt("\n\t", ++step_count, ". ");
|
||||
}, steps...)
|
||||
<< std::endl;
|
||||
std::cout << "=========================================================================================================================" << std::endl;
|
||||
std::cout << "*************************************************************************************************************************" << std::endl;
|
||||
std::cout << std::endl;
|
||||
OrderDefinition def;
|
||||
// price is irrelevant
|
||||
auto state = init_state(def, side, qty, -99999, price_offset, gates);
|
||||
state.settlement(settle);
|
||||
(steps.execute(state), ...);
|
||||
}
|
||||
|
||||
template<typename... Steps>
|
||||
inline void test_permutations(
|
||||
uint32_t qty,
|
||||
double settle,
|
||||
std::vector<LimitCoverGate> gates,
|
||||
Steps... steps) {
|
||||
std::vector<double> price_offsets { 0.0, 0.75, 1.25, -0.75, -1.25 };
|
||||
for (auto po : price_offsets) {
|
||||
// complete
|
||||
test_impl(api::SIDE_BUY, qty, po, settle, gates, steps..., test_steps::Complete());
|
||||
test_impl(api::SIDE_SELL, qty, po, settle, gates, steps..., test_steps::Complete());
|
||||
// cancel child
|
||||
test_impl(api::SIDE_BUY, qty, po, settle, gates, steps..., test_steps::CancelChild());
|
||||
test_impl(api::SIDE_SELL, qty, po, settle, gates, steps..., test_steps::CancelChild());
|
||||
// cancel algo
|
||||
test_impl(api::SIDE_BUY, qty, po, settle, gates, steps..., test_steps::CancelAlgo());
|
||||
test_impl(api::SIDE_SELL, qty, po, settle, gates, steps..., test_steps::CancelAlgo());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("test_activation_thresholds.", "[algo][limit_cover]") {
|
||||
SECTION("Activate 1st gate.") {
|
||||
test_permutations(25, 100, {
|
||||
LimitCoverGate(4, 4, 4),
|
||||
LimitCoverGate(10, 7, 5),
|
||||
LimitCoverGate(15, 12, 11),
|
||||
},
|
||||
// test steps
|
||||
test_steps::BeginLaunch(),
|
||||
test_steps::Launch(),
|
||||
test_steps::BreechActivationThresh());
|
||||
}
|
||||
SECTION("Activate 1st then 2nd gate.") {
|
||||
test_permutations(25, 100, {
|
||||
LimitCoverGate(4, 4, 4),
|
||||
LimitCoverGate(10, 7, 5),
|
||||
LimitCoverGate(15, 12, 11),
|
||||
},
|
||||
// test steps
|
||||
test_steps::BeginLaunch(),
|
||||
test_steps::Launch(),
|
||||
test_steps::BreechActivationThresh(),
|
||||
test_steps::Fill([](const auto& state) { return test_params::fills::child(state, 8); }),
|
||||
test_steps::BreechActivationThresh(),
|
||||
test_steps::Fill([](const auto& state) { return test_params::fills::child(state, 2); }),
|
||||
test_steps::BreechActivationThresh());
|
||||
}
|
||||
SECTION("Activate 2nd gate.") {
|
||||
test_permutations(25, 100, {
|
||||
LimitCoverGate(4, 4, 4),
|
||||
LimitCoverGate(10, 7, 5),
|
||||
LimitCoverGate(15, 12, 11),
|
||||
},
|
||||
// test steps
|
||||
test_steps::BeginLaunch(),
|
||||
test_steps::Launch(),
|
||||
test_steps::BreechActivationThresh(1, 1),
|
||||
test_steps::Fill([](const auto& state) { return test_params::fills::child(state, 8); }));
|
||||
}
|
||||
}
|
||||
} // namespace limit_cover
|
||||
} // namespace test
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,199 +0,0 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/limit_cover/LimitCoverState.h"
|
||||
#include "catch/catch.hpp"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/md/IntPrice.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/orderman/orderman_utils/BookUtils.h"
|
||||
#include "lib/orderman/orderman_utils/Formatter.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverAdapter.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/limit_cover/LimitCoverTestUtils.h"
|
||||
|
||||
#define VALIDATE Validation __VALIDATION(this)
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace test {
|
||||
namespace limit_cover {
|
||||
using namespace std;
|
||||
using namespace bts::api::orderman;
|
||||
using namespace bts::orderman::limit_cover::utils;
|
||||
using Size = bts::md::Size;
|
||||
using Side = bts::api::Side;
|
||||
|
||||
LimitCoverState::LimitCoverState(
|
||||
const OrderDefinition& def,
|
||||
uint32_t quantity,
|
||||
double price)
|
||||
: driver(def, int_price(price), Quantity(quantity)), _definition(def), _gates(), _book(SecurityKey(security().id())) {
|
||||
auto ngs = limit_cover().normal_gates();
|
||||
_gates.insert(ngs.begin(), ngs.end());
|
||||
// attach callbacks
|
||||
driver.on_child_launching([&](OrderKey key, Quantity child_qty, Price child_price) {
|
||||
// should never deploy twice
|
||||
REQUIRE(_child == null_order_key());
|
||||
REQUIRE(key != null_order_key());
|
||||
_child = key;
|
||||
// qty should match the algo's, but price must be the limit price
|
||||
// (the algo's price is irrelevant)
|
||||
REQUIRE(child_qty.value() == driver.quantity().value());
|
||||
REQUIRE(child_price.value() == limit_price());
|
||||
// todo: verify delete and cover def matches the current gate
|
||||
//REQUIRE(child_def.custom_def().delete_and_cover())
|
||||
REQUIRE(limit_volume() >= activation_threshold());
|
||||
});
|
||||
driver.on_child_changed([&](const ChildOrder&, const ChildOrder& new_state) {
|
||||
REQUIRE(new_state.key == _child);
|
||||
REQUIRE(new_state.def.has_value());
|
||||
REQUIRE(new_state.def.value().has_custom_def());
|
||||
REQUIRE(new_state.def.value().custom_def().has_delete_and_cover());
|
||||
});
|
||||
driver.after_child_changed([&](const ChildOrder&, const ChildOrder& new_state) {
|
||||
REQUIRE(driver.total_fills().value() == new_state.cstate.total_fills.value());
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t LimitCoverState::quantity() const {
|
||||
return driver.quantity().value();
|
||||
}
|
||||
double LimitCoverState::price() const {
|
||||
return driver.price().value();
|
||||
}
|
||||
const api::Security& LimitCoverState::security() const {
|
||||
return driver.security();
|
||||
}
|
||||
const bts::api::orderman::OrderDefinition& LimitCoverState::def() const {
|
||||
return _definition;
|
||||
}
|
||||
LimitCoverDefinition LimitCoverState::limit_cover() const {
|
||||
return LimitCoverAdapter::from_proto(def());
|
||||
}
|
||||
uint32_t LimitCoverState::limit_volume() const {
|
||||
return _limit_volume;
|
||||
}
|
||||
double LimitCoverState::limit_price() const {
|
||||
return _limit_price;
|
||||
}
|
||||
OrderKey LimitCoverState::child() const {
|
||||
return _child;
|
||||
}
|
||||
optional<ChildOrder> LimitCoverState::child_state() const {
|
||||
if (has_been_deployed()) {
|
||||
return ChildOrder(*driver.child_orders().begin());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
void LimitCoverState::update_child(const Update& u) {
|
||||
driver.update_child(child(), u);
|
||||
}
|
||||
bool LimitCoverState::has_been_deployed() const {
|
||||
return _child != null_order_key();
|
||||
}
|
||||
bool LimitCoverState::is_terminated() const {
|
||||
switch (driver.state()) {
|
||||
case AlgoState::CANCELED:
|
||||
case AlgoState::TERMINATED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const std::map<uint32_t, LimitCoverGate>& LimitCoverState::gates() const {
|
||||
return _gates;
|
||||
}
|
||||
const LimitCoverGate& LimitCoverState::curr_gate() const {
|
||||
REQUIRE(!gates().empty());
|
||||
return gates().begin()->second;
|
||||
}
|
||||
uint32_t LimitCoverState::activation_threshold() const {
|
||||
return gates().empty() ? std::numeric_limits<uint32_t>::max() : gates().begin()->first;
|
||||
}
|
||||
// settlement
|
||||
double LimitCoverState::settlement() const { return _settlement; }
|
||||
void LimitCoverState::settlement(double value) {
|
||||
VALIDATE;
|
||||
// the algo is not supposed to disregard settlement changes
|
||||
// after the child has been deployed
|
||||
if (!has_been_deployed()) {
|
||||
_settlement = value;
|
||||
recompute_all();
|
||||
}
|
||||
driver.update_settlement(SecurityKey(security().id()), Price(value));
|
||||
}
|
||||
|
||||
// book
|
||||
const Book& LimitCoverState::book() const { return _book; }
|
||||
void LimitCoverState::book(Book value) {
|
||||
VALIDATE;
|
||||
_book = value;
|
||||
recompute_all();
|
||||
driver.update_book(value);
|
||||
}
|
||||
|
||||
OrderOpResult LimitCoverState::before_launch() {
|
||||
_gates.clear();
|
||||
auto ngs = limit_cover().normal_gates();
|
||||
_gates.insert(ngs.begin(), ngs.end());
|
||||
return driver.before_launch();
|
||||
}
|
||||
void LimitCoverState::launch() {
|
||||
VALIDATE;
|
||||
_is_initialized = true;
|
||||
recompute_all();
|
||||
driver.launch();
|
||||
}
|
||||
void LimitCoverState::do_update(const Update& upd) {
|
||||
REQUIRE(!is_terminated());
|
||||
// updates should not affect the child's price (after it has been launched).
|
||||
// this is bc the literal 'price' parameter is irrelevant to this algo,
|
||||
// for all intents and purposes, its price is the tick offset from settlement.
|
||||
auto upd_valid = validate_update(upd);
|
||||
// keep definition in sync with update
|
||||
if (upd.has_custom() && upd_valid.ok()) {
|
||||
*(_definition.mutable_custom_def()) = upd.custom();
|
||||
}
|
||||
driver.do_update(upd);
|
||||
if (upd_valid.ok()) {
|
||||
recompute_all();
|
||||
} else {
|
||||
REQUIRE(driver.state() == AlgoState::TERMINATED);
|
||||
}
|
||||
}
|
||||
|
||||
void LimitCoverState::recompute_limit_price() {
|
||||
_limit_price = os::is_nan(settlement()) ? os::nan() :
|
||||
securities::improve_price(side(), Price(settlement()), Price(limit_cover().price_offset()))
|
||||
.value();
|
||||
}
|
||||
|
||||
void LimitCoverState::recompute_limit_volume() {
|
||||
_limit_volume = os::is_nan(settlement()) ? os::nan() :
|
||||
BookUtils::volume_at_or_better(_book, side(), limit_price());
|
||||
}
|
||||
|
||||
void LimitCoverState::recompute_all() {
|
||||
recompute_limit_price();
|
||||
recompute_limit_volume();
|
||||
if (_is_initialized)
|
||||
recompute_gate();
|
||||
}
|
||||
|
||||
void LimitCoverState::recompute_gate() {
|
||||
if (gates().empty())
|
||||
return;
|
||||
auto it = _gates.lower_bound(limit_volume());
|
||||
if (it->first > limit_volume() && it != _gates.begin())
|
||||
it--;
|
||||
_gates.erase(_gates.begin(), it);
|
||||
}
|
||||
|
||||
LimitCoverState::ValidateDeployment::ValidateDeployment(LimitCoverState* state) :
|
||||
_state(state), _had_been_deployed(state->has_been_deployed()) { }
|
||||
|
||||
LimitCoverState::ValidateDeployment::~ValidateDeployment() {
|
||||
if (!_had_been_deployed && _state->limit_volume() >= _state->activation_threshold())
|
||||
REQUIRE(_state->has_been_deployed());
|
||||
}
|
||||
} // namespace limit_cover
|
||||
} // namespace test
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,121 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/orderman/orderman_utils/Book.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/LimitCoverAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverDefinition.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/AlgoTestDriver.h"
|
||||
#include "lib/sparkproto/SparkProtos.pb.h"
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace test {
|
||||
namespace limit_cover {
|
||||
struct LimitCoverState {
|
||||
AlgoTestDriver<LimitCoverAlgo> driver;
|
||||
|
||||
LimitCoverState(
|
||||
const api::orderman::OrderDefinition& def,
|
||||
uint32_t quantity,
|
||||
double price);
|
||||
|
||||
const api::orderman::OrderDefinition& def() const;
|
||||
uint32_t quantity() const;
|
||||
double price() const;
|
||||
const api::Security& security() const;
|
||||
LimitCoverDefinition limit_cover() const;
|
||||
api::Side side() const { return def().side(); }
|
||||
uint32_t activation_threshold() const;
|
||||
// main child
|
||||
OrderKey child() const;
|
||||
std::optional<ChildOrder> child_state() const;
|
||||
// gates/curr gate
|
||||
const std::map<uint32_t, LimitCoverGate>& gates() const;
|
||||
const LimitCoverGate& curr_gate() const;
|
||||
// book
|
||||
const Book& book() const;
|
||||
void book(Book new_book);
|
||||
//
|
||||
bool has_been_deployed() const;
|
||||
// settle
|
||||
double settlement() const;
|
||||
void settlement(double value);
|
||||
// limit price
|
||||
double limit_price() const;
|
||||
// limit volume
|
||||
uint32_t limit_volume() const;
|
||||
bool is_terminated() const;
|
||||
OrderOpResult before_launch();
|
||||
void launch();
|
||||
void do_update(const api::orderman::Update&);
|
||||
void update_child(const api::orderman::Update&);
|
||||
|
||||
private:
|
||||
api::orderman::OrderDefinition _definition;
|
||||
uint32_t _limit_volume = 0;
|
||||
OrderKey _child;
|
||||
double _limit_price = os::nan();
|
||||
double _settlement = os::nan();
|
||||
bool _is_initialized = false;
|
||||
std::map<uint32_t, LimitCoverGate> _gates;
|
||||
Book _book;
|
||||
|
||||
void recompute_limit_price();
|
||||
void recompute_limit_volume();
|
||||
void recompute_all();
|
||||
void recompute_gate();
|
||||
|
||||
struct ValidateDeployment {
|
||||
ValidateDeployment(LimitCoverState* state);
|
||||
~ValidateDeployment();
|
||||
|
||||
private:
|
||||
LimitCoverState* _state;
|
||||
bool _had_been_deployed;
|
||||
};
|
||||
|
||||
struct Validation : public ValidateDeployment {
|
||||
Validation(LimitCoverState* state)
|
||||
: ValidateDeployment(state) {}
|
||||
|
||||
~Validation() {}
|
||||
};
|
||||
};
|
||||
|
||||
// custom formatting uses 1 line (unlike pb::to_string).
|
||||
inline std::ostream& operator<<(std::ostream& ostr, const bts::spark::QuantityMeasure& qm) {
|
||||
switch (qm.quantity_case()) {
|
||||
case bts::spark::QuantityMeasure::kAbsolute:
|
||||
ostr << "[absolute] " << qm.absolute();
|
||||
break;
|
||||
case bts::spark::QuantityMeasure::kRatio:
|
||||
ostr << "[ratio] " << qm.ratio();
|
||||
break;
|
||||
default:
|
||||
ostr << "[quantity_case_" << static_cast<int>(qm.quantity_case()) << "]";
|
||||
break;
|
||||
}
|
||||
return ostr;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& ostr, const LimitCoverState& lcs) {
|
||||
ostr << "qty: " << lcs.quantity() << std::endl;
|
||||
ostr << "limit price: " << lcs.limit_price() << std::endl;
|
||||
ostr << "limit volume: " << lcs.limit_volume() << std::endl;
|
||||
ostr << "activation/curr gate thresh: " << lcs.activation_threshold() << std::endl;
|
||||
ostr << "settlement: " << lcs.settlement() << std::endl;
|
||||
ostr << "bbo: " << Formatter::to_string(lcs.book()) << std::endl;
|
||||
ostr << "has been deployed: " << lcs.has_been_deployed() << std::endl;
|
||||
ostr << "status: " << lcs.driver.state() << std::endl;
|
||||
ostr << "child: " << lcs.child() << std::endl;
|
||||
ostr << "child qty: " << (lcs.child_state().has_value() ? lcs.child_state().value().cstate.quantity.value() : 0) << std::endl;
|
||||
ostr << "filled qty: " << lcs.driver.total_fills().value() << std::endl;
|
||||
return ostr;
|
||||
}
|
||||
} // namespace limit_cover
|
||||
} // namespace test
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/md/IntPrice.h"
|
||||
#include "lib/orderman/orderman_utils/Formatter.h"
|
||||
#include "lib/sparkproto/SparkProtos.pb.h"
|
||||
#include <iostream>
|
||||
|
||||
#define SUMMARIZE(...) Summary __summary__ (Formatter::fmt(__VA_ARGS__))
|
||||
|
||||
using Side = bts::api::Side;
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace test {
|
||||
namespace limit_cover {
|
||||
using namespace std;
|
||||
inline bts::md::IntPrice int_price(double value) {
|
||||
return bts::md::IntPrice_from_double<8>(value);
|
||||
}
|
||||
inline bts::md::PriceSize price_size(double price, uint32_t size) {
|
||||
return bts::md::PriceSize(int_price(price), size);
|
||||
}
|
||||
inline bts::spark::QuantityMeasure qty_measure(uint32_t abs) {
|
||||
bts::spark::QuantityMeasure qm;
|
||||
qm.set_absolute(abs);
|
||||
return qm;
|
||||
}
|
||||
|
||||
struct Summary {
|
||||
Summary(std::string header)
|
||||
: _header(header) {
|
||||
std::cout << "Begin " << header << std::endl;
|
||||
}
|
||||
~Summary() {
|
||||
std::cout << "End " << _header << std::endl;
|
||||
}
|
||||
private:
|
||||
std::string _header;
|
||||
};
|
||||
|
||||
} // namespace limit_cover
|
||||
} // namespace test
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,221 +0,0 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/limit_cover/TestSteps.h"
|
||||
#include "catch/catch.hpp"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/limit_cover/LimitCoverTestUtils.h"
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace test {
|
||||
namespace limit_cover {
|
||||
|
||||
std::string ITestStep::name() const { return "step with no name"; }
|
||||
void ITestStep::execute(LimitCoverState &state) const {
|
||||
SUMMARIZE(name());
|
||||
assert_pre_conditions(state);
|
||||
_execute(state);
|
||||
assert_post_conditions(state);
|
||||
}
|
||||
void ITestStep::assert_pre_conditions_core(const LimitCoverState &) const {}
|
||||
void ITestStep::execute_core(LimitCoverState &) const { bts_assert(false); }
|
||||
void ITestStep::assert_post_conditions_core(const LimitCoverState &) const {}
|
||||
void ITestStep::assert_pre_conditions(const LimitCoverState &state) const {
|
||||
// SUMMARIZE("Asserting Pre-conditions (", name(), ")...");
|
||||
assert_pre_conditions_core(state);
|
||||
}
|
||||
void ITestStep::assert_post_conditions(const LimitCoverState &state) const {
|
||||
// SUMMARIZE("Asserting Post-conditions (", name(), ")...");
|
||||
assert_post_conditions_core(state);
|
||||
}
|
||||
void ITestStep::_execute(LimitCoverState &state) const {
|
||||
// SUMMARIZE("Executing (", name(), ")...");
|
||||
execute_core(state);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& ostr, const ITestStep& ts) {
|
||||
ostr << ts.name();
|
||||
return ostr;
|
||||
}
|
||||
|
||||
LimitCoverState& operator<<(LimitCoverState& state, const ITestStep& ts) {
|
||||
SUMMARIZE(ts.name());
|
||||
ts.execute(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Test Params
|
||||
/////////////////////
|
||||
namespace test_params {
|
||||
namespace fills {
|
||||
Fill child(const LimitCoverState& state, std::optional<uint32_t> qty, std::optional<double> price) {
|
||||
if (!qty.has_value()) {
|
||||
REQUIRE(state.child_state().value().active_qty() > 0);
|
||||
}
|
||||
if (!price.has_value()) {
|
||||
REQUIRE(state.child_state().has_value());
|
||||
}
|
||||
return test_params::Fill(
|
||||
qty.has_value() ? qty.value() : state.child_state().value().active_qty(),
|
||||
price.has_value() ? price.value() : state.child_state().value().cstate.price.value());
|
||||
}
|
||||
} // namespace fills
|
||||
} // namespace test_params
|
||||
|
||||
/////////////////////
|
||||
// Order Selectors
|
||||
/////////////////////
|
||||
namespace order_selectors {
|
||||
std::vector<OrderKey> child(const LimitCoverState& state) {
|
||||
std::vector<OrderKey> rv;
|
||||
if (state.has_been_deployed()) {
|
||||
rv.push_back(state.child());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
std::vector<OrderKey> self(const LimitCoverState&) {
|
||||
std::vector<OrderKey> rv;
|
||||
rv.push_back(null_order_key());
|
||||
return rv;
|
||||
}
|
||||
} // namespace order_selectors
|
||||
|
||||
/////////////////////////
|
||||
// Order Ops (step impls)
|
||||
/////////////////////////
|
||||
namespace ops {
|
||||
void begin_launch(LimitCoverState& state) {
|
||||
REQUIRE(state.driver.before_launch().ok());
|
||||
}
|
||||
|
||||
void launch(LimitCoverState& state) {
|
||||
state.driver.launch();
|
||||
}
|
||||
|
||||
void breech_activation_thresh(LimitCoverState& state, uint32_t thresh_count, int offset) {
|
||||
auto book = state.book();
|
||||
REQUIRE(!os::is_nan(state.limit_price()));
|
||||
auto gate_it = state.gates().upper_bound(state.limit_volume());
|
||||
REQUIRE(gate_it != state.gates().end());
|
||||
while (thresh_count-- > 0 && gate_it != state.gates().end())
|
||||
gate_it++;
|
||||
auto thresh = gate_it->first + offset;
|
||||
int gi = 1;
|
||||
auto& bs = book.side(state.side());
|
||||
if (bs[0].size() == 0) {
|
||||
bs[0] = price_size(state.limit_price(), thresh);
|
||||
} else if (BookUtils::price_comp(state.side())(state.limit_price(), bs[0].price())) {
|
||||
// shift existing levels down 1 index,
|
||||
// put the limit volume at the top
|
||||
for (int i = bs.size() - 1; i > 0; i--) {
|
||||
bs[i] = bs[i - 1];
|
||||
}
|
||||
bs[0] = price_size(state.limit_price(), thresh);
|
||||
} else {
|
||||
// add the difference to the qty for the best price
|
||||
bs[0] = price_size(bs[0].price(), bs[0].size() + thresh - state.limit_volume());
|
||||
}
|
||||
state.book(book);
|
||||
}
|
||||
|
||||
void fill_child(LimitCoverState& state, uint32_t qty, double price, bool allow_overfill) {
|
||||
SUMMARIZE("filling child: ", Formatter::fmt_order(state.side(), qty, price));
|
||||
bts_assert(os::is_finite(price));
|
||||
bts_assert(qty > 0);
|
||||
REQUIRE(state.has_been_deployed());
|
||||
REQUIRE(!state.is_terminated());
|
||||
uint32_t orig_filled_qty = state.driver.total_fills().value();
|
||||
if (state.child_state().has_value()) {
|
||||
auto cs = state.child_state().value();
|
||||
REQUIRE(((cs.cstate.total_fills.value() - cs.cstate.fills_at_attach_time.value()) == orig_filled_qty));
|
||||
if (!allow_overfill) {
|
||||
REQUIRE(cs.active_qty() >= qty);
|
||||
}
|
||||
} else {
|
||||
REQUIRE(orig_filled_qty == 0);
|
||||
}
|
||||
state.driver.fill(state.child(), Price(price), Quantity(qty));
|
||||
}
|
||||
|
||||
void fill_child(LimitCoverState& lcs, uint32_t qty, bool allow_overfill) {
|
||||
fill_child(lcs, qty, lcs.limit_price(), allow_overfill);
|
||||
}
|
||||
|
||||
void cancel_child(LimitCoverState& state) {
|
||||
REQUIRE(state.has_been_deployed());
|
||||
REQUIRE(!state.is_terminated());
|
||||
state.driver.cancel_child(state.child());
|
||||
REQUIRE(state.driver.state() == AlgoState::TERMINATED);
|
||||
}
|
||||
|
||||
void complete(LimitCoverState& state) {
|
||||
REQUIRE(state.has_been_deployed());
|
||||
REQUIRE(!state.is_terminated());
|
||||
REQUIRE(state.driver.quantity().value() > state.driver.total_fills().value());
|
||||
fill_child(state, state.driver.quantity().value() - state.driver.total_fills().value());
|
||||
REQUIRE(state.driver.state() == AlgoState::COMPLETED);
|
||||
}
|
||||
} // namespace ops
|
||||
|
||||
/////////////////////
|
||||
// Test Steps
|
||||
/////////////////////
|
||||
namespace test_steps {
|
||||
// Begin Launch
|
||||
std::string BeginLaunch::name() const { return "Begin Launch"; }
|
||||
void BeginLaunch::execute_core(LimitCoverState &state) const { ops::begin_launch(state); }
|
||||
|
||||
// Launch
|
||||
std::string Launch::name() const { return "Launch"; }
|
||||
void Launch::execute_core(LimitCoverState &state) const { ops::launch(state); }
|
||||
|
||||
// Breech Activation Threshold
|
||||
std::string BreechActivationThresh::name() const {
|
||||
std::stringstream title;
|
||||
title << "Breech Activation Threshold " << (thresh_count + 1);
|
||||
if (offset > 0) {
|
||||
title << " (+" << offset << ")";
|
||||
} else if (offset < 0) {
|
||||
title << " (" << offset << ")";
|
||||
}
|
||||
return title.str();
|
||||
}
|
||||
void BreechActivationThresh::execute_core(LimitCoverState &state) const {
|
||||
ops::breech_activation_thresh(state, thresh_count, offset);
|
||||
}
|
||||
|
||||
// Fill
|
||||
std::string Fill::name() const { return "Fill"; }
|
||||
void Fill::execute_core(LimitCoverState &state) const {
|
||||
auto p = get_params(state);
|
||||
ops::fill_child(state, p.quantity, p.price);
|
||||
}
|
||||
|
||||
// Complete
|
||||
std::string Complete::name() const { return "Complete"; }
|
||||
void Complete::execute_core(LimitCoverState &state) const {
|
||||
ops::complete(state);
|
||||
}
|
||||
|
||||
// Cancel Child
|
||||
std::string CancelChild::name() const { return "Cancel Child"; }
|
||||
void CancelChild::execute_core(LimitCoverState& state) const {
|
||||
ops::cancel_child(state);
|
||||
}
|
||||
|
||||
// Cancel Algo
|
||||
std::string CancelAlgo::name() const { return "Cancel Algo"; }
|
||||
void CancelAlgo::execute_core(LimitCoverState& state) const {
|
||||
state.driver.cancel();
|
||||
REQUIRE(state.driver.state() == AlgoState::CANCELED);
|
||||
}
|
||||
} // namespace test_steps
|
||||
} // namespace limit_cover
|
||||
} // namespace test
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,126 +0,0 @@
|
||||
#pragma once
|
||||
#include "catch/catch.hpp"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/tests/limit_cover/LimitCoverState.h"
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace test {
|
||||
namespace limit_cover {
|
||||
// In Visual Studio, intellisense and syntax highlighting don't work (at all)
|
||||
// when <optional> is included, unless 'using namespace std;' is specified.
|
||||
using namespace std;
|
||||
|
||||
struct ITestStep {
|
||||
virtual std::string name() const;
|
||||
void execute(LimitCoverState& state) const;
|
||||
protected:
|
||||
virtual void assert_pre_conditions_core(const LimitCoverState&) const;
|
||||
virtual void execute_core(LimitCoverState&) const;
|
||||
virtual void assert_post_conditions_core(const LimitCoverState&) const;
|
||||
private:
|
||||
void assert_pre_conditions(const LimitCoverState& state) const;
|
||||
void assert_post_conditions(const LimitCoverState& state) const;
|
||||
void _execute(LimitCoverState& state) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& ostr, const ITestStep& ts);
|
||||
LimitCoverState& operator<<(LimitCoverState& state, const ITestStep& ts);
|
||||
|
||||
namespace order_selectors {
|
||||
std::vector<OrderKey> child(const LimitCoverState &lcs);
|
||||
std::vector<OrderKey> self(const LimitCoverState &);
|
||||
} // namespace order_selectors
|
||||
|
||||
namespace ops {
|
||||
void begin_launch(LimitCoverState& lcs);
|
||||
void launch(LimitCoverState& lcs);
|
||||
void breech_activation_thresh(LimitCoverState& lcs, uint32_t thresh_count = 0, int offset = 0);
|
||||
|
||||
void fill_child(LimitCoverState& lcs, uint32_t qty, double price, bool allow_overfill = false);
|
||||
void fill_child(LimitCoverState& lcs, uint32_t qty, bool allow_overfill = false);
|
||||
|
||||
void cancel_child(LimitCoverState& state);
|
||||
void complete(LimitCoverState& lcs);
|
||||
}
|
||||
|
||||
namespace test_params {
|
||||
struct Fill {
|
||||
uint32_t quantity;
|
||||
double price;
|
||||
|
||||
Fill(uint32_t quantity, double price)
|
||||
: quantity(quantity), price(price) {}
|
||||
};
|
||||
|
||||
namespace fills {
|
||||
Fill child(const LimitCoverState& state, optional<uint32_t> qty = {}, optional<double> price = {});
|
||||
} // namespace fills
|
||||
} // namespace test_params
|
||||
namespace test_steps {
|
||||
struct BeginLaunch : public ITestStep {
|
||||
std::string name() const override;
|
||||
void execute_core(LimitCoverState& state) const override;
|
||||
};
|
||||
|
||||
struct Launch : public ITestStep {
|
||||
std::string name() const override;
|
||||
void execute_core(LimitCoverState& state) const override;
|
||||
};
|
||||
|
||||
struct BreechActivationThresh : public ITestStep {
|
||||
uint32_t thresh_count;
|
||||
int offset;
|
||||
|
||||
BreechActivationThresh(uint32_t thresh_count = 0, int offset = 0)
|
||||
: thresh_count(thresh_count), offset(offset) { }
|
||||
|
||||
std::string name() const override;
|
||||
void execute_core(LimitCoverState& state) const override;
|
||||
};
|
||||
|
||||
struct Fill : public ITestStep {
|
||||
std::function<test_params::Fill(const LimitCoverState&)> get_params;
|
||||
|
||||
Fill(std::function<test_params::Fill(const LimitCoverState&)> get_params)
|
||||
: get_params(get_params) {}
|
||||
|
||||
std::string name() const override;
|
||||
void execute_core(LimitCoverState& state) const override;
|
||||
};
|
||||
|
||||
struct Complete : public ITestStep {
|
||||
std::string name() const override;
|
||||
void execute_core(LimitCoverState& state) const override;
|
||||
};
|
||||
|
||||
struct CancelChild : public ITestStep {
|
||||
std::string name() const override;
|
||||
void execute_core(LimitCoverState& state) const override;
|
||||
};
|
||||
|
||||
struct CancelAlgo : public ITestStep {
|
||||
std::string name() const override;
|
||||
void execute_core(LimitCoverState& state) const override;
|
||||
};
|
||||
|
||||
struct Custom : public ITestStep {
|
||||
Custom(std::string name, std::function<void(LimitCoverState&)> impl)
|
||||
: _name(name), _impl(impl) { }
|
||||
|
||||
std::string name() const override { return _name; }
|
||||
void execute_core(LimitCoverState& state) const override { _impl(state); }
|
||||
private:
|
||||
std::string _name;
|
||||
std::function<void(LimitCoverState&)> _impl;
|
||||
};
|
||||
} // namespace test_steps
|
||||
} // namespace limit_cover
|
||||
} // namespace test
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -577,7 +577,7 @@ TEST_CASE("Increase then decrease", "[algo][splitter]") {
|
||||
|
||||
// Set up some instant fills
|
||||
bool applied_second_update = false;
|
||||
state.on_child_launching([&, update](const auto &, auto, auto) mutable {
|
||||
state.on_child_launching([&, update](const auto &) mutable {
|
||||
if (!applied_second_update) {
|
||||
applied_second_update = true;
|
||||
update.mutable_custom()->mutable_splitter()->set_exposed_qty(350);
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
#pragma once
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_utils.h"
|
||||
#include "lib/plugin/SparkServices.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <cstdarg>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace dynamic_adapter {
|
||||
#define NOT_IMPLEMENTED bts_assert_msg(false, "Not implemented.")
|
||||
#define _CONCAT_(x,y) x##y
|
||||
#define VALUE_CASE(x) bts::api::orderman::DynamicOrderParam::_CONCAT_(kAs,x)
|
||||
|
||||
#define VALIDATE_PROTO(x,y)\
|
||||
template<> inline bool validate_proto<x>(const bts::api::orderman::DynamicOrderParam& p) { return p.value_case() == VALUE_CASE(y); }
|
||||
|
||||
#define _ASSIGN_TO_PROTO_(x,y)\
|
||||
template<> inline void assign_to_proto<x>(bts::api::orderman::DynamicOrderParam& param, x value) { param.y(value); }
|
||||
#define ASSIGN_TO_PROTO(x,y) _ASSIGN_TO_PROTO_(x,_CONCAT_(set_as_,y))
|
||||
|
||||
#define _GET_FROM_PROTO_(x,y)\
|
||||
template<> inline x get_from_proto<x>(const bts::api::orderman::DynamicOrderParam& param) { return param.y(); }
|
||||
#define GET_FROM_PROTO(x,y) _GET_FROM_PROTO_(x,_CONCAT_(as_,y))
|
||||
|
||||
#define _DEFINE_PARAM_(x,y)\
|
||||
template<> inline void define_param<x>(bts::api::orderman::DynamicOrderDescriptor& desc, std::string name) {\
|
||||
*desc.add_param_desc() = y(name);\
|
||||
}
|
||||
#define DEFINE_PARAM(x,y) _DEFINE_PARAM_(x,_CONCAT_(param_,y))
|
||||
|
||||
#define PRIMITIVE_PROTO_IMPL(x,y,z)\
|
||||
VALIDATE_PROTO(x,z)\
|
||||
ASSIGN_TO_PROTO(x,y)\
|
||||
GET_FROM_PROTO(x,y)\
|
||||
DEFINE_PARAM(x,y)
|
||||
|
||||
using CustomOrderDefinition = bts::api::orderman::CustomOrderDefinition;
|
||||
using DynamicOrderParam = bts::api::orderman::DynamicOrderParam;
|
||||
using DynamicOrderEnumerated = bts::api::orderman::DynamicOrderEnumerated;
|
||||
|
||||
template<class T> inline bool validate_proto(const DynamicOrderParam&) { NOT_IMPLEMENTED; return false; }
|
||||
template<class T> inline void assign_to_proto(DynamicOrderParam&, T) { NOT_IMPLEMENTED; }
|
||||
template<class T> inline T get_from_proto(const api::orderman::DynamicOrderParam&) { NOT_IMPLEMENTED; return T(); }
|
||||
template<class T> inline void define_param(bts::api::orderman::DynamicOrderDescriptor&, std::string) { NOT_IMPLEMENTED; }
|
||||
|
||||
// primitives
|
||||
PRIMITIVE_PROTO_IMPL(int, int32, Int32)
|
||||
PRIMITIVE_PROTO_IMPL(uint32_t, uint32, Uint32)
|
||||
PRIMITIVE_PROTO_IMPL(double, double, Double)
|
||||
PRIMITIVE_PROTO_IMPL(bool, bool, Bool)
|
||||
|
||||
// order def
|
||||
VALIDATE_PROTO(bts::api::orderman::CustomOrderDefinition, OrderDef)
|
||||
GET_FROM_PROTO(bts::api::orderman::CustomOrderDefinition, order_def)
|
||||
template<> inline void assign_to_proto<CustomOrderDefinition>(DynamicOrderParam& param, CustomOrderDefinition value) {
|
||||
*(param.mutable_as_order_def()) = value;
|
||||
}
|
||||
template<> inline void define_param<CustomOrderDefinition>(api::orderman::DynamicOrderDescriptor& desc, std::string name) {
|
||||
*desc.add_param_desc() = param_order(name);
|
||||
}
|
||||
|
||||
// enumerated
|
||||
VALIDATE_PROTO(bts::api::orderman::DynamicOrderEnumerated, Enumerated)
|
||||
GET_FROM_PROTO(bts::api::orderman::DynamicOrderEnumerated, enumerated)
|
||||
template<> inline void assign_to_proto<DynamicOrderEnumerated>(DynamicOrderParam& param, DynamicOrderEnumerated value) {
|
||||
*(param.mutable_as_enumerated()) = value;
|
||||
}
|
||||
|
||||
template<class TDef, class TValue>
|
||||
struct ParamKey {
|
||||
ParamKey(std::string name, std::function<TValue(const TDef&)> get, std::function<void(TDef&, TValue)> set)
|
||||
: _name(name), _get(get), _set(set) { }
|
||||
|
||||
const std::string& name() const { return _name; }
|
||||
virtual void assign_to_def(const api::orderman::DynamicOrderParam& param, TDef& def) {
|
||||
if (dynamic_adapter::validate_proto<TValue>(param))
|
||||
_set(def, get_from_proto<TValue>(param));
|
||||
}
|
||||
virtual void assign_to_proto(const TDef& def, api::orderman::DynamicOrderParam& param) {
|
||||
dynamic_adapter::assign_to_proto<TValue>(param, _get(def));
|
||||
}
|
||||
virtual void assign_to_proto(const TDef& def, api::orderman::DynamicOrderDefinition& dyn) {
|
||||
api::orderman::DynamicOrderParam dop;
|
||||
assign_to_proto(def, dop);
|
||||
(*dyn.mutable_order_params())[name()] = dop;
|
||||
}
|
||||
virtual void define_param(api::orderman::DynamicOrderDescriptor& desc) {
|
||||
dynamic_adapter::define_param<TValue>(desc, name());
|
||||
}
|
||||
virtual bool validate_proto(const api::orderman::DynamicOrderDefinition& dyn) {
|
||||
auto it = dyn.order_params().find(name());
|
||||
return it != dyn.order_params().end() && dynamic_adapter::validate_proto<TValue>(it->second);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
std::function<TValue(const TDef&)> _get;
|
||||
std::function<void(TDef&, TValue)> _set;
|
||||
};
|
||||
|
||||
template<class TDef, class TValue>
|
||||
struct EnumParamKey : public ParamKey<TDef, TValue> {
|
||||
EnumParamKey(std::string name, std::vector<std::pair<TValue, std::string>> named_values, std::function<TValue(const TDef&)> get, std::function<void(TDef&, TValue)> set)
|
||||
: ParamKey<TDef, TValue>(name, get, set), _name(name), _named_values(named_values), _get(get), _set(set) {
|
||||
}
|
||||
|
||||
void assign_to_def(const api::orderman::DynamicOrderParam& param, TDef& def) override {
|
||||
if (dynamic_adapter::validate_proto<DynamicOrderEnumerated>(param))
|
||||
_set(def, _from_proto(param.as_enumerated()));
|
||||
}
|
||||
|
||||
void assign_to_proto(const TDef& def, api::orderman::DynamicOrderParam& param) override {
|
||||
dynamic_adapter::assign_to_proto(param, _to_proto(_get(def)));
|
||||
}
|
||||
|
||||
void assign_to_proto(const TDef& def, api::orderman::DynamicOrderDefinition& dyn) override {
|
||||
api::orderman::DynamicOrderParam param;
|
||||
assign_to_proto(def, param);
|
||||
(*dyn.mutable_order_params())[_name] = param;
|
||||
}
|
||||
|
||||
void define_param(api::orderman::DynamicOrderDescriptor& order_desc) override {
|
||||
// enum desc
|
||||
api::orderman::DynamicOrderEnumeratedDescriptor enum_desc;
|
||||
for (auto& p : _named_values) {
|
||||
enum_desc.add_element(p.second);
|
||||
}
|
||||
// param type desc
|
||||
api::orderman::DynamicOrderTypeDescriptor param_type_desc;
|
||||
*param_type_desc.mutable_enumerated() = enum_desc;
|
||||
// param desc
|
||||
api::orderman::DynamicOrderParamDescriptor param_desc;
|
||||
param_desc.set_param_name(_name);
|
||||
*param_desc.mutable_param_type() = param_type_desc;
|
||||
// order desc
|
||||
*order_desc.add_param_desc() = param_desc;
|
||||
}
|
||||
|
||||
bool validate_proto(const api::orderman::DynamicOrderDefinition& dyn) override {
|
||||
auto it = dyn.order_params().find(_name);
|
||||
return it != dyn.order_params().end() && it->second.value_case() == VALUE_CASE(Enumerated);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
std::vector<std::pair<TValue, std::string>> _named_values;
|
||||
|
||||
DynamicOrderEnumerated _to_proto(TValue v) {
|
||||
DynamicOrderEnumerated deo;
|
||||
for (auto p : _named_values) {
|
||||
if (p.first == v) {
|
||||
deo.set_enum_value(p.second);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return deo;
|
||||
}
|
||||
|
||||
TValue _from_proto(const std::string& enum_name) {
|
||||
for (auto p : _named_values) {
|
||||
if (p.second == enum_name)
|
||||
return p.first;
|
||||
}
|
||||
bts_assert_msg(false, "Failed to match enum name to enum value.");
|
||||
return TValue();
|
||||
}
|
||||
TValue _from_proto(DynamicOrderEnumerated deo) {
|
||||
return _from_proto(deo.enum_value());
|
||||
}
|
||||
|
||||
std::function<TValue(const TDef&)> _get;
|
||||
std::function<void(TDef&, TValue)> _set;
|
||||
};
|
||||
|
||||
template<class TParam, class... TNextParam>
|
||||
inline void define_proto(api::orderman::DynamicOrderDescriptor& desc, TParam param, TNextParam... next_param) {
|
||||
param.define_param(desc);
|
||||
(define_proto(desc, next_param), ...);
|
||||
}
|
||||
|
||||
template<class TDef, class TParam, class... TNextParam>
|
||||
inline void from_proto(const google::protobuf::Map<std::string, api::orderman::DynamicOrderParam>& map, TDef& def, TParam param, TNextParam... next_param) {
|
||||
auto pit = map.find(param.name());
|
||||
if (pit != map.end()) {
|
||||
param.assign_to_def(pit->second, def);
|
||||
}
|
||||
(from_proto(map, def, next_param), ...);
|
||||
}
|
||||
|
||||
template<class TDef, class TParam, class... TNextParam>
|
||||
inline void to_proto(api::orderman::DynamicOrderDefinition& dyn, const TDef& def, TParam param_key, TNextParam... next_param) {
|
||||
param_key.assign_to_proto(def, dyn);
|
||||
(to_proto(dyn, def, next_param), ...);
|
||||
}
|
||||
|
||||
template<class TParam, class... TNextParam>
|
||||
inline bool validate_proto(const api::orderman::DynamicOrderDefinition& dyn, TParam param, TNextParam... next_param) {
|
||||
return param.validate_proto(dyn) && validate_proto(dyn, next_param...);
|
||||
}
|
||||
|
||||
template<typename TDef, typename... T>
|
||||
struct DynamicOrderAdapter {
|
||||
void define_proto(api::orderman::DynamicOrderDescriptor& desc) { (dynamic_adapter::define_proto(desc, T()), ...); }
|
||||
void from_proto(const api::orderman::DynamicOrderDefinition& dyn, TDef& def) { (dynamic_adapter::from_proto(dyn.order_params(), def, T()), ...); }
|
||||
void to_proto(api::orderman::DynamicOrderDefinition& dyn, const TDef& def) { (dynamic_adapter::to_proto(dyn, def, T()), ...); }
|
||||
bool validate_proto(const api::orderman::DynamicOrderDefinition& dyn) { return (T().validate_proto(dyn), ...); }
|
||||
};
|
||||
} // namespace dynamic_adapter
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -18,7 +18,10 @@ public:
|
||||
// driver_price and driver_quantity are the price and quantity
|
||||
// in the hedging (or driver) leg that can drive a quoter_leg_quantity
|
||||
// sized order in the quoting leg
|
||||
QuoteLegDriver();
|
||||
QuoteLegDriver();
|
||||
QuoteLegDriver(QuoteLegDriver const&src) {
|
||||
operator=(src);
|
||||
}
|
||||
QuoteLegDriver& operator=(const QuoteLegDriver& s){
|
||||
set(s.driver_price(),s.driver_quantity(), s.quoter_leg_quantity());
|
||||
return *this;
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
: _owner(owner), _pos(pos), _cache(nullptr)
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
ConcurrentAppendCollectionIterator& operator=(const ConcurrentAppendCollectionIterator& other) {
|
||||
_owner = other._owner;
|
||||
_pos = other._pos;
|
||||
@@ -141,4 +141,4 @@ bool operator!=(ConcurrentAppendCollectionIterator<T,CHUNK>& left, ConcurrentApp
|
||||
}
|
||||
|
||||
|
||||
}//namespace
|
||||
}//namespace
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
{
|
||||
path_ /= boost::filesystem::unique_path ("bts-utest-%%%%-%%%%-%%%%-%%%%");
|
||||
}
|
||||
catch(std::exception)
|
||||
catch(std::exception const&)
|
||||
{
|
||||
std::ostringstream digits;
|
||||
digits << "bts-utest";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import sys
|
||||
f = open(sys.argv[1], 'rb+')
|
||||
license = '1C9210495AA3C9C884B7A37A4C7AD8F9'
|
||||
license = '5E50A1DACD94D7C1BECD316EEAE5AB02'
|
||||
data = f.read()
|
||||
match = data.find(license)
|
||||
if match > 0:
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<CSharpAPIRoot>$(MSBuildThisFileDirectory)</CSharpAPIRoot>
|
||||
<Configuration Condition="'$(configuration)'==''">Debug</Configuration>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Output">
|
||||
<BaseOutputPath>$(CSharpAPIRoot)\build\$(Configuration)</BaseOutputPath>
|
||||
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
|
||||
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
|
||||
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30104.148
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bts.api.csharp", "src/bts.api.csharp.csproj", "{2B9FECC6-16F4-4B85-92BF-62AAA2B7652D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2B9FECC6-16F4-4B85-92BF-62AAA2B7652D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2B9FECC6-16F4-4B85-92BF-62AAA2B7652D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2B9FECC6-16F4-4B85-92BF-62AAA2B7652D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B9FECC6-16F4-4B85-92BF-62AAA2B7652D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {FC6CE0CA-B0CB-44E3-8176-1D759D05AAD4}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="spark_sdk" Version="2.15.10" />
|
||||
<PackageReference Include="System.Reactive" Version="4.4.1" />
|
||||
<PackageReference Include="System.Reactive.Core" Version="4.4.1" />
|
||||
<PackageReference Include="System.Reactive.Linq" Version="4.4.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,23 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace bts.api.csharp {
|
||||
readonly struct NameIDPair {
|
||||
public NameIDPair(string name, uint id) {
|
||||
this.name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public string name { get; }
|
||||
public uint id { get; }
|
||||
}
|
||||
|
||||
static class Users {
|
||||
public static NameIDPair Default =>
|
||||
new NameIDPair("bts", 1);
|
||||
}
|
||||
|
||||
static class Accounts {
|
||||
public static NameIDPair Default =>
|
||||
new NameIDPair("TEST", 1);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using bts.api.core;
|
||||
using bts.api.csharp.examples;
|
||||
using bts.api.orderman;
|
||||
using bts.api.position;
|
||||
using CommandLine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace bts.api.csharp {
|
||||
class Program {
|
||||
static void Main(string[] args) =>
|
||||
Parser.Default.ParseArguments<Options>(args).WithParsed(_run);
|
||||
|
||||
static void _run(Options options) {
|
||||
var exit_event = new ManualResetEvent(false);
|
||||
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
|
||||
var service_context = new ServiceContext();
|
||||
var login = service_context.login("bts", "");
|
||||
login.Wait();
|
||||
var examples = new List<IDisposable>();
|
||||
if (options.subscribe_securities ||! options.any_enabled) {
|
||||
examples.Add(new SecurityExample(service_context.locate<MarketDataService>()));
|
||||
}
|
||||
if (options.subscribe_positions || !options.any_enabled) {
|
||||
examples.Add(new PositionExample(service_context.locate<PositionService>()));
|
||||
}
|
||||
if (options.subscribe_orders || !options.any_enabled) {
|
||||
examples.Add(new OrderExample(service_context.locate<OrderService>()));
|
||||
}
|
||||
if (options.subscribe_book || !options.any_enabled) {
|
||||
examples.Add(new BookExample(service_context.locate<MarketDataService>()));
|
||||
}
|
||||
if (options.send_orders || !options.any_enabled) {
|
||||
examples.Add(SendOrderLoop.init_loop(service_context.locate<OrderService>(), service_context.locate<MarketDataService>()));
|
||||
}
|
||||
Console.CancelKeyPress += on_cancel_key_press;
|
||||
exit_event.WaitOne();
|
||||
foreach (var ex in examples)
|
||||
ex.Dispose();
|
||||
//
|
||||
void on_cancel_key_press(object sender, ConsoleCancelEventArgs e) {
|
||||
exit_event.Set();
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
class Options {
|
||||
[Option("subscribe-securities", Required = false, HelpText = "run security-subscription example.")]
|
||||
public bool subscribe_securities { get; set; }
|
||||
|
||||
[Option("subscribe-orders", Required = false, HelpText = "run order-subscription example.")]
|
||||
public bool subscribe_orders { get; set; }
|
||||
|
||||
[Option("subscribe-positions", Required = false, HelpText = "run position-subscription example.")]
|
||||
public bool subscribe_positions { get; set; }
|
||||
|
||||
[Option("subscribe-books", Required = false, HelpText = "run book-subscription example.")]
|
||||
public bool subscribe_book { get; set; }
|
||||
|
||||
[Option("send-orders", Required = false, HelpText = "run send-orders example.")]
|
||||
public bool send_orders { get; set; }
|
||||
|
||||
// if none are enabled, all options will be flipped on,
|
||||
// so the user need not specify a single argument to see the examples run.
|
||||
public bool any_enabled =>
|
||||
subscribe_securities ||
|
||||
subscribe_orders ||
|
||||
subscribe_positions ||
|
||||
subscribe_book ||
|
||||
send_orders;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using bts.api.core;
|
||||
using bts.api.csharp.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using SecurityKey = System.UInt32;
|
||||
|
||||
namespace bts.api.csharp.examples {
|
||||
/// <summary>
|
||||
/// Uses the <see cref="MarketDataService"/> api to observe book updates
|
||||
/// for security in the system, and organize them into a Dictionary.
|
||||
/// </summary>
|
||||
class BookExample : IDisposable {
|
||||
readonly Dictionary<SecurityKey, BookEntryImmutable> _books = new Dictionary<SecurityKey, BookEntryImmutable>();
|
||||
readonly IDisposable _subscription;
|
||||
|
||||
public BookExample(MarketDataService market_data_service) {
|
||||
_subscription = market_data_service.subscribei_book()
|
||||
.ObserveOn(SynchronizationContext.Current)
|
||||
.Subscribe(_on_book_update);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
_subscription.Dispose();
|
||||
}
|
||||
|
||||
void _on_book_update(Update<BookEntryImmutable, uint> update) {
|
||||
foreach (var new_book in update.added) {
|
||||
// of course, we shouldn't have entries for any books in update.added yet
|
||||
Debug.Assert(!_books.ContainsKey(new_book.Key));
|
||||
// display bbo in the console window
|
||||
_print_book_info(new_book.Key, $"book received for the first time, bbo is: {BookUtils.fmt_bbo(new_book)}");
|
||||
// add the new book to our map
|
||||
_books.Add(new_book.Key, new_book);
|
||||
}
|
||||
foreach (var change in update.changes) {
|
||||
// of course, the security key shouldn't change
|
||||
Debug.Assert(change.Old.Key == change.New.Key);
|
||||
// our book entry for this security should be identitical to change.Old
|
||||
Debug.Assert(change.Old.Bid.SequenceEqual(_books[change.Old.Key].Bid));
|
||||
Debug.Assert(change.Old.Ask.SequenceEqual(_books[change.Old.Key].Ask));
|
||||
// display the current book in the console window
|
||||
_print_book_info(change.New.Key, $"book changed, now bbo is: {BookUtils.fmt_bbo(change.New)}");
|
||||
// update our map with the changed book
|
||||
_books[change.New.Key] = change.New;
|
||||
}
|
||||
foreach (var old_book in update.removed) {
|
||||
// of course, we should have entries for all books in update.removed
|
||||
Debug.Assert(_books.Remove(old_book.Key));
|
||||
}
|
||||
}
|
||||
|
||||
static void _print_book_info(SecurityKey key, string message) {
|
||||
Console.WriteLine($"Security {key}: {message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
-139
@@ -1,139 +0,0 @@
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using AccountKey = System.UInt32;
|
||||
using OrderKey = System.UInt32;
|
||||
|
||||
namespace bts.api.csharp.examples {
|
||||
/// <summary>
|
||||
/// Uses the <see cref="OrderService"/> to observe order updates,
|
||||
/// and group them by account.
|
||||
/// </summary>
|
||||
class OrderExample : IDisposable {
|
||||
readonly Dictionary<AccountKey, Dictionary<OrderKey, OrderRecordImmutable>> _data = new Dictionary<AccountKey, Dictionary<AccountKey, OrderRecordImmutable>>();
|
||||
IDisposable _subscription;
|
||||
|
||||
public OrderExample(OrderService order_service) {
|
||||
/// 'OrderService' has 2 methods which can be used to subscribe to order updates.
|
||||
/// -------------------------------------------------------------------------------------
|
||||
/// 1) <see cref="OrderService.subscribe_orders(string)"/>.
|
||||
/// 2) <see cref="OrderService.subscribei_orders(string)"/>.
|
||||
/// This method (no. 2) is typically preferrable because the data it provides is immutable.
|
||||
/// --------------------------------------------------------------------------------------
|
||||
_subscription = order_service.subscribei_orders()
|
||||
// the 'ObserveOn' call below ensures that the
|
||||
// callback will be invoked this thread.
|
||||
// Be sure to do this if you don't want to worry
|
||||
// about multi-threading issues (locking, etc).
|
||||
.ObserveOn(SynchronizationContext.Current)
|
||||
.Subscribe(_on_update);
|
||||
|
||||
}
|
||||
|
||||
public event Action<AccountKey> account_orders_changed;
|
||||
|
||||
public void Dispose() {
|
||||
_subscription.Dispose();
|
||||
}
|
||||
|
||||
public IEnumerable<OrderRecordImmutable> get_account_orders(AccountKey account) {
|
||||
if (_data.TryGetValue(account, out var account_orders)) {
|
||||
return account_orders.Values;
|
||||
}
|
||||
return Enumerable.Empty<OrderRecordImmutable>();
|
||||
}
|
||||
|
||||
void _on_order_added(OrderRecordImmutable new_order) {
|
||||
if (!_data.TryGetValue(new_order.Def.AccountId, out var account_orders)) {
|
||||
// this is the first order for this account.
|
||||
// initialize the account orders dictionary, and add it to our data.
|
||||
account_orders = new Dictionary<AccountKey, OrderRecordImmutable>();
|
||||
_data.Add(new_order.Def.AccountId, account_orders);
|
||||
}
|
||||
// now add the order to account_orders
|
||||
account_orders.Add(new_order.Key, new_order);
|
||||
}
|
||||
|
||||
void _on_order_changed(Change<OrderRecordImmutable> change) {
|
||||
// ch.New and ch.Old have the same key.
|
||||
Debug.Assert(change.New.Key == change.Old.Key);
|
||||
var key = change.New.Key;
|
||||
// react to a changed order
|
||||
if (change.New.State.FilledQuantity != change.Old.State.FilledQuantity) {
|
||||
// Display new fills in the console window.
|
||||
var num_new_fills = change.New.State.FilledQuantity - change.Old.State.FilledQuantity;
|
||||
_print_order_info(key, $"received {num_new_fills} new fills.");
|
||||
// Be mindful that orders can be overfilled.
|
||||
if (change.New.State.FilledQuantity > change.New.State.Quantity) {
|
||||
// Display overfills in the console window.
|
||||
var num_over_fills = change.New.State.FilledQuantity - change.New.State.Quantity;
|
||||
_print_order_info(key, $"overfilled by {num_over_fills}");
|
||||
} else {
|
||||
// Display working qty in the console window.
|
||||
var working_qty = change.New.State.Quantity - change.New.State.FilledQuantity;
|
||||
_print_order_info(key, $"still working {working_qty}.");
|
||||
}
|
||||
}
|
||||
// Display order status changes in the console window.
|
||||
if (change.New.State.Status != change.Old.State.Status) {
|
||||
_print_order_info(key, $"status changed from {change.Old.State.Status} to {change.New.State.Status}.");
|
||||
}
|
||||
// Account will not change, so our dictionary will definitely have a key for this account.
|
||||
Debug.Assert(_data.ContainsKey(change.New.Def.AccountId));
|
||||
var account_orders = _data[change.New.Def.AccountId];
|
||||
// The value for this key in account_orders should equal ch.Old.
|
||||
Debug.Assert(account_orders[key].Equals(change.Old));
|
||||
// update account orders with the changed order.
|
||||
account_orders[key] = change.New;
|
||||
}
|
||||
|
||||
void _on_order_removed(OrderRecordImmutable old_val) {
|
||||
// An entry for this order should be present in our data,
|
||||
// as it is in the 'removed' items.
|
||||
// Assert this is the case to illustrate the point.
|
||||
Debug.Assert(_data.ContainsKey(old_val.Def.AccountId));
|
||||
var account_orders = _data[old_val.Def.AccountId];
|
||||
// the removed value should match the one currently in our data.
|
||||
// Assert this is the case, then remove the order.
|
||||
Debug.Assert(account_orders[old_val.Key].Equals(old_val));
|
||||
account_orders.Remove(old_val.Key);
|
||||
_print_order_info(old_val.Key, "has been removed.");
|
||||
}
|
||||
|
||||
void _on_update(Update<OrderRecordImmutable, OrderKey> update) {
|
||||
// record which accounts experienced an order update, then
|
||||
// at the end of this method, fire the account_orders_changed event for each
|
||||
var accounts_with_order_updates = new HashSet<AccountKey>();
|
||||
foreach (OrderRecordImmutable new_order in update.added) {
|
||||
_on_order_added(new_order);
|
||||
accounts_with_order_updates.Add(new_order.Def.AccountId);
|
||||
}
|
||||
foreach (Change<OrderRecordImmutable> change in update.changes) {
|
||||
_on_order_changed(change);
|
||||
accounts_with_order_updates.Add(change.New.Def.AccountId);
|
||||
}
|
||||
foreach (OrderRecordImmutable old_order in update.removed) {
|
||||
_on_order_removed(old_order);
|
||||
accounts_with_order_updates.Add(old_order.Def.AccountId);
|
||||
}
|
||||
// display all accounts with order updates in the Console window.
|
||||
Console.WriteLine("The following accounts experienced order changes: ");
|
||||
foreach (var account in accounts_with_order_updates) {
|
||||
Console.WriteLine("\n\t" + account);
|
||||
}
|
||||
// lastly, fire the account_orders_changed event for each account experienced an order update.
|
||||
foreach (var account in accounts_with_order_updates) {
|
||||
account_orders_changed?.Invoke(account);
|
||||
}
|
||||
}
|
||||
|
||||
void _print_order_info(OrderKey order_key, string message) {
|
||||
Console.WriteLine($"Order {order_key}: {message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
using bts.api.core;
|
||||
using bts.api.position;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace bts.api.csharp.examples {
|
||||
/// <summary>
|
||||
/// A pair of security, and account id,
|
||||
/// which uniquely identifies each PositionUnit.
|
||||
/// </summary>
|
||||
readonly struct PositionKey {
|
||||
public PositionKey(uint security_id, uint account_id) {
|
||||
this.security_id = security_id;
|
||||
this.account_id = account_id;
|
||||
}
|
||||
|
||||
public uint security_id { get; }
|
||||
public uint account_id { get; }
|
||||
|
||||
public static PositionKey from_position(PositionUnitImmutable p) =>
|
||||
new PositionKey(p.SecurityId, p.AccountId);
|
||||
|
||||
public override string ToString() =>
|
||||
$"Sec {security_id}, Acc {account_id}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the <see cref="PositionService"/> api to monitor positions
|
||||
/// for every account/security pair, and organize them into a Dictionary.
|
||||
/// </summary>
|
||||
class PositionExample : IDisposable {
|
||||
readonly Dictionary<PositionKey, PositionUnitImmutable> _data = new Dictionary<PositionKey, PositionUnitImmutable>();
|
||||
IDisposable _subscription;
|
||||
|
||||
public PositionExample(PositionService position_service) {
|
||||
/// 'PositionService' has 2 methods which can be used to subscribe to position updates.
|
||||
/// -------------------------------------------------------------------------------------
|
||||
/// 1) <see cref="PositionService.subscribe_position_data(string)"/>.
|
||||
/// 2) <see cref="PositionService.subscribei_position_data(string)"/>.
|
||||
/// This method (no. 2) is typically preferrable because the data it provides is immutable.
|
||||
/// --------------------------------------------------------------------------------------
|
||||
_subscription = position_service.subscribei_position_data()
|
||||
// the 'ObserveOn' call below ensures that the
|
||||
// callback will be invoked this thread.
|
||||
// Be sure to do this if you don't want to worry
|
||||
// about multi-threading issues (locking, etc).
|
||||
.ObserveOn(SynchronizationContext.Current)
|
||||
.Subscribe(_on_update);
|
||||
}
|
||||
|
||||
public event Action data_changed;
|
||||
|
||||
public IReadOnlyDictionary<PositionKey, PositionUnitImmutable> data => _data;
|
||||
|
||||
public void Dispose() =>
|
||||
_subscription.Dispose();
|
||||
|
||||
void _on_position_added(PositionUnitImmutable new_val) {
|
||||
var k = PositionKey.from_position(new_val);
|
||||
// this entry should NOT be present in our _data, as it is in the 'added' items.
|
||||
// Assert this is the case to illustrate the point.
|
||||
Debug.Assert(!_data.ContainsKey(k));
|
||||
_print_position_info(k, $"initial position received. Day: Long {new_val.DayLongPos}, Short {new_val.DayShortPos}, Net {new_val.DayLongPos - new_val.DayShortPos}.");
|
||||
// add the new position unit to _data
|
||||
_data.Add(k, new_val);
|
||||
}
|
||||
|
||||
void _on_position_changed(Change<PositionUnitImmutable> change) {
|
||||
var k = PositionKey.from_position(change.New);
|
||||
// ch.New and ch.Old have the same key (i.e., the Account/Security pair),
|
||||
Debug.Assert(change.New.AccountId == change.Old.AccountId);
|
||||
Debug.Assert(change.New.SecurityId == change.Old.SecurityId);
|
||||
// of course, the value for this key in our dictionary should equal ch.Old.
|
||||
Debug.Assert(_data[k].Equals(change.Old));
|
||||
// Display changes to the day position in the console window.
|
||||
// 1) check if the day long changed
|
||||
if (change.New.DayLongPos != change.Old.DayLongPos) {
|
||||
Console.WriteLine($"[{k}] day long changed from {change.Old.DayLongPos} to {change.New.DayLongPos}.");
|
||||
}
|
||||
// 2) check if day short changed
|
||||
if (change.New.DayShortPos != change.Old.DayShortPos) {
|
||||
Console.WriteLine($"[{k}] day short changed from {change.Old.DayShortPos} to {change.New.DayShortPos}.");
|
||||
}
|
||||
// 3) check if net day position changed
|
||||
var new_net_day_pos = change.New.DayLongPos - change.New.DayShortPos;
|
||||
var old_net_day_pos = change.Old.DayLongPos - change.Old.DayShortPos;
|
||||
if (new_net_day_pos != old_net_day_pos) {
|
||||
Console.WriteLine($"[{k}] day net changed from {old_net_day_pos} to {new_net_day_pos}.");
|
||||
}
|
||||
// update _data with the changed position.
|
||||
_data[k] = change.New;
|
||||
}
|
||||
|
||||
void _on_position_removed(PositionUnitImmutable old_val) {
|
||||
var k = PositionKey.from_position(old_val);
|
||||
// this entry should be present in our _data, as it is in the 'removed' items.
|
||||
// Assert this is the case to illustrate the point.
|
||||
Debug.Assert(_data.ContainsKey(k));
|
||||
// this should match our data. Assert this is the case.
|
||||
Debug.Assert(_data[k].Equals(old_val));
|
||||
_data.Remove(k);
|
||||
Console.WriteLine($"[{k}] position has been removed.");
|
||||
}
|
||||
|
||||
void _on_update(Update<PositionUnitImmutable, int> update) {
|
||||
foreach (PositionUnitImmutable p in update.added) {
|
||||
_on_position_added(p);
|
||||
}
|
||||
foreach (Change<PositionUnitImmutable> ch in update.changes) {
|
||||
_on_position_changed(ch);
|
||||
}
|
||||
foreach (PositionUnitImmutable p in update.removed) {
|
||||
_on_position_removed(p);
|
||||
}
|
||||
data_changed?.Invoke();
|
||||
}
|
||||
|
||||
void _print_position_info(PositionKey position_key, string message) {
|
||||
Console.WriteLine($"Position {position_key}: {message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
using bts.api.core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using SecurityKey = System.UInt32;
|
||||
|
||||
namespace bts.api.csharp.examples {
|
||||
/// <summary>
|
||||
/// Uses the <see cref="MarketDataService"/> api to observe the
|
||||
/// state of securities in the system.
|
||||
/// </summary>
|
||||
class SecurityExample : IDisposable {
|
||||
readonly IDisposable _subscription;
|
||||
readonly Dictionary<SecurityKey, SecurityImmutable> _securities = new Dictionary<SecurityKey, SecurityImmutable>();
|
||||
|
||||
public SecurityExample(MarketDataService market_data_service) {
|
||||
_subscription = market_data_service.subscribei_securities()
|
||||
.ObserveOn(SynchronizationContext.Current)
|
||||
.Subscribe(_on_securities_update);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
_subscription.Dispose();
|
||||
}
|
||||
|
||||
void _on_securities_update(Update<SecurityImmutable, uint> update) {
|
||||
foreach (var new_security in update.added) {
|
||||
// of course, we shouldn't have entries for any books in update.added yet
|
||||
Debug.Assert(!_securities.ContainsKey(new_security.Id));
|
||||
// display bbo in the console window
|
||||
Console.WriteLine($"received new security: {new_security}.");
|
||||
// add the new security to our map
|
||||
_securities.Add(new_security.Id, new_security);
|
||||
}
|
||||
// As you may have noticed, all of the subcribe methods in the api
|
||||
// return an IObservable<Update<T>>. It is worth noting that for security updates,
|
||||
// 'changes' will always be empty.
|
||||
Debug.Assert(!update.changes.Any());
|
||||
foreach (var old_security in update.removed) {
|
||||
// of course, we should have entries for all books in update.removed
|
||||
Debug.Assert(_securities.Remove(old_security.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
using bts.api.csharp.utils;
|
||||
using bts.api.orderman;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.api.csharp.examples {
|
||||
static class OrderTemplates {
|
||||
public static CustomOrderDefinition limit() => new CustomOrderDefinition { Limit = new LimitOrderDefinition() };
|
||||
public static CustomOrderDefinition market() => new CustomOrderDefinition { Market = new MarketOrderDefinition() };
|
||||
public static CustomOrderDefinition iceberg(double show_quantity) => iceberg(show_quantity, limit());
|
||||
public static CustomOrderDefinition iceberg(double show_quantity, CustomOrderDefinition child) =>
|
||||
new CustomOrderDefinition {
|
||||
Iceberg = new IcebergOrderDefinition {
|
||||
ShowQuantity = show_quantity,
|
||||
Child = child
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exemplifies how the <see cref="OrderService"/> api may be used to
|
||||
/// send several common order types (limit, market, and iceberg).
|
||||
/// </summary>
|
||||
class SendOrderModel {
|
||||
readonly OrderService _service;
|
||||
|
||||
public SendOrderModel(OrderService service) {
|
||||
_service = service;
|
||||
}
|
||||
|
||||
public OrderDefinition create_order_definition(NameIDPair user, NameIDPair account, uint security_key, CustomOrderDefinition custom_def) {
|
||||
return new OrderDefinition {
|
||||
Account = account.name,
|
||||
AccountId = account.id,
|
||||
User = user.name,
|
||||
UserId = user.id,
|
||||
SecurityKey = security_key,
|
||||
CustomDef = custom_def
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a limit order (asynchronous).
|
||||
/// </summary>
|
||||
/// <returns>The order id if the submission was successful, otherwise null.</returns>
|
||||
public Task<uint?> send_limit_order_async(NameIDPair user, NameIDPair account, uint security_key, uint quantity, double price) {
|
||||
return send_order_async(user, account, security_key, OrderTemplates.limit(), quantity, price);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a market order (asynchronous). Note no price parameter is required for market orders.
|
||||
/// </summary>
|
||||
/// <returns>The order id if the submission was successful, otherwise null.</returns>
|
||||
public Task<uint?> send_market_order_async(NameIDPair user, NameIDPair account, uint security_key, uint quantity) {
|
||||
return send_order_async(user, account, security_key, OrderTemplates.market(), quantity, 0.0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an iceberg of limit orders (asynchronous).
|
||||
/// </summary>
|
||||
/// <returns>The order id if the submission was successful, otherwise null.</returns>
|
||||
public Task<uint?> send_iceberg_order_async(NameIDPair user, NameIDPair account, uint security_key, uint quantity, double price, double show_quantity) {
|
||||
return send_order_async(user, account, security_key, OrderTemplates.iceberg(show_quantity), quantity, price);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an order of the type specified by <paramref name="custom_order_definition"/> (asynchronous).
|
||||
/// </summary>
|
||||
/// <returns>The order id if the submission was successful, otherwise null.</returns>
|
||||
public async Task<uint?> send_order_async(NameIDPair user, NameIDPair account, uint security_key, CustomOrderDefinition custom_order_definition, uint quantity, double price) {
|
||||
Console.WriteLine($"submitting order for user ({user}), account ({account}): {OrderUtils.fmt_order(quantity, price, custom_order_definition)}.");
|
||||
var reply = await _service.create_order_async(new Create {
|
||||
Quantity = quantity,
|
||||
Price = price,
|
||||
NewOrder = create_order_definition(user, account, security_key, custom_order_definition)
|
||||
});
|
||||
if (reply.success) {
|
||||
Console.WriteLine($"received ack that order has been submitted. Order id is {reply.value.OrderKey}.");
|
||||
return reply.value.OrderKey;
|
||||
} else {
|
||||
Console.WriteLine($"order submission was unsuccessful: [{reply.value.ErrorCode}] {reply.value.Msg}.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void send_order(NameIDPair user, NameIDPair account, uint security_key, CustomOrderDefinition custom_order_definition, uint quantity, double price) {
|
||||
/// Note, the thread will be blocked until a response is received from the server.
|
||||
/// For this reason, <see cref="OrderService.create_order_async(Create)"/> is usually preferred.
|
||||
/// An example of how to use that method is provided in <see cref="send_order_async(NameIDPair, NameIDPair, CustomOrderDefinition, uint, double)"/>.
|
||||
Console.WriteLine($"Creating order {OrderUtils.fmt_order(quantity, price, custom_order_definition)}");
|
||||
var response = _service.create_order(new Create {
|
||||
Quantity = quantity,
|
||||
Price = price,
|
||||
NewOrder = create_order_definition(user, account, security_key, custom_order_definition)
|
||||
});
|
||||
switch (response.ErrorCode) {
|
||||
case OrderError.Ok:
|
||||
_print_order_info(response.OrderKey, "successfully created.");
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"Failed to create order: [{response.ErrorCode}] {response.Msg}.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _print_order_info(uint order_key, string message) {
|
||||
Console.WriteLine($"Order {order_key}: {message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using mbts;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.api.csharp.examples {
|
||||
/// <summary>
|
||||
/// The code in this example is a bit more advanced than the others,
|
||||
/// as it is designed to send orders, asynchronously, in a continuous loop.
|
||||
/// For straightforward examples of how to send orders with the api,
|
||||
/// check out <see cref="SendOrderModel"/>.
|
||||
/// </summary>
|
||||
static class SendOrderLoop {
|
||||
/// <summary>
|
||||
/// As a security key is required to send an order, this method
|
||||
/// uses the <see cref="MarketDataService"/> to observe the securities stream,
|
||||
/// then on the first update, initializes the send-order-loop <see cref="LoopImpl"/>
|
||||
/// with one of the newly received security keys.
|
||||
/// </summary>
|
||||
public static IDisposable init_loop(OrderService order_service, MarketDataService market_data_service) {
|
||||
// If you need to familiarize yourself with the IObservable pattern, check out the official docs:
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.iobservable-1?view=netcore-3.1
|
||||
|
||||
var single_security_stream = market_data_service.subscribei_securities()
|
||||
.Where(u => u.all.Any())
|
||||
.Take(1)
|
||||
.Select(u => u.all.FirstOrDefault());
|
||||
|
||||
return init_loop(single_security_stream, create_loop);
|
||||
IDisposable create_loop(SecurityImmutable sec) {
|
||||
return new LoopImpl(order_service, sec.Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Observes the security stream and creates a new send-order-loop
|
||||
/// whenever a new security arrives. The previous send-order-loop
|
||||
/// is disposed when the next security arrives, so only one send-order-loop
|
||||
/// shall be active at a time.
|
||||
/// </summary>
|
||||
/// <param name="security">The security stream used to initialize send-order-loops.</param>
|
||||
/// <returns>An IDisposable that unsubsribes from the securities stream, and disposes the active send-order-loop.</returns>
|
||||
public static IDisposable init_loop(IObservable<SecurityImmutable> security, Func<SecurityImmutable, IDisposable> factory) {
|
||||
IDisposable active_loop = null;
|
||||
var security_sub = security.Subscribe(s =>
|
||||
Interlocked.Exchange(ref active_loop, factory(s))?.Dispose());
|
||||
return Disposable.Create(dispose);
|
||||
void dispose() {
|
||||
security_sub.Dispose();
|
||||
active_loop?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class LoopImpl : IDisposable {
|
||||
bool _is_disposed;
|
||||
|
||||
public LoopImpl(OrderService service, uint security_key)
|
||||
: this(service, Users.Default, Accounts.Default, security_key, 10, TimeSpan.FromSeconds(3)) { }
|
||||
|
||||
public LoopImpl(OrderService service, NameIDPair user, NameIDPair account, uint security_key, int max_orders, TimeSpan delay_between_orders) {
|
||||
var model = new SendOrderModel(service);
|
||||
send_orders_continuously();
|
||||
|
||||
async void send_orders_continuously() {
|
||||
// Periodically send an order with one of the CustomOrderDefinitions below.
|
||||
// If you are unfamiliar with async/await, this will also serve as a simple example
|
||||
// of how those keywords may be used. However, there is no substite for the official docs:
|
||||
// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
|
||||
var order_templates = new CustomOrderDefinitionImmutable[] {
|
||||
new CustomOrderDefinitionImmutable(OrderTemplates.limit()),
|
||||
new CustomOrderDefinitionImmutable(OrderTemplates.market()),
|
||||
new CustomOrderDefinitionImmutable(OrderTemplates.iceberg(5))
|
||||
};
|
||||
for (int num_orders_sent = 0; num_orders_sent < max_orders && !_is_disposed; num_orders_sent++) {
|
||||
int template_index = num_orders_sent % order_templates.Length;
|
||||
CustomOrderDefinitionImmutable templatei = order_templates[template_index];
|
||||
// The order templates we created are immutable, however the api uses the mutable form.
|
||||
// Not to worry tho, there is a nifty method called 'CloneToMutable.'
|
||||
// Note that all of the non-primitive immutable parameters in the api methods have a 'CloneToMutable' method,
|
||||
// it is not unique to CustomOrderDefinitionImmutable.
|
||||
CustomOrderDefinition template = templatei.CloneToMutable();
|
||||
await model.send_order_async(user, account, security_key, template, 12, 100);
|
||||
// pause for a moment before sending the next order
|
||||
await Task.Delay(delay_between_orders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
// flip _is_disposed to true, so the 'send_orders_continuously' loop
|
||||
// knows it's time to stop.
|
||||
_is_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.api.csharp.utils {
|
||||
static class BookUtils {
|
||||
/// <summary>
|
||||
/// Get a display string of the bbo.
|
||||
/// </summary>
|
||||
public static string fmt_bbo(BookEntryImmutable book) {
|
||||
return fmt_bbo(get_bbo(book));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a display string of the bbo.
|
||||
/// </summary>
|
||||
public static string fmt_bbo((PriceEntryImmutable? bid, PriceEntryImmutable? ask) bbo) {
|
||||
return $"{fmt_price_entry(bbo.bid)} <--> {fmt_price_entry(bbo.ask)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a display string of a <see cref="PriceEntryImmutable"/>.
|
||||
/// </summary>
|
||||
public static string fmt_price_entry(PriceEntryImmutable? price_entry) {
|
||||
return price_entry.HasValue ? fmt_price_entry(price_entry.Value) : "N/A";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a display string of a <see cref="PriceEntryImmutable"/>.
|
||||
/// </summary>
|
||||
public static string fmt_price_entry(PriceEntryImmutable price_entry) {
|
||||
return $"{price_entry}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a display string of the bbo.
|
||||
/// </summary>
|
||||
public static (PriceEntryImmutable? best_bid, PriceEntryImmutable? best_ask) get_bbo(BookEntryImmutable book) {
|
||||
return (get_top_of_book(book.Bid), get_top_of_book(book.Ask));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first element in <paramref name="depth"/>,
|
||||
/// as this represents the top price level
|
||||
/// of a particular side of the market (bid or ask).
|
||||
/// </summary>
|
||||
public static PriceEntryImmutable? get_top_of_book(IEnumerable<PriceEntryImmutable> depth) {
|
||||
foreach (var d in depth)
|
||||
return d;
|
||||
// there are no elements (the book is empty for this side), so return null.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using bts.api.orderman;
|
||||
|
||||
namespace bts.api.csharp.utils {
|
||||
static class OrderUtils {
|
||||
public static string fmt_order(uint quantity, double price, CustomOrderDefinition custom_order_definition) {
|
||||
return $"{quantity} @ {price}: {custom_order_definition}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace bts.api.csharp.utils {
|
||||
static class SecurityUtils {
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,12 @@ using namespace boost::posix_time;
|
||||
DownloadWindow::DownloadWindow(const DownloadWindow &src)
|
||||
: _start(src.start()), _stop(src.stop()) {}
|
||||
|
||||
DownloadWindow &DownloadWindow::operator=(const DownloadWindow &src) {
|
||||
_start = src.start();
|
||||
_stop = src.stop();
|
||||
return *this;
|
||||
}
|
||||
|
||||
DownloadWindow ::DownloadWindow(datetime::PTime now, time_duration start_time,
|
||||
time_duration stop_time) {
|
||||
bts_assert(start_time != stop_time);
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace position {
|
||||
class DownloadWindow {
|
||||
public:
|
||||
DownloadWindow (const DownloadWindow&);
|
||||
DownloadWindow&operator=(const DownloadWindow&) ;
|
||||
DownloadWindow(datetime::PTime now,
|
||||
boost::posix_time::time_duration window_begin,
|
||||
boost::posix_time::time_duration window_end);
|
||||
|
||||
@@ -1 +1 @@
|
||||
set(sources ../../externals/linux/include/google/protobuf/descriptor.proto;../../externals/linux/include/google/protobuf/timestamp.proto;../lib/api/apicore.proto;../lib/api/apigateway.proto;../lib/api/apioptions.proto;../lib/api/services/AdminService.proto;../lib/api/services/AlgoDefinitionService.proto;../lib/api/services/AlgoService.proto;../lib/api/services/ApiUnitTestService.proto;../lib/api/services/AuthService.proto;../lib/api/services/BasePositionService.proto;../lib/api/services/ErrorReportService.proto;../lib/api/services/HelloService.proto;../lib/api/services/MBOService.proto;../lib/api/services/MarginService.proto;../lib/api/services/MarketDataAuxiliaryService.proto;../lib/api/services/MarketDataRelay.proto;../lib/api/services/MarketDataService.proto;../lib/api/services/MoneyFileService.proto;../lib/api/services/OrderService.proto;../lib/api/services/OutOfProcessURLListener.proto;../lib/api/services/PersistenceService.proto;../lib/api/services/PositionService.proto;../lib/api/services/ShellDiagnosticService.proto;../lib/api/services/ShellLatencyService.proto;../lib/api/services/SyntheticSecurityService.proto;../lib/domain_model/Security.proto;../lib/md/protos/co2.proto;../lib/sparkproto/SparkProtos.proto;api/build.sh;api/quiet_build.sh;api/src/DynamicService.ts;api/src/ServiceContext.ts;api/src/bts_assert.ts;ui/src/Access.fs;ui/src/Accounts/ClipLimits.fs;ui/src/Accounts/ProductLimits.fs;ui/src/Accounts/SecurityLimits.fs;ui/src/Accounts/State.fs;ui/src/Accounts/Types.fs;ui/src/Accounts/View.fs;ui/src/App.fs;ui/src/Cmd.fs;ui/src/Controls/Button.fs;ui/src/Controls/DataGrid.fs;ui/src/Controls/Fields.fs;ui/src/Controls/Modal.fs;ui/src/Controls/ReactGrid.fs;ui/src/Controls/Table.fs;ui/src/Controls/nbsp.fs;ui/src/Global.fs;ui/src/Imports.fs;ui/src/Lens.fs;ui/src/Login/Login.fs;ui/src/Nav.fs;ui/src/State.fs;ui/src/Types.fs;ui/src/Users/State.fs;ui/src/Users/Types.fs;ui/src/Users/View.fs;ui/src/Util/EnumUtils.fs;ui/src/Util/JSMap.fs;ui/src/Util/StreamUtils.fs;ui/src/obj/Debug/netstandard2.0/ui.AssemblyInfo.fs)
|
||||
set(sources ../../externals/linux/include/google/protobuf/descriptor.proto;../../externals/linux/include/google/protobuf/timestamp.proto;../lib/api/apicore.proto;../lib/api/apigateway.proto;../lib/api/apioptions.proto;../lib/api/services/AdminService.proto;../lib/api/services/AlgoDefinitionService.proto;../lib/api/services/AlgoService.proto;../lib/api/services/ApiUnitTestService.proto;../lib/api/services/AuthService.proto;../lib/api/services/BasePositionService.proto;../lib/api/services/ErrorReportService.proto;../lib/api/services/HelloService.proto;../lib/api/services/MBOService.proto;../lib/api/services/MarginService.proto;../lib/api/services/MarketDataAuxiliaryService.proto;../lib/api/services/MarketDataRelay.proto;../lib/api/services/MarketDataService.proto;../lib/api/services/MoneyFileService.proto;../lib/api/services/OrderService.proto;../lib/api/services/OutOfProcessURLListener.proto;../lib/api/services/PersistenceService.proto;../lib/api/services/PositionService.proto;../lib/api/services/ShellDiagnosticService.proto;../lib/api/services/ShellLatencyService.proto;../lib/api/services/SyntheticSecurityService.proto;../lib/domain_model/Security.proto;../lib/md/protos/co2.proto;../lib/sparkproto/SparkProtos.proto;api/build.sh;api/node_modules/cssstyle/scripts/run_tests.sh;api/node_modules/exit/test/fixtures/create-files.sh;api/node_modules/protobufjs/cli/node_modules/acorn-jsx/node_modules/acorn/bin/update_authors.sh;api/node_modules/watch/release.sh;api/quiet_build.sh;api/src/DynamicService.ts;api/src/ServiceContext.ts;api/src/bts_assert.ts;ui/src/Access.fs;ui/src/Accounts/ClipLimits.fs;ui/src/Accounts/ProductLimits.fs;ui/src/Accounts/SecurityLimits.fs;ui/src/Accounts/State.fs;ui/src/Accounts/Types.fs;ui/src/Accounts/View.fs;ui/src/App.fs;ui/src/Cmd.fs;ui/src/Controls/Button.fs;ui/src/Controls/DataGrid.fs;ui/src/Controls/Fields.fs;ui/src/Controls/Modal.fs;ui/src/Controls/ReactGrid.fs;ui/src/Controls/Table.fs;ui/src/Controls/nbsp.fs;ui/src/Global.fs;ui/src/Imports.fs;ui/src/Lens.fs;ui/src/Login/Login.fs;ui/src/Nav.fs;ui/src/State.fs;ui/src/Types.fs;ui/src/Users/State.fs;ui/src/Users/Types.fs;ui/src/Users/View.fs;ui/src/Util/EnumUtils.fs;ui/src/Util/JSMap.fs;ui/src/Util/StreamUtils.fs;ui/src/obj/Debug/netstandard2.0/ui.AssemblyInfo.fs)
|
||||
Generated
+115
-115
@@ -32,12 +32,12 @@
|
||||
"@protobufjs/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
|
||||
"integrity": "sha1-TIVzDlm5ofHzSQR9vyQpYDS7JzU="
|
||||
},
|
||||
"@protobufjs/codegen": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
|
||||
"integrity": "sha1-fvN/DQEPsCitGtWXIuUG2SYoFcs="
|
||||
},
|
||||
"@protobufjs/eventemitter": {
|
||||
"version": "1.1.0",
|
||||
@@ -81,7 +81,7 @@
|
||||
"@types/jest": {
|
||||
"version": "22.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-22.2.3.tgz",
|
||||
"integrity": "sha512-e74sM9W/4qqWB6D4TWV9FQk0WoHtX1X4FJpbjxucMSVJHtFjbQOH3H6yp+xno4br0AKG0wz/kPtaN599GUOvAg==",
|
||||
"integrity": "sha1-AVfAMW3DcixDp7cd4/3zrLzO8Q0=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/long": {
|
||||
@@ -160,7 +160,7 @@
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
@@ -169,7 +169,7 @@
|
||||
"anymatch": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
|
||||
"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
|
||||
"integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"micromatch": "^3.1.4",
|
||||
@@ -191,7 +191,7 @@
|
||||
"braces": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
|
||||
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
|
||||
"integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arr-flatten": "^1.1.0",
|
||||
@@ -304,7 +304,7 @@
|
||||
"kind-of": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
|
||||
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
|
||||
"integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -312,7 +312,7 @@
|
||||
"extglob": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
|
||||
"integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
|
||||
"integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-unique": "^0.3.2",
|
||||
@@ -371,7 +371,7 @@
|
||||
"is-accessor-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
|
||||
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -380,7 +380,7 @@
|
||||
"is-data-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
|
||||
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -389,7 +389,7 @@
|
||||
"is-descriptor": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
|
||||
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
|
||||
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-accessor-descriptor": "^1.0.0",
|
||||
@@ -426,13 +426,13 @@
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=",
|
||||
"dev": true
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
|
||||
"integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
|
||||
"integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arr-diff": "^4.0.0",
|
||||
@@ -464,7 +464,7 @@
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
@@ -482,7 +482,7 @@
|
||||
"arr-flatten": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
|
||||
"integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
|
||||
"integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=",
|
||||
"dev": true
|
||||
},
|
||||
"arr-union": {
|
||||
@@ -548,7 +548,7 @@
|
||||
"astral-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
|
||||
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
|
||||
"integrity": "sha1-bIw/uCfdQ+45GPJ7gngqt2WKb9k=",
|
||||
"dev": true
|
||||
},
|
||||
"async": {
|
||||
@@ -569,7 +569,7 @@
|
||||
"async-limiter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
|
||||
"integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=",
|
||||
"dev": true
|
||||
},
|
||||
"asynckit": {
|
||||
@@ -673,7 +673,7 @@
|
||||
"babel-generator": {
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
|
||||
"integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
|
||||
"integrity": "sha1-GERAjTuPDTWkBOp6wYDwh6YBvZA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-messages": "^6.23.0",
|
||||
@@ -718,7 +718,7 @@
|
||||
"babel-plugin-istanbul": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz",
|
||||
"integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==",
|
||||
"integrity": "sha1-NsWbIZLvzoHFs3gyG3QXWt0cmkU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
||||
@@ -789,7 +789,7 @@
|
||||
"source-map-support": {
|
||||
"version": "0.4.18",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
|
||||
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
|
||||
"integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"source-map": "^0.5.6"
|
||||
@@ -852,7 +852,7 @@
|
||||
"babylon": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
|
||||
"integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
|
||||
"integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=",
|
||||
"dev": true
|
||||
},
|
||||
"balanced-match": {
|
||||
@@ -864,7 +864,7 @@
|
||||
"base": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
|
||||
"integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
|
||||
"integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cache-base": "^1.0.1",
|
||||
@@ -888,7 +888,7 @@
|
||||
"is-accessor-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
|
||||
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -897,7 +897,7 @@
|
||||
"is-data-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
|
||||
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -906,7 +906,7 @@
|
||||
"is-descriptor": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
|
||||
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
|
||||
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-accessor-descriptor": "^1.0.0",
|
||||
@@ -923,7 +923,7 @@
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -956,7 +956,7 @@
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@@ -1007,7 +1007,7 @@
|
||||
"cache-base": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
||||
"integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
|
||||
"integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"collection-visit": "^1.0.0",
|
||||
@@ -1090,7 +1090,7 @@
|
||||
"anymatch": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
|
||||
"integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
|
||||
"integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"micromatch": "^2.1.5",
|
||||
@@ -1108,7 +1108,7 @@
|
||||
"class-utils": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||
"integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
|
||||
"integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arr-union": "^3.1.0",
|
||||
@@ -1331,7 +1331,7 @@
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
@@ -1377,7 +1377,7 @@
|
||||
"define-property": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
|
||||
"integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
|
||||
"integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-descriptor": "^1.0.2",
|
||||
@@ -1387,7 +1387,7 @@
|
||||
"is-accessor-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
|
||||
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -1396,7 +1396,7 @@
|
||||
"is-data-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
|
||||
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -1405,7 +1405,7 @@
|
||||
"is-descriptor": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
|
||||
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
|
||||
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-accessor-descriptor": "^1.0.0",
|
||||
@@ -1422,7 +1422,7 @@
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -1451,13 +1451,13 @@
|
||||
"diff": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||
"integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=",
|
||||
"dev": true
|
||||
},
|
||||
"domexception": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
|
||||
"integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
|
||||
"integrity": "sha1-k3RCZEymoxJh7zbj7Gd/6AVYLJA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"webidl-conversions": "^4.0.2"
|
||||
@@ -1540,7 +1540,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
@@ -1615,7 +1615,7 @@
|
||||
"expect": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-22.4.3.tgz",
|
||||
"integrity": "sha512-XcNXEPehqn8b/jm8FYotdX0YrXn36qp4HWlrVT4ktwQas1l1LPxiVWncYnnL2eyMtKAmVIaG0XAp0QlrqJaxaA==",
|
||||
"integrity": "sha1-1aKdCg4fshU1V8rvJnTUVH6RRnQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
@@ -1645,7 +1645,7 @@
|
||||
"is-extendable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
|
||||
"integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
|
||||
"integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-plain-object": "^2.0.4"
|
||||
@@ -2753,7 +2753,7 @@
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=",
|
||||
"dev": true
|
||||
},
|
||||
"get-caller-file": {
|
||||
@@ -2828,7 +2828,7 @@
|
||||
"globals": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
|
||||
"integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=",
|
||||
"dev": true
|
||||
},
|
||||
"graceful-fs": {
|
||||
@@ -3009,7 +3009,7 @@
|
||||
"html-encoding-sniffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
|
||||
"integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
|
||||
"integrity": "sha1-5w2EuU2lOqN14R/jo1G+ZkLKRvg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"whatwg-encoding": "^1.0.1"
|
||||
@@ -3035,7 +3035,7 @@
|
||||
"import-local": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz",
|
||||
"integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==",
|
||||
"integrity": "sha1-Xk/9wD9P5sAJxnKb6yljHC+CJ7w=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pkg-dir": "^2.0.0",
|
||||
@@ -3067,7 +3067,7 @@
|
||||
"invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
||||
"integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
@@ -3106,7 +3106,7 @@
|
||||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||
"integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=",
|
||||
"dev": true
|
||||
},
|
||||
"is-builtin-module": {
|
||||
@@ -3151,7 +3151,7 @@
|
||||
"is-descriptor": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
|
||||
"integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
|
||||
"integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-accessor-descriptor": "^0.1.6",
|
||||
@@ -3162,7 +3162,7 @@
|
||||
"kind-of": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
|
||||
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
|
||||
"integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -3253,7 +3253,7 @@
|
||||
"is-plain-object": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isobject": "^3.0.1"
|
||||
@@ -3315,7 +3315,7 @@
|
||||
"is-windows": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
|
||||
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
|
||||
"integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=",
|
||||
"dev": true
|
||||
},
|
||||
"isarray": {
|
||||
@@ -3538,7 +3538,7 @@
|
||||
"jest-changed-files": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-22.4.3.tgz",
|
||||
"integrity": "sha512-83Dh0w1aSkUNFhy5d2dvqWxi/y6weDwVVLU6vmK0cV9VpRxPzhTeGimbsbRDSnEoszhF937M4sDLLeS7Cu/Tmw==",
|
||||
"integrity": "sha1-iIIYHgIsOL1GouTRjUTRnZCpD7I=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"throat": "^4.0.0"
|
||||
@@ -3566,7 +3566,7 @@
|
||||
"jest-diff": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz",
|
||||
"integrity": "sha512-/QqGvCDP5oZOF6PebDuLwrB2BMD8ffJv6TAGAdEVuDx1+uEgrHpSFrfrOiMRx2eJ1hgNjlQrOQEHetVwij90KA==",
|
||||
"integrity": "sha1-4YzD/v8K7vFZ0CMQ8mhtQGU3gDA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.0.1",
|
||||
@@ -3578,7 +3578,7 @@
|
||||
"jest-docblock": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-22.4.3.tgz",
|
||||
"integrity": "sha512-uPKBEAw7YrEMcXueMKZXn/rbMxBiSv48fSqy3uEnmgOlQhSX+lthBqHb1fKWNVmFqAp9E/RsSdBfiV31LbzaOg==",
|
||||
"integrity": "sha1-UIhvEytCsoDJA8WSNzu26Tu2ixk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"detect-newline": "^2.1.0"
|
||||
@@ -3587,7 +3587,7 @@
|
||||
"jest-environment-jsdom": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz",
|
||||
"integrity": "sha512-FviwfR+VyT3Datf13+ULjIMO5CSeajlayhhYQwpzgunswoaLIPutdbrnfUHEMyJCwvqQFaVtTmn9+Y8WCt6n1w==",
|
||||
"integrity": "sha1-1n2qQVXjNRauzdNa/YLUq/D6ih4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jest-mock": "^22.4.3",
|
||||
@@ -3598,7 +3598,7 @@
|
||||
"jest-environment-node": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-22.4.3.tgz",
|
||||
"integrity": "sha512-reZl8XF6t/lMEuPWwo9OLfttyC26A5AMgDyEQ6DBgZuyfyeNUzYT8BFo6uxCCP/Av/b7eb9fTi3sIHFPBzmlRA==",
|
||||
"integrity": "sha1-VMTqo3TIPdUqnah1m+FOvh0LkSk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jest-mock": "^22.4.3",
|
||||
@@ -3608,13 +3608,13 @@
|
||||
"jest-get-type": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
|
||||
"integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==",
|
||||
"integrity": "sha1-46hQTYR5NC3UQgI2syKGnxiQDOQ=",
|
||||
"dev": true
|
||||
},
|
||||
"jest-haste-map": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-22.4.3.tgz",
|
||||
"integrity": "sha512-4Q9fjzuPVwnaqGKDpIsCSoTSnG3cteyk2oNVjBX12HHOaF1oxql+uUiqZb5Ndu7g/vTZfdNwwy4WwYogLh29DQ==",
|
||||
"integrity": "sha1-JYQvoro1AgB2esJ/ZY1YudXC4gs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fb-watchman": "^2.0.0",
|
||||
@@ -3648,7 +3648,7 @@
|
||||
"jest-leak-detector": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-22.4.3.tgz",
|
||||
"integrity": "sha512-NZpR/Ls7+ndO57LuXROdgCGz2RmUdC541tTImL9bdUtU3WadgFGm0yV+Ok4Fuia/1rLAn5KaJ+i76L6e3zGJYQ==",
|
||||
"integrity": "sha1-K3smMQOvroxStrkSQaLeQBF+WzU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pretty-format": "^22.4.3"
|
||||
@@ -3657,7 +3657,7 @@
|
||||
"jest-matcher-utils": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz",
|
||||
"integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==",
|
||||
"integrity": "sha1-RjL+Qo68c+vBlNPHtl03sWH3EP8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.0.1",
|
||||
@@ -3668,7 +3668,7 @@
|
||||
"jest-message-util": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz",
|
||||
"integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==",
|
||||
"integrity": "sha1-zz04qv5L792/xFXlfWXVI545nrc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0-beta.35",
|
||||
@@ -3681,19 +3681,19 @@
|
||||
"jest-mock": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-22.4.3.tgz",
|
||||
"integrity": "sha512-+4R6mH5M1G4NK16CKg9N1DtCaFmuxhcIqF4lQK/Q1CIotqMs/XBemfpDPeVZBFow6iyUNu6EBT9ugdNOTT5o5Q==",
|
||||
"integrity": "sha1-9jui8HoVEXcs3Hl5czOX33cKq8c=",
|
||||
"dev": true
|
||||
},
|
||||
"jest-regex-util": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz",
|
||||
"integrity": "sha512-LFg1gWr3QinIjb8j833bq7jtQopiwdAs67OGfkPrvy7uNUbVMfTXXcOKXJaeY5GgjobELkKvKENqq1xrUectWg==",
|
||||
"integrity": "sha1-qCbrGRzfIlAhmMVAGh/ATenO9a8=",
|
||||
"dev": true
|
||||
},
|
||||
"jest-resolve": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-22.4.3.tgz",
|
||||
"integrity": "sha512-u3BkD/MQBmwrOJDzDIaxpyqTxYH+XqAXzVJP51gt29H8jpj3QgKof5GGO2uPGKGeA1yTMlpbMs1gIQ6U4vcRhw==",
|
||||
"integrity": "sha1-DOnUOMhDgimqm5FpaOxrBcGrtOo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browser-resolve": "^1.11.2",
|
||||
@@ -3703,7 +3703,7 @@
|
||||
"jest-resolve-dependencies": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz",
|
||||
"integrity": "sha512-06czCMVToSN8F2U4EvgSB1Bv/56gc7MpCftZ9z9fBgUQM7dzHGCMBsyfVA6dZTx8v0FDcnALf7hupeQxaBCvpA==",
|
||||
"integrity": "sha1-4iVqWoRnMtw5acty88mtdyWoGV4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jest-regex-util": "^22.4.3"
|
||||
@@ -3767,13 +3767,13 @@
|
||||
"jest-serializer": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-22.4.3.tgz",
|
||||
"integrity": "sha512-uPaUAppx4VUfJ0QDerpNdF43F68eqKWCzzhUlKNDsUPhjOon7ZehR4C809GCqh765FoMRtTVUVnGvIoskkYHiw==",
|
||||
"integrity": "sha1-pnm4Gn8RHkdmI19PDEbSMO4PdDY=",
|
||||
"dev": true
|
||||
},
|
||||
"jest-snapshot": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz",
|
||||
"integrity": "sha512-JXA0gVs5YL0HtLDCGa9YxcmmV2LZbwJ+0MfyXBBc5qpgkEYITQFJP7XNhcHFbUvRiniRpRbGVfJrOoYhhGE0RQ==",
|
||||
"integrity": "sha1-tcm0KEb/ufrMt2uEExW6Z4hzYtI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.0.1",
|
||||
@@ -3787,7 +3787,7 @@
|
||||
"jest-util": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz",
|
||||
"integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==",
|
||||
"integrity": "sha1-xw/sjuxIfDexCwgJ3AZKfs9qr6w=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"callsites": "^2.0.0",
|
||||
@@ -3802,7 +3802,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -3823,7 +3823,7 @@
|
||||
"jest-worker": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-22.4.3.tgz",
|
||||
"integrity": "sha512-B1ucW4fI8qVAuZmicFxI1R3kr2fNeYJyvIQ1rKcuLYnenFV5K5aMbxFj6J0i00Ju83S8jP2d7Dz14+AvbIHRYQ==",
|
||||
"integrity": "sha1-XEIUF8uhwKv2S/Vr1ft5aNed1As=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"merge-stream": "^1.0.1"
|
||||
@@ -3992,7 +3992,7 @@
|
||||
"left-pad": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
|
||||
"integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==",
|
||||
"integrity": "sha1-W4o6d2Xf4AEmHd6RVYnngvjJTR4=",
|
||||
"dev": true
|
||||
},
|
||||
"leven": {
|
||||
@@ -4049,7 +4049,7 @@
|
||||
"long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
"integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg="
|
||||
},
|
||||
"longest": {
|
||||
"version": "1.0.1",
|
||||
@@ -4164,13 +4164,13 @@
|
||||
"mimic-fn": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
|
||||
"integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
@@ -4195,7 +4195,7 @@
|
||||
"is-extendable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
|
||||
"integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
|
||||
"integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-plain-object": "^2.0.4"
|
||||
@@ -4260,7 +4260,7 @@
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -4475,7 +4475,7 @@
|
||||
"os-locale": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
|
||||
"integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
|
||||
"integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"execa": "^0.7.0",
|
||||
@@ -4543,7 +4543,7 @@
|
||||
"parse5": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
|
||||
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
|
||||
"integrity": "sha1-bXhlbj2o14tOwLkG98CO8d/j9gg=",
|
||||
"dev": true
|
||||
},
|
||||
"pascalcase": {
|
||||
@@ -4626,7 +4626,7 @@
|
||||
"pn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
|
||||
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
|
||||
"integrity": "sha1-4vTO8OIZ9GPBeas3Rj5OHs3Muvs=",
|
||||
"dev": true
|
||||
},
|
||||
"posix-character-classes": {
|
||||
@@ -4650,7 +4650,7 @@
|
||||
"pretty-format": {
|
||||
"version": "22.4.3",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz",
|
||||
"integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==",
|
||||
"integrity": "sha1-+HPXgIOanALpZkyKCC6e556qwW8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^3.0.0",
|
||||
@@ -4668,7 +4668,7 @@
|
||||
"private": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
|
||||
"integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
|
||||
"integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=",
|
||||
"dev": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
@@ -4801,7 +4801,7 @@
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
@@ -4837,13 +4837,13 @@
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
|
||||
"integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=",
|
||||
"dev": true
|
||||
},
|
||||
"regex-cache": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
|
||||
"integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
|
||||
"integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-equal-shallow": "^0.1.3"
|
||||
@@ -4852,7 +4852,7 @@
|
||||
"regex-not": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
|
||||
"integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
|
||||
"integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^3.0.2",
|
||||
@@ -4978,7 +4978,7 @@
|
||||
"ret": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
|
||||
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
|
||||
"integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=",
|
||||
"dev": true
|
||||
},
|
||||
"right-align": {
|
||||
@@ -5046,7 +5046,7 @@
|
||||
"braces": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
|
||||
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
|
||||
"integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arr-flatten": "^1.1.0",
|
||||
@@ -5159,7 +5159,7 @@
|
||||
"kind-of": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
|
||||
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
|
||||
"integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -5167,7 +5167,7 @@
|
||||
"extglob": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
|
||||
"integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
|
||||
"integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-unique": "^0.3.2",
|
||||
@@ -5226,7 +5226,7 @@
|
||||
"is-accessor-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
|
||||
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -5235,7 +5235,7 @@
|
||||
"is-data-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
|
||||
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -5244,7 +5244,7 @@
|
||||
"is-descriptor": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
|
||||
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
|
||||
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-accessor-descriptor": "^1.0.0",
|
||||
@@ -5281,13 +5281,13 @@
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=",
|
||||
"dev": true
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
|
||||
"integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
|
||||
"integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arr-diff": "^4.0.0",
|
||||
@@ -5316,7 +5316,7 @@
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
|
||||
"integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
@@ -5390,7 +5390,7 @@
|
||||
"shellwords": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
|
||||
"integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
|
||||
"integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs=",
|
||||
"dev": true
|
||||
},
|
||||
"signal-exit": {
|
||||
@@ -5408,7 +5408,7 @@
|
||||
"snapdragon": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
|
||||
"integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
|
||||
"integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base": "^0.11.1",
|
||||
@@ -5444,7 +5444,7 @@
|
||||
"snapdragon-node": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
|
||||
"integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
|
||||
"integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-property": "^1.0.0",
|
||||
@@ -5464,7 +5464,7 @@
|
||||
"is-accessor-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
|
||||
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -5473,7 +5473,7 @@
|
||||
"is-data-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
|
||||
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
|
||||
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^6.0.0"
|
||||
@@ -5482,7 +5482,7 @@
|
||||
"is-descriptor": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
|
||||
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
|
||||
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-accessor-descriptor": "^1.0.0",
|
||||
@@ -5499,7 +5499,7 @@
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -5507,7 +5507,7 @@
|
||||
"snapdragon-util": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
|
||||
"integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
|
||||
"integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"kind-of": "^3.2.0"
|
||||
@@ -5553,7 +5553,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -5583,7 +5583,7 @@
|
||||
"spdx-expression-parse": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
|
||||
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
|
||||
"integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"spdx-exceptions": "^2.1.0",
|
||||
@@ -5599,7 +5599,7 @@
|
||||
"split-string": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
||||
"integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
|
||||
"integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^3.0.0"
|
||||
@@ -5673,7 +5673,7 @@
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||
"integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
@@ -5683,7 +5683,7 @@
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
@@ -6078,7 +6078,7 @@
|
||||
"to-regex": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
|
||||
"integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
|
||||
"integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-property": "^2.0.2",
|
||||
@@ -6231,7 +6231,7 @@
|
||||
"tsutils": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.24.0.tgz",
|
||||
"integrity": "sha512-rOIkvoe17acR3r96IPnqwa1+Z7zx9AroEtEKl20IeExXtoWptqG/zb806cYOvdbQGcxh1eOaZQNruOQ716Edig==",
|
||||
"integrity": "sha1-qEE0NY4QxCA5/tCo8VSEE/EYr/c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
@@ -6421,7 +6421,7 @@
|
||||
"util.promisify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
|
||||
"integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
|
||||
"integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "^1.1.2",
|
||||
@@ -6494,7 +6494,7 @@
|
||||
"webidl-conversions": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
|
||||
"integrity": "sha1-qFWYCx8LazWbodXZ+zmulB+qY60=",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-encoding": {
|
||||
@@ -6621,7 +6621,7 @@
|
||||
"xml-name-validator": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
|
||||
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
|
||||
"integrity": "sha1-auc+Bt5NjG5H+fsYH3jWSK1FfGo=",
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
@@ -6639,7 +6639,7 @@
|
||||
"yargs": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz",
|
||||
"integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==",
|
||||
"integrity": "sha1-RU0HTCsWpRpD4vt4B+T53mnMtcU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^4.0.0",
|
||||
@@ -6672,7 +6672,7 @@
|
||||
"yargs-parser": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz",
|
||||
"integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==",
|
||||
"integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^4.1.0"
|
||||
|
||||
Generated
+1772
-1743
File diff suppressed because it is too large.
Load diff
@@ -1,9 +1,8 @@
|
||||
#!/bin/bash
|
||||
BUILD=clang_debug
|
||||
BUILD_LOC=/home/ppinto/temp/spark_build
|
||||
if [ -f /.dockerenv ]; then
|
||||
cd $BUILD_LOC/$BUILD && ninja
|
||||
cd /home/ppinto/temp/spark_build/$BUILD && ninja
|
||||
else
|
||||
cid=`docker ps | grep -P "emacs_ide|bluetradingsystems/linux_docker" | awk '{print $1}'`
|
||||
docker exec $cid /bin/bash -c "cd $BUILD_LOC/$BUILD && ninja"
|
||||
cid=`docker ps | grep -P "emacs_ide|bluetradingsystems/linux_docker" | awk '{print $1}'`
|
||||
docker exec $cid /bin/bash -c "cd /home/ppinto/temp/spark_build/$BUILD && ninja"
|
||||
fi
|
||||
@@ -12,12 +12,6 @@ ignore = Path docker
|
||||
ignore = Regex .*\.pyc
|
||||
ignore = Path x64
|
||||
ignore = Path .vs
|
||||
ignore = Path src/.vs
|
||||
ignore = Path src/lib/.vs
|
||||
ignore = Path src/lib/out
|
||||
ignore = Path src/sdk/out
|
||||
ignore = Path src/server/sparkd/out
|
||||
ignore = Path src/web/out
|
||||
ignore = Path .vscode
|
||||
ignore = Path src/client
|
||||
ignore = Path src/web/api/node_modules
|
||||
|
||||
Reference in new issue
Block a user