Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28b03e1cd2 | ||
|
|
a7acb89c15 | ||
|
|
35289710ba | ||
|
|
4677acfb39 | ||
|
|
ac05d0d69e | ||
|
|
25a9b6b433 | ||
|
|
b78713ad0d | ||
|
|
1f3d0e879c | ||
|
|
baad4dc68e | ||
|
|
ef7c0c7a30 | ||
|
|
3b3c4a2a35 | ||
|
|
ef9fc66a31 | ||
|
|
212d9c853b | ||
|
|
7815b18b6b | ||
|
|
05126074d4 | ||
|
|
3611d2c1f3 | ||
|
|
b5ad4b8104 | ||
|
|
43d7c0a2b5 | ||
|
|
12e361cec7 | ||
|
|
82c126362d |
No files matched your search
@@ -3,7 +3,6 @@ namespace mbts.spark {
|
||||
using bts.api;
|
||||
using bts.api.analytics;
|
||||
using bts.collections;
|
||||
using bts.reactive;
|
||||
using bts.utils;
|
||||
|
||||
using mbts;
|
||||
@@ -18,6 +17,7 @@ namespace mbts.spark {
|
||||
using Spark.Core;
|
||||
using Spark.Core.Collections;
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.api;
|
||||
|
||||
using mbts;
|
||||
using mbts.analytics;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
|
||||
// For each vs, it sends the vol surface calc request (_do_periodic_auto_fit)
|
||||
// and then commits the response
|
||||
public class SingleVolSurfaceAutoFitSource : IDisposable {
|
||||
private readonly ThreadedBlackboard _bb;
|
||||
private readonly int _vs_id;
|
||||
private readonly ITag<VolSurfaceInput> _vs_input_tag;
|
||||
private readonly ITag<VolSurfaceCalculationResponse> _vs_calc_response_tag;
|
||||
private DateTime _last_req_time = DateTime.MinValue;
|
||||
private TimeSpan _req_period = TimeSpan.MaxValue;
|
||||
private int _req_id = 0;
|
||||
public SingleVolSurfaceAutoFitSource(int vs_id, ThreadedBlackboard bb) {
|
||||
_bb = bb;
|
||||
_vs_id = vs_id;
|
||||
_vs_input_tag = SparkTags.VolSurfaceCommittedInputTag(vs_id);
|
||||
_vs_calc_response_tag = SparkTags.VolSurfaceAutoCalculationResponseTag(vs_id);
|
||||
_bb.subscribe(_vs_input_tag);
|
||||
_bb.subscribe(_vs_calc_response_tag);
|
||||
_bb.subscribe(SparkTags.CurrentUser);
|
||||
_bb.data_callback += _on_bb_data;
|
||||
_bb.timer_callback += _on_bb_timer;
|
||||
}
|
||||
|
||||
private void _on_bb_data(object tag, object data) {
|
||||
if (tag.Equals(_vs_input_tag))
|
||||
_possibly_autofit();
|
||||
else if (tag.Equals(_vs_calc_response_tag)) {
|
||||
VolSurfaceCalculationResponse calc_resp = data as VolSurfaceCalculationResponse;
|
||||
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource._on_bb_data vs_id: {_vs_id} " +
|
||||
$"received calculation response");
|
||||
_bb.post(SparkTags.VolSurfaceCommitTag(_vs_id),
|
||||
calc_resp.volsurface_calculation_output.volsurface_input);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _user_has_vs_control() {
|
||||
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
|
||||
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
|
||||
return vs_input != null &&
|
||||
vs_input.surface_settings.core_settings.vs_manager == current_user;
|
||||
}
|
||||
|
||||
private void _on_bb_timer() {
|
||||
_possibly_autofit();
|
||||
}
|
||||
|
||||
private void _possibly_autofit() {
|
||||
var auto_fit_period = _get_auto_fit_periodic();
|
||||
if (!auto_fit_period.HasValue)
|
||||
return;
|
||||
if (DateTime.Now - _last_req_time <= auto_fit_period)
|
||||
return;
|
||||
if (!_user_has_vs_control())
|
||||
return;
|
||||
// auto fit here
|
||||
bts.Log.Gen.info($"SingleVolSurfaceAutoFitSource vs_id: {_vs_id} requesting fit " +
|
||||
$"last_req_time: {_last_req_time} period: {auto_fit_period.Value}");
|
||||
_send_request();
|
||||
}
|
||||
|
||||
private Nullable<TimeSpan> _get_auto_fit_periodic() {
|
||||
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
|
||||
if (vs_input == null)
|
||||
return null;
|
||||
var fit_settings =
|
||||
VolSurfaceDataFunctions.get_market_fit_settings(vs_input.surface_settings);
|
||||
if (fit_settings == null)
|
||||
return null;
|
||||
var auto_fit_setting = fit_settings.vs_auto_fit_settings;
|
||||
if (auto_fit_setting == null)
|
||||
return null;
|
||||
if (auto_fit_setting.auto_fit_region == VolSurfaceEnums.VolCurveFitRegion.None)
|
||||
return null;
|
||||
var time_based_fit_setting = auto_fit_setting.time_based_settings;
|
||||
if (time_based_fit_setting == null)
|
||||
return null;
|
||||
if (!time_based_fit_setting.period_seconds.HasValue)
|
||||
return null;
|
||||
return TimeSpan.FromSeconds(time_based_fit_setting.period_seconds.Value);
|
||||
}
|
||||
|
||||
private void _send_request() {
|
||||
VolSurfaceInput vs_input = _bb.read_state(_vs_input_tag);
|
||||
if (vs_input == null)
|
||||
return;
|
||||
VolCurveMarketFitAction mf_action = new VolCurveMarketFitAction(
|
||||
VolCurveMarketFitAction.ActionType.FitWholeCurve, null, false);
|
||||
VolCurveAction vc_action = new VolCurveAction(mf_action);
|
||||
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
|
||||
vs_input.surface_parameters.curve_parameters_map.ToDictionary(
|
||||
i => i.Item1,
|
||||
i => vc_action
|
||||
);
|
||||
VolSurfaceCalculationRequest req = new VolSurfaceCalculationRequest(vs_input,
|
||||
_req_id++, action_map, true, true);
|
||||
_last_req_time = DateTime.Now;
|
||||
_bb.post(SparkTags.VolSurfaceAutoCalculationRequestTag(_vs_id), req);
|
||||
}
|
||||
|
||||
|
||||
#region IDisposable
|
||||
public void Dispose() {
|
||||
_bb.data_callback -= _on_bb_data;
|
||||
_bb.timer_callback -= _on_bb_timer;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
using bts.api.analytics;
|
||||
|
||||
using mbts.analytics;
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
@@ -13,6 +14,7 @@
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
|
||||
// For a single VolSurface this class
|
||||
// 1. Listents to commmits and calls the service to persist data
|
||||
@@ -24,7 +26,6 @@
|
||||
private readonly ITag<VolSurfaceInput> _in_bb_committ_tag;
|
||||
private readonly AnalyticsService _analytics_service;
|
||||
private readonly HighPerformanceTimer _timer = new HighPerformanceTimer();
|
||||
private readonly string _user;
|
||||
private readonly List<IDisposable> _disposables = new List<IDisposable>();
|
||||
private IDictionary<UInt64, VolSurfaceInputSnapshot> _snp_shot_map = new
|
||||
Dictionary<UInt64, VolSurfaceInputSnapshot>();
|
||||
@@ -35,10 +36,10 @@
|
||||
volsurface_id));
|
||||
_volsurface_id = volsurface_id;
|
||||
_bb = threaded_bb;
|
||||
_user = "TEST_USER"; // TODO
|
||||
_in_bb_committ_tag = SparkTags.VolSurfaceCommitTag(volsurface_id);
|
||||
_analytics_service = analytics_service;
|
||||
_bb.subscribe(_in_bb_committ_tag);
|
||||
_bb.subscribe(SparkTags.CurrentUser);
|
||||
_bb.data_callback += _on_bb_data;
|
||||
var vs_sub = _analytics_service.subscribei_volsurfaces()
|
||||
.ObserveOn(SynchronizationContext.Current)
|
||||
@@ -83,15 +84,27 @@
|
||||
static VolSurfaceInput _to_vs_input(VolSurfaceEntryImmutable vs) =>
|
||||
ProtobufSerializationHelper.deserialize<VolSurfaceInput>(vs.Volsurface.ToByteArray());
|
||||
|
||||
// private void _show_save_error(string message) {
|
||||
// BTSMessageBox.Show(message, "Vol Save Error", BTSMessageBoxButtons.OK);
|
||||
// }
|
||||
|
||||
private void _on_bb_data(object tag, object data) {
|
||||
if (_in_bb_committ_tag.Equals(tag)) {
|
||||
try {
|
||||
_timer.start();
|
||||
VolSurfaceInput vs_input = data as VolSurfaceInput;
|
||||
var user_tup = _bb.read_state(SparkTags.CurrentUser);
|
||||
string current_user = user_tup.name;
|
||||
string vs_manager = vs_input.surface_settings.core_settings.vs_manager;
|
||||
if (current_user != vs_manager) {
|
||||
string msg = $"User {current_user} does not have the permission to save vols. " +
|
||||
$"Permission can be obtained via the grid context menu item 'Acquire ownership from {vs_manager}'";
|
||||
BTSMessageBox.Show(msg, "Vol Save Error", BTSMessageBoxButtons.OK);
|
||||
return;
|
||||
}
|
||||
_sanitize_input(ref vs_input);
|
||||
vs_input = VolSurfaceDataFunctions.unset_driver_overrides(vs_input);
|
||||
vs_input.saver_login = _user;
|
||||
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, _user);
|
||||
VolSurfaceUtilityFunctions.save_volsurface(vs_input, _analytics_service, current_user);
|
||||
_timer.stop();
|
||||
Log.Gen.info(String.Format(
|
||||
"SingleVolSurfaceGateway._on_bb_data Time to commit volsurface {0} {1} msecs",
|
||||
|
||||
@@ -36,7 +36,11 @@ namespace mbts.spark {
|
||||
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
|
||||
a_syms => new SingleVolSurfaceCommittedOutputRetriever(_vs_id, _bb, a_syms)),
|
||||
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
|
||||
a_syms => new SingleVolSurfaceRequestCalculator(_vs_id, a_syms, _bb)),
|
||||
a_syms => new SingleVolSurfaceRequestCalculator(
|
||||
SingleVolSurfaceRequestCalculator.RequestType.UI, _vs_id, a_syms, _bb)),
|
||||
new SingleVolSurfaceDataSource(SingleVolSurfaceDataSource.SymbolListType.Combined, _vs_id, _bb,
|
||||
a_syms => new SingleVolSurfaceRequestCalculator(
|
||||
SingleVolSurfaceRequestCalculator.RequestType.AUTO, _vs_id, a_syms, _bb)),
|
||||
new SingleVolSurfaceMarketImpliedVolSource(_vs_id, _bb),
|
||||
// new SingleVolSurfaceTheoSource(_vs_id, _bb),
|
||||
// TODO: suspending heatmaps for now (12/24/21)
|
||||
@@ -44,6 +48,7 @@ namespace mbts.spark {
|
||||
// server work that can optionally be done on the client
|
||||
new SingleVolSurfaceDataSource (SingleVolSurfaceDataSource.SymbolListType.Committed, _vs_id, _bb,
|
||||
a_syms => new SingleVolSurfaceInterpGreeksPublisher (_vs_id, _bb, a_syms)),
|
||||
new SingleVolSurfaceAutoFitSource(_vs_id, _bb),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,16 @@ namespace mbts.spark {
|
||||
// Pubs :
|
||||
// 1. Calc Resp
|
||||
public class SingleVolSurfaceRequestCalculator : IDisposable {
|
||||
public enum RequestType {
|
||||
UI = 0,
|
||||
AUTO
|
||||
}
|
||||
private readonly RequestType _request_type = RequestType.UI;
|
||||
private readonly int _vs_id;
|
||||
private readonly HashSet<string> _symbols;
|
||||
private readonly ThreadedBlackboard _bb;
|
||||
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_request_tag;
|
||||
private readonly ITag<VolSurfaceCalculationRequest> _in_bb_auto_request_tag;
|
||||
private readonly ITag<VolSurfaceNonVolData> _in_bb_nonvol_data_tag;
|
||||
private readonly ITag<VolSurfaceStandardTime> _in_bb_vs_standard_time_tag;
|
||||
private readonly ITag<VolSurfaceEnums.MarketSide> _in_bb_market_impliedvol_side_tag;
|
||||
@@ -30,18 +36,22 @@ namespace mbts.spark {
|
||||
/// data
|
||||
private VolSurfaceOutput _last_output;
|
||||
|
||||
public SingleVolSurfaceRequestCalculator(int vs_id, IEnumerable<string> symbols,
|
||||
public SingleVolSurfaceRequestCalculator(RequestType request_type,
|
||||
int vs_id, IEnumerable<string> symbols,
|
||||
ThreadedBlackboard threaded_bb) {
|
||||
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id: {vs_id}");
|
||||
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator cons vs_id: {vs_id} request_type: {request_type}");
|
||||
_request_type = request_type;
|
||||
_vs_id = vs_id;
|
||||
_symbols = new HashSet<string>(symbols);
|
||||
_bb = threaded_bb;
|
||||
_in_bb_request_tag = SparkTags.VolSurfaceCalculationRequestTag(vs_id);
|
||||
_in_bb_auto_request_tag = SparkTags.VolSurfaceAutoCalculationRequestTag(vs_id);
|
||||
_in_bb_nonvol_data_tag = SparkTags.VolSurfaceNonVolDataTag(vs_id);
|
||||
_in_bb_vs_standard_time_tag = SparkTags.VolSurfaceStandardTimeTag(vs_id);
|
||||
_in_bb_market_impliedvol_side_tag = SparkTags.VolSurfaceMarketImpliedVolSide(vs_id);
|
||||
_in_bb_dirty_tag = SparkTags.VolSurfaceDirtyStateTag(vs_id);
|
||||
_bb.subscribe(_in_bb_request_tag);
|
||||
_bb.subscribe(_in_bb_auto_request_tag);
|
||||
_bb.subscribe(_in_bb_nonvol_data_tag);
|
||||
_bb.subscribe(_in_bb_vs_standard_time_tag);
|
||||
_bb.subscribe(_in_bb_vs_standard_time_tag);
|
||||
@@ -51,11 +61,21 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
private void _on_bb_data(object tag, object data) {
|
||||
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
|
||||
_recompute_and_publish(true);
|
||||
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
|
||||
_recompute_and_publish(false);
|
||||
|
||||
switch (_request_type) {
|
||||
case RequestType.UI: {
|
||||
if (_in_bb_request_tag.Equals(tag) || _in_bb_dirty_tag.Equals(tag))
|
||||
_recompute_and_publish(true);
|
||||
else if (_in_bb_nonvol_data_tag.Equals(tag) || _in_bb_vs_standard_time_tag.Equals(tag))
|
||||
_recompute_and_publish(false);
|
||||
break;
|
||||
}
|
||||
case RequestType.AUTO: {
|
||||
if (tag.Equals(_in_bb_auto_request_tag)) {
|
||||
_serve_auto_request();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _recompute_and_publish(bool is_new_request) {
|
||||
@@ -80,7 +100,7 @@ namespace mbts.spark {
|
||||
nonvol_data.driver_price_map,
|
||||
vs_standard_time, true);
|
||||
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
|
||||
_post_process_action(effective_req, aux_data_map);
|
||||
_post_process_action(_bb, _vs_id, effective_req, aux_data_map);
|
||||
_last_output = GeneralVolSurfaceCalculator.compute(effective_req.volsurface_input,
|
||||
nonvol_data.contracts,
|
||||
aux_data_map,
|
||||
@@ -89,16 +109,49 @@ namespace mbts.spark {
|
||||
effective_req.cascade_up,
|
||||
effective_req.cascade_down);
|
||||
_timer.stop();
|
||||
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish time: " +
|
||||
$"{_timer.lastIntervalTime}");
|
||||
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._recompute_and_publish vs_id: {_vs_id} " +
|
||||
$"time: {_timer.lastIntervalTime}");
|
||||
_bb.post(SparkTags.VolSurfaceCalculationResponseTag(_vs_id),
|
||||
new VolSurfaceCalculationResponse(_last_output, effective_req.request_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _serve_auto_request() {
|
||||
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request " +
|
||||
$"vs_id: {_vs_id} auto request received");
|
||||
bool is_dirty = _bb.read_state(_in_bb_dirty_tag, false);
|
||||
VolSurfaceNonVolData nonvol_data = _bb.read_state(_in_bb_nonvol_data_tag);
|
||||
VolSurfaceStandardTime vs_standard_time = _bb.read_state(_in_bb_vs_standard_time_tag);
|
||||
VolSurfaceCalculationRequest auto_req = _bb.read_state(_in_bb_auto_request_tag);
|
||||
if (is_dirty || nonvol_data == null || vs_standard_time == null || auto_req == null) {
|
||||
return;
|
||||
}
|
||||
_timer.start();
|
||||
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map =
|
||||
VolSurfaceUtilityFunctions.compute_aux_data(auto_req.volsurface_input,
|
||||
nonvol_data.input,
|
||||
nonvol_data.driver_price_map,
|
||||
vs_standard_time, true);
|
||||
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map =
|
||||
_post_process_action(_bb, _vs_id, auto_req, aux_data_map);
|
||||
var vs_output = GeneralVolSurfaceCalculator.compute(auto_req.volsurface_input,
|
||||
nonvol_data.contracts,
|
||||
aux_data_map,
|
||||
action_map,
|
||||
null,
|
||||
auto_req.cascade_up,
|
||||
auto_req.cascade_down);
|
||||
_timer.stop();
|
||||
bts.Log.Gen.info($"SingleVolSurfaceRequestCalculator._serve_auto_request vs_id: {_vs_id} " +
|
||||
$"time: {_timer.lastIntervalTime}");
|
||||
_bb.post(SparkTags.VolSurfaceAutoCalculationResponseTag(_vs_id),
|
||||
new VolSurfaceCalculationResponse(vs_output, auto_req.request_id));
|
||||
}
|
||||
|
||||
/// Computes the impl vol if the action is fit to market
|
||||
private IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
|
||||
private static IDictionary<VolSurfaceExpiration, VolCurveAction> _post_process_action(
|
||||
ThreadedBlackboard bb, int vs_id,
|
||||
VolSurfaceCalculationRequest req,
|
||||
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map) {
|
||||
IDictionary<VolSurfaceExpiration, VolCurveAction> rv = req.action_map;
|
||||
@@ -109,7 +162,7 @@ namespace mbts.spark {
|
||||
VolCurveAction rep_action = rv.ElementAt(0).Value;
|
||||
if (rep_action.action_type == VolCurveAction.ActionType.FitToMarket) {
|
||||
IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> impl_vol_map =
|
||||
_fill_in_implied_vols(req.volsurface_input, req.action_map, aux_data_map,
|
||||
_fill_in_implied_vols(bb, vs_id, req.volsurface_input, req.action_map, aux_data_map,
|
||||
req.cascade_up, req.cascade_down);
|
||||
rv = impl_vol_map.ToDictionary(kv => kv.Key, kv => new VolCurveAction(
|
||||
new VolCurveMarketFitAction(rep_action.market_fit_action.fit_action, kv.Value,
|
||||
@@ -119,7 +172,8 @@ namespace mbts.spark {
|
||||
return rv;
|
||||
}
|
||||
|
||||
private IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
|
||||
private static IDictionary<VolSurfaceExpiration, VolCurveMarketImpliedVolData> _fill_in_implied_vols(
|
||||
ThreadedBlackboard bb, int vs_id,
|
||||
VolSurfaceInput vs_inp,
|
||||
IDictionary<VolSurfaceExpiration, VolCurveAction> action_map,
|
||||
IDictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData> aux_data_map,
|
||||
@@ -140,15 +194,15 @@ namespace mbts.spark {
|
||||
applicable_exps = applicable_exps.Where(vs_exp => master_exps.Contains(vs_exp));
|
||||
|
||||
VolSurfaceEnums.MarketSide impliedvol_side =
|
||||
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(_bb, _vs_id);
|
||||
SingleVolSurfaceMarketImpliedVolSource._get_market_impliedvol_side(bb, vs_id);
|
||||
foreach (VolSurfaceExpiration vs_exp in applicable_exps) {
|
||||
VolSurfaceExpiration md_exp;
|
||||
VolCurveMarketImpliedVolData exp_impl_data =
|
||||
VolSurfaceCoreFunctions.compute_implied_vols(vs_inp, vs_exp, aux_data_map,
|
||||
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(_bb, arg_vs_exp),
|
||||
arg_vs_exp => SingleVolSurfaceMarketImpliedVolSource.get_option_bbo(bb, arg_vs_exp),
|
||||
impliedvol_side,
|
||||
SingleVolSurfaceMarketImpliedVolSource._ignore_market_implied_vol_source_settings(
|
||||
_bb, _vs_id),
|
||||
bb, vs_id),
|
||||
out md_exp);
|
||||
rv[vs_exp] = exp_impl_data;
|
||||
}
|
||||
|
||||
@@ -26,13 +26,13 @@ namespace mbts.spark {
|
||||
_refresh = new(_is_dirty.refresh);
|
||||
_committed_value.Changed += on_committed_changed;
|
||||
_candidate_value.Changed += on_candidate_changed;
|
||||
_is_dirty.Changed += (o, n) => {
|
||||
Log.assert(o != n);
|
||||
// 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}");
|
||||
};
|
||||
if (column.type.is_editable()) {
|
||||
_is_dirty.Changed += (o, n) => {
|
||||
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) =>
|
||||
_is_dirty.refresh();
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace mbts.spark.VolSurface {
|
||||
|
||||
class VolSurfaceGridVM : DisposableViewModelBase {
|
||||
readonly VolSurfaceVM _parent;
|
||||
readonly UserTuple _user;
|
||||
readonly ObservableCollection<VolSurfaceDisplayGridExpiration> _expirations = new();
|
||||
readonly DeferrableAction _invalidate_rows;
|
||||
readonly ColumnsModel _columns_model;
|
||||
@@ -41,10 +42,12 @@ namespace mbts.spark.VolSurface {
|
||||
VolSurfaceVM vm,
|
||||
VolSurfaceDisplayModel model,
|
||||
ISet<string> hidden_columns,
|
||||
ISet<VolSurfaceExpiration> hidden_expirations) {
|
||||
ISet<VolSurfaceExpiration> hidden_expirations,
|
||||
UserTuple user) {
|
||||
Log.notnull(hidden_columns);
|
||||
Log.notnull(hidden_expirations);
|
||||
_parent = vm;
|
||||
_user = user;
|
||||
this.model = model;
|
||||
_columns_model = new(create_exp_column_view());
|
||||
_hidden_columns = hidden_columns;
|
||||
@@ -83,7 +86,7 @@ namespace mbts.spark.VolSurface {
|
||||
_columns_model);
|
||||
|
||||
VolSurfaceRowVM row_factory(VolSurfaceDisplayGridExpiration exp) =>
|
||||
new(this, exp);
|
||||
new(this, exp, _user);
|
||||
|
||||
IDisposable connect_row(VolSurfaceRowVM row) {
|
||||
row.request_calculate += request_recalc;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.collections;
|
||||
|
||||
using mbts.spark.VolSurface;
|
||||
@@ -9,6 +10,7 @@ namespace mbts.spark {
|
||||
using mbts.ui.utility;
|
||||
|
||||
using Spark.Core.Disposables;
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -22,6 +24,10 @@ namespace mbts.spark {
|
||||
readonly VolSurfaceGridVM _parent;
|
||||
readonly Property<VolSurfaceColumn?> _column;
|
||||
readonly Property<DIYGridSuperHeaderVM?> _super_header;
|
||||
readonly UserTuple _user;
|
||||
readonly Property<bool> _is_open;
|
||||
readonly DelegateProperty<string> _vs_manager;
|
||||
readonly List<IDisposable> _resources = new();
|
||||
|
||||
ICommand? _revert_cmd;
|
||||
ICommand? _shift_selection_cmd;
|
||||
@@ -34,21 +40,29 @@ namespace mbts.spark {
|
||||
ICommand? _copy_all_to_clipboard_cmd;
|
||||
ICommand? _smart_copy_rolls_rates_cmd;
|
||||
ICommand? _import_csv_cmd;
|
||||
ObservantCommand? _acquire_ownership_cmd;
|
||||
|
||||
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row) {
|
||||
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row, UserTuple user) {
|
||||
_parent = parent;
|
||||
_row = row;
|
||||
_user = user;
|
||||
_column = backing<VolSurfaceColumn?>(nameof(column));
|
||||
_super_header = backing<DIYGridSuperHeaderVM?>(nameof(super_header));
|
||||
_vs_manager = backing(
|
||||
nameof(vs_manager),
|
||||
() => _parent.model.get_active_settings()?.core_settings?.vs_manager ?? string.Empty);
|
||||
_is_open = backing(nameof(is_open), false);
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
_resources.AddRange(
|
||||
_column.invalidate_others_on_change(nameof(column_name)),
|
||||
_super_header.invalidate_others_on_change(nameof(group_name)),
|
||||
_is_open.Subscribe(_ => _vs_manager.refresh()),
|
||||
_parent.visible_rows.ObserveSelf()
|
||||
.BufferLatest(TimeSpan.FromSeconds(1))
|
||||
.OnDispatcher()
|
||||
.Subscribe(_ => sync_symbols()));
|
||||
|
||||
_dispose = Disposable.Create(() => _resources.DisposeAll(true));
|
||||
void sync_symbols() {
|
||||
var visible_exps = _parent.visible_rows.Map(r => r.expiration.symbol);
|
||||
symbols.sync_with_models(
|
||||
@@ -164,6 +178,34 @@ namespace mbts.spark {
|
||||
_parent.model.csv_importing_enabled,
|
||||
null);
|
||||
|
||||
public ICommand acquire_ownership_cmd {
|
||||
get {
|
||||
if (_acquire_ownership_cmd is null) {
|
||||
_acquire_ownership_cmd = new(
|
||||
() => {
|
||||
var settings = _parent.model.get_active_settings();
|
||||
if (settings is null) {
|
||||
Log.Ui.error("Unable to acquire volsurface ownership: settings are null.");
|
||||
return;
|
||||
}
|
||||
settings.core_settings.vs_manager = _user.name;
|
||||
_parent.model.set_candidate_settings(settings);
|
||||
});
|
||||
_resources.Add(_vs_manager.observe_current(true)
|
||||
.Subscribe(SelectorObserver.builder(_acquire_ownership_cmd).wrap<string>(
|
||||
curr => curr != _user.name)));
|
||||
}
|
||||
return _acquire_ownership_cmd;
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_open {
|
||||
get => _is_open.Value;
|
||||
set => _is_open.set(value);
|
||||
}
|
||||
|
||||
public string vs_manager => _vs_manager.Value;
|
||||
|
||||
VolSurfaceDisplayGridColumn _assert_model() =>
|
||||
column?.model ?? throw new InvalidOperationException();
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark.VolSurface {
|
||||
using bts;
|
||||
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
@@ -16,14 +18,16 @@ namespace mbts.spark.VolSurface {
|
||||
readonly VolSurfaceGridVM _parent;
|
||||
readonly VolSurfaceDisplayGridExpiration _exp;
|
||||
readonly Dictionary<string, CellData> _cells = new();
|
||||
readonly UserTuple _user;
|
||||
readonly DeferrableAction _request_calculate;
|
||||
|
||||
VolSurfaceRowContextMenuVM? _context_menu;
|
||||
bool _is_selected;
|
||||
|
||||
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp) {
|
||||
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp, UserTuple user) {
|
||||
_parent = parent;
|
||||
_exp = exp;
|
||||
_user = user;
|
||||
_request_calculate = new(() => request_calculate?.Invoke());
|
||||
_dispose = Disposable.Create(() => {
|
||||
_cells.Values.DisposeAll();
|
||||
@@ -49,7 +53,7 @@ namespace mbts.spark.VolSurface {
|
||||
}
|
||||
|
||||
public VolSurfaceRowContextMenuVM context_menu =>
|
||||
_context_menu ??= new(_parent, this);
|
||||
_context_menu ??= new(_parent, this, _user);
|
||||
|
||||
public VolSurfaceCellVM get_cell(VolSurfaceDisplayGridColumn column) =>
|
||||
_cells.GetOrAdd(column.unique_col_name, _ => _create_cell(column)).vm;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#nullable enable
|
||||
|
||||
namespace mbts.spark {
|
||||
using bts.api.domain;
|
||||
|
||||
using mbts.analytics;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using VolCurveFitRegion = analytics.VolSurfaceEnums.VolCurveFitRegion;
|
||||
|
||||
public class VolSurfaceAutoFitSettingsVM : DisposableViewModelBase {
|
||||
readonly Property<VolCurveFitRegion> _fit_region;
|
||||
readonly Property<uint?> _period_secs;
|
||||
readonly Property<double?> _driver_points_moved;
|
||||
readonly Property<double?> _atm_points_moved;
|
||||
readonly Property<bool> _is_auto_fit_enabled;
|
||||
|
||||
public VolSurfaceAutoFitSettingsVM() {
|
||||
_fit_region = backing(nameof(fit_region), VolCurveFitRegion.None);
|
||||
_is_auto_fit_enabled = backing(
|
||||
nameof(is_auto_fit_enabled),
|
||||
_fit_region.observe_current(true),
|
||||
r => r != VolCurveFitRegion.None);
|
||||
_period_secs = backing<uint?>(nameof(period_secs), 10);
|
||||
_driver_points_moved = backing<double?>(nameof(driver_points_moved));
|
||||
_atm_points_moved = backing<double?>(nameof(atm_points_moved));
|
||||
_fit_region.Changed += (o, n) => RaisePropertyChanged(nameof(is_auto_fit_enabled));
|
||||
}
|
||||
|
||||
public IEnumerable<VolCurveFitRegion> fit_regions =>
|
||||
EnumHelper.get_values<VolCurveFitRegion>();
|
||||
|
||||
public VolCurveFitRegion fit_region {
|
||||
get => _fit_region.Value;
|
||||
set => _fit_region.set(value);
|
||||
}
|
||||
|
||||
public bool is_auto_fit_enabled => _is_auto_fit_enabled.Value;
|
||||
|
||||
public uint? period_secs {
|
||||
get => _period_secs.Value;
|
||||
set => _period_secs.set(value);
|
||||
}
|
||||
|
||||
public double? driver_points_moved {
|
||||
get => _driver_points_moved.Value;
|
||||
set => _driver_points_moved.set(value);
|
||||
}
|
||||
|
||||
public TickDisplaySettingsImmutable? driver_points_display =>
|
||||
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
|
||||
|
||||
public double? atm_points_moved {
|
||||
get => _atm_points_moved.Value;
|
||||
set => _atm_points_moved.set(value);
|
||||
}
|
||||
|
||||
public TickDisplaySettingsImmutable? atm_points_display =>
|
||||
user.settings.TickFactors.pennies.to_api_tick_display_settingsi();
|
||||
|
||||
public void load_from(VolSurfaceAutoFitSettings value) {
|
||||
fit_region = value?.auto_fit_region ?? VolCurveFitRegion.None;
|
||||
period_secs = value?.time_based_settings?.period_seconds;
|
||||
driver_points_moved = value?.driver_move_based_settings?.points_moved;
|
||||
atm_points_moved = value?.atm_implied_vol_based_settings?.points_moved;
|
||||
}
|
||||
|
||||
public void assign_to(VolSurfaceAutoFitSettings value) {
|
||||
if (value is null) {
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
value.auto_fit_region = fit_region;
|
||||
value.time_based_settings = new() {
|
||||
period_seconds = period_secs
|
||||
};
|
||||
value.driver_move_based_settings = new() {
|
||||
points_moved = driver_points_moved
|
||||
};
|
||||
value.atm_implied_vol_based_settings = new() {
|
||||
points_moved = atm_points_moved
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ namespace mbts.spark {
|
||||
vol_model_cubic_spline_vm.load_from(settings.spline_model_settings);
|
||||
vol_model_dual_quadratic_vm.load_from(settings.dualquadratic_model_settings);
|
||||
vol_path_parabolic_vm.load_from(settings.core_settings.volpath_settings.parabolic_vol_path_settings);
|
||||
auto_fit_vm.load_from(settings.core_settings.market_fit_settings.vs_auto_fit_settings);
|
||||
|
||||
init_rotation_vms(call_rotations, _core_settings.call_rotation.strike_vol_movement_pairs);
|
||||
init_rotation_vms(put_rotations, _core_settings.put_rotation.strike_vol_movement_pairs);
|
||||
@@ -105,6 +106,7 @@ namespace mbts.spark {
|
||||
}).ToList();
|
||||
_core_settings.strangle.strike_vol_movement_pairs = strangles.SelectMany(r => new[] { Tuple.Create(r.target_put_strike, r.put_move), Tuple.Create(r.target_call_strike, r.call_move) }).ToList();
|
||||
_core_settings.atm_shift.atm_strike_vol_movement = atm_shifts.Count == 1 ? atm_shifts[0].move : 0;
|
||||
auto_fit_vm.assign_to(_core_settings.market_fit_settings.vs_auto_fit_settings);
|
||||
commit(_settings);
|
||||
}
|
||||
|
||||
@@ -233,23 +235,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public bool enable_auto_fit {
|
||||
get => _core_settings.market_fit_settings.enable_auto_fit;
|
||||
set {
|
||||
using var _ = detect_property_change();
|
||||
_core_settings.market_fit_settings.enable_auto_fit = value;
|
||||
}
|
||||
}
|
||||
|
||||
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region {
|
||||
get => _core_settings.market_fit_settings.auto_fit_region;
|
||||
set { _core_settings.market_fit_settings.auto_fit_region = value; }
|
||||
}
|
||||
|
||||
public double auto_fit_time_ms {
|
||||
get => _core_settings.market_fit_settings.auto_fit_time_ms;
|
||||
set { _core_settings.market_fit_settings.auto_fit_time_ms = value; }
|
||||
}
|
||||
public VolSurfaceAutoFitSettingsVM auto_fit_vm { get; } = new();
|
||||
|
||||
public bool auto_decay_vol_time {
|
||||
get => _core_settings.time_settings.time_mode == VolSurfaceEnums.VolSurfaceTimeMode.AutoDecay;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
|
||||
@@ -27,6 +28,7 @@ namespace mbts.spark {
|
||||
readonly IBus _bus;
|
||||
readonly string _request_id = Guid.NewGuid().ToString();
|
||||
readonly VolSurfaceDisplayModel _model;
|
||||
readonly UserTuple _user;
|
||||
readonly IRaiseCanExecuteChangedCommand _revert_cmd;
|
||||
readonly IRaiseCanExecuteChangedCommand _commit_cmd;
|
||||
readonly IDialogCommand _main_settings_cmd;
|
||||
@@ -38,10 +40,16 @@ namespace mbts.spark {
|
||||
bool _ignore_implied_vol_settings;
|
||||
|
||||
public VolSurfaceVM(
|
||||
VolSurfaceDisplayModel model, int id, IBus bus, VolSurfaceWindowSettings startup_data) {
|
||||
VolSurfaceDisplayModel model,
|
||||
int id,
|
||||
IBus bus,
|
||||
VolSurfaceWindowSettings startup_data,
|
||||
UserTuple user
|
||||
) {
|
||||
_model = model;
|
||||
_vs_id = id;
|
||||
_bus = bus;
|
||||
_user = user;
|
||||
macro_actions = new VolSurfaceMacroActionsVM(_model);
|
||||
globals = new VolSurfaceGlobalParameterVM(_model);
|
||||
|
||||
@@ -49,7 +57,7 @@ namespace mbts.spark {
|
||||
_grid_settings = startup_data.macro_grid_settings ?? new VolSurfaceMacroGridSettings();
|
||||
var hidden_exps_set = startup_data.hidden_exps.empty_if_null().ToHashSet();
|
||||
grid = new VolSurfaceGridVM(
|
||||
this, _model, _grid_settings.hidden_cols(), hidden_exps_set);
|
||||
this, _model, _grid_settings.hidden_cols(), hidden_exps_set, _user);
|
||||
grid.on_selection_changed += _on_grid_selection_changed;
|
||||
_model.publish_grid_settings(_grid_settings);
|
||||
|
||||
|
||||
@@ -4,13 +4,11 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.analytics
|
||||
{
|
||||
namespace mbts.analytics {
|
||||
/// These are the VolSurface settings that are common
|
||||
/// to any Vol Model
|
||||
[ProtoBuf.ProtoContract(Name = "VolSurfaceCoreSettings")]
|
||||
public class VolSurfaceCoreSettings
|
||||
{
|
||||
public class VolSurfaceCoreSettings {
|
||||
[ProtoBuf.ProtoMember(1)]
|
||||
public string vol_surface_name;
|
||||
[ProtoBuf.ProtoMember(2)]
|
||||
@@ -87,5 +85,7 @@ namespace mbts.analytics
|
||||
get => _time_settings ??= new();
|
||||
set => _time_settings = value;
|
||||
}
|
||||
[ProtoBuf.ProtoMember(240)]
|
||||
public string vs_manager { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,7 @@ namespace mbts.analytics {
|
||||
if (core_settings.market_fit_settings == null) {
|
||||
core_settings.market_fit_settings = new VolSurfaceMarketFitSettings();
|
||||
}
|
||||
core_settings.market_fit_settings.vs_auto_fit_settings ??= new();
|
||||
if (core_settings.time_settings == null) {
|
||||
core_settings.time_settings = new VolSurfaceTimeSettings();
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ namespace mbts.analytics
|
||||
|
||||
public enum VolCurveFitRegion
|
||||
{
|
||||
None,
|
||||
ATM,
|
||||
All
|
||||
}
|
||||
@@ -230,4 +231,4 @@ namespace mbts.analytics
|
||||
USE_CALENDAR_FOR_BUSINESS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,9 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.analytics
|
||||
{
|
||||
namespace mbts.analytics {
|
||||
[ProtoBuf.ProtoContract(Name = "VolSurfaceMarketFitSettings")]
|
||||
public class VolSurfaceMarketFitSettings
|
||||
{
|
||||
public class VolSurfaceMarketFitSettings {
|
||||
/// use one sided market or not
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public bool ignore_one_sided_markets;
|
||||
@@ -22,19 +20,44 @@ namespace mbts.analytics
|
||||
public double graph_update_period_ms = 2500;
|
||||
/// If this is set to true a regression curve is drawn
|
||||
/// and the vol model adheres to that
|
||||
[ProtoBuf.ProtoMember(50)]
|
||||
[ProtoBuf.ProtoMember(50)]
|
||||
public bool use_weighted_regression_fit = false;
|
||||
[ProtoBuf.ProtoMember(55)]
|
||||
public uint regression_order = 4;
|
||||
/// The system auto fits and saves vols if this is
|
||||
/// set to true
|
||||
[ProtoBuf.ProtoMember(60)]
|
||||
public bool enable_auto_fit = false;
|
||||
/// If enable_auto_fit = true, this governs
|
||||
/// how often the system auto-fits
|
||||
[ProtoBuf.ProtoMember(70)]
|
||||
public double auto_fit_time_ms = 30000;
|
||||
[ProtoBuf.ProtoMember(80)]
|
||||
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region = VolSurfaceEnums.VolCurveFitRegion.ATM;
|
||||
|
||||
[ProtoBuf.ProtoMember(90)]
|
||||
public VolSurfaceAutoFitSettings vs_auto_fit_settings { get; set; }
|
||||
}
|
||||
|
||||
[ProtoBuf.ProtoContract(Name = "VolSurfaceAutoFitSettings")]
|
||||
public class VolSurfaceAutoFitSettings {
|
||||
// each of these settings can be enabled independently. if they are enabled
|
||||
// then any of them being true will result in an autmatic fit
|
||||
[ProtoBuf.ProtoContract(Name = "TimeBasedSettings")]
|
||||
public class TimeBasedSettings {
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public uint? period_seconds { get; set; }
|
||||
}
|
||||
|
||||
[ProtoBuf.ProtoContract(Name = "DriverMoveBasedSettings")]
|
||||
public class DriverMoveBasedSettings {
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public double? points_moved { get; set; }
|
||||
}
|
||||
|
||||
[ProtoBuf.ProtoContract(Name = "ATMImpliedVolBasedSettings")]
|
||||
public class ATMImpliedVolBasedSettings {
|
||||
[ProtoBuf.ProtoMember(20)]
|
||||
public double? points_moved { get; set; }
|
||||
// we can do price terms moved as well
|
||||
}
|
||||
[ProtoBuf.ProtoMember(5)]
|
||||
public VolSurfaceEnums.VolCurveFitRegion auto_fit_region;
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public TimeBasedSettings time_based_settings = null;
|
||||
[ProtoBuf.ProtoMember(20)]
|
||||
public DriverMoveBasedSettings driver_move_based_settings = null;
|
||||
[ProtoBuf.ProtoMember(30)]
|
||||
public ATMImpliedVolBasedSettings atm_implied_vol_based_settings = null;
|
||||
}
|
||||
}
|
||||
@@ -475,6 +475,7 @@ namespace mbts.analytics {
|
||||
public static ResultMessage save_volsurface(
|
||||
VolSurfaceInput vs_input, AnalyticsService service, string user = "") {
|
||||
vs_input.saver_login = user;
|
||||
vs_input.surface_settings.core_settings.vs_manager = user;
|
||||
// convert to VolSurfaceEntry
|
||||
var to_save = new VolSurfaceEntry();
|
||||
var serialized_vs = ProtobufSerializationHelper.serialize(vs_input);
|
||||
@@ -595,7 +596,7 @@ namespace mbts.analytics {
|
||||
_greek_getter(g)(t, is_call);
|
||||
|
||||
public static IDictionary<double, IEnumerable<Theos>> hash_strikes(IEnumerable<Theos> theos) =>
|
||||
theos.Map(t => t.strike);
|
||||
theos.Map(t => t.strike);
|
||||
|
||||
static Func<Theos, bool, double> _greek_getter(VolSurfaceGreeks g) =>
|
||||
g switch {
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace mbts.spark {
|
||||
r => new ReportImmutable?(r),
|
||||
r => r?.Id ?? throw new ArgumentNullException(nameof(r)));
|
||||
_window_factories = new() {
|
||||
new VolSurfaceWindowFactory(this, bus),
|
||||
new VolSurfaceWindowFactory(this, bus, user.Value),
|
||||
new AnalyticsExpirationParametersWindowFactory(this, bus),
|
||||
new SlideReportWindowFactory(this, bus, reports_demuxer.subscribe),
|
||||
new ComboReportWindowFactory(this, bus, reports_demuxer.subscribe),
|
||||
|
||||
@@ -793,70 +793,106 @@
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="4"
|
||||
DataContext="{Binding auto_fit_vm}"
|
||||
Header="Auto Fit"
|
||||
Margin="0,5,0,0"
|
||||
Padding="5"
|
||||
IsEnabled="False">
|
||||
Padding="5">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox
|
||||
Grid.ColumnSpan="2" Content="Enabled" IsChecked="{Binding enable_auto_fit}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Text="Region" Margin="16,5,0,0">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger
|
||||
Binding="{Binding enable_auto_fit}"
|
||||
Value="False">
|
||||
<Setter
|
||||
Property="Foreground"
|
||||
Value="Gray" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="Fit Region" />
|
||||
<ComboBox
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{Binding fit_regions}"
|
||||
SelectedItem="{Binding fit_region}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static shell:VolSurfaceSettingsEditor.AutoFitRegionDisplayTextConverter}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
Text="Every" />
|
||||
<c:TickUpDownNullableUInt32
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="12,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding auto_fit_region_choices}"
|
||||
SelectedValue="{Binding auto_fit_region}"
|
||||
IsEnabled="{Binding enable_auto_fit}" />
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Minimum="30"
|
||||
Margin="8,0,0,0"
|
||||
Width="60"
|
||||
Value="{Binding period_secs}"
|
||||
IsEnabled="{Binding is_auto_fit_enabled}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="3,5,0,0"
|
||||
Text="secs" />
|
||||
|
||||
<TextBlock Grid.Row="2" Text="Fit Time" Margin="16,5,0,0">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding enable_auto_fit}" Value="False">
|
||||
<Setter Property="Foreground" Value="Gray" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<c:TextBoxDouble
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Margin="0,5,0,0"
|
||||
Text="When Driver moves" />
|
||||
<c:TickUpDownNullableDouble
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
c:TextBoxEx.Suffix="ms"
|
||||
ToolTipService.ShowOnDisabled="True"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Margin="8,0,0,0"
|
||||
Width="60"
|
||||
Margin="12,5,0,0"
|
||||
IsEnabled="{Binding enable_auto_fit}"
|
||||
Value="{Binding auto_fit_time_ms}"
|
||||
Template="{StaticResource SuffixTemplate}" />
|
||||
Value="{Binding driver_points_moved}"
|
||||
Increment="0.01"
|
||||
TickDisplaySettings="{Binding driver_points_display}"
|
||||
IsEnabled="False"
|
||||
ToolTip="Not yet supported (coming soon)" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="3,5,0,0"
|
||||
Text="%" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Margin="0,5,0,0"
|
||||
Text="When ATM moves" />
|
||||
<c:TickUpDownNullableDouble
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
ToolTipService.ShowOnDisabled="True"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="8,0,0,0"
|
||||
Width="60"
|
||||
Value="{Binding atm_points_moved}"
|
||||
Increment="0.01"
|
||||
TickDisplaySettings="{Binding atm_points_display}"
|
||||
IsEnabled="False"
|
||||
ToolTip="Not yet supported (coming soon)" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="3,5,0,0"
|
||||
Text="%" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
#nullable enable
|
||||
namespace mbts.ui.shell {
|
||||
using mbts.analytics;
|
||||
using mbts.ui.converters;
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
partial class VolSurfaceSettingsEditor : Window {
|
||||
public VolSurfaceSettingsEditor() =>
|
||||
InitializeComponent();
|
||||
|
||||
public static IValueConverter AutoFitRegionDisplayTextConverter =>
|
||||
ValueConverter.create<VolSurfaceEnums.VolCurveFitRegion, string>(fr =>
|
||||
fr switch {
|
||||
VolSurfaceEnums.VolCurveFitRegion.None => "Disabled",
|
||||
_ => fr.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -262,7 +262,7 @@
|
||||
IsDragAdornerEnabled="False"
|
||||
ContextMenuOpening="_on_context_menu_opening">
|
||||
<c:DIYGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<ContextMenu IsOpen="{Binding is_open}">
|
||||
<MenuItem
|
||||
Header="Populate Empty Expirations"
|
||||
Command="{Binding populate_empty_exps_cmd}" />
|
||||
@@ -413,6 +413,11 @@
|
||||
</MenuItem.ItemContainerStyle>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Import CSV..." Command="{Binding import_csv_cmd}" />
|
||||
<MenuItem Command="{Binding acquire_ownership_cmd}">
|
||||
<MenuItem.Header>
|
||||
<TextBlock Text="{Binding vs_manager, StringFormat='Acquire ownership from \'{0}\''}" />
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</c:DIYGrid.ContextMenu>
|
||||
<c:DIYGrid.RowStyle>
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
}
|
||||
var mouse_pos = Mouse.GetPosition(this);
|
||||
if (!this.ChildAtPoint<VolSurfaceRowView>(mouse_pos, out var row)) {
|
||||
cm.IsOpen = false;
|
||||
cm.SetCurrentValue(ContextMenu.IsOpenProperty, false);
|
||||
return;
|
||||
}
|
||||
var cgs = g.GetColumnGeometry().ToList();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.spark;
|
||||
|
||||
@@ -14,14 +15,20 @@
|
||||
public const string WINDOW_TYPE = "VolSurface";
|
||||
|
||||
readonly VolSurfaceWindowSettings _startup_data;
|
||||
readonly UserTuple _user;
|
||||
|
||||
VolSurfaceVM _vm;
|
||||
VolSurfaceView _view;
|
||||
|
||||
public VolSurfaceWindow(
|
||||
VolSurfaceWindowFactory factory, IBus bus, string vs_name, VolSurfaceWindowSettings startup_data) :
|
||||
base(vs_name, bus, factory, Icons.vol_surface) {
|
||||
VolSurfaceWindowFactory factory,
|
||||
IBus bus,
|
||||
string vs_name,
|
||||
VolSurfaceWindowSettings startup_data,
|
||||
UserTuple user
|
||||
) : base(vs_name, bus, factory, Icons.vol_surface) {
|
||||
_startup_data = startup_data ?? throw new ArgumentNullException(nameof(startup_data));
|
||||
_user = user;
|
||||
this.vs_name = vs_name;
|
||||
_dispose = Disposable.Create(() => Disposable.dispose(ref _vm));
|
||||
}
|
||||
@@ -37,7 +44,7 @@
|
||||
public override UIElement Content => _view;
|
||||
|
||||
public void thread_init() {
|
||||
_vm = new VolSurfaceVM(new VolSurfaceDisplayModel(vs_id, _bus, true), vs_id, _bus, _startup_data);
|
||||
_vm = new VolSurfaceVM(new (vs_id, _bus, true), vs_id, _bus, _startup_data, _user);
|
||||
_view = new VolSurfaceView {
|
||||
DataContext = _vm
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
|
||||
@@ -10,10 +11,12 @@ namespace mbts.spark {
|
||||
|
||||
class VolSurfaceWindowFactory : ShellWindowFactory<VolSurfaceWindowSettings> {
|
||||
readonly IBus _bus;
|
||||
readonly UserTuple _user;
|
||||
|
||||
public VolSurfaceWindowFactory(IShellModule module, IBus bus)
|
||||
public VolSurfaceWindowFactory(IShellModule module, IBus bus, UserTuple user)
|
||||
: base(module, "VolSurface") {
|
||||
_bus = bus;
|
||||
_user = user;
|
||||
_dispose = bus.subscribe(SparkTags.ShowVolSurface).OnDispatcher().Subscribe(
|
||||
async state => _bus.post(SparkTags.ShowWindow, await _create(state)));
|
||||
}
|
||||
@@ -22,7 +25,7 @@ namespace mbts.spark {
|
||||
_create(state);
|
||||
|
||||
async Task<IShellWindow> _create(VolSurfaceWindowSettings state) =>
|
||||
new VolSurfaceWindow(this, _bus, await _lookup_vs_name(state.id), state);
|
||||
new VolSurfaceWindow(this, _bus, await _lookup_vs_name(state.id), state, _user);
|
||||
|
||||
async Task<string> _lookup_vs_name(int vs_id) {
|
||||
var (vol_surfaces, success) = await _bus.subscribe(SparkTags.VolSurfaceListTag).FirstAsyncTask(TimeSpan.FromSeconds(2));
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace mbts.spark {
|
||||
public static ITag<int> CreateHeatmapTag { get; } = _mk_tag<int>();
|
||||
|
||||
// Publisher: VolSurfaceBlackboardManager
|
||||
// User: VoLSurfaceModule
|
||||
// User: VolSurfaceModule
|
||||
public static ITag<IDictionary<int, string>> VolSurfaceListTag { get; } =
|
||||
_mk_tag<IDictionary<int, string>>();
|
||||
|
||||
@@ -202,11 +202,17 @@ namespace mbts.spark {
|
||||
public static ITag<VolSurfaceCalculationRequest> VolSurfaceCalculationRequestTag(int vs_id) =>
|
||||
_mk_tag<VolSurfaceCalculationRequest, int>(vs_id);
|
||||
|
||||
public static ITag<VolSurfaceCalculationRequest> VolSurfaceAutoCalculationRequestTag(int vs_id) =>
|
||||
_mk_tag<VolSurfaceCalculationRequest, int>(vs_id);
|
||||
|
||||
// Publisher : SingleVolSurfaceRequestCalculator
|
||||
// Data : VolSurfaceCalculationResponse
|
||||
public static ITag<VolSurfaceCalculationResponse> VolSurfaceCalculationResponseTag(int vs_id) =>
|
||||
_mk_tag<VolSurfaceCalculationResponse, int>(vs_id);
|
||||
|
||||
public static ITag<VolSurfaceCalculationResponse> VolSurfaceAutoCalculationResponseTag(int vs_id) =>
|
||||
_mk_tag<VolSurfaceCalculationResponse, int>(vs_id);
|
||||
|
||||
// When the UI intends to save the vol surface (data or input)
|
||||
// Publisher: VolSurfaceDisplayModel
|
||||
public static ITag<VolSurfaceInput> VolSurfaceCommitTag(int vs_id) =>
|
||||
|
||||
@@ -485,6 +485,10 @@
|
||||
Width="{TemplateBinding Width}"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MaxWidth="{TemplateBinding MaxWidth}"
|
||||
TickUpCommand="{TemplateBinding TickUpCommand}"
|
||||
TickUpCommandParameter="{TemplateBinding TickUpCommandParameter}"
|
||||
TickDownCommand="{TemplateBinding TickDownCommand}"
|
||||
TickDownCommandParameter="{TemplateBinding TickDownCommandParameter}"
|
||||
>
|
||||
<TextBox
|
||||
Name="PART_TextBox"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
namespace mbts.ui.controls {
|
||||
using bts.api.domain;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.utilities;
|
||||
@@ -228,6 +229,12 @@ namespace mbts.ui.controls {
|
||||
get => _explicit_props.value;
|
||||
set => _explicit_props.value = value;
|
||||
}
|
||||
|
||||
bool _has_valid_bounds =>
|
||||
Minimum < Maximum;
|
||||
|
||||
T _default_value =>
|
||||
_has_valid_bounds ? T.Zero.within_range(Minimum, Maximum) : T.Zero;
|
||||
|
||||
public override void OnApplyTemplate() {
|
||||
Disposable.dispose(ref _text_hooks);
|
||||
@@ -272,7 +279,7 @@ namespace mbts.ui.controls {
|
||||
var v = _value_d;
|
||||
if (!v.HasValue) {
|
||||
if (AllowNullTick) {
|
||||
SetCurrentValue(ValueProperty, T.Zero);
|
||||
SetCurrentValue(ValueProperty, _default_value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -280,6 +287,10 @@ namespace mbts.ui.controls {
|
||||
TickUpDownHelper.nullable_tick_up(v, _increment_d, out var result_d, _maximum_d) :
|
||||
TickUpDownHelper.nullable_tick_down(v, _increment_d, out result_d, _minimum_d)) {
|
||||
var result = NumericCastOutConverter<T>.convert(result_d.assert_value());
|
||||
// Enforce min/max bounds (if bounds are valid)
|
||||
if (_has_valid_bounds) {
|
||||
result = result.within_range(Minimum, Maximum);
|
||||
}
|
||||
SetCurrentValue(ValueProperty, result);
|
||||
CoerceValue(ValueProperty);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace bts.reactive {
|
||||
namespace Spark.Core.Reactive {
|
||||
using System;
|
||||
|
||||
public static class ObserverExtensions {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace bts.reactive {
|
||||
namespace Spark.Core.Reactive {
|
||||
using System;
|
||||
using System.Reactive;
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace bts.reactive {
|
||||
throw new ArgumentNullException(nameof(selector));
|
||||
}
|
||||
|
||||
return new AnonymousObserver<TOuter>(outer_val => observer.OnNext(selector(outer_val)), observer.OnError, observer.OnCompleted);
|
||||
return new SelectorObserver<TOuter, TInner>(observer, selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Spark.Core.Reactive {
|
||||
using System;
|
||||
|
||||
public static class SelectorObserver {
|
||||
public static SelectorObserverBuilder<T> builder<T>(IObserver<T> observer) =>
|
||||
new(observer);
|
||||
}
|
||||
|
||||
public class SelectorObserver<T, TResult> : IObserver<T> {
|
||||
readonly IObserver<TResult> _observer;
|
||||
readonly Func<T, TResult> _selector;
|
||||
|
||||
public SelectorObserver(IObserver<TResult> observer, Func<T, TResult> selector) {
|
||||
_observer = observer;
|
||||
_selector = selector;
|
||||
}
|
||||
|
||||
public void OnCompleted() =>
|
||||
_observer.OnCompleted();
|
||||
|
||||
public void OnError(Exception error) =>
|
||||
_observer.OnError(error);
|
||||
|
||||
public void OnNext(T value) =>
|
||||
_observer.OnNext(_selector(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Spark.Core.Reactive {
|
||||
using System;
|
||||
|
||||
public class SelectorObserverBuilder<TResult> {
|
||||
readonly IObserver<TResult> _observer;
|
||||
|
||||
public SelectorObserverBuilder(IObserver<TResult> observer) =>
|
||||
_observer = observer;
|
||||
|
||||
public SelectorObserver<T, TResult> wrap<T>(Func<T, TResult> selector) =>
|
||||
new(_observer, selector);
|
||||
}
|
||||
}
|
||||
@@ -310,8 +310,6 @@ def build(
|
||||
build_cmd_steps.append(f'cmake --build {build_dir} --config {configuration}')
|
||||
build_cmd_steps.append(f'dotnet publish -c Release -r win-x64 --sc {t.path}')
|
||||
libspark_path=build_dir.joinpath("src", "lib", configuration, "libspark.dll")
|
||||
print(f'libspark-src: {libspark_path}')
|
||||
print(f'libspark-dst: {bins.joinpath("win-x64", "publish")}')
|
||||
build_cmd_steps.append(
|
||||
f'copy {libspark_path} {bins.joinpath("win-x64", "publish")}')
|
||||
|
||||
|
||||
Reference in new issue
Block a user