* Move Anonymous/FinalizableDisposable to own file * Rename DisposableCombo -> ImmutableDisposable and move it to its own file * Clean up IDisposable utils impl, but keep changes to calling code minimial
256 lines
10 KiB
C#
256 lines
10 KiB
C#
namespace mbts.spark {
|
|
using bts;
|
|
using bts.api;
|
|
using bts.api.analytics;
|
|
using bts.api.core;
|
|
using bts.collections;
|
|
using bts.shell;
|
|
using bts.shell.pnl;
|
|
using bts.spark;
|
|
|
|
using mbts.analytics;
|
|
using mbts.persist;
|
|
using mbts.ui;
|
|
using mbts.ui.shell;
|
|
using mbts.ui.utility;
|
|
|
|
using Spark.Core.Data;
|
|
using Spark.Core.Disposables;
|
|
using Spark.Core.Reactive;
|
|
using Spark.Trading;
|
|
using Spark.ViewModel;
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Composition;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Media;
|
|
|
|
[Export(typeof(IShellModule))]
|
|
class AnalyticsShellModule : IShellModule, IShellModuleViewFactory {
|
|
readonly IBus _bus;
|
|
readonly IWindowManager _wm;
|
|
readonly List<IShellWindowFactory> _window_factories;
|
|
|
|
IDisposable _dispose;
|
|
|
|
[ImportingConstructor]
|
|
public AnalyticsShellModule(
|
|
IBus bus,
|
|
WindowManager wm,
|
|
UserSettingsHub settings_hub,
|
|
OrdersModel orders_model,
|
|
IPositionModel position_model,
|
|
ISecurityMetadataStore sec_meta_store,
|
|
Lazy<UserTuple> user) {
|
|
_wm = wm;
|
|
_bus = bus;
|
|
var reports_demuxer = new TableUpdateDemuxer<ReportImmutable?, int>(default, false);
|
|
var nullable_reports_src = bus.subscribe(SparkTags.ReportBlackboardTags.ListTag)
|
|
.Table(r => r.Id)
|
|
.Select(
|
|
r => new ReportImmutable?(r),
|
|
r => r?.Id ?? throw new ArgumentNullException(nameof(r)));
|
|
_window_factories = new() {
|
|
new VolSurfaceWindowFactory(this, bus),
|
|
new AnalyticsExpirationParametersWindowFactory(this, bus),
|
|
new SlideReportWindowFactory(this, bus, reports_demuxer.subscribe),
|
|
new ComboReportWindowFactory(this, bus, reports_demuxer.subscribe),
|
|
new PNLWindowFactory(
|
|
this, bus, settings_hub, orders_model, position_model, sec_meta_store),
|
|
new DayFillsWindowFactory(
|
|
this, bus, settings_hub, position_model, sec_meta_store, user.Value)
|
|
};
|
|
var vol_surfaces_src = _bus.subscribe(SparkTags.VolSurfaceListTag).OnDispatcher();
|
|
var reports_src = _bus.subscribe(SparkTags.ReportBlackboardTags.ListTag).OnDispatcher();
|
|
_dispose = Disposable.Create(
|
|
vol_surfaces_src.Subscribe(_on_vol_surfaces),
|
|
reports_src.Subscribe(_on_reports),
|
|
nullable_reports_src.Subscribe(reports_demuxer),
|
|
reports_demuxer,
|
|
vm.launch_exp_params_view_cmd.subscribe_executed(_launch_exp_params_view),
|
|
vm.launch_pnl_cmd.subscribe_executed(_launch_pnl),
|
|
vm.launch_day_fills_cmd.subscribe_executed(_launch_day_fills),
|
|
// vol surface
|
|
vm.vol_surface_launcher_vm.launch_cmd.subscribe_executed(_launch_vol_surface),
|
|
vm.vol_surface_launcher_vm.create_cmd.subscribe_executed(_create_vol_surface),
|
|
vm.vol_surface_launcher_vm.delete_cmd.subscribe_executed(_delete_vol_surface),
|
|
// slide
|
|
vm.slide_report_launcher_vm.launch_cmd.subscribe_executed(_launch_slide_report),
|
|
vm.slide_report_launcher_vm.create_cmd.subscribe_executed(_create_slide_report),
|
|
vm.slide_report_launcher_vm.delete_cmd.subscribe_executed(_delete_slide_report),
|
|
// combo
|
|
vm.combo_report_launcher_vm.launch_cmd.subscribe_executed(_launch_combo_report),
|
|
vm.combo_report_launcher_vm.create_cmd.subscribe_executed(_create_combo_report),
|
|
vm.combo_report_launcher_vm.delete_cmd.subscribe_executed(_delete_combo_report));
|
|
}
|
|
|
|
public enum WindowType { Main, Heatmap }
|
|
|
|
public string name => "Analytics";
|
|
public ImageSource icon => Icons.analytics;
|
|
public int priority => 20;
|
|
public IEnumerable<SparkCommand> actions => Enumerable.Empty<SparkCommand>();
|
|
|
|
public AnalyticsShellModuleVM vm { get; } = new();
|
|
|
|
public Task<IShellWindow> create(Stream load_stream) =>
|
|
ShellWindowFactory.create(load_stream, _window_factories);
|
|
|
|
public UIElement create_view() =>
|
|
new AnalyticsShellModuleView { DataContext = vm };
|
|
|
|
public void Dispose() =>
|
|
Disposable.dispose(ref _dispose);
|
|
|
|
void _create_vol_surface(string _) {
|
|
var settings = VolSurfaceSettings.Default(string.Empty);
|
|
var vm = new VolSurfaceSettingsEditorVM(
|
|
_bus, settings, v => _bus.post(SparkTags.VolSurfaceCreateTag, v)) {
|
|
can_edit_name = true
|
|
};
|
|
var window = new VolSurfaceSettingsEditor {
|
|
DataContext = vm,
|
|
WindowStartupLocation = WindowStartupLocation.CenterScreen
|
|
};
|
|
window.Show();
|
|
select_section_delayed("configuration");
|
|
|
|
async void select_section_delayed(string section) {
|
|
await Task.Delay(100);
|
|
vm.selected_section = section;
|
|
}
|
|
}
|
|
|
|
void _launch_exp_params_view() {
|
|
var settings = AnalyticsExpirationParametersSettings.Default;
|
|
_bus.post(SparkTags.show_analytics_exp_params, settings);
|
|
}
|
|
|
|
void _launch_pnl() =>
|
|
_bus.post(SparkTags.ShowPNLTag, null);
|
|
|
|
void _launch_day_fills() =>
|
|
_bus.post(SparkTags.show_day_fills, DayFillsSettings.Default());
|
|
|
|
void _launch_vol_surface(int id) {
|
|
var windows = _wm.list_windows();
|
|
if (windows.OfType<VolSurfaceWindow>().TryGetFirst(w => w.vs_id == id, out var win)) {
|
|
_wm.MakeVisible(win);
|
|
return;
|
|
}
|
|
_bus.post(SparkTags.ShowVolSurface, new() { id = id });
|
|
}
|
|
|
|
void _delete_vol_surface(int id) {
|
|
if (BTSMessageBox.Confirm($"Are you sure you want to delete {name}?", "Vol Surface")) {
|
|
_bus.post(SparkTags.VolSurfaceDeleteTag, id);
|
|
}
|
|
}
|
|
|
|
void _on_vol_surfaces(IDictionary<int, string> id_names) {
|
|
vm.vol_surface_launcher_vm.items.sync_with(id_names.Select(
|
|
kv => new KeyValueItemVM<int, string>(kv.Key, kv.Value)));
|
|
// reset titles for all open vol surface windows
|
|
foreach (var window in _wm.list_windows().OfType<VolSurfaceWindow>()) {
|
|
window.Title = id_names.GetValueOrDefault(window.vs_id) ?? "(...)";
|
|
}
|
|
}
|
|
|
|
void _create_slide_report(string _) {
|
|
var vm = new SlideReportSettingsVM(
|
|
SlideReportSettings.Default(),
|
|
new(new Report { ReportType = Report.Types.ReportType.Slide }),
|
|
_bus.subscribe(SparkTags.VolSurfaceListTag),
|
|
_bus.subscribe(SparkTags.AccountsTag),
|
|
_bus.subscribe(SparkTags.SecuritiesTag));
|
|
vm.selected_section = vm.configuration_vm;
|
|
var win = new SlideReportCreationDialog() {
|
|
DataContext = vm
|
|
};
|
|
WindowUtilities.connect_vm(win, vm);
|
|
vm.ok_cmd.executed += commit;
|
|
win.move_to_mouse(-100, -200);
|
|
win.Show();
|
|
|
|
async void commit((GreekDisplay[] greeks, PriceDisplayFactors price_disp, ReportImmutable rep) tup) {
|
|
WindowUtilities.force_close(win);
|
|
_bus.post(SparkTags.ReportBlackboardTags.Create, tup.rep);
|
|
var persisted_rep = await _bus.subscribe(SparkTags.ReportBlackboardTags.ListTag)
|
|
.Select(reps => reps.Values.TryGetFirst(
|
|
r => r.ReportName == tup.rep.ReportName, out var prep) ? prep : (ReportImmutable?)null)
|
|
.Where(r => r.HasValue)
|
|
.FirstOrDefaultAsync();
|
|
if (!persisted_rep.HasValue) {
|
|
return;
|
|
}
|
|
var settings = SlideReportSettings.Default(persisted_rep.Value.Id);
|
|
settings.greeks = tup.greeks;
|
|
settings.price = tup.price_disp;
|
|
vm.grid_vm.assign_to(settings);
|
|
_bus.post(SparkTags.ShowSlideReportTag, settings);
|
|
}
|
|
}
|
|
|
|
void _delete_slide_report(int id) =>
|
|
_bus.post(SparkTags.ReportBlackboardTags.Delete, id);
|
|
|
|
void _launch_slide_report(int id) =>
|
|
_bus.post(SparkTags.ShowSlideReportTag, SlideReportSettings.Default(id));
|
|
|
|
void _create_combo_report(string _) {
|
|
var vm = new ComboReportSettingsVM(
|
|
_bus.subscribe(SparkTags.AccountsTag), _bus.subscribe(SparkTags.SecuritiesTag));
|
|
vm.load_from(new(new Report { ReportType = Report.Types.ReportType.Slide }));
|
|
var win = new ComboReportCreationDialog() {
|
|
DataContext = vm
|
|
};
|
|
WindowUtilities.connect_vm(win, vm);
|
|
vm.commit_cmd.executed += commit;
|
|
win.move_to_mouse(-100, -100);
|
|
win.Show();
|
|
|
|
async void commit(ReportImmutable rep) {
|
|
WindowUtilities.force_close(win);
|
|
_bus.post(SparkTags.ReportBlackboardTags.Create, rep);
|
|
var persisted_rep = await _bus.subscribe(SparkTags.ReportBlackboardTags.ListTag)
|
|
.Select(reps => reps.Values.TryGetFirst(
|
|
r => r.ReportName == rep.ReportName, out var prep) ? prep : (ReportImmutable?)null)
|
|
.Where(r => r.HasValue)
|
|
.FirstOrDefaultAsync();
|
|
if (!persisted_rep.HasValue) {
|
|
return;
|
|
}
|
|
var settings = ComboReportSettings.Default(persisted_rep.Value.Id);
|
|
_bus.post(SparkTags.ShowComboReportTag, settings);
|
|
}
|
|
}
|
|
|
|
|
|
void _delete_combo_report(int id) =>
|
|
_bus.post(SparkTags.ReportBlackboardTags.Delete, id);
|
|
|
|
void _launch_combo_report(int id) =>
|
|
_bus.post(SparkTags.ShowComboReportTag, ComboReportSettings.Default(id));
|
|
|
|
void _on_reports(IReadOnlyDictionary<int, ReportImmutable> reports) {
|
|
vm.slide_report_launcher_vm.items.Clear();
|
|
vm.combo_report_launcher_vm.items.Clear();
|
|
foreach (var rep in reports.Values) {
|
|
switch (rep.ReportType) {
|
|
case Report.Types.ReportType.Slide:
|
|
vm.slide_report_launcher_vm.items.Add(new(rep.Id, rep.ReportName));
|
|
break;
|
|
case Report.Types.ReportType.Combo:
|
|
vm.combo_report_launcher_vm.items.Add(new(rep.Id, rep.ReportName));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|