Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
575195c67a | ||
|
|
9c20fe892e | ||
|
|
4b626a160d | ||
|
|
bfef852de7 | ||
|
|
dbb859cce9 | ||
|
|
abb451349c | ||
|
|
e77ecaa48c | ||
|
|
1895a75bc7 | ||
|
|
f9357d0860 | ||
|
|
f7bd209efa | ||
|
|
f794ffdace | ||
|
|
504323d5b7 | ||
|
|
9450a952d4 | ||
|
|
62394d0e71 | ||
|
|
8d9697d06d | ||
|
|
d9e967610e | ||
|
|
2cfbd655cb | ||
|
|
3ed5e5275e | ||
|
|
d997aeca1e | ||
|
|
3b371f3588 | ||
|
|
b68aaa0a00 | ||
|
|
36e58378e4 | ||
|
|
0ef2378dc9 | ||
|
|
d1d4a1bfb7 | ||
|
|
e45ee1730e | ||
|
|
6bd3c6b0ca | ||
|
|
6f65a958ae | ||
|
|
53a4ab4974 | ||
|
|
6e4ed3f328 | ||
|
|
4a292c8030 | ||
|
|
29fbfd4479 | ||
|
|
500e5d508a | ||
|
|
396ac023c4 | ||
|
|
e32f40caee | ||
|
|
47e7d69897 | ||
|
|
4f830cdd40 | ||
|
|
00e9de3b1e | ||
|
|
ddb4e55d13 | ||
|
|
c2dfcbe343 | ||
|
|
582fffb1f4 | ||
|
|
e67756d055 | ||
|
|
141e1a4ea0 | ||
|
|
097db2f340 | ||
|
|
b49c764c54 | ||
|
|
87218962e1 | ||
|
|
88d8248f63 | ||
|
|
ffbda10200 | ||
|
|
7eb8b21cea | ||
|
|
986a791801 | ||
|
|
c834a6e840 | ||
|
|
f14c36223f | ||
|
|
d66c8e0ad8 | ||
|
|
8673f366d7 | ||
|
|
88e48ebcf7 | ||
|
|
1a6a10a834 | ||
|
|
d780e2aab2 | ||
|
|
a4bc34e633 | ||
|
|
d9b37efbbd | ||
|
|
d23b7942b6 | ||
|
|
f41c3a5334 | ||
|
|
d8e8f90131 | ||
|
|
0358afccf9 | ||
|
|
9f58327d0a | ||
|
|
d3ed6e21e4 | ||
|
|
de83f19388 | ||
|
|
4d993f2ef7 | ||
|
|
ca81be3865 | ||
|
|
19ae05cb7d | ||
|
|
79ec41556e | ||
|
|
088377582d | ||
|
|
c8b3973b8f | ||
|
|
82a2ba4a86 | ||
|
|
ccbe20eb57 | ||
|
|
70a2feb92a | ||
|
|
a4d542067a | ||
|
|
efd222133e | ||
|
|
361491ebd2 | ||
|
|
6aeba4f599 | ||
|
|
b35dae9fab | ||
|
|
9d96fcaa45 | ||
|
|
523f02b4c2 | ||
|
|
edb4ed02c9 | ||
|
|
96da8e3276 | ||
|
|
c056bfcf7a | ||
|
|
2ddf1a1762 | ||
|
|
8a9ddfded7 | ||
|
|
a83f97e1ef | ||
|
|
3630bf5b9a | ||
|
|
b9d3a4d04c | ||
|
|
c7fb636e49 | ||
|
|
c7ea6c4ca4 | ||
|
|
1ffda9c413 | ||
|
|
e2f314d007 | ||
|
|
80d8307822 | ||
|
|
1ca5ed4520 | ||
|
|
8115f9df74 | ||
|
|
306cbaa837 | ||
|
|
efa31059d9 | ||
|
|
73089634f9 | ||
|
|
a84e5e3460 | ||
|
|
eca11d0317 | ||
|
|
ced485856e | ||
|
|
6c0cc76773 | ||
|
|
4d53bfb3f0 | ||
|
|
717391afb3 | ||
|
|
38e0d40364 | ||
|
|
7053c2ee03 | ||
|
|
cdde730fc5 | ||
|
|
95b0119d22 | ||
|
|
07a965a3e0 | ||
|
|
eba153e289 | ||
|
|
fc5238c56f | ||
|
|
bc367b9f56 | ||
|
|
8adcc98ff1 | ||
|
|
ba07041c26 | ||
|
|
d3101b1cd8 | ||
|
|
3a9a86020b | ||
|
|
42afdc2994 | ||
|
|
6c93208e11 | ||
|
|
bf1ca4e5fa | ||
|
|
3f9e64016b | ||
|
|
c672967caf | ||
|
|
1e0dac0fbc | ||
|
|
aec8de54bd | ||
|
|
a960093cf6 | ||
|
|
4601e49106 | ||
|
|
7e65db38cf | ||
|
|
d70bd63659 | ||
|
|
246f9240e7 | ||
|
|
a8ebb9cdcb | ||
|
|
bdb2d6cf9e | ||
|
|
1966c8e0c6 | ||
|
|
a418f47d1c | ||
|
|
df516fbd2d | ||
|
|
b8e298c524 | ||
|
|
3233187fd9 | ||
|
|
02d1badf0d | ||
|
|
b69958007d | ||
|
|
f433a3163b | ||
|
|
bb82590e5e | ||
|
|
1855fe3045 | ||
|
|
e3dba306a7 | ||
|
|
74f79a2f52 | ||
|
|
2c384af157 | ||
|
|
99fd77c1f1 | ||
|
|
7b0e3edfcf | ||
|
|
99b71ad2aa | ||
|
|
58c5eeb6d1 | ||
|
|
cbb638ff7a | ||
|
|
9cd2518853 | ||
|
|
2db8d717d4 | ||
|
|
df56c85e7b | ||
|
|
fb12d429cb |
No files matched your search
Vendored
+2
-1
@@ -209,7 +209,8 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": ["test","Co2RelayTests"],
|
||||
// "args": ["test","Co2RelayTests"],
|
||||
"args": ["md", "run", "10"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;/home/ppinto/Dropbox/bts/spark/doc/user/mkdocs.yml;bts-help-theme/404.html;bts-help-theme/base.html;bts-help-theme/content.html;bts-help-theme/css/base.css;bts-help-theme/css/bts.css;bts-help-theme/css/font-awesome.min.css;bts-help-theme/css/highlight.css;bts-help-theme/css/local.css;bts-help-theme/fonts/fontawesome-webfont.eot;bts-help-theme/fonts/glyphicons-halflings-regular.eot;bts-help-theme/img/favicon.ico;bts-help-theme/js/base.js;bts-help-theme/js/bootstrap-3.0.3.min.js;bts-help-theme/js/highlight.pack.js;bts-help-theme/js/jquery-1.10.2.min.js;bts-help-theme/keyboard-modal.html;bts-help-theme/main.html;bts-help-theme/nav-sub.html;bts-help-theme/nav.html;bts-help-theme/search-modal.html;bts-help-theme/toc.html;docs/Help.md;docs/Launcher.md;docs/Logging-In-to-the-System.md;docs/Market.md;docs/My-Windows.md;docs/Settings.md;docs/Spreader.md;docs/Trading.md;docs/images/image1.png;docs/images/image10.png;docs/images/image11.png;docs/images/image12.png;docs/images/image13.png;docs/images/image14.png;docs/images/image15.png;docs/images/image16.png;docs/images/image17.png;docs/images/image18.png;docs/images/image19.png;docs/images/image2.png;docs/images/image20.png;docs/images/image21.png;docs/images/image22.png;docs/images/image23.png;docs/images/image24.png;docs/images/image25.png;docs/images/image26.png;docs/images/image27.png;docs/images/image28.png;docs/images/image29.png;docs/images/image3.png;docs/images/image30.png;docs/images/image31.png;docs/images/image32.png;docs/images/image33.png;docs/images/image34.png;docs/images/image35.png;docs/images/image36.png;docs/images/image37.png;docs/images/image38.png;docs/images/image39.png;docs/images/image4.png;docs/images/image40.png;docs/images/image41.png;docs/images/image42.png;docs/images/image43.png;docs/images/image44.png;docs/images/image45.png;docs/images/image46.png;docs/images/image47.png;docs/images/image48.png;docs/images/image49.png;docs/images/image5.png;docs/images/image50.png;docs/images/image51.png;docs/images/image52.png;docs/images/image53.png;docs/images/image54.png;docs/images/image55.png;docs/images/image56.png;docs/images/image57.png;docs/images/image58.png;docs/images/image59.png;docs/images/image6.png;docs/images/image60.png;docs/images/image61.png;docs/images/image62.png;docs/images/image63.png;docs/images/image64.png;docs/images/image65.png;docs/images/image66.png;docs/images/image67.png;docs/images/image68.png;docs/images/image69.png;docs/images/image7.png;docs/images/image70.png;docs/images/image71.png;docs/images/image72.png;docs/images/image73.png;docs/images/image74.png;docs/images/image76.png;docs/images/image8.png;docs/images/image9.png;docs/index.md;docs/release_notes.md)
|
||||
@@ -16,6 +16,7 @@ set(DOC_GLOBS
|
||||
favicon.ico
|
||||
)
|
||||
file (GLOB_RECURSE All_Deps RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${DOC_GLOBS})
|
||||
set (All_Deps ${All_Deps} ${CMAKE_CURRENT_SOURCE_DIR}/build.sh ${CMAKE_CURRENT_SOURCE_DIR}/mkdocs.yml)
|
||||
update_deps_file("${All_Deps}")
|
||||
# END DEPENDENCIES
|
||||
|
||||
|
||||
+10
-1
@@ -1,2 +1,11 @@
|
||||
#!/bin/bash
|
||||
python -m mkdocs build -d $1
|
||||
tmpfile=$(mktemp /tmp/quiet_build_log.XXXX)
|
||||
python -m mkdocs build -d $1 >$tmpfile 2>&1
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
rm $tmpfile
|
||||
exit 0
|
||||
else
|
||||
cat $tmpfile
|
||||
exit 1
|
||||
fi
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The shortcut icon will appear on your desktop like below:
|
||||
|
||||

|
||||

|
||||
|
||||
Simply double click or single click and hit enter to launch the
|
||||
login screen:
|
||||
|
||||
+10
-17
@@ -54,22 +54,22 @@
|
||||
|
||||

|
||||
|
||||
- ][Fills -
|
||||
- Fills -
|
||||
clicking on this will launch a fills window
|
||||
- ][ Modify -
|
||||
-  Modify -
|
||||
clicking on this will launch an order ticket for a live order
|
||||
highlighted in the Order Book.
|
||||
- ][Cancel
|
||||
- Cancel
|
||||
selected - clicking on this will cancel the selected orders you have
|
||||
highlighted in the order book. You can choose more than one by
|
||||
holding hold the Ctrl key when click on each row of orders
|
||||
- ][Cancel
|
||||
- Cancel
|
||||
buys - clicking on this will cancel all the buy orders you have in
|
||||
the order book
|
||||
- ][Cancel
|
||||
- Cancel
|
||||
sells - clicking on this will cancel all the sell orders you have in
|
||||
the order book
|
||||
- ][Cancel
|
||||
- Cancel
|
||||
all - clicking on this will cancel all working orders you have the
|
||||
order book
|
||||
|
||||
@@ -168,10 +168,10 @@
|
||||
- Iceberg
|
||||
|
||||
- This is a buy or sell order where the order is divided into a
|
||||
visible or “show” portion which is seen at the exchange. ][The
|
||||
visible or “show” portion which is seen at the exchange. The
|
||||
rest of the order is hidden and will remain hidden until the show
|
||||
quantity is filled, essentially reloading the order back to the show
|
||||
quantity. You will need to ][define what type of order that will
|
||||
quantity. You will need to define what type of order that will
|
||||
be used for the child order to be sent.
|
||||
|
||||

|
||||
@@ -198,7 +198,7 @@
|
||||
|
||||

|
||||
|
||||
- Tick
|
||||
- Lean
|
||||
|
||||
- This is a specialized algo order which allows the user to
|
||||
essentially watch the opposite-inside market size and cross when
|
||||
@@ -207,16 +207,9 @@
|
||||
buying into the asks. This is often referred to as “paying up” as
|
||||
well
|
||||
|
||||

|
||||

|
||||
|
||||
- Lean
|
||||
|
||||
- This is a specialized algo order which allows the user to enter an
|
||||
order which will watch the opposite-inside market size and cross
|
||||
when that size goes below the threshold defined in the
|
||||
parameters
|
||||
|
||||

|
||||
|
||||
- Join
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 11 KiB |
@@ -1,5 +1,37 @@
|
||||
# Release Notes
|
||||
|
||||
## 1.4.1
|
||||
|
||||
### Tabs
|
||||
- Tabs now shrink and expand to make better use of horizontal space
|
||||
### Bug Fixes
|
||||
- Fixed a bug that could cause excessive CPU usage when a connection broke
|
||||
- Fixed a layout bug in hedging/requoting performance reports
|
||||
- Fixed account name mismatching bug for old fills
|
||||
### Absolute Position limits
|
||||
- Positions can now be netted or absolute for purposes of limit enforcing
|
||||
|
||||
## 1.4.0
|
||||
|
||||
### Speed Enhancements
|
||||
- Improved requoting and hedging performance
|
||||
- Improved order book form performance
|
||||
### Server Side SDK
|
||||
- Added beta version of the server side SDK
|
||||
### Front End
|
||||
- Added status bars to all UI grids
|
||||
### Reporting
|
||||
- Added reporting of hedging and requoting performance
|
||||
|
||||
## 1.3.8
|
||||
|
||||
### Speed Enhancements
|
||||
- Further speed enhancements
|
||||
### Conditional (new quoting parameter)
|
||||
- Reduces re-quoting updates by only quoting if N ticks away from the opposite inside market.
|
||||
- Quotes are cancelled when it gets further away.
|
||||
- "Price Type" dictates if this logic applies to the quoting market or to the synthetic spread market.
|
||||
|
||||
## 1.3.7
|
||||
|
||||
### Net Change pricing
|
||||
@@ -130,4 +162,4 @@
|
||||
- PnL report now has PnL columns calculated based on your start of day, current day, and total position
|
||||
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
|
||||
- Corrected cash per point values, used in pnl calculations, for a number of products
|
||||
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
|
||||
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
|
||||
+1
-1
Submodule docker/linux updated: aedfd79e02...8ee9692597.
@@ -62,7 +62,7 @@ namespace bts.perf {
|
||||
if (response.Status == IPStatus.Success) {
|
||||
_current_missed_ping_count = 0;
|
||||
if (response.RoundtripTime > config.logging_milliseconds_threshold) {
|
||||
Log.Ui.warn($"Ping response not recieved in a timely manner:\n" +
|
||||
Log.Ui.warn($"Ping response not received in a timely manner:\n" +
|
||||
$"\tAddress:\t{response.Address}\n" +
|
||||
$"\tRoundTrip:\t{response.RoundtripTime} ms");
|
||||
}
|
||||
|
||||
@@ -48,14 +48,12 @@
|
||||
</c:DIYGrid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Securities: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding ElementName=_grid, Path=Rows.Count}"/>
|
||||
</Grid>
|
||||
<TextBlock>
|
||||
<Run Text="Securities:"/>
|
||||
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
|
||||
<Run Text="of"/>
|
||||
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace mbts.spark {
|
||||
var update_securities_cmd = SparkCommands.change_securities_callback(_window_type_display_name);
|
||||
_commands = new List<SparkCommand> {
|
||||
SparkCommands.ShowLadders.complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ObjectHelper.ValueOrDefault),
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
SparkCommands.ShowFills.complete(_vm.selected_securities_observable(), _ => true,
|
||||
secs => new FillsWindowFactory.StartupData(new FillsWindowFactory.FilterStates {
|
||||
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
|
||||
|
||||
@@ -49,13 +49,7 @@
|
||||
</c:DIYGrid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text=" "/>
|
||||
</Grid>
|
||||
<TextBlock Text=" "/>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
|
||||
@@ -62,8 +62,8 @@ namespace mbts.spark {
|
||||
var current_smd_source = connectable_builder.create(vm => vm.current_security_md_observable(_bus)).OnDispatcher();
|
||||
var selected_secs_source = connectable_builder.create(vm => vm.selected_securities_observable()).OnDispatcher();
|
||||
_commands = new SparkCommand[] {
|
||||
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
|
||||
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
|
||||
SparkCommands.ShowFills.complete(selected_secs_source, always_true, get_fills_startup_data),
|
||||
SparkCommands.ShowMarketView.complete(selected_secs_source, always_true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
|
||||
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
UIntTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
MarketDataTypeTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
MarketDataCalculationTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
ConditionalPriceTypeTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
>
|
||||
<usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
|
||||
<DataTemplate>
|
||||
@@ -122,6 +123,11 @@
|
||||
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:LegSpreaderParams.Types.LegMarketDataCalculation}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.MarketDataCalculationTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:ConditionalQuoting.Types.PriceType}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
|
||||
<DataTemplate>
|
||||
<Grid IsEnabled="{Binding enabled}">
|
||||
|
||||
@@ -304,6 +304,7 @@ namespace mbts.ui.spark {
|
||||
public DataTemplate QuantityMeasureTemplate { get; set; }
|
||||
public DataTemplate MarketDataTypeTemplate { get; set; }
|
||||
public DataTemplate MarketDataCalculationTemplate { get; set; }
|
||||
public DataTemplate ConditionalPriceTypeTemplate { get; set; }
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
Type t = (Type)value;
|
||||
@@ -314,6 +315,7 @@ namespace mbts.ui.spark {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,11 @@ namespace bts.shell {
|
||||
OutsideTicks = 3,
|
||||
Priority = RulePriority
|
||||
};
|
||||
public static ConditionalQuoting ConditionalQuoting => new ConditionalQuoting
|
||||
{
|
||||
NTicks = 2,
|
||||
PriceType = ConditionalQuoting.Types.PriceType.Leg
|
||||
};
|
||||
public static QuoteInsideMarketDistanceWithTravel QuoteInsideMarketDistanceWithTravel => new QuoteInsideMarketDistanceWithTravel { Priority = RulePriority };
|
||||
public static QuoteInsideMarketDistance QuoteInsideMarketDistance => new QuoteInsideMarketDistance { Priority = RulePriority };
|
||||
#endregion
|
||||
|
||||
@@ -46,14 +46,12 @@
|
||||
</c:DIYGrid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Synthetics: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding ElementName=_grid, Path=Rows.Count}"/>
|
||||
</Grid>
|
||||
<TextBlock>
|
||||
<Run Text="Synthetics:"/>
|
||||
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
|
||||
<Run Text="of"/>
|
||||
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace mbts.spark {
|
||||
current_row_connectable_builder.create(observe_row_strategy),
|
||||
rs => rs.HasValue, select_row_strategy_tuple
|
||||
), SparkCommands.ShowLadder.complete(
|
||||
vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault
|
||||
vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ConvertUtils.ValueOrDefault
|
||||
), SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData {
|
||||
filters = new OrderBookWindowFactory.FilterStates {
|
||||
security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security))
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class EnableHedgeModel : RowModelBase<bool> {
|
||||
public override string display_name { get; } = "Enable Hedging";
|
||||
public override string help_text { get; } = "Send a hedge for this leg?";
|
||||
public override string man_page { get; } = string.Empty;
|
||||
|
||||
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.EnableHedging;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) =>
|
||||
leg_tup.leg_params.EnableHedging = value;
|
||||
|
||||
public override bool has_errors(IReadOnlyCollection<LegSpreaderParamsTuple> leg_tups) =>
|
||||
!leg_tups.Any(lt => lt.leg_params.EnableHedging);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using mbts.ui.spark;
|
||||
using System;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class MinLeanQtyModel : RowModelBase<System.UInt32> {
|
||||
public override string display_name { get; } = "Min Lean Qty";
|
||||
public override string help_text { get; } = "Minimum Quantity required at this leg to quote";
|
||||
public override string man_page { get; } = string.Empty;
|
||||
|
||||
public override System.UInt32 get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.QuoteParams.MinLeanQty;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, System.UInt32 value) =>
|
||||
leg_tup.leg_params.QuoteParams.MinLeanQty = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using bts.shell;
|
||||
using mbts.ui.spark;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class ConditionalQuoteEnabledModel : RowModelBase<bool> {
|
||||
readonly ConditionalQuotingTicksModel _ticks;
|
||||
readonly ConditionalQuotingPriceTypeModel _price_type;
|
||||
|
||||
public ConditionalQuoteEnabledModel(ConditionalQuotingTicksModel ticks, ConditionalQuotingPriceTypeModel price_type)
|
||||
{
|
||||
_ticks = ticks;
|
||||
_price_type = price_type;
|
||||
}
|
||||
|
||||
public override string display_name { get; } = string.Empty;
|
||||
public override string help_text { get; } = string.Empty;
|
||||
public override string man_page { get; } = string.Empty;
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
|
||||
|
||||
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.QuoteParams.ConditionalQuoting != null;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
|
||||
if (value) {
|
||||
if (leg_tup.leg_params.QuoteParams.ConditionalQuoting == null) {
|
||||
leg_tup.leg_params.QuoteParams.ConditionalQuoting = SyntheticDataHelper.Defaults.ConditionalQuoting;
|
||||
_ticks.apply_value(leg_tup);
|
||||
_price_type.apply_value(leg_tup);
|
||||
}
|
||||
} else {
|
||||
leg_tup.leg_params.QuoteParams.ConditionalQuoting = null;
|
||||
}
|
||||
_ticks.set_enabled(leg_tup, value);
|
||||
_price_type.set_enabled(leg_tup, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
using mbts.ui.spark;
|
||||
using bts.spark;
|
||||
using PriceType = bts.spark.ConditionalQuoting.Types.PriceType;
|
||||
using mbts.collections.extensions;
|
||||
using System.Collections.Generic;
|
||||
using bts.shell;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
partial class SyntheticEditorVM
|
||||
{
|
||||
class ConditionalQuotingPriceTypeModel : RowModelBase<PriceType>
|
||||
{
|
||||
IDictionary<uint, PriceType> _value_store = new Dictionary<uint, PriceType>();
|
||||
|
||||
public override string display_name { get; } = "Price Type";
|
||||
public override string help_text { get; } = string.Empty;
|
||||
public override string man_page { get; } = string.Empty;
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
|
||||
|
||||
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_value(leg_tup).PriceType = _value_from_store(leg_tup);
|
||||
|
||||
public override PriceType get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_value(leg_tup) is ConditionalQuoting cond ?
|
||||
cond.PriceType :
|
||||
_value_from_store(leg_tup);
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, PriceType value)
|
||||
{
|
||||
_value_store[leg_tup.SecurityKey] = value;
|
||||
if (_get_value(leg_tup) is ConditionalQuoting cond)
|
||||
cond.PriceType = value;
|
||||
}
|
||||
|
||||
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) => _get_value(leg_tup) != null; //TAHA_SPREADER
|
||||
|
||||
ConditionalQuoting _get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
|
||||
|
||||
PriceType _value_from_store(LegSpreaderParamsTuple leg_tup) =>
|
||||
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.PriceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class ConditionalQuotingTicksModel : RowModelBase<int> {
|
||||
IDictionary<uint, int> _value_store = new Dictionary<uint, int>();
|
||||
|
||||
public override string display_name { get; } = "Ticks";
|
||||
public override string help_text { get; } = string.Empty;
|
||||
public override string man_page { get; } = string.Empty;
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
|
||||
|
||||
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_value(leg_tup).NTicks = _value_from_store(leg_tup);
|
||||
|
||||
public override int get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_value(leg_tup) is ConditionalQuoting cond ?
|
||||
cond.NTicks :
|
||||
_value_from_store(leg_tup);
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, int value) {
|
||||
_value_store[leg_tup.SecurityKey] = value;
|
||||
if (_get_value(leg_tup) is ConditionalQuoting cond)
|
||||
cond.NTicks = value;
|
||||
}
|
||||
|
||||
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_value(leg_tup) != null;
|
||||
|
||||
ConditionalQuoting _get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
|
||||
|
||||
int _value_from_store(LegSpreaderParamsTuple leg_tup) =>
|
||||
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.NTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,13 @@ namespace mbts.spark {
|
||||
"Reduces re-quoting updates by defining a region on either side " +
|
||||
"of the working quoting order, inside which quote updates will not be sent.");
|
||||
|
||||
public static ParameterSection Conditional =>
|
||||
new ParameterSection("Conditional", true,
|
||||
"Reduces re-quoting updates by only quoting if N ticks away from the opposite " +
|
||||
"inside market. Quotes are cancelled when it gets further away. 'Price Type' " +
|
||||
"dictates if this logic applies to the quoting market or to the synthetic spread " +
|
||||
"market");
|
||||
|
||||
public static ParameterSection InsideMarketThrottleThresholds =>
|
||||
new ParameterSection("Inside Market Throttle Thresholds", true,
|
||||
"Reduces re-quoting for quotes that are moving toward the inside market. " +
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.spark;
|
||||
@@ -110,7 +109,7 @@ namespace mbts.spark {
|
||||
_validate_leg_count();
|
||||
};
|
||||
|
||||
async Task commit_async(){
|
||||
async Task commit_async() {
|
||||
RequestUpdate?.Invoke();
|
||||
bool valid = true;
|
||||
if (string.IsNullOrWhiteSpace(name)) {
|
||||
@@ -125,8 +124,7 @@ namespace mbts.spark {
|
||||
valid = false;
|
||||
}
|
||||
if (valid) {
|
||||
_synthetic.Synthetic.Legs.Clear();
|
||||
_synthetic.Synthetic.Legs.AddRange(leg_tuples.Select(l => l.leg).OrderBy(l => l.Security, SecurityHelper.Comparer.Instance));
|
||||
_synthetic.Synthetic.Legs.SetItems(leg_tuples.Select(l => l.leg));
|
||||
_synthetic.Symbol = _deduce_symbol(_synthetic);
|
||||
double tick_frac = _tick_factors.tick_unit / _tick_factors.tick_base;
|
||||
_synthetic.ContractSpec.LowTick = tick_frac;
|
||||
@@ -180,6 +178,9 @@ namespace mbts.spark {
|
||||
var quote_throttle_outside_ticks = new QuoteThrottleOutsideTicksModel();
|
||||
var quote_throttle_inside_ticks = new QuoteThrottleInsideTicksModel();
|
||||
|
||||
var conditional_quoting_ticks = new ConditionalQuotingTicksModel();
|
||||
var conditional_quoting_price_type = new ConditionalQuotingPriceTypeModel();
|
||||
|
||||
var quote_inside_market_throttle_ticks = new QuoteInsideMarketThrottleTicksModel();
|
||||
var quote_inside_market_throttle_min_move = new QuoteInsideMarketThrottleMinMoveModel();
|
||||
|
||||
@@ -201,13 +202,18 @@ namespace mbts.spark {
|
||||
_quantity_model,
|
||||
_price_mult_model,
|
||||
new IsQuoteModel(),
|
||||
//new EnableHedgeModel(), //TAHA_SPREADER
|
||||
new HedgeRatioModel(),
|
||||
//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,
|
||||
new ConditionalQuoteEnabledModel(conditional_quoting_ticks, conditional_quoting_price_type),
|
||||
conditional_quoting_ticks,
|
||||
conditional_quoting_price_type,
|
||||
//new QuoteInsideMarketThrottleEnabledModel(quote_inside_market_throttle_ticks, quote_inside_market_throttle_min_move),
|
||||
//quote_inside_market_throttle_ticks,
|
||||
//quote_inside_market_throttle_min_move,
|
||||
@@ -296,7 +302,6 @@ namespace mbts.spark {
|
||||
|
||||
static IEnumerable<LegSpreaderParamsTuple> _get_legs(Security synthetic, ExecutionStrategyImmutable strategy) =>
|
||||
synthetic.Synthetic.Legs
|
||||
.OrderBy(l => l.Security, SecurityHelper.Comparer.Instance)
|
||||
.Select(l => new LegSpreaderParamsTuple(l, strategy.LegParamsMap[l.Security.Id].Clone()));
|
||||
|
||||
void _close_new_leg_popup() {
|
||||
@@ -339,4 +344,4 @@ namespace mbts.spark {
|
||||
else clear_error(nameof(leg_count));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,14 +49,12 @@
|
||||
</c:DIYGrid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Fills: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding ElementName=_grid, Path=Rows.Count}"/>
|
||||
</Grid>
|
||||
<TextBlock>
|
||||
<Run Text="Fills:"/>
|
||||
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
|
||||
<Run Text="of"/>
|
||||
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
|
||||
@@ -423,7 +423,8 @@ namespace mbts.spark {
|
||||
base.InsertItem(index, item);
|
||||
} catch {
|
||||
_unhook(col);
|
||||
} finally { }
|
||||
throw;
|
||||
}
|
||||
} else {
|
||||
Log.Ui.warn($"{nameof(OrderBookColumnCollection)} added a column that is not of type {nameof(ICVOrderBookColumn)}");
|
||||
base.InsertItem(index, item);
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace mbts.spark {
|
||||
if (old_order.order_type() != order.order_type())
|
||||
RaisePropertyChanged(nameof(order_type), nameof(order_metadata));
|
||||
if (!had_security && _security_md.HasValue) {
|
||||
RaisePropertyChanged(nameof(security));
|
||||
RaisePropertyChanged(nameof(security), nameof(security_text));
|
||||
// need to raise invalidated to refresh the price cells (the security.ContractSpec determines how they are formatted - Ticks!)
|
||||
invalidate = true;
|
||||
}
|
||||
|
||||
@@ -155,18 +155,12 @@
|
||||
</c:DIYGrid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Orders: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding ElementName=_grid, Path=Rows.Count}"/>
|
||||
<TextBlock Grid.Column="2" Text=" of "/>
|
||||
<TextBlock Grid.Column="3" Text="{Binding rows_view.SourceCollection.Count}"/>
|
||||
</Grid>
|
||||
<TextBlock>
|
||||
<Run Text="Orders:"/>
|
||||
<Run Text="{Binding rows.Count, Mode=OneWay}" />
|
||||
<Run Text="of"/>
|
||||
<Run Text="{Binding rows.total_row_count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace mbts.spark {
|
||||
// data cache
|
||||
readonly Dictionary<uint, OrderRecordTuple> _state = new Dictionary<uint, OrderRecordTuple>();
|
||||
readonly SetMap<uint, uint> _security_orders = new SetMap<uint, uint>();
|
||||
readonly IDictionary<uint, SecurityMD> _security_md_cache = new Dictionary<uint, SecurityMD>(0);
|
||||
IReadOnlyDictionary<uint, OrderRecordImmutable> _orders_cache = new Dictionary<uint, OrderRecordImmutable>(0);
|
||||
IReadOnlyDictionary<uint, SecurityMD> _security_md_cache = new Dictionary<uint, SecurityMD>(0);
|
||||
IDisposable _dispose;
|
||||
|
||||
public OrderRecordTupleSource(IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities, IObservable<TableUpdate<OrderRecordImmutable, uint>> orders) =>
|
||||
@@ -92,11 +92,14 @@ namespace mbts.spark {
|
||||
Dictionary<uint, OrderRecordTuple> added = new Dictionary<uint, OrderRecordTuple>(), removed = new Dictionary<uint, OrderRecordTuple>();
|
||||
Dictionary<uint, SecurityMD> added_securities = new Dictionary<uint, SecurityMD>(), removed_securities = new Dictionary<uint, SecurityMD>();
|
||||
_security_md_cache.Keys.difference(securities.Keys, out IEnumerable<uint> added_keys, out IEnumerable<uint> removed_keys);
|
||||
/// Warning: <see cref="_security_md_cache"/> must NOT be reassigned with <see cref="securities"/>; the latter is always the same object.
|
||||
/// This would mean that the <see cref="GlobalExtensions.difference{T}(IEnumerable{T}, IEnumerable{T}, out IEnumerable{T}, out IEnumerable{T})"/>
|
||||
/// call above would literally be calculating the difference between the keys of the same dictionary!
|
||||
foreach (var k in removed_keys) {
|
||||
if (_security_orders.try_lookup(k, out ISet<uint> order_keys)) {
|
||||
removed_securities.Add(k, _security_md_cache[k]);
|
||||
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
|
||||
SecurityMD old_sec_md = _security_md_cache[k];
|
||||
removed_securities.Add(k, old_sec_md);
|
||||
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
|
||||
foreach (OrderRecordImmutable order in orders) {
|
||||
OrderRecordTuple old_ft = new OrderRecordTuple(order, old_sec_md);
|
||||
removed.Add(old_ft.order.Key, old_ft);
|
||||
@@ -104,12 +107,13 @@ namespace mbts.spark {
|
||||
added.Add(new_ft.order.Key, new_ft);
|
||||
}
|
||||
}
|
||||
_security_md_cache.Remove(k);
|
||||
}
|
||||
foreach (var k in added_keys) {
|
||||
SecurityMD new_sec_md = securities[k];
|
||||
if (_security_orders.try_lookup(k, out ISet<uint> order_keys)) {
|
||||
added_securities.Add(k, securities[k]);
|
||||
added_securities.Add(k, new_sec_md);
|
||||
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
|
||||
SecurityMD new_sec_md = securities[k];
|
||||
foreach (OrderRecordImmutable order in orders) {
|
||||
OrderRecordTuple old_ft = new OrderRecordTuple(order, null);
|
||||
removed.Add(old_ft.order.Key, old_ft);
|
||||
@@ -117,8 +121,8 @@ namespace mbts.spark {
|
||||
added.Add(new_ft.order.Key, new_ft);
|
||||
}
|
||||
}
|
||||
_security_md_cache.Add(k, new_sec_md);
|
||||
}
|
||||
_security_md_cache = securities;
|
||||
_apply_changes(added, removed, added_securities, removed_securities);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace mbts.ui {
|
||||
public bool CanExecute(object parameter) {
|
||||
if (!allow_concurrent_executions && _n_executing > 0)
|
||||
return false;
|
||||
else return _can_execute(ObjectHelper.try_convert<T>(parameter));
|
||||
else return _can_execute(ConvertUtils.try_convert<T>(parameter));
|
||||
}
|
||||
|
||||
public async Task Execute(object parameter) {
|
||||
@@ -51,7 +51,7 @@ namespace mbts.ui {
|
||||
if (!allow_concurrent_executions)
|
||||
RaiseCanExecuteChanged();
|
||||
}
|
||||
await _execute(ObjectHelper.try_convert<T>(parameter));
|
||||
await _execute(ConvertUtils.try_convert<T>(parameter));
|
||||
if (--_n_executing == 0) {
|
||||
_raise_is_executing_changed();
|
||||
if (!allow_concurrent_executions)
|
||||
|
||||
@@ -27,13 +27,13 @@ namespace mbts.ui
|
||||
if (_can_execute == null)
|
||||
return true;
|
||||
else
|
||||
return _can_execute(ObjectHelper.try_convert<T>(parameter));
|
||||
return _can_execute(ConvertUtils.try_convert<T>(parameter));
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public void Execute(object parameter) =>
|
||||
_execute(ObjectHelper.try_convert<T>(parameter));
|
||||
_execute(ConvertUtils.try_convert<T>(parameter));
|
||||
}
|
||||
|
||||
public class DelegateCommand : DelegateCommand<object> {
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace mbts.ui {
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public bool CanExecute(object parameter) => _can_execute;
|
||||
public void Execute(object parameter) => _execute(ObjectHelper.try_convert<T>(parameter));
|
||||
public void Execute(object parameter) => _execute(ConvertUtils.try_convert<T>(parameter));
|
||||
|
||||
public void OnCompleted() { }
|
||||
public void OnError(Exception error) { }
|
||||
|
||||
@@ -364,7 +364,7 @@ namespace System.Collections.Generic {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses <see cref="ObjectHelper.try_convert{TResult}(object)"/> to convert the elements of an <see cref="IEnumerable"/> to the specified type.
|
||||
/// Uses <see cref="ConvertUtils.try_convert{TResult}(object)"/> to convert the elements of an <see cref="IEnumerable"/> to the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type to convert the elements of source to.</typeparam>
|
||||
/// <param name="source">The <see cref="IEnumerable"/> that contains the elements to be cast to type <typeparamref name="TResult"/></param>
|
||||
@@ -373,7 +373,7 @@ namespace System.Collections.Generic {
|
||||
public static IEnumerable<TResult> convert<TResult>(this IEnumerable source) {
|
||||
ExceptionAssert.Argument.NotNull(source, nameof(source));
|
||||
foreach (object obj in source)
|
||||
yield return ObjectHelper.try_convert<TResult>(obj);
|
||||
yield return ConvertUtils.try_convert<TResult>(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,12 +131,6 @@ namespace mbts
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int safe_compare<T>(this T a, T b, Func<T, T, int> compare) =>
|
||||
a == null ? (b == null ? 0 : -1) : (b == null ? 1 : compare(a, b));
|
||||
|
||||
public static int safe_compare<T>(this T a, T b) where T : IComparable<T> =>
|
||||
a == null ? (b == null ? 0 : -1) : (b == null ? 1 : a.CompareTo(b));
|
||||
|
||||
public static bool safe_equals<T>(this T a, T b, Func<T, T, bool> equals) =>
|
||||
a == null ? b == null : b != null && equals(a, b);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using bts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.utils {
|
||||
|
||||
@@ -32,8 +32,11 @@ namespace mbts.utils {
|
||||
// need to lock bc simultaneous callbacks on diff threads could generate 2 subscriptions at once,
|
||||
// only 1 of which would get disposed in the next callback.
|
||||
Disposable.dispose(ref _current_key_sub);
|
||||
if (!_is_disposed)
|
||||
_current_key_sub = _selector(key)?.Subscribe(_value_subject);
|
||||
if (!_is_disposed) {
|
||||
// IMPORTANT: do NOT subscribe _value_subject directly to the inner sequences. Subscribe with a callback delegate instead (e.g. _value_subject.OnNext).
|
||||
// Reason: If a sequence 'completes' while _value_subject's subscription is active, it will enter a 'Terminated' state, and forever cease notifying observers of subsequent updates.
|
||||
_current_key_sub = _selector(key)?.Subscribe(_value_subject.OnNext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ namespace mbts.ui.controls {
|
||||
|
||||
class BadgeTextLinesConverter : IMultiValueConverter {
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
|
||||
if (ObjectHelper.try_convert_args(values, out Side side, out uint quantity, out uint filled, out mbts.spark.LadderSettings.BadgeStyle style))
|
||||
if (ConvertUtils.try_convert_args(values, out Side side, out uint quantity, out uint filled, out mbts.spark.LadderSettings.BadgeStyle style))
|
||||
return BadgeRender.format(side, quantity, filled, style);
|
||||
else return new string[0];
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace mbts.ui.controls.color_picker {
|
||||
public DataTemplate EditableTemplate { get; set; }
|
||||
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
|
||||
if (ObjectHelper.try_convert_args(values, out ColorPaletteVM vm, out bool is_selected)) {
|
||||
if (ConvertUtils.try_convert_args(values, out ColorPaletteVM vm, out bool is_selected)) {
|
||||
if (vm.can_user_edit && is_selected)
|
||||
return EditableTemplate;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace mbts.ui.controls.color_picker {
|
||||
|
||||
class NamedColorItemToolTipConverter : IMultiValueConverter {
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
|
||||
ObjectHelper.try_convert_args(values, out string name, out bool can_user_edit) ?
|
||||
ConvertUtils.try_convert_args(values, out string name, out bool can_user_edit) ?
|
||||
$"{name} (Alt + Click for info{(can_user_edit ? ", Ctrl + Click to assign" : string.Empty)})" :
|
||||
string.Empty;
|
||||
|
||||
|
||||
@@ -109,6 +109,6 @@ namespace mbts.spark.filters {
|
||||
protected virtual void _validate() { }
|
||||
|
||||
bool IDataGridColumnFilter.filter(object row) =>
|
||||
filter(ObjectHelper.try_convert<TRow>(row));
|
||||
filter(ConvertUtils.try_convert<TRow>(row));
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,6 @@ namespace mbts.spark.filters {
|
||||
|
||||
|
||||
bool IDataGridColumnFilter.filter(object row) =>
|
||||
filter(ObjectHelper.try_convert<TRow>(row));
|
||||
filter(ConvertUtils.try_convert<TRow>(row));
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
Content="Apply"
|
||||
CommandParameter="{TemplateBinding ApplyCommandParameter}"
|
||||
Command="{TemplateBinding ApplyCommand}"
|
||||
CommandTarget="{TemplateBinding ApplyCommandTarget}"
|
||||
IsDefault="True"
|
||||
Focusable="False"
|
||||
Style="{StaticResource CommonButton}"
|
||||
|
||||
@@ -24,6 +24,11 @@ namespace mbts.ui.spark {
|
||||
public object ApplyCommandParameter { get => GetValue(ApplyCommandParameterProperty); set => SetValue(ApplyCommandParameterProperty, value); }
|
||||
#endregion
|
||||
|
||||
#region ApplyCommandTarget
|
||||
public static readonly DependencyProperty ApplyCommandTargetProperty = WPFHelper.create_dp<IInputElement, FilterContentControl>(nameof(ApplyCommandTarget));
|
||||
public IInputElement ApplyCommandTarget { get => GetValue(ApplyCommandTargetProperty) as IInputElement; set => SetValue(ApplyCommandTargetProperty, value); }
|
||||
#endregion
|
||||
|
||||
#region ClearCommand
|
||||
public static readonly DependencyProperty ClearCommandProperty = DependencyProperty.Register(
|
||||
nameof(ClearCommand), typeof(ICommand), typeof(FilterContentControl));
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
<usp:FilterContentControl
|
||||
ApplyCommandParameter="{Binding}"
|
||||
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
|
||||
ApplyCommandTarget="{Binding ElementName=Filter}"
|
||||
ClearCommandParameter="{Binding}"
|
||||
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
|
||||
>
|
||||
@@ -115,6 +116,9 @@
|
||||
Begin="{Binding begin}"
|
||||
End="{Binding end}"
|
||||
>
|
||||
<c:TimeFilterControl.CommandBindings>
|
||||
<CommandBinding Command="{x:Static c:DataGridFilterProperties.CommitCommand}" Executed="{x:Static c:TimeFilterControl.CommitExecuted}"/>
|
||||
</c:TimeFilterControl.CommandBindings>
|
||||
<u:Behavior.BindingUpdateGroups>
|
||||
<u:BindingUpdateGroupCollection>
|
||||
<u:BindingUpdateGroup Source="{Binding DataContext, Source={u:DirectReference Filter}}" Target="{Binding Source={u:DirectReference Filter}}">
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
@@ -14,6 +15,18 @@ namespace mbts.ui.controls {
|
||||
/// Interaction logic for TimeFilterControl.xaml
|
||||
/// </summary>
|
||||
public class TimeFilterControl : Control {
|
||||
public static readonly ExecutedRoutedEventHandler CommitExecuted = (s, e) => {
|
||||
if (s is TimeFilterControl tfc) {
|
||||
tfc.get_template_child<DateTimePicker>(_begin_dtp_name)?.flush_pending_changes();
|
||||
tfc.get_template_child<DateTimePicker>(_end_dtp_name)?.flush_pending_changes();
|
||||
(e.Parameter as IDataGridColumnFilter)?.apply.TryExecuteIfCan(null);
|
||||
e.Handled = true;
|
||||
}
|
||||
};
|
||||
const string
|
||||
_begin_dtp_name = "PART_Begin_DateTimePicker",
|
||||
_end_dtp_name = "PART_End_DateTimePicker";
|
||||
|
||||
static TimeFilterControl() =>
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(TimeFilterControl), new FrameworkPropertyMetadata(typeof(TimeFilterControl)));
|
||||
|
||||
@@ -45,17 +58,17 @@ namespace mbts.ui.controls {
|
||||
var clear_begin_time = this.assert_template_child<Hyperlink>("ClearBeginLink");
|
||||
var clear_end_time = this.assert_template_child<Hyperlink>("ClearEndLink");
|
||||
var set_last_hour = this.assert_template_child<Hyperlink>("SetLastHourLink");
|
||||
clear_begin_time.Command = new DelegateCommand(() => Begin = default);
|
||||
clear_end_time.Command = new DelegateCommand(() => End = default);
|
||||
set_last_hour.Command = new DelegateCommand(() => Begin = TimeTextTuple.FromDateTime(DateTime.Now.AddHours(-1)));
|
||||
clear_begin_time.Command = new DelegateCommand(() => SetCurrentValue(BeginProperty, default(TimeTextTuple)));
|
||||
clear_end_time.Command = new DelegateCommand(() => SetCurrentValue(EndProperty, default(TimeTextTuple)));
|
||||
set_last_hour.Command = new DelegateCommand(() => SetCurrentValue(BeginProperty, TimeTextTuple.FromDateTime(DateTime.Now.AddHours(-1))));
|
||||
}
|
||||
|
||||
class TimeFilterErrorAdorner : NotifyDataErrorAdorner<TimeFilterControl, ITimeFilterVM>, IDisposable {
|
||||
readonly ChildProperty<DateTimePicker> _begin_picker, _end_picker;
|
||||
|
||||
public TimeFilterErrorAdorner(TimeFilterControl view) : base(view) {
|
||||
_begin_picker = child_property<DateTimePicker>("PART_Begin_DateTimePicker");
|
||||
_end_picker = child_property<DateTimePicker>("PART_End_DateTimePicker");
|
||||
_begin_picker = child_property<DateTimePicker>(_begin_dtp_name);
|
||||
_end_picker = child_property<DateTimePicker>(_end_dtp_name);
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext drawing_context) {
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace mbts.ui.controls
|
||||
protected DataGridExRow() { }
|
||||
|
||||
public abstract object get_cell(TColKey col_key);
|
||||
public object get_cell(object col_key) { return get_cell(ObjectHelper.try_convert<TColKey>(col_key)); }
|
||||
public object get_cell(object col_key) { return get_cell(ConvertUtils.try_convert<TColKey>(col_key)); }
|
||||
|
||||
|
||||
}
|
||||
@@ -60,6 +60,6 @@ namespace mbts.ui.controls
|
||||
public abstract class DataGridExCell<TData> : ViewModelBase, IDataGridExCell<TData>, IDataGridExCell
|
||||
{
|
||||
public abstract void update(TData data);
|
||||
public void update(object data) { update(ObjectHelper.try_convert<TData>(data)); }
|
||||
public void update(object data) { update(ConvertUtils.try_convert<TData>(data)); }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using mbts.ui.utility;
|
||||
using mbts.ui.utility.text;
|
||||
using mbts.utils;
|
||||
using bts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
@@ -77,7 +77,7 @@ namespace mbts.ui.controls {
|
||||
|
||||
class FontFamilySupportsWeightConverter : IMultiValueConverter {
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
|
||||
if (ObjectHelper.try_convert_args(values, out FontFamily ff, out FontWeight fw))
|
||||
if (ConvertUtils.try_convert_args(values, out FontFamily ff, out FontWeight fw))
|
||||
return TextUtils.available_weights(ff).Contains(fw);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using mbts.utils;
|
||||
using bts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -231,13 +231,37 @@
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="1"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="When the size of the opposite-inside market falls below this size threshold the order will payup." />
|
||||
</StackPanel>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
<TextBlock Grid.Row="3" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="3"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="The max number of ticks the order will continue to pay up after the initial payup." />
|
||||
</StackPanel>
|
||||
<c:TickUpDownUInt Grid.Row="3" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
|
||||
<TextBlock Grid.Row="5">On Toward</TextBlock>
|
||||
<ComboBox Grid.Row="5" Grid.Column="2" ItemsSource="{Binding trade_toward_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding trade_toward_behavior}"/>
|
||||
<TextBlock Grid.Row="7">Child</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="5"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">On Toward</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="What should happen when the market moves toward the order." />
|
||||
</StackPanel>
|
||||
<ComboBox Grid.Row="5" Grid.Column="2" ItemsSource="{Binding trade_toward_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding trade_toward_behavior}"/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="7"
|
||||
>
|
||||
<TextBlock >Child</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered" />
|
||||
</StackPanel>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="7" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
@@ -266,9 +290,21 @@
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Join Thresh</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="1"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Join Thresh</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="The Threshold at which the order will join the market when the market flips (the event where the best offer becomes the best bid in the buy case)." />
|
||||
</StackPanel>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding join_threshold}"/>
|
||||
<TextBlock Grid.Row="3">Child</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="3"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Child</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered" />
|
||||
</StackPanel>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
@@ -295,9 +331,22 @@
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Top Thresh</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="1"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Top Thresh</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="If the order is at the top of book, the quantity remaining below which the order will be canceled." />
|
||||
</StackPanel>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding top_threshold}"/>
|
||||
<TextBlock Grid.Row="3">Child</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="3"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Child</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered." />
|
||||
</StackPanel>
|
||||
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
@@ -326,9 +375,21 @@
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center" >Hedge Ticks</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="1"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center" >Hedge Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks to offset the hedge." />
|
||||
</StackPanel>
|
||||
<c:TickUpDownInt Grid.Row="1" Grid.Column="2" Value="{Binding hedge_ticks}"/>
|
||||
<TextBlock Grid.Row="3">Hedge</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="3"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Hedge</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the hedge order to send for each fill of Child Order." />
|
||||
</StackPanel>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="3" Grid.Column="2"
|
||||
IsOpen="{Binding hedge_order.editable_properties_open, FallbackValue=False}"
|
||||
@@ -336,7 +397,14 @@
|
||||
SelectedItem="{Binding hedge_order}"
|
||||
HasEditableProperties="{Binding hedge_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
<TextBlock Grid.Row="5">Child</TextBlock>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Grid.Row="5"
|
||||
>
|
||||
<TextBlock VerticalAlignment="Center">Child</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered." />
|
||||
</StackPanel>
|
||||
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="5" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
@@ -377,7 +445,7 @@
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.OffsetOrderTemplate>
|
||||
</usp:OrderModificationTemplateSelector.OffsetOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.TriggerOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Grid.IsSharedSizeScope="True">
|
||||
@@ -458,7 +526,7 @@
|
||||
<GroupBox.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock FontWeight="SemiBold" Margin="0,0,4,0">Price</TextBlock>
|
||||
<c:HelpButton ToolTip="The child order, resulting from the trigger condition being met, can be sent either at a specified price specified price (Fixed) price or payup/less ticks from the Trigger Price" />
|
||||
<c:HelpButton ToolTip="The child order, resulting from the trigger condition being met, can be sent either at a specified price (Fixed) price or payup/less ticks from the Trigger Price" />
|
||||
</StackPanel>
|
||||
</GroupBox.Header>
|
||||
<Grid>
|
||||
@@ -634,8 +702,8 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding descriptor.ParamName}"/>
|
||||
<CheckBox HorizontalAlignment="Stretch" Grid.Column="2" IsChecked="{Binding param.AsBool, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.BoolTemplate>
|
||||
<usp:DynamicOrderParamTemplateSelector.DoubleTemplate>
|
||||
<DataTemplate>
|
||||
@@ -647,9 +715,9 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding descriptor.ParamName}"/>
|
||||
<c:TickUpDownDouble Grid.Column="2" Value="{Binding param.AsDouble, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.DoubleTemplate>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.DoubleTemplate>
|
||||
<usp:DynamicOrderParamTemplateSelector.IntTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -660,9 +728,9 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding descriptor.ParamName}"/>
|
||||
<c:TickUpDownInt Grid.Column="2" Value="{Binding param.AsInt32, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.IntTemplate>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.IntTemplate>
|
||||
<usp:DynamicOrderParamTemplateSelector.UIntTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -673,9 +741,9 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding descriptor.ParamName}"/>
|
||||
<c:TickUpDownUInt Grid.Column="2" Value="{Binding param.AsUint32, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.UIntTemplate>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.UIntTemplate>
|
||||
<usp:DynamicOrderParamTemplateSelector.StringTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -686,9 +754,9 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding descriptor.ParamName}"/>
|
||||
<TextBox HorizontalAlignment="Stretch" Grid.Column="2" Text="{Binding param.AsString, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.StringTemplate>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.StringTemplate>
|
||||
<usp:DynamicOrderParamTemplateSelector.EnumTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -699,11 +767,11 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding descriptor.ParamName}"/>
|
||||
<ComboBox Grid.Column="2" ItemsSource="{Binding descriptor.ParamType.Enumerated.Element}" SelectedItem="{Binding param.AsEnumerated.EnumValue}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.EnumTemplate>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.EnumTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector>
|
||||
</ItemsControl.ItemTemplateSelector>
|
||||
</ItemsControl.ItemTemplateSelector>
|
||||
</ItemsControl>
|
||||
<TextBlock Grid.Row="3">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<cv:KeyValue Key="{x:Null}" Value="{u:Visibility Visible}"/>
|
||||
</cv:ValueMap>
|
||||
|
||||
<ControlTemplate x:Key="HoverButtonOverlayTemplate2" TargetType="c:TickUpDown">
|
||||
<ControlTemplate x:Key="HoverButtonOverlayTemplate" TargetType="c:TickUpDown">
|
||||
<Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
@@ -86,8 +86,8 @@
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="38"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<c:TickUpDown x:Name="PART_time_tick" HorizontalAlignment="Stretch" EnableArrowKeyTick="False" Template="{StaticResource HoverButtonOverlayTemplate2}">
|
||||
<TextBox x:Name="PART_time_text" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True">
|
||||
<c:TickUpDown x:Name="PART_time_tick" HorizontalAlignment="Stretch" EnableArrowKeyTick="False" Template="{StaticResource HoverButtonOverlayTemplate}">
|
||||
<TextBox x:Name="PART_time_text" u:TextBoxBehavior.CommitOnEnter="True" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True">
|
||||
<TextBox.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static c:DateTimePicker.TickTimeComponentCommand}" CommandParameter="{u:Int 1}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static c:DateTimePicker.TickTimeComponentCommand}" CommandParameter="{u:Int -1}"/>
|
||||
@@ -95,15 +95,15 @@
|
||||
</TextBox.InputBindings>
|
||||
</TextBox>
|
||||
</c:TickUpDown>
|
||||
<c:TickUpDown x:Name="PART_month_tick" Margin="2,0,0,0" Grid.Column="1" Template="{StaticResource HoverButtonOverlayTemplate2}">
|
||||
<c:TickUpDown x:Name="PART_month_tick" Margin="2,0,0,0" Grid.Column="1" Template="{StaticResource HoverButtonOverlayTemplate}">
|
||||
<TextBox x:Name="PART_month_text" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True"/>
|
||||
</c:TickUpDown>
|
||||
<TextBlock Grid.Column="2" Margin="2,0">/</TextBlock>
|
||||
<c:TickUpDown x:Name="PART_day_tick" Grid.Column="3" Template="{StaticResource HoverButtonOverlayTemplate2}">
|
||||
<c:TickUpDown x:Name="PART_day_tick" Grid.Column="3" Template="{StaticResource HoverButtonOverlayTemplate}">
|
||||
<TextBox x:Name="PART_day_text" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True"/>
|
||||
</c:TickUpDown>
|
||||
<TextBlock Grid.Column="4" Margin="2,0">/</TextBlock>
|
||||
<c:TickUpDown x:Name="PART_year_tick" Grid.Column="5" Template="{StaticResource HoverButtonOverlayTemplate2}">
|
||||
<c:TickUpDown x:Name="PART_year_tick" Grid.Column="5" Template="{StaticResource HoverButtonOverlayTemplate}">
|
||||
<TextBox x:Name="PART_year_text" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True"/>
|
||||
</c:TickUpDown>
|
||||
<TextBlock Grid.Column="0" Text="hh:mm tt" Style="{StaticResource TextPromptStyle}" Visibility="{Binding Text, ElementName=PART_time_text, Converter={StaticResource NullOrEmptyVisibilityConverter}}"/>
|
||||
|
||||
@@ -18,6 +18,11 @@ namespace mbts.ui.controls {
|
||||
public static readonly RoutedCommand TickTimeComponentCommand = new RoutedCommand();
|
||||
|
||||
DTPController _controller;
|
||||
TextBox
|
||||
_month_text,
|
||||
_day_text,
|
||||
_year_text,
|
||||
_time_text;
|
||||
bool _suspend_notify_vm;
|
||||
|
||||
static DateTimePicker() =>
|
||||
@@ -34,18 +39,25 @@ namespace mbts.ui.controls {
|
||||
|
||||
IList<IDisposable> _hooks = new List<IDisposable>();
|
||||
|
||||
public void flush_pending_changes() {
|
||||
_month_text?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
|
||||
_day_text?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
|
||||
_year_text?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
|
||||
_time_text?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
|
||||
}
|
||||
|
||||
public override void OnApplyTemplate() {
|
||||
var month = this.assert_template_child<TickUpDown>("PART_month_tick");
|
||||
var month_text = this.assert_template_child<TextBox>("PART_month_text");
|
||||
_month_text = this.assert_template_child<TextBox>("PART_month_text");
|
||||
var day = this.assert_template_child<TickUpDown>("PART_day_tick");
|
||||
var day_text = this.assert_template_child<TextBox>("PART_day_text");
|
||||
_day_text = this.assert_template_child<TextBox>("PART_day_text");
|
||||
var year = this.assert_template_child<TickUpDown>("PART_year_tick");
|
||||
var year_text = this.assert_template_child<TextBox>("PART_year_text");
|
||||
_year_text = this.assert_template_child<TextBox>("PART_year_text");
|
||||
var time = this.assert_template_child<TickUpDown>("PART_time_tick");
|
||||
var time_text = this.assert_template_child<TextBox>("PART_time_text");
|
||||
_time_text = this.assert_template_child<TextBox>("PART_time_text");
|
||||
|
||||
if (_controller != null) _controller.PropertyChanged -= _vm_PropertyChanged;
|
||||
_controller = new DTPController(time_text);
|
||||
_controller = new DTPController(_time_text);
|
||||
_controller.result = TimeTuple;
|
||||
_controller.PropertyChanged += _vm_PropertyChanged;
|
||||
time.CommandBindings.Add(new CommandBinding(TickTimeComponentCommand, (s, e) => {
|
||||
@@ -53,53 +65,52 @@ namespace mbts.ui.controls {
|
||||
}));
|
||||
time.CommandBindings.Add(new CommandBinding(TextBoxBehavior.SelectAllCommand, TextBoxBehavior.SelectAllCommandExecutedHandler));
|
||||
|
||||
const int delay = 500;
|
||||
time.SetBinding(TickUpDown.TickCommandProperty, new Binding("tick_time") { Source = _controller });
|
||||
time.TickUpParameter = 1;
|
||||
time.TickDownParameter = -1;
|
||||
time.TickUpAltParameter = 60;
|
||||
time.TickDownAltParameter = -60;
|
||||
time_text.SetBinding(TextBox.TextProperty, new Binding("time_text") { Source = _controller, Delay = delay, ValidatesOnExceptions = true });
|
||||
_hooks.Add(time_text.SubscribePreviewTextInput((s, e) => {
|
||||
string preview = time_text.PeekText(e);
|
||||
_time_text.SetBinding(TextBox.TextProperty, new Binding("time_text") { Source = _controller, ValidatesOnExceptions = true });
|
||||
_hooks.Add(_time_text.SubscribePreviewTextInput((s, e) => {
|
||||
string preview = _time_text.PeekText(e);
|
||||
if (RegexFilters.Time.StandardTimeOptionalSeconds.IsMatch(preview)) {
|
||||
_controller.use_military_time = false;
|
||||
} else if (RegexFilters.Time.MilitaryTimeOptionalSeconds.IsMatch(preview)) {
|
||||
_controller.use_military_time = true;
|
||||
} else {
|
||||
e.Handled = true;
|
||||
e.Handled = !RegexFilters.Time.FlexibleTime.IsMatch(preview);
|
||||
}
|
||||
}));
|
||||
|
||||
month.SetBinding(TickUpDown.TickCommandProperty, new Binding("tick_month") { Source = _controller });
|
||||
month.TickUpParameter = 1;
|
||||
month.TickDownParameter = -1;
|
||||
month_text.SetBinding(TextBox.TextProperty, new Binding("month_text") { Source = _controller, Delay = delay });
|
||||
_hooks.Add(month_text.SubscribePreviewTextInput((s, e) =>
|
||||
e.Handled = !RegexFilters.Time.MonthAbbrev.IsMatch(month_text.PeekText(e))
|
||||
_month_text.SetBinding(TextBox.TextProperty, new Binding("month_text") { Source = _controller });
|
||||
_hooks.Add(_month_text.SubscribePreviewTextInput((s, e) =>
|
||||
e.Handled = !RegexFilters.Time.MonthAbbrev.IsMatch(_month_text.PeekText(e))
|
||||
));
|
||||
|
||||
day.SetBinding(TickUpDown.TickCommandProperty, new Binding("tick_day") { Source = _controller });
|
||||
day.TickUpParameter = 1;
|
||||
day.TickDownParameter = -1;
|
||||
day_text.SetBinding(TextBox.TextProperty, new Binding("day_text") { Source = _controller, Delay = delay });
|
||||
_hooks.Add(day_text.SubscribePreviewTextInput((s, e) =>
|
||||
e.Handled = !RegexFilters.Time.DaysInMonth.IsMatch(day_text.PeekText(e))
|
||||
_day_text.SetBinding(TextBox.TextProperty, new Binding("day_text") { Source = _controller });
|
||||
_hooks.Add(_day_text.SubscribePreviewTextInput((s, e) =>
|
||||
e.Handled = !RegexFilters.Time.DaysInMonth.IsMatch(_day_text.PeekText(e))
|
||||
));
|
||||
|
||||
year.SetBinding(TickUpDown.TickCommandProperty, new Binding("tick_year") { Source = _controller });
|
||||
year.TickUpParameter = 1;
|
||||
year.TickDownParameter = -1;
|
||||
year_text.SetBinding(TextBox.TextProperty, new Binding("year_text") { Source = _controller, Delay = delay });
|
||||
_hooks.Add(year_text.SubscribePreviewTextInput((s, e) =>
|
||||
e.Handled = !RegexFilters.Time.Year.IsMatch(year_text.PeekText(e))
|
||||
_year_text.SetBinding(TextBox.TextProperty, new Binding("year_text") { Source = _controller });
|
||||
_hooks.Add(_year_text.SubscribePreviewTextInput((s, e) =>
|
||||
e.Handled = !RegexFilters.Time.Year.IsMatch(_year_text.PeekText(e))
|
||||
));
|
||||
}
|
||||
|
||||
void _vm_PropertyChanged(object sender, PropertyChangedEventArgs e) {
|
||||
if (e.PropertyName == nameof(DTPController.result)) {
|
||||
_suspend_notify_vm = true;
|
||||
TimeTuple = _controller.result;
|
||||
SetCurrentValue(TimeTupleProperty, _controller.result);
|
||||
_suspend_notify_vm = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using bts;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui.controls.adorners;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
public class UniformTabGrid : UniformGrid {
|
||||
public UniformTabGrid () {
|
||||
this.IsItemsHost = true;
|
||||
this.Rows = 1;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size constraint) {
|
||||
var child_tabs = this.Children.OfType<TabItem>();
|
||||
var child_tabs_max_width = child_tabs.Sum(t => t.MaxWidth);
|
||||
// I noticed when debugging that this can sometimes be infinty,
|
||||
// especially when a TabItem has not been set with a MaxWidth.
|
||||
if (!double.IsInfinity(child_tabs_max_width)) {
|
||||
// if the window width is greater than the total tab width, then
|
||||
// there is more than enough space for all the tabs. If this
|
||||
// is set to the default of Stretch and the window has only 1
|
||||
// tab or few tabs, the tab appears in the middle, which is not good.
|
||||
// i.e. we want tabs to added left to right, so align left
|
||||
//
|
||||
// When the window is less than the total tab width, then align
|
||||
// can be set to stretch b/c the UniformGrid will take care of
|
||||
// sizing each tab accordingly.
|
||||
this.HorizontalAlignment = (constraint.Width > child_tabs_max_width)
|
||||
? HorizontalAlignment.Left
|
||||
: HorizontalAlignment.Stretch;
|
||||
|
||||
// this bit here is necessary because when the window is expanded
|
||||
// beyond the maxwidth of all the tabs, we switch to Left and the
|
||||
// headers snap to some smaller than MaxWidth size, which is not
|
||||
// the behavior we want
|
||||
foreach (var child in child_tabs) {
|
||||
child.Width = this.HorizontalAlignment == HorizontalAlignment.Left
|
||||
? child.MaxWidth
|
||||
: double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
return base.MeasureOverride(constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,8 @@ namespace mbts.ui.converters {
|
||||
private readonly Func<TIn, TOut> _convert;
|
||||
private readonly Func<TOut, TIn> _convert_back;
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return _convert(ObjectHelper.try_convert<TIn>(value)); }
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return _convert_back(ObjectHelper.try_convert<TOut>(value)); }
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return _convert(ConvertUtils.try_convert<TIn>(value)); }
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return _convert_back(ConvertUtils.try_convert<TOut>(value)); }
|
||||
}
|
||||
|
||||
public class AnonymousMultiValueConverter<TIn1, TIn2, TOut> : IMultiValueConverter {
|
||||
@@ -49,7 +49,7 @@ namespace mbts.ui.converters {
|
||||
public TOut ErrorValue { get; set; }
|
||||
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
|
||||
ObjectHelper.try_convert_args(values, out TIn1 a, out TIn2 b) ? _convert(a, b) : ErrorValue;
|
||||
ConvertUtils.try_convert_args(values, out TIn1 a, out TIn2 b) ? _convert(a, b) : ErrorValue;
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace mbts.ui.converters {
|
||||
public TOut ErrorValue { get; set; }
|
||||
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
|
||||
ObjectHelper.try_convert_args(values, out TIn1 a, out TIn2 b, out TIn3 c) ? _convert(a, b, c) : ErrorValue;
|
||||
ConvertUtils.try_convert_args(values, out TIn1 a, out TIn2 b, out TIn3 c) ? _convert(a, b, c) : ErrorValue;
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using mbts.utils;
|
||||
using bts.utils;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
@@ -19,7 +19,7 @@ namespace mbts.ui.converters
|
||||
}
|
||||
|
||||
public class DynamicValueEquals<T> : DynamicValueEquals {
|
||||
protected override bool ValuesEqual(object x, object y) { return ObjectHelper.try_convert(x, out T tx) && ObjectHelper.try_convert(y, out T ty) && ValuesEqual(tx, ty); }
|
||||
protected override bool ValuesEqual(object x, object y) { return ConvertUtils.try_convert(x, out T tx) && ConvertUtils.try_convert(y, out T ty) && ValuesEqual(tx, ty); }
|
||||
protected virtual bool ValuesEqual(T x, T y) { return CompareUtils.NullCheckEquals(x, y); }
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace mbts.ui.converters
|
||||
}
|
||||
|
||||
public class StaticValueEqualsConverter<T> : StaticValueEqualsConverter {
|
||||
protected override bool ValueEquals(object value) { return ObjectHelper.try_convert(value, out T result) && ValueEquals(result); }
|
||||
protected override bool ValueEquals(object value) { return ConvertUtils.try_convert(value, out T result) && ValueEquals(result); }
|
||||
protected virtual bool ValueEquals(T value) { return base.ValueEquals(value); }
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace mbts.ui
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public bool CanExecute(object parameter) {
|
||||
return !_is_executing && (_can_execute == null || _can_execute(ObjectHelper.try_convert<T>(parameter)));
|
||||
return !_is_executing && (_can_execute == null || _can_execute(ConvertUtils.try_convert<T>(parameter)));
|
||||
}
|
||||
|
||||
public void RaiseCanExecuteChanged() {
|
||||
@@ -45,7 +45,7 @@ namespace mbts.ui
|
||||
_is_executing = true;
|
||||
RaiseCanExecuteChanged();
|
||||
try {
|
||||
var w = await _factory(ObjectHelper.try_convert<T>(parameter));
|
||||
var w = await _factory(ConvertUtils.try_convert<T>(parameter));
|
||||
if (w != null) {
|
||||
if (_off_x.HasValue && _off_y.HasValue)
|
||||
w.move_to_mouse(_off_x.Value, _off_x.Value);
|
||||
|
||||
@@ -28,12 +28,12 @@ namespace mbts.ui
|
||||
private IDisposable _dialog;
|
||||
public Predicate<T> can_execute { get; set; }
|
||||
|
||||
public bool CanExecute(object parameter) { return can_execute == null ? true : can_execute(ObjectHelper.try_convert<T>(parameter)); }
|
||||
public bool CanExecute(object parameter) { return can_execute == null ? true : can_execute(ConvertUtils.try_convert<T>(parameter)); }
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public void Execute(object parameter) {
|
||||
Disposable.dispose(ref _dialog);
|
||||
Window w = _factory(ObjectHelper.try_convert<T>(parameter));
|
||||
Window w = _factory(ConvertUtils.try_convert<T>(parameter));
|
||||
if (w != null) {
|
||||
if (_try_dispose_vm_on_close) {
|
||||
w.Closed += (s, e) => {
|
||||
|
||||
@@ -101,10 +101,10 @@ namespace mbts.ui {
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public bool CanExecute(object parameter) => _can_execute(ObjectHelper.try_convert<TParam>(parameter));
|
||||
public bool CanExecute(object parameter) => _can_execute(ConvertUtils.try_convert<TParam>(parameter));
|
||||
public void Execute(object parameter) {
|
||||
_safe_close_dialog();
|
||||
TParam tparam = ObjectHelper.try_convert<TParam>(parameter);
|
||||
TParam tparam = ConvertUtils.try_convert<TParam>(parameter);
|
||||
_dialog = _dialog_factory(tparam);
|
||||
if (_dialog.ShowDialog() == DialogResult.OK)
|
||||
_commit(tparam, _dialog.FileName);
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace mbts.ui {
|
||||
|
||||
public static T get_template_child<T>(this Control control, string name) {
|
||||
object obj = get_template_child(control, name);
|
||||
return ObjectHelper.try_convert<T>(obj);
|
||||
return ConvertUtils.try_convert<T>(obj);
|
||||
}
|
||||
|
||||
public static bool try_get_template_child(this Control control, string name, out object result) {
|
||||
@@ -40,7 +40,7 @@ namespace mbts.ui {
|
||||
|
||||
public static bool try_get_template_child<T>(this Control control, string name, out T result) {
|
||||
if (try_get_template_child(control, name, out object obj))
|
||||
return ObjectHelper.try_convert(obj, out result);
|
||||
return ConvertUtils.try_convert(obj, out result);
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
|
||||
xmlns:ui="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
mc:Ignorable="d" AllowDrop="True"
|
||||
Style="{StaticResource BTSFloatingWindow}"
|
||||
d:DesignHeight="300" d:DesignWidth="300"
|
||||
@@ -25,18 +26,25 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<WrapPanel x:Name="HeaderPanel" Orientation="Horizontal" Margin="0,0,100,0"
|
||||
<!-- This stack panel serves as the area between the last tab and the widget control on right side of title bar (below).
|
||||
It allows the region in between (if such a region exists) to be grabbed to move the window.
|
||||
-->
|
||||
<StackPanel Grid.Row="0" Margin="0,0,100,0" Background="Transparent" ui:WindowStyle.IsNonClientArea="True">
|
||||
<c:UniformTabGrid x:Name="HeaderPanel"
|
||||
Grid.Row="0"
|
||||
Panel.ZIndex="1"
|
||||
IsItemsHost="True"
|
||||
KeyboardNavigation.TabIndex="1"
|
||||
Background="Transparent" ui:WindowStyle.IsNonClientArea="True">
|
||||
</WrapPanel>
|
||||
Background="Transparent"
|
||||
ui:WindowStyle.IsNonClientArea="True"
|
||||
>
|
||||
</c:UniformTabGrid>
|
||||
</StackPanel>
|
||||
<Border Grid.Row="1" BorderThickness="0"
|
||||
Padding="{Binding Path=Padding, RelativeSource={RelativeSource AncestorType=Window}}" ui:WindowStyle.IsNonClientArea="False" >
|
||||
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
|
||||
</Border>
|
||||
<Border Grid.Row="0" Width="100" HorizontalAlignment="Right" VerticalAlignment="Stretch" Panel.ZIndex="2" Background="Transparent" ui:WindowStyle.IsNonClientArea="True">
|
||||
<!-- Window widget control area-->
|
||||
<Border Grid.Row="0" MinWidth="100" HorizontalAlignment="Right" Panel.ZIndex="2" Background="Transparent" ui:WindowStyle.IsNonClientArea="True">
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" Panel.ZIndex="2" Background="Transparent" Margin="5">
|
||||
<Button Style="{StaticResource RoundMinimizeButton}" HorizontalAlignment="Right" Command="{x:Static SystemCommands.MinimizeWindowCommand}" />
|
||||
<Button Style="{StaticResource RoundMaximizeButton}" HorizontalAlignment="Right" Command="{x:Static SystemCommands.MaximizeWindowCommand}" />
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</TabItem.RenderTransform>
|
||||
<TabItem.Template>
|
||||
<ControlTemplate TargetType="{x:Type TabItem}">
|
||||
<Border x:Name="bd" BorderBrush="Black" BorderThickness="0,0,0.5,0" Background="#d6d6d6" Width="{DynamicResource TabWidth}" Height="{DynamicResource TabHeight}" MinWidth="40" Margin="0" SnapsToDevicePixels="True" DockPanel.Dock="Left">
|
||||
<Border x:Name="bd" BorderBrush="Black" BorderThickness="0,0,0.5,0" Background="#d6d6d6" Height="{DynamicResource TabHeight}" MinWidth="40" Margin="0" SnapsToDevicePixels="True" DockPanel.Dock="Left">
|
||||
<Border.ToolTip>
|
||||
<TextBlock Text="{Binding Title}" />
|
||||
</Border.ToolTip>
|
||||
@@ -90,6 +90,7 @@
|
||||
</TabItem.Template>
|
||||
<TabItem.Style>
|
||||
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
|
||||
<Setter Property="MaxWidth" Value="{DynamicResource TabWidth}" />
|
||||
<Setter Property="HeaderTemplate" Value="{StaticResource _template}" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding EditMode}" Value="True">
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace mbts.ui.shell
|
||||
{
|
||||
public class WorkspaceViewModel : ViewModelBase
|
||||
{
|
||||
private const string INITIAL_VISUAL_STATE = "Normal";
|
||||
|
||||
WorkspacesModel _parent;
|
||||
public WorkspaceViewModel(WorkspacesModel parent, string name, WorkspaceDefinition definition) {
|
||||
_parent = parent;
|
||||
@@ -22,8 +24,23 @@ namespace mbts.ui.shell
|
||||
get { return Icons.Window; }
|
||||
}
|
||||
|
||||
private string _vstate = "Normal";
|
||||
public string VisualState { get { return _vstate; } set { _vstate = value; RaisePropertyChanged("VisualState"); } }
|
||||
private string _vstate = INITIAL_VISUAL_STATE;
|
||||
public string VisualState {
|
||||
get {
|
||||
// this unconventional getter acts as a reset after the VisualStateManager has played
|
||||
// i.e. if we overwrite a workspace then save, then close the window, when we open the
|
||||
// window back up, we'll see item animate b/c it has never been reset from the animate
|
||||
// state. This is necessary b/c the lifetime of Workspaces is not tied to the lifetime
|
||||
// of the View, rather it lives in the Model and is only instantiated on startup via MEF
|
||||
var return_val = _vstate;
|
||||
_vstate = INITIAL_VISUAL_STATE;
|
||||
return return_val;
|
||||
}
|
||||
set {
|
||||
_vstate = value;
|
||||
RaisePropertyChanged("VisualState");
|
||||
}
|
||||
}
|
||||
|
||||
internal WorkspaceDefinition Definition { get; private set; }
|
||||
|
||||
|
||||
@@ -45,13 +45,13 @@ namespace mbts.ui.shell
|
||||
{
|
||||
clear_error("SaveName");
|
||||
}
|
||||
var save_candidate_already_exists = Workspaces.Any(x => x.Name == SaveName);
|
||||
var save_candidate_already_exists = Workspaces.Any(x => x.Name.Replace(WorkspacesModel.SHARED_MONIKER, string.Empty) == SaveName && x.IsShared == IsPublicWorkspace);
|
||||
// Save
|
||||
_work_model.save_workspace(SaveName, ispublic: IsPublicWorkspace);
|
||||
|
||||
// if we just overwrote something, have the vm reflect it
|
||||
if (save_candidate_already_exists)
|
||||
Workspaces.Where(x => x.Name == SaveName).ForEach(x => x.VisualState = "Overridden");
|
||||
Workspaces.Where(x => x.Name.Replace(WorkspacesModel.SHARED_MONIKER, string.Empty) == SaveName && x.IsShared == IsPublicWorkspace).ForEach(x => x.VisualState = "Overridden");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace mbts.ui.shell
|
||||
{
|
||||
internal const string WORKSPACE_NAMESPACE = "workspace";
|
||||
internal const string ACTIVE_WORKSPACE_PREFIX = "Where I left off on ";
|
||||
public const string SHARED_MONIKER = " (shared)";
|
||||
public static string ACTIVE_WORKSPACE_NAME { get { return ACTIVE_WORKSPACE_PREFIX + Environment.MachineName; } }
|
||||
|
||||
private ISettingsCollector _settings_collector;
|
||||
@@ -178,7 +179,7 @@ namespace mbts.ui.shell
|
||||
public void save_workspace(string name, bool persist=true, bool ispublic=false) {
|
||||
WorkspaceDefinition workspace = get_workspace();
|
||||
workspace.is_shared = ispublic;
|
||||
if (ispublic) { name += " (shared)"; }
|
||||
if (ispublic) { name += SHARED_MONIKER; }
|
||||
|
||||
// Remove the old one if a workspace by this name exists already
|
||||
var old = Workspaces.SingleOrDefault(w => w.Name == name && ispublic == w.IsShared);
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace mbts.spark {
|
||||
Comparer<double>.Create(_compare_unit);
|
||||
|
||||
public static IComparer<TickFactors> base_unit_comparer =>
|
||||
Comparer<TickFactors>.Create(utils.CompareUtils.safe_comparison<TickFactors>(compare_base_unit));
|
||||
Comparer<TickFactors>.Create(bts.utils.CompareUtils.safe_comparison<TickFactors>(compare_base_unit));
|
||||
|
||||
public static int compare_base_unit(TickFactors a, TickFactors b) {
|
||||
int comp = _compare_base(a.tick_base, b.tick_base);
|
||||
|
||||
@@ -853,11 +853,11 @@ namespace mbts.spark {
|
||||
LadderSettings _settings => _owner._settings;
|
||||
|
||||
public void refresh() {
|
||||
bid_dirty = ObjectHelper.update_value_type(ref _bid, _settings.bid_break_line.show ? _owner._get_bid_market_break() : double.NaN);
|
||||
ask_dirty = ObjectHelper.update_value_type(ref _ask, _settings.ask_break_line.show ? _owner._get_ask_market_break() : double.NaN);
|
||||
mid_dirty = ObjectHelper.update_value_type(ref _mid, _settings.mid_break_line.show ? _owner._get_mid_market_break() : double.NaN);
|
||||
session_low_dirty = ObjectHelper.update_value_type(ref _session_low, (_settings.session_low_line.show || _settings.session_range_fill.show) ? _owner._get_session_low() : double.NaN);
|
||||
session_high_dirty = ObjectHelper.update_value_type(ref _session_high, (_settings.session_high_line.show || _settings.session_range_fill.show) ? _owner._get_session_high() : double.NaN);
|
||||
bid_dirty = ConvertUtils.update_value_type(ref _bid, _settings.bid_break_line.show ? _owner._get_bid_market_break() : double.NaN);
|
||||
ask_dirty = ConvertUtils.update_value_type(ref _ask, _settings.ask_break_line.show ? _owner._get_ask_market_break() : double.NaN);
|
||||
mid_dirty = ConvertUtils.update_value_type(ref _mid, _settings.mid_break_line.show ? _owner._get_mid_market_break() : double.NaN);
|
||||
session_low_dirty = ConvertUtils.update_value_type(ref _session_low, (_settings.session_low_line.show || _settings.session_range_fill.show) ? _owner._get_session_low() : double.NaN);
|
||||
session_high_dirty = ConvertUtils.update_value_type(ref _session_high, (_settings.session_high_line.show || _settings.session_range_fill.show) ? _owner._get_session_high() : double.NaN);
|
||||
if (session_low_dirty)
|
||||
_low_index = null;
|
||||
if (session_high_dirty)
|
||||
@@ -871,7 +871,7 @@ namespace mbts.spark {
|
||||
else new_origin = mid;
|
||||
if (double.IsNaN(new_origin))
|
||||
new_origin = _owner._get_bid_market_break();
|
||||
origin_dirty = ObjectHelper.update_value_type(ref _origin, new_origin);
|
||||
origin_dirty = ConvertUtils.update_value_type(ref _origin, new_origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ namespace mbts.ui.utility.text {
|
||||
StandardTimeOptionalSeconds = new Regex(@"^((\d?|0\d|1[0-2])|((\d|0\d|1[0-2])(:?|(:([0-5]?|[0-5]\d(( ?|( ([aApP]?|[aApP][mM])))(:?|(: ?)|(: [aApP]?[mM]?)|(:[0-5]?\d? ?[aApP]?[mM]?))|:([0-5]?|([0-5]\d( ?|( ([aApP]?|([aApP][mM]))))))))))))$"),
|
||||
MilitaryTime = new Regex(@"^(([0-1]?[0-9])|(2?[0-3]?))(:?|:([0-5]?|[0-5][0-9]?))$"),
|
||||
MilitaryTimeOptionalSeconds = new Regex(@"^((\d?|[0-1]\d|2[0-3])|((\d|[0-1]\d|2[0-3])(:?|(:([0-5]?|[0-5]\d(:?|:([0-5]?|[0-5]\d)))))))$"),
|
||||
FlexibleTime = new Regex(@"^[0-9:aApPmM ]*$"),
|
||||
DaysInMonth = new Regex(@"^([0-9]?|[0-2][0-9]|3[0-1])?$"),
|
||||
MonthAbbrev = new Regex(@"^(?i)(a(ug?|pr?)?|d(e|ec)?|f(e?b)?|j(an?|u[nl]?)?|m(a[ry]?)?|(o(ct?)?)|s(ep?)?|n(ov?)?)?$"),
|
||||
Year = new Regex(@"^\d{0,4}$"),
|
||||
|
||||
@@ -259,13 +259,13 @@ namespace mbts.ui {
|
||||
}
|
||||
|
||||
public static PropertyChangedCallback prop_changed_callback<TValue, TOwner>(PropertyChangedCallback<TValue, TOwner> callback) =>
|
||||
(d, e) => callback(ObjectHelper.try_convert<TOwner>(d), ObjectHelper.try_convert<TValue>(e.OldValue), ObjectHelper.try_convert<TValue>(e.NewValue));
|
||||
(d, e) => callback(ConvertUtils.try_convert<TOwner>(d), ConvertUtils.try_convert<TValue>(e.OldValue), ConvertUtils.try_convert<TValue>(e.NewValue));
|
||||
|
||||
public static PropertyChangedCallback prop_changed_callback<TValue, TOwner>(Action<TOwner, TValue> callback) =>
|
||||
(d, e) => callback(ObjectHelper.try_convert<TOwner>(d), ObjectHelper.try_convert<TValue>(e.NewValue));
|
||||
(d, e) => callback(ConvertUtils.try_convert<TOwner>(d), ConvertUtils.try_convert<TValue>(e.NewValue));
|
||||
|
||||
public static CoerceValueCallback coerce_value_callback<TValue, TOwner>(Func<TOwner, TValue, TValue> coerce_value) =>
|
||||
(d, obj) => coerce_value(ObjectHelper.try_convert<TOwner>(d), ObjectHelper.try_convert<TValue>(obj));
|
||||
(d, obj) => coerce_value(ConvertUtils.try_convert<TOwner>(d), ConvertUtils.try_convert<TValue>(obj));
|
||||
|
||||
public static double control_base_width(Thickness border_thickness, Thickness padding) =>
|
||||
border_thickness.Left + padding.Left + padding.Right + border_thickness.Right;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -57,8 +58,23 @@ namespace bts.collections {
|
||||
_replace(node, item);
|
||||
return false;
|
||||
} else {
|
||||
node = _init_node(item);
|
||||
_insert_node(node);
|
||||
if (_nursery.TryPopValue(k, out SortedList<Node> children)) {
|
||||
// remove orphans from _tree (orphan-nodes must reside in the main _tree until parents arrive)
|
||||
foreach (var n in children)
|
||||
_tree.Remove(n);
|
||||
node = new Node(item, children);
|
||||
} else node = new Node(item, _comparer);
|
||||
var pk = parent(item);
|
||||
if (pk.Equals(_root_id))
|
||||
_tree.Add(node);
|
||||
else if (_index.TryGetValue(pk, out Node pnode))
|
||||
pnode.children.Add(node);
|
||||
else {
|
||||
if (!_nursery.TryGetValue(pk, out SortedList<Node> l))
|
||||
_nursery.Add(pk, l = new SortedList<Node>(_comparer));
|
||||
l.Add(node);
|
||||
_tree.Add(node);
|
||||
}
|
||||
_index.Add(k, node);
|
||||
return true;
|
||||
}
|
||||
@@ -93,7 +109,10 @@ namespace bts.collections {
|
||||
_remove_from_nursery(pk, node);
|
||||
}
|
||||
if (node.children.Count > 0) {
|
||||
// any children left behind are orphans now, add them to the main _tree and _nursery
|
||||
_nursery.Add(k, node.children);
|
||||
foreach (var n in node.children)
|
||||
_tree.Add(n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,17 +134,22 @@ namespace bts.collections {
|
||||
// We are sure it is in the index because we just added it
|
||||
var node = _index[id(item)];
|
||||
if (_nursery.TryPopValue(id(item), out SortedList<Node> nl)) {
|
||||
// remove orphans from _tree (orphan-nodes must reside in the main _tree until parents arrive)
|
||||
foreach (var n in nl)
|
||||
_tree.Remove(n);
|
||||
if (node.children.Count == 0) {
|
||||
node.children = nl;
|
||||
} else if (node.children.Count > nl.Count) {
|
||||
// merge nl into node.children
|
||||
foreach (var cn in nl)
|
||||
node.children.Add(cn);
|
||||
} else {
|
||||
// merge node.children into nl
|
||||
foreach (var cn in node.children)
|
||||
nl.Add(cn);
|
||||
node.children = nl;
|
||||
if (node.children.Count > nl.Count) {
|
||||
// merge nl into node.children
|
||||
foreach (var cn in nl)
|
||||
node.children.Add(cn);
|
||||
} else {
|
||||
// merge node.children into nl
|
||||
foreach (var cn in node.children)
|
||||
nl.Add(cn);
|
||||
node.children = nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
TKey pk = parent(item);
|
||||
@@ -137,6 +161,7 @@ namespace bts.collections {
|
||||
if (!_nursery.TryGetValue(pk, out nl))
|
||||
_nursery.Add(pk, nl = new SortedList<Node>(_comparer));
|
||||
nl.Add(node);
|
||||
_tree.Add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +197,6 @@ namespace bts.collections {
|
||||
} else if (!_nursery.TryGetValue(pk, out node_list)) {
|
||||
throw new KeyNotFoundException($"Replace was called with a parent-key ({pk}) that was not found in _index or _nursery; item-key was ({id(newval)}).");
|
||||
}
|
||||
//var node_list = pk.Equals(_root_id) ? _tree : _index.TryGetValue(pk, out Node pnode) ? pnode.children : _nursery[pk];
|
||||
node_list.Remove(node);
|
||||
node.value = newval;
|
||||
node_list.Add(node);
|
||||
@@ -187,32 +211,24 @@ namespace bts.collections {
|
||||
}
|
||||
}
|
||||
node.value = newval;
|
||||
_insert_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
Node _init_node(T item) =>
|
||||
_nursery.TryPopValue(id(item), out SortedList<Node> nursery_list) ? new Node(item, nursery_list) : new Node(item, _comparer);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the node into the appropriate SortedList (i.e. roots, parent.inner, or nursery), but does NOT add it to <see cref="_index"/>, NOR check the nursery for any child-nodes waiting on this item.
|
||||
/// </summary>
|
||||
void _insert_node(Node node) {
|
||||
TKey pk = parent(node.value);
|
||||
if (pk.Equals(_root_id)) {
|
||||
_tree.Add(node);
|
||||
} else if (_index.TryGetValue(pk, out Node pnode)) {
|
||||
pnode.children.Add(node);
|
||||
} else {
|
||||
if (!_nursery.TryGetValue(pk, out SortedList<Node> nursery_list)) {
|
||||
_nursery.Add(pk, nursery_list = new SortedList<Node>(_comparer));
|
||||
if (pk.Equals(_root_id)) {
|
||||
_tree.Add(node);
|
||||
} else if (_index.TryGetValue(pk, out Node pnode)) {
|
||||
pnode.children.Add(node);
|
||||
} else {
|
||||
if (!_nursery.TryGetValue(pk, out SortedList<Node> nursery_list)) {
|
||||
_nursery.Add(pk, nursery_list = new SortedList<Node>(_comparer));
|
||||
}
|
||||
nursery_list.Add(node);
|
||||
_tree.Add(node);
|
||||
}
|
||||
nursery_list.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
void _remove_from_nursery(TKey pk, Node node) =>
|
||||
void _remove_from_nursery(TKey pk, Node node) {
|
||||
_nursery.TryPopValueIf(pk, nl => nl.Remove(node) && nl.Count == 0);
|
||||
_tree.Remove(node);
|
||||
}
|
||||
|
||||
IEnumerable<T> iterate(SortedList<Node> nodes) {
|
||||
foreach (var n in nodes) {
|
||||
@@ -259,10 +275,6 @@ namespace bts.collections {
|
||||
public IEnumerator<T> GetEnumerator() {
|
||||
foreach (T item in iterate(_tree))
|
||||
yield return item;
|
||||
foreach (var kv in _nursery) {
|
||||
foreach (T item in iterate(kv.Value))
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
|
||||
+8
-2
@@ -2,8 +2,14 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.utils {
|
||||
namespace bts.utils {
|
||||
public static class CompareUtils {
|
||||
public static int safe_compare<T>(this T a, T b, Func<T, T, int> compare) =>
|
||||
a == null ? (b == null ? 0 : -1) : (b == null ? 1 : compare(a, b));
|
||||
|
||||
public static int safe_compare<T>(this T a, T b) where T : IComparable<T> =>
|
||||
a == null ? (b == null ? 0 : -1) : (b == null ? 1 : a.CompareTo(b));
|
||||
|
||||
#region Equality
|
||||
public static bool NullCheckEquals<T>(T x, T y) =>
|
||||
NullCheckEquals(x, y, (_x, _y) => _x.Equals(_y));
|
||||
@@ -41,7 +47,7 @@ namespace mbts.utils {
|
||||
public NonGenericComparerWrapper(IComparer<T> comparer) => _comparer = comparer;
|
||||
|
||||
public int Compare(object x, object y) =>
|
||||
_comparer.Compare(ObjectHelper.try_convert<T>(x), ObjectHelper.try_convert<T>(y));
|
||||
_comparer.Compare(ConvertUtils.try_convert<T>(x), ConvertUtils.try_convert<T>(y));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
+14
-7
@@ -1,9 +1,5 @@
|
||||
using mbts.ui.utility;
|
||||
|
||||
namespace System
|
||||
{
|
||||
public static class ObjectHelper
|
||||
{
|
||||
namespace System {
|
||||
public static class ConvertUtils {
|
||||
public static object try_convert(Type type, object obj) {
|
||||
try_convert(type, obj, out object result);
|
||||
return result;
|
||||
@@ -20,7 +16,7 @@ namespace System
|
||||
result = obj;
|
||||
return true;
|
||||
} else if (type.IsEnum && obj is string) {
|
||||
result = EnumHelper.try_parse(type, obj.ToString());
|
||||
result = try_parse_enum(type, obj.ToString());
|
||||
return true;
|
||||
} else if (type.IsNullable()) {
|
||||
return try_convert(type.GetGenericArguments()[0], obj, out result);
|
||||
@@ -85,6 +81,17 @@ namespace System
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static object try_parse_enum(Type enum_type, string value, bool ignore_case = true) {
|
||||
if (!enum_type.IsEnum) throw new ArgumentException("Must an enum type.", nameof(enum_type));
|
||||
Array vals = Enum.GetValues(enum_type);
|
||||
var str_comp = ignore_case ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture;
|
||||
foreach (object v in vals) {
|
||||
if (string.Equals(v.ToString(), value, str_comp))
|
||||
return v;
|
||||
}
|
||||
return Activator.CreateInstance(enum_type);
|
||||
}
|
||||
|
||||
public static bool update_value_type<T>(ref T field, T newval) where T : struct, IEquatable<T> {
|
||||
bool rv = field.Equals(newval);
|
||||
field = newval;
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,5 +1,6 @@
|
||||
using bts.collections;
|
||||
using bts.test;
|
||||
using bts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -81,21 +82,6 @@ namespace bts.tests {
|
||||
int id(Order order) => order.id;
|
||||
int pid(Order order) => order.parent_id;
|
||||
|
||||
/// <summary>
|
||||
/// This test has been made obsolete by <see cref="test_sorted_tree_state_transitions"/>; leaving it in place until the latter has been code-reviewed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void test_sordid_tree() {
|
||||
for (int x = 0; x < 10; ++x) {
|
||||
var input = orders.ToList();
|
||||
input.Shuffle();
|
||||
var tree_asc = new SortedTree<Order, int>(Order.name_asc, id, pid, input);
|
||||
var tree_desc = new SortedTree<Order, int>(Order.name_desc, id, pid, input);
|
||||
Assert.IsTrue(tree_asc.SequenceEqual(orders));
|
||||
Assert.IsTrue(tree_desc.SequenceEqual(orders_desc));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void test_enumerate_group() {
|
||||
var comparer = Comparer<Order>.Create((l, r) => l.name.CompareTo(r.name));
|
||||
@@ -151,17 +137,20 @@ namespace bts.tests {
|
||||
// 1) Constructor Initialization
|
||||
var tree_asc = new SortedTree<Order, int>(Order.name_asc, id, pid, input);
|
||||
var tree_desc = new SortedTree<Order, int>(Order.name_desc, id, pid, input);
|
||||
assert_populated_then_clear();
|
||||
assert_state_then_clear(tree_asc, input, Order.name_asc);
|
||||
assert_state_then_clear(tree_desc, input, Order.name_desc);
|
||||
// 2) Insert
|
||||
foreach (var o in input) {
|
||||
tree_asc.Insert(o);
|
||||
tree_desc.Insert(o);
|
||||
}
|
||||
assert_populated_then_clear();
|
||||
assert_state_then_clear(tree_asc, input, Order.name_asc);
|
||||
assert_state_then_clear(tree_desc, input, Order.name_desc);
|
||||
// 3) Reset
|
||||
tree_asc.Reset(input);
|
||||
tree_desc.Reset(input);
|
||||
assert_populated_then_clear();
|
||||
assert_state_then_clear(tree_asc, input, Order.name_asc);
|
||||
assert_state_then_clear(tree_desc, input, Order.name_desc);
|
||||
// 4) Replace
|
||||
tree_asc.Reset(input);
|
||||
tree_desc.Reset(input);
|
||||
@@ -170,28 +159,12 @@ namespace bts.tests {
|
||||
Assert.IsTrue(tree_asc.TryReplace(id(v), _ => v));
|
||||
Assert.IsTrue(tree_desc.TryReplace(id(v), _ => v));
|
||||
}
|
||||
// verify asc
|
||||
var (expected_asc, expected_nursery_asc) = _build_tree(modified_input, Order.name_asc);
|
||||
assert_sequence_then_clear(tree_asc, expected_asc, expected_nursery_asc);
|
||||
// verify desc
|
||||
var (expected_desc, expected_nursery_desc) = _build_tree(modified_input, Order.name_desc);
|
||||
assert_sequence_then_clear(tree_desc, expected_desc, expected_nursery_desc);
|
||||
// DONE!! (local methods)
|
||||
void assert_populated_then_clear() {
|
||||
Assert.IsTrue(tree_asc.SequenceEqual(orders));
|
||||
Assert.IsTrue(tree_desc.SequenceEqual(orders_desc));
|
||||
clear_action(tree_asc);
|
||||
clear_action(tree_desc);
|
||||
Assert.IsFalse(tree_asc.Any());
|
||||
Assert.IsFalse(tree_desc.Any());
|
||||
}
|
||||
// verify asc/desc
|
||||
assert_state_then_clear(tree_asc, modified_input, Order.name_asc);
|
||||
assert_state_then_clear(tree_desc, modified_input, Order.name_desc);
|
||||
|
||||
void assert_sequence_then_clear(SortedTree<Order, int> tree, List<Order> expected_items, HashSet<int> expected_nursery) {
|
||||
var tree_list = tree.ToList();
|
||||
Assert.AreEqual(tree_list.Count, expected_items.Count + expected_nursery.Count);
|
||||
Assert.IsTrue(tree_list.Take(expected_items.Count).SequenceEqual(expected_items));
|
||||
// nursery order is non-deterministic, just make sure they are equivalent id-sets.
|
||||
Assert.IsTrue(new HashSet<int>(tree_list.Skip(expected_items.Count).Select(id)).SetEquals(expected_nursery));
|
||||
void assert_state_then_clear(SortedTree<Order, int> tree, IEnumerable<Order> vals, IComparer<Order> comp) {
|
||||
Assert.IsTrue(tree.SequenceEqual(_hierarchically_sorted_list(vals, comp)));
|
||||
clear_action(tree);
|
||||
Assert.IsFalse(tree.Any());
|
||||
}
|
||||
@@ -199,6 +172,74 @@ namespace bts.tests {
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void test_nursery() {
|
||||
var tree = new SortedTree<Order, int>(Order.name_asc, id, pid);
|
||||
int next_id = orders.Max(id) + 1;
|
||||
var parent = new Order('a', next_id++, Order.no_parent);
|
||||
// add 2 generations of orphans (in the test loop, these orphans will ALWAYS be added before there parent, so they aren't orphans persay, more just with a babysitter).
|
||||
// gen 1:
|
||||
var orphans = new List<Order> {
|
||||
new Order('b', next_id++, parent.id),
|
||||
new Order('c', next_id++, parent.id)
|
||||
};
|
||||
// gen 2: (Warning: there are some "magic" numbers here...this is a unit test, with fake data, so they are hard to avoid entirely).
|
||||
orphans.Add(new Order('d', next_id++, id(orphans[0])));
|
||||
orphans.Add(new Order('e', next_id++, id(orphans[1])));
|
||||
// let's add 1 orphan who's parent will NEVER arrive, unlike the orphans added above
|
||||
orphans.Add(new Order('x', next_id++, next_id++));
|
||||
var expected_result = _hierarchically_sorted_list(orders.Concat(orphans), Order.name_asc);
|
||||
// insert orphans at every position with respect to orders
|
||||
int count = 0;
|
||||
while (count++ < 10) {
|
||||
for (int i = 0; i < orders.Length; i++) {
|
||||
var input = orders.ToList();
|
||||
input.Shuffle();
|
||||
orphans.Shuffle();
|
||||
for (int j = 0; j < input.Count; j++) {
|
||||
if (j == i) {
|
||||
foreach (var o in orphans)
|
||||
tree.Insert(o);
|
||||
}
|
||||
tree.Insert(input[j]);
|
||||
}
|
||||
Assert.IsTrue(tree.SequenceEqual(expected_result));
|
||||
tree.Clear();
|
||||
Assert.IsFalse(tree.Any());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Order> _hierarchically_sorted_list(IEnumerable<Order> input, IComparer<Order> comp) {
|
||||
var node_index = input.ToDictionary(id, v => new TestNode(v));
|
||||
var tree_dict = new Dictionary<int, TestNode>();
|
||||
foreach (var o in input) {
|
||||
if (pid(o).Equals(Order.no_parent)) {
|
||||
tree_dict.Add(id(o), node_index[id(o)]);
|
||||
} else {
|
||||
if (node_index.TryGetValue(pid(o), out TestNode pnode))
|
||||
pnode.inner.Add(id(o), node_index[id(o)]);
|
||||
else {
|
||||
tree_dict.Add(id(o), node_index[id(o)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// build flat sorted list
|
||||
var flat_sorted = new List<Order>();
|
||||
var node_comp = comp.wrap<Order, TestNode>(n => n.value);
|
||||
insert_vals(tree_dict);
|
||||
return flat_sorted;
|
||||
// local methods
|
||||
void insert_vals(Dictionary<int, TestNode> map) {
|
||||
var sorted = map.Values.ToList();
|
||||
sorted.Sort(node_comp);
|
||||
foreach (var n in sorted) {
|
||||
flat_sorted.Add(n.value);
|
||||
insert_vals(n.inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Order> _randomize_values(List<Order> input) {
|
||||
// shuffle ids
|
||||
var rand_ids = input.Select(id).ToList();
|
||||
@@ -224,47 +265,18 @@ namespace bts.tests {
|
||||
return reshuffled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of Orders, hierarchically sorted, and a HashSet of Order-ids whose parent is not present. Parentless nodes are NOT included in the list of values.
|
||||
/// </summary>
|
||||
(List<Order> sorted_vals, HashSet<int> nursery) _build_tree(IEnumerable<Order> items, IComparer<Order> comp) {
|
||||
var roots = new List<TestNode>();
|
||||
var nursery_map = new Dictionary<int, Dictionary<int, TestNode>>();
|
||||
var raw_item_map = items.ToDictionary(id);
|
||||
var node_map = new Dictionary<int, TestNode>();
|
||||
foreach (var v in items) {
|
||||
int k = id(v);
|
||||
int pk = pid(v);
|
||||
TestNode node;
|
||||
if (nursery_map.TryGetValue(k, out Dictionary<int, TestNode> inner)) {
|
||||
// adopt orphans from nursery
|
||||
node = new TestNode(v, inner);
|
||||
nursery_map.Remove(k);
|
||||
} else {
|
||||
node = new TestNode(v);
|
||||
}
|
||||
node_map.Add(k, node);
|
||||
if (pk.Equals(Order.no_parent)) {
|
||||
roots.Add(node);
|
||||
} else if (node_map.TryGetValue(pk, out TestNode pnode)) {
|
||||
pnode.inner.Add(k, node);
|
||||
} else {
|
||||
if (!nursery_map.TryGetValue(pk, out Dictionary<int, TestNode> ndict)) {
|
||||
nursery_map.Add(pk, ndict = new Dictionary<int, TestNode>());
|
||||
}
|
||||
ndict.Add(k, node);
|
||||
}
|
||||
void _write_tree_to_console(SortedTree<Order, int> tree) {
|
||||
foreach (var str in _fmt_output(tree.RootNodes))
|
||||
Console.WriteLine(str);
|
||||
}
|
||||
|
||||
IEnumerable<string> _fmt_output(IEnumerable<INode<Order>> nodes) => _fmt_output(nodes, 0);
|
||||
IEnumerable<string> _fmt_output(IEnumerable<INode<Order>> en, int depth) {
|
||||
foreach (var curr in en) {
|
||||
yield return new string('-', depth * 4) + curr.value.ToString();
|
||||
foreach (var str in _fmt_output(curr.inner, depth + 1))
|
||||
yield return str;
|
||||
}
|
||||
return (roots.OrderBy(n => n.value, comp)
|
||||
.SelectMany(iterate_inner_vals).ToList(),
|
||||
new HashSet<int>(nursery_map.Values
|
||||
.SelectMany(d => d.Values.SelectMany(iterate_inner_vals))
|
||||
.Select(id)));
|
||||
// local methods
|
||||
IEnumerable<Order> iterate_inner_vals(TestNode n) =>
|
||||
new[] { n.value }.Concat(
|
||||
n.inner.Values.OrderBy(cn => cn.value, comp)
|
||||
.SelectMany(iterate_inner_vals));
|
||||
}
|
||||
|
||||
class TestNode {
|
||||
@@ -279,4 +291,4 @@ namespace bts.tests {
|
||||
public readonly Dictionary<int, TestNode> inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>BTS Spark Socket To Socket Requote Times</title>
|
||||
<title>BTS Spark Socket To Socket Requote and Hedge Times</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/perf_trace/compiled.css">
|
||||
<script src="https://cdn.polyfill.io/v2/polyfill.js?features=es6"></script>
|
||||
<style type="text/css">
|
||||
.summary-block {
|
||||
margin: 2em 1em;
|
||||
padding: 1em 1em;
|
||||
}
|
||||
.summary-block > ul {
|
||||
margin: 0em 1em;
|
||||
@@ -23,6 +23,16 @@
|
||||
.clear {
|
||||
clear:both;
|
||||
}
|
||||
.session-table tr {
|
||||
padding-top: 1em;
|
||||
}
|
||||
.session-table th {
|
||||
background-color: #4169AA;
|
||||
color:#fff;
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
border-left: 1px solid #f6f6f6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -47,7 +57,7 @@
|
||||
</ul>
|
||||
</div-->
|
||||
<div style="margin:2em 0 0 2em;"><!--div class="is-tab-content" is-tab-content is shorter than the screen to accommodate tabs at the top -->
|
||||
<h1 class="is-size-2">Socket To Socket Requote Times</h1>
|
||||
<h1 class="is-size-2">Socket To Socket Latencies</h1>
|
||||
<div id="output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,9 @@ $( document ).ready(function (){
|
||||
let urlParams = new URLSearchParams(window.location.search);
|
||||
num_rows = urlParams.get('r');
|
||||
let show_rows = urlParams.get('showrows');
|
||||
let rows_of = urlParams.get('rowsof');
|
||||
|
||||
//let trace_data_url = "./perf_trace_data";
|
||||
let trace_data_url = "/perf_trace_data";
|
||||
if(num_rows != null) {
|
||||
trace_data_url += "/" + num_rows;
|
||||
@@ -15,14 +17,50 @@ $( document ).ready(function (){
|
||||
error: ajax_err,
|
||||
crossDomain: true
|
||||
}).done(function(result) {
|
||||
|
||||
if(show_rows != null) {
|
||||
list_rows(show_rows, result);
|
||||
if(rows_of === "quot") {
|
||||
list_rows(show_rows, rows_of, result.requotes);
|
||||
}
|
||||
else {
|
||||
list_rows(show_rows, rows_of, result.hedges);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
if(result.length > 0){
|
||||
let summary_info = "<h1 class='is-size-3'><b>Host:</b> " + result[0].host + "</h1>";
|
||||
if((result.requotes.length + result.hedges.length) > 0) {
|
||||
let useful_result = (result.requotes.length > 0 ? result.requotes : result.hedges);
|
||||
let less_useful_result = (result.requotes.length === 0 ? result.requotes : result.hedges);
|
||||
|
||||
let summary_info = "<h1 class='is-size-3'><b>Host:</b> " + useful_result[0].host + "</h1><br>";
|
||||
|
||||
$("#output").html(summary_info);
|
||||
$(result).each(display_session);
|
||||
|
||||
let whole_result = result.requotes.concat(result.hedges);
|
||||
let unique_sessions = [];
|
||||
whole_result.filter(function(value) {
|
||||
if (unique_sessions.find(function(elm) {
|
||||
return elm.session_name === value.session_name;
|
||||
}) === undefined) {
|
||||
unique_sessions.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
$(unique_sessions).each(function (index, sess_val){
|
||||
console.log(sess_val.session_name);
|
||||
let r = result.requotes.find(function(elm){
|
||||
return elm.session_name === sess_val.session_name;
|
||||
});
|
||||
|
||||
let h = result.hedges.find(function(elm){
|
||||
return elm.session_name === sess_val.session_name;
|
||||
});
|
||||
|
||||
display_session(r, h);
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,29 +68,30 @@ $( document ).ready(function (){
|
||||
|
||||
});
|
||||
|
||||
function list_rows(show_rows, data) {
|
||||
function list_rows(show_rows, rows_of, data) {
|
||||
$.each(data, function(index, value){
|
||||
if(value.session_name === show_rows){
|
||||
let summary_info = "<table class='table bts-data-table'>";
|
||||
summary_info += "<thead><tr><th>Trace Time</th>" +
|
||||
"<th>Order Key</th>" +
|
||||
"<th>MD</th>" +
|
||||
"<th>Spreader</th>" +
|
||||
"<th>Fix</th>" +
|
||||
"<th>Total</th></tr></thead><tbody>";
|
||||
|
||||
summary_info += "<thead><tr><th>Trace Time</th>"
|
||||
$.each(value.rows[0], function(key, unval){
|
||||
if(key !== "trace_time" && key !== "total")
|
||||
{
|
||||
summary_info +="<th>" + format_title(key) + "</th>";
|
||||
}
|
||||
});
|
||||
summary_info += "<th>Total</th></tr></thead><tbody>";
|
||||
|
||||
$.each(value.rows, function(key, val) {
|
||||
summary_info += "<tr>";
|
||||
console.log(val);
|
||||
|
||||
summary_info += "<td>" + val.trace_time + "</td>" +
|
||||
"<td>" + val.order_key + "</td>" +
|
||||
"<td>" + val.md + "</td>" +
|
||||
"<td>" + val.spreader + "</td>" +
|
||||
"<td>" + val.fix + "</td>" +
|
||||
"<td>" + val.total + "</td>";
|
||||
|
||||
|
||||
summary_info += "<td>" + val.trace_time + "</td>";
|
||||
$.each(val, function(key, uval){
|
||||
if(key !== "trace_time" && key !== "total")
|
||||
{
|
||||
summary_info += "<td>" + uval + "</td>";
|
||||
}
|
||||
});
|
||||
summary_info += "<td>" + val.total + "</td>";
|
||||
summary_info += "</tr>";
|
||||
});
|
||||
summary_info += "</tbody></table>";
|
||||
@@ -77,7 +116,26 @@ function ajax_err (jqXHR, textStatus,errorThrown) {
|
||||
function first_upper (str)
|
||||
{
|
||||
let ret_str = str[0].toUpperCase() + str.substring(1, str.length);
|
||||
return ret_str
|
||||
return ret_str;
|
||||
}
|
||||
|
||||
function format_title (str)
|
||||
{
|
||||
let ret_str = "";
|
||||
|
||||
for(let i=0; i < str.length; i++) {
|
||||
if(i === 0 || str[i-1] === "_") {
|
||||
ret_str += str[i].toUpperCase();
|
||||
}
|
||||
else if(str[i] === "_") {
|
||||
ret_str += " ";
|
||||
}
|
||||
else {
|
||||
ret_str += str[i];
|
||||
}
|
||||
}
|
||||
|
||||
return ret_str;
|
||||
}
|
||||
|
||||
function generate_histogram(id, data)
|
||||
@@ -95,70 +153,143 @@ function generate_histogram(id, data)
|
||||
Plotly.newPlot(id, plot_data);
|
||||
}
|
||||
|
||||
function display_session (index, value) {
|
||||
//console.log(value);
|
||||
let session_info = "<ul class=''>";
|
||||
session_info += "<li id='" + value.session_name + "' class='is-size-5'><b>Session:</b> " + value.session_name + "</li>";
|
||||
session_info += "<li><b>Build Type:</b> " + value.build_type + "</li>";
|
||||
session_info += "<li><b>Version:</b> " + value.version + "</li>";
|
||||
function write_summary (histo_id, v)
|
||||
{
|
||||
session_info = "<ul>";
|
||||
session_info += "<li><b>" + format_title(v.value) + "</b></li>";
|
||||
session_info += "<li>Min: " + v.min + "</li>";
|
||||
session_info += "<li>Median: " + v.median + "</li>";
|
||||
session_info += "<li>Max: " + v.max + "</li>";
|
||||
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
|
||||
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
|
||||
session_info += "</ul>";
|
||||
session_info += "<div id='" + histo_id + "' class='histogram-div'></div>"
|
||||
|
||||
|
||||
return session_info;
|
||||
}
|
||||
|
||||
function display_session (quot_val, hed_val) {
|
||||
let safe_quot_val = (quot_val != undefined) ? quot_val : {
|
||||
session_name: hed_val.session_name,
|
||||
build_type: hed_val.build_type,
|
||||
version: hed_val.version,
|
||||
n_rows: 0,
|
||||
total: 0,
|
||||
summaries: []
|
||||
};
|
||||
let safe_hed_val = (hed_val != undefined) ? hed_val : {
|
||||
session_name: quot_val.session_name,
|
||||
build_type: quot_val.build_type,
|
||||
version: quot_val.version,
|
||||
n_rows: 0,
|
||||
total: 0,
|
||||
summaries: []
|
||||
};
|
||||
|
||||
let session_info = "<table class='session-table'>";
|
||||
session_info += "<tr><td colspan='2' id='" + safe_quot_val.session_name + "' class='is-size-5'><b>Session:</b> " + safe_quot_val.session_name + "<br>";
|
||||
session_info += "<b>Build Type:</b> " + safe_quot_val.build_type + "<br>";
|
||||
session_info += "<b>Version:</b> " + safe_quot_val.version + "<br><br></td></tr>";
|
||||
|
||||
//column headers
|
||||
session_info += "<tr><th>Requotes</th><th>Hedges</th></tr>"
|
||||
|
||||
if(num_rows != null) {
|
||||
session_info += "<li><a href='?showrows=" + value.session_name + "&r=" + num_rows + "'><b>Rows:</b> " + value.n_rows + "</a></li>";
|
||||
}
|
||||
else {
|
||||
session_info += "<li><a href='?showrows=" + value.session_name + "'><b>Rows:</b> " + value.n_rows + "</a></li>";
|
||||
}
|
||||
session_info += "<tr><td ><a href='?showrows=" + safe_quot_val.session_name + "&rowsof=quot" + "&r=" + num_rows + "'><b>Rows:</b> " + safe_quot_val.n_rows + "</a></td>";
|
||||
session_info += "<td><a href='?showrows=" + safe_hed_val.session_name + "&rowsof=hed" + "&r=" + num_rows + "'><b>Rows:</b> " + safe_hed_val.n_rows + "</a></td></tr>";
|
||||
}
|
||||
else {
|
||||
session_info += "<tr><td><a href='?showrows=" + safe_quot_val.session_name + "&rowsof=quot" + "'><b>Rows:</b> " + safe_quot_val.n_rows + "</a></td>";
|
||||
session_info += "<td><a href='?showrows=" + safe_hed_val.session_name + "&rowsof=hed" + "'><b>Rows:</b> " + safe_hed_val.n_rows + "</a></td></tr>";
|
||||
}
|
||||
|
||||
$(value.summaries).each(function(i,v){
|
||||
session_info += "<tr><td class='summary-block'>";
|
||||
$(safe_quot_val.summaries).each(function(i,v){
|
||||
if(v.value === "total") {
|
||||
session_info += "<li class='summary-block'><ul>";
|
||||
session_info += "<li><b>" + first_upper(v.value) + "</b></li>";
|
||||
session_info += "<li>Min: " + v.min + "</li>";
|
||||
session_info += "<li>Median: " + v.median + "</li>";
|
||||
session_info += "<li>Max: " + v.max + "</li>";
|
||||
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
|
||||
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
|
||||
session_info += "</ul>";
|
||||
session_info += "<div id='" + value.session_name + "-" + v.value + "' class='histogram-div'></div>"
|
||||
session_info += "</li>";
|
||||
return false
|
||||
session_info += write_summary(safe_quot_val.session_name + "-quot-" + v.value, v);
|
||||
return false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(value.summaries).each(function(i,v){
|
||||
if(v.value !== "total") {
|
||||
session_info += "<li class='summary-block'><ul>";
|
||||
session_info += "<li><b>" + first_upper(v.value) + "</b></li>";
|
||||
session_info += "<li>Min: " + v.min + "</li>";
|
||||
session_info += "<li>Median: " + v.median + "</li>";
|
||||
session_info += "<li>Max: " + v.max + "</li>";
|
||||
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
|
||||
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
|
||||
session_info += "</ul>";
|
||||
session_info += "<div id='" + value.session_name + "-" + v.value + "' class='histogram-div'></div>"
|
||||
session_info += "</li>";
|
||||
session_info += "</td>";
|
||||
session_info += "<td class='summary-block'>";
|
||||
|
||||
$(safe_hed_val.summaries).each(function(i,v){
|
||||
if(v.value === "total") {
|
||||
session_info += write_summary(safe_hed_val.session_name + "-hed-" + v.value, v);
|
||||
return false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
session_info += "</ul><hr class='clear'>";
|
||||
session_info += "</td></tr>";
|
||||
|
||||
let loop_val = 0;
|
||||
|
||||
if(safe_quot_val.summaries.length >= safe_hed_val.summaries.length ) {
|
||||
loop_val = safe_quot_val.summaries.length;
|
||||
}
|
||||
else {
|
||||
loop_val = safe_hed_val.summaries.length;
|
||||
}
|
||||
|
||||
/*
|
||||
for(let i = 0; i < loop_val; i++) {
|
||||
session_info += "<tr><td class='summary-block'>";
|
||||
if (safe_quot_val.summaries[i] !== undefined && safe_quot_val.summaries[i].value !== "total") {
|
||||
session_info += write_summary(safe_quot_val.session_name + "-quot-" + safe_quot_val.summaries[i].value, safe_quot_val.summaries[i]);
|
||||
}
|
||||
session_info += "</td><td class='summary-block'>";
|
||||
if (safe_hed_val.summaries[i] !== undefined && safe_hed_val.summaries[i].value !== "total") {
|
||||
session_info += write_summary(safe_hed_val.session_name + "-hed-" + safe_hed_val.summaries[i].value, safe_hed_val.summaries[i]);
|
||||
}
|
||||
session_info += "</td></tr>";
|
||||
}
|
||||
*/
|
||||
|
||||
session_info += "</table><hr class='clear'>";
|
||||
$("#output").append(session_info);
|
||||
|
||||
let all_data = {};
|
||||
let quot_val_data = {};
|
||||
|
||||
$(value.rows).each(function(i, row) {
|
||||
$(safe_quot_val.rows).each(function(i, row) {
|
||||
$.each(row, function(name, v) {
|
||||
if(all_data[name] === undefined){
|
||||
all_data[name] = [v];
|
||||
if(quot_val_data[name] === undefined){
|
||||
quot_val_data[name] = [v];
|
||||
}
|
||||
else {
|
||||
all_data[name].push(v);
|
||||
quot_val_data[name].push(v);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//console.log(all_data);
|
||||
let hed_val_data = {};
|
||||
|
||||
$(value.summaries).each(function(i,v){
|
||||
generate_histogram(value.session_name + "-" + v.value, all_data[v.value]);
|
||||
$(safe_hed_val.rows).each(function(i, row) {
|
||||
$.each(row, function(name, v) {
|
||||
if(hed_val_data[name] === undefined){
|
||||
hed_val_data[name] = [v];
|
||||
}
|
||||
else {
|
||||
hed_val_data[name].push(v);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if(safe_quot_val.summaries.length > 0){
|
||||
generate_histogram(safe_quot_val.session_name + "-quot-total", quot_val_data["total"]);
|
||||
}
|
||||
/*
|
||||
$(safe_quot_val.summaries).each(function(i,v){
|
||||
generate_histogram(safe_quot_val.session_name + "-quot-" + v.value, quot_val_data[v.value]);
|
||||
});
|
||||
*/
|
||||
if(safe_hed_val.summaries.length > 0){
|
||||
generate_histogram(safe_hed_val.session_name + "-hed-total", hed_val_data["total"]);
|
||||
}
|
||||
/*
|
||||
$(safe_hed_val.summaries).each(function(i,v){
|
||||
generate_histogram(safe_hed_val.session_name + "-hed-" + v.value, hed_val_data[v.value]);
|
||||
});
|
||||
*/
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPriceLantencyTester.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/thread_manager.cpp)
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/thread_manager.cpp)
|
||||
@@ -4,7 +4,10 @@
|
||||
#include "lib/config/protos/cme_reference_data.pb.h"
|
||||
#include "lib/config/protos/jetfix.pb.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/system.pb.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
namespace bts{
|
||||
namespace admin{
|
||||
@@ -83,8 +86,7 @@ AdminServiceUtils::resolve_cme_connection(const bts::api::admin::AdminData& data
|
||||
const bts::api::admin::ExchangeConnection*
|
||||
AdminServiceUtils::resolve_sim_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec){
|
||||
bts_assert(sec.exchange() == api::Venue::VENUE_SIM);
|
||||
const bts::api::Security& ){
|
||||
if(account.has_connections())
|
||||
for (const std::string& conn_name : account.connections().exchange_connection_names()){
|
||||
const bts::api::admin::ExchangeConnection* conn = find_connection(data, conn_name);
|
||||
@@ -100,24 +102,30 @@ AdminServiceUtils::resolve_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::Security& sec){
|
||||
const bts::api::admin::ExchangeConnection* rv = nullptr;
|
||||
switch(sec.exchange()){
|
||||
case api::Venue::VENUE_GLBX:
|
||||
rv = resolve_cme_connection(data, account, sec);
|
||||
break;
|
||||
case api::Venue::VENUE_UNSET:
|
||||
case api::Venue::VENUE_BTS:
|
||||
case api::Venue::VENUE_SIM:
|
||||
case api::Venue::VENUE_GLBX: {
|
||||
auto deployment_type =
|
||||
boost::to_lower_copy(config::get<config::DeploymentType>().type());
|
||||
rv = resolve_cme_connection(data, account, sec);
|
||||
if (rv == nullptr && (deployment_type == "dev" ||
|
||||
deployment_type == "demo"))
|
||||
rv = resolve_sim_connection(data, account, sec);
|
||||
break;
|
||||
case api::Venue::VENUE_CBOE:
|
||||
case api::Venue::VENUE_CBOECFE:
|
||||
case api::Venue::VENUE_ICE:
|
||||
case api::Venue::Venue_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
break;
|
||||
}
|
||||
case api::Venue::VENUE_UNSET:
|
||||
case api::Venue::VENUE_BTS:
|
||||
case api::Venue::VENUE_SIM:
|
||||
rv = resolve_sim_connection(data, account, sec);
|
||||
break;
|
||||
case api::Venue::VENUE_CBOE:
|
||||
case api::Venue::VENUE_CBOECFE:
|
||||
case api::Venue::VENUE_ICE:
|
||||
case api::Venue::Venue_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case api::Venue::Venue_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
rv = nullptr;
|
||||
break;
|
||||
rv = nullptr;
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////
|
||||
// connection mutators /////////////////////
|
||||
////////////////////////////////////////////
|
||||
|
||||
@@ -141,6 +141,12 @@ message BTSAccount{
|
||||
AccountSecurityClipLimits security_clip_limits = 90;
|
||||
AccountExchangeConnections connections = 100;
|
||||
ExchangeAccountData exchange_account_data = 110;
|
||||
// extra parameters for risk enforcement
|
||||
RiskLimitEnforcement risk_limit_enforcement=120;
|
||||
}
|
||||
|
||||
message RiskLimitEnforcement{
|
||||
bool product_limit_in_net_terms = 10;
|
||||
}
|
||||
|
||||
message AccountProductPositionLimitUnit{
|
||||
|
||||
@@ -204,7 +204,7 @@ message OrderState {
|
||||
CoverOrderState cover = 22;
|
||||
OffsetOrderState offset = 24;
|
||||
|
||||
DynamicOrderState dynamic = 1000;
|
||||
DynamicOrderState dynamic = 255; // do not use anything bigger than 255, or things will break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ message LimitOrderState {NativeOrderState native_state = 1;}
|
||||
message StopOrderState {NativeOrderState native_state = 1;}
|
||||
message StopLimitOrderState {NativeOrderState native_state = 1;}
|
||||
message IcebergOrderState {}
|
||||
message SpreaderOrderState {SpreaderChildKind child_kind = 1;}
|
||||
message SpreaderOrderState {}
|
||||
message TimeBoxOrderState {}
|
||||
message TickOrderState {}
|
||||
message LeanOrderState {}
|
||||
@@ -276,9 +276,7 @@ message TopOrderState {}
|
||||
message TriggerOrderState {}
|
||||
message CoverOrderState {}
|
||||
message OffsetOrderState {}
|
||||
message DynamicOrderState {
|
||||
map<string,string> state = 1;
|
||||
}
|
||||
message DynamicOrderState {}
|
||||
|
||||
enum OrderStatus {
|
||||
|
||||
@@ -403,12 +401,6 @@ enum ExecType
|
||||
EXEC_TYPE_TRADE_CANCEL = 72 ;
|
||||
}
|
||||
|
||||
enum SpreaderChildKind {
|
||||
SPREADER_CHILD_KIND_UNDEFINED = 0;
|
||||
SPREADER_CHILD_KIND_SPREADER_HEDGING = 1;
|
||||
SPREADER_CHILD_KIND_SPREADER_QUOTING = 2;
|
||||
}
|
||||
|
||||
enum OrderError
|
||||
{
|
||||
ORDER_ERROR_UNDEFINED = 0;
|
||||
|
||||
@@ -76,7 +76,6 @@ message CMEOptions
|
||||
// Defines amount of time Feed Engine keeps cycling before going to sleep when no useful activity can be done
|
||||
optional uint32 spin_time = 4 [default = 1];
|
||||
optional uint32 channels_per_thread = 5 [default = 3];
|
||||
optional string thread_group = 10 [default=""]; // if present this will be used to retrieve affinity settings from Threads config key
|
||||
optional uint32 udp_socket_buffer_size = 11 [default = 33554432]; //32mb
|
||||
}
|
||||
message CMEChannel {
|
||||
|
||||
@@ -64,6 +64,6 @@ message ShellPingOptions {
|
||||
// 4000 is default Windows ping timeout as specified here:
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490968(v=technet.10)
|
||||
optional int32 timeout_milliseconds_threshold = 3 [default = 4000];
|
||||
optional int32 logging_milliseconds_threshold = 4 [default = 25];
|
||||
optional int32 logging_milliseconds_threshold = 4 [default = 50];
|
||||
optional int32 maximum_backoff_milliseconds = 5 [default = 60000];
|
||||
}
|
||||
@@ -8,4 +8,5 @@ message OrdermanCfg {
|
||||
repeated string hosted_actor = 3;
|
||||
}
|
||||
repeated Executor executor = 1;
|
||||
optional uint32 default_iceberg_pause_secs = 2 [default = 0];
|
||||
}
|
||||
@@ -7,6 +7,6 @@ message OrdersSoftCleanupCutoff{
|
||||
|
||||
message SODTime{
|
||||
// utc time
|
||||
optional uint32 sod_hh = 20 [default=15];
|
||||
optional uint32 sod_hh = 20 [default=16];
|
||||
optional uint32 sod_mm = 30 [default=30];
|
||||
}
|
||||
@@ -163,6 +163,7 @@ void AsyncReader::dispatch ()
|
||||
|
||||
for (auto &msg : framed_msgs) {
|
||||
msg.set_trace(trace);
|
||||
LOG(Latency, Diag, "perf_event AsyncReader.on_fix");
|
||||
tracepoint(bts_trace, trace_order, (char *)"AsyncReader.on_fix", 0,
|
||||
msg.get_seq_num(), 0);
|
||||
me->sink.in_msg_raw (msg);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "lib/utils/TimeStamp.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "lib/orderman/orderman_utils/PerfTraceLog.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fstream>
|
||||
@@ -84,6 +83,7 @@ int SocketConnection::read(char *to_buffer, size_t read_count) {
|
||||
|
||||
bts_assert(_fd >= 0);
|
||||
auto result = ::read(_fd, to_buffer, read_count);
|
||||
LOG(Latency, Diag, "perf_event after_socket_read");
|
||||
if (_log && result > 0) {
|
||||
_in_log.write(to_buffer, result);
|
||||
_in_log.flush();
|
||||
@@ -123,18 +123,20 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
|
||||
trace.md_sn, s2s.microseconds());
|
||||
}
|
||||
|
||||
LOG(Latency, Diag, "perf_event before_socket_write");
|
||||
auto result = ::writev(_fd, iv, (int)vec_size);
|
||||
|
||||
if (trace.md.to_uint64()) {
|
||||
LOG(Latency, Info,
|
||||
"perf_event:tick_to_order s2s_us:{} fix_socket_r:{} md:{} "
|
||||
"perf_event:tick_to_order s2s_us:{} do_work_begin:{} fix_socket_r:{} "
|
||||
"md:{} "
|
||||
"md_src:{} spreader_on_book:{} spreader_modify_order:{} "
|
||||
"spreader_on_child_order_update:{} "
|
||||
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{} order_key:{} "
|
||||
"send_fix_buffer:{}",
|
||||
s2s.microseconds(), trace_cpy.fix_socket_r.to_uint64(),
|
||||
trace_cpy.md.to_uint64(), trace_cpy.md_src.to_uint64(),
|
||||
trace_cpy.spreader_on_book.to_uint64(),
|
||||
s2s.microseconds(), trace_cpy.do_work_begin.to_uint64(),
|
||||
trace_cpy.fix_socket_r.to_uint64(), trace_cpy.md.to_uint64(),
|
||||
trace_cpy.md_src.to_uint64(), trace_cpy.spreader_on_book.to_uint64(),
|
||||
trace_cpy.spreader_modify_order.to_uint64(),
|
||||
trace_cpy.spreader_on_child_order_update.to_uint64(),
|
||||
trace_cpy.order_update.to_uint64(), trace_cpy.exe_gw.to_uint64(),
|
||||
@@ -145,19 +147,20 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
|
||||
|
||||
if (trace.fix_socket_r.to_uint64()) {
|
||||
LOG(Latency, Info,
|
||||
"perf_event:fill_to_order s2s_us:{} fix_socket_r:{} md:{} md_src:{} "
|
||||
"perf_event:fill_to_order s2s_us:{} do_work_begin:{} fix_socket_r:{} "
|
||||
"md:{} md_src:{} "
|
||||
"spreader_on_book:{} spreader_modify_order:{} "
|
||||
"spreader_on_child_order_update:{} "
|
||||
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{} order_key:{} "
|
||||
"send_fix_buffer:{} ",
|
||||
s2s.microseconds(), trace_cpy.fix_socket_r.to_uint64(),
|
||||
trace_cpy.md.to_uint64(), trace_cpy.md_src.to_uint64(),
|
||||
trace_cpy.spreader_on_book.to_uint64(),
|
||||
s2s.microseconds(), trace_cpy.do_work_begin.to_uint64(),
|
||||
trace_cpy.fix_socket_r.to_uint64(), trace_cpy.md.to_uint64(),
|
||||
trace_cpy.md_src.to_uint64(), trace_cpy.spreader_on_book.to_uint64(),
|
||||
trace_cpy.spreader_modify_order.to_uint64(),
|
||||
trace_cpy.spreader_on_child_order_update.to_uint64(),
|
||||
trace_cpy.order_update.to_uint64(), trace_cpy.exe_gw.to_uint64(),
|
||||
trace_cpy.fix_socket_w.to_uint64(), trace_cpy.md_sn, trace.order_key,
|
||||
trace.send_fix_buffer.to_uint64());
|
||||
trace_cpy.fix_socket_w.to_uint64(), trace_cpy.md_sn, trace_cpy.order_key,
|
||||
trace_cpy.send_fix_buffer.to_uint64());
|
||||
_perf_log.log(trace_cpy);
|
||||
}
|
||||
|
||||
|
||||
@@ -394,6 +394,7 @@ void CoreGateway::sync_reader_disconnected(EventData const &) {
|
||||
}
|
||||
|
||||
void CoreGateway::sync_in_msg_raw(EventData const &ev) {
|
||||
LOG(Latency, Diag, "perf_event sync_in_msg_raw");
|
||||
switch (me->state) {
|
||||
case State::OFF:
|
||||
case State::PAUSED:
|
||||
@@ -412,6 +413,7 @@ void CoreGateway::sync_in_msg_raw(EventData const &ev) {
|
||||
}
|
||||
|
||||
void CoreGateway::sync_in_msg_sequenced(EventData const &ev) {
|
||||
LOG(Latency, Diag, "perf_event sync_in_msg_sequenced");
|
||||
switch (me->state) {
|
||||
case State::OFF:
|
||||
case State::PAUSED:
|
||||
|
||||
@@ -83,8 +83,10 @@ private:
|
||||
|
||||
void dispatch (InMsgBuffer const&in_msg) override
|
||||
{
|
||||
LOG(Latency, Diag, "perf_event JetFIXGateway::before_parse");
|
||||
msg.read (in_msg.data (),in_msg.size ());
|
||||
msg.set_trace(in_msg.trace());
|
||||
LOG(Latency, Diag, "perf_event JetFIXGateway::after_parse");
|
||||
handler (msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -467,6 +467,7 @@ void Session::on_hb(InMsgBuffer const &msg) {
|
||||
}
|
||||
|
||||
void Session::on_app(InMsgBuffer const &msg) {
|
||||
LOG(Latency, Diag, "perf_event on_app");
|
||||
switch (me->state) {
|
||||
case State::WLOGON:
|
||||
terminate_session(
|
||||
|
||||
@@ -54,6 +54,15 @@ struct LoggerHandle {
|
||||
Logger *logger;
|
||||
};
|
||||
|
||||
struct CharPtrWrapper
|
||||
{const char* ptr;};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream&s,CharPtrWrapper const&ptr)
|
||||
{
|
||||
s << ptr.ptr;
|
||||
return s;
|
||||
}
|
||||
|
||||
extern thread_local LoggerHandle _thread_logger;
|
||||
void configure_logging(LoggingConfig const&cfg);
|
||||
void configure_logging(bts::config::Logging const&cfg,bts::config::ThreadGroupCfg const&);
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
#include "MDPeriodicDumper.h"
|
||||
#include "lib/md/MDConsumer.h"
|
||||
#include "lib/utils/TimeStamp.h"
|
||||
#include "lib/utils/Dispatcher.h"
|
||||
#include "lib/utils/datetime.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace bts {
|
||||
namespace md {
|
||||
|
||||
/*
|
||||
* Design considerations
|
||||
*
|
||||
* - We want this test to be 'fair'
|
||||
* - We want a manageable amount of data
|
||||
* -
|
||||
*/
|
||||
|
||||
struct Entry {
|
||||
uint32_t seq;
|
||||
MDConsumerHandle handle;
|
||||
std::unique_ptr<api::Security> security;
|
||||
};
|
||||
|
||||
std::string print_book(
|
||||
PrintFormat format,
|
||||
const Entry& e,
|
||||
utils::TimeStamp twrite,
|
||||
utils::TimeStamp tread,
|
||||
uint32_t seq,
|
||||
bool ambiguous,
|
||||
const std::array<PriceSize,COCONUT_MAX_DEPTH>& bid,
|
||||
const std::array<PriceSize,COCONUT_MAX_DEPTH>& ask) {
|
||||
|
||||
std::stringstream ss;
|
||||
if(format == PrintFormat::Human) {
|
||||
ss << std::endl;
|
||||
ss << e.security->glbx().security_desc() << " (" << e.handle.key() << ")" << std::endl;
|
||||
ss << "Sequence: " << seq;
|
||||
if (ambiguous) ss << "*";
|
||||
ss << std::endl;
|
||||
ss << "TWrit: " << twrite.to_uint64() << " ns" << std::endl;
|
||||
ss << "TRead: " << tread.to_uint64() << " ns" << std::endl;
|
||||
char buffer[512];
|
||||
|
||||
snprintf(buffer, 512, "%-5s %-14s | %-14s %-5s\n", "sz", "bid", "ask", "sz");
|
||||
ss << buffer;
|
||||
ss << "-------------------- - -------------------- \n";
|
||||
for (auto x = COCONUT_MAX_DEPTH; x != 0; --x) {
|
||||
auto a = ask[x-1];
|
||||
if (!os::is_nan(a.price())) {
|
||||
snprintf(buffer, 512, "%5s %14s | %14.7f %5d\n", "", "", a.price(), a.size());
|
||||
ss << buffer;
|
||||
}
|
||||
}
|
||||
ss << "-------------------- - -------------------- \n";
|
||||
for (uint32_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
|
||||
auto b = bid[x];
|
||||
if (!os::is_nan(b.price())) {
|
||||
snprintf(buffer, 512, "%5d %14.7f | \n", b.size(), b.price());
|
||||
ss << buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (format == PrintFormat::CSV) {
|
||||
// Symbol, Id, Sequence, Ambiguous, TWrite, TRead, BidBook, AskBook
|
||||
ss << e.security->glbx().security_desc() << ","
|
||||
<< e.security->id() << ","
|
||||
<< seq << ","
|
||||
<< (ambiguous ? "true," : "false,")
|
||||
<< twrite.to_uint64() << ","
|
||||
<< tread.to_uint64() << ",";
|
||||
for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
|
||||
if (!bid[x].is_nan()) {
|
||||
ss << " " << std::setprecision(11) << bid[x].price() << "-";
|
||||
if (bid[x].implied_size() > 0) { ss << "*"; }
|
||||
ss << bid[x].size();
|
||||
}
|
||||
}
|
||||
ss << ",";
|
||||
for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
|
||||
if (!ask[x].is_nan()) {
|
||||
ss << " " << std::setprecision(11) << ask[x].price() << "-";
|
||||
if (ask[x].implied_size() > 0) { ss << "*"; }
|
||||
ss << ask[x].size();
|
||||
}
|
||||
}
|
||||
ss << std::endl;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
datetime::Date expiration(const api::Security& sec) {
|
||||
auto f = sec.future();
|
||||
return { (uint16_t)f.expiration_year(), (uint8_t)f.expiration_month(), (uint8_t)f.expiration_day() };
|
||||
}
|
||||
|
||||
std::vector<Entry> filter_entries(std::vector<Entry>& entries) {
|
||||
// Choose the front month for all symbols
|
||||
auto today = datetime::today();
|
||||
std::map<UserSymbol, Entry*> map;
|
||||
for (auto& e : entries) {
|
||||
auto type = securitytype_of(*e.security);
|
||||
// Only futures because front month is complicated
|
||||
if (type != api::SECURITYTYPE_FUTURE) continue;
|
||||
UserSymbol key { e.security->symbol(), e.security->exchange(), type };
|
||||
auto& current_entry = map[key];
|
||||
if (current_entry == nullptr) {
|
||||
current_entry = &e;
|
||||
}
|
||||
else {
|
||||
auto cur = expiration(*current_entry->security);
|
||||
auto next = expiration(*e.security);
|
||||
if (next > today && next < cur) {
|
||||
current_entry = &e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Entry> result;
|
||||
for (const auto& pair : map) {
|
||||
result.push_back(std::move(*pair.second));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void write_header(PrintFormat format, long seq_mod, const std::vector<Entry>& entries) {
|
||||
if (format == PrintFormat::Human) {
|
||||
std::cout << "Dumping every sequence % " << seq_mod << " == 0 for\n";
|
||||
std::cout << "Selected securities: " << std::endl;
|
||||
for (const auto& e : entries) {
|
||||
std::cout << e.security->glbx().security_desc() << std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// CSV header
|
||||
std::cout << "Symbol,Id,Sequence,Ambiguous,TWrite,TRead,BidBook,AskBook";
|
||||
// for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
|
||||
// std::cout << "," << "Bid" << x << ", DirectBidSize" << x << ", ImpliedBidSize" << x;
|
||||
// }
|
||||
// for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
|
||||
// std::cout << "," << "Ask" << x << ", AskSize" << x << ", ImpliedAskSize" << x;
|
||||
// }
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void run_periodic_dumper(const std::set<UserSymbol>& symbols, uint32_t seq_mod, PrintFormat format) {
|
||||
|
||||
MDConsumer consumer(symbols, nullptr);
|
||||
|
||||
// read all securities into a vector
|
||||
std::vector<Entry> entries;
|
||||
consumer.walk_security_list([&](const api::Security& sec, MDConsumerHandle handle) {
|
||||
entries.push_back({ 0, handle, std::make_unique<api::Security>(sec) });
|
||||
});
|
||||
|
||||
entries = filter_entries(entries);
|
||||
|
||||
write_header(format, seq_mod, entries);
|
||||
|
||||
auto& disp = utils::Dispatcher::background();
|
||||
|
||||
// spin around on the vector detecting changes
|
||||
std::array<PriceSize,COCONUT_MAX_DEPTH> bid,ask;
|
||||
while(true) {
|
||||
for(auto& e : entries) {
|
||||
auto sequence = e.handle.sequence();
|
||||
if (sequence != e.seq && sequence % seq_mod == 0) {
|
||||
e.seq = sequence;
|
||||
auto tread = TimeStamp::now();
|
||||
utils::TimeStamp twrite;
|
||||
e.handle.get_book(twrite, COCONUT_MAX_DEPTH, bid.data(), ask.data());
|
||||
auto seq2 = e.handle.sequence();
|
||||
// print book to stdout (async?)
|
||||
disp.post([=,&e]() {
|
||||
std::cout << print_book(format, e, twrite, tread, sequence, (sequence != seq2), bid, ask);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}//ns
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "lib/md/UserSymbol.h"
|
||||
#include <set>
|
||||
|
||||
namespace bts {
|
||||
namespace md {
|
||||
|
||||
enum class PrintFormat {
|
||||
Human,
|
||||
CSV
|
||||
};
|
||||
|
||||
void run_periodic_dumper(const std::set<UserSymbol>&, uint32_t seq_mod, PrintFormat format);
|
||||
|
||||
|
||||
}}//ns
|
||||
@@ -1,69 +0,0 @@
|
||||
#ifndef NO_CPP0X
|
||||
#include "MDPriceLantencyTester.h"
|
||||
#include "../MDConsumer.h"
|
||||
#include "lib/utils/Dispatcher.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
namespace bts {
|
||||
namespace md {
|
||||
|
||||
struct HandleData {
|
||||
MDConsumerHandle handle;
|
||||
utils::TimeStamp time;
|
||||
utils::TimeStamp last_price_change;
|
||||
float bid, ask;
|
||||
};
|
||||
|
||||
void run_md_price_latency_test(UserSymbol symbol) {
|
||||
set<UserSymbol> symbols;
|
||||
symbols.insert(symbol);
|
||||
std::shared_ptr<api::ISyntheticSecurityService> null;
|
||||
MDConsumer consumer(symbols, null);
|
||||
|
||||
vector<HandleData> handles;
|
||||
|
||||
consumer.walk_security_list([&](const Security&, MDConsumerHandle handle) {
|
||||
HandleData entry = { handle, TimeStamp(), TimeStamp(), 0, 0 };
|
||||
if (!entry.handle.is_initialized()) {
|
||||
cout << "Please start the md publisher before running this command." << endl;
|
||||
return;
|
||||
}
|
||||
handles.push_back(entry);
|
||||
});
|
||||
|
||||
cout << "key, sequence, handover, delta, bid price, ask price" << endl;
|
||||
while (true) {
|
||||
for(auto& entry : handles) {
|
||||
PriceSize bid, ask;
|
||||
if (entry.handle.get_direct_book(entry.time, 1, &bid, &ask) == HandleReadResult::SUCCESS) {
|
||||
auto bid_price = bid.price();
|
||||
auto ask_price = ask.price();
|
||||
if (bid_price != entry.bid || ask_price != entry.ask) {
|
||||
auto now = TimeStamp::now();
|
||||
auto handover = (now - entry.time);
|
||||
auto delta = (now - entry.last_price_change);
|
||||
entry.last_price_change = entry.time;
|
||||
entry.bid = bid_price;
|
||||
entry.ask = ask_price;
|
||||
utils::Dispatcher::background().post([=]() {
|
||||
cout <<
|
||||
entry.handle.key() << ','
|
||||
<< entry.handle.sequence() << ','
|
||||
<< handover.microseconds() << ','
|
||||
<< delta.microseconds() << ','
|
||||
<< bid_price << ',' << ask_price
|
||||
<< endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}}//namespace
|
||||
#endif
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/utils/bts_export.h"
|
||||
#include "../common.h"
|
||||
#include "../UserSymbol.h"
|
||||
#include <string>
|
||||
|
||||
namespace bts {
|
||||
namespace md {
|
||||
|
||||
BTS_EXPORT void run_md_price_latency_test(UserSymbol);
|
||||
|
||||
|
||||
}}//namespace
|
||||
|
||||
@@ -33,8 +33,11 @@ string dump(IntPrice price) {
|
||||
string dump(PriceSize ps) {
|
||||
stringstream res;
|
||||
res << "{";
|
||||
res << "'price': " << std::setprecision(11) << ps.price() << ", ";
|
||||
res << "'size': " << ps.size() << " ";
|
||||
res << "'price': " << std::setprecision(11) << ps.price();
|
||||
if (!ps.is_nan()) {
|
||||
res << " (^" << (int)ps.exponent() << ")";
|
||||
}
|
||||
res << ", 'size': " << ps.size() << " ";
|
||||
// res << "'mant': " << ps.mantissa() << ", ";
|
||||
// res << "'exp': " << (int)ps.exponent() << " ";
|
||||
res << "}";
|
||||
@@ -162,34 +165,42 @@ string dump_entry(const HotDepthEntry& entry) {
|
||||
res << "{ ";
|
||||
res << "'timestamp': " << dump(entry.time()) << ", ";
|
||||
res << "'key': " << entry.key << ", ";
|
||||
|
||||
res << "\n'bid': [";
|
||||
for (const auto& level : entry.direct) {
|
||||
res << dump(level.bid()) << ", ";
|
||||
}
|
||||
res << "], ";
|
||||
|
||||
res << "\n'ask': [";
|
||||
for (const auto& level : entry.direct) {
|
||||
res << dump(level.ask()) << ", ";
|
||||
}
|
||||
res << "], ";
|
||||
|
||||
res << "'\nimplied_bid': [";
|
||||
for (const auto& level : entry.implied) {
|
||||
res << dump(level.bid()) << ", ";
|
||||
}
|
||||
res << "], ";
|
||||
|
||||
res << "'\nimplied_ask': [";
|
||||
for (const auto& level : entry.implied) {
|
||||
res << dump(level.ask()) << ", ";
|
||||
}
|
||||
res << "], ";
|
||||
|
||||
res << "\n'settlement': '" << dump(entry.settlement) << "', ";
|
||||
res << "'settlement': '" << dump(entry.settlement) << "', ";
|
||||
res << "'status': '" << CoconutSecurityStatus_Name((CoconutSecurityStatus)entry.status) << "', ";
|
||||
res << "'sequence': '" << entry.sequence << "', ";
|
||||
|
||||
res << "\n 'bid': [";
|
||||
for (const auto& level : entry.direct) {
|
||||
if (!level.bid().is_nan()) {
|
||||
res << dump(level.bid()) << ", ";
|
||||
}
|
||||
}
|
||||
res << "], ";
|
||||
|
||||
res << "\n 'ask': [";
|
||||
for (const auto& level : entry.direct) {
|
||||
if (!level.ask().is_nan()) {
|
||||
res << dump(level.ask()) << ", ";
|
||||
}
|
||||
}
|
||||
res << "], ";
|
||||
|
||||
res << "\n 'implied_bid': [";
|
||||
for (const auto& level : entry.implied) {
|
||||
if (!level.bid().is_nan()) {
|
||||
res << dump(level.bid()) << ", ";
|
||||
}
|
||||
}
|
||||
res << "], ";
|
||||
|
||||
res << "\n 'implied_ask': [";
|
||||
for (const auto& level : entry.implied) {
|
||||
if (!level.ask().is_nan()) {
|
||||
res << dump(level.ask()) << ", ";
|
||||
}
|
||||
}
|
||||
res << "]";
|
||||
|
||||
res << " }";
|
||||
return res.str();
|
||||
}
|
||||
|
||||
@@ -305,17 +305,6 @@ CoconutSecurityStatus MDConsumerHandle::status() const {
|
||||
}
|
||||
never;
|
||||
}
|
||||
uint16_t MDConsumerHandle::sequence() const {
|
||||
switch(_type) {
|
||||
case HANDLETYPE_VOID: return 0;
|
||||
case HANDLETYPE_DEPTH: return book.hot->sequence;
|
||||
case HANDLETYPE_BBO: return bbo.hot->sequence;
|
||||
case HANDLETYPE_SYNTHETIC:
|
||||
bts_assert(!synthetic.def->legs.empty());
|
||||
return sane::min_by<MDConsumerHandle, TimeStamp>(synthetic.def->legs, [](const MDConsumerHandle& h) { return h.last_market_update(); })->sequence();
|
||||
}
|
||||
never;
|
||||
}
|
||||
|
||||
}}//namespace
|
||||
|
||||
|
||||
@@ -159,7 +159,17 @@ public:
|
||||
bool is_heart_beating() const;
|
||||
TimeStamp heartbeat() const;
|
||||
CoconutSecurityStatus status() const;
|
||||
uint16_t sequence() const;
|
||||
uint32_t sequence() const {
|
||||
switch(_type) {
|
||||
case HANDLETYPE_VOID: return 0;
|
||||
case HANDLETYPE_DEPTH: return book.hot->sequence;
|
||||
case HANDLETYPE_BBO: return bbo.hot->sequence;
|
||||
case HANDLETYPE_SYNTHETIC:
|
||||
bts_assert(!synthetic.def->legs.empty());
|
||||
return sane::min_by<MDConsumerHandle, TimeStamp>(synthetic.def->legs, [](const MDConsumerHandle& h) { return h.last_market_update(); })->sequence();
|
||||
}
|
||||
bts_assert(false);
|
||||
}
|
||||
security_key key() const {
|
||||
switch(_type) {
|
||||
case HANDLETYPE_VOID: return invalid_security_key;
|
||||
|
||||
Loaded 100 of 202 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user