Merged from master, note that SpreaderOrderVM.cs was not merged yet, so this client build will fail

This commit is contained in:
Pedro Pinto committed 2019-03-15 14:41:02 -04:00
commit e998edc20f
108 files changed
+1297 -570

No files matched your search

+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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})
+15 -3
View File
@@ -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
+2 -2
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

+1 -1
View File
@@ -25,7 +25,7 @@ insert_anchor_links = "right"
which will list what version you are running as well as who is
currently logged in.
![](image32.png)
![](about_spark.png)
## Activity log
This is an overall log viewer which will show
Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

+20 -16
View File
@@ -51,7 +51,7 @@ insert_anchor_links = "right"
securities you have chosen, in this example, it appears like the
following:
![](image63.png)
![](market_view.png)
- 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 ![](image12.png)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 ![](change_securities.png) 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
![](image23.png) 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
![](image69.png) 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.
![](image30.png)
![](time_and_sales.png)
- 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 ![](settings_icon.png) 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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

+137 -40
View File
@@ -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:
- ![](image4.png)
![](spark_menu_order_book.png)
- ![](image48.png)Fills -
clicking on this will launch a fills window
@@ -79,6 +79,18 @@ insert_anchor_links = "right"
- ![](image2.png)Cancel
all - clicking on this will cancel all working orders you have the
order book
- ![](recreate_icon.png) 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_icon.png) Collapse all - This will collapse all trees inside the order book to show only the top level of the order.
- ![](expand_all_icon.png) Expand all - This will expand to show everything inside the order book.
- ![](settings_icon.png) 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:
![](mini_orderbook.png)
## 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:
![](image67.png)
![](pnl_calculation.png)
- Below is an example P&L window showing a user’s multiple accounts and
various positions in each:
![](pnl_window.png)
## Ladder
@@ -125,7 +149,7 @@ insert_anchor_links = "right"
market with a single click
- Below is an example ladder:
![](image36.png)
![](ladder.png)
- 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"
         
![](image50.png)
- 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
![](image22.png?v2)
![](lean_algo_control.png)
- 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.
![](image41.png)
![](join_algo_control.png)
- 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
![](top_algo_control.png)
![](image26.png)
- 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.
![](offset_algo_control.png)
- 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.
![](oco_algo_control.png)
- 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.
![](cover_algo_control.png)
- 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.
![](trigger_algo_control.png)
- 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
![](ladder_settings.png)
- 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.
![](image71.png)
- The other, main part of the ladder, is the ladder itself as
displayed below
![](image65.png)
![](ladder_main_part.png)
- 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
![](image46.png)
![](ladder_badges.png)
- 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:
![](algo_studio.png)
- 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)
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+11
View File
@@ -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
+197 -223
View File
@@ -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();
});
}
+6 -1
View File
@@ -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 {
+9 -9
View File
@@ -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}}
+1
View File
@@ -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];
}
//
+3 -3
View File
@@ -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;
}
}
+34 -35
View File
@@ -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;
+6 -3
View File
@@ -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() };
}
}
+9
View File
@@ -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) {
+5
View File
@@ -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
+14 -7
View File
@@ -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);
+3
View File
@@ -15,6 +15,9 @@ struct Co2TestInstrument
MDProducerHandle producer();
MDConsumerHandle consumer();
ColdEntry& cold() { return *_cold; }
private:
std::shared_ptr<uint8_t> _cold_buffer;
ColdEntry* _cold;
+6
View File
@@ -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);
}
}
+32
View File
@@ -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
+67 -22
View File
@@ -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}});
}
}
+45 -28
View File
@@ -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)
+4 -1
View File
@@ -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;
};
+1 -1
View File
@@ -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;
+6 -3
View File
@@ -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,
+11 -6
View File
@@ -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();
}
+3 -1
View File
@@ -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();
}
+3 -1
View File
@@ -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();
+2 -2
View File
@@ -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,
Loaded 100 of 108 files, more files were not shown because too many files have changed in this diff. Show more