Merged from master, note that SpreaderOrderVM.cs was not merged yet, so this client build will fail
No files matched your search
@@ -1 +1 @@
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/index.md;content/bts_spark/Help/image32.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Help/index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Launcher/index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Logging-In-to-the-System/index.md;content/bts_spark/Market/image11.png;content/bts_spark/Market/image12.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image30.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image63.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/My-Windows/index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Settings/index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Spreader/index.md;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image22.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image26.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image36.png;content/bts_spark/Trading/image4.png;content/bts_spark/Trading/image41.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image46.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image65.png;content/bts_spark/Trading/image67.png;content/bts_spark/Trading/image71.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/index.md;content/bts_spark/_index.md;content/bts_spark_sdk/Client/Overview.md;content/bts_spark_sdk/Client/Threading.md;content/bts_spark_sdk/Client/_index.md;content/bts_spark_sdk/Server/_index.md;content/bts_spark_sdk/_index.md;content/release_notes/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Help/index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Launcher/index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Logging-In-to-the-System/index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/index.md;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/My-Windows/index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Settings/index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Spreader/index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/index.md;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/bts_spark_sdk/Client/Overview.md;content/bts_spark_sdk/Client/Threading.md;content/bts_spark_sdk/Client/_index.md;content/bts_spark_sdk/Server/_index.md;content/bts_spark_sdk/_index.md;content/release_notes/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
|
||||
@@ -22,7 +22,7 @@ update_deps_file("${All_Deps}")
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/web/docs/index.html
|
||||
COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/build.sh
|
||||
ARGS ${CMAKE_BINARY_DIR}/web/docs/
|
||||
ARGS ${CMAKE_BINARY_DIR}/web/docs/ ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Building Manual UI"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${All_Deps})
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
#!/bin/bash
|
||||
tmpfile=$(mktemp /tmp/quiet_build_log.XXXX)
|
||||
cp -r ../../src/sdk/doc/* ./content/bts_spark_sdk/Server/
|
||||
./run_zola.sh build -u '/docs/' -o $1 >$tmpfile 2>&1
|
||||
rm ./content/bts_spark_sdk/Server/algos.md
|
||||
|
||||
cur_dir=$(pwd)
|
||||
temp_path="$2/web_temp"
|
||||
zola_path=$(readlink -f "../../externals/linux/bin/zola/zola")
|
||||
|
||||
mkdir -p $temp_path
|
||||
cp -r ./* $temp_path
|
||||
cp -r ../../src/sdk/doc/* $temp_path/content/bts_spark_sdk/Server/
|
||||
|
||||
cd $temp_path
|
||||
$zola_path build -u '/docs/' -o $1 >$tmpfile 2>&1
|
||||
|
||||
cd $cur_dir
|
||||
rm -r $temp_path
|
||||
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
rm $tmpfile
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# The URL the site will be built for
|
||||
base_url = "https://www.bluetradingsystems.com/docs"
|
||||
base_url = "https://www.bluetradingsystems.com/documentation"
|
||||
|
||||
title = "BTS Spark Documentation"
|
||||
|
||||
@@ -15,4 +15,4 @@ build_search_index = true
|
||||
|
||||
[extra]
|
||||
# Put all your custom variables here
|
||||
book_numbered_chapters = false
|
||||
book_numbered_chapters = false
|
||||
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 29 KiB |
@@ -25,7 +25,7 @@ insert_anchor_links = "right"
|
||||
which will list what version you are running as well as who is
|
||||
currently logged in.
|
||||
|
||||

|
||||

|
||||
|
||||
## Activity log
|
||||
This is an overall log viewer which will show
|
||||
|
||||
|
After Width: | Height: | Size: 942 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 42 KiB |
@@ -51,7 +51,7 @@ insert_anchor_links = "right"
|
||||
securities you have chosen, in this example, it appears like the
|
||||
following:
|
||||
|
||||

|
||||

|
||||
|
||||
- Once a market view is launched and you are viewing data, you can
|
||||
customize the view further by the following:
|
||||
@@ -88,31 +88,34 @@ insert_anchor_links = "right"
|
||||
|
||||
- Change securities
|
||||
|
||||
- Click on the settings icon which will launch the market view security selection. You can add or remove Securities you have already selected and are shown in the market view.
|
||||
- Click on the settings icon  which will launch the market view security selection. You can add or remove Securities you have already selected and are shown in the market view.
|
||||
|
||||
- Launch an order ticket
|
||||
|
||||
- This is done 1 of 2 ways:
|
||||
|
||||
- This is done 1 of 3 ways:
|
||||
- Single left clicking on either the Bid Qty, Bid Price, Ask Price,
|
||||
or Ask Qty which will bring up an order ticket pre-populated with
|
||||
either the Bid or Ask price and volume.
|
||||
- Highlighting a single security and clicking on the order ticket
|
||||
 icon
|
||||
on the left-hand browser window of the market view
|
||||
- Highlighting a single security and right clicking bringing up the
|
||||
menu options to launch an order ticket.
|
||||
|
||||
- Launch a ladder for 1 security
|
||||
- You can double-click on any security in the market view, besides
|
||||
the Bid Qty, Bid Price, Ask Price, or Ask Qty columns to launch a
|
||||
ladder for that Security.
|
||||
- This is done 1 of 2 ways:
|
||||
- You can double-click on any security in the market view, besides
|
||||
the Bid Qty, Bid Price, Ask Price, or Ask Qty columns to launch a
|
||||
ladder for that Security.
|
||||
- Highlighting a single security and right clicking bringing up the
|
||||
menu options to launch a ladder.
|
||||
|
||||
- Launching multiple ladders
|
||||
|
||||
- If you hold down the Ctrl key and select multiple securities, you
|
||||
can then click on the ladder icon
|
||||
 on
|
||||
the left-hand side to bring up the selected ladders all at
|
||||
once.
|
||||
- This is done 1 of 2 ways:
|
||||
- If you hold down the Ctrl key and select multiple securities, you
|
||||
can then click on the ladder icon on the left hand side to bring
|
||||
up the selected ladders all at once.
|
||||
- Highlighting multiple securities and right clicking to bring up
|
||||
the menu options to launch a ladder.
|
||||
|
||||
- Launching a Fills window for 1 or many securities
|
||||
|
||||
@@ -158,8 +161,9 @@ insert_anchor_links = "right"
|
||||
- On launching a security browser will appear, allowing you to choose
|
||||
the products you would like to view.
|
||||
|
||||

|
||||

|
||||
|
||||
- From time and sales, you can launch the same items as the market
|
||||
view with the exception of a launching the synthetic window to
|
||||
create a synthetic product
|
||||
create a synthetic product
|
||||
- From the settings icon  you can customize the time and sales modifying grid; the colors of the aggressor, status of trades coming in, as well as font sizes and which columns to see.
|
||||
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 70 KiB |
@@ -59,7 +59,7 @@ insert_anchor_links = "right"
|
||||
functionality to quickly perform various tasks with live orders as
|
||||
well as launch other modules. It appears like the below:
|
||||
|
||||
- 
|
||||

|
||||
|
||||
- Fills -
|
||||
clicking on this will launch a fills window
|
||||
@@ -79,6 +79,18 @@ insert_anchor_links = "right"
|
||||
- Cancel
|
||||
all - clicking on this will cancel all working orders you have the
|
||||
order book
|
||||
-  Recreate - clicking this will recreate already cancelled orders, sending them back to market with the same price and quantity as they were previously at before being cancelled.
|
||||
-  Collapse all - This will collapse all trees inside the order book to show only the top level of the order.
|
||||
-  Expand all - This will expand to show everything inside the order book.
|
||||
-  Settings - This launches the settings for the orderbook.
|
||||
- Furthermore, there is a “floating” or “mini orderbook” feature as well
|
||||
integrated within the ladder itself.
|
||||
- To access this, you need to center click with your mouse wheel over
|
||||
working orders on the ladder.
|
||||
- From here, you can change the price and quantity of any working order
|
||||
within the mini orderbook straight from the window
|
||||
- This looks like below:
|
||||

|
||||
|
||||
## Fills
|
||||
|
||||
@@ -89,6 +101,8 @@ insert_anchor_links = "right"
|
||||
like
|
||||
- All fill windows are brought up as tabs and can be combined with
|
||||
all other windows within BTS
|
||||
- There is also a settings button in the left hand tab which can be
|
||||
launched to customize the fills window
|
||||
|
||||
## P&L
|
||||
|
||||
@@ -104,10 +118,20 @@ insert_anchor_links = "right"
|
||||
other windows within BTS
|
||||
- All columns are sortable and either searchable or can be further
|
||||
filtered by entering in min and max values.
|
||||
- Below is an example P&L window showing a user’s multiple accounts
|
||||
and various positions in each:
|
||||
- There is also a settings button in the left hand tab which can be
|
||||
launched to customize the P&L window
|
||||
- In regards to the PnL specifically, you can choose how you want the
|
||||
PnL calculated based on Last, MidPoint, or Settle price.
|
||||
- This is accomplished on the top bar, which when clicked on, displays
|
||||
the choices for calculation, this appears like below:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
- Below is an example P&L window showing a user’s multiple accounts and
|
||||
various positions in each:
|
||||
|
||||

|
||||
|
||||
|
||||
## Ladder
|
||||
@@ -125,7 +149,7 @@ insert_anchor_links = "right"
|
||||
market with a single click
|
||||
- Below is an example ladder:
|
||||
|
||||

|
||||

|
||||
|
||||
- The ladder consists of 2 major parts, the Ladder itself and the
|
||||
information and control section on the left-hand side
|
||||
@@ -213,37 +237,59 @@ insert_anchor_links = "right"
|
||||
|
||||

|
||||
|
||||
- Lean
|
||||
|
||||
- This is a specialized algo order which allows the user to
|
||||
- Lean
|
||||
- This is a specialized algo order which allows the user to
|
||||
essentially watch the opposite-inside market size and cross when
|
||||
that size goes below the threshold defined.
|
||||
- “Crossing” means to take an order working on the bid and 'cross' by
|
||||
buying into the asks. This is often referred to as “paying up” as
|
||||
well
|
||||
- “Cross” means to take an order working on the bid and 'cross' by
|
||||
buying into the asks. This is often referred to as “paying up” as well
|
||||
|
||||

|
||||

|
||||
|
||||
- Join
|
||||
|
||||
- This is a specialized algo order which allows the user to enter an
|
||||
- Join
|
||||
- This is a specialized algo order which allows the user to enter an
|
||||
order which will watch the opposite-inside market and join when the
|
||||
market flips and has at least threshold size as defined.
|
||||
- “Flip” refers to the event where the best offer becomes the best
|
||||
bid or the market trades away from you, i.e. the line moves up in
|
||||
the GUI. The reverse is true if selling.
|
||||
|
||||

|
||||

|
||||
|
||||
- Top
|
||||
|
||||
- This is a specialized algo order which allows the user to
|
||||
- Top
|
||||
- This is a specialized algo order which allows the user to
|
||||
essentially not trade by themselves
|
||||
- If the order is on the market (current bid price) and the quantity
|
||||
- If the order is on the market (current bid price) and the quantity
|
||||
on that bid price is below the top threshold defined the order will
|
||||
cancel and do nothing else
|
||||

|
||||
|
||||

|
||||
- Offset
|
||||
|
||||
- The goal of the offset order is to send your order X amount of ticks
|
||||
from where you actually place it on the ladder. This is sometimes
|
||||
known as a "fat finger" safety.
|
||||

|
||||
|
||||
- OCO
|
||||
|
||||
- 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.
|
||||
|
||||

|
||||
|
||||
- Cover
|
||||
|
||||
- A Cover order’s purpose is to fire a hedge order (of any algo-type) in response to fills from it’s child order on the opposite side of the market N ticks away.
|
||||
|
||||

|
||||
|
||||
- Trigger
|
||||
|
||||
- The Trigger order is exactly as it sounds, where it will trigger an order based on conditions defined.
|
||||
- This order also has exit strategy built in where a user can define when the trigger order can auto-cancel based on time as well as price conditional.
|
||||
|
||||

|
||||
|
||||
- The middle of the control section is where you can click on
|
||||
predefined values to quickly populate the volume at which you wish
|
||||
@@ -264,20 +310,36 @@ insert_anchor_links = "right"
|
||||
- The bottom section of the control area of a ladder has 4 aspects
|
||||
to it
|
||||
|
||||
- Cancel Buys, which will cancel all buys currently working on the
|
||||
ladder
|
||||
- Cancel Sells, which will cancel all sells currently working on the
|
||||
ladder
|
||||
- Cancel All, which will cancel all orders on the ladder
|
||||
- Settings, which will launch the ladder settings window which
|
||||
appears like below
|
||||
- Cancel Buys, which will cancel all buys currently working on the
|
||||
ladder
|
||||
- Cancel Sells, which will cancel all sells currently working on the
|
||||
ladder
|
||||
- Cancel All, which will cancel all orders on the ladder
|
||||
- Settings, which will launch the ladder settings window which
|
||||
appears like below
|
||||

|
||||
- Fonts and Colors - This is where you can customize the overall main
|
||||
aspects of the ladder.
|
||||
- Market Break Lines - These are where can control the viewing of what
|
||||
has been traded through various shading.
|
||||
- Market Size Display - This is where you can define if you wish
|
||||
to see implieds or not and how exactly you would like to see them.
|
||||
- Volume Display - Where you can choose to show the volume traded at price
|
||||
graph and what details you would like to see.
|
||||
- Size Presets - Here you can define what volume of predefined quick buttons
|
||||
you would like to see or any at all. You can remove these by clicking on the “X” next to the volume number to remove one or many.
|
||||
- Algo Favorites - Here you can set up which “quick button” or user defined
|
||||
algos to see. You can customize the amount of columns to be
|
||||
displayed as well.
|
||||
- Left Panel - This is where you can remove predefined information on the
|
||||
left panel to save room and get rid of data that you might not require
|
||||
to see.
|
||||
|
||||

|
||||
|
||||
- The other, main part of the ladder, is the ladder itself as
|
||||
displayed below
|
||||
|
||||

|
||||

|
||||
|
||||
- There are 5 main columns
|
||||
|
||||
@@ -291,14 +353,10 @@ insert_anchor_links = "right"
|
||||
where the last trade occurred
|
||||
|
||||
- Functionality of the ladder
|
||||
- To enter an order directly by clicking in the ladder, left click on either the bid or ask volume with a volume greater than 0 on the predefined value box on the left-hand section
|
||||
- A badge will be displayed within the orders column of the value you have chosen and the quantity filled as displayed below
|
||||
|
||||
- To enter an order directly by clicking in the ladder, left click on
|
||||
either the bid or ask volume with a volume greater than 0 on the
|
||||
predefined value box on the left-hand section
|
||||
- A badge will be displayed within the orders column of the value you
|
||||
have chosen and the quantity filled as displayed below
|
||||
|
||||

|
||||

|
||||
|
||||
- To change the price level you wish to work the order, you can right-click and hold on the badge to move it up or down the ladder for the
|
||||
desired price
|
||||
@@ -344,3 +402,42 @@ insert_anchor_links = "right"
|
||||
- Please note you can also launch the required parameters for algo or
|
||||
synthetic orders by clicking on the right arrow next to the algo or
|
||||
synthetic
|
||||
|
||||
|
||||
## Algo Studio
|
||||
|
||||
- The BTS Algo Studio was built for users to experience algo composability
|
||||
never seen before in a professional futures trading system.
|
||||
|
||||
- There is no limit for putting both “canned algos” already present in the
|
||||
system as well as user defined algos fed into the system via the API.
|
||||
|
||||
- This is all in place in a very user friendly GUI as shown below:
|
||||
|
||||

|
||||
|
||||
|
||||
- There are 3 main areas of the Algo Studio:
|
||||
- Left Pane (from top to bottom)
|
||||
- Add Algo
|
||||
- Clone Algo
|
||||
- Save Algo
|
||||
- Remove Algo
|
||||
- Middle Pane
|
||||
- This is where your customized algo will be displayed in a running list
|
||||
- Right Pane
|
||||
- This is where your complete customization happens of combining various
|
||||
algos together and saving them to fit your exact needs
|
||||
- On the very top are the name and abbreviation boxes
|
||||
- For the name you can simply name the algo whatever you would like
|
||||
- For the abbreviation section (which is optional), you can put in an
|
||||
abbreviated name for the algo which will be displayed on the Algo
|
||||
buttons on the ladder. (Example - Algo name = PROFIT_OCO_LEAN,
|
||||
Abbrev. = POL)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -4,6 +4,17 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 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,223 +1,197 @@
|
||||
function initToggleMenu() {
|
||||
var $menu = document.querySelector(".menu");
|
||||
var $menuIcon = document.querySelector(".menu-icon");
|
||||
var $page = document.querySelector(".page");
|
||||
$menuIcon.addEventListener("click", function() {
|
||||
$menu.classList.toggle("menu-hidden");
|
||||
$page.classList.toggle("page-without-menu");
|
||||
});
|
||||
}
|
||||
|
||||
function debounce(func, wait) {
|
||||
var timeout;
|
||||
|
||||
return function () {
|
||||
var context = this;
|
||||
var args = arguments;
|
||||
clearTimeout(timeout);
|
||||
|
||||
timeout = setTimeout(function () {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// Taken from mdbook
|
||||
// The strategy is as follows:
|
||||
// First, assign a value to each word in the document:
|
||||
// Words that correspond to search terms (stemmer aware): 40
|
||||
// Normal words: 2
|
||||
// First word in a sentence: 8
|
||||
// Then use a sliding window with a constant number of words and count the
|
||||
// sum of the values of the words within the window. Then use the window that got the
|
||||
// maximum sum. If there are multiple maximas, then get the last one.
|
||||
// Enclose the terms in <b>.
|
||||
function makeTeaser(body, terms) {
|
||||
var TERM_WEIGHT = 40;
|
||||
var NORMAL_WORD_WEIGHT = 2;
|
||||
var FIRST_WORD_WEIGHT = 8;
|
||||
var TEASER_MAX_WORDS = 30;
|
||||
|
||||
var stemmedTerms = terms.map(function (w) {
|
||||
return elasticlunr.stemmer(w.toLowerCase());
|
||||
});
|
||||
var termFound = false;
|
||||
var index = 0;
|
||||
var weighted = []; // contains elements of ["word", weight, index_in_document]
|
||||
|
||||
// split in sentences, then words
|
||||
var sentences = body.toLowerCase().split(". ");
|
||||
|
||||
for (var i in sentences) {
|
||||
var words = sentences[i].split(" ");
|
||||
var value = FIRST_WORD_WEIGHT;
|
||||
|
||||
for (var j in words) {
|
||||
var word = words[j];
|
||||
|
||||
if (word.length > 0) {
|
||||
for (var k in stemmedTerms) {
|
||||
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
|
||||
value = TERM_WEIGHT;
|
||||
termFound = true;
|
||||
}
|
||||
}
|
||||
weighted.push([word, value, index]);
|
||||
value = NORMAL_WORD_WEIGHT;
|
||||
}
|
||||
|
||||
index += word.length;
|
||||
index += 1; // ' ' or '.' if last word in sentence
|
||||
}
|
||||
|
||||
index += 1; // because we split at a two-char boundary '. '
|
||||
}
|
||||
|
||||
if (weighted.length === 0) {
|
||||
return body;
|
||||
}
|
||||
|
||||
var windowWeights = [];
|
||||
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
|
||||
// We add a window with all the weights first
|
||||
var curSum = 0;
|
||||
for (var i = 0; i < windowSize; i++) {
|
||||
curSum += weighted[i][1];
|
||||
}
|
||||
windowWeights.push(curSum);
|
||||
|
||||
for (var i = 0; i < weighted.length - windowSize; i++) {
|
||||
curSum -= weighted[i][1];
|
||||
curSum += weighted[i + windowSize][1];
|
||||
windowWeights.push(curSum);
|
||||
}
|
||||
|
||||
// If we didn't find the term, just pick the first window
|
||||
var maxSumIndex = 0;
|
||||
if (termFound) {
|
||||
var maxFound = 0;
|
||||
// backwards
|
||||
for (var i = windowWeights.length - 1; i >= 0; i--) {
|
||||
if (windowWeights[i] > maxFound) {
|
||||
maxFound = windowWeights[i];
|
||||
maxSumIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var teaser = [];
|
||||
var startIndex = weighted[maxSumIndex][2];
|
||||
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
|
||||
var word = weighted[i];
|
||||
if (startIndex < word[2]) {
|
||||
// missing text from index to start of `word`
|
||||
teaser.push(body.substring(startIndex, word[2]));
|
||||
startIndex = word[2];
|
||||
}
|
||||
|
||||
// add <em/> around search terms
|
||||
if (word[1] === TERM_WEIGHT) {
|
||||
teaser.push("<b>");
|
||||
}
|
||||
startIndex = word[2] + word[0].length;
|
||||
teaser.push(body.substring(word[2], startIndex));
|
||||
|
||||
if (word[1] === TERM_WEIGHT) {
|
||||
teaser.push("</b>");
|
||||
}
|
||||
}
|
||||
teaser.push("…");
|
||||
return teaser.join("");
|
||||
}
|
||||
|
||||
function formatSearchResultItem(item, terms) {
|
||||
var li = document.createElement("li");
|
||||
li.classList.add("search-results__item");
|
||||
li.innerHTML = `<a href="${item.ref}">${item.doc.title}</a>`;
|
||||
li.innerHTML += `<div class="search-results__teaser">${makeTeaser(item.doc.body, terms)}</div>`;
|
||||
return li;
|
||||
}
|
||||
|
||||
// Go from the book view to the search view
|
||||
function toggleSearchMode() {
|
||||
var $bookContent = document.querySelector(".book-content");
|
||||
var $searchContainer = document.querySelector(".search-container");
|
||||
if ($searchContainer.classList.contains("search-container--is-visible")) {
|
||||
$searchContainer.classList.remove("search-container--is-visible");
|
||||
document.body.classList.remove("search-mode");
|
||||
$bookContent.style.display = "block";
|
||||
} else {
|
||||
$searchContainer.classList.add("search-container--is-visible");
|
||||
document.body.classList.add("search-mode");
|
||||
$bookContent.style.display = "none";
|
||||
document.getElementById("search").focus();
|
||||
}
|
||||
}
|
||||
|
||||
function initSearch() {
|
||||
var $searchInput = document.getElementById("search");
|
||||
if (!$searchInput) {
|
||||
return;
|
||||
}
|
||||
var $searchIcon = document.querySelector(".search-icon");
|
||||
$searchIcon.addEventListener("click", toggleSearchMode);
|
||||
|
||||
var $searchResults = document.querySelector(".search-results");
|
||||
var $searchResultsHeader = document.querySelector(".search-results__header");
|
||||
var $searchResultsItems = document.querySelector(".search-results__items");
|
||||
var MAX_ITEMS = 10;
|
||||
|
||||
var options = {
|
||||
bool: "AND",
|
||||
fields: {
|
||||
title: {boost: 2},
|
||||
body: {boost: 1},
|
||||
}
|
||||
};
|
||||
var currentTerm = "";
|
||||
var index = elasticlunr.Index.load(window.searchIndex);
|
||||
|
||||
$searchInput.addEventListener("keyup", debounce(function() {
|
||||
var term = $searchInput.value.trim();
|
||||
if (term === currentTerm || !index) {
|
||||
return;
|
||||
}
|
||||
$searchResults.style.display = term === "" ? "none" : "block";
|
||||
$searchResultsItems.innerHTML = "";
|
||||
if (term === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
var results = index.search(term, options).filter(function (r) {
|
||||
return r.doc.body !== "";
|
||||
});
|
||||
if (results.length === 0) {
|
||||
$searchResultsHeader.innerText = `No search results for '${term}'.`;
|
||||
return;
|
||||
}
|
||||
|
||||
currentTerm = term;
|
||||
$searchResultsHeader.innerText = `${results.length} search results for '${term}':`;
|
||||
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
|
||||
if (!results[i].doc.body) {
|
||||
continue;
|
||||
}
|
||||
// var item = document.createElement("li");
|
||||
// item.innerHTML = formatSearchResultItem(results[i], term.split(" "));
|
||||
console.log(results[i]);
|
||||
$searchResultsItems.appendChild(formatSearchResultItem(results[i], term.split(" ")));
|
||||
}
|
||||
}, 150));
|
||||
}
|
||||
|
||||
if (document.readyState === "complete" ||
|
||||
(document.readyState !== "loading" && !document.documentElement.doScroll)
|
||||
) {
|
||||
initToggleMenu();
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initToggleMenu();
|
||||
initSearch();
|
||||
});
|
||||
}
|
||||
function initToggleMenu() {
|
||||
var $menu = document.querySelector(".menu");
|
||||
var $menuIcon = document.querySelector(".menu-icon");
|
||||
var $page = document.querySelector(".page");
|
||||
$menuIcon.addEventListener("click", function () {
|
||||
$menu.classList.toggle("menu-hidden");
|
||||
$page.classList.toggle("page-without-menu");
|
||||
});
|
||||
}
|
||||
function debounce(func, wait) {
|
||||
var timeout;
|
||||
return function () {
|
||||
var context = this;
|
||||
var args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function () {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
// Taken from mdbook
|
||||
// The strategy is as follows:
|
||||
// First, assign a value to each word in the document:
|
||||
// Words that correspond to search terms (stemmer aware): 40
|
||||
// Normal words: 2
|
||||
// First word in a sentence: 8
|
||||
// Then use a sliding window with a constant number of words and count the
|
||||
// sum of the values of the words within the window. Then use the window that got the
|
||||
// maximum sum. If there are multiple maximas, then get the last one.
|
||||
// Enclose the terms in <b>.
|
||||
function makeTeaser(body, terms) {
|
||||
var TERM_WEIGHT = 40;
|
||||
var NORMAL_WORD_WEIGHT = 2;
|
||||
var FIRST_WORD_WEIGHT = 8;
|
||||
var TEASER_MAX_WORDS = 30;
|
||||
var stemmedTerms = terms.map(function (w) {
|
||||
return elasticlunr.stemmer(w.toLowerCase());
|
||||
});
|
||||
var termFound = false;
|
||||
var index = 0;
|
||||
var weighted = []; // contains elements of ["word", weight, index_in_document]
|
||||
// split in sentences, then words
|
||||
var sentences = body.toLowerCase().split(". ");
|
||||
for (var i in sentences) {
|
||||
var words = sentences[i].split(" ");
|
||||
var value = FIRST_WORD_WEIGHT;
|
||||
for (var j in words) {
|
||||
var word = words[j];
|
||||
if (word.length > 0) {
|
||||
for (var k in stemmedTerms) {
|
||||
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
|
||||
value = TERM_WEIGHT;
|
||||
termFound = true;
|
||||
}
|
||||
}
|
||||
weighted.push([word, value, index]);
|
||||
value = NORMAL_WORD_WEIGHT;
|
||||
}
|
||||
index += word.length;
|
||||
index += 1; // ' ' or '.' if last word in sentence
|
||||
}
|
||||
index += 1; // because we split at a two-char boundary '. '
|
||||
}
|
||||
if (weighted.length === 0) {
|
||||
return body;
|
||||
}
|
||||
var windowWeights = [];
|
||||
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
|
||||
// We add a window with all the weights first
|
||||
var curSum = 0;
|
||||
for (var i = 0; i < windowSize; i++) {
|
||||
curSum += weighted[i][1];
|
||||
}
|
||||
windowWeights.push(curSum);
|
||||
for (var i = 0; i < weighted.length - windowSize; i++) {
|
||||
curSum -= weighted[i][1];
|
||||
curSum += weighted[i + windowSize][1];
|
||||
windowWeights.push(curSum);
|
||||
}
|
||||
// If we didn't find the term, just pick the first window
|
||||
var maxSumIndex = 0;
|
||||
if (termFound) {
|
||||
var maxFound = 0;
|
||||
// backwards
|
||||
for (var i = windowWeights.length - 1; i >= 0; i--) {
|
||||
if (windowWeights[i] > maxFound) {
|
||||
maxFound = windowWeights[i];
|
||||
maxSumIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
var teaser = [];
|
||||
var startIndex = weighted[maxSumIndex][2];
|
||||
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
|
||||
var word = weighted[i];
|
||||
if (startIndex < word[2]) {
|
||||
// missing text from index to start of `word`
|
||||
teaser.push(body.substring(startIndex, word[2]));
|
||||
startIndex = word[2];
|
||||
}
|
||||
// add <em/> around search terms
|
||||
if (word[1] === TERM_WEIGHT) {
|
||||
teaser.push("<b>");
|
||||
}
|
||||
startIndex = word[2] + word[0].length;
|
||||
teaser.push(body.substring(word[2], startIndex));
|
||||
if (word[1] === TERM_WEIGHT) {
|
||||
teaser.push("</b>");
|
||||
}
|
||||
}
|
||||
teaser.push("…");
|
||||
return teaser.join("");
|
||||
}
|
||||
function formatSearchResultItem(item, terms) {
|
||||
var li = document.createElement("li");
|
||||
li.classList.add("search-results__item");
|
||||
li.innerHTML = "<a href=\"" + item.ref + "\">" + item.doc.title + "</a>";
|
||||
li.innerHTML += "<div class=\"search-results__teaser\">" + makeTeaser(item.doc.body, terms) + "</div>";
|
||||
return li;
|
||||
}
|
||||
// Go from the book view to the search view
|
||||
function toggleSearchMode() {
|
||||
var $bookContent = document.querySelector(".book-content");
|
||||
var $searchContainer = document.querySelector(".search-container");
|
||||
if ($searchContainer.classList.contains("search-container--is-visible")) {
|
||||
$searchContainer.classList.remove("search-container--is-visible");
|
||||
document.body.classList.remove("search-mode");
|
||||
$bookContent.style.display = "block";
|
||||
}
|
||||
else {
|
||||
$searchContainer.classList.add("search-container--is-visible");
|
||||
document.body.classList.add("search-mode");
|
||||
$bookContent.style.display = "none";
|
||||
document.getElementById("search").focus();
|
||||
}
|
||||
}
|
||||
function initSearch() {
|
||||
var $searchInput = document.getElementById("search");
|
||||
if (!$searchInput) {
|
||||
return;
|
||||
}
|
||||
var $searchIcon = document.querySelector(".search-icon");
|
||||
$searchIcon.addEventListener("click", toggleSearchMode);
|
||||
var $searchResults = document.querySelector(".search-results");
|
||||
var $searchResultsHeader = document.querySelector(".search-results__header");
|
||||
var $searchResultsItems = document.querySelector(".search-results__items");
|
||||
var MAX_ITEMS = 10;
|
||||
var options = {
|
||||
bool: "AND",
|
||||
fields: {
|
||||
title: { boost: 2 },
|
||||
body: { boost: 1 }
|
||||
}
|
||||
};
|
||||
var currentTerm = "";
|
||||
var index = elasticlunr.Index.load(window.searchIndex);
|
||||
$searchInput.addEventListener("keyup", debounce(function () {
|
||||
var term = $searchInput.value.trim();
|
||||
if (term === currentTerm || !index) {
|
||||
return;
|
||||
}
|
||||
$searchResults.style.display = term === "" ? "none" : "block";
|
||||
$searchResultsItems.innerHTML = "";
|
||||
if (term === "") {
|
||||
return;
|
||||
}
|
||||
var results = index.search(term, options).filter(function (r) {
|
||||
return r.doc.body !== "";
|
||||
});
|
||||
if (results.length === 0) {
|
||||
$searchResultsHeader.innerText = "No search results for '" + term + "'.";
|
||||
return;
|
||||
}
|
||||
currentTerm = term;
|
||||
$searchResultsHeader.innerText = results.length + " search results for '" + term + "':";
|
||||
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
|
||||
if (!results[i].doc.body) {
|
||||
continue;
|
||||
}
|
||||
// var item = document.createElement("li");
|
||||
// item.innerHTML = formatSearchResultItem(results[i], term.split(" "));
|
||||
console.log(results[i]);
|
||||
$searchResultsItems.appendChild(formatSearchResultItem(results[i], term.split(" ")));
|
||||
}
|
||||
}, 150));
|
||||
}
|
||||
if (document.readyState === "complete" ||
|
||||
(document.readyState !== "loading" && !document.documentElement.doScroll)) {
|
||||
initToggleMenu();
|
||||
}
|
||||
else {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initToggleMenu();
|
||||
initSearch();
|
||||
});
|
||||
}
|
||||
@@ -8,5 +8,10 @@ $(document).ready(function() {
|
||||
|
||||
$(".back-button")
|
||||
.html("Go Back")
|
||||
.click(_ => window.history.back());
|
||||
.click(function(_) { window.history.back()});
|
||||
|
||||
if(navigator.userAgent.indexOf("Trident") > -1) {
|
||||
console.log(navigator.userAgent);
|
||||
$(".search-icon").css("display", "none");
|
||||
}
|
||||
});
|
||||
@@ -20,6 +20,8 @@
|
||||
<sys:Double x:Key="RowHeight">16</sys:Double>
|
||||
<sys:Double x:Key="NegativeRowHeight">-16</sys:Double>
|
||||
<sys:Double x:Key="ItemsHostCanBeDisabledCollapsedHeight">15</sys:Double>
|
||||
<sys:Double x:Key="HeaderPanelItemSeparation">6</sys:Double>
|
||||
<Thickness x:Key="Margin.LHeaderPanelItem" Left="{StaticResource HeaderPanelItemSeparation}"/>
|
||||
<Thickness
|
||||
x:Key="GroupLevelMargin"
|
||||
Left="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
|
||||
@@ -100,7 +102,7 @@
|
||||
</usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}" Increment="{Binding tick_increment, FallbackValue={u:Double 1}}"/>
|
||||
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}" Increment="{Binding tick_increment, FallbackValue={u:Double 1}}" FlushTextImmediately="False"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.IntTemplate>
|
||||
@@ -369,16 +371,21 @@
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="7*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="3*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="5*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" IsEnabled="{TemplateBinding IsNew}">Name</TextBlock>
|
||||
<TextBox
|
||||
x:Name="PART_Name_TextBox" Grid.Column="1" Margin="6,0,0,0" HorizontalAlignment="Stretch" IsEnabled="{TemplateBinding IsNew}"
|
||||
x:Name="PART_Name_TextBox"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
MinWidth="80"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="{TemplateBinding IsNew}"
|
||||
Text="{Binding SyntheticName, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"
|
||||
>
|
||||
<u:Behavior.BindingUpdateGroups>
|
||||
@@ -390,18 +397,22 @@
|
||||
</u:Behavior.BindingUpdateGroups>
|
||||
</TextBox>
|
||||
<TextBlock
|
||||
Grid.Column="2" VerticalAlignment="Center" Margin="6,0,0,0" Text="Ticks" IsEnabled="{TemplateBinding IsNew}"
|
||||
Grid.Column="2" VerticalAlignment="Center" Margin="{StaticResource Margin.LHeaderPanelItem}" Text="Ticks" IsEnabled="{TemplateBinding IsNew}"
|
||||
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
|
||||
/>
|
||||
<c:TickFactorsPicker
|
||||
x:Name="PART_TickFactorsPicker" Grid.Column="3" Margin="6,0,0,0" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
|
||||
x:Name="PART_TickFactorsPicker" Grid.Column="3" Margin="{StaticResource Margin.LHeaderPanelItem}" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
|
||||
SelectedTickFactors="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
|
||||
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
|
||||
/>
|
||||
<TextBlock Grid.Column="4" VerticalAlignment="Center" Margin="6,0,0,0">Pricing</TextBlock>
|
||||
<TextBlock Grid.Column="4" VerticalAlignment="Center" Margin="{StaticResource Margin.LHeaderPanelItem}" IsEnabled="{TemplateBinding IsNew}" >Pricing</TextBlock>
|
||||
<ComboBox
|
||||
Grid.Column="5" Margin="6,0,0,0" IsEnabled="False"
|
||||
ItemsSource="{x:Static usp:ExecutionStrategyEditor.PriceCalcItems}" SelectedItem="{Binding PriceCalc, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Grid.Column="5"
|
||||
Margin="{StaticResource Margin.LHeaderPanelItem}"
|
||||
Width="90"
|
||||
IsEnabled="{TemplateBinding IsNew}"
|
||||
ItemsSource="{x:Static usp:ExecutionStrategyEditor.PriceCalcItems}"
|
||||
SelectedItem="{Binding price_calc}"
|
||||
>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -409,7 +420,7 @@
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<c:HelpButton Grid.Column="6" Margin="3,0,0,0"/>
|
||||
<c:HelpButton Grid.Column="8" Margin="3,0,0,0" ManPage="bts_spark/spreader/#creating-a-synthetic" />
|
||||
</Grid>
|
||||
<c:DataGridEx
|
||||
x:Name="PART_DataGrid"
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace mbts.ui.spark {
|
||||
|
||||
#region PriceCalc
|
||||
public static readonly DependencyProperty PriceCalcProperty = DependencyProperty.Register("PriceCalc", typeof(SyntheticContract.Types.PriceCalc), typeof(ExecutionStrategyEditor),
|
||||
new FrameworkPropertyMetadata(SyntheticContract.Types.PriceCalc.Diff, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
|
||||
new FrameworkPropertyMetadata(SyntheticContract.Types.PriceCalc.Standard, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
|
||||
public SyntheticContract.Types.PriceCalc PriceCalc { get => (SyntheticContract.Types.PriceCalc)GetValue(PriceCalcProperty); set => SetValue(PriceCalcProperty, value); }
|
||||
|
||||
public static readonly IEnumerable<SyntheticContract.Types.PriceCalc> PriceCalcItems = EnumHelper.get_values<SyntheticContract.Types.PriceCalc>();
|
||||
@@ -315,7 +315,6 @@ namespace mbts.ui.spark {
|
||||
else if (t == typeof(uint)) return UIntTemplate;
|
||||
else if (t == typeof(QuantityMeasure)) return QuantityMeasureTemplate;
|
||||
else if (t == typeof(LegSpreaderParams.Types.MarketDataType)) return MarketDataTypeTemplate;
|
||||
else if (t == typeof(LegSpreaderParams.Types.LegMarketDataCalculation)) return MarketDataCalculationTemplate;
|
||||
else if (t == typeof(ConditionalQuoting.Types.PriceType)) return ConditionalPriceTypeTemplate;
|
||||
else return null;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using mbts.ui.spark;
|
||||
using LegMarketDataCalculation = bts.spark.LegSpreaderParams.Types.LegMarketDataCalculation;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class MarketDataCalculationModel : RowModelBase<LegMarketDataCalculation> {
|
||||
public override string display_name { get; } = "Market Data Calculation";
|
||||
public override string help_text { get; }
|
||||
public override string man_page { get; } = string.Empty;
|
||||
|
||||
public override LegMarketDataCalculation get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.LegMdCalc;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, LegMarketDataCalculation value) =>
|
||||
leg_tup.leg_params.LegMdCalc = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ namespace mbts.spark {
|
||||
readonly PriceMultiplierModel _price_mult_model;
|
||||
readonly List<IDisposable> _resources = new List<IDisposable>();
|
||||
readonly Property<bool> _has_working_orders, _can_add_leg;
|
||||
|
||||
Security _synthetic;
|
||||
bool _set_name_automatically, _set_ticks_automatically, _set_quantities_automatically, _set_price_mults_automatically, _is_setting_quantities, _is_setting_price_mults;
|
||||
TickFactors _tick_factors;
|
||||
@@ -207,7 +208,6 @@ namespace mbts.spark {
|
||||
//new MinLeanQtyModel(), //TAHA_SPREADER
|
||||
//new QuoteHiddenModel(),
|
||||
new MarketDataTypeModel(),
|
||||
new MarketDataCalculationModel(),
|
||||
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
|
||||
quote_throttle_inside_ticks,
|
||||
quote_throttle_outside_ticks,
|
||||
@@ -291,7 +291,9 @@ namespace mbts.spark {
|
||||
public ExecutionStrategyImmutable get_strategy() {
|
||||
var mutable_strategy = _strategy.CloneToMutable();
|
||||
mutable_strategy.LegParamsMap.Clear();
|
||||
foreach (var lt in leg_tuples) mutable_strategy.LegParamsMap.Add(lt.leg.Security.Id, lt.leg_params);
|
||||
foreach (var lt in leg_tuples) {
|
||||
mutable_strategy.LegParamsMap.Add(lt.leg.Security.Id, lt.leg_params);
|
||||
}
|
||||
return new ExecutionStrategyImmutable(mutable_strategy);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace bts.shell {
|
||||
void _on_orders(TableUpdate<(SecurityImmutable?,OrderRecordImmutable),uint> update) {
|
||||
// must update notification_service_data_cache first, some of the predicate/triggers (esp. ToastNotifications) will use the notification_service_data_cache to lookup order-history
|
||||
_data_cache.update(update.as_iincremental());
|
||||
foreach (var o in update.added)
|
||||
foreach (var o in update.added.Concat(update.changed))
|
||||
_order_notifications.process_item(o);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,15 +79,27 @@ namespace mbts.spark {
|
||||
set_error(nameof(template), "Waiting for security and/or strategy.", true);
|
||||
else clear_error(nameof(template));
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
protected override void on_model_internal() => _reset_strategy_sub();
|
||||
protected override void on_security_internal() => _reset_strategy_sub();
|
||||
=======
|
||||
|
||||
protected override void on_security_md_internal() => _reset_strategy_sub();
|
||||
protected override void on_model_internal() => _reset_strategy_sub();
|
||||
>>>>>>> master
|
||||
|
||||
void _reset_strategy_sub() {
|
||||
Disposable.dispose(ref _strategy_sub);
|
||||
if (model != null) {
|
||||
<<<<<<< HEAD
|
||||
if (security.HasValue) {
|
||||
_strategy_sub = model.spreader.security_strategy(security.Value.Id)
|
||||
.Select(strategy => new SpreaderDefi?(model.factory.spreader_orderi(security.Value, strategy)))
|
||||
=======
|
||||
if (security_md.HasValue) {
|
||||
_strategy_sub = model.spreader.security_strategy(security_md.Value.security.Id)
|
||||
.Select(strategy => new SpreaderDefi?(model.factory.spreader_orderi(security_md.Value.security, strategy)))
|
||||
>>>>>>> master
|
||||
.OnDispatcher()
|
||||
.Subscribe(s => {
|
||||
is_loaded = true;
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace mbts {
|
||||
|
||||
#region Public Extensions (is_valid, format, parse, tick_up, tick_down)
|
||||
public static bool is_valid(this TickFactors tick) =>
|
||||
tick.tick_unit > 0 && tick.tick_unit <= (tick.tick_base * 0.5);
|
||||
(tick.tick_unit > 0 && tick.tick_unit <= (tick.tick_base * 0.5)) ||
|
||||
tick.tick_unit == tick.tick_base;
|
||||
|
||||
public static bool is_valid(this RegionalTickFactors regional_tick) =>
|
||||
regional_tick.low_tick.is_valid() && regional_tick.high_tick.is_valid();
|
||||
@@ -20,6 +21,9 @@ namespace mbts {
|
||||
|
||||
public static double reduced_base(this TickFactors tick) => factor_out(tick.tick_base, 10);
|
||||
|
||||
public static string format_without_decimal_part(this TickFactors tick, double value) =>
|
||||
format(tick, value, double.NaN, false, _nan_text, true);
|
||||
|
||||
public static string format(this TickFactors tick, double value) =>
|
||||
format(tick, value, double.NaN, false, _nan_text);
|
||||
|
||||
@@ -32,13 +36,16 @@ namespace mbts {
|
||||
public static string format(this TickFactors tick, double value, double display_multiplier, bool show_commas) =>
|
||||
format(tick, value, display_multiplier, show_commas, _nan_text);
|
||||
|
||||
public static string format(this TickFactors tick, double value, double display_multiplier, bool show_commas, string nan_text) {
|
||||
public static string format(this TickFactors tick, double value, double display_multiplier, bool show_commas, string nan_text, bool hide_decimal_part = false) {
|
||||
if (double.IsNaN(value)) return nan_text;
|
||||
if (double.IsInfinity(value)) return value.ToString();
|
||||
if (!tick.is_valid()) return _format_no_tick(value, display_multiplier, show_commas);
|
||||
return _format_tick(tick, double.IsNaN(display_multiplier) ? value : value * display_multiplier, show_commas);
|
||||
return _format_tick(tick, double.IsNaN(display_multiplier) ? value : value * display_multiplier, show_commas, hide_decimal_part);
|
||||
}
|
||||
|
||||
public static string format_without_decimal_part(this RegionalTickFactors regional_tick, double value) =>
|
||||
format_without_decimal_part(value <= regional_tick.low_tick_inclusive_end ? regional_tick.low_tick : regional_tick.high_tick, value);
|
||||
|
||||
public static string format(this RegionalTickFactors regional_tick, double value) =>
|
||||
format(value <= regional_tick.low_tick_inclusive_end ? regional_tick.low_tick : regional_tick.high_tick, value);
|
||||
|
||||
@@ -193,7 +200,12 @@ namespace mbts {
|
||||
if (DoubleHelper.IsNaNOrInfinity(input)) return input;
|
||||
return input <= regional_tick.low_tick_inclusive_end ? regional_tick.low_tick.round(input) : regional_tick.high_tick.round(input);
|
||||
}
|
||||
public static int display_digits(this TickFactors tick) => display_digits(tick.tick_base);
|
||||
public static int display_digits(this TickFactors tick) {
|
||||
if (tick.tick_unit == tick.tick_base) {
|
||||
return (int)Math.Floor(Math.Log10(tick.tick_base));
|
||||
}
|
||||
return display_digits(tick.tick_base);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static int display_digits(double tick_base) {
|
||||
@@ -265,15 +277,21 @@ namespace mbts {
|
||||
return integer_part + numerator_part / tick.tick_base;
|
||||
}
|
||||
|
||||
private static string _format_tick(TickFactors tick, double value, bool show_commas) {
|
||||
if (value < 0) return "-" + _format_tick(tick, Math.Abs(value), show_commas);
|
||||
private static string _format_tick(TickFactors tick, double value, bool show_commas, bool hide_decimal_part) {
|
||||
if (value < 0) return "-" + _format_tick(tick, Math.Abs(value), show_commas, hide_decimal_part);
|
||||
try {
|
||||
checked {
|
||||
int integer_part = (int)Math.Floor(value);
|
||||
double numerator_dec = value - integer_part;
|
||||
double unit_frac = tick.tick_unit / tick.tick_base;
|
||||
int n_units = (int)Math.Round(numerator_dec / unit_frac);
|
||||
if (n_units == tick.tick_base) return (integer_part + 1).ToString() + tick.separator() + new string('0', display_digits(tick.tick_base));
|
||||
if (n_units == tick.tick_base) {
|
||||
if (hide_decimal_part) {
|
||||
return integer_part.ToString();
|
||||
} else {
|
||||
return (integer_part + 1).ToString() + tick.separator() + new string('0', display_digits(tick.tick_base));
|
||||
}
|
||||
}
|
||||
double decimal_part = n_units * tick.tick_unit;
|
||||
if (decimal_part == tick.tick_base) {
|
||||
++integer_part;
|
||||
@@ -284,7 +302,11 @@ namespace mbts {
|
||||
decimal_part = Math.Floor(decimal_part);
|
||||
}
|
||||
string integer_part_text = show_commas ? integer_part.ToString("#,0") : integer_part.ToString();
|
||||
return integer_part_text + tick.separator() + decimal_part.ToString(new string('0', display_digits(tick.tick_base)));
|
||||
if (hide_decimal_part) {
|
||||
return integer_part_text;
|
||||
} else {
|
||||
return integer_part_text + tick.separator() + decimal_part.ToString(new string('0', display_digits(tick.tick_base)));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OverflowException ex) {
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace mbts.ui.controls {
|
||||
}));
|
||||
|
||||
static IEnumerable<double> _get_dbased_tick_unit_presets(int log10) =>
|
||||
Enumerable.Range(0, log10).SelectMany(_coins);
|
||||
Enumerable.Range(0, log10).SelectMany(_coins).Concat(new[] { Math.Pow(10, log10) });
|
||||
|
||||
static IEnumerable<double> _coins(int pow10) {
|
||||
double d = Math.Pow(10, pow10);
|
||||
|
||||
@@ -9,10 +9,7 @@ namespace mbts.ui.converters {
|
||||
class SyntheticPriceCalcDisplayTextConverter : IValueConverter {
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
if (value is SyntheticContract.Types.PriceCalc price_calc) {
|
||||
switch (price_calc) {
|
||||
case SyntheticContract.Types.PriceCalc.Diff: return "Standard";
|
||||
default: return DisplayNameHelper.camel_to_title_case(value.ToString());
|
||||
}
|
||||
return DisplayNameHelper.camel_to_title_case(value.ToString());
|
||||
} else return value != null ? value.ToString() : string.Empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,13 @@ namespace mbts.spark {
|
||||
public static double ticksize(this ContractSpecificationImmutable spec, double price) =>
|
||||
Math.Abs(price) > spec.PriceBreak ? spec.HighTick : spec.LowTick;
|
||||
|
||||
public static string format(this ContractSpecificationImmutable spec, double price, double with_regard_to_price, bool in_tick_terms) {
|
||||
double net_change = price - with_regard_to_price;
|
||||
return (in_tick_terms) ?
|
||||
TickStore.get(spec).format_without_decimal_part((net_change) / spec.ticksize(net_change)) :
|
||||
TickStore.get(spec).format(net_change);
|
||||
}
|
||||
|
||||
public static string format(this ContractSpecificationImmutable spec, double price) =>
|
||||
TickStore.get(spec).format(price);
|
||||
|
||||
@@ -289,7 +296,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
static bool _check_tick_size(double tick_size) =>
|
||||
tick_size > 0 && tick_size <= 0.5;
|
||||
(tick_size > 0 && tick_size <= 0.5) || tick_size == 1;
|
||||
|
||||
struct SpecKey : IEquatable<SpecKey> {
|
||||
public readonly uint denominator, display_digits;
|
||||
|
||||
@@ -6,6 +6,7 @@ using mbts.co2;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.ui.utility.text;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
@@ -355,14 +356,27 @@ namespace mbts.spark {
|
||||
case ColumnType.Order: _ordershost.set_col_width(column.settings.width); break;
|
||||
case ColumnType.Bid: render_market_col(_bids); break;
|
||||
case ColumnType.Price:
|
||||
LadderRender.render_prices(
|
||||
column.visual_tup.visual,
|
||||
column.visual_tup.glyph_context,
|
||||
_row_count,
|
||||
_settings.row_height,
|
||||
_price_list,
|
||||
column.settings,
|
||||
security.ContractSpec);
|
||||
if (_settings.net_change != LadderSettings.NetChangeTerms.None) {
|
||||
LadderRender.render_prices(
|
||||
column.visual_tup.visual,
|
||||
column.visual_tup.glyph_context,
|
||||
_row_count,
|
||||
_settings.row_height,
|
||||
_price_list,
|
||||
_vm.md.settlement_price,
|
||||
_settings.net_change == LadderSettings.NetChangeTerms.Ticks,
|
||||
column.settings,
|
||||
security.ContractSpec);
|
||||
} else {
|
||||
LadderRender.render_prices(
|
||||
column.visual_tup.visual,
|
||||
column.visual_tup.glyph_context,
|
||||
_row_count,
|
||||
_settings.row_height,
|
||||
_price_list,
|
||||
column.settings,
|
||||
security.ContractSpec);
|
||||
}
|
||||
break;
|
||||
case ColumnType.Ask: render_market_col(_asks); break;
|
||||
case ColumnType.Volume:
|
||||
@@ -556,7 +570,9 @@ namespace mbts.spark {
|
||||
|
||||
void _refresh_prices() {
|
||||
var prev_price_list = _price_list;
|
||||
_price_list = LadderRender.render_prices(_price_vis_tup.visual, _price_vis_tup.glyph_context, _mkt_break_lines.origin, _row_count, _settings.row_height, _settings.price_column, security.ContractSpec);
|
||||
_price_list = (!(_settings.net_change == LadderSettings.NetChangeTerms.None)) ?
|
||||
LadderRender.render_prices(_price_vis_tup.visual, _price_vis_tup.glyph_context, _mkt_break_lines.origin, _vm.md.settlement_price, _settings.net_change == LadderSettings.NetChangeTerms.Ticks, _row_count, _settings.row_height, _settings.price_column, security.ContractSpec) :
|
||||
LadderRender.render_prices(_price_vis_tup.visual, _price_vis_tup.glyph_context, _mkt_break_lines.origin, _row_count, _settings.row_height, _settings.price_column, security.ContractSpec);
|
||||
_ordershost.update_settings(_price_list, _price_comparer, _settings.row_height, _settings.orders_column);
|
||||
if (prev_price_list != null) {
|
||||
// maintain previous visible price range if possible
|
||||
|
||||
@@ -299,12 +299,15 @@ namespace mbts.spark {
|
||||
order_entry_panel_initial_width = _default_order_entry_panel_width,
|
||||
zoom_slider_visible = false,
|
||||
zoom_factor = _default_zoom_factor,
|
||||
net_change = NetChangeTerms.None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum BadgeStyle { Normal, Alternate }
|
||||
|
||||
public enum NetChangeTerms { None, Dollars, Ticks }
|
||||
|
||||
[ProtoMember(1)]
|
||||
public double row_height { get; set; }
|
||||
[ProtoMember(2)]
|
||||
@@ -411,6 +414,9 @@ namespace mbts.spark {
|
||||
[ProtoMember(47)]
|
||||
public LadderFavoriteAlgosSettings favorite_algos { get; set; }
|
||||
|
||||
[ProtoMember(48)]
|
||||
public NetChangeTerms net_change { get; set; }
|
||||
|
||||
public bool Equals(LadderSettings other) =>
|
||||
other != null &&
|
||||
row_height == other.row_height &&
|
||||
@@ -456,7 +462,8 @@ namespace mbts.spark {
|
||||
zoom_slider_visible == other.zoom_slider_visible &&
|
||||
zoom_factor == other.zoom_factor &&
|
||||
use_defaults == other.use_defaults &&
|
||||
favorite_algos == other.favorite_algos;
|
||||
favorite_algos == other.favorite_algos &&
|
||||
net_change == other.net_change;
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as LadderSettings);
|
||||
|
||||
@@ -505,7 +512,8 @@ namespace mbts.spark {
|
||||
zoom_slider_visible.GetHashCode(),
|
||||
zoom_factor.GetHashCode(),
|
||||
use_defaults.GetHashCode(),
|
||||
favorite_algos.GetHashCode());
|
||||
favorite_algos.GetHashCode(),
|
||||
net_change.GetHashCode());
|
||||
|
||||
public struct WindowSpecifics {
|
||||
public readonly double order_entry_panel_width, zoom_factor;
|
||||
|
||||
@@ -56,6 +56,10 @@ namespace mbts.spark {
|
||||
_cancel_buys_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Buys"),
|
||||
_cancel_sells_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Sells"),
|
||||
_cancel_all_visibility = new OrderEntryPanelItemVisibilityVM("Cancel All");
|
||||
readonly Property<bool>
|
||||
_show_in_net_change,
|
||||
_show_in_net_change_dollars,
|
||||
_show_in_net_change_ticks;
|
||||
|
||||
public LadderSettingsVM(OrdersModel model, bool is_synthetic, Func<double> get_left_panel_width, Func<double> get_zoom_factor) {
|
||||
this.is_synthetic = is_synthetic;
|
||||
@@ -67,6 +71,10 @@ namespace mbts.spark {
|
||||
_alternate_order_size = backing<uint>(nameof(alternate_order_size));
|
||||
_get_order_entry_panel_width = get_left_panel_width;
|
||||
_get_zoom_factor = get_zoom_factor;
|
||||
_show_in_net_change = backing<bool>(nameof(show_in_net_change));
|
||||
_show_in_net_change_dollars = backing<bool>(nameof(show_in_net_change_dollars));
|
||||
_show_in_net_change_ticks = backing<bool>(nameof(show_in_net_change_ticks));
|
||||
|
||||
// be sure to initialize backings before this measure_row_height call
|
||||
_row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result()));
|
||||
_orders.font_size_changed += on_font_size_changed;
|
||||
@@ -74,6 +82,7 @@ namespace mbts.spark {
|
||||
_price.font_size_changed += on_font_size_changed;
|
||||
_ask.font_size_changed += on_font_size_changed;
|
||||
_last.font_size_changed += on_font_size_changed;
|
||||
|
||||
void on_font_size_changed(double oldval, double newval) => _row_height.refresh();
|
||||
}
|
||||
|
||||
@@ -159,6 +168,32 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public bool show_in_net_change {
|
||||
get => _show_in_net_change.Value;
|
||||
set {
|
||||
// when unchecking, clear out dollars and ticks
|
||||
if (!value) {
|
||||
show_in_net_change_dollars = false;
|
||||
show_in_net_change_ticks = false;
|
||||
}
|
||||
// when checking, by defalt check dollars
|
||||
if (value) {
|
||||
show_in_net_change_dollars = true;
|
||||
}
|
||||
_show_in_net_change.Value = value;
|
||||
}
|
||||
}
|
||||
public bool show_in_net_change_dollars {
|
||||
get => _show_in_net_change_dollars.Value;
|
||||
set => _show_in_net_change_dollars.Value = value;
|
||||
}
|
||||
|
||||
public bool show_in_net_change_ticks {
|
||||
get => _show_in_net_change_ticks.Value;
|
||||
set => _show_in_net_change_ticks.Value = value;
|
||||
}
|
||||
|
||||
|
||||
public string key => GridOrLadderSettingsSelectorKeys.Ladder;
|
||||
|
||||
public void load(LadderSettings settings) {
|
||||
@@ -201,6 +236,9 @@ namespace mbts.spark {
|
||||
// because it could have changed from the UI
|
||||
// there might be a better way of doing this, like how
|
||||
// does the widths of the ladder grid column pull this off?
|
||||
show_in_net_change = settings.net_change != LadderSettings.NetChangeTerms.None;
|
||||
show_in_net_change_dollars = settings.net_change == LadderSettings.NetChangeTerms.Dollars;
|
||||
show_in_net_change_ticks = settings.net_change == LadderSettings.NetChangeTerms.Ticks;
|
||||
}
|
||||
|
||||
public LadderSettings get_result() {
|
||||
@@ -242,7 +280,19 @@ namespace mbts.spark {
|
||||
clone.order_entry_panel_initial_width = _get_order_entry_panel_width();
|
||||
clone.zoom_slider_visible = _zoom_slider_visbility.show;
|
||||
clone.zoom_factor = _get_zoom_factor();
|
||||
clone.net_change = _create_net_change_terms(show_in_net_change_dollars, show_in_net_change_ticks);
|
||||
return clone;
|
||||
|
||||
LadderSettings.NetChangeTerms _create_net_change_terms(bool dollars, bool ticks) {
|
||||
if (dollars) {
|
||||
return LadderSettings.NetChangeTerms.Dollars;
|
||||
}
|
||||
if (ticks) {
|
||||
return LadderSettings.NetChangeTerms.Ticks;
|
||||
}
|
||||
return LadderSettings.NetChangeTerms.None;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Dispose() => favorite_algos_vm.Dispose();
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" VerticalAlignment="Center">Alternate Order Qty</TextBlock>
|
||||
<TextBox Grid.Row="2" Grid.Column="3" Text="{Binding alternate_order_size}" u:TextBoxBehavior.InputFilter="{u:RegexInputFilter Regex={x:Static Member=Integer, MemberType={u:Nested txt:TextUtils.RegexFilters}}}"/>
|
||||
|
||||
<TabControl Grid.Row="4" Grid.ColumnSpan="5" Width="543" Height="239">
|
||||
<TabControl Grid.Row="4" Grid.ColumnSpan="5" Width="543" Height="275">
|
||||
<TabControl.ItemContainerStyle>
|
||||
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
|
||||
<Setter Property="VerticalAlignment" Value="Stretch"/>
|
||||
@@ -353,13 +353,14 @@
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<!-- Market Size Tab -->
|
||||
<TabItem Header="Market Size Display">
|
||||
<!-- Market Price & Size Tab -->
|
||||
<TabItem Header="Market Price and Size Display">
|
||||
<Grid DataContext="{Binding size_display}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -381,6 +382,31 @@
|
||||
FontStyle="Italic"
|
||||
Foreground="{Binding can_choose_alternate_implied_format, Converter={StaticResource IsEnabledForegroundConverter}}"
|
||||
/>
|
||||
<GroupBox Grid.Row="3" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,10,0,0" Grid.ColumnSpan="3">
|
||||
<GroupBox.Header>
|
||||
<CheckBox
|
||||
x:Name="show_name_change_chk"
|
||||
IsChecked="{Binding DataContext.show_in_net_change, RelativeSource={RelativeSource AncestorType=usp:LadderSettingsView}}">
|
||||
Show Price in Net Change
|
||||
</CheckBox>
|
||||
</GroupBox.Header>
|
||||
<StackPanel Margin="10,5,0,0">
|
||||
<RadioButton
|
||||
Content="Price"
|
||||
GroupName="FormatTerms"
|
||||
Style="{x:Null}"
|
||||
IsEnabled="{Binding ElementName=show_name_change_chk, Path=IsChecked}"
|
||||
IsChecked="{Binding DataContext.show_in_net_change_dollars, RelativeSource={RelativeSource AncestorType=usp:LadderSettingsView}}"
|
||||
/>
|
||||
<RadioButton
|
||||
Content="Ticks"
|
||||
GroupName="FormatTerms"
|
||||
Style="{x:Null}"
|
||||
IsEnabled="{Binding ElementName=show_name_change_chk, Path=IsChecked}"
|
||||
IsChecked="{Binding DataContext.show_in_net_change_ticks, RelativeSource={RelativeSource AncestorType=usp:LadderSettingsView}}"
|
||||
/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
<ComboBox
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace mbts.spark {
|
||||
readonly Property<Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
|
||||
IWindowSettingsManager<LadderSettings> _settings_manager;
|
||||
ulong _misc_time = 0;
|
||||
ulong _last_trade_time = 0;
|
||||
IEqualityComparer<double> _equality;
|
||||
Dictionary<double, LadderOrderVM> _slot_price_vs_orders;
|
||||
|
||||
@@ -355,25 +356,24 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
void _update_header() {
|
||||
if (_misc_time != _model.md.misc_timestamp) {
|
||||
var spec = security.ContractSpec;
|
||||
bool last_updated = md.get_last_trade(ref _last_trade_time, out NativePriceSize last, out DateTime time, out ulong volume);
|
||||
if (_misc_time != _model.md.misc_timestamp || last_updated) {
|
||||
_high.set(md.session_high);
|
||||
_low.set(md.session_low);
|
||||
_open.set(md.open_price);
|
||||
_settlement.set(md.settlement_price);
|
||||
ulong unused = 0;
|
||||
md.get_last_trade(ref unused, out NativePriceSize last, out DateTime time, out ulong volume);
|
||||
if (!double.IsNaN(last.price)) {
|
||||
_change.set(last.price - md.settlement_price);
|
||||
} else {
|
||||
unused = 0;
|
||||
ulong unused = 0;
|
||||
_model.md.get_bbo(ref unused, out NativePriceSize bid, out NativePriceSize ask);
|
||||
double mid = TickUtils.mid_tick(spec, bid.price, ask.price);
|
||||
double mid = TickUtils.mid_tick(security.ContractSpec, bid.price, ask.price);
|
||||
_change.set(mid - md.settlement_price);
|
||||
}
|
||||
_volume.set(volume);
|
||||
_misc_time = _model.md.misc_timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void _show_book() =>
|
||||
|
||||
@@ -73,6 +73,12 @@ namespace mbts.spark {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static GlyphRun[] format_prices(double[] prices, double reference_price, bool format_in_tick_terms, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, ContractSpecificationImmutable spec) {
|
||||
var result = new GlyphRun[prices.Length];
|
||||
Parallel.For(0, prices.Length, i =>
|
||||
result[i] = format_text(spec.format(prices[i], reference_price, format_in_tick_terms), i, row_height, settings, glyph_context));
|
||||
return result;
|
||||
}
|
||||
public static GlyphRun[] format_prices(double[] prices, double row_height, LadderColumnSettings settings, GlyphContext glyph_context, ContractSpecificationImmutable spec) {
|
||||
var result = new GlyphRun[prices.Length];
|
||||
Parallel.For(0, prices.Length, i =>
|
||||
@@ -80,6 +86,13 @@ namespace mbts.spark {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double[] render_prices(DrawingVisual price, GlyphContext glyph_context, double mid, double wrt_price, bool format_in_tick_terms, int row_count, double row_height,
|
||||
LadderColumnSettings settings, ContractSpecificationImmutable spec) {
|
||||
var dprices = gen_prices(mid, row_count, spec);
|
||||
render_prices(price, glyph_context, row_count, row_height, dprices, wrt_price, format_in_tick_terms, settings, spec);
|
||||
return dprices;
|
||||
}
|
||||
|
||||
public static double[] render_prices(DrawingVisual price, GlyphContext glyph_context, double mid, int row_count, double row_height,
|
||||
LadderColumnSettings settings, ContractSpecificationImmutable spec) {
|
||||
var dprices = gen_prices(mid, row_count, spec);
|
||||
@@ -87,6 +100,18 @@ namespace mbts.spark {
|
||||
return dprices;
|
||||
}
|
||||
|
||||
public static void render_prices(DrawingVisual price, GlyphContext glyph_context, int row_count, double row_height, double[] dprices, double wrt_price, bool format_in_tick_terms, LadderColumnSettings settings, ContractSpecificationImmutable spec) {
|
||||
var foreground = settings.foreground.rgb_brush();
|
||||
//var background = new SolidColorBrush(settings.background.rgb_color());
|
||||
var fprices = format_prices(dprices, wrt_price, format_in_tick_terms, row_height, settings, glyph_context, spec);
|
||||
using (var dc = price.RenderOpen())
|
||||
using (_draw_with_clip(dc, settings, row_height, row_count)) {
|
||||
//dc.DrawRectangle(background, null, new Rect(0, 0, settings.width, row_count * row_height));
|
||||
for (int x = 0; x < row_count; ++x)
|
||||
dc.DrawGlyphRun(foreground, fprices[x]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void render_prices(DrawingVisual price, GlyphContext glyph_context, int row_count, double row_height, double[] dprices, LadderColumnSettings settings, ContractSpecificationImmutable spec) {
|
||||
var foreground = settings.foreground.rgb_brush();
|
||||
//var background = new SolidColorBrush(settings.background.rgb_color());
|
||||
|
||||
@@ -949,10 +949,10 @@ namespace mbts.spark.utilities {
|
||||
public static IcebergOrderDefinition IcebergOrderDefinition() => new IcebergOrderDefinition { Child = CustomOrderDefinition(), ShowQuantity = 1 };
|
||||
public static TimeBoxOrderDefinition TimeBoxOrderDefinition() => new TimeBoxOrderDefinition { Child = CustomOrderDefinition(), Start = Timestamp.FromDateTime(DateTime.UtcNow), Stop = Timestamp.FromDateTime(DateTime.UtcNow) };
|
||||
public static TickOrderDefinition TickOrderDefinition() => new TickOrderDefinition { Child = CustomOrderDefinition(), CrossThreshold = QuantityMeasure };
|
||||
public static LeanOrderDefinition LeanOrderDefinition() => new LeanOrderDefinition { Child = CustomOrderDefinition(), CrossThreshold = QuantityMeasure };
|
||||
public static LeanOrderDefinition LeanOrderDefinition() => new LeanOrderDefinition { Child = CustomOrderDefinition(), CrossThreshold = QuantityMeasure, MaxChaseTicks = 2 };
|
||||
public static JoinOrderDefinition JoinOrderDefinition() => new JoinOrderDefinition { Child = CustomOrderDefinition(), JoinThreshold = QuantityMeasure };
|
||||
public static TopOrderDefinition TopOrderDefinition() => new TopOrderDefinition { Child = CustomOrderDefinition(), TopThreshold = QuantityMeasure };
|
||||
public static CoverOrderDefinition CoverOrderDefinition() => new CoverOrderDefinition { Child = CustomOrderDefinition(), Hedge = CustomOrderDefinition() };
|
||||
public static CoverOrderDefinition CoverOrderDefinition() => new CoverOrderDefinition { Child = CustomOrderDefinition(), Hedge = CustomOrderDefinition(), HedgeTicks = 1 };
|
||||
public static OCOOrderDefinition OCOOrderDefinition() => new OCOOrderDefinition { Child = CustomOrderDefinition(), Other = CustomOrderDefinition(), OtherSide = RelativeSide.Same };
|
||||
public static PricerOrderDefinition PricerOrderDefinition() => new PricerOrderDefinition { Child = CustomOrderDefinition() };
|
||||
public static OffsetOrderDefinition OffsetOrderDefinition() => new OffsetOrderDefinition { Child = CustomOrderDefinition(), };
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace bts {
|
||||
Currency = Currency.Usd,
|
||||
Synthetic = new SyntheticContract {
|
||||
Name = "Synthetic1",
|
||||
PriceCalc = SyntheticContract.Types.PriceCalc.Ratio,
|
||||
PriceCalc = SyntheticContract.Types.PriceCalc.Standard,
|
||||
Legs = {
|
||||
new ContractLeg {
|
||||
Security = new Security {
|
||||
|
||||
@@ -282,19 +282,19 @@ group by
|
||||
drop view if exists event_log_aux;
|
||||
create view event_log_aux as
|
||||
select
|
||||
log_archive.*,
|
||||
log_view.*,
|
||||
event_type,
|
||||
event_id,
|
||||
event_archive.duration event_duration,
|
||||
event_archive.hts_start hts_start,
|
||||
event_archive.start event_start,
|
||||
event_archive.stop event_stop
|
||||
event_view.duration event_duration,
|
||||
event_view.hts_start hts_start,
|
||||
event_view.start event_start,
|
||||
event_view.stop event_stop
|
||||
from
|
||||
log_archive,
|
||||
event_archive
|
||||
log_view,
|
||||
event_view
|
||||
where
|
||||
log_archive.session_start = event_archive.session_start and
|
||||
log_archive.ts >= event_archive.start and log_archive.ts <= event_archive.stop;
|
||||
log_view.session_start = event_view.session_start and
|
||||
log_view.ts >= event_view.start and log_view.ts <= event_view.stop;
|
||||
|
||||
begin transaction;
|
||||
insert into hedge_trace_archive select * from hedge_trace_view;
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
|
||||
protected:
|
||||
Authenticated{{name}}Base(bts::api::ServiceContext& context, const std::string& qualifier="");
|
||||
Authenticated{{name}}Base(const Authenticated{{name}}Base&) = delete;
|
||||
|
||||
const char* name() const override;
|
||||
|
||||
@@ -72,6 +73,7 @@ private:
|
||||
class {{name}}Base : public Authenticated{{name}}Base {
|
||||
protected:
|
||||
{{name}}Base(bts::api::ServiceContext& context, const std::string& qualifier="") : Authenticated{{name}}Base(context, qualifier) {}
|
||||
{{name}}Base(const {{name}}Base&)=delete;
|
||||
public:
|
||||
{{#each method}}
|
||||
{{#unless server_streaming}}
|
||||
|
||||
@@ -129,6 +129,7 @@ message RecordOptions {
|
||||
// optional string output_path = 2 [default = "/home/bts/md/today"];
|
||||
optional bool enable_depth = 3 [default = false];
|
||||
optional bool enable_future_depth = 4 [default = true];
|
||||
optional bool record_sim = 5 [default=false];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -135,9 +135,9 @@ message SyntheticContract {
|
||||
repeated ContractLeg legs = 20;
|
||||
PriceCalc price_calc = 30;
|
||||
enum PriceCalc {
|
||||
diff = 0;
|
||||
net_change = 1;
|
||||
ratio = 2;
|
||||
PRICECALC_STANDARD = 0;
|
||||
PRICECALC_NET_CHANGE = 1;
|
||||
// RATIO = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class IntPrice {
|
||||
int64_t _mantissa : 56;
|
||||
int8_t _exponent : 8;
|
||||
#endif
|
||||
|
||||
public:
|
||||
IntPrice() : _mantissa(0), _exponent(127) {/*nan*/}
|
||||
IntPrice(int64_t mantissa, int8_t exponent) : _mantissa(mantissa), _exponent(exponent) {}
|
||||
@@ -57,49 +58,24 @@ class IntPrice {
|
||||
}
|
||||
}
|
||||
IntPrice operator - (const IntPrice& o) const {
|
||||
if(_exponent==o._exponent) {
|
||||
return IntPrice(_mantissa-o.mantissa(), _exponent);
|
||||
}
|
||||
else {
|
||||
// not sure there's ever a cause for a positive exponent unless we get into scientific computing
|
||||
if (is_nan() || o.is_nan()) return IntPrice();
|
||||
bts_assert_d(o.exponent() <= 0 && _exponent <= 0);
|
||||
if (o.exponent() < _exponent) {
|
||||
int8_t delta = _exponent - o.exponent();
|
||||
int64_t m = _mantissa * pow10(delta);
|
||||
return { m - o.mantissa(), o.exponent() };
|
||||
}
|
||||
else {
|
||||
int8_t delta = o.exponent() - _exponent;
|
||||
int64_t m = o.mantissa() * pow10(delta);
|
||||
return { _mantissa - m, exponent() };
|
||||
}
|
||||
}
|
||||
return op(o, [](int64_t m1, int64_t m2) { return m1 - m2; });
|
||||
}
|
||||
IntPrice operator + (const IntPrice& o) const {
|
||||
if (_exponent==o._exponent) {
|
||||
return IntPrice { _mantissa+o.mantissa(), exponent() };
|
||||
}
|
||||
else {
|
||||
if (is_nan() || o.is_nan()) return IntPrice();
|
||||
bts_assert_d(o.exponent() <= 0 && _exponent <= 0);
|
||||
if (o.exponent() < _exponent) {
|
||||
int8_t delta = _exponent - o.exponent();
|
||||
int64_t m = _mantissa * pow10(delta);
|
||||
return { m + o.mantissa(), o.exponent() };
|
||||
}
|
||||
else {
|
||||
int8_t delta = o.exponent() - _exponent;
|
||||
int64_t m = o.mantissa() * pow10(delta);
|
||||
return { _mantissa + m, exponent() };
|
||||
}
|
||||
}
|
||||
return op(o, [](int64_t m1, int64_t m2) { return m1 + m2; });
|
||||
}
|
||||
IntPrice operator * (const IntPrice& other) const {
|
||||
return op(other, [](int64_t m1, int64_t m2) { return m1 * m2; });
|
||||
}
|
||||
IntPrice operator / (double v) const {
|
||||
return { (int64_t)(_mantissa / v), exponent() };
|
||||
}
|
||||
|
||||
IntPrice sign_extend(int8_t new_exponent) const {
|
||||
int8_t delta = _exponent - new_exponent;
|
||||
int64_t m = _mantissa * pow10(delta);
|
||||
return { m, new_exponent };
|
||||
}
|
||||
|
||||
int64_t mantissa() const { return _mantissa; }
|
||||
int8_t exponent() const { return _exponent; }
|
||||
double price() const {
|
||||
@@ -107,6 +83,29 @@ class IntPrice {
|
||||
return mantissa() * pow10(exponent());
|
||||
}
|
||||
bool is_nan() const { return _exponent == 127; }
|
||||
|
||||
private:
|
||||
template <class MantissaOp>
|
||||
IntPrice op(const IntPrice& o, MantissaOp operation) const {
|
||||
if (_exponent==o._exponent) {
|
||||
return IntPrice { operation(_mantissa,o.mantissa()), exponent() };
|
||||
}
|
||||
else {
|
||||
if (is_nan() || o.is_nan()) return IntPrice();
|
||||
bts_assert_d(o.exponent() <= 0 && _exponent <= 0);
|
||||
if (o.exponent() < _exponent) {
|
||||
int8_t delta = _exponent - o.exponent();
|
||||
int64_t m = _mantissa * pow10(delta);
|
||||
return { operation(m, o.mantissa()), o.exponent() };
|
||||
}
|
||||
else {
|
||||
int8_t delta = o.exponent() - _exponent;
|
||||
int64_t m = o.mantissa() * pow10(delta);
|
||||
return { operation(_mantissa, m), exponent() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
static_assert(sizeof(IntPrice) == 8, "IntPrice should be this big |--------|");
|
||||
extern IntPrice INT_NAN;
|
||||
|
||||
@@ -255,11 +255,14 @@ TimeStamp copy_synthetic_depth(const SyntheticStrategy& synth, size_t count, Pri
|
||||
if (r == HandleReadResult::BUSY || t == TimeStamp::invalid()) return TimeStamp::invalid();
|
||||
|
||||
// NET Change
|
||||
if (leg_params.leg_md_calc() == spark::LegSpreaderParams::NET_CHANGE) {
|
||||
if (synth.security.synthetic().price_calc() == api::SyntheticContract::PRICECALC_NET_CHANGE) {
|
||||
auto settle = synth.legs[x].settlement_price();
|
||||
double tick = synth.legs[x].get_minimum_tick();
|
||||
for (size_t d = 0; d < COCONUT_MAX_DEPTH; ++d) {
|
||||
bids[d] = bids[d] - settle;
|
||||
asks[d] = asks[d] - settle;
|
||||
auto b = (bids[d] - settle);
|
||||
auto a = (asks[d] - settle);
|
||||
bids[d] = { b.int_price() / tick, b.size() };
|
||||
asks[d] = { a.int_price() / tick, a.size() };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -197,6 +197,15 @@ public:
|
||||
}
|
||||
bts_assert(false);
|
||||
}
|
||||
double get_minimum_tick() const {
|
||||
switch(_type) {
|
||||
case HANDLETYPE_DEPTH: return book.cdata->security.low_tick * pow10(book.cdata->security.display_factor);
|
||||
case HANDLETYPE_BBO: return bbo.cdata->security.low_tick * pow10(bbo.cdata->security.display_factor);
|
||||
case HANDLETYPE_SYNTHETIC: return synthetic.def->security.contract_spec().low_tick();
|
||||
default:
|
||||
case HANDLETYPE_VOID: return os::nan();
|
||||
}
|
||||
}
|
||||
|
||||
TimeStamp last_market_update() const {
|
||||
switch(_type) {
|
||||
|
||||
@@ -57,6 +57,11 @@ public:
|
||||
res._price=res._price-price;
|
||||
return res;
|
||||
}
|
||||
PriceSize get_divided_price(double divisor) const{
|
||||
PriceSize res=*this;
|
||||
res._price=res._price/divisor;
|
||||
return res;
|
||||
}
|
||||
//
|
||||
// Accessors
|
||||
|
||||
|
||||
@@ -335,12 +335,12 @@ void relay_everything() {
|
||||
relay_selected(ctx, system_symbols, producer);
|
||||
}
|
||||
|
||||
void local_relay(IRelayWriter& writer) {
|
||||
api::ServiceContext ctx = elevated_relay_context();
|
||||
ctx.login(api::auth::generate_wheel_token());
|
||||
auto relay = ctx.locate<MarketDataRelay>();
|
||||
std::set<UserSymbol> get_relay_symbols(bool record_sim) {
|
||||
std::set<UserSymbol> symbols;
|
||||
{
|
||||
if (record_sim) {
|
||||
get_system_md_symbols(symbols);
|
||||
}
|
||||
else {
|
||||
std::set<UserSymbol> system_symbols;
|
||||
get_system_md_symbols(system_symbols);
|
||||
|
||||
@@ -351,6 +351,14 @@ void local_relay(IRelayWriter& writer) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
|
||||
void local_relay(IRelayWriter& writer) {
|
||||
api::ServiceContext ctx = elevated_relay_context();
|
||||
ctx.login(api::auth::generate_wheel_token());
|
||||
auto relay = ctx.locate<MarketDataRelay>();
|
||||
auto symbols = get_relay_symbols(false);
|
||||
relay_selected(ctx, symbols, writer);
|
||||
}
|
||||
|
||||
@@ -382,8 +390,7 @@ void record_everything(std::string path, std::string filename) {
|
||||
api::ServiceContext ctx = elevated_relay_context();
|
||||
auto relay = ctx.locate<MarketDataRelay>();
|
||||
|
||||
std::set<UserSymbol> system_symbols;
|
||||
get_system_md_symbols(system_symbols);
|
||||
std::set<UserSymbol> system_symbols = get_relay_symbols(options.record_sim());
|
||||
|
||||
RelayClient client(relay, nullptr, system_symbols, recorder);
|
||||
RelayScheduler scheduler(ctx, naggle_ms, client);
|
||||
|
||||
@@ -15,6 +15,9 @@ struct Co2TestInstrument
|
||||
|
||||
MDProducerHandle producer();
|
||||
MDConsumerHandle consumer();
|
||||
|
||||
ColdEntry& cold() { return *_cold; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<uint8_t> _cold_buffer;
|
||||
ColdEntry* _cold;
|
||||
|
||||
@@ -2407,6 +2407,12 @@ TEST_CASE("IntPrice", "[md]") {
|
||||
auto buy = round_to_quote_price(tick, price, api::SIDE_BUY);
|
||||
REQUIRE(buy == IntPrice_from_double<7>(-4.360));
|
||||
}
|
||||
|
||||
SECTION("division by double") {
|
||||
IntPrice price = { -43595000, -7 };
|
||||
IntPrice price_div10 = { -43595000, -8 };
|
||||
REQUIRE(price / 10.0 == price_div10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/md/IntPrice.h"
|
||||
#include "lib/md/PriceSize.h"
|
||||
#include <vector>
|
||||
|
||||
namespace bts {
|
||||
namespace md {
|
||||
namespace test {
|
||||
|
||||
struct PS {
|
||||
double price;
|
||||
Size size;
|
||||
bool operator == (const PS& other) const {
|
||||
// Convert to IntPrice to avoid double rounding issues
|
||||
auto p1 = IntPrice_from_double<11>(price);
|
||||
auto p2 = IntPrice_from_double<11>(other.price);
|
||||
return p1 == p2 && size == other.size;
|
||||
}
|
||||
};
|
||||
|
||||
struct Book {
|
||||
std::vector<PS> bids;
|
||||
std::vector<PS> asks;
|
||||
};
|
||||
|
||||
Book compute_synthetic_book(const api::Security& security, const std::vector<Book>& leg_books);
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const PS& val);
|
||||
|
||||
|
||||
}}}//namespace
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef _WIN32
|
||||
#include "catch/catch.hpp"
|
||||
#include "../MDConsumerHandle.h"
|
||||
#include "md_test_utils.h"
|
||||
#include "Co2TestInstrument.h"
|
||||
#include "lib/utils/sane_algorithm.h"
|
||||
#include <memory>
|
||||
@@ -23,16 +24,6 @@ namespace md {
|
||||
namespace test {
|
||||
|
||||
// std::tuple is hard to read in the debugger
|
||||
struct PS {
|
||||
double price;
|
||||
Size size;
|
||||
bool operator == (const PS& other) const {
|
||||
// Convert to IntPrice to avoid double rounding issues
|
||||
auto p1 = IntPrice_from_double<11>(price);
|
||||
auto p2 = IntPrice_from_double<11>(other.price);
|
||||
return p1 == p2 && size == other.size;
|
||||
}
|
||||
};
|
||||
|
||||
ostream& operator<< (ostream& os, const PS& val) {
|
||||
os << "{ " << val.price << "," << val.size << " }";
|
||||
@@ -103,22 +94,29 @@ tuple<shared_ptr<SyntheticStrategy>,vector<Co2TestInstrument>> make_securities(c
|
||||
api::Side side;
|
||||
std::tie(quantity, mult, tick_size, side) = ldesc;
|
||||
Co2TestInstrument instr("ES", api::SECURITYTYPE_FUTURE);
|
||||
// tick_size / display_factor
|
||||
auto display_factor = pow10(instr.cold().security.display_factor);
|
||||
instr.cold().security.low_tick = tick_size / display_factor;
|
||||
instr.cold().security.high_tick = tick_size / display_factor;
|
||||
instr.producer().set_key(security_key);
|
||||
++security_key;
|
||||
|
||||
auto& s = synthetic->security;
|
||||
// Set the synthetic tick size equal to the (last) leg. (needs to not be 0)
|
||||
s.mutable_contract_spec()->set_low_tick(tick_size);
|
||||
s.mutable_contract_spec()->set_high_tick(tick_size);
|
||||
auto* ps = s.mutable_synthetic();
|
||||
auto l = ps->mutable_legs()->Add();
|
||||
l->mutable_security()->CopyFrom(instr.consumer().get_normalized_security());
|
||||
l->mutable_security()->mutable_contract_spec()->set_low_tick(tick_size);
|
||||
l->mutable_security()->mutable_contract_spec()->set_high_tick(tick_size);
|
||||
l->set_quantity(quantity);
|
||||
l->set_price_multiplier(quantity * (side == api::SIDE_BUY ? 1.0 : -1));
|
||||
l->set_side(side);
|
||||
synthetic->legs.push_back(instr.consumer());
|
||||
legs.push_back(instr);
|
||||
|
||||
}
|
||||
|
||||
return { synthetic, legs };
|
||||
}
|
||||
// tuple = (quantity, tick_size, side)
|
||||
@@ -142,6 +140,56 @@ void set_leg_params(SyntheticStrategy& strat, uint32_t security_key, const spark
|
||||
map->insert({ security_key, params });
|
||||
}
|
||||
|
||||
|
||||
Book compute_synthetic_book(const api::Security& security, const std::vector<Book>& leg_books) {
|
||||
auto synthetic = std::make_shared<SyntheticStrategy>();
|
||||
synthetic->security.CopyFrom(security);
|
||||
vector<Co2TestInstrument> leg_instruments;
|
||||
for (const auto& leg : synthetic->security.synthetic().legs()) {
|
||||
const auto& security = leg.security();
|
||||
Co2TestInstrument instr(security.symbol(), securitytype_of(security));
|
||||
convert_security(security, instr.cold().security, true);
|
||||
leg_instruments.push_back(instr);
|
||||
synthetic->legs.push_back(instr.consumer());
|
||||
}
|
||||
|
||||
// Write leg books
|
||||
for (size_t x = 0; x < leg_books.size(); ++x) {
|
||||
std::array<PriceSize,COCONUT_MAX_DEPTH> bids = {}, asks = {};
|
||||
std::transform(
|
||||
leg_books[x].bids.begin(),
|
||||
leg_books[x].bids.end(),
|
||||
bids.begin(),
|
||||
[](const PS& ps) {
|
||||
return PriceSize_from_double<7>(ps.price, ps.size);
|
||||
});
|
||||
std::transform(
|
||||
leg_books[x].asks.begin(),
|
||||
leg_books[x].asks.end(),
|
||||
asks.begin(),
|
||||
[](const PS& ps) {
|
||||
return PriceSize_from_double<7>(ps.price, ps.size);
|
||||
});
|
||||
auto ts = TimeStamp::now();
|
||||
leg_instruments[x].producer().update_direct(ts, bids, asks);
|
||||
}
|
||||
|
||||
// Read synthetic book back
|
||||
MDConsumerHandle synthetic_handle { synthetic };
|
||||
std::array<PriceSize,COCONUT_MAX_DEPTH> raw_bids, raw_asks;
|
||||
TimeStamp t;
|
||||
synthetic_handle.get_direct_book(t, COCONUT_MAX_DEPTH, raw_bids.data(), raw_asks.data());
|
||||
Book result {};
|
||||
sane::map(raw_bids, result.bids, [](PriceSize ps) -> PS { return {ps.price(), ps.size()}; });
|
||||
sane::map(raw_asks, result.asks, [](PriceSize ps) -> PS { return {ps.price(), ps.size()}; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// Test Cases
|
||||
///////////////////////
|
||||
|
||||
TEST_CASE("SyntheticBookMerge", "[md][synthetic]") {
|
||||
shared_ptr<SyntheticStrategy> synthetic;
|
||||
vector<Co2TestInstrument> test_instr;
|
||||
@@ -381,17 +429,16 @@ TEST_CASE("AggregationAcrossLevels", "[md][synthetic]") {
|
||||
TEST_CASE("Synthetic_net_change", "[md][synthetic]") {
|
||||
shared_ptr<SyntheticStrategy> synthetic;
|
||||
vector<Co2TestInstrument> test_instr;
|
||||
tie(synthetic, test_instr) = make_securities({ {1.0, 0.01, api::SIDE_BUY}, {2.0, 0.01, api::SIDE_SELL }});
|
||||
// This tick size is what you get when you make up a test and then later have to figure out the tick size because NetChange suddenly depends on tick size
|
||||
tie(synthetic, test_instr) = make_securities({ {1.0, 10.0, api::SIDE_BUY}, {2.0, 10.0, api::SIDE_SELL }});
|
||||
// Set to tick in "# ticks"
|
||||
synthetic->security.mutable_contract_spec()->set_low_tick(1.0);
|
||||
synthetic->security.mutable_contract_spec()->set_high_tick(1.0);
|
||||
synthetic->security.mutable_synthetic()->set_price_calc(api::SyntheticContract::PRICECALC_NET_CHANGE);
|
||||
auto es_buy1 = test_instr[0];
|
||||
auto es_sell2 = test_instr[1];
|
||||
MDConsumerHandle synthetic_handle{synthetic};
|
||||
|
||||
// Set legs to use net change
|
||||
spark::LegSpreaderParams lparam;
|
||||
lparam.set_leg_md_calc(spark::LegSpreaderParams::NET_CHANGE);
|
||||
set_leg_params(*synthetic, es_buy1.producer().key(), lparam);
|
||||
set_leg_params(*synthetic, es_sell2.producer().key(), lparam);
|
||||
|
||||
// set settlement prices
|
||||
es_buy1.producer().update_settlement(TimeStamp::now(), { 100, 0 }, datetime::today());
|
||||
es_sell2.producer().update_settlement(TimeStamp::now(), { 50, 0 }, datetime::today());
|
||||
@@ -404,11 +451,9 @@ TEST_CASE("Synthetic_net_change", "[md][synthetic]") {
|
||||
{{90,5}, {80,9}, {70,11}, {60,10}, {50,1}},
|
||||
{{100,16}, {110,8}, {120,12}, {130,5}});
|
||||
|
||||
// Validate merge
|
||||
expect_market(synthetic_handle,
|
||||
{{-10,1}, {-20,7}, {-40,4}, {-70,4}, {-80,2}, {-100,2}},
|
||||
{{20,2}, {50,5}, {70,1}, {80,4}, {100,4}, {110,1}, {130,1}});
|
||||
// precise {{20,2}, {40,1}, {50,4}, {70,1}, {80,4}, {90,1}, {100,3}, {110,1}, {120,1}});
|
||||
{{-1,1}, {-2,7}, {-4,4}, {-7,4}, {-8,2}, {-10,2}},
|
||||
{{2,2}, {5,5}, {7,1}, {8,4}, {10,4}, {11,1}, {13,1}});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/mercury/actors/Actor.h"
|
||||
#include "lib/tracing/PerfLog.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "lib/utils/bts_assert.h"
|
||||
|
||||
namespace bts {
|
||||
namespace mercury {
|
||||
|
||||
@@ -40,59 +40,71 @@ template <typename EV, typename KE = KeyExtractor> class ContentSubscription {
|
||||
}
|
||||
};
|
||||
|
||||
ContentSubscription(Actor &actor)
|
||||
ContentSubscription(Actor &actor, trace::PerfLog &log)
|
||||
: _actor(&actor), _sub(_actor->subscribe<EV>(
|
||||
[this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1),
|
||||
_pre_sub([](auto const &) {}), _pre_event([](auto const &) {}) {}
|
||||
_pre_sub([](auto const &) {}), _log(log),
|
||||
_name(utils::type_name<EV>()), _pre_event([](auto const &) {}) {}
|
||||
|
||||
ContentSubscription(Actor &actor, std::function<void(Key)> const &pscb)
|
||||
ContentSubscription(Actor &actor, std::function<void(Key)> const &pscb,
|
||||
trace::PerfLog &log)
|
||||
: _actor(&actor), _sub(_actor->subscribe<EV>(
|
||||
[this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1), _pre_sub(pscb),
|
||||
_log(log), _name(utils::type_name<EV>()),
|
||||
_pre_event([](auto const &) {}) {}
|
||||
|
||||
ContentSubscription(Actor &actor, std::function<void(Key)> const &pscb,
|
||||
std::function<void(EV const &)> const &pre_ev)
|
||||
std::function<void(EV const &)> const &pre_ev,
|
||||
trace::PerfLog &log)
|
||||
: _actor(&actor), _sub(_actor->subscribe<EV>(
|
||||
[this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1), _pre_sub(pscb),
|
||||
_pre_event(pre_ev) {}
|
||||
_log(log), _name(utils::type_name<EV>()), _pre_event(pre_ev) {}
|
||||
|
||||
template <class Tag>
|
||||
ContentSubscription(Actor &actor, Tag const &tag)
|
||||
ContentSubscription(Actor &actor, Tag const &tag, trace::PerfLog &log)
|
||||
: _actor(&actor),
|
||||
_sub(_actor->subscribe<EV>(
|
||||
tag, [this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1),
|
||||
_pre_sub([](auto const &) {}), _pre_event([](auto const &) {}) {}
|
||||
|
||||
template <class Tag>
|
||||
ContentSubscription(Actor &actor, Tag const &tag,
|
||||
std::function<void(Key)> const &pscb)
|
||||
: _actor(&actor),
|
||||
_sub(_actor->subscribe<EV>(
|
||||
tag, [this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1), _pre_sub(pscb),
|
||||
_pre_event([](auto const &) {}) {}
|
||||
|
||||
template <class Tag>
|
||||
ContentSubscription(Actor &actor, Tag const &tag,
|
||||
std::function<void(EV const &)> const &pre_ev)
|
||||
: _actor(&actor),
|
||||
_sub(_actor->subscribe<EV>(
|
||||
tag, [this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1),
|
||||
_pre_sub([](auto const &) {}), _pre_event(pre_ev) {}
|
||||
_pre_sub([](auto const &) {}), _log(log),
|
||||
_name(utils::type_name<EV>()), _pre_event([](auto const &) {}) {}
|
||||
|
||||
template <class Tag>
|
||||
ContentSubscription(Actor &actor, Tag const &tag,
|
||||
std::function<void(Key)> const &pscb,
|
||||
std::function<void(EV const &)> const &pre_ev)
|
||||
trace::PerfLog &log)
|
||||
: _actor(&actor),
|
||||
_sub(_actor->subscribe<EV>(
|
||||
tag, [this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1), _pre_sub(pscb),
|
||||
_log(log), _name(utils::type_name<EV>()),
|
||||
_pre_event([](auto const &) {}) {}
|
||||
|
||||
template <class Tag>
|
||||
ContentSubscription(Actor &actor, Tag const &tag,
|
||||
std::function<void(EV const &)> const &pre_ev,
|
||||
trace::PerfLog &log)
|
||||
: _actor(&actor),
|
||||
_sub(_actor->subscribe<EV>(
|
||||
tag, [this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1),
|
||||
_pre_sub([](auto const &) {}),
|
||||
_log(log), _name(utils::type_name<EV>()),
|
||||
_pre_event(pre_ev) {}
|
||||
|
||||
template <class Tag>
|
||||
ContentSubscription(Actor &actor, Tag const &tag,
|
||||
std::function<void(Key)> const &pscb,
|
||||
std::function<void(EV const &)> const &pre_ev,
|
||||
trace::PerfLog &log)
|
||||
: _actor(&actor),
|
||||
_sub(_actor->subscribe<EV>(
|
||||
tag, [this](auto const &u) { this->on_update(u); })),
|
||||
_my_subs(), _dispatching(false), _current_sub_id(1), _pre_sub(pscb),
|
||||
_log(log), _name(utils::type_name<EV>()),
|
||||
_pre_event(pre_ev) {}
|
||||
|
||||
template <class CBT> SubID sub(Key k, CBT const &cb) {
|
||||
@@ -138,9 +150,14 @@ template <typename EV, typename KE = KeyExtractor> class ContentSubscription {
|
||||
bool _dispatching;
|
||||
uint32_t _current_sub_id;
|
||||
std::function<void(Key k)> _pre_sub;
|
||||
trace::PerfLog &_log;
|
||||
std::string _name;
|
||||
|
||||
std::function<void(EV const &)> _pre_event;
|
||||
|
||||
void on_update(Update<EV> const &u) {
|
||||
auto scoped_do_work =
|
||||
trace::log_scope(_log, {}, "ContentSub::on_update", 0, _name.c_str());
|
||||
KE ke;
|
||||
_dispatching = true;
|
||||
if (u.events)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace trace {
|
||||
class PerfLog;
|
||||
}
|
||||
namespace mercury {
|
||||
|
||||
// interface to mark an actor that requires on_poll() calls
|
||||
@@ -10,7 +13,7 @@ class IPollableActor
|
||||
{
|
||||
public:
|
||||
~IPollableActor();
|
||||
virtual void on_poll() = 0;
|
||||
virtual void on_poll(trace::PerfLog&) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class Timer : public Actor, public mercury::IPollableActor {
|
||||
}
|
||||
|
||||
private:
|
||||
void on_poll() override {
|
||||
void on_poll(trace::PerfLog&) override {
|
||||
auto now = bts::Timer::now();
|
||||
if (ms_diff(now, _last_ts) > 10) {
|
||||
_last_ts = now;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/mercury/common/mercury_common.h"
|
||||
#include "lib/mercury/topics/Topic.h"
|
||||
#include "lib/utils/GrowableSPSCQueue.h"
|
||||
#include "lib/tracing/PerfLog.h"
|
||||
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
#include <memory>
|
||||
@@ -79,9 +80,9 @@ class BrokerProxy : public ILocalBroker {
|
||||
send(m);
|
||||
}
|
||||
|
||||
void do_work() {
|
||||
void do_work(trace::PerfLog& log) {
|
||||
_cycles++;
|
||||
_outgoing.consume_all([this](BrokerMsg *m) {
|
||||
_outgoing.consume_all([this,&log](BrokerMsg *m) {
|
||||
switch (m->msg_type) {
|
||||
case BrokerMsgType::ADD_NEIGHBOUR:
|
||||
_real_broker.add_neighbour(*m->neighbour);
|
||||
@@ -101,11 +102,13 @@ class BrokerProxy : public ILocalBroker {
|
||||
case BrokerMsgType::UNSUBSCRIBE:
|
||||
_real_broker.unsubscribe(m->broker_id, m->sub_id, m->stream_id);
|
||||
break;
|
||||
case BrokerMsgType::NOTIFY:
|
||||
case BrokerMsgType::NOTIFY: {
|
||||
LOG(Latency, Diag, "Notify broker {}. Cycles: {}", _id.value(),
|
||||
_cycles);
|
||||
auto scoped_do_work = trace::log_scope(log, {}, "BrokerProxy::notify");
|
||||
_real_broker.notify(m->broker_id, m->stream_id, m->sub_id,
|
||||
m->notification);
|
||||
}
|
||||
break;
|
||||
case BrokerMsgType::LOCAL_SUBSCRIBE:
|
||||
_real_broker.local_subscribe(m->local_sub_id, m->filter,
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
#include "lib/mercury/broker/Broker.h"
|
||||
#include "lib/mercury/broker/BrokerProxy.h"
|
||||
#include "lib/mercury/actors/ActorBroker.h"
|
||||
#include "lib/tracing/PerfLog.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace bts {
|
||||
|
||||
namespace mercury {
|
||||
class Actor;
|
||||
class BrokerProxy;
|
||||
@@ -22,13 +24,16 @@ class Executor {
|
||||
activate(new A(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void do_work() {
|
||||
for (auto p : _pollable_actors)
|
||||
p->on_poll();
|
||||
for (auto &bp : _incoming_proxies)
|
||||
bp->do_work();
|
||||
for (auto &bp : _outgoing_proxies)
|
||||
void do_work(trace::PerfLog& log) {
|
||||
for (auto p : _pollable_actors) {
|
||||
p->on_poll(log);
|
||||
}
|
||||
for (auto &bp : _incoming_proxies) {
|
||||
bp->do_work(log);
|
||||
}
|
||||
for (auto &bp : _outgoing_proxies) {
|
||||
bp->drain();
|
||||
}
|
||||
_my_proxy.drain();
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,8 @@ void ExecutionGateway::on_exe_report(
|
||||
SecurityKey sk;
|
||||
decode_clorid(msg.get<fix::ClOrdID>(), &ok, &sk);
|
||||
|
||||
auto scoped_log = trace::log_scope(me->perf_log, msg.trace(), "ExecutionGateway::on_exe_report", ok.value());
|
||||
|
||||
LOG(Latency, Info, "Started handling of execution report for order {}",
|
||||
ok.value());
|
||||
auto sec = me->securities[sk.value()];
|
||||
@@ -296,7 +298,7 @@ void ExecutionGateway::dispatch_incoming_fix_messages() {
|
||||
|
||||
void ExecutionGateway::send_fix(jetfix::OutFIXPtr const &fix,
|
||||
trace::CorID trace) {
|
||||
trace::ScopedPerfLogEntry scoped_log (me->perf_log,trace,"ExecutionGateway::send_fix");
|
||||
auto scoped_log = trace::log_scope(me->perf_log,trace,"ExecutionGateway::send_fix");
|
||||
me->gw.send(fix,trace);
|
||||
me->gw.dispatch_events();
|
||||
}
|
||||
|
||||
@@ -95,7 +95,9 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
}
|
||||
|
||||
private:
|
||||
void on_poll() override {
|
||||
void on_poll(trace::PerfLog& log) override {
|
||||
|
||||
auto scoped_do_work = trace::log_scope(log, {}, "MDSource::on_poll", utils::TimeStamp::Interval::from_microseconds(2));
|
||||
|
||||
// Book Updates
|
||||
Book book;
|
||||
|
||||
@@ -51,7 +51,7 @@ void MDSupplementarySource::init() {
|
||||
});
|
||||
}
|
||||
|
||||
void MDSupplementarySource::on_poll() {
|
||||
void MDSupplementarySource::on_poll(trace::PerfLog&) {
|
||||
for (auto &kvp : me->handle_map) {
|
||||
SecurityKey key(kvp.first);
|
||||
HandleRecord &hr = kvp.second;
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
~MDSupplementarySource();
|
||||
|
||||
void init() override;
|
||||
void on_poll() override;
|
||||
void on_poll(trace::PerfLog&) override;
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
@@ -358,6 +358,7 @@ OrderManager::free_buffers(std::string const &session_name) {
|
||||
}
|
||||
|
||||
void OrderManager::init_order_services() {
|
||||
auto& log = me->services_data.log;
|
||||
|
||||
for (auto &s : me->services_data.sessions) {
|
||||
auto gw_name = GatewayName(s->session);
|
||||
@@ -409,13 +410,13 @@ void OrderManager::init_order_services() {
|
||||
};
|
||||
s->exe_report_subs =
|
||||
std::make_unique<ContentSubscription<NewExecutionReportEvent>>(
|
||||
*this, gw_name, on_exe_r);
|
||||
*this, gw_name, on_exe_r,log);
|
||||
|
||||
s->cancel_reject_subs =
|
||||
std::make_unique<ContentSubscription<CancelRejectEvent>>(*this,
|
||||
gw_name);
|
||||
gw_name,log);
|
||||
s->gateway_status_subs = std::make_unique<
|
||||
ContentSubscription<GatewayStatusChangeEvent, NoKey>>(*this, gw_name);
|
||||
ContentSubscription<GatewayStatusChangeEvent, NoKey>>(*this, gw_name,log);
|
||||
|
||||
// publishers for execution reports, gateway status and cancel
|
||||
// reject evets
|
||||
@@ -494,17 +495,17 @@ void OrderManager::init_order_services() {
|
||||
};
|
||||
me->services_data.md_subs =
|
||||
std::make_unique<ContentSubscription<BookUpdateEvent>>(*this, on_md_sub,
|
||||
on_book_update);
|
||||
on_book_update,log);
|
||||
me->services_data.ticker_subs =
|
||||
std::make_unique<ContentSubscription<TickerUpdateEvent>>(*this,
|
||||
on_ticker_sub);
|
||||
on_ticker_sub,log);
|
||||
me->services_data.settlement_subs =
|
||||
std::make_unique<ContentSubscription<SettlementUpdateEvent>>(
|
||||
*this, on_settlement_sub);
|
||||
*this, on_settlement_sub,log);
|
||||
me->services_data.time_subs =
|
||||
std::make_unique<ContentSubscription<TimerEvent, NoKey>>(*this);
|
||||
std::make_unique<ContentSubscription<TimerEvent, NoKey>>(*this,log);
|
||||
me->services_data.order_creation_subs =
|
||||
std::make_unique<ContentSubscription<CreateOrderReplyEvent>>(*this);
|
||||
std::make_unique<ContentSubscription<CreateOrderReplyEvent>>(*this,log);
|
||||
}
|
||||
|
||||
void OrderManager::after_event() {
|
||||
@@ -513,6 +514,7 @@ void OrderManager::after_event() {
|
||||
for (auto &osd : me->services_data.sessions) {
|
||||
auto &fix_stream = osd->current_buffer->first_stream();
|
||||
if (fix_stream.n_msgs()) {
|
||||
auto scoped_do_work = trace::log_scope(me->services_data.log, {}, "OrderManager::after_event");
|
||||
fix_stream.close();
|
||||
osd->gw.send_fix(osd->current_buffer, osd->current_trace);
|
||||
reload_fix_buffer(*osd);
|
||||
@@ -534,7 +536,8 @@ void OrderManager::reload_fix_buffer(SessionServicesData &osd) {
|
||||
}
|
||||
}
|
||||
|
||||
void OrderManager::on_poll() {
|
||||
void OrderManager::on_poll(trace::PerfLog&log) {
|
||||
auto scoped_do_work = trace::log_scope(log, {}, "OrderManager::dispatch_incoming_fix_messages");
|
||||
for (auto &osd : me->services_data.sessions)
|
||||
osd->gw.dispatch_incoming_fix_messages();
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class OrderManager : public mercury::Actor, public mercury::IPollableActor {
|
||||
void on_gateway_state(const mercury::Update<GatewayStatusChangeEvent> &);
|
||||
void on_timer(const mercury::Update<mercury::TimerEvent> &);
|
||||
void on_admin_data(const mercury::Update<NewAdminDataEvent> &);
|
||||
void on_poll() override;
|
||||
void on_poll(trace::PerfLog& log) override;
|
||||
// end external events
|
||||
|
||||
void init_order_services();
|
||||
|
||||
@@ -423,12 +423,12 @@ OrderOpResult NativeOrder::on_activate(Price p, Quantity q,
|
||||
switch (me->state.status()) {
|
||||
|
||||
case ORDER_STATUS_INACTIVE: {
|
||||
auto scoped_log =
|
||||
services.log_scope("NativeOrder::on_activate", key().value());
|
||||
// this is a brand new order
|
||||
|
||||
result = _on_activate(Warn, Info, p, q, services);
|
||||
if (result.ok()) {
|
||||
auto scoped_log =
|
||||
services.log_scope("NativeOrder::on_activate", key().value());
|
||||
me->services->queue_fix(*me->session_services,
|
||||
jetfix::MessageTypes::NewOrderSingle,
|
||||
me->create_encoding);
|
||||
|
||||
@@ -467,7 +467,7 @@ bool SpreaderOrder::_contains_net_change_leg(){
|
||||
for (size_t l_idx = 0; l_idx < _bookkeeping_data().n_legs(); l_idx++){
|
||||
const LegPropertyData& leg_data =
|
||||
_bookkeeping_data().get_leg_data(l_idx).leg_property_data();
|
||||
if(leg_data.leg_md_calc()==bts::spark::LegSpreaderParams::NET_CHANGE)
|
||||
if(leg_data.is_net_change())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -477,7 +477,7 @@ Price SpreaderOrder::_compute_effective_price (Price input_price){
|
||||
if(_contains_net_change_leg()){
|
||||
for (size_t l_idx = 0; l_idx < _bookkeeping_data().n_legs(); l_idx++){
|
||||
const SpreaderLegData& leg_data = _bookkeeping_data().get_leg_data(l_idx);
|
||||
if(leg_data.leg_property_data().leg_md_calc()==bts::spark::LegSpreaderParams::NET_CHANGE){
|
||||
if(leg_data.leg_property_data().is_net_change()){
|
||||
if (!leg_data.settlement_populated())
|
||||
return Price(os::nan());
|
||||
else
|
||||
|
||||
@@ -8,12 +8,14 @@ namespace orderman {
|
||||
LegPropertyData::LegPropertyData(size_t leg_index,
|
||||
const bts::api::ContractLeg& leg_contract,
|
||||
const bts::spark::LegSpreaderParams& leg_params,
|
||||
api::Side trading_side)
|
||||
api::Side trading_side,
|
||||
bts::api::SyntheticContract::PriceCalc price_calc)
|
||||
:
|
||||
_leg_index(leg_index),
|
||||
_leg_contract(leg_contract),
|
||||
_leg_params(leg_params),
|
||||
_trading_side(trading_side){
|
||||
_trading_side(trading_side),
|
||||
_price_calc(price_calc){
|
||||
LOG(OrderMan, Info, "LegPropertyData.cons leg_idx: "
|
||||
"{} sec_key: {} side: {}",
|
||||
leg_index,
|
||||
|
||||
@@ -18,11 +18,14 @@ private:
|
||||
const bts::api::ContractLeg _leg_contract;
|
||||
const bts::spark::LegSpreaderParams _leg_params;
|
||||
const api::Side _trading_side;
|
||||
const bts::api::SyntheticContract::PriceCalc _price_calc;
|
||||
public:
|
||||
LegPropertyData(size_t leg_index,
|
||||
const bts::api::ContractLeg& leg_contract,
|
||||
const bts::spark::LegSpreaderParams& leg_params,
|
||||
api::Side trading_side);
|
||||
api::Side trading_side,
|
||||
bts::api::SyntheticContract::PriceCalc price_calc=
|
||||
bts::api::SyntheticContract::PRICECALC_STANDARD);
|
||||
size_t leg_index() const {return _leg_index;}
|
||||
const bts::api::ContractLeg& leg_contract() const {return _leg_contract;}
|
||||
const bts::spark::LegSpreaderParams& leg_params () const {return _leg_params;}
|
||||
@@ -34,8 +37,8 @@ public:
|
||||
double size_multiplier() const { return _leg_contract.quantity(); }
|
||||
double tick_size() const { return _leg_contract.security().contract_spec().low_tick(); }
|
||||
bool is_active_quoting() const {return _leg_params.is_active_quoting();}
|
||||
bts::spark::LegSpreaderParams::LegMarketDataCalculation leg_md_calc() const {
|
||||
return _leg_params.leg_md_calc();
|
||||
bool is_net_change() const{
|
||||
return _price_calc==bts::api::SyntheticContract::PRICECALC_NET_CHANGE;
|
||||
}
|
||||
bts::spark::LegSpreaderParams::MarketDataType md_type() const {
|
||||
return _leg_params.market_data_type();
|
||||
|
||||
@@ -73,19 +73,23 @@ size_t SpreaderBookkeepingData::_calc_n_quoting_legs(
|
||||
void SpreaderBookkeepingData::total_fills (Quantity& out_quantity,
|
||||
Price& out_price) const{
|
||||
double avg_price = 0.0;
|
||||
double fills = std::numeric_limits<double>::max();
|
||||
double fills = std::numeric_limits<double>::max();
|
||||
for (const SpreaderLegData& leg_data : _leg_data){
|
||||
Quantity leg_fill_qty; Price leg_fill_price;
|
||||
leg_data.total_fills(leg_fill_qty, leg_fill_price);
|
||||
double normalized_qty = std::abs(static_cast<double>(leg_fill_qty.value()) / leg_data.leg_property_data().size_multiplier());
|
||||
fills = normalized_qty < fills ? normalized_qty : fills;
|
||||
if(leg_data.leg_property_data().leg_md_calc()==bts::spark::LegSpreaderParams::NET_CHANGE)
|
||||
double leg_normalized_qty = static_cast<double>(leg_fill_qty.value()) /
|
||||
leg_data.leg_property_data().size_multiplier();
|
||||
leg_normalized_qty=std::abs(leg_normalized_qty);
|
||||
// the spreader fill qty = min(normalized leg fills)
|
||||
fills = leg_normalized_qty < fills ? leg_normalized_qty : fills;
|
||||
if(leg_data.leg_property_data().is_net_change())
|
||||
avg_price += (leg_fill_price.value()-leg_data.settlement_price().value()) *
|
||||
leg_data.leg_property_data().price_multiplier();
|
||||
else
|
||||
avg_price += leg_fill_price.value() * leg_data.leg_property_data().price_multiplier();
|
||||
}
|
||||
out_quantity = Quantity(fills);
|
||||
avg_price=fills==0?0.0:avg_price;//should it be nan?
|
||||
out_quantity = Quantity(fills);
|
||||
out_price = Price(avg_price);
|
||||
}
|
||||
}}
|
||||
@@ -161,16 +161,18 @@ public:
|
||||
void update_book(const Book& book) {
|
||||
SpreaderUtils::copy_book(book,
|
||||
_settlement_price,
|
||||
_leg_property_data.tick_size(),
|
||||
_leg_property_data.md_type(),
|
||||
bts::spark::LegSpreaderParams::BOOK,
|
||||
bts::api::SyntheticContract::PRICECALC_STANDARD,
|
||||
_book);
|
||||
}
|
||||
void update_settlement_price(md::IntPrice price){
|
||||
_settlement_price=price;
|
||||
SpreaderUtils::copy_book(_book,
|
||||
_settlement_price,
|
||||
_leg_property_data.tick_size(),
|
||||
_leg_property_data.md_type(),
|
||||
bts::spark::LegSpreaderParams::BOOK,
|
||||
bts::api::SyntheticContract::PRICECALC_STANDARD,
|
||||
_book);
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ bool SpreaderUtils::resolve_synthetic_spread (const SpreaderPreActivationOrderSe
|
||||
&order_def.custom_def().spreader().security();
|
||||
if (sec_ptr != nullptr){
|
||||
const spark::ExecutionStrategy& execution_strategy = order_def.custom_def().spreader().execution_strategy();
|
||||
const bts::api::Security& sec = *sec_ptr;
|
||||
bts::api::Security sec = *sec_ptr;
|
||||
if (sec.has_synthetic() && sec.synthetic().legs().size() > 0){
|
||||
transform_spread_price_mults(sec);
|
||||
size_t leg_idx = 0;
|
||||
for (auto it = sec.synthetic().legs().begin(); it != sec.synthetic().legs().end(); ++it){
|
||||
const bts::api::ContractLeg& leg = *it;
|
||||
@@ -53,7 +54,7 @@ bool SpreaderUtils::resolve_synthetic_spread (const SpreaderPreActivationOrderSe
|
||||
api::orderman::OrderDefinition hedger_order_def = quoter_order_def;
|
||||
hedger_order_def.set_time_in_force(bts::api::orderman::TimeInForce::TIME_IN_FORCE_DAY);
|
||||
hedger_order_def.set_sub_type(bts::api::orderman::OrderSubType::ORDER_SUB_TYPE_HEDGING_LEG);
|
||||
LegPropertyData leg_property_data(leg_idx, leg, leg_params, leg_order_side);
|
||||
LegPropertyData leg_property_data(leg_idx, leg, leg_params, leg_order_side,sec.synthetic().price_calc());
|
||||
out_resolved.push_back(leg_property_data);
|
||||
out_quoter_defs.push_back(quoter_order_def);
|
||||
out_hedger_defs.push_back(hedger_order_def);
|
||||
@@ -81,7 +82,18 @@ bool SpreaderUtils::resolve_synthetic_spread (const SpreaderPreActivationOrderSe
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SpreaderUtils::transform_spread_price_mults (api::Security& sec){
|
||||
if(sec.has_synthetic() &&
|
||||
sec.synthetic().price_calc()==api::SyntheticContract::PRICECALC_NET_CHANGE){
|
||||
for(int i=0;i<sec.synthetic().legs_size();i++){
|
||||
api::ContractLeg* leg_ptr=sec.mutable_synthetic()->mutable_legs(i);
|
||||
double tick_size=leg_ptr->security().contract_spec().low_tick(),
|
||||
price_mult=leg_ptr->price_multiplier(),
|
||||
new_mult=price_mult/tick_size;
|
||||
leg_ptr->set_price_multiplier(new_mult);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string SpreaderUtils::book_to_str(const bts::orderman::Book& book,
|
||||
api::Side side){
|
||||
std::ostringstream oss;
|
||||
|
||||
@@ -77,6 +77,8 @@ public:
|
||||
std::vector<bts::api::orderman::OrderDefinition>& out_quoter_defs,
|
||||
std::vector<bts::api::orderman::OrderDefinition>& out_hedger_defs,
|
||||
bool resolve_synthetic = true);
|
||||
// for each leg, changes its price_mult to price_mult/leg_tick_size
|
||||
static void transform_spread_price_mults (api::Security& sec);
|
||||
////////////////////////////////////////////////////
|
||||
// orders //////////////////////////////////////////
|
||||
////////////////////////////////////////////////////
|
||||
@@ -167,8 +169,9 @@ public:
|
||||
void static copy_book (
|
||||
const Book& source,
|
||||
md::IntPrice settlement_price,
|
||||
double tick_size,
|
||||
bts::spark::LegSpreaderParams::MarketDataType md_type,
|
||||
bts::spark::LegSpreaderParams::LegMarketDataCalculation leg_calc,
|
||||
bts::api::SyntheticContract::PriceCalc leg_calc,
|
||||
Book& target){
|
||||
SecurityKey target_key=target.security_key();
|
||||
if(md_type==bts::spark::LegSpreaderParams::DIRECT){
|
||||
@@ -197,24 +200,27 @@ public:
|
||||
target.bid()=source.bid();
|
||||
target.ask()=source.ask();
|
||||
}
|
||||
if(leg_calc==bts::spark::LegSpreaderParams::NET_CHANGE){
|
||||
if(leg_calc==bts::api::SyntheticContract::PRICECALC_NET_CHANGE){
|
||||
if(settlement_price.is_nan()){
|
||||
empty_out_book(target.bid(),0,target.n_levels());
|
||||
empty_out_book(target.ask(),0,target.n_levels());
|
||||
}
|
||||
else{
|
||||
offset_book(target.bid(), target.n_levels(),settlement_price);
|
||||
offset_book(target.ask(), target.n_levels(),settlement_price);
|
||||
offset_book_ticks(target.bid(), target.n_levels(),
|
||||
settlement_price, tick_size);
|
||||
offset_book_ticks(target.ask(), target.n_levels(),
|
||||
settlement_price, tick_size);
|
||||
}
|
||||
}
|
||||
bts_assert(target_key==target.security_key());
|
||||
}
|
||||
void static offset_book(BookSide& book_side,
|
||||
size_t n_levels,
|
||||
md::IntPrice offset){
|
||||
void static offset_book_ticks(BookSide& book_side,
|
||||
size_t n_levels,
|
||||
md::IntPrice offset,
|
||||
double tick_size){
|
||||
for(size_t i=0;i<n_levels;i++){
|
||||
if(book_side[i].size()!=0)
|
||||
book_side[i]=book_side[i]-offset;
|
||||
book_side[i]=(book_side[i]-offset).get_divided_price(tick_size);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3215,8 +3215,9 @@ TEST_CASE ("simple book addition", "[spreader]"){
|
||||
Book out_book(key);
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(1999.0),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::DIRECT,
|
||||
bts::spark::LegSpreaderParams::BOOK,
|
||||
bts::api::SyntheticContract::PRICECALC_STANDARD,
|
||||
out_book);
|
||||
|
||||
validate_book(out_book,
|
||||
@@ -3239,8 +3240,9 @@ TEST_CASE ("simple book addition", "[spreader]"){
|
||||
Book out_book(key);
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(1999.0),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::DIRECT,
|
||||
bts::spark::LegSpreaderParams::BOOK,
|
||||
bts::api::SyntheticContract::PRICECALC_STANDARD,
|
||||
out_book);
|
||||
|
||||
validate_book(out_book,
|
||||
@@ -3263,8 +3265,9 @@ TEST_CASE ("simple book addition", "[spreader]"){
|
||||
Book out_book(key);
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(1999.0),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::DIRECT,
|
||||
bts::spark::LegSpreaderParams::NET_CHANGE,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
out_book);
|
||||
|
||||
validate_book(out_book,
|
||||
@@ -3287,8 +3290,9 @@ TEST_CASE ("simple book addition", "[spreader]"){
|
||||
Book out_book(key);
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::INT_NAN,
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::DIRECT,
|
||||
bts::spark::LegSpreaderParams::NET_CHANGE,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
out_book);
|
||||
|
||||
validate_book(out_book,
|
||||
@@ -3311,8 +3315,9 @@ TEST_CASE ("simple book addition", "[spreader]"){
|
||||
Book out_book(key);
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(1999.0),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::MERGED,
|
||||
bts::spark::LegSpreaderParams::BOOK,
|
||||
bts::api::SyntheticContract::PRICECALC_STANDARD,
|
||||
out_book);
|
||||
|
||||
validate_book(out_book,
|
||||
@@ -3335,8 +3340,9 @@ TEST_CASE ("simple book addition", "[spreader]"){
|
||||
Book out_book(key);
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(1999.0),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::MERGED,
|
||||
bts::spark::LegSpreaderParams::NET_CHANGE,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
out_book);
|
||||
|
||||
validate_book(out_book,
|
||||
@@ -3385,8 +3391,9 @@ TEST_CASE ("random book addition", "[spreader]"){
|
||||
{
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(settlement_price),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::DIRECT,
|
||||
bts::spark::LegSpreaderParams::BOOK,
|
||||
bts::api::SyntheticContract::PRICECALC_STANDARD,
|
||||
out_book);
|
||||
validate_book(out_book,
|
||||
key,
|
||||
@@ -3411,8 +3418,9 @@ TEST_CASE ("random book addition", "[spreader]"){
|
||||
}
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(settlement_price),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::DIRECT,
|
||||
bts::spark::LegSpreaderParams::NET_CHANGE,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
out_book);
|
||||
validate_book(out_book,
|
||||
key,
|
||||
@@ -3437,8 +3445,9 @@ TEST_CASE ("random book addition", "[spreader]"){
|
||||
}
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(settlement_price),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::MERGED,
|
||||
bts::spark::LegSpreaderParams::BOOK,
|
||||
bts::api::SyntheticContract::PRICECALC_STANDARD,
|
||||
out_book);
|
||||
validate_book(out_book,
|
||||
key,
|
||||
@@ -3465,8 +3474,9 @@ TEST_CASE ("random book addition", "[spreader]"){
|
||||
}
|
||||
SpreaderUtils::copy_book(in_book,
|
||||
md::IntPrice_from_double<7>(settlement_price),
|
||||
1.0,
|
||||
bts::spark::LegSpreaderParams::MERGED,
|
||||
bts::spark::LegSpreaderParams::NET_CHANGE,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
out_book);
|
||||
validate_book(out_book,
|
||||
key,
|
||||
@@ -3749,6 +3759,182 @@ TEST_CASE ("4legs_hedging", "[spreader_perf]")
|
||||
}
|
||||
test_sleep(10000,true);
|
||||
}
|
||||
// for net change spreads, we
|
||||
// a. transform the spread into a non net change spread
|
||||
// with converted price mults
|
||||
// b. transform the price
|
||||
// this test verifies that the quoting prices after the
|
||||
// the transformations are the same as it would have been
|
||||
// had we dealt in ticks
|
||||
TEST_CASE ("net_change_transformation", "[spreader]")
|
||||
{
|
||||
// hard coded values
|
||||
SECTION("basic"){
|
||||
// HG book
|
||||
double settle1=2.9570,
|
||||
settle2=1.5998,
|
||||
tick1=0.0005,
|
||||
tick2=0.0001;
|
||||
Book b1_raw=get_book(SecurityKey(0),
|
||||
5,
|
||||
new double[5] {2.9570,2.9565,2.9560,2.9555,2.9550},
|
||||
new uint16_t[5] {5,15,5,15,0},
|
||||
new double[5] {2.9580,2.9585,2.9590,2.9595,2.9600},
|
||||
new uint16_t[5] {5,20,10,0,0},
|
||||
true);
|
||||
Book b2_raw=get_book(SecurityKey(1),
|
||||
5,
|
||||
new double[5] {1.5998,1.5996,1.5995,1.5994,1.5993},
|
||||
new uint16_t[5] {5,5,10,0,0},
|
||||
new double[5] {1.5999,1.6000,1.6001,1.6002,1.6003},
|
||||
new uint16_t[5] {10,5,15,5,0},
|
||||
true);
|
||||
// ticks mode :
|
||||
// generate books in ticks
|
||||
Book b1_ticks, b2_ticks;
|
||||
SpreaderUtils::copy_book(b1_raw,
|
||||
md::IntPrice_from_double<7>(settle1),
|
||||
0.0005,
|
||||
spark::LegSpreaderParams::DIRECT,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
b1_ticks);
|
||||
SpreaderUtils::copy_book(b2_raw,
|
||||
md::IntPrice_from_double<7>(settle2),
|
||||
0.0001,
|
||||
spark::LegSpreaderParams::DIRECT,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
b2_ticks);
|
||||
// generate the leg properties accordingly (notice the tick size is 1
|
||||
// since the market is in ticks)
|
||||
LegPropertyData lp1=create_leg_property_data(0,1,1.0,api::Side::SIDE_BUY,1,0,true),
|
||||
lp2=create_leg_property_data(1,-1,-1.0,api::Side::SIDE_SELL,1,1,true);
|
||||
Price out_p; Quantity out_q;
|
||||
SpreaderUtils::compute_price_quantity_2_legs(Price(-2.0),
|
||||
Quantity(5),
|
||||
lp2,
|
||||
lp1,
|
||||
b1_ticks,
|
||||
out_p,
|
||||
out_q);
|
||||
REQUIRE(out_p.value()==4.0);
|
||||
REQUIRE(out_q.value()==5);
|
||||
// raw mode :
|
||||
// transform the spread def, price_mult/=tick_size
|
||||
LegPropertyData lp1_trf=change_ticks_price_mult(lp1,tick1,lp1.price_multiplier()/tick1),
|
||||
lp2_trf=change_ticks_price_mult(lp2,tick2,lp2.price_multiplier()/tick2);
|
||||
// transform the spread price
|
||||
/// transformed price = tick_price + sum(price_ratio*settlement)
|
||||
double price_trf=-2+(lp1_trf.price_multiplier()*settle1)+(lp2_trf.price_multiplier()*settle2);
|
||||
SpreaderUtils::compute_price_quantity_2_legs(Price(price_trf),
|
||||
Quantity(5),
|
||||
lp2_trf,
|
||||
lp1_trf,
|
||||
b1_raw,
|
||||
out_p,
|
||||
out_q);
|
||||
REQUIRE(out_p.value()==1.6002);
|
||||
REQUIRE(out_q.value()==5);
|
||||
}
|
||||
SECTION("random_books"){
|
||||
// HG book
|
||||
double tick1=0.0005,
|
||||
tick2=0.0001;
|
||||
int n_runs=1000;
|
||||
size_t n_levels=5;
|
||||
for(int i=0;i<n_runs;i++){
|
||||
double leg1_qty=gen_rand(1,5,1),
|
||||
leg2_qty=-1.0*gen_rand(1,5,1),
|
||||
leg1_pmult=gen_rand(1,5,0.25),
|
||||
leg2_pmult=-1*gen_rand(1,5,0.25);
|
||||
double settle1=gen_rand(1,3,tick1),
|
||||
settle2=gen_rand(1,3,tick2);
|
||||
double price = bts::securities::raw_round_to_quote_price(gen_rand(-20,20),1.0,api::Side::SIDE_BUY);
|
||||
Book b1_raw=generate_random_book(n_levels,
|
||||
tick1,
|
||||
settle1,
|
||||
settle1+tick1),
|
||||
b2_raw=generate_random_book(n_levels,
|
||||
tick2,
|
||||
settle2,
|
||||
settle2+tick2);
|
||||
// ticks mode :
|
||||
// generate books in ticks
|
||||
Book b1_ticks, b2_ticks;
|
||||
SpreaderUtils::copy_book(b1_raw,
|
||||
md::IntPrice_from_double<7>(settle1),
|
||||
tick1,
|
||||
spark::LegSpreaderParams::MERGED,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
b1_ticks);
|
||||
SpreaderUtils::copy_book(b2_raw,
|
||||
md::IntPrice_from_double<7>(settle2),
|
||||
tick2,
|
||||
spark::LegSpreaderParams::MERGED,
|
||||
bts::api::SyntheticContract::PRICECALC_NET_CHANGE,
|
||||
b2_ticks);
|
||||
// generate the leg properties accordingly (notice the tick size is 1
|
||||
// since the market is in ticks)
|
||||
const LegPropertyData lp1=create_leg_property_data(0,leg1_qty,leg1_pmult,api::Side::SIDE_BUY,1,0,true),
|
||||
lp2=create_leg_property_data(1,leg2_qty,leg2_pmult,api::Side::SIDE_SELL,1,1,true);
|
||||
// raw mode transformations
|
||||
// 1. transform the spread def, price_mult/=tick_size
|
||||
const LegPropertyData lp1_trf=change_ticks_price_mult(lp1,tick1,lp1.price_multiplier()/tick1),
|
||||
lp2_trf=change_ticks_price_mult(lp2,tick2,lp2.price_multiplier()/tick2);
|
||||
// transform the spread price == tick_price + sum(price_ratio*settlement)
|
||||
const double price_trf=price+(lp1_trf.price_multiplier()*settle1)+(lp2_trf.price_multiplier()*settle2);
|
||||
// leg 2 quoting price : ticks, raw
|
||||
{
|
||||
Price q2_price_ticks; Quantity q2_qty_ticks;
|
||||
SpreaderUtils::compute_price_quantity_2_legs(Price(price),
|
||||
Quantity(5),
|
||||
lp2,
|
||||
lp1,
|
||||
b1_ticks,
|
||||
q2_price_ticks,
|
||||
q2_qty_ticks);
|
||||
Price q2_price_raw; Quantity q2_qty_raw;
|
||||
SpreaderUtils::compute_price_quantity_2_legs(Price(price_trf),
|
||||
Quantity(5),
|
||||
lp2_trf,
|
||||
lp1_trf,
|
||||
b1_raw,
|
||||
q2_price_raw,
|
||||
q2_qty_raw);
|
||||
if(!bts::securities::test_price_equality(q2_price_ticks.value()*tick2+settle2,
|
||||
q2_price_raw.value(),
|
||||
tick2)){
|
||||
REQUIRE(q2_price_ticks.value()*tick2+settle2 == q2_price_raw.value());
|
||||
}
|
||||
REQUIRE(q2_qty_ticks.value()==q2_qty_raw.value());
|
||||
}
|
||||
// leg 2 quoting price : ticks, raw
|
||||
{
|
||||
Price q1_price_ticks; Quantity q1_qty_ticks;
|
||||
SpreaderUtils::compute_price_quantity_2_legs(Price(price),
|
||||
Quantity(5),
|
||||
lp1,
|
||||
lp2,
|
||||
b2_ticks,
|
||||
q1_price_ticks,
|
||||
q1_qty_ticks);
|
||||
Price q1_price_raw; Quantity q1_qty_raw;
|
||||
SpreaderUtils::compute_price_quantity_2_legs(Price(price_trf),
|
||||
Quantity(5),
|
||||
lp1_trf,
|
||||
lp2_trf,
|
||||
b2_raw,
|
||||
q1_price_raw,
|
||||
q1_qty_raw);
|
||||
if(!bts::securities::test_price_equality(q1_price_ticks.value()*tick1+settle1,
|
||||
q1_price_raw.value(),
|
||||
tick1)){
|
||||
REQUIRE(q1_price_ticks.value()*tick1+settle1 == q1_price_raw.value());
|
||||
}
|
||||
REQUIRE(q1_qty_ticks.value()==q1_qty_raw.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class RV
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -203,7 +203,11 @@ void _fill_future_security (Security& sec,
|
||||
// (internal function)
|
||||
// gives a security fills it with spread leg data (mults etc)
|
||||
void _fill_contract_leg (ContractLeg& leg,
|
||||
const Security& sec, double leg_quantity, double price_multiplier, Side side, double leg_delta)
|
||||
const Security& sec,
|
||||
double leg_quantity,
|
||||
double price_multiplier,
|
||||
Side side,
|
||||
double leg_delta)
|
||||
{
|
||||
leg.mutable_security()->MergeFrom (sec);
|
||||
leg.set_quantity (leg_quantity);
|
||||
@@ -214,9 +218,16 @@ void _fill_contract_leg (ContractLeg& leg,
|
||||
// (public function)
|
||||
// given the future leg data, fills it in ContractLeg
|
||||
void fill_future_contract_leg (ContractLeg& leg,
|
||||
uint32_t key, const std::string& symbol, Venue exchange,
|
||||
uint32_t year, uint32_t month, uint32_t day,
|
||||
double leg_quantity, double price_multiplier, Side side, double leg_delta,
|
||||
uint32_t key,
|
||||
const std::string& symbol,
|
||||
Venue exchange,
|
||||
uint32_t year,
|
||||
uint32_t month,
|
||||
uint32_t day,
|
||||
double leg_quantity,
|
||||
double price_multiplier,
|
||||
Side side,
|
||||
double leg_delta,
|
||||
double tick_size)
|
||||
{
|
||||
Security sec;
|
||||
@@ -236,7 +247,10 @@ void fill_synthetic_security (Security& out_sec, uint32_t sec_id, const std::str
|
||||
out_sec.mutable_synthetic()->MergeFrom (sc);
|
||||
}
|
||||
LegPropertyData create_leg_property_data (const ContractLeg& leg,
|
||||
size_t leg_index, double price_multiplier, bool is_active_quoting, double lean_mult)
|
||||
size_t leg_index,
|
||||
double price_multiplier,
|
||||
bool is_active_quoting,
|
||||
double lean_mult)
|
||||
{
|
||||
QuoteParams qp;
|
||||
fill_quote_params(qp, lean_mult);
|
||||
@@ -247,6 +261,29 @@ LegPropertyData create_leg_property_data (const ContractLeg& leg,
|
||||
bts::api::orderman::OrderDefinition order_def;
|
||||
return LegPropertyData (leg_index, leg, lsp, leg.side());
|
||||
}
|
||||
LegPropertyData create_leg_property_data (
|
||||
uint32_t key,
|
||||
double leg_quantity,
|
||||
double price_multiplier,
|
||||
Side side,
|
||||
double tick_size,
|
||||
size_t leg_index,
|
||||
bool is_active_quoting){
|
||||
ContractLeg leg;
|
||||
fill_future_contract_leg(leg,key,"X",api::Venue::VENUE_GLBX,2019, 12, 15, leg_quantity,price_multiplier,side,1.0,tick_size);
|
||||
return create_leg_property_data(leg,leg_index,price_multiplier,is_active_quoting,1);
|
||||
}
|
||||
LegPropertyData change_ticks_price_mult(const LegPropertyData& lpd,
|
||||
double new_tick_size,
|
||||
double new_price_mult){
|
||||
return create_leg_property_data(lpd.security_key(),
|
||||
lpd.size_multiplier(),
|
||||
new_price_mult,
|
||||
lpd.trading_side(),
|
||||
new_tick_size,
|
||||
lpd.leg_index(),
|
||||
lpd.is_active_quoting());
|
||||
}
|
||||
SpreaderPreActivationOrderServices::TestOnlySpreaderPreActivationOrderServices _create_spreader_pao_services(const Security& synthetic_security)
|
||||
{
|
||||
std::vector<Security> rv;
|
||||
@@ -601,6 +638,13 @@ double gen_rand (double min, double max)
|
||||
double f = (double)rand() / RAND_MAX;
|
||||
return min + f * (max - min);
|
||||
}
|
||||
double gen_rand (double min, double max, double mult_of)
|
||||
{
|
||||
return bts::securities::raw_round_to_quote_price(
|
||||
gen_rand(min,max),
|
||||
mult_of,
|
||||
api::Side::SIDE_BUY);
|
||||
}
|
||||
void fill_random_driver(QuoteLegDriver& driver){
|
||||
driver.set(Price(gen_rand(0,10)),
|
||||
Quantity(gen_rand(1,100)),
|
||||
@@ -629,12 +673,12 @@ void generate_random_book(size_t n_levels,
|
||||
double b=top_book,
|
||||
a=top_ask;
|
||||
for(size_t i=0;i<n_levels;i++){
|
||||
bid_prices[i]=b;
|
||||
bid_prices[i]= bts::securities::raw_round_to_quote_price(b,tick_size,api::Side::SIDE_BUY);
|
||||
direct_bid_sizes[i]=gen_rand(10,100);
|
||||
implied_bid_sizes[i]=gen_rand(10,100);
|
||||
b-=gen_rand(1,10)*tick_size;
|
||||
|
||||
ask_prices[i]=a;
|
||||
ask_prices[i]=bts::securities::raw_round_to_quote_price(a,tick_size,api::Side::SIDE_SELL);
|
||||
direct_ask_sizes[i]=gen_rand(10,100);
|
||||
implied_ask_sizes[i]=gen_rand(10,100);
|
||||
a+=gen_rand(1,10)*tick_size;
|
||||
@@ -646,6 +690,25 @@ void generate_random_book(size_t n_levels,
|
||||
ask_prices,direct_ask_sizes,implied_ask_sizes,
|
||||
bts::api::Side::SIDE_SELL,false);
|
||||
}
|
||||
Book generate_random_book(size_t n_levels,
|
||||
double tick_size,
|
||||
double top_book,
|
||||
double top_ask){
|
||||
double* bid_prices=new double[n_levels];
|
||||
uint16_t* direct_bid_sizes=new uint16_t[n_levels];
|
||||
uint16_t* implied_bid_sizes=new uint16_t[n_levels];
|
||||
double* ask_prices=new double[n_levels];
|
||||
uint16_t* direct_ask_sizes=new uint16_t[n_levels];
|
||||
uint16_t* implied_ask_sizes=new uint16_t[n_levels];
|
||||
Book rv;
|
||||
generate_random_book(n_levels,tick_size,top_book,top_ask,
|
||||
bid_prices,direct_bid_sizes,implied_bid_sizes,
|
||||
ask_prices,direct_ask_sizes,implied_ask_sizes,
|
||||
rv);
|
||||
delete[] bid_prices;delete[] direct_bid_sizes;delete[] implied_bid_sizes;
|
||||
delete[] ask_prices;delete[] direct_ask_sizes;delete[] implied_ask_sizes;
|
||||
return rv;
|
||||
}
|
||||
void stats (std::vector<double> p,
|
||||
double& min,
|
||||
double& max,
|
||||
|
||||