Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea4e599ac6 | ||
|
|
fd2ba70031 | ||
|
|
ab410c339f | ||
|
|
9e3f16d8bc | ||
|
|
a3f194d708 | ||
|
|
bf87c46fcd | ||
|
|
222d4b266e | ||
|
|
d593eaf058 | ||
|
|
55d021ccb1 | ||
|
|
b02f814c65 | ||
|
|
8c42aa93a2 | ||
|
|
357bc5ad44 | ||
|
|
07fb9e8eb7 | ||
|
|
9f9fc4c8ab | ||
|
|
2aef052398 | ||
|
|
1c2027506a | ||
|
|
c3274b2008 | ||
|
|
99aef9e86e | ||
|
|
8e9233022e | ||
|
|
4c91538f22 | ||
|
|
320427c9c5 | ||
|
|
5e8543902e |
No files matched your search
@@ -7,6 +7,24 @@
|
||||
"commands": [
|
||||
"dotnet-format"
|
||||
]
|
||||
},
|
||||
"fable": {
|
||||
"version": "4.0.0-theta-018",
|
||||
"commands": [
|
||||
"fable"
|
||||
]
|
||||
},
|
||||
"fantomas": {
|
||||
"version": "5.2.2",
|
||||
"commands": [
|
||||
"fantomas"
|
||||
]
|
||||
},
|
||||
"fake-cli": {
|
||||
"version": "6.0.0",
|
||||
"commands": [
|
||||
"fake"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
Submodule externals updated: 4d96613ca8...4a3e40193e.
@@ -1,53 +1,55 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<ComputerSpecificConfig>..\..\tools\override\$(computername).props</ComputerSpecificConfig>
|
||||
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
|
||||
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
|
||||
<Externals>$(SparkRoot)\externals\windows</Externals>
|
||||
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
|
||||
<ErrorReport>None</ErrorReport>
|
||||
<Prefer32Bit>False</Prefer32Bit>
|
||||
<Deterministic>true</Deterministic>
|
||||
<EnableDefaultContentItems>False</EnableDefaultContentItems>
|
||||
<ShowMissingItemTypes>false</ShowMissingItemTypes>
|
||||
<Configuration Condition="'$(configuration)'==''">Debug</Configuration>
|
||||
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Output">
|
||||
<BaseOutputPath>$(SparkRoot)\build\$(Configuration)</BaseOutputPath>
|
||||
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
|
||||
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
|
||||
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import
|
||||
Project="$(ComputerSpecificConfig)"
|
||||
Condition="Exists($(ComputerSpecificConfig))" />
|
||||
|
||||
<PropertyGroup Label="ProtoBuf">
|
||||
<ProtocPluginBase>$(SparkRoot)\src\lib\api\service_compiler</ProtocPluginBase>
|
||||
<protoc>$(Externals)\bin\protoc.exe</protoc>
|
||||
<protogen>$(Externals)\bin\protogen.exe</protogen>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<Proto2></Proto2>
|
||||
<Proto3>
|
||||
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
|
||||
<OutCS>%(OutBase)%(FileName).cs</OutCS>
|
||||
<OutCSClient>%(OutBase)%(FileName).client.pb.cs</OutCSClient>
|
||||
<OutCSServer>%(OutBase)%(FileName).server.pb.cs</OutCSServer>
|
||||
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
|
||||
<CompileOptions></CompileOptions>
|
||||
</Proto3>
|
||||
<Proto3Excel>
|
||||
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
|
||||
<OutCSExcel>%(OutBase)%(FileName).excel.pb.cs</OutCSExcel>
|
||||
<CompileOptions></CompileOptions>
|
||||
</Proto3Excel>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
</Project>
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<ComputerSpecificConfig>..\..\tools\override\$(computername).props</ComputerSpecificConfig>
|
||||
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
|
||||
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
|
||||
<Externals>$(SparkRoot)\externals\windows</Externals>
|
||||
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
|
||||
<ErrorReport>None</ErrorReport>
|
||||
<Prefer32Bit>False</Prefer32Bit>
|
||||
<Deterministic>true</Deterministic>
|
||||
<EnableDefaultContentItems>False</EnableDefaultContentItems>
|
||||
<ShowMissingItemTypes>false</ShowMissingItemTypes>
|
||||
<Configuration Condition="'$(configuration)'==''">Debug</Configuration>
|
||||
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
||||
<LangVersion>11</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Output">
|
||||
<BaseOutputPath>$(SparkRoot)\build\$(Configuration)</BaseOutputPath>
|
||||
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
|
||||
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
|
||||
<ProtoOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</ProtoOutputPath>
|
||||
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import
|
||||
Project="$(ComputerSpecificConfig)"
|
||||
Condition="Exists($(ComputerSpecificConfig))" />
|
||||
|
||||
<PropertyGroup Label="ProtoBuf">
|
||||
<ProtocPluginBase>$(SparkRoot)\src\lib\api\service_compiler</ProtocPluginBase>
|
||||
<protoc>$(Externals)\bin\protoc.exe</protoc>
|
||||
<protogen>$(Externals)\bin\protogen.exe</protogen>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<Proto2></Proto2>
|
||||
<Proto3>
|
||||
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
|
||||
<OutCS>%(OutBase)%(FileName).cs</OutCS>
|
||||
<OutCSClient>%(OutBase)%(FileName).client.pb.cs</OutCSClient>
|
||||
<OutCSServer>%(OutBase)%(FileName).server.pb.cs</OutCSServer>
|
||||
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
|
||||
<CompileOptions></CompileOptions>
|
||||
</Proto3>
|
||||
<Proto3Excel>
|
||||
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
|
||||
<OutCSExcel>%(OutBase)%(FileName).excel.pb.cs</OutCSExcel>
|
||||
<CompileOptions></CompileOptions>
|
||||
</Proto3Excel>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
</Project>
|
||||
@@ -43,6 +43,7 @@ namespace mbts.spark {
|
||||
readonly ObservableHashSet<AnalyticsExpirationParametersRowVM> _selected_rows = new();
|
||||
readonly DeferrableAction _raise_selected_rows_changed;
|
||||
readonly IRaiseCanExecuteChangedCommand _set_driver_to_nearest_exp_cmd;
|
||||
IList<string>? _pending_selected_symbols;
|
||||
|
||||
AnalyticsExpirationParametersMap? _parameter_map;
|
||||
(ICommand cmd, IDisposable can_exe_sub)? _smart_copy_roll_target_to_selection_cmd;
|
||||
@@ -65,9 +66,7 @@ namespace mbts.spark {
|
||||
_is_dirty = backing(nameof(is_dirty), () => rows.Any(r => r.is_dirty));
|
||||
|
||||
if (startup_data is not null) {
|
||||
if (!startup_data.selected_symbols.is_null_or_empty()) {
|
||||
selected_symbols.AddRange(startup_data.selected_symbols);
|
||||
}
|
||||
_pending_selected_symbols = startup_data.selected_symbols;
|
||||
auto_copy_rolls_is_checked = startup_data.auto_copy_rolls_is_checked;
|
||||
auto_copy_rates_is_checked = startup_data.auto_copy_rates_is_checked;
|
||||
}
|
||||
@@ -297,8 +296,8 @@ namespace mbts.spark {
|
||||
symbols.sync_with(_parameters.Select(p => p.security.symbol).Distinct().Ordered());
|
||||
using var _ = _sync_visible_rows.defer();
|
||||
if (is_first_update) {
|
||||
// select all symbols on first update
|
||||
selected_symbols.sync_with(symbols);
|
||||
selected_symbols.sync_with(_pending_selected_symbols?.Intersect(symbols) ?? symbols);
|
||||
_pending_selected_symbols = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,9 +63,9 @@ namespace mbts.spark {
|
||||
vs_input, vs_nonvol_data.contracts, aux_data_map);
|
||||
_timer.stop();
|
||||
var tag_str = tag == null ? "Null" : tag.ToString();
|
||||
bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
|
||||
$"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
|
||||
$"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
|
||||
// bts.Log.Gen.info($"SingleVolSurfaceCommittedOutputRetriever._recompute_and_publish " +
|
||||
// $"vs_id: {_vs_id} time:{_timer.lastIntervalTime} ms tag:{tag_str} " +
|
||||
// $"tid:{_tid()}"); //output: {vs_output.log_string()}*/");
|
||||
_logger.log(_log_id, _log_id, _timer.lastIntervalTime);
|
||||
_bb.post(_out_bb_computed_vol_tag, vs_output);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace mbts.spark {
|
||||
var exp_price_map = OptionDriverPriceMap.get_vs_price_map(
|
||||
all_sym_price_map, (s) => _option_symbols.Contains(s));
|
||||
if (sc_data != null && vs_nonvol_input != null && exp_price_map != null) {
|
||||
bts.Log.Gen.info($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
|
||||
bts.Log.Gen.diag($"SingleVolSurfaceNonVolDataSource vs_id:{_vs_id} " +
|
||||
"publishing VolSurfaceNonVolData");
|
||||
_bb.post(_out_bb_vs_nonvol_data_tag,
|
||||
new VolSurfaceNonVolData(
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace mbts.analytics {
|
||||
output.Add(symbol, symbol_data);
|
||||
}
|
||||
IReadOnlyDictionary<string, TData> ro_output = output;
|
||||
bts.Log.Gen.info($"SymbolAggregationPublisher._on_timer publishing " +
|
||||
bts.Log.Gen.diag($"SymbolAggregationPublisher._on_timer publishing " +
|
||||
$"tag: {_out_tag} sz: {ro_output.Count}");
|
||||
_bb.post(_out_tag, ro_output);
|
||||
}
|
||||
|
||||
@@ -28,9 +28,10 @@ namespace mbts.spark {
|
||||
_candidate_value.Changed += on_candidate_changed;
|
||||
_is_dirty.Changed += (o, n) => {
|
||||
Log.assert(o != n);
|
||||
Log.Ui.info(
|
||||
$"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
|
||||
$"commited: {committed_value}; candidate: {candidate_value}");
|
||||
// TODO: John, this was logged too many times
|
||||
// Log.Ui.info(
|
||||
// $"VolSurface '{expiration}.{column}' is_dirty changed to {n}. " +
|
||||
// $"commited: {committed_value}; candidate: {candidate_value}");
|
||||
};
|
||||
|
||||
void on_committed_changed(object? oldval, object? newval) =>
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace mbts.spark {
|
||||
get => _core_settings.font_weight_override;
|
||||
set {
|
||||
using var _ = detect_property_change();
|
||||
_core_settings.with(font_weight_override: value.HasValue ? new NullableFontWeightProto(value) : null);
|
||||
_core_settings.with(font_weight_override: value.HasValue ? new(value) : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace mbts {
|
||||
!(x == y);
|
||||
|
||||
public override string ToString() =>
|
||||
$"{security_tuple} {expiration.ToShortDateString()}";
|
||||
$"{security_tuple} {SecurityFormatter.format_expiration(expiration)}";
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -88,8 +88,8 @@ namespace mbts.analytics {
|
||||
|
||||
public void set_driver_price(
|
||||
IDictionary<VolSurfaceExpiration, double> driver_price_map) {
|
||||
Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_driver_price " +
|
||||
$"vs_id: {_vs_id} exp_count: {driver_price_map.Count}");
|
||||
// Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_driver_price " +
|
||||
// $"vs_id: {_vs_id} exp_count: {driver_price_map.Count}");
|
||||
_driver_price_map = new Dictionary<VolSurfaceExpiration, double>(driver_price_map);
|
||||
_compute_and_fire(InputType.DriverPrice);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace bts {
|
||||
}
|
||||
|
||||
private void _on_server_sec_map(IReadOnlyDictionary<uint, SecurityMD> sec_map) {
|
||||
_log_options(sec_map);
|
||||
//_log_options(sec_map);
|
||||
bts.Log.Gen.info(String.Format("MergedMDDaemon._on_server_sec_map sz: {0}", sec_map.Count()));
|
||||
_server_sec_map = new Dictionary<uint, SecurityMD>(sec_map);
|
||||
_merge_and_post();
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
_window_factories = new() {
|
||||
new MarketViewWindowFactory(
|
||||
this, bus, settings_hub, orders_model, position_model, sec_meta_store),
|
||||
new OptionsViewWindowFactory(this, bus),
|
||||
new OptionsViewWindowFactory(this, orders_model, bus),
|
||||
new TimeAndSalesWindowFactory(this, bus, consumer, settings_hub, orders_model.security_auxiliary_model),
|
||||
};
|
||||
|
||||
|
||||
@@ -79,10 +79,12 @@ namespace bts.shell.market_view {
|
||||
var ask_col_index = columns.FindIndex(x => x.id == MarketViewWindowFactory.col_id(Columns.Ask));
|
||||
if (bid_col_index > -1 && ask_col_index > -1) {
|
||||
if (!columns[bid_col_index].background_color_override.HasValue) {
|
||||
columns[bid_col_index] = columns[bid_col_index].with(background_color_override: new NullableColorProto(BTSColors.MarketData.bid.value));
|
||||
columns[bid_col_index] = columns[bid_col_index].with(
|
||||
background_color_override: new(BTSColors.MarketData.bid));
|
||||
}
|
||||
if (!columns[ask_col_index].background_color_override.HasValue) {
|
||||
columns[ask_col_index] = columns[ask_col_index].with(background_color_override: new NullableColorProto(BTSColors.MarketData.ask.value));
|
||||
columns[ask_col_index] = columns[ask_col_index].with(
|
||||
background_color_override: new(BTSColors.MarketData.ask));
|
||||
}
|
||||
}
|
||||
// and enable background color upgrade
|
||||
|
||||
@@ -70,11 +70,13 @@ namespace bts.shell {
|
||||
bool applied = false;
|
||||
if (bid_col_index > -1 && ask_col_index > -1) {
|
||||
if (!columns[bid_col_index].background_color_override.HasValue) {
|
||||
columns[bid_col_index] = columns[bid_col_index].with(background_color_override: new NullableColorProto(BTSColors.MarketData.bid.value));
|
||||
columns[bid_col_index] = columns[bid_col_index].with(
|
||||
background_color_override: new(BTSColors.MarketData.bid));
|
||||
applied = true;
|
||||
}
|
||||
if (!columns[ask_col_index].background_color_override.HasValue) {
|
||||
columns[ask_col_index] = columns[ask_col_index].with(background_color_override: new NullableColorProto(BTSColors.MarketData.ask.value));
|
||||
columns[ask_col_index] = columns[ask_col_index].with(
|
||||
background_color_override: new(BTSColors.MarketData.ask));
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,14 @@ namespace mbts.spark {
|
||||
|
||||
class OptionsViewWindowFactory : ShellWindowFactory<OptionsViewSettings> {
|
||||
readonly IBus _bus;
|
||||
readonly NMSheetAllTaskingDataSource _tasking_datasource;
|
||||
|
||||
public OptionsViewWindowFactory(IShellModule module, IBus bus)
|
||||
public OptionsViewWindowFactory(IShellModule module, OrdersModel orders_model, IBus bus)
|
||||
: base(module, OptionsViewWindow.WINDOW_TYPE) {
|
||||
_bus = bus;
|
||||
_dispose = bus.subscribe(SparkTags.show_default_options_view).OnDispatcher().Subscribe(
|
||||
sc => _bus.post(SparkTags.ShowWindow, _create(OptionsViewSettings.Default(sc))));
|
||||
_tasking_datasource = new NMSheetAllTaskingDataSource(orders_model, bus);
|
||||
}
|
||||
|
||||
public override Task<IShellWindow> load(OptionsViewSettings state) =>
|
||||
|
||||
@@ -410,13 +410,13 @@ namespace mbts.spark {
|
||||
day_change = new DaySettings {
|
||||
// (neutral)
|
||||
font = _default_font,
|
||||
background_color = Colors.Transparent.rgb_color(),
|
||||
background_color = Colors.Transparent.to_uint(),
|
||||
// negative
|
||||
negative_font_color = Colors.White.rgb_color(),
|
||||
negative_background_color = Colors.Red.rgb_color(),
|
||||
negative_font_color = Colors.White.to_uint(),
|
||||
negative_background_color = Colors.Red.to_uint(),
|
||||
// positive
|
||||
positive_font_color = Colors.White.rgb_color(),
|
||||
positive_background_color = Colors.Green.rgb_color()
|
||||
positive_font_color = Colors.White.to_uint(),
|
||||
positive_background_color = Colors.Green.to_uint()
|
||||
},
|
||||
session_low = _default_item,
|
||||
session_high = _default_item,
|
||||
@@ -431,8 +431,8 @@ namespace mbts.spark {
|
||||
public static LadderPanelPricePointSettings tt_default {
|
||||
get {
|
||||
var rv = historic_default;
|
||||
rv.day_change.background_color = Color.FromRgb(113, 201, 69).rgb_color();
|
||||
rv.day_change.font.font_color = Colors.Black.rgb_color();
|
||||
rv.day_change.background_color = Color.FromRgb(113, 201, 69).to_uint();
|
||||
rv.day_change.font.font_color = Colors.Black.to_uint();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
@@ -484,7 +484,7 @@ namespace mbts.spark {
|
||||
static ItemSettings _default_item =>
|
||||
new ItemSettings {
|
||||
font = _default_font,
|
||||
background_color = Colors.Transparent.rgb_color()
|
||||
background_color = Colors.Transparent.to_uint()
|
||||
};
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace mbts.spark {
|
||||
security_key = use_self_security ? null : selected_security_key,
|
||||
price_type = _selected_price_type,
|
||||
display = new LadderMarketBreakLine {
|
||||
color = color.rgb_color(),
|
||||
color = color.to_uint(),
|
||||
show = show,
|
||||
refresh_mode = refresh_mode
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace mbts.spark {
|
||||
class LadderOrderVM : DisposableViewModelBase {
|
||||
static readonly Color _colorbg = Colors.White;
|
||||
static readonly Color _darkbg = Color.FromRgb(0xbf, 0xbf, 0xbf);
|
||||
static readonly Brush _color_bg_brush = _colorbg.rgb_color().rgb_brush();
|
||||
static readonly Brush _color_bg_brush = _colorbg.to_uint().rgb_brush();
|
||||
|
||||
readonly LadderVM _owner;
|
||||
readonly double _real_price;
|
||||
@@ -228,7 +228,7 @@ namespace mbts.spark {
|
||||
if (_orders.TryGetValue(order.Key, out var opm)) {
|
||||
algo_studio_abbreviation = (opm.meta?.abbreviation).is_null_or_empty() ?
|
||||
_abbreviate_algo(opm.algo) : opm.meta?.abbreviation ?? string.Empty;
|
||||
border = opm.meta?.badge_color?.rgb_color().rgb_brush() ?? border;
|
||||
border = opm.meta?.badge_color?.to_uint().rgb_brush() ?? border;
|
||||
}
|
||||
algo_type = order.order_type().is_bts_algo_type() ?
|
||||
order.order_type() : CustomOrderDefinition.CustomOneofCase.None;
|
||||
|
||||
@@ -42,13 +42,13 @@ namespace mbts.spark {
|
||||
public LadderPanelPricePointSettings.ItemSettings create() =>
|
||||
new LadderPanelPricePointSettings.ItemSettings {
|
||||
font = new FontParamsProto {
|
||||
font_color = font_color.rgb_color(),
|
||||
font_color = font_color.to_uint(),
|
||||
font_size = font_size,
|
||||
typeface = new(
|
||||
font_family.name() ?? TextUtils.StandardFontFamilyName,
|
||||
font_weight.ToOpenTypeWeight())
|
||||
},
|
||||
background_color = background_color.rgb_color(),
|
||||
background_color = background_color.to_uint(),
|
||||
show_label = show_label
|
||||
};
|
||||
|
||||
@@ -111,11 +111,11 @@ namespace mbts.spark {
|
||||
show_label = base_settings.show_label,
|
||||
background_color = base_settings.background_color,
|
||||
// negative
|
||||
negative_font_color = negative_font_color.rgb_color(),
|
||||
negative_background_color = negative_background_color.rgb_color(),
|
||||
negative_font_color = negative_font_color.to_uint(),
|
||||
negative_background_color = negative_background_color.to_uint(),
|
||||
// positive
|
||||
positive_font_color = positive_font_color.rgb_color(),
|
||||
positive_background_color = positive_background_color.rgb_color()
|
||||
positive_font_color = positive_font_color.to_uint(),
|
||||
positive_background_color = positive_background_color.to_uint()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,8 +700,8 @@ namespace mbts.spark {
|
||||
font_weight = font_weight.ToOpenTypeWeight(),
|
||||
width = width,
|
||||
show = show,
|
||||
foreground = foreground.rgb_color(),
|
||||
background = background.rgb_color()
|
||||
foreground = foreground.to_uint(),
|
||||
background = background.to_uint()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -891,7 +891,7 @@ namespace mbts.spark {
|
||||
};
|
||||
root_bd.SetBinding(
|
||||
Border.BackgroundProperty, new Binding(nameof(background_brush)) { Source = this });
|
||||
return button;
|
||||
return root_bd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace bts.shell {
|
||||
sorting_criteria,
|
||||
_vm.anchors,
|
||||
filters,
|
||||
state.grid.with(columns: cols, background: _vm.grid_background_color.rgb_color()),
|
||||
state.grid.with(columns: cols, background: _vm.grid_background_color.to_uint()),
|
||||
calc_type,
|
||||
_vm.header_row_display,
|
||||
menu_display: _menu_display_vm?.create());
|
||||
|
||||
@@ -68,10 +68,10 @@ namespace bts.shell.pnl {
|
||||
typeface = new TypefaceProto(
|
||||
cell_appearance.font_family?.name(),
|
||||
(cell_appearance.font_weight ?? FontWeights.Normal).ToOpenTypeWeight()),
|
||||
font_color = cell_appearance.foreground?.rgb_color() ?? 0,
|
||||
font_color = cell_appearance.foreground?.to_uint() ?? 0,
|
||||
font_size = cell_appearance.font_size,
|
||||
},
|
||||
background = cell_appearance.background?.rgb_color()
|
||||
background = cell_appearance.background?.to_uint()
|
||||
};
|
||||
|
||||
public PNLCellAppearance to_appearance() {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -103,14 +103,14 @@ namespace bts.shell.pnl {
|
||||
public PNLAnimation to_animation(PNLView.Columns key) =>
|
||||
new PNLAnimation {
|
||||
column = key,
|
||||
foreground = is_font_color_overridden ? font_color_override.color.rgb_color() : (uint?)null,
|
||||
background = is_background_overridden ? background_override.color.rgb_color() : (uint?)null,
|
||||
foreground = is_font_color_overridden ? font_color_override.color.to_uint() : (uint?)null,
|
||||
background = is_background_overridden ? background_override.color.to_uint() : (uint?)null,
|
||||
font_weight = is_font_weight_overridden ? font_weight_override.ToOpenTypeWeight() : (int?)null
|
||||
};
|
||||
|
||||
public PNLCellAppearanceProto to_appearance_proto() =>
|
||||
new PNLCellAppearanceProto {
|
||||
background = is_background_overridden ? background_override.color.rgb_color() : (uint?)null,
|
||||
background = is_background_overridden ? background_override.color.to_uint() : (uint?)null,
|
||||
font_params = to_font_params()
|
||||
};
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace bts.shell.pnl {
|
||||
typeface = new TypefaceProto(
|
||||
font_family_override?.name(),
|
||||
is_font_weight_overridden ? font_weight_override.ToOpenTypeWeight() : 0),
|
||||
font_color = is_font_color_overridden ? font_color_override.color.rgb_color() : 0,
|
||||
font_color = is_font_color_overridden ? font_color_override.color.to_uint() : 0,
|
||||
font_size = font_size_override
|
||||
};
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace bts.shell.pnl {
|
||||
// group
|
||||
var (group_cell_appearance, group_row_background_colors) = group_row_defaults.create();
|
||||
s.group_cell_appearance = PNLCellAppearanceProto.from_appearance(group_cell_appearance);
|
||||
s.group_background_colors.SetItems(group_row_background_colors.Select(c => c.rgb_color()));
|
||||
s.group_background_colors.SetItems(group_row_background_colors.Select(c => c.to_uint()));
|
||||
//
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -329,20 +329,19 @@ namespace bts.shell.pnl {
|
||||
}
|
||||
|
||||
static (string desc, Color fg) _fills_title(Columns key) {
|
||||
const uint net_fg = 0x333333u;
|
||||
switch (key) {
|
||||
case Columns.SODLongPos: return ("Open Buy Fills", BTSColors.MarketData.bid);
|
||||
case Columns.SODShortPos: return ("Open Sell Fills", BTSColors.MarketData.ask);
|
||||
case Columns.SODNetPos: return ("Open Fills", net_fg.rgb_color());
|
||||
case Columns.SODNetPos: return ("Open Fills", BTSColors.UI.default_text);
|
||||
case Columns.DayLongPos: return ("Day Buy Fills", BTSColors.MarketData.bid);
|
||||
case Columns.DayShortPos: return ("Day Sell Fills", BTSColors.MarketData.ask);
|
||||
case Columns.DayNetPos: return ("Day Fills", net_fg.rgb_color());
|
||||
case Columns.DayNetPos: return ("Day Fills", BTSColors.UI.default_text);
|
||||
case Columns.RawDayLongPos: return ("Raw Day Buy Fills", BTSColors.MarketData.bid);
|
||||
case Columns.RawDayShortPos: return ("Raw Day Sell Fills", BTSColors.MarketData.ask);
|
||||
case Columns.RawDayNetPos: return ("Raw Day Fills", net_fg.rgb_color());
|
||||
case Columns.RawDayNetPos: return ("Raw Day Fills", BTSColors.UI.default_text);
|
||||
case Columns.TotalLongPos: return ("All Buy Fills", BTSColors.MarketData.bid);
|
||||
case Columns.TotalShortPos: return ("All Sell Fills", BTSColors.MarketData.ask);
|
||||
case Columns.TotalNetPos: return ("All Fills", net_fg.rgb_color());
|
||||
case Columns.TotalNetPos: return ("All Fills", BTSColors.UI.default_text);
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(key, nameof(key));
|
||||
}
|
||||
}
|
||||
@@ -368,7 +367,7 @@ namespace bts.shell.pnl {
|
||||
switch (key) {
|
||||
case Columns.WorkingLongPos: return ("Working Buy Orders", BTSColors.MarketData.bid);
|
||||
case Columns.WorkingShortPos: return ("Working Sell Orders", BTSColors.MarketData.ask);
|
||||
case Columns.WorkingNetPos: return ("Working Orders", 0x333333u.rgb_color());
|
||||
case Columns.WorkingNetPos: return ("Working Orders", BTSColors.UI.default_text);
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(key, nameof(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using BCG;
|
||||
|
||||
namespace mbts.ui {
|
||||
public class NMSheetAllTaskingData {
|
||||
public IDictionary<UInt32, NMSheetTaskEyeData>
|
||||
bids = new Dictionary<UInt32, NMSheetTaskEyeData>(),
|
||||
asks = new Dictionary<UInt32, NMSheetTaskEyeData>();
|
||||
public void add_sec(UInt32 sec, bts.api.Side side, double edge, double size) {
|
||||
IDictionary<UInt32, NMSheetTaskEyeData> map = side == bts.api.Side.Buy ? bids : asks;
|
||||
NMSheetTaskEyeData sec_data;
|
||||
if (!map.TryGetValue(sec, out sec_data)) {
|
||||
sec_data = new NMSheetTaskEyeData(true, edge, size);
|
||||
map.Add(sec, sec_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using mbts.ui;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -17,7 +19,7 @@ namespace BCG
|
||||
private readonly IDictionary<NMSheetSymbol, NMSheetSingleOptionData> _sym_data_map;
|
||||
private readonly IDictionary<NMSheetSymbol, IDictionary<uint, BBOPacket>> _bbo_map;
|
||||
private readonly IDictionary<uint, NMSheetTickerData> _complete_ticker_data;
|
||||
private readonly TaskingConsolidatedData _task_data;
|
||||
private readonly NMSheetAllTaskingData _task_data;
|
||||
|
||||
private readonly double[] _cached_sorted_strikes;
|
||||
private readonly NMSheetExpiration[] _cached_sorted_expirations;
|
||||
@@ -27,7 +29,7 @@ namespace BCG
|
||||
public NMSheetData(IDictionary<NMSheetSymbol, NMSheetSingleOptionData> sym_data_map,
|
||||
IDictionary<NMSheetSymbol, IDictionary<uint, BBOPacket>> bbo_map,
|
||||
IDictionary<uint, NMSheetTickerData> complete_ticker_data,
|
||||
TaskingConsolidatedData task_data)
|
||||
NMSheetAllTaskingData task_data)
|
||||
{
|
||||
_sym_data_map = sym_data_map;
|
||||
_bbo_map = bbo_map;
|
||||
@@ -61,14 +63,14 @@ namespace BCG
|
||||
|
||||
public NMSheetData filter_for_strikes_in_tasks(bool is_on_only = false)
|
||||
{
|
||||
if (_task_data != null)
|
||||
{
|
||||
IEnumerable<uint> eye_secs = _task_data.get_set_securities(mbts.tasks.TaskType.Eye, is_on_only),
|
||||
quoter_secs = _task_data.get_set_securities(mbts.tasks.TaskType.Quoter, is_on_only);
|
||||
HashSet<uint> all_secs = new HashSet<uint>(Enumerable.Concat(eye_secs, quoter_secs));
|
||||
return filter_data(this, all_secs);
|
||||
}
|
||||
else
|
||||
// if (_task_data != null)
|
||||
// {
|
||||
// IEnumerable<uint> eye_secs = _task_data.get_set_securities(mbts.tasks.TaskType.Eye, is_on_only),
|
||||
// quoter_secs = _task_data.get_set_securities(mbts.tasks.TaskType.Quoter, is_on_only);
|
||||
// HashSet<uint> all_secs = new HashSet<uint>(Enumerable.Concat(eye_secs, quoter_secs));
|
||||
// return filter_data(this, all_secs);
|
||||
// }
|
||||
// else
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -104,9 +106,7 @@ namespace BCG
|
||||
return symbol_data.get_security_key(exp.option_expiration, strike, is_call, out security_key);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public TaskingConsolidatedData get_task_data() { return _task_data; }
|
||||
}
|
||||
|
||||
#region INMSheetGridFacingData
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace BCG {
|
||||
private readonly IDictionary<DateTime, Tuple<double, double>> _option_exp_driver_detailed_price_data = new Dictionary<DateTime, Tuple<double, double>>();
|
||||
private IDictionary<uint, BBOPacket> _bbo_data_map = new Dictionary<uint, BBOPacket>();
|
||||
private IDictionary<uint, NMSheetTickerData> _ticker_data = new Dictionary<uint, NMSheetTickerData>();
|
||||
private TaskingConsolidatedData _tasking_data;
|
||||
private readonly ExchangeSecurityMap _sec_map;
|
||||
private NMSheetAllTaskingData _ee_tasking_data = new NMSheetAllTaskingData();
|
||||
|
||||
public NMSheetSingleOptionData(NMSheetSymbol option,
|
||||
NMSheetDataRequest data_request,
|
||||
@@ -93,7 +93,7 @@ namespace BCG {
|
||||
|
||||
public void set_ticker(IDictionary<uint, NMSheetTickerData> ticker_data) { _ticker_data = ticker_data; }
|
||||
|
||||
public void set_task_data(TaskingConsolidatedData tasking_data) { _tasking_data = tasking_data; }
|
||||
public void set_task_data(NMSheetAllTaskingData tasking_data) { _ee_tasking_data = tasking_data; }
|
||||
|
||||
/// Strikes are not sorted
|
||||
public double[] get_strikes() { return _theo_data_map.get_strikes(); }
|
||||
@@ -128,7 +128,7 @@ namespace BCG {
|
||||
_fill_roll_over_data(cp_row, v, expiration, strike);
|
||||
_fill_presave_theos(cp_row, v, expiration, strike);
|
||||
NMSheetCallPutBidAskWrapper<NMSheetTaskEyeData> eye_data;
|
||||
_fill_eye_data(tp_with_seckey, driver_bid_ask, call_bbo_packet, put_bbo_packet, ref cp_row, out eye_data);
|
||||
_fill_eye_data(tp_with_seckey, ref cp_row, out eye_data);
|
||||
NMSheetCallPutBidAskWrapper<NMSheetTaskQuoterData> quoter_data;
|
||||
_fill_quoter_data(tp_with_seckey, driver_bid_ask, cp_row, out quoter_data);
|
||||
NMSheetTickerData c_trade_data, p_ticker_data;
|
||||
|
||||
@@ -3,45 +3,32 @@
|
||||
using bts.api;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
namespace BCG {
|
||||
public partial class NMSheetSingleOptionData {
|
||||
private void _fill_eye_data(Triplet<mbts.spark.TheoPoint, uint, uint> tp_with_seckeys, Tuple<double, double> driver_bid_ask, BBOPacket call_bbo, BBOPacket put_bbo,
|
||||
ref double[] cp_row, out NMSheetCallPutBidAskWrapper<NMSheetTaskEyeData> out_eye_data) {
|
||||
// Gets the data from the bus, and fills in out_eye_data, and cp_row
|
||||
private void _fill_eye_data(Triplet<mbts.spark.TheoPoint, uint, uint> tp_with_seckeys,
|
||||
ref double[] cp_row,
|
||||
out NMSheetCallPutBidAskWrapper<NMSheetTaskEyeData> out_eye_data) {
|
||||
NMSheetTaskEyeData call_bid_data, call_ask_data, put_bid_data, put_ask_data;
|
||||
_get_eye_security_side_data(tp_with_seckeys.first, PutOrCall.Call, Side.Buy, tp_with_seckeys.second, driver_bid_ask, call_bbo, out call_bid_data, ref cp_row,
|
||||
NMSheetColumnName.call_ee_set_bid_edge, NMSheetColumnName.call_ee_set_bid_size, NMSheetColumnName.call_ee_bid_price);
|
||||
_get_eye_security_side_data(tp_with_seckeys.first, PutOrCall.Call, Side.Sell, tp_with_seckeys.second, driver_bid_ask, call_bbo, out call_ask_data, ref cp_row,
|
||||
NMSheetColumnName.call_ee_set_ask_edge, NMSheetColumnName.call_ee_set_ask_size, NMSheetColumnName.call_ee_ask_price);
|
||||
_get_eye_security_side_data(tp_with_seckeys.first, PutOrCall.Put, Side.Buy, tp_with_seckeys.third, driver_bid_ask, put_bbo, out put_bid_data, ref cp_row,
|
||||
NMSheetColumnName.put_ee_set_bid_edge, NMSheetColumnName.put_ee_set_bid_size, NMSheetColumnName.put_ee_bid_price);
|
||||
_get_eye_security_side_data(tp_with_seckeys.first, PutOrCall.Put, Side.Sell, tp_with_seckeys.third, driver_bid_ask, put_bbo, out put_ask_data, ref cp_row,
|
||||
NMSheetColumnName.put_ee_set_ask_edge, NMSheetColumnName.put_ee_set_ask_size, NMSheetColumnName.put_ee_ask_price);
|
||||
out_eye_data = new NMSheetCallPutBidAskWrapper<NMSheetTaskEyeData>(call_bid_data, call_ask_data, put_bid_data, put_ask_data);
|
||||
// call
|
||||
_get_eye_security_side_data(tp_with_seckeys.second, Side.Buy, out call_bid_data);
|
||||
_get_eye_security_side_data(tp_with_seckeys.second, Side.Sell, out call_ask_data);
|
||||
// put
|
||||
_get_eye_security_side_data(tp_with_seckeys.third, Side.Buy, out put_bid_data);
|
||||
_get_eye_security_side_data(tp_with_seckeys.third, Side.Sell, out put_ask_data);
|
||||
out_eye_data = new NMSheetCallPutBidAskWrapper<NMSheetTaskEyeData>(
|
||||
call_bid_data, call_ask_data, put_bid_data, put_ask_data);
|
||||
}
|
||||
|
||||
private void _get_eye_security_side_data(mbts.spark.TheoPoint tp, PutOrCall cp, Side side, uint sec_key, Tuple<double, double> driver_bid_ask, BBOPacket bbo,
|
||||
out NMSheetTaskEyeData inp_out_data,
|
||||
ref double[] out_array,
|
||||
NMSheetColumnName inp_edge_col, NMSheetColumnName inp_size_col, NMSheetColumnName eye_price_col) {
|
||||
mbts.tasks.TaskSecurityEyeData eye_data = _tasking_data.get_eye_data(sec_key, side);
|
||||
double edge = Double.NaN, eye_price = Double.NaN;
|
||||
uint size = 0;
|
||||
if (eye_data != null) {
|
||||
edge = eye_data.edge;
|
||||
size = eye_data.size;
|
||||
/// Why is the eye sending the price instead of theo ?
|
||||
if (eye_data.is_on)
|
||||
eye_price = eye_data.eye_published_price;
|
||||
else {
|
||||
double theo = _compute_theo(tp, cp, side, driver_bid_ask);
|
||||
eye_price = Math.Max(0.0, side == Side.Buy ? theo - edge : theo + edge);
|
||||
}
|
||||
}
|
||||
out_array[(int)inp_edge_col] = edge;
|
||||
out_array[(int)inp_size_col] = (double)size;
|
||||
out_array[(int)eye_price_col] = eye_price;
|
||||
inp_out_data = new NMSheetTaskEyeData(eye_data, side == Side.Buy ? eye_price >= BBOPacket.get_ask_price(bbo) : eye_price <= BBOPacket.get_bid_price(bbo));
|
||||
private void _get_eye_security_side_data(uint sec_key, Side side,
|
||||
out NMSheetTaskEyeData inp_out_data) {
|
||||
inp_out_data = null;
|
||||
if (_ee_tasking_data == null)
|
||||
return;
|
||||
var map = side == Side.Buy ? _ee_tasking_data.bids : _ee_tasking_data.asks;
|
||||
map.TryGetValue(sec_key, out inp_out_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using bts.api;
|
||||
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using BCG;
|
||||
using System.Collections;
|
||||
using mbts.analytics;
|
||||
using bts.api.analytics;
|
||||
using System.Reactive.Linq;
|
||||
using bts.api.orderman;
|
||||
|
||||
namespace mbts.ui {
|
||||
internal class NMSheetAllTaskingDataSource {
|
||||
private IBus _bus;
|
||||
HighPerformanceTimer _timer = new HighPerformanceTimer();
|
||||
|
||||
private Nullable<DynamicOrderDefinitionImmutable> get_tasking_order(
|
||||
OrderDefinitionImmutable def) {
|
||||
if (def.HasCustomDef && def.CustomDef.HasComposite &&
|
||||
def.CustomDef.Composite.HasDefinition &&
|
||||
def.CustomDef.Composite.Definition.HasDynamic) {
|
||||
var dyn = def.CustomDef.Composite.Definition.Dynamic;
|
||||
if ("Tasking Eye".Equals(dyn.DynamicOrderType)) {
|
||||
return dyn;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool is_working(OrderStatus status) {
|
||||
return status == OrderStatus.Working ||
|
||||
status == OrderStatus.PendingWorking ||
|
||||
status == OrderStatus.Disconnected;
|
||||
}
|
||||
|
||||
private void add_task_secs(ref NMSheetAllTaskingData t_data,
|
||||
bts.api.Side side,
|
||||
DynamicOrderDefinitionImmutable t_order) {
|
||||
DynamicOrderParam sec_p;
|
||||
if (t_order.OrderParams.TryGetValue("Securities", out sec_p)) {
|
||||
if (!String.IsNullOrEmpty(sec_p.AsString)) {
|
||||
DynamicOrderParam edge_param = null;
|
||||
t_order.OrderParams.TryGetValue("Edge", out edge_param);
|
||||
DynamicOrderParam size_param = null;
|
||||
t_order.OrderParams.TryGetValue("Shot Size", out size_param);
|
||||
if (edge_param != null && size_param != null) {
|
||||
var toks = sec_p.AsString.Split(',');
|
||||
foreach (var s in toks) {
|
||||
UInt32 sec_key;
|
||||
if (UInt32.TryParse(s.Trim(), out sec_key)) {
|
||||
t_data.add_sec(sec_key, side, edge_param.AsDouble, size_param.AsDouble);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public NMSheetAllTaskingDataSource(OrdersModel orders_model, IBus bus) {
|
||||
bts.Log.Gen.info("NMSheetAllTaskingDataSource constructed");
|
||||
_bus = bus;
|
||||
orders_model.on_orders.Subscribe(ords => {
|
||||
_timer.start();
|
||||
NMSheetAllTaskingData t_data = new NMSheetAllTaskingData();
|
||||
bts.Log.Gen.info($"NMSheetAllTaskingDataSource on_orders n_orders: {ords.state.Values.Count()}");
|
||||
foreach (var k in ords.state.Values) {
|
||||
OrderRecordImmutable order = k.order;
|
||||
if (!is_working(order.State.Status))
|
||||
continue;
|
||||
var t_dyn = get_tasking_order(order.Def);
|
||||
if (t_dyn.HasValue) {
|
||||
add_task_secs(ref t_data, order.Def.Side, t_dyn.Value);
|
||||
}
|
||||
}
|
||||
_timer.stop();
|
||||
bts.Log.Gen.info($"NMSheetAllTaskingDataSource n_bids: {t_data.bids.Count()} " +
|
||||
$"n_asks: {t_data.asks.Count()} t_msecs: {_timer.lastIntervalTime}");
|
||||
bus.post(SparkTags.TC2Tags.TaskData, t_data);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,12 +95,13 @@ namespace BCG {
|
||||
}
|
||||
|
||||
public class NMSheetTaskEyeData {
|
||||
public readonly mbts.tasks.TaskSecurityEyeData eye_data;
|
||||
public readonly bool is_eye_cross;
|
||||
public readonly bool is_on;
|
||||
public readonly double edge, size;
|
||||
|
||||
public NMSheetTaskEyeData(mbts.tasks.TaskSecurityEyeData eye_data, bool is_eye_cross) {
|
||||
this.eye_data = eye_data;
|
||||
this.is_eye_cross = is_eye_cross;
|
||||
public NMSheetTaskEyeData(bool is_on, double edge, double size) {
|
||||
this.is_on = is_on;
|
||||
this.edge = edge;
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using bts.api;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
namespace BCG {
|
||||
public class NMSheetDataCoalescer : IDisposable {
|
||||
@@ -31,12 +32,12 @@ namespace BCG {
|
||||
_bbo_data_source = new NMSheetBBODataSource(threaded_bb, nmsheet_symbol_list, () => { _is_dirty = true; });
|
||||
_ticker_data_source = new NMSheetTickerDataSource(threaded_bb, nmsheet_symbol_list, req_id, () => { _is_dirty = true; });
|
||||
_presaved_theos_provider = new NMSheetPresavedTheosProvider(threaded_bb, nmsheet_symbol_list, req_id);
|
||||
//_threaded_bb.add_to_subscription_list(TaskingTags.ConsolidatedDataTag);
|
||||
_threaded_bb.subscribe(SparkTags.TC2Tags.TaskData);
|
||||
_threaded_bb.data_callback += _on_bb_update;
|
||||
}
|
||||
|
||||
private void _on_bb_update(object tag, object data) {
|
||||
if (/*TaskingTags.ConsolidatedDataTag.Equals(tag) ||*/ _in_bb_settings_tag.Equals(tag))
|
||||
if (SparkTags.TC2Tags.TaskData.Equals(tag) || _in_bb_settings_tag.Equals(tag))
|
||||
_is_dirty = true;
|
||||
}
|
||||
|
||||
@@ -44,9 +45,9 @@ namespace BCG {
|
||||
if (_is_dirty) {
|
||||
_is_dirty = false;
|
||||
_timer.start();
|
||||
TaskingConsolidatedData d = null;//_threaded_bb.sec_thread_retrieve_from_cache (TaskingTags.ConsolidatedDataTag) as TaskingConsolidatedData;
|
||||
NMSheetData rv = new NMSheetData(_main_data_source.get_data(), _bbo_data_source.get_all_data(), _ticker_data_source.get_data(),
|
||||
d ?? TaskingConsolidatedData.empty);
|
||||
NMSheetAllTaskingData d = _threaded_bb.read_state(
|
||||
SparkTags.TC2Tags.TaskData, new NMSheetAllTaskingData());
|
||||
NMSheetData rv = new NMSheetData(_main_data_source.get_data(), _bbo_data_source.get_all_data(), _ticker_data_source.get_data(), d);
|
||||
rv = _filter_data(rv, _threaded_bb.read_state(_in_bb_settings_tag));
|
||||
_timer.stop();
|
||||
_stats_logger.log(String.Format("NMSheetDataCoalescer_{0}", _req_id), "NMSheetData.cons", _timer.lastIntervalTime);
|
||||
|
||||
@@ -58,5 +58,10 @@ namespace BCG {
|
||||
public static UInt32 get_ask_size(BBOPacket md) {
|
||||
return md != null ? md._ask_size : (UInt32)0;
|
||||
}
|
||||
public static Tuple<double, UInt32> get_md(BBOPacket md, bool is_bid) {
|
||||
double price = is_bid ? BBOPacket.get_bid_price(md) : BBOPacket.get_ask_price(md);
|
||||
UInt32 sz = is_bid ? BBOPacket.get_bid_size(md) : BBOPacket.get_ask_size(md);
|
||||
return Tuple.Create(price, sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
|
||||
namespace mbts.tasks {
|
||||
[ProtoContract(Name = "ElectronicTradingTask")]
|
||||
[Serializable]
|
||||
public class ElectronicTradingTask {
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public readonly TaskCommon common = null;
|
||||
[ProtoBuf.ProtoMember(20)]
|
||||
public readonly ExplicitTask explicit_task = null;
|
||||
[ProtoBuf.ProtoMember(30)]
|
||||
public readonly ImplicitTask implicit_task = null;
|
||||
|
||||
private ElectronicTradingTask() { }
|
||||
|
||||
public ElectronicTradingTask(TaskCommon common, ExplicitTask task) {
|
||||
// Logger.assert(common != null);
|
||||
// Logger.assert(task != null);
|
||||
this.common = common;
|
||||
this.explicit_task = task;
|
||||
this.implicit_task = null;
|
||||
}
|
||||
public ElectronicTradingTask(TaskCommon common, ImplicitTask task) {
|
||||
// Logger.assert(common != null);
|
||||
// Logger.assert(task != null);
|
||||
this.common = common;
|
||||
this.explicit_task = null;
|
||||
this.implicit_task = task;
|
||||
}
|
||||
|
||||
public ElectronicTradingTask with(TaskCommon common) {
|
||||
return explicit_task == null ?
|
||||
new ElectronicTradingTask(common, implicit_task) :
|
||||
new ElectronicTradingTask(common, explicit_task);
|
||||
}
|
||||
public ElectronicTradingTask with(TaskCommon common = null, ExplicitTask explicit_task = null) {
|
||||
return new ElectronicTradingTask(common ?? this.common, explicit_task ?? this.explicit_task);
|
||||
}
|
||||
public ElectronicTradingTask with(TaskCommon common = null, ImplicitTask implicit_task = null) {
|
||||
return new ElectronicTradingTask(common ?? this.common, explicit_task ?? this.explicit_task);
|
||||
}
|
||||
public override string ToString() {
|
||||
return common.id;
|
||||
}
|
||||
}
|
||||
|
||||
public class QuickStartElectronicTradingTask {
|
||||
private readonly ElectronicTradingTask _task;
|
||||
public QuickStartElectronicTradingTask(TaskCommon common, ExplicitTask task) {
|
||||
_task = new ElectronicTradingTask(common, task);
|
||||
}
|
||||
public static implicit operator ElectronicTradingTask(QuickStartElectronicTradingTask task) {
|
||||
return task._task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.tasks {
|
||||
[Serializable]
|
||||
//[RecordType]
|
||||
[ProtoContract(Name = "TaskCommon")]
|
||||
public class TaskCommon {
|
||||
[ProtoMember(1)]
|
||||
public readonly string session;
|
||||
[ProtoMember(10)]
|
||||
public readonly string id;
|
||||
[ProtoMember(20)]
|
||||
public readonly DateTime create_time;
|
||||
[ProtoMember(30)]
|
||||
public readonly TaskType task_type;
|
||||
[ProtoMember(40)]
|
||||
public readonly string name;
|
||||
[ProtoMember(50)]
|
||||
public readonly TaskEdgeType edge_type;
|
||||
[ProtoMember(60)]
|
||||
public readonly string edge_surface;
|
||||
[ProtoMember(70)]
|
||||
public readonly double bid_edge_param;
|
||||
[ProtoMember(75)]
|
||||
public readonly double ask_edge_param;
|
||||
[ProtoMember(80)]
|
||||
public readonly double bid_size_param;
|
||||
[ProtoMember(85)]
|
||||
public readonly double ask_size_param;
|
||||
[ProtoMember(90)]
|
||||
public readonly TaskExitCondition exit_condition;
|
||||
[ProtoMember(100)]
|
||||
public readonly double exit_condition_parameter;
|
||||
[ProtoMember(110)]
|
||||
public readonly bool rfq_only;
|
||||
[ProtoMember(120)]
|
||||
public readonly double rfq_quote_for_seconds;
|
||||
[ProtoMember(130)]
|
||||
public readonly TaskSide side;
|
||||
[ProtoMember(140)]
|
||||
public readonly DateTime last_modification_time;
|
||||
[ProtoMember(150)]
|
||||
public readonly bool auto_hedge;
|
||||
//[ProtoMember(160)]
|
||||
//public readonly bool auto_start;
|
||||
[ProtoMember(200)]
|
||||
public readonly List<Tuple<int, double>> overridden_edges;
|
||||
[ProtoMember(210)]
|
||||
public readonly List<Tuple<int, uint>> overridden_sizes;
|
||||
|
||||
private TaskCommon() { /*for protobuf*/ }
|
||||
|
||||
public TaskCommon(string id,
|
||||
string session,
|
||||
DateTime create_time,
|
||||
TaskType task_type,
|
||||
string task_name,
|
||||
TaskExitCondition exit_condition,
|
||||
double exit_condition_param,
|
||||
bool rfq_only,
|
||||
double rfq_quote_for_seconds,
|
||||
TaskEdgeType edge_type,
|
||||
string edge_surface,
|
||||
double bid_edge_param,
|
||||
double ask_edge_param,
|
||||
double bid_size_param,
|
||||
double ask_size_param,
|
||||
TaskSide side,
|
||||
DateTime last_modification_time,
|
||||
bool auto_hedge,
|
||||
List<Tuple<int, double>> overridden_edges,
|
||||
List<Tuple<int, uint>> overridden_sizes) {
|
||||
this.id = id;
|
||||
this.session = session;
|
||||
this.create_time = create_time;
|
||||
this.task_type = task_type;
|
||||
this.name = task_name;
|
||||
this.exit_condition = exit_condition;
|
||||
this.exit_condition_parameter = exit_condition_param;
|
||||
this.rfq_only = rfq_only;
|
||||
this.rfq_quote_for_seconds = rfq_quote_for_seconds;
|
||||
this.edge_type = edge_type;
|
||||
this.edge_surface = edge_surface;
|
||||
this.bid_edge_param = bid_edge_param;
|
||||
this.ask_edge_param = ask_edge_param;
|
||||
this.bid_size_param = bid_size_param;
|
||||
this.ask_size_param = ask_size_param;
|
||||
this.side = side;
|
||||
this.last_modification_time = last_modification_time;
|
||||
this.auto_hedge = auto_hedge;
|
||||
this.overridden_edges = overridden_edges;
|
||||
this.overridden_sizes = overridden_sizes;
|
||||
}
|
||||
|
||||
public static TaskCommon Default {
|
||||
get {
|
||||
return new TaskCommon(
|
||||
id: Guid.NewGuid().ToString(),
|
||||
session: String.Empty,
|
||||
create_time: DateTime.Now,
|
||||
task_type: TaskType.Quoter,
|
||||
task_name: "",
|
||||
exit_condition: TaskExitCondition.Contracts,
|
||||
exit_condition_param: 100,
|
||||
rfq_only: false,
|
||||
rfq_quote_for_seconds: 10,
|
||||
edge_type: TaskEdgeType.Manual,
|
||||
edge_surface: "",
|
||||
bid_edge_param: double.NaN,
|
||||
ask_edge_param: double.NaN,
|
||||
bid_size_param: double.NaN,
|
||||
ask_size_param: double.NaN,
|
||||
side: TaskSide.Buy | TaskSide.Sell,
|
||||
last_modification_time: DateTime.Now,
|
||||
auto_hedge: true,
|
||||
overridden_edges: new List<Tuple<int, double>>(),
|
||||
overridden_sizes: new List<Tuple<int, uint>>());
|
||||
}
|
||||
}
|
||||
|
||||
public TaskCommon with(string id = null, string session = null, DateTime? create_time = null,
|
||||
TaskType? task_type = null, string name = null, TaskExitCondition? exit_condition = null,
|
||||
double? exit_condition_parameter = null, bool? rfq_only = null, double? rfq_quote_for_seconds = null, TaskEdgeType? edge_type = null,
|
||||
string edge_surface = null,
|
||||
double? bid_edge_param = null, double? bid_size_param = null,
|
||||
double? ask_edge_param = null, double? ask_size_param = null,
|
||||
TaskSide? side = null, DateTime? last_modification_time = null,
|
||||
bool? auto_hedge = null,
|
||||
List<Tuple<int, double>> overridden_edges = null,
|
||||
List<Tuple<int, uint>> overridden_sizes = null) {
|
||||
return new TaskCommon(id ?? this.id,
|
||||
session ?? this.session,
|
||||
create_time ?? this.create_time,
|
||||
task_type ?? this.task_type,
|
||||
name ?? this.name,
|
||||
exit_condition ?? this.exit_condition,
|
||||
exit_condition_parameter ?? this.exit_condition_parameter,
|
||||
rfq_only ?? this.rfq_only,
|
||||
rfq_quote_for_seconds ?? this.rfq_quote_for_seconds,
|
||||
edge_type ?? this.edge_type,
|
||||
edge_surface ?? this.edge_surface,
|
||||
bid_edge_param ?? this.bid_edge_param,
|
||||
ask_edge_param ?? this.ask_edge_param,
|
||||
bid_size_param ?? this.bid_size_param,
|
||||
ask_size_param ?? this.ask_size_param,
|
||||
side ?? this.side,
|
||||
last_modification_time ?? this.last_modification_time,
|
||||
auto_hedge ?? this.auto_hedge,
|
||||
overridden_edges ?? this.overridden_edges,
|
||||
overridden_sizes ?? this.overridden_sizes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.tasks
|
||||
{
|
||||
/// Task persisted state combined with
|
||||
/// the live running state
|
||||
public class TaskLiveData : IEquatable<TaskLiveData>
|
||||
{
|
||||
public readonly ElectronicTradingTask task;
|
||||
public readonly TaskSwitchState switch_state;
|
||||
public readonly TaskHealthState task_state;
|
||||
public readonly double net_accumulation, abs_accumulation;
|
||||
|
||||
public TaskLiveData(ElectronicTradingTask task, TaskSwitchState switch_state, TaskHealthState health_state,
|
||||
double net_accumulation, double abs_accumulation)
|
||||
{
|
||||
this.task = task;
|
||||
this.switch_state = switch_state;
|
||||
this.task_state = health_state;
|
||||
this.net_accumulation = net_accumulation;
|
||||
this.abs_accumulation = abs_accumulation;
|
||||
}
|
||||
|
||||
public string task_id { get { return task.common.id; } }
|
||||
|
||||
public TaskType task_type { get { return task.common.task_type; } }
|
||||
|
||||
public string name { get { return task.common.name; } }
|
||||
|
||||
public double completion_ratio { get { return abs_accumulation / task.common.exit_condition_parameter; } }
|
||||
|
||||
public bool is_filled { get { return completion_ratio >= 1.0; } }
|
||||
|
||||
public DateTime modification_time { get { return task.common.last_modification_time; } }
|
||||
|
||||
public TaskLiveData with(ElectronicTradingTask task = null, TaskSwitchState? switch_state = null, TaskHealthState? task_state = null,
|
||||
double? net_accumulation = null, double? abs_accumulation = null)
|
||||
{
|
||||
return new TaskLiveData(task ?? this.task,
|
||||
switch_state ?? this.switch_state,
|
||||
task_state ?? this.task_state,
|
||||
net_accumulation ?? this.net_accumulation,
|
||||
abs_accumulation ?? this.abs_accumulation);
|
||||
}
|
||||
|
||||
public int GetHashCode(TaskLiveData obj) { return obj.task_id.GetHashCode(); }
|
||||
|
||||
public bool Equals(TaskLiveData other)
|
||||
{
|
||||
return ReferenceEquals(task, other.task) &&
|
||||
switch_state == other.switch_state &&
|
||||
task_state == other.task_state &&
|
||||
completion_ratio == other.completion_ratio;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return task.ToString();
|
||||
}
|
||||
|
||||
public string get_summary_text()
|
||||
{
|
||||
string sign = task.common.side == TaskSide.Buy ? "+" : (task.common.side == TaskSide.Sell ? "-" : "+/-");
|
||||
return String.Format("{0}{1} ({2}/{3})", sign, task.common.name, abs_accumulation, task.common.exit_condition_parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.tasks {
|
||||
public class TaskSecurityEyeData {
|
||||
|
||||
public TaskSecurityEyeData() {
|
||||
}
|
||||
|
||||
public bool is_output_present {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_on {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// If the security is on, returns the edge as sent by the eye
|
||||
/// otherwise returns the input edge
|
||||
public double edge {
|
||||
get {
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
/// If the security is on, returns the size as sent by the eye
|
||||
/// otherwise returns the input size
|
||||
public uint size {
|
||||
get {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public double eye_published_price {
|
||||
get {
|
||||
// if (is_on && is_output_present)
|
||||
// return input.secside_key.side == bts.o2.msgs.Side.type.BUY ? output.price - output.edge : output.price + output.edge;
|
||||
// return Double.NaN;
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,53 +48,3 @@ namespace mbts.engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace BCG {
|
||||
public class TaskingConsolidatedData {
|
||||
public TaskingConsolidatedData(IEnumerable<TaskLiveData> input_tasks,
|
||||
object input_securities,
|
||||
object eye_output,
|
||||
object quoter_output) {
|
||||
}
|
||||
public readonly static TaskingConsolidatedData empty = new TaskingConsolidatedData(Enumerable.Empty<TaskLiveData>(),
|
||||
null, null, null);
|
||||
// public TaskingConsolidatedData with(IEnumerable<TaskLiveData> input_tasks = null,
|
||||
// IEnumerable<TaskSecurityData> input_securities = null,
|
||||
// IEnumerable<EyeSecurityState> eye_output = null,
|
||||
// IEnumerable<QuoterSecurityState> quoter_output = null) {
|
||||
// return new TaskingConsolidatedData(input_tasks ?? this._input_tasks, input_securities ?? this._input_securities,
|
||||
// eye_output ?? _eye_output, quoter_output ?? _quoter_output);
|
||||
// }
|
||||
|
||||
public IEnumerable<TaskLiveData> get_tasks(TaskType task_type) { return new TaskLiveData[] { }; }
|
||||
|
||||
public TaskLiveData get_task_by_name(TaskType task_type, string name) {
|
||||
IEnumerable<TaskLiveData> live_tasks = get_tasks(task_type);
|
||||
return live_tasks.FirstOrDefault(lt => lt.task.common.name == name);
|
||||
}
|
||||
|
||||
public IEnumerable<TaskLiveData> get_security_owning_tasks(IEnumerable<SecuritySideKey> secside_keys, TaskType t_type) {
|
||||
HashSet<TaskLiveData> rv = new HashSet<TaskLiveData>();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public HashSet<uint> get_set_securities(TaskType task_type, bool is_on_only = false) {
|
||||
return new HashSet<uint>();
|
||||
}
|
||||
|
||||
public TaskSecurityEyeData get_eye_data(uint key, Side side) { return get_eye_data(new SecuritySideKey(key, side)); }
|
||||
public TaskSecurityEyeData get_eye_data(SecuritySideKey key) {
|
||||
return new TaskSecurityEyeData();
|
||||
}
|
||||
|
||||
|
||||
public TaskSecurityQuoterData get_quoter_data(uint key, Side side) { return get_quoter_data(new SecuritySideKey(key, side)); }
|
||||
public TaskSecurityQuoterData get_quoter_data(SecuritySideKey key) {
|
||||
return new TaskSecurityQuoterData();
|
||||
}
|
||||
|
||||
public TaskLiveData get_security_task(SecuritySideKey key, TaskType task_type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,15 @@ namespace BCG {
|
||||
|
||||
#endregion
|
||||
|
||||
#region Orders
|
||||
[System.ComponentModel.Category("Orders / Algo")]
|
||||
[System.ComponentModel.DisplayName("Default Account")]
|
||||
public string Account { get; set; }
|
||||
[System.ComponentModel.Category("Orders / Algo")]
|
||||
[System.ComponentModel.DisplayName("Default Qty")]
|
||||
public Nullable<double> DefaultQty { get; set; }
|
||||
#endregion
|
||||
|
||||
public NMSheetSettingLayout() {
|
||||
show_settings_header = false;
|
||||
show_exp_navigator = false;
|
||||
|
||||
@@ -18,6 +18,7 @@ using mbts.spark.filters;
|
||||
using System.Collections;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using bts.api.orderman;
|
||||
|
||||
namespace BCG {
|
||||
public partial class NMSheetControl : UserControl {
|
||||
@@ -555,6 +556,17 @@ namespace BCG {
|
||||
}
|
||||
private void _launch_order_entry_window(NMSheetTheoGrid.GridCell cell,
|
||||
OrderEntryWindow window_type) {
|
||||
Func<string, bts.AccountTuple> get_account = (string default_acc) => {
|
||||
string account = _settings.setting_layout.Account;
|
||||
if (String.IsNullOrEmpty(account)) {
|
||||
account = default_acc;
|
||||
}
|
||||
Nullable<uint> id = _get_account_id(account);
|
||||
if (!id.HasValue) {
|
||||
id = 0;
|
||||
}
|
||||
return new bts.AccountTuple(id.Value, account);
|
||||
};
|
||||
uint key;
|
||||
var found = _sheet_data.get_security_key(cell.expiration,
|
||||
cell.strike, cell.call_selected, out key);
|
||||
@@ -566,7 +578,20 @@ namespace BCG {
|
||||
if (sc_data.sec_md_map.TryGetValue(key, out var smd)) {
|
||||
switch (window_type) {
|
||||
case OrderEntryWindow.Ticket:
|
||||
_threaded_bb.post(SparkTags.ShowOrderTicketFromSecurityTag, smd);
|
||||
bool is_bid = NMSheetSettingColumnOrganization.get_column_info(cell.col).is_bid;
|
||||
bts.AccountTuple acc_tup = get_account("SMALL");
|
||||
Nullable<double> qty = _settings.setting_layout.DefaultQty;
|
||||
OrderDefinition od = new OrderDefinition() {
|
||||
SecurityKey = smd.id,
|
||||
TimeInForce = TimeInForce.Gtc,
|
||||
Account = acc_tup.name,
|
||||
AccountId = acc_tup.id
|
||||
};
|
||||
BBOPacket bbo = _sheet_data.get_bbo(smd.id);
|
||||
Tuple<double, UInt32> pr_sz = BBOPacket.get_md(bbo, is_bid);
|
||||
UInt32 effective_qty = qty.HasValue ? Convert.ToUInt32(Math.Abs(qty.Value)) : pr_sz.Item2;
|
||||
_threaded_bb.post(SparkTags.ShowOrderTicketTag,
|
||||
(new OrderDefinitionImmutable(od), pr_sz.Item1, effective_qty));
|
||||
break;
|
||||
case OrderEntryWindow.Ladder:
|
||||
_threaded_bb.post(SparkTags.ShowLadderTag, smd);
|
||||
|
||||
@@ -87,21 +87,21 @@ namespace BCG {
|
||||
}
|
||||
|
||||
private void _refresh_task_data(uint seckey) {
|
||||
string eye_task_text = String.Empty, quoter_task_text = String.Empty;
|
||||
bool dummy, is_bid, is_ask;
|
||||
NMSheetSettingColumnOrganization.parse_column(_last_mousemove_cell.col, out dummy, out dummy, out is_bid, out is_ask);
|
||||
if (is_bid ^ is_ask) {
|
||||
TaskingConsolidatedData task_data = _sheet_data.get_task_data();
|
||||
mbts.tasks.TaskLiveData eye_task_ld = task_data != null ? task_data.get_security_task(new mbts.engine.SecuritySideKey(seckey, is_bid ? Side.Buy : Side.Sell), mbts.tasks.TaskType.Eye) : null;
|
||||
if (eye_task_ld != null)
|
||||
eye_task_text = String.Format("Eye '{0}' {1} {2}", eye_task_ld.name, eye_task_ld.switch_state.ToString().ToUpper(),
|
||||
eye_task_ld.abs_accumulation.ToString("0") + "/" + eye_task_ld.task.common.exit_condition_parameter.ToString("0"));
|
||||
mbts.tasks.TaskLiveData quoter_task_ld = task_data != null ? task_data.get_security_task(new mbts.engine.SecuritySideKey(seckey, is_bid ? Side.Buy : Side.Sell), mbts.tasks.TaskType.Quoter) : null;
|
||||
if (quoter_task_ld != null)
|
||||
quoter_task_text += String.Format("Quoter '{0}' {1} {2}", quoter_task_ld.name, quoter_task_ld.switch_state.ToString().ToUpper(),
|
||||
quoter_task_ld.abs_accumulation.ToString("0") + "/" + quoter_task_ld.task.common.exit_condition_parameter.ToString("0"));
|
||||
}
|
||||
_set_text_visibility(_statusbar_toolstripstatus_task, true, String.Format("{0}{1}{2}", eye_task_text, String.IsNullOrEmpty(eye_task_text) ? String.Empty : " ", quoter_task_text));
|
||||
// string eye_task_text = String.Empty, quoter_task_text = String.Empty;
|
||||
// bool dummy, is_bid, is_ask;
|
||||
// NMSheetSettingColumnOrganization.parse_column(_last_mousemove_cell.col, out dummy, out dummy, out is_bid, out is_ask);
|
||||
// if (is_bid ^ is_ask) {
|
||||
// TaskingConsolidatedData task_data = _sheet_data.get_task_data();
|
||||
// mbts.tasks.TaskLiveData eye_task_ld = task_data != null ? task_data.get_security_task(new mbts.engine.SecuritySideKey(seckey, is_bid ? Side.Buy : Side.Sell), mbts.tasks.TaskType.Eye) : null;
|
||||
// if (eye_task_ld != null)
|
||||
// eye_task_text = String.Format("Eye '{0}' {1} {2}", eye_task_ld.name, eye_task_ld.switch_state.ToString().ToUpper(),
|
||||
// eye_task_ld.abs_accumulation.ToString("0") + "/" + eye_task_ld.task.common.exit_condition_parameter.ToString("0"));
|
||||
// mbts.tasks.TaskLiveData quoter_task_ld = task_data != null ? task_data.get_security_task(new mbts.engine.SecuritySideKey(seckey, is_bid ? Side.Buy : Side.Sell), mbts.tasks.TaskType.Quoter) : null;
|
||||
// if (quoter_task_ld != null)
|
||||
// quoter_task_text += String.Format("Quoter '{0}' {1} {2}", quoter_task_ld.name, quoter_task_ld.switch_state.ToString().ToUpper(),
|
||||
// quoter_task_ld.abs_accumulation.ToString("0") + "/" + quoter_task_ld.task.common.exit_condition_parameter.ToString("0"));
|
||||
// }
|
||||
// _set_text_visibility(_statusbar_toolstripstatus_task, true, String.Format("{0}{1}{2}", eye_task_text, String.IsNullOrEmpty(eye_task_text) ? String.Empty : " ", quoter_task_text));
|
||||
}
|
||||
|
||||
private static void _set_text_visibility(System.Windows.Forms.ToolStripStatusLabel l, bool new_visibility, string new_text) {
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
using mbts;
|
||||
using mbts.tasks;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BCG {
|
||||
partial class NMSheetControl {
|
||||
//private bool _task_quoter_enabled = true;
|
||||
|
||||
private void _init_task_quoter_ui() {
|
||||
// _task_quoter_enabled = true;
|
||||
// if (_task_quoter_enabled)
|
||||
// _disable_task_quoter_submenus();
|
||||
// else
|
||||
// _toolstripmenuitem_task_quoter.Enabled = false;
|
||||
// _toolstripmenuitem_settings_show_task_quoter_data.Click += (s, e) => _get_toolstripmenuitem_display_state_and_publish(_toolstripmenuitem_settings_show_task_quoter_data,
|
||||
// NMSheetSettingColumnOrganization.get_cols_for_type(NMSheetColumnType.Quoter).ToArray());
|
||||
}
|
||||
|
||||
private void _disable_task_quoter_submenus() {
|
||||
// _toolstripmenuitem_task_quoter.Enabled = _toolstripmenuitem_task_quoter_add_buys_to_task.Enabled =
|
||||
// _toolstripmenuitem_task_quoter_add_sells_to_task.Enabled = _toolstripmenuitem_task_quoter_add_buys_and_sells_to_task.Enabled = false;
|
||||
// _toolstripmenuitem_task_quoter_add_buys_to_task.DropDownItems.Clear();
|
||||
// _toolstripmenuitem_task_quoter_add_sells_to_task.DropDownItems.Clear();
|
||||
// _toolstripmenuitem_task_quoter_add_buys_and_sells_to_task.DropDownItems.Clear();
|
||||
|
||||
// List<ToolStripItem> create_menu_items = new List<ToolStripItem>();
|
||||
// foreach (ToolStripItem ti in _toolstripmenuitem_task_quoter.DropDownItems)
|
||||
// if (_create_menu_tag.Equals(ti.Tag))
|
||||
// create_menu_items.Add(ti);
|
||||
// create_menu_items.ForEach(ti => _toolstripmenuitem_task_quoter.DropDownItems.Remove(ti));
|
||||
}
|
||||
|
||||
private void _adapt_contextmenu_for_tasks_quoter() {
|
||||
// if (_task_quoter_enabled) {
|
||||
// _disable_task_quoter_submenus();
|
||||
// if (_task_selection.is_active) {
|
||||
// _toolstripmenuitem_task_quoter.Enabled = true;
|
||||
// ///Create
|
||||
// IEnumerable<TaskCommon> templates = _threaded_bb.sec_thread_retrieve_from_cache(TaskingTags.TemplatesTag) as IEnumerable<TaskCommon>;
|
||||
// templates = templates == null || templates.Count() == 0 ? new TaskCommon[] { TaskCommon.Default.with(name: "New Task") } : templates;
|
||||
// double min_strike = _task_selection.strikes.Min(), max_strike = _task_selection.strikes.Max();
|
||||
// string name = TaskType.Quoter.ToString() + " " +
|
||||
// _task_selection.exp.get_display_text() + " " +
|
||||
// (min_strike == max_strike ? min_strike.ToString("0.00") : String.Format("{0} - {1}", min_strike.ToString("0.00"), max_strike.ToString("0.00")));
|
||||
// int add_task_index = 0;
|
||||
// foreach (TaskCommon t in templates) {
|
||||
// ToolStripMenuItem add_task_item = new ToolStripMenuItem(t.name, null, (s, e) => {
|
||||
// mbts.tasks.TaskCommon common_task = t.with(task_type: TaskType.Quoter, name: name, side: TaskSide.None, id: Guid.NewGuid().ToString());
|
||||
// ExplicitTask explicit_task = ExplicitTask.Default.with(securities: _task_selection.keys, security_descriptions: _task_selection.names);
|
||||
// _threaded_bb.sec_thread_write(TaskingTags.ShowCreateTask, new ElectronicTradingTask(common_task, explicit_task));
|
||||
// });
|
||||
// add_task_item.Tag = _create_menu_tag;
|
||||
// add_task_item.Enabled = true;
|
||||
// _toolstripmenuitem_task_quoter.DropDownItems.Insert(add_task_index++, add_task_item);
|
||||
// }
|
||||
// //Add Buys to
|
||||
// TaskingConsolidatedData task_data = _sheet_data.get_task_data();
|
||||
// _toolstripmenuitem_task_quoter_add_buys_to_task.Enabled = true;
|
||||
// IEnumerable<TaskLiveData> tasks = task_data.get_tasks(TaskType.Quoter).OrderBy(t => t.name);
|
||||
// foreach (TaskLiveData t in tasks) {
|
||||
// ElectronicTradingTask mod_task = _get_mod_task(t.task, _task_selection);
|
||||
// ToolStripMenuItem add_item = new ToolStripMenuItem(t.get_summary_text(), null, (s, e) => _threaded_bb.sec_thread_write(TaskingTags.ShowEditTask, mod_task));
|
||||
// add_item.Enabled = t.task.common.side != TaskSide.Sell;
|
||||
// _toolstripmenuitem_task_quoter_add_buys_to_task.DropDownItems.Add(add_item);
|
||||
// }
|
||||
// /// Add Sells to
|
||||
// _toolstripmenuitem_task_quoter_add_sells_to_task.Enabled = true;
|
||||
// foreach (TaskLiveData t in tasks) {
|
||||
// ElectronicTradingTask mod_task = _get_mod_task(t.task, _task_selection);
|
||||
// ToolStripMenuItem add_item = new ToolStripMenuItem(t.get_summary_text(), null, (s, e) => _threaded_bb.sec_thread_write(TaskingTags.ShowEditTask, mod_task));
|
||||
// add_item.Enabled = t.task.common.side != TaskSide.Buy;
|
||||
// _toolstripmenuitem_task_quoter_add_sells_to_task.DropDownItems.Add(add_item);
|
||||
// }
|
||||
// /// Add Buys/Sells to
|
||||
// _toolstripmenuitem_task_quoter_add_buys_and_sells_to_task.Enabled = true;
|
||||
// foreach (TaskLiveData t in tasks) {
|
||||
// ElectronicTradingTask mod_task = _get_mod_task(t.task, _task_selection);
|
||||
// ToolStripMenuItem add_item = new ToolStripMenuItem(t.get_summary_text(), null, (s, e) => _threaded_bb.sec_thread_write(TaskingTags.ShowEditTask, mod_task));
|
||||
// add_item.Enabled = t.task.common.side == (TaskSide.Buy | TaskSide.Sell);
|
||||
// _toolstripmenuitem_task_quoter_add_buys_and_sells_to_task.DropDownItems.Add(add_item);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// private ElectronicTradingTask _get_task_quoter_mod_task(ElectronicTradingTask task) {
|
||||
// List<uint> secs = new List<uint>(task.explicit_task.securities);
|
||||
// List<string> secs_desc = new List<string>(task.explicit_task.security_descriptions);
|
||||
// secs.AddRange(_task_selection.keys);
|
||||
// secs_desc.AddRange(_task_selection.names);
|
||||
// ElectronicTradingTask mod_task = task.with(explicit_task: task.explicit_task.with(securities: secs, security_descriptions: secs_desc));
|
||||
// return mod_task;
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -83,26 +83,25 @@ namespace BCG {
|
||||
}
|
||||
|
||||
private bool _get_ee_switch_override(NMSheetSettings settings, MyAlertHelperData helper_data, int exp_index, int strike_index, ref Color out_color) {
|
||||
if (helper_data.is_ee_set_edge_or_size || helper_data.is_ee_price) {
|
||||
NMSheetRowAuxiliaryData aux_data = _formatted_theos.get_aux_data(exp_index, strike_index);
|
||||
if (aux_data != null) {
|
||||
NMSheetCallPutBidAskWrapper<NMSheetTaskEyeData> eye_data = aux_data.task_ee_data;
|
||||
if (eye_data != null) {
|
||||
NMSheetTaskEyeData secside_eye_data = eye_data.get_data(helper_data.is_call, helper_data.is_bid);
|
||||
if (secside_eye_data != null && secside_eye_data.eye_data != null) {
|
||||
if (helper_data.is_ee_price) {
|
||||
if (secside_eye_data.is_eye_cross) {
|
||||
out_color = _settings.setting_alerts.ee_fire_color.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (secside_eye_data.eye_data.is_on) {
|
||||
out_color = _settings.setting_alerts.ee_on_color.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!helper_data.is_theo)
|
||||
return false;
|
||||
NMSheetRowAuxiliaryData aux_data = _formatted_theos.get_aux_data(exp_index, strike_index);
|
||||
if (aux_data == null)
|
||||
return false;
|
||||
NMSheetCallPutBidAskWrapper<NMSheetTaskEyeData> t_data = aux_data.task_ee_data;
|
||||
if (t_data == null)
|
||||
return false;
|
||||
NMSheetTaskEyeData bid_sec_t_data =
|
||||
t_data.get_data(helper_data.is_call ? bts.api.PutOrCall.Call : bts.api.PutOrCall.Put,
|
||||
bts.api.Side.Buy);
|
||||
NMSheetTaskEyeData ask_sec_t_data =
|
||||
t_data.get_data(helper_data.is_call ? bts.api.PutOrCall.Call : bts.api.PutOrCall.Put,
|
||||
bts.api.Side.Sell);
|
||||
if ((bid_sec_t_data != null && bid_sec_t_data.is_on) ||
|
||||
(ask_sec_t_data != null && ask_sec_t_data.is_on)) {
|
||||
out_color = settings.setting_alerts.ee_on_color.HasValue ?
|
||||
settings.setting_alerts.ee_on_color.Value : Color.DarkGreen;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace mbts.spark {
|
||||
get {
|
||||
var m = new OrderMetadata { KeyBinding = string.Empty, NoProps = false };
|
||||
if (_studio_meta.HasValue && _studio_meta.Value.badge_color.HasValue)
|
||||
m.Color = _studio_meta.Value.badge_color.Value.rgb_color();
|
||||
m.Color = _studio_meta.Value.badge_color.Value.to_uint();
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace mbts.analytics {
|
||||
IEnumerable<VolSurfaceExpiration> all_exps) {
|
||||
var exps = all_exps.Where(vse => vse.symbol.is_null_or_empty()).ToArray();
|
||||
var exp_header_cells = exps
|
||||
.Select(vse => DateFormatter.Format(vse.expiration, DateFormat.DayMonthAbvDiff2000))
|
||||
.Select(vse => SecurityFormatter.format_expiration(vse.expiration))
|
||||
.ToArray();
|
||||
return (exps, exp_header_cells);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ namespace mbts.analytics {
|
||||
IEnumerable<VolSurfaceExpiration> all_exps) {
|
||||
var exps = all_exps.ToArray();
|
||||
var exp_header_cells = exps.Select(vse => string.IsNullOrEmpty(vse.symbol) ?
|
||||
DateFormatter.Format(vse.expiration, DateFormat.DayMonthAbvDiff2000) : vse.symbol)
|
||||
SecurityFormatter.format_expiration(vse.expiration) : vse.symbol)
|
||||
.ToArray();
|
||||
return (exps, exp_header_cells);
|
||||
}
|
||||
|
||||
@@ -57,8 +57,6 @@
|
||||
<analytics:ComboFutureDataColumn x:Key="FutureSymDataColumn" HeaderTemplate="{StaticResource SymHeaderTemplate}" ElementStyle="{StaticResource FutureSymDataElementStyle}" MinWidth="60" x:Shared="False" />
|
||||
<analytics:ComboOptionDataColumn x:Key="OptionExpDataColumn" HeaderTemplate="{StaticResource ExpHeaderTemplate}" ElementStyle="{StaticResource OptionExpDataElementStyle}" MinWidth="60" x:Shared="False" />
|
||||
<analytics:ComboOptionDataColumn x:Key="OptionSymDataColumn" HeaderTemplate="{StaticResource SymHeaderTemplate}" ElementStyle="{StaticResource OptionSymDataElementStyle}" MinWidth="60" x:Shared="False" />
|
||||
<MenuItem x:Key="LaunchConfigurationEditorMenuItem" x:Shared="False" Header="Report Configuration..." Command="{Binding DataContext.launch_configuration_editor, Source={x:Reference ComboWindow}}" />
|
||||
<MenuItem x:Key="ExportToCSVMenuItem" Header="Export To CSV..." Command="{Binding export_to_csv_command}" CommandParameter="{Binding DataContext.show_call_put, Source={x:Reference ComboWindow}}" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -99,14 +97,7 @@
|
||||
VerticalGridLinesBrush="LightGray"
|
||||
CanUserResizeColumns="False"
|
||||
CanUserReorderColumns="False"
|
||||
IsReadOnly="True"
|
||||
>
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<StaticResourceExtension ResourceKey="LaunchConfigurationEditorMenuItem" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
</c:DataGridEx>
|
||||
IsReadOnly="True" />
|
||||
</AdornerDecorator>
|
||||
<GridSplitter Grid.Row="2" Height="3" Background="Black" HorizontalAlignment="Stretch" />
|
||||
|
||||
@@ -144,12 +135,6 @@
|
||||
<TextBlock Text="{Binding Item.strike, RelativeSource={RelativeSource AncestorType=DataGridRow}}" />
|
||||
</DataTemplate>
|
||||
</DataGrid.RowHeaderTemplate>
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<StaticResourceExtension ResourceKey="LaunchConfigurationEditorMenuItem" />
|
||||
<StaticResourceExtension ResourceKey="ExportToCSVMenuItem" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
</c:DataGridEx>
|
||||
</AdornerDecorator>
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace mbts.analytics {
|
||||
timer.stop();
|
||||
Log.Ui.info($"PNLReportCalculator n_pos: {output.position_data.Count()} " +
|
||||
$"n_day_trades: {output.day_trades.Count()} time(msecs): {timer.lastIntervalTime}");
|
||||
_log_before_posting(output.position_data);
|
||||
//_log_before_posting(output.position_data);
|
||||
_bus.post(SparkTags.ReportBlackboardTags.PNL.PNLData, output);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#nullable enable
|
||||
namespace mbts.analytics {
|
||||
using bts.api;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
@@ -17,13 +19,9 @@ namespace mbts.analytics {
|
||||
symbol = null;
|
||||
}
|
||||
|
||||
private static string _format_exp(DateTime exp) {
|
||||
return mbts.ui.utility.ExpirationFormatter.DateFormatter.format_day_month_year(exp);
|
||||
}
|
||||
|
||||
SlideGreekUnitKey(DateTime exp_date) {
|
||||
exp = exp_date;
|
||||
_unique_name = _format_exp(exp_date);
|
||||
_unique_name = SecurityFormatter.format_expiration(exp_date);
|
||||
type = GroupType.Exp;
|
||||
symbol = null;
|
||||
}
|
||||
@@ -31,7 +29,7 @@ namespace mbts.analytics {
|
||||
SlideGreekUnitKey(DateTime exp_date, string symbol) {
|
||||
exp = exp_date;
|
||||
this.symbol = symbol;
|
||||
_unique_name = $"{_format_exp(exp_date)}:{symbol}";
|
||||
_unique_name = $"{SecurityFormatter.format_expiration(exp_date)}:{symbol}";
|
||||
type = GroupType.SymExp;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,21 +115,21 @@ namespace mbts.analytics {
|
||||
}
|
||||
|
||||
private void _handle_nonvol_data(VolSurfaceNonVolData vs_nonvol_data) {
|
||||
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_nonvol_data report {0}",
|
||||
_report.ReportName));
|
||||
// bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_nonvol_data report {0}",
|
||||
// _report.ReportName));
|
||||
_vs_nonvol_data = vs_nonvol_data;
|
||||
}
|
||||
|
||||
private void _handle_future_prices(
|
||||
IReadOnlyDictionary<SecurityExpiration, FutureTheoStructure> future_prices_map) {
|
||||
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_future_prices report {0}",
|
||||
_report.ReportName));
|
||||
// bts.Log.Gen.Diag(String.Format("SlideReportCalculator._handle_future_prices report {0}",
|
||||
// _report.ReportName));
|
||||
_future_prices_map = future_prices_map.ToDictionary(kv => kv.Key, kv => kv.Value.theo);
|
||||
}
|
||||
|
||||
private void _handle_trades(IEnumerable<ITradeLogRow> trades) {
|
||||
bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_trades report {0}",
|
||||
_report.ReportName));
|
||||
// bts.Log.Gen.info(String.Format("SlideReportCalculator._handle_trades report {0}",
|
||||
// _report.ReportName));
|
||||
_all_trades = trades.Where(t => t.is_active);
|
||||
if (_report.CalcMode == Report.Types.ReportCalculationMode.PositionOrTheoChange) {
|
||||
_compute_dirty = true;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Authors>Blue Trading Systems</Authors>
|
||||
<Company>Blue Trading Systems</Company>
|
||||
@@ -34,7 +34,6 @@
|
||||
<PackageReference Include="System.Reactive.Interfaces" Version="5.0.0" />
|
||||
<PackageReference Include="System.Reactive.Linq" Version="5.0.0" />
|
||||
<PackageReference Include="System.Reactive.PlatformServices" Version="5.0.0" />
|
||||
<PackageReference Include="System.Runtime.Experimental" Version="6.0.2" />
|
||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||
<ProjectReference Include="../spark_api/spark_api.csproj" />
|
||||
<ProjectReference Include="../spark_core/spark_core.csproj" />
|
||||
|
||||
@@ -71,7 +71,8 @@ namespace BCG {
|
||||
foreach (string sym in temp.Keys) {
|
||||
SortedList<DateTime, string> temp2 = temp[sym];
|
||||
foreach (KeyValuePair<DateTime, string> kvp in temp2) {
|
||||
string datecode = bts.api.DateFormatter.Format(kvp.Key, bts.api.DateFormat.MonthCodeLastDigitYear);
|
||||
string datecode = bts.api.SecurityFormatter.format_expiration(
|
||||
kvp.Key, date_format: bts.api.DateFormat.MonthCodeLastDigitYear);
|
||||
|
||||
string explegs = (temp.Keys.Count > 1) ? string.Concat(sym, datecode, "_", kvp.Value) :
|
||||
string.Concat(datecode, "_", kvp.Value);
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace BCG {
|
||||
string rv = String.Empty;
|
||||
foreach (SingleSpreadLeg l in legs)
|
||||
rv += (rv == String.Empty ? "" : "/") + l.symbol + //BCG.Utilities.Dates.DateCode.getMonthSymbol(l.exp) + (l.exp.Year - 2000);
|
||||
bts.api.DateFormatter.Format(l.exp, bts.api.DateFormat.DayMonthAbvDiff2000);
|
||||
SecurityFormatter.format_expiration(l.exp);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace bts.core {
|
||||
// TODO: for debugging purposes, remove it later
|
||||
if (tag != mbts.spark.SparkTags.SparkTimer) {
|
||||
var tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
bts.Log.Ui.diag($"SimpleBus.post tag:{tag} tid:{tid}");
|
||||
//bts.Log.Ui.diag($"SimpleBus.post tag:{tag} tid:{tid}");
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -45,7 +45,7 @@ namespace bts.core {
|
||||
|
||||
public IObservable<T> subscribe<T>(ITag<T> tag) {
|
||||
var tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
bts.Log.Ui.info($"SimpleBus.post tag:{tag} tid:{tid}");
|
||||
//bts.Log.Ui.info($"SimpleBus.subscribe tag:{tag} tid:{tid}");
|
||||
return subject<T>(tag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,6 +366,8 @@ namespace mbts.spark {
|
||||
public class Nothing { Nothing() { } }
|
||||
|
||||
public class TC2Tags {
|
||||
public static ITag<NMSheetAllTaskingData> TaskData { get; } =
|
||||
_mk_tag<NMSheetAllTaskingData>();
|
||||
// Data request for a sheet
|
||||
// Published by NMSheetDriverControlStrip
|
||||
public static ITag<NMSheetDataRequest> NMSheetDataRequestTag(string id) =>
|
||||
|
||||
@@ -253,6 +253,7 @@ namespace mbts.ui {
|
||||
public static BTSColor foreground => UI.default_foreground;
|
||||
public static BTSColor anchor => Colors.Gold;
|
||||
public static BTSColor profit => Colors.Green;
|
||||
public static BTSColor net_zero => BTSCoreColors.gray;
|
||||
public static BTSColor loss => BTSCoreColors.venetian_red;
|
||||
public static BTSColor header_row_background => 0xffaaaaaa;
|
||||
// group background colors
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace bts.proto {
|
||||
readonly double _opacity;
|
||||
|
||||
public ColorOpacityProto(Color color = default, double opacity = 1.0)
|
||||
: this(color.rgb_color(), opacity) { }
|
||||
: this(color.to_uint(), opacity) { }
|
||||
|
||||
public ColorOpacityProto(uint color = 0u, double opacity = 1.0) {
|
||||
_color = color;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace bts.proto {
|
||||
readonly bool _show;
|
||||
|
||||
public ColorShowProto(Color value, bool show = true)
|
||||
: this(value.rgb_color(), show) { }
|
||||
: this(value.to_uint(), show) { }
|
||||
|
||||
public ColorShowProto(uint value, bool show = true) {
|
||||
_value = value;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.utils;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
|
||||
namespace bts.proto {
|
||||
[ProtoContract]
|
||||
public struct ExpDate : IEquatable<ExpDate> {
|
||||
[ProtoMember(1)]
|
||||
readonly uint _month;
|
||||
[ProtoMember(2)]
|
||||
readonly uint _year;
|
||||
[ProtoMember(3)]
|
||||
readonly uint _day;
|
||||
|
||||
public ExpDate(SecurityImmutable security)
|
||||
: this(security.earliest_expiration()) { }
|
||||
|
||||
public ExpDate(DateTime date) {
|
||||
_month = (uint)date.Month;
|
||||
_year = (uint)date.Year;
|
||||
_day = (uint)date.Day;
|
||||
}
|
||||
|
||||
public uint month => _month;
|
||||
public uint year => _year;
|
||||
public uint day => _day;
|
||||
|
||||
public bool Equals(ExpDate other) =>
|
||||
other._month == _month && other._year == year && other._day == day;
|
||||
|
||||
public override bool Equals(object obj) => obj is ExpDate ed && Equals(ed);
|
||||
public static bool operator ==(ExpDate a, ExpDate b) => a.Equals(b);
|
||||
public static bool operator !=(ExpDate a, ExpDate b) => !a.Equals(b);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash((int)_month, (int)_day, (int)_year);
|
||||
|
||||
public override string ToString() =>
|
||||
DateFormatter.Format((int)_year, (int)_month, (int)_day, DateFormat.MonthCodeLastDigitYear);
|
||||
}
|
||||
|
||||
static class ExpDateExtensions {
|
||||
public static DateTime to_date_time(this ExpDate exp_date) =>
|
||||
new DateTime((int)exp_date.year, (int)exp_date.month, (int)exp_date.day);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,42 @@
|
||||
using bts.utils;
|
||||
|
||||
using mbts.ui;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
#nullable enable
|
||||
namespace bts.proto {
|
||||
using bts.utils;
|
||||
|
||||
using mbts.ui;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Windows.Media;
|
||||
|
||||
[ProtoContract]
|
||||
struct NullableColorProto : IEquatable<NullableColorProto> {
|
||||
readonly struct NullableColorProto : IEquatable<NullableColorProto> {
|
||||
[ProtoMember(1)]
|
||||
readonly uint? _value;
|
||||
|
||||
public NullableColorProto(Color? value) =>
|
||||
_value = value?.rgb_color();
|
||||
_value = value?.to_uint();
|
||||
|
||||
public NullableColorProto(uint value) =>
|
||||
_value = value;
|
||||
public static NullableColorProto none => new(null);
|
||||
|
||||
public Color? value => _value?.rgb_color();
|
||||
public Color? argb => _value?.argb_color();
|
||||
public Color? value => _value?.argb_color();
|
||||
|
||||
[MemberNotNullWhen(true, nameof(value))]
|
||||
public bool has_value => value.HasValue;
|
||||
|
||||
public bool Equals(NullableColorProto other) =>
|
||||
EqualityComparer.nullable_equals(_value, other._value);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
public override bool Equals(object? obj) =>
|
||||
obj is NullableColorProto ncp && Equals(ncp);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
_value.HasValue ? (int)_value.Value : 0;
|
||||
|
||||
public override string ToString() =>
|
||||
_value?.ToString() ?? string.Empty;
|
||||
|
||||
public static bool operator ==(NullableColorProto a, NullableColorProto b) => a.Equals(b);
|
||||
public static bool operator !=(NullableColorProto a, NullableColorProto b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
using bts.utils;
|
||||
|
||||
using mbts.utils;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace mbts.ui.settings {
|
||||
[ProtoContract]
|
||||
struct NullableFontWeightProto : IEquatable<NullableFontWeightProto> {
|
||||
[ProtoMember(1)]
|
||||
readonly ushort? _value;
|
||||
|
||||
public NullableFontWeightProto(FontWeight? value) =>
|
||||
_value = (ushort?)value?.ToOpenTypeWeight();
|
||||
|
||||
public NullableFontWeightProto(ushort value) =>
|
||||
_value = value;
|
||||
|
||||
public FontWeight? value {
|
||||
get {
|
||||
if (_value.HasValue)
|
||||
return FontWeight.FromOpenTypeWeight(_value.Value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(NullableFontWeightProto other) =>
|
||||
EqualityComparer.nullable_equals(_value, other._value);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is NullableFontWeightProto ncp && Equals(ncp);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
_value.HasValue ? _value.Value : 0;
|
||||
|
||||
public static bool operator ==(NullableFontWeightProto a, NullableFontWeightProto b) => a.Equals(b);
|
||||
public static bool operator !=(NullableFontWeightProto a, NullableFontWeightProto b) => !a.Equals(b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#nullable enable
|
||||
namespace bts.proto {
|
||||
using bts.utils;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Windows;
|
||||
|
||||
[ProtoContract]
|
||||
readonly struct NullableFontWeightProto : IEquatable<NullableFontWeightProto> {
|
||||
[ProtoMember(1)]
|
||||
readonly int? _value;
|
||||
|
||||
public NullableFontWeightProto(FontWeight? value) =>
|
||||
_value = value?.ToOpenTypeWeight();
|
||||
|
||||
public NullableFontWeightProto(int value) =>
|
||||
_value = value;
|
||||
|
||||
public static NullableFontWeightProto none =>
|
||||
new(null);
|
||||
|
||||
public FontWeight? value =>
|
||||
_value.HasValue ? FontWeight.FromOpenTypeWeight(_value.Value) : null;
|
||||
|
||||
[MemberNotNullWhen(true, nameof(value))]
|
||||
public bool has_value => value.HasValue;
|
||||
|
||||
public bool Equals(NullableFontWeightProto other) =>
|
||||
EqualityComparer.nullable_equals(_value, other._value);
|
||||
|
||||
public override bool Equals(object? obj) =>
|
||||
obj is NullableFontWeightProto fw && Equals(fw);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
_value ?? 0;
|
||||
|
||||
public override string ToString() =>
|
||||
_value?.ToString() ?? string.Empty;
|
||||
|
||||
public static bool operator ==(NullableFontWeightProto a, NullableFontWeightProto b) =>
|
||||
a.Equals(b);
|
||||
|
||||
public static bool operator !=(NullableFontWeightProto a, NullableFontWeightProto b) =>
|
||||
!a.Equals(b);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ namespace mbts.ui.controls {
|
||||
color = brush.Color;
|
||||
opacity = brush.Opacity;
|
||||
}
|
||||
public ColorOpacityProto to_proto() => new ColorOpacityProto(color.rgb_color(), opacity);
|
||||
public ColorOpacityProto to_proto() => new ColorOpacityProto(color.to_uint(), opacity);
|
||||
}
|
||||
|
||||
class NamedColorPickerPopupVM : ColorPickerPopupVM {
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace mbts.ui.settings {
|
||||
public bool is_expanded => _is_expanded;
|
||||
public FontWeight? font_weight_override => _font_weight_override.value;
|
||||
public Color? font_color_override => _font_color_override.value;
|
||||
public Color? background_color_override => _background_color_override.argb;
|
||||
public Color? background_color_override => _background_color_override.value;
|
||||
public double width => _width;
|
||||
public HorizontalAlignment horizontal_alignment => _horizontal_alignment;
|
||||
public double horizontal_offset => _horizontal_offset;
|
||||
@@ -126,7 +126,7 @@ namespace mbts.ui.settings {
|
||||
width ?? this.width,
|
||||
horizontal_alignment ?? this.horizontal_alignment,
|
||||
horizontal_offset ?? this.horizontal_offset,
|
||||
(background_color_override ?? _background_color_override).argb);
|
||||
(background_color_override ?? _background_color_override).value);
|
||||
|
||||
public GridColumnSettings Clone() =>
|
||||
ProtobufSerializationHelper.clone(this);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
public static GridSettings Default =>
|
||||
new GridSettings(
|
||||
selection_highlight: Colors.MediumSlateBlue.with(0x63).argb_color(),
|
||||
selection_highlight: Colors.MediumSlateBlue.with(0x63).to_uint(),
|
||||
background: BTSColors.Grid.default_column_background,
|
||||
font_color: BTSColors.Grid.default_column_foreground);
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace mbts.ui.settings {
|
||||
var a = sl.A.within_range((byte)30, (byte)200);
|
||||
if (sl.A != a) {
|
||||
sl.A = a;
|
||||
new_settings = settings.with(selection_highlight: sl.rgb_color());
|
||||
new_settings = settings.with(selection_highlight: sl.to_uint());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,9 +137,9 @@ namespace mbts.spark {
|
||||
foreach (IGrouping<TPredicateData, CellAnimation> g in animations)
|
||||
animation_map.AddOrUpdate(g.Key, p => animation_tuple(c: g), (p, t) => t.cell_animations.AddRange(g));
|
||||
}
|
||||
return _create_settings_obj(new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.color.rgb_color(), col_settings,
|
||||
return _create_settings_obj(new GridSettings(font_family.name(), font_size, font_weight, font_color.color.to_uint(), selection_highlight.color.to_uint(), col_settings,
|
||||
background: background_color.color.with(
|
||||
(byte)(background_color.opacity.within_range(0, 1) * 255.0)).rgb_color()),
|
||||
(byte)(background_color.opacity.within_range(0, 1) * 255.0)).to_uint()),
|
||||
animation_map.Select(kv => PredicateAnimations.create_data<TRowData, TPredicateData>(kv.Key, kv.Value.cell_animations, kv.Value.row_animations, kv.Value.sounds)));
|
||||
//
|
||||
(List<RowAnimation> row_animations, List<SoundNotification> sounds, List<CellAnimation> cell_animations) animation_tuple(IEnumerable<RowAnimation> r = null, IEnumerable<SoundNotification> s = null, IGrouping<TPredicateData, CellAnimation> c = null) =>
|
||||
|
||||
@@ -120,10 +120,10 @@ namespace mbts.spark {
|
||||
font_family.name(),
|
||||
font_size,
|
||||
font_weight,
|
||||
font_color.color.rgb_color(),
|
||||
selection_highlight.color.rgb_color(),
|
||||
font_color.color.to_uint(),
|
||||
selection_highlight.color.to_uint(),
|
||||
columns.Select(c => c.create()),
|
||||
background: background_color.color.argb_color());
|
||||
background: background_color.color.to_uint());
|
||||
|
||||
public void load_from(GridSettings value) {
|
||||
if (value == null)
|
||||
|
||||
@@ -258,13 +258,13 @@ namespace mbts.spark {
|
||||
settings.title_font_params = new() {
|
||||
typeface = new(title_font_family, title_font_weight.ToOpenTypeWeight()),
|
||||
font_size = title_font_size,
|
||||
font_color = title_font_color.argb_color(),
|
||||
font_color = title_font_color.to_uint(),
|
||||
};
|
||||
|
||||
settings.font_params = new() {
|
||||
typeface = new(font_family, font_weight.ToOpenTypeWeight()),
|
||||
font_size = font_size,
|
||||
font_color = font_color.argb_color(),
|
||||
font_color = font_color.to_uint(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<TextBlock
|
||||
x:Name="PART_Context"
|
||||
FontWeight="Bold"
|
||||
Foreground="{u:BTSBrush UI.default_text}"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="4,0,0,0"
|
||||
Text="{Binding context}"
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace mbts.ui.controls {
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
|
||||
static class TickUpDownHelper {
|
||||
public const string
|
||||
@@ -13,7 +14,7 @@ namespace mbts.ui.controls {
|
||||
|
||||
public static string? format<T>(
|
||||
T? new_value, string curr_text, TickDisplaySettingsImmutable? settings = null)
|
||||
where T : struct, IFloatingPoint<T> {
|
||||
where T : struct, IFloatingPointIeee754<T> {
|
||||
if (T.TryParse(curr_text, null, out var from_text)) {
|
||||
if (!from_text.Equals(new_value)) {
|
||||
return format(new_value, settings);
|
||||
@@ -25,7 +26,7 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
|
||||
public static string format<T>(T? value, TickDisplaySettingsImmutable? settings = null)
|
||||
where T : struct, IFloatingPoint<T> {
|
||||
where T : struct, IFloatingPointIeee754<T> {
|
||||
if (!value.HasValue) {
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -39,7 +40,7 @@ namespace mbts.ui.controls {
|
||||
string? text,
|
||||
[NotNullWhen(true)] out T? result,
|
||||
TickDisplaySettingsImmutable? settings = null
|
||||
) where T : IFloatingPoint<T> {
|
||||
) where T : IFloatingPointIeee754<T> {
|
||||
if (text is null) {
|
||||
result = default;
|
||||
return false;
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace mbts.ui.controls {
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
@@ -3,13 +3,14 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace mbts.ui.converters {
|
||||
class DictionaryLookupConverter : IMultiValueConverter {
|
||||
[ThreadStatic]
|
||||
Dictionary<Type, Lookup> _type_impls_backing;
|
||||
Dictionary<Type, Lookup> _type_impls => _type_impls_backing ?? (_type_impls_backing = new Dictionary<Type, Lookup>());
|
||||
Dictionary<Type, Lookup> _type_impls =>
|
||||
Interlocked.CompareExchange(ref _type_impls_backing, new(), null);
|
||||
|
||||
delegate bool Lookup(object source, object key, out object result);
|
||||
|
||||
|
||||
@@ -22,16 +22,11 @@ namespace mbts.ui.converters {
|
||||
object value, Type target_type, object parameter, CultureInfo culture) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public string convert(SecurityExpiration exp) {
|
||||
var sb = new StringBuilder();
|
||||
if (ShowType) {
|
||||
sb.Append($"[{SecurityFormatter.format_security_type(exp.security_type)}] ");
|
||||
}
|
||||
if (ShowSymbol) {
|
||||
sb.Append(exp.symbol);
|
||||
}
|
||||
sb.Append(DateFormatter.Format(exp.expiration, DateFormat));
|
||||
return sb.ToString();
|
||||
}
|
||||
public string convert(SecurityExpiration exp) =>
|
||||
SecurityFormatter.format_expiration(
|
||||
exp.expiration,
|
||||
ShowSymbol ? exp.symbol : null,
|
||||
ShowType ? exp.security_type.contract_case() : null,
|
||||
DateFormat);
|
||||
}
|
||||
}
|
||||
@@ -114,10 +114,7 @@ namespace mbts.ui {
|
||||
public static SolidColorBrush rgb_solid_brush(this uint color) =>
|
||||
new SolidColorBrush(rgb_color(color));
|
||||
|
||||
public static UInt32 argb_color(this Color color) =>
|
||||
(uint)color.A << 24 | (uint)color.R << 16 | (uint)color.G << 8 | (uint)color.B;
|
||||
|
||||
public static UInt32 rgb_color(this Color color) =>
|
||||
public static uint to_uint(this Color color) =>
|
||||
(uint)color.A << 24 | (uint)color.R << 16 | (uint)color.G << 8 | (uint)color.B;
|
||||
|
||||
public static Brush to_brush(this Color color) =>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
using Spark.Core.Disposables;
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using Spark.Core.Disposables;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.ui {
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace mbts {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
|
||||
public static class TickExtensions {
|
||||
static string _nan_text => TickDisplaySettings.Default.nan_text;
|
||||
@@ -109,20 +110,20 @@ namespace mbts {
|
||||
public static string format_without_decimal_part(this TickFactors tick, double value) =>
|
||||
format(tick, value, double.NaN, false, _nan_text, true);
|
||||
|
||||
public static string format<T>(this TickFactors tick, T value) where T : IFloatingPoint<T> =>
|
||||
public static string format<T>(this TickFactors tick, T value) where T : IFloatingPointIeee754<T> =>
|
||||
format(tick, value, T.NaN, false, _nan_text);
|
||||
|
||||
public static string format<T>(this TickFactors tick, T value, bool show_commas)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
format(tick, value, T.NaN, show_commas, _nan_text);
|
||||
|
||||
public static string format<T>(this TickDisplaySettings settings, T value)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
format(settings.tick_factors, value, settings);
|
||||
|
||||
public static string format<T>(
|
||||
this bts.api.domain.TickDisplaySettingsImmutable settings, T value)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
settings.to_tick_display_settings().format(value);
|
||||
|
||||
public static string format<T>(this TickFactors tick, T value, TickDisplaySettings settings)
|
||||
@@ -149,7 +150,7 @@ namespace mbts {
|
||||
bool show_commas,
|
||||
string nan_text,
|
||||
bool show_decimal_part = true)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tick.format(
|
||||
value,
|
||||
TickDisplaySettings.Default.with(
|
||||
@@ -196,7 +197,7 @@ namespace mbts {
|
||||
parse(display_settings.tick_factors, text, display_settings);
|
||||
|
||||
public static T parse<T>(this TickDisplaySettings display_settings, string text)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
parse<T>(display_settings.tick_factors, text, display_settings);
|
||||
#nullable disable
|
||||
|
||||
@@ -204,7 +205,7 @@ namespace mbts {
|
||||
parse<double>(tick, text, display_settings);
|
||||
|
||||
public static T parse<T>(this TickFactors tick, string text, TickDisplaySettings display_settings)
|
||||
where T : IFloatingPoint<T> {
|
||||
where T : IFloatingPointIeee754<T> {
|
||||
if (display_settings is null) {
|
||||
return parse(tick, text, T.NaN, _nan_text);
|
||||
}
|
||||
@@ -213,7 +214,9 @@ namespace mbts {
|
||||
return parse(tick, text, display_multiplier, nan_text);
|
||||
}
|
||||
|
||||
public static T parse<T>(this TickFactors tick, string text, T display_multiplier, string nan_text) where T : IFloatingPoint<T> =>
|
||||
public static T parse<T>(
|
||||
this TickFactors tick, string text, T display_multiplier, string nan_text)
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tick?.is_valid() ?? false ?
|
||||
_parse(tick, text, display_multiplier, nan_text) :
|
||||
_parse_no_tick(text, display_multiplier, nan_text);
|
||||
@@ -223,37 +226,37 @@ namespace mbts {
|
||||
string text,
|
||||
T display_multiplier,
|
||||
string nan_text
|
||||
) where T : IFloatingPoint<T> =>
|
||||
) where T : IFloatingPointIeee754<T> =>
|
||||
regional_tick.is_valid() ?
|
||||
_parse(regional_tick, text, display_multiplier, nan_text) :
|
||||
_parse_no_tick(text, display_multiplier, nan_text);
|
||||
|
||||
public static T tick<T>(this TickFactors tick, T value, int count)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
count < 0 ? tick.tickdown(value, (uint)-count) : tick.tickup(value, (uint)count);
|
||||
|
||||
public static T tick<T>(this bts.api.domain.TickFactorsImmutable tick, T value, int count)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tick.to_tick_factors().tick(value, count);
|
||||
|
||||
public static T tick<T>(
|
||||
this bts.api.domain.TickDisplaySettingsImmutable settings, T value, int count)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
settings.HasTickFactors ?
|
||||
settings.TickFactors.tick(value, count) :
|
||||
throw new ArgumentException(
|
||||
"Can't tick because 'settings.HasTickFactors' is false", nameof(settings));
|
||||
|
||||
public static T tickup<T>(this TickFactors tick, T input, uint count = 1)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tickup(tick, input, count, TickUtils.precision_const<T>());
|
||||
|
||||
public static T tickup<T>(this TickFactors tick, T input, uint count, T precision)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tickup<T, NumericCastOutConverter<T>>(tick, input, count, precision);
|
||||
|
||||
public static T tickup<T, TFromUInt32>(this TickFactors tick, T input, uint count, T precision)
|
||||
where T : IFloatingPoint<T>
|
||||
where T : IFloatingPointIeee754<T>
|
||||
where TFromUInt32 : IConverter<uint, T> {
|
||||
if (!precision.is_real_positive()) {
|
||||
throw new ArgumentException("Must be a real positive number.", nameof(precision));
|
||||
@@ -262,7 +265,7 @@ namespace mbts {
|
||||
return input;
|
||||
}
|
||||
var tuf = tick.tick_unit_frac<T>();
|
||||
var ieeer = T.IEEERemainder(input, tuf);
|
||||
var ieeer = T.Ieee754Remainder(input, tuf);
|
||||
input -= ieeer;
|
||||
if (ieeer < -precision) {
|
||||
count--;
|
||||
@@ -271,15 +274,18 @@ namespace mbts {
|
||||
return _round_tick(tuf, result);
|
||||
}
|
||||
|
||||
public static T tickup<T>(this RegionalTickFactors regional_tick, T input) where T : IFloatingPoint<T> =>
|
||||
public static T tickup<T>(
|
||||
this RegionalTickFactors regional_tick, T input) where T : IFloatingPointIeee754<T> =>
|
||||
regional_tick.tickup(input, 1);
|
||||
|
||||
public static T tickup<T>(this RegionalTickFactors regional_tick, T input, uint count) where T : IFloatingPoint<T> =>
|
||||
public static T tickup<T>(
|
||||
this RegionalTickFactors regional_tick, T input, uint count)
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
regional_tick.tickup(input, count, TickUtils.precision_const<T>());
|
||||
|
||||
public static T tickup<T>(
|
||||
this RegionalTickFactors regional_tick, T input, uint count, T precision)
|
||||
where T : IFloatingPoint<T> {
|
||||
where T : IFloatingPointIeee754<T> {
|
||||
if (!precision.is_real_positive()) {
|
||||
throw new ArgumentException("Must be a real positive number.", nameof(precision));
|
||||
}
|
||||
@@ -292,7 +298,7 @@ namespace mbts {
|
||||
T ltie = regional_tick.low_tick_inclusive_end.as_type<double, T>();
|
||||
if (input < ltie) {
|
||||
tuf = regional_tick.low_tick.tick_unit_frac<T>();
|
||||
var ieeer = T.IEEERemainder(input, tuf);
|
||||
var ieeer = T.Ieee754Remainder(input, tuf);
|
||||
input -= ieeer;
|
||||
if (ieeer < -precision) count--;
|
||||
val = input + tuf * count.as_type<uint, T>();
|
||||
@@ -303,7 +309,7 @@ namespace mbts {
|
||||
}
|
||||
} else {
|
||||
tuf = regional_tick.high_tick.tick_unit_frac<T>();
|
||||
var ieeer = T.IEEERemainder(input, tuf);
|
||||
var ieeer = T.Ieee754Remainder(input, tuf);
|
||||
input -= ieeer;
|
||||
if (ieeer < -precision) count--;
|
||||
val = input + tuf * count.as_type<uint, T>();
|
||||
@@ -312,21 +318,21 @@ namespace mbts {
|
||||
}
|
||||
|
||||
public static T tickdown<T>(this TickFactors tick, T input, uint count = 1)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tickdown(tick, input, count, TickUtils.precision_const<T>());
|
||||
|
||||
public static T tickdown<T>(this TickFactors tick, T input, uint count, T precision)
|
||||
where T : IFloatingPoint<T> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tickdown<T, NumericCastOutConverter<T>>(tick, input, count, precision);
|
||||
|
||||
public static T tickdown<T, TFromUInt32>(this TickFactors tick, T input, uint count, T precision)
|
||||
where T : IFloatingPoint<T>
|
||||
where T : IFloatingPointIeee754<T>
|
||||
where TFromUInt32 : IConverter<uint, T> {
|
||||
if (!input.is_real()) {
|
||||
return input;
|
||||
}
|
||||
var tuf = tick.tick_unit_frac<T>();
|
||||
var ieeer = T.IEEERemainder(input, tuf);
|
||||
var ieeer = T.Ieee754Remainder(input, tuf);
|
||||
input -= ieeer;
|
||||
if (ieeer > precision) {
|
||||
count--;
|
||||
@@ -336,21 +342,22 @@ namespace mbts {
|
||||
}
|
||||
|
||||
public static T tickdown<T>(this RegionalTickFactors regional_tick, T input)
|
||||
where T : IFloatingPoint<T>, IComparisonOperators<T, double> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
regional_tick.tickdown(input, 1);
|
||||
|
||||
public static T tickdown<T>(this RegionalTickFactors regional_tick, T input, uint count)
|
||||
where T : IFloatingPoint<T>, IComparisonOperators<T, double> =>
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tickdown(regional_tick, input, count, TickUtils.precision_const<T>());
|
||||
|
||||
public static T tickdown<T>(
|
||||
this RegionalTickFactors regional_tick, T input, uint count, T precision) where T : IFloatingPoint<T> =>
|
||||
this RegionalTickFactors regional_tick, T input, uint count, T precision)
|
||||
where T : IFloatingPointIeee754<T> =>
|
||||
tickdown<T, NumericCastOutConverter<T>, NumericCastOutConverter<T>, NumericCastInConverter<T>>(
|
||||
regional_tick, input, count, precision);
|
||||
|
||||
public static T tickdown<T, TFromUInt32, TFromDouble, TToUInt16>(
|
||||
this RegionalTickFactors regional_tick, T input, uint count, T precision)
|
||||
where T : IFloatingPoint<T>
|
||||
where T : IFloatingPointIeee754<T>
|
||||
where TFromDouble : IConverter<double, T>
|
||||
where TFromUInt32 : IConverter<uint, T>
|
||||
where TToUInt16 : IConverter<T, uint> {
|
||||
@@ -365,7 +372,7 @@ namespace mbts {
|
||||
T low_tick_inclusive_end = TFromDouble.convert(regional_tick.low_tick_inclusive_end);
|
||||
if (input > low_tick_inclusive_end) {
|
||||
tuf = regional_tick.high_tick.tick_unit_frac<T>();
|
||||
var ieeer = T.IEEERemainder(input, tuf);
|
||||
var ieeer = T.Ieee754Remainder(input, tuf);
|
||||
input -= ieeer;
|
||||
if (ieeer > precision) count--;
|
||||
val = input - tuf * TFromDouble.convert(count);
|
||||
@@ -377,7 +384,7 @@ namespace mbts {
|
||||
}
|
||||
} else {
|
||||
tuf = regional_tick.low_tick.tick_unit_frac<T>();
|
||||
var ieeer = T.IEEERemainder(input, tuf);
|
||||
var ieeer = T.Ieee754Remainder(input, tuf);
|
||||
input -= ieeer;
|
||||
if (ieeer > precision) count--;
|
||||
val = input - tuf * TFromDouble.convert(count);
|
||||
@@ -483,7 +490,7 @@ namespace mbts {
|
||||
return display_digits(tick.tick_base);
|
||||
}
|
||||
|
||||
public static int display_digits<T>(T tick_base) where T : IFloatingPoint<T> {
|
||||
public static int display_digits<T>(T tick_base) where T : IFloatingPoint<T>, ILogarithmicFunctions<T> {
|
||||
var log_tick_base = T.Log10(tick_base);
|
||||
var floor = T.Floor(log_tick_base);
|
||||
var dec = log_tick_base - floor;
|
||||
@@ -500,14 +507,14 @@ namespace mbts {
|
||||
}
|
||||
|
||||
static string _format_no_tick<T>(T value, T display_multiplier, bool show_commas)
|
||||
where T : IFloatingPoint<T> {
|
||||
where T : INumberBase<T> {
|
||||
if (!display_multiplier.is_nan()) {
|
||||
value *= display_multiplier;
|
||||
}
|
||||
return value.ToString(show_commas ? "#,0" : "0", null);
|
||||
}
|
||||
|
||||
static T _parse_no_tick<T>(string text, T display_multiplier, string nan_text) where T : IFloatingPoint<T> {
|
||||
static T _parse_no_tick<T>(string text, T display_multiplier, string nan_text) where T : IFloatingPointIeee754<T> {
|
||||
if (text == nan_text) {
|
||||
return T.NaN;
|
||||
}
|
||||
@@ -515,7 +522,7 @@ namespace mbts {
|
||||
result.safe_divide(display_multiplier) : T.NaN;
|
||||
}
|
||||
|
||||
static T _get_integer_part<T>(string integer_text) where T : IFloatingPoint<T> {
|
||||
static T _get_integer_part<T>(string integer_text) where T : IFloatingPointIeee754<T> {
|
||||
if (integer_text.is_null_or_white_space()) {
|
||||
return T.Zero;
|
||||
}
|
||||
@@ -524,7 +531,7 @@ namespace mbts {
|
||||
result : T.NaN;
|
||||
}
|
||||
|
||||
static T _get_numerator_part<T>(string numerator_text, T tick_base) where T : IFloatingPoint<T> {
|
||||
static T _get_numerator_part<T>(string numerator_text, T tick_base) where T : IFloatingPointIeee754<T> {
|
||||
if (string.IsNullOrEmpty(numerator_text)) {
|
||||
return T.Zero;
|
||||
}
|
||||
@@ -554,7 +561,7 @@ namespace mbts {
|
||||
|
||||
static T _parse<T>(
|
||||
RegionalTickFactors regional_tick, string text, T display_multiplier, string nan_text)
|
||||
where T : IFloatingPoint<T> {
|
||||
where T : IFloatingPointIeee754<T> {
|
||||
T result = _parse(regional_tick.low_tick, text, display_multiplier, nan_text);
|
||||
|
||||
T ltie = regional_tick.low_tick_inclusive_end.as_type<double, T>()
|
||||
@@ -566,7 +573,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
static T _parse<T>(TickFactors tick, string text, T display_multiplier, string nan_text)
|
||||
where T : IFloatingPoint<T> {
|
||||
where T : IFloatingPointIeee754<T> {
|
||||
if (text.is_null_or_white_space() || text == nan_text) {
|
||||
return T.NaN;
|
||||
}
|
||||
@@ -584,7 +591,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
static T _parse<T>(TickFactors tick, T integer_part, string numerator_part_text)
|
||||
where T : IFloatingPoint<T> {
|
||||
where T : IFloatingPointIeee754<T> {
|
||||
T tick_base = tick.tick_base.as_type<double, T>();
|
||||
T numerator_part = _get_numerator_part(numerator_part_text, tick_base);
|
||||
return integer_part + numerator_part / tick_base;
|
||||
@@ -657,7 +664,7 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
|
||||
static T _round_tick<T>(T tick_unit_frac, T input) where T : IFloatingPoint<T> =>
|
||||
input - T.IEEERemainder(input, tick_unit_frac);
|
||||
static T _round_tick<T>(T tick_unit_frac, T input) where T : IFloatingPointIeee754<T> =>
|
||||
input - T.Ieee754Remainder(input, tick_unit_frac);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Spark.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace mbts.spark {
|
||||
static class TickUtils {
|
||||
|
||||
@@ -33,13 +33,6 @@
|
||||
public static string Format(DateTime date, DateFormat format) =>
|
||||
Format(date.Year, date.Month, date.Day, format);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a concise string representation of a date (using single-letter month codes)
|
||||
/// appropriate for UIs (e.g. March 22, 2019: 22 Mar 19, or 22 H19).
|
||||
/// </summary>
|
||||
public static string Format(uint year, uint month, uint day, DateFormat format) =>
|
||||
Format((int)year, (int)month, (int)day, format);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a concise string representation of a date (using single-letter month codes)
|
||||
/// appropriate for UIs (e.g. March 22, 2019: 22 Mar 19, or 22 H19).
|
||||
|
||||
@@ -10,6 +10,9 @@ namespace bts.api {
|
||||
public partial class Security {
|
||||
public static security_key key(Security s) { return s.Id; }
|
||||
|
||||
public static bool is_perpetual_exp(DateTime exp) =>
|
||||
exp.Year < 2000 || exp.Year > 2999;
|
||||
|
||||
public static class Defaults {
|
||||
public static ContractSpecification ContractSpecification =>
|
||||
new() {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
format_future(symbol, (uint)exp.Year, (uint)exp.Month, (uint)exp.Day);
|
||||
|
||||
public static string format_future(string symbol, uint exp_year, uint exp_month, uint exp_day) =>
|
||||
$"{symbol}{DateFormatter.Format(exp_year, exp_month, exp_day, DateFormat.DayMonthAbvDiff2000)}";
|
||||
$"{symbol}{format_expiration(new((int)exp_year, (int)exp_month, (int)exp_day))}";
|
||||
|
||||
public static string format_future(string symbol, FutureContractImmutable future) =>
|
||||
format_future(symbol, future.ExpirationYear, future.ExpirationMonth, future.ExpirationDay);
|
||||
@@ -108,12 +108,17 @@
|
||||
string? symbol = null,
|
||||
Security.ContractOneofCase? type = null,
|
||||
DateFormat date_format = DateFormat.DayMonthAbvDiff2000) {
|
||||
var result = DateFormatter.Format(exp_date, date_format);
|
||||
var result = Security.is_perpetual_exp(exp_date) ?
|
||||
"Perp" : DateFormatter.Format(exp_date, date_format);
|
||||
if (symbol is not null) {
|
||||
result = symbol + " " + result;
|
||||
}
|
||||
if (type.HasValue) {
|
||||
result = format_security_type(type.Value) + " " + result;
|
||||
if (result.Length == 0) {
|
||||
result = format_security_type(type.Value);
|
||||
} else {
|
||||
result = $"[{format_security_type(type.Value)}] {result}";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Authors>Blue Trading Systems</Authors>
|
||||
<Company>Blue Trading Systems</Company>
|
||||
<Description />
|
||||
@@ -11,6 +11,7 @@
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<NoWarn>8981</NoWarn>
|
||||
<Version>$(major).$(minor).$(patch)</Version>
|
||||
<Version Condition="'$(version)'=='..'">0.0.0</Version>
|
||||
</PropertyGroup>
|
||||
@@ -20,7 +21,6 @@
|
||||
<PackageReference Include="Google.Protobuf" Version="3.21.4" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageReference Include="System.Runtime.Experimental" Version="6.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
|
||||
class Program {
|
||||
static int Main(string[] args) {
|
||||
Console.WriteLine("Starting spark_api_server...");
|
||||
Console.WriteLine("Starting spark_api_server... just checking build");
|
||||
// TODO
|
||||
bts.websocket_core.WebsocketDataSync sync =
|
||||
new bts.websocket_core.WebsocketDataSync();
|
||||
bts.websocket_core.WebsocketConnectionGateway gwy =
|
||||
new bts.websocket_core.WebsocketConnectionGateway();
|
||||
bts.websocket_core.WebsocketServerParams prms = new bts.websocket_core.WebsocketServerParams();
|
||||
gwy.init(sync);
|
||||
sync.init(gwy);
|
||||
gwy.start_listening(prms);
|
||||
while (true) {
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
|
||||
@@ -61,9 +61,6 @@ namespace System.Collections.Generic {
|
||||
public static IDictionary<TKey, TValue> as_idictionary<TKey, TValue>(this Dictionary<TKey, TValue> dictionary) =>
|
||||
dictionary;
|
||||
|
||||
public static IReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(this IDictionary<TKey, TValue> dictionary) =>
|
||||
new ReadOnlyDictionary<TKey, TValue>(dictionary);
|
||||
|
||||
public static void Difference<TKey, TValue>(this IDictionary<TKey, TValue> left,
|
||||
IDictionary<TKey, TValue> right,
|
||||
out IEnumerable<KeyValuePair<TKey, TValue>> exclusive_left,
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace System.Collections.Generic {
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
@@ -15,7 +15,6 @@
|
||||
<PackageReference Include="Google.Protobuf" Version="3.21.4" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageReference Include="protobuf-net" Version="3.1.17" />
|
||||
<PackageReference Include="System.Runtime.Experimental" Version="6.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace mbts.utils {
|
||||
static class ExceptionAssert {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace bts.utils {
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
public static class MathUtils {
|
||||
public const double EPSILON = 1E-12;
|
||||
@@ -14,7 +15,7 @@ namespace bts.utils {
|
||||
public static readonly IComparer<double> epsilon_comparer = new ErrorMarginComparer<double>(EPSILON);
|
||||
|
||||
public static T abs_diff<T>(T x, T y)
|
||||
where T : ISubtractionOperators<T, T, T>, IComparisonOperators<T, T> =>
|
||||
where T : ISubtractionOperators<T, T, T>, IComparisonOperators<T, T, bool> =>
|
||||
x > y ? (x - y) : (y - x);
|
||||
|
||||
public static IComparer<double> error_margin_comparer(double error_margin) =>
|
||||
@@ -36,14 +37,14 @@ namespace bts.utils {
|
||||
epsilon_equals(x, y, NormalNumericOps<T>.epsilon);
|
||||
|
||||
public static bool epsilon_equals<T>(T x, T y, T epsilon)
|
||||
where T : ISubtractionOperators<T, T, T>, IComparisonOperators<T, T> =>
|
||||
where T : ISubtractionOperators<T, T, T>, IComparisonOperators<T, T, bool> =>
|
||||
abs_diff(x, y) < epsilon;
|
||||
|
||||
public static bool marginal_equals(double x, double y) =>
|
||||
marginal_equals(x, y, EPSILON);
|
||||
|
||||
public static bool marginal_equals<T>(T x, T y, T margin)
|
||||
where T : ISubtractionOperators<T, T, T>, IComparisonOperators<T, T> =>
|
||||
where T : ISubtractionOperators<T, T, T>, IComparisonOperators<T, T, bool> =>
|
||||
(x > y ? (x - y) : (y - x)) < margin;
|
||||
|
||||
public static bool is_nan<T>(T value) where T : INumber<T> =>
|
||||
@@ -132,7 +133,7 @@ namespace bts.utils {
|
||||
public static T nan_diff<T>(T a, T b) where T : IFloatingPoint<T> =>
|
||||
T.IsNaN(a) ? -b : T.IsNaN(b) ? a : a - b;
|
||||
|
||||
public static bool is_within_range<T>(T value, T min, T max) where T : IComparisonOperators<T, T> =>
|
||||
public static bool is_within_range<T>(T value, T min, T max) where T : IComparisonOperators<T, T, bool> =>
|
||||
value >= min && value <= max;
|
||||
|
||||
public static T weighted_average<T>(T x, T x_weight, T y, T y_weight) where T : IFloatingPoint<T> {
|
||||
@@ -145,7 +146,7 @@ namespace bts.utils {
|
||||
return x + (y - x) * (x_weight / (x_weight + y_weight));
|
||||
}
|
||||
|
||||
public static T within_range<T>(T value, T min, T max) where T : IComparisonOperators<T, T> {
|
||||
public static T within_range<T>(T value, T min, T max) where T : IComparisonOperators<T, T, bool> {
|
||||
if (value < min) {
|
||||
return min;
|
||||
}
|
||||
@@ -156,7 +157,7 @@ namespace bts.utils {
|
||||
}
|
||||
|
||||
public static T within_range<T>(
|
||||
T value, T? min = null, T? max = null) where T : struct, IComparisonOperators<T, T> {
|
||||
T value, T? min = null, T? max = null) where T : struct, IComparisonOperators<T, T, bool> {
|
||||
if (min.HasValue && value < min.Value) {
|
||||
return min.Value;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ namespace Spark.Core {
|
||||
using bts;
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
public class NumericCastConverter :
|
||||
IConverter<int, int>,
|
||||
@@ -213,7 +214,7 @@ namespace Spark.Core {
|
||||
IConverter<T, double>,
|
||||
IConverter<T, float>,
|
||||
IConverter<T, decimal>
|
||||
where T : INumber<T> {
|
||||
where T : INumberBase<T> {
|
||||
|
||||
static readonly Func<T, short> _to_int16;
|
||||
static readonly Func<T, ushort> _to_uint16;
|
||||
@@ -342,7 +343,9 @@ namespace Spark.Core {
|
||||
static decimal IConverter<T, decimal>.convert(T value) => _to_decimal(value);
|
||||
}
|
||||
|
||||
public class NumericCastConverter<TIn, TOut> : IConverter<TIn, TOut> where TIn : INumber<TIn> where TOut : INumber<TOut> {
|
||||
public class NumericCastConverter<TIn, TOut> : IConverter<TIn, TOut>
|
||||
where TIn : INumberBase<TIn>
|
||||
where TOut : INumberBase<TOut> {
|
||||
static readonly Func<TIn, TOut> _convert;
|
||||
|
||||
static NumericCastConverter() {
|
||||
|
||||
@@ -3,44 +3,45 @@ namespace bts.utils {
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
public static class NumericExtensions {
|
||||
public static TOut as_type<TOut>(this short value) where TOut : INumber<TOut> =>
|
||||
public static TOut as_type<TOut>(this short value) where TOut : INumberBase<TOut> =>
|
||||
as_type<short, TOut>(value);
|
||||
|
||||
public static TOut as_type<TOut>(this ushort value) where TOut : INumber<TOut> =>
|
||||
public static TOut as_type<TOut>(this ushort value) where TOut : INumberBase<TOut> =>
|
||||
as_type<ushort, TOut>(value);
|
||||
|
||||
public static TOut as_type<TOut>(this int value) where TOut : INumber<TOut> =>
|
||||
public static TOut as_type<TOut>(this int value) where TOut : INumberBase<TOut> =>
|
||||
as_type<int, TOut>(value);
|
||||
|
||||
public static TOut as_type<TOut>(this uint value) where TOut : INumber<TOut> =>
|
||||
public static TOut as_type<TOut>(this uint value) where TOut : INumberBase<TOut> =>
|
||||
as_type<uint, TOut>(value);
|
||||
|
||||
public static TOut as_type<TOut>(this long value) where TOut : INumber<TOut> =>
|
||||
public static TOut as_type<TOut>(this long value) where TOut : INumberBase<TOut> =>
|
||||
as_type<long, TOut>(value);
|
||||
|
||||
public static TOut as_type<TOut>(this ulong value) where TOut : INumber<TOut> =>
|
||||
public static TOut as_type<TOut>(this ulong value) where TOut : INumberBase<TOut> =>
|
||||
as_type<ulong, TOut>(value);
|
||||
|
||||
public static TOut as_type<TOut>(this float value) where TOut : INumber<TOut> =>
|
||||
public static TOut as_type<TOut>(this float value) where TOut : INumberBase<TOut> =>
|
||||
as_type<float, TOut>(value);
|
||||
|
||||
public static TOut as_type<TOut>(this double value) where TOut : INumber<TOut> =>
|
||||
public static TOut as_type<TOut>(this double value) where TOut : INumberBase<TOut> =>
|
||||
as_type<double, TOut>(value);
|
||||
|
||||
public static TOut as_type<TIn, TOut>(this TIn value)
|
||||
where TIn : INumber<TIn>
|
||||
where TOut : INumber<TOut> =>
|
||||
where TIn : INumberBase<TIn>
|
||||
where TOut : INumberBase<TOut> =>
|
||||
NumericCastConverter<TIn, TOut>.convert(value);
|
||||
|
||||
public static T abs<T>(this T value) where T : INumber<T> =>
|
||||
public static T abs<T>(this T value) where T : INumberBase<T> =>
|
||||
T.Abs(value);
|
||||
|
||||
public static bool is_nan<T>(this T value) where T : IFloatingPoint<T> =>
|
||||
public static bool is_nan<T>(this T value) where T : INumberBase<T> =>
|
||||
T.IsNaN(value);
|
||||
|
||||
public static T if_nan<T>(this T d, T if_nan) where T : IFloatingPoint<T> =>
|
||||
public static T if_nan<T>(this T d, T if_nan) where T : INumberBase<T> =>
|
||||
d.is_nan() ? if_nan : d;
|
||||
|
||||
public static bool is_int<T>(this T value) where T : IFloatingPoint<T> =>
|
||||
@@ -71,15 +72,15 @@ namespace bts.utils {
|
||||
MathUtils.marginal_equals(x, y, margin);
|
||||
|
||||
public static bool is_within_range<T>(
|
||||
this T value, T min, T max) where T : IComparisonOperators<T, T> =>
|
||||
this T value, T min, T max) where T : IComparisonOperators<T, T, bool> =>
|
||||
MathUtils.is_within_range(value, min, max);
|
||||
|
||||
public static T within_range<T>(
|
||||
this T value, T min, T max) where T : IComparisonOperators<T, T> =>
|
||||
this T value, T min, T max) where T : IComparisonOperators<T, T, bool> =>
|
||||
MathUtils.within_range(value, min, max);
|
||||
|
||||
public static T within_range<T>(
|
||||
this T value, T? min = null, T? max = null) where T : struct, IComparisonOperators<T, T> =>
|
||||
this T value, T? min = null, T? max = null) where T : struct, IComparisonOperators<T, T, bool> =>
|
||||
MathUtils.within_range(value, min, max);
|
||||
|
||||
public static bool is_real<T>(this T value) where T : IFloatingPoint<T> =>
|
||||
@@ -118,7 +119,7 @@ namespace bts.utils {
|
||||
/// If only <paramref name="b"/> is real, returns <paramref name="b"/>.
|
||||
/// If neither term is real, returns NaN.
|
||||
/// </remarks>
|
||||
public static T add_real<T>(this T a, T b) where T : IFloatingPoint<T> =>
|
||||
public static T add_real<T>(this T a, T b) where T : IFloatingPointIeee754<T> =>
|
||||
new[] { a, b }.sum_real(true);
|
||||
|
||||
/// <summary>
|
||||
@@ -129,7 +130,7 @@ namespace bts.utils {
|
||||
/// If only <paramref name="b"/> is real, returns -<paramref name="b"/>
|
||||
/// If neither term is real, returns NaN.
|
||||
/// </remarks>
|
||||
public static T subtract_real<T>(this T a, T b) where T : IFloatingPoint<T> =>
|
||||
public static T subtract_real<T>(this T a, T b) where T : IFloatingPointIeee754<T> =>
|
||||
new[] { a, -b }.sum_real(true);
|
||||
|
||||
/// <summary>
|
||||
@@ -187,7 +188,7 @@ namespace bts.utils {
|
||||
/// contains no real numbers
|
||||
/// </param>
|
||||
public static T sum_real<T>(this IEnumerable<T> values, bool nan_if_none = false)
|
||||
where T : IFloatingPoint<T> {
|
||||
where T : IFloatingPointIeee754<T> {
|
||||
return nan_if_none ? nan_if_none_impl() : normal_impl();
|
||||
|
||||
T normal_impl() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<GeneratePackageOnBuild Condition="$(Configuration) == 'Release' or $(Configuration) == 'Release'">true</GeneratePackageOnBuild>
|
||||
<PackageOutputPath>$(OutputPath)\net6.0</PackageOutputPath>
|
||||
<PackageOutputPath>$(OutputPath)\net7.0</PackageOutputPath>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
|
||||
@@ -40,19 +40,19 @@
|
||||
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences" Condition=" '$(SparkRoot)' != '' ">
|
||||
<ItemGroup>
|
||||
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
|
||||
<BuildOutputInPackage Include="$(OutputPath)..\net6.0\libspark.dll" />
|
||||
<BuildOutputInPackage Include="$(OutputPath)..\net7.0\libspark.dll" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!-- deployed sdk build net6.0 -->
|
||||
<ItemGroup Condition=" '$(SparkRoot)' == '' and '$(TargetFramework)' == 'net6.0'">
|
||||
<!-- deployed sdk build net7.0 -->
|
||||
<ItemGroup Condition=" '$(SparkRoot)' == '' and '$(TargetFramework)' == 'net7.0'">
|
||||
<Reference Include="spark_api">
|
||||
<HintPath>..\..\lib\net6.0\spark_api.dll</HintPath>
|
||||
<HintPath>..\..\lib\net7.0\spark_api.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="copy_lib_deployed" AfterTargets="PostBuildEvent" Condition=" '$(SparkRoot)' == '' ">
|
||||
<Copy SourceFiles="..\..\lib\net6.0\libspark.dll" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="..\..\lib\net7.0\libspark.dll" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -3,26 +3,23 @@ using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace bts.websocket_core {
|
||||
// Represents a unique client
|
||||
// Represents a unique client, a little redundant
|
||||
public class WebsocketClientID {
|
||||
readonly string ip;
|
||||
readonly string port;
|
||||
// not sure if we need this, lets go with this for now
|
||||
readonly string uuid;
|
||||
public WebsocketClientID(string ip, string port, string uuid) {
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
this.uuid = uuid;
|
||||
|
||||
public Guid Id { get; }
|
||||
|
||||
public WebsocketClientID() {
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
public override int GetHashCode() {
|
||||
return uuid.GetHashCode();
|
||||
return this.Id.GetHashCode();
|
||||
}
|
||||
public override string ToString() {
|
||||
return uuid;
|
||||
return this.Id.ToString();
|
||||
}
|
||||
public override bool Equals(object? obj) {
|
||||
var other = obj == null ? null : obj as WebsocketClientID;
|
||||
return other != null && other.uuid == uuid;
|
||||
return other != null && other.Id == Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,259 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
// #nullable enable
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
namespace bts.websocket_core {
|
||||
|
||||
/// <summary>
|
||||
/// Handles all websocket related processing, interfaces with datasync, gets initial paramters from WebsocketServerParams
|
||||
/// </summary>
|
||||
public class WebsocketConnectionGateway : IWebsocketConnectionGateway {
|
||||
private IWebsocketDataSync? _sync = null;
|
||||
private WebsocketServerParams? _server_params = null;
|
||||
private WebApplication? app = null;
|
||||
|
||||
// IDictionary<int, WebSocket> clients = new Dictionary<int, WebSocket>();
|
||||
private ConcurrentDictionary<WebsocketClientID, WebSocket>? clients = null;
|
||||
|
||||
/// <summary>
|
||||
/// To Avoid null reference warning, only called when object is not null, should only be called inside getNonNull, is separate function so can be changed later on
|
||||
/// </summary>
|
||||
/// <returns> non nullable version of object </returns>
|
||||
private T getObject<T>(T? obj) {
|
||||
return obj!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// as explained in c# documentation this can be used to instruct the compiler that obj is not null, for now this is redundant
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsNotNull([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] object? obj) => obj != null;
|
||||
|
||||
/// <summary>
|
||||
/// will assert that object is not null and return the non nullable version
|
||||
/// </summary>
|
||||
/// <returns> non nullable version of object </returns>
|
||||
private object GetNonNull(object? obj) {
|
||||
if (IsNotNull(obj)) {
|
||||
return getObject<object>(obj);
|
||||
} else {
|
||||
bts.Log.assert(false);
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Should be called in the start, stores pointer for sync and creates dictionary for clients, does not start server
|
||||
/// </summary>
|
||||
/// <param name="sync"></param>
|
||||
public void init(IWebsocketDataSync sync) {
|
||||
// first call
|
||||
bts.Log.assert(_sync == null);
|
||||
bts.Log.assert(clients == null);
|
||||
_sync = sync;
|
||||
clients = new ConcurrentDictionary<WebsocketClientID, WebSocket>();
|
||||
}
|
||||
/// <summary>
|
||||
/// stores the current clients, one client corresponds to one open socket which corresponds to one request, will update getter to c# convention
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<WebsocketClientID, WebSocket> GetClients() => (ConcurrentDictionary<WebsocketClientID, WebSocket>)GetNonNull(clients);
|
||||
|
||||
/// <summary>
|
||||
/// will update getter to c# convention
|
||||
/// </summary>
|
||||
private IWebsocketDataSync GetSync() => (IWebsocketDataSync)GetNonNull(_sync);
|
||||
|
||||
/// <summary>
|
||||
/// creates ID and adds it to clients, in case of collision just updates the previous value
|
||||
/// </summary>
|
||||
WebsocketClientID createID(WebSocket ws) {
|
||||
var id = new WebsocketClientID();
|
||||
var check = GetClients().TryAdd(id, ws);
|
||||
if (!check) {
|
||||
var log_txt = $"WebsocketDataSync.createID client_id: {id.ToString()} Already exits";
|
||||
bts.Log.Gen.warn(log_txt);
|
||||
GetClients()[id] = ws;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// wrapper to gets socket from clients dictionary against given id, returns null if not found
|
||||
/// </summary>
|
||||
private WebSocket? GetSocket(WebsocketClientID id) {
|
||||
return GetClients().ContainsKey(id) ? GetClients()[id] : null;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// wrapper to remove socket from clients dictionary against given id, cleans up removed socket as well
|
||||
/// </summary>
|
||||
private void RemoveSocket(WebsocketClientID id) {
|
||||
if (GetClients().ContainsKey(id)) {
|
||||
if (GetClients().TryRemove(id, out WebSocket? ws)) {
|
||||
ws.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages client disconnection, does the following three things:
|
||||
/// <para>1. tries sending close request to client </para>
|
||||
/// <para>2. removes client from dictionary of active clients</para>
|
||||
/// <para>3. asks sync to handle disconnection</para>
|
||||
/// </summary>
|
||||
public async Task HandleDisconnection(WebsocketClientID id, string optional_message = "") {
|
||||
await SendDisconnectRequestAsync(id, optional_message);
|
||||
RemoveSocket(id);
|
||||
GetSync().handle_disconnection(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// should be called after init
|
||||
/// creates server using the server params provided and starts listening for socket based requests on that URI
|
||||
/// keeps running async
|
||||
/// </summary>
|
||||
/// <param name="server_params"></param>
|
||||
public void start_listening(WebsocketServerParams server_params) {
|
||||
// start listening, assert that init has been called already
|
||||
bts.Log.assert(_sync != null);
|
||||
bts.Log.assert(_server_params == null);
|
||||
bts.Log.assert(clients != null);
|
||||
bts.Log.assert(app == null);
|
||||
|
||||
|
||||
_server_params = server_params;
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseUrls(_server_params.Uri);
|
||||
app = builder.Build();
|
||||
app.UseWebSockets(_server_params.get_ws_options());
|
||||
|
||||
app.Map(_server_params.Default_end_point, async context => {
|
||||
if (context.WebSockets.IsWebSocketRequest) {
|
||||
using (var ws = await context.WebSockets.AcceptWebSocketAsync()) {
|
||||
await ProcessWebSocketConnectionAsync(ws);
|
||||
}
|
||||
} else {
|
||||
context.Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
|
||||
}
|
||||
});
|
||||
app.RunAsync();
|
||||
|
||||
}
|
||||
// Messages are sent to the client through this
|
||||
/// <summary>
|
||||
/// sends the list of messafes to respectives sockets, does not wait for messages to be sent
|
||||
/// </summary>
|
||||
/// <param name="messages">list of messages each containing a string based message and client ID</param>
|
||||
public void send_to_clients(
|
||||
IEnumerable<WebsocketOutgoingMessage> messages) {
|
||||
var tasks = new List<Task>();
|
||||
foreach (var om in messages) {
|
||||
tasks.Add(SendWebSocketRequestAsync(om.receiver_client_id, om.message_body));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// After adding new socket to dictionary send the new client id to sync to store and process
|
||||
/// while socket stays open this keeps waiting for requests
|
||||
/// </summary>
|
||||
/// <param name="ws"></param>
|
||||
/// <returns>Task that keeps waiting for requests until the socket is closed</returns>
|
||||
public async Task ProcessWebSocketConnectionAsync(WebSocket ws) {
|
||||
var request = createID(ws);
|
||||
GetSync().handle_connection(request, null);//for now thinking this is async/ no IO
|
||||
while (ws != null && ws.State == WebSocketState.Open) {
|
||||
await ProcessWebSocketRequestAsync(request);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 1. makes sure state is consistent
|
||||
/// 2. listens for request
|
||||
/// 3. if request is the close, calls handleDisconnection
|
||||
/// 4. otherwise sends request to sync to process
|
||||
/// catches exception of client closes without handshake
|
||||
/// </summary>
|
||||
/// <param name="id">id of client from whom the message is to be recieved</param>
|
||||
/// <returns>Task that runs until socket is open</returns>
|
||||
public async Task ProcessWebSocketRequestAsync(WebsocketClientID id) {
|
||||
var socket = GetSocket(id);
|
||||
if (socket == null || socket.State != WebSocketState.Open) {
|
||||
await HandleDisconnection(id, "WebsocketGateway.ProcessWebSocketRequestAsync could not receive Request as client closed");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[256]);
|
||||
WebSocketReceiveResult result;
|
||||
string request = "";
|
||||
do {
|
||||
result = await socket.ReceiveAsync(buffer, CancellationToken.None);
|
||||
if (result.MessageType == WebSocketMessageType.Close) {
|
||||
var log_txt = $"WebsocketGateway.ProcessWebSocketRequestAsync connection closed client_id: {id}";
|
||||
await HandleDisconnection(id, log_txt);
|
||||
return;
|
||||
}
|
||||
if (buffer.Array != null)// added because of warning
|
||||
{
|
||||
request += Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
|
||||
} else {
|
||||
bts.Log.assert(false);//this should never happen
|
||||
}
|
||||
if (result.EndOfMessage) {
|
||||
GetSync().handle_client_message(new WebsocketIncomingMessage(id, request));
|
||||
request = "";
|
||||
}
|
||||
} while (socket.State == WebSocketState.Open);
|
||||
|
||||
} catch (WebSocketException ex) {
|
||||
var ex_str = ex.ToString();
|
||||
var log_txt = $"WebsocketGateway.ProcessWebSocketRequestAsync connection closed client_id: {id} Error: {ex_str}";
|
||||
await HandleDisconnection(id, log_txt);
|
||||
} catch (OperationCanceledException) {//not sure about this read that this should just be ignored
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// tries to send request to client to disconnect
|
||||
/// </summary>
|
||||
/// <param name="id">id of client to whom the Disconnect message should be sent</param>
|
||||
/// <param name="message"></param>
|
||||
/// <returns>task that completes if client disconnects or if there is an error</returns>
|
||||
public async Task SendDisconnectRequestAsync(WebsocketClientID id, string message) {
|
||||
var log_txt = $"WebsocketGateway.SendDisconnectRequestAsync tried closing client_id: {id} with message: {message}";
|
||||
var socket = GetSocket(id);
|
||||
if (socket != null && socket.State == WebSocketState.Open) {
|
||||
try {
|
||||
await socket.CloseAsync((WebSocketCloseStatus)1000, "", CancellationToken.None);
|
||||
log_txt += " done succesfully";
|
||||
} catch {
|
||||
log_txt += " failed as client didnt respond";
|
||||
}
|
||||
} else if (socket == null) {
|
||||
log_txt += " failed as could not find socket in dictionary";
|
||||
} else if (socket.State != WebSocketState.Open) {
|
||||
log_txt += " Socket already closed";
|
||||
}
|
||||
bts.Log.Gen.info(log_txt);
|
||||
Console.WriteLine(log_txt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sends message iver socket correspoding to id
|
||||
/// </summary>
|
||||
/// <param name="id">id of client to whom the message should be sent</param>
|
||||
/// <param name="message">message to be sent</param>
|
||||
/// <returns></returns>
|
||||
public async Task SendWebSocketRequestAsync(WebsocketClientID id, string message) {
|
||||
var socket = GetSocket(id);
|
||||
if (socket == null || socket.State != WebSocketState.Open) {
|
||||
await HandleDisconnection(id, "WebsocketGateway.SendWebSocketRequestAsync could not send Request as client closed");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await socket.SendAsync(System.Text.Encoding.ASCII.GetBytes(message), System.Net.WebSockets.WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
} catch (WebSocketException ex) {
|
||||
var ex_str = ex.ToString();
|
||||
var log_txt = $"WebsocketGateway.SendWebSocketRequestAsync connection closed client_id: {id} Error: {ex_str}";
|
||||
await HandleDisconnection(id, log_txt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace bts.websocket_core {
|
||||
// This does all the bookkeeping for conenctions and data, and passes
|
||||
// it over to the business layer
|
||||
/// <summary>
|
||||
/// Immutable to store the state of the client
|
||||
/// </summary>
|
||||
public class ClientState {
|
||||
public UInt64 MessageCount { get; }
|
||||
public ClientState(UInt64 messageCount) {
|
||||
MessageCount = messageCount;
|
||||
}
|
||||
public ClientState IncrementMessages(ClientState st) => new ClientState(st.MessageCount + 1);
|
||||
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// This does all the bookkeeping for conenctions and data, and passes
|
||||
/// it over to the business layer CURRENT VERSION IS JUST TO SHOW HOW TO USE GATEWAY
|
||||
/// </summary>
|
||||
public class WebsocketDataSync : IWebsocketDataSync {
|
||||
WebsocketConnectionGateway? Gateway;
|
||||
/// <summary>
|
||||
/// stores the current connected clients, can be converted the concurrent dictionary
|
||||
/// </summary>
|
||||
private Dictionary<WebsocketClientID, ClientState>? clients;
|
||||
/// <summary>
|
||||
/// to access the dictionary
|
||||
/// </summary>
|
||||
private static object _clientsAccessLock = new object();
|
||||
/// <summary>
|
||||
/// Thread that will broadcast messages to all connected messages
|
||||
/// </summary>
|
||||
private Thread? broadcast;
|
||||
/// <summary>
|
||||
/// number of messages after which broadcast thread will disconnect that particular socket
|
||||
/// </summary>
|
||||
public uint BROADCAST_LIMIT { get; } = 20;
|
||||
/// <summary>
|
||||
/// will update getter to c# convention
|
||||
/// </summary>
|
||||
/// <returns>non nullable version</returns>
|
||||
private WebsocketConnectionGateway GetGateway() => (WebsocketConnectionGateway)GetNonNull(Gateway);
|
||||
/// <summary>
|
||||
/// will update getter to c# convention
|
||||
/// </summary>
|
||||
/// <returns>non nullable version</returns>
|
||||
private Dictionary<WebsocketClientID, ClientState> GetClients() => (Dictionary<WebsocketClientID, ClientState>)GetNonNull(clients);
|
||||
/// <summary>
|
||||
/// adds client to dictionary is thread safe
|
||||
/// </summary>
|
||||
/// <param name="client_id"></param>
|
||||
void AddClient(WebsocketClientID client_id) {
|
||||
lock (_clientsAccessLock) {
|
||||
GetClients().Add(client_id, new ClientState(0));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// removes client from dictionary is thread safe
|
||||
/// </summary>
|
||||
/// <param name="client_id"></param>
|
||||
void RemoveClient(WebsocketClientID client_id) {
|
||||
lock (_clientsAccessLock) {
|
||||
if (GetClients().ContainsKey(client_id))
|
||||
GetClients().Remove(client_id);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// will update getter to c# convention
|
||||
/// </summary>
|
||||
/// <returns>non nullable version</returns>
|
||||
private Thread GetBroadcast() => (Thread)GetNonNull(broadcast);
|
||||
/// <summary>
|
||||
/// To Avoid null reference warning, only called when object is not null, should only be called inside getNonNull, is separate function so can be changed later on
|
||||
/// </summary>
|
||||
/// <returns> non nullable version of object </returns>
|
||||
private T getObject<T>(T? obj) {
|
||||
return obj!;
|
||||
}
|
||||
/// <summary>
|
||||
/// as explained in c# documentation this can be used to instruct the compiler that obj is not null, for now this is redundant
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsNotNull([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] object? obj) => obj != null;
|
||||
/// <summary>
|
||||
/// will assert that object is not null and return the non nullable version
|
||||
/// </summary>
|
||||
/// <returns> non nullable version of object </returns>
|
||||
private object GetNonNull(object? obj) {
|
||||
if (IsNotNull(obj)) {
|
||||
return getObject<object>(obj);
|
||||
} else {
|
||||
bts.Log.assert(false);
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Should be called in the start, stores pointer for gtwy and creates dictionary for clients, starts broadcast thread
|
||||
/// </summary>
|
||||
/// <param name="gtwy"></param>
|
||||
public void init(WebsocketConnectionGateway gtwy) {
|
||||
Gateway = gtwy;
|
||||
clients = new Dictionary<WebsocketClientID, ClientState>();
|
||||
broadcast = new Thread(() => BroadcastToClients());
|
||||
broadcast.Start();
|
||||
}
|
||||
/// <summary>
|
||||
/// Runs on different thread, wakes after given duration sends a message to all connected sockets, will close sockets that have been sent messages equal to limit
|
||||
/// </summary>
|
||||
/// <param name="duration">wakes up after given duration in ms</param>
|
||||
private void BroadcastToClients(int duration = 1000) {
|
||||
while (true) {
|
||||
List<WebsocketOutgoingMessage> messages = new List<WebsocketOutgoingMessage>();
|
||||
List<WebsocketClientID> toRemove = new List<WebsocketClientID>();
|
||||
lock (_clientsAccessLock) {
|
||||
foreach (var client in GetClients()) {
|
||||
messages.Add(new WebsocketOutgoingMessage(client.Key, $"New Message number: {client.Value.MessageCount}, clients connected: {GetClients().Count}"));
|
||||
GetClients()[client.Key] = client.Value.IncrementMessages(client.Value);
|
||||
if (GetClients()[client.Key].MessageCount == BROADCAST_LIMIT) {
|
||||
toRemove.Add(client.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
//example on how to send multiple messages to multiple sockets
|
||||
GetGateway().send_to_clients(messages);
|
||||
foreach (var key in toRemove) {
|
||||
///example on how to disconnect, this should ideally happen in its own thread
|
||||
GetGateway().HandleDisconnection(key, "closing client").Wait();
|
||||
}
|
||||
Thread.Sleep(duration);
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a new client is connected
|
||||
public void handle_connection(WebsocketClientID client_id,
|
||||
WebsocketIncomingMessage? optional_message) {
|
||||
AddClient(client_id);
|
||||
var log_txt = $"WebsocketDataSync.handle_connection client_id: {client_id} " +
|
||||
$"message: {(optional_message == null ? "null" : optional_message.ToString())}";
|
||||
bts.Log.Gen.info(log_txt);
|
||||
System.Console.WriteLine(log_txt);
|
||||
if (IsNotNull(optional_message)) {
|
||||
handle_client_message(optional_message);
|
||||
}
|
||||
}
|
||||
// Called when a client is disconnected
|
||||
public void handle_disconnection(WebsocketClientID client_id) {
|
||||
RemoveClient(client_id);
|
||||
var log_txt = $"WebsocketDataSync.handle_disconnection client_id: {client_id}";
|
||||
bts.Log.Gen.info(log_txt);
|
||||
System.Console.WriteLine(log_txt);
|
||||
}
|
||||
// Called when a client sends data
|
||||
// Called when a client sends data sends the same message back
|
||||
public void handle_client_message(WebsocketIncomingMessage message) {
|
||||
var log_txt = $"WebsocketDataSync.handle_client_message message: {message}";
|
||||
bts.Log.Gen.info(log_txt);
|
||||
System.Console.WriteLine(log_txt);
|
||||
//example to show how to send messages to clients
|
||||
GetGateway().send_to_clients(new List<WebsocketOutgoingMessage>() { new WebsocketOutgoingMessage(message.sender_client_id, message.message_body + " RESPONSE") });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,32 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace bts.websocket_core {
|
||||
// Placeholder for whatever is needed for the server to start
|
||||
// listening to clients (port, security etc)
|
||||
public class WebsocketServerParams {
|
||||
public string Port { get; }
|
||||
public string Host { get; }
|
||||
public string Scheme { get; }
|
||||
public string Default_end_point { get; }
|
||||
public double Keep_alive_interval_seconds { get; }
|
||||
public string Uri { get; }
|
||||
|
||||
public WebsocketServerParams() {
|
||||
//will later do this from conf file
|
||||
this.Default_end_point = "/";
|
||||
this.Host = "localhost";
|
||||
this.Scheme = "http";
|
||||
this.Port = "6666";
|
||||
this.Keep_alive_interval_seconds = 0.1;
|
||||
this.Uri = this.Scheme + "://" + this.Host + ":" + this.Port; ;
|
||||
}
|
||||
public WebSocketOptions get_ws_options() {
|
||||
return new WebSocketOptions() {
|
||||
KeepAliveInterval = TimeSpan.FromSeconds(this.Keep_alive_interval_seconds)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user