Compare commits

...
Author SHA1 Message Date
John Colvin 9fbf84eeb7 fix (ui): Change qty to double in Manual Fills Import dialog 2025-08-18 12:11:22 -05:00
John Colvin 0bea6e223a fix (ui): Show decimal places in Manual Fills Qty column 2025-08-18 12:11:22 -05:00
John Colvin 005207e061 refactor: Expose price/qty display settings as Security ext fns
Previously, these display settings were only available via SecurityMD objs.
However, many data sources (e.g., orders, fills, etc.) are joined with regular
Security objs (not SecurityMD). This saves us from doing awkward joins to get
the SecurityMD to get the display settings.
2025-08-18 12:11:13 -05:00
5 changed files with 32 additions and 31 deletions

No files matched your search

+3 -22
View File
@@ -1,7 +1,6 @@
#nullable enable
namespace bts {
using bts.api;
using bts.api.domain;
using bts.api.synthetic;
using bts.md;
using bts.spark;
@@ -133,31 +132,13 @@ namespace bts {
// set 'unit quantity' for each sec
foreach (var smd in exch_secs.Values) {
var (priceDisplay, qtyDisplay) = GetDisplaySettings(smd.security);
var meta = _security_metadata.get(smd.security);
var qtyDisplay = smd.security.QuantityDisplaySettings(meta.HasValue && meta.Value.HasQuantityDisplay ? meta.Value.QuantityDisplay : null);
var priceDisplay = smd.security.PriceDisplaySettings(meta.HasValue && meta.Value.HasPriceDisplay ? meta.Value.PriceDisplay : null);
exch_secs[smd.id] = new(smd.security, smd.md, priceDisplay, qtyDisplay);
}
_bus.post(SparkTags.ServerSecuritiesTag, exch_secs);
}
(TickDisplaySettingsImmutable? Price, TickDisplaySettingsImmutable? Quantity) GetDisplaySettings(SecurityImmutable sec) {
var meta = _security_metadata.get(sec);
var priceDisplay = meta.HasValue && meta.Value.HasPriceDisplay ? meta.Value.PriceDisplay : Security.Defaults.price_display_settingsi;
var qtyDisplay = meta.HasValue && meta.Value.HasQuantityDisplay ? meta.Value.QuantityDisplay : Security.Defaults.quantity_display_settingsi;
if (sec.HasContractSpec) {
var pd = priceDisplay.CloneToMutable();
pd.TickFactors = TickUtils.get_tick(sec, 0).to_api_tick_factors();
priceDisplay = new(pd);
if (sec.ContractSpec.QuantityInfos.TryGetFirst(out var qtyInfo)) {
var qd = qtyDisplay.CloneToMutable();
qd.ShowDecimalPart = qtyInfo.TickBase.is_real_non_zero() && !(qtyInfo.TickUnit / qtyInfo.TickBase).is_int();
qd.TickFactors ??= new();
qd.TickFactors.TickBase = qtyInfo.TickBase;
qd.TickFactors.TickUnit = qtyInfo.TickUnit;
qtyDisplay = new(qd);
}
}
return (priceDisplay, qtyDisplay);
}
}
public void Dispose() =>
@@ -58,12 +58,8 @@ namespace bts.shell.manual_fills {
_quantity.actual_value = data.value.manual_fill.Quantity;
_quantity_display = backing(
nameof(quantity_display),
() => {
return Security.Defaults.quantity_display_settingsi.with(tick_factors: new() {
TickBase = 1000000,
TickUnit = 1
});
});
() => Security.Defaults.quantity_display_settingsi.with(
show_decimal_part: true, tick_factors: new() { TickBase = 1000000, TickUnit = 1 }));
// TODO (prob): remove conversion_data. Replace get_qty_display_divisor delegates w/
// TickDisplaySettingsImmutable for qty, obtained via ISecurityMetadataStore.
_quantity.conversion_data = new(1, QuantityConversionOpType.DivideActual);
@@ -61,7 +61,7 @@
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="{x:Static Quantity, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
<DataTemplate>
<c:TickUpDownUInt Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
<c:TickUpDownDouble Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="{x:Static Price, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
@@ -39,7 +39,7 @@ namespace bts.shell.manual_fills {
case ManualFillComponent.Account: return (mf, val) => mf.AccountId = ConvertUtils.try_convert<AccountTuple?>(val)?.id ?? 0;
case ManualFillComponent.User: return (mf, val) => mf.UserId = ConvertUtils.try_convert<UserTuple?>(val)?.id ?? 0;
case ManualFillComponent.Side: return (mf, val) => mf.Side = ConvertUtils.try_convert<Side>(val, Side.Undefined);
case ManualFillComponent.Quantity: return (mf, val) => mf.Quantity = ConvertUtils.try_convert<uint>(val);
case ManualFillComponent.Quantity: return (mf, val) => mf.Quantity = ConvertUtils.try_convert<double>(val);
case ManualFillComponent.Price: return (mf, val) => mf.Price = ConvertUtils.try_convert(val, double.NaN);
default: throw new NotImplementedException();
}
@@ -130,7 +130,7 @@ namespace bts.shell.manual_fills {
}
public static void quantity(ref ParsedManualFill pmf, string cell_text) {
if (uint.TryParse(cell_text, out uint v)) {
if (double.TryParse(cell_text, out var v)) {
pmf.result.Quantity = v;
pmf.quantity = (cell_text, true);
} else {
@@ -2,6 +2,7 @@ namespace bts.api {
using bts.api.domain;
using bts.utils;
using mbts;
using mbts.spark;
using Spark.Api.Data;
@@ -124,6 +125,29 @@ namespace bts.api {
return double.NaN;
}
public static TickDisplaySettingsImmutable PriceDisplaySettings(this SecurityImmutable sec, TickDisplaySettingsImmutable? defaults = null) {
var priceDisplay = defaults ?? Security.Defaults.price_display_settingsi;
if (sec.HasContractSpec) {
var pd = priceDisplay.CloneToMutable();
pd.TickFactors = TickUtils.get_tick(sec, 0).to_api_tick_factors();
priceDisplay = new(pd);
}
return priceDisplay;
}
public static TickDisplaySettingsImmutable QuantityDisplaySettings(this SecurityImmutable sec, TickDisplaySettingsImmutable? defaults = null) {
var qtyDisplay = defaults ?? Security.Defaults.quantity_display_settingsi;
if (sec.HasContractSpec && sec.ContractSpec.QuantityInfos.TryGetFirst(out var qtyInfo)) {
var qd = qtyDisplay.CloneToMutable();
qd.ShowDecimalPart = qtyInfo.TickBase.is_real_non_zero() && !(qtyInfo.TickUnit / qtyInfo.TickBase).is_int();
qd.TickFactors ??= new();
qd.TickFactors.TickBase = qtyInfo.TickBase;
qd.TickFactors.TickUnit = qtyInfo.TickUnit;
qtyDisplay = new(qd);
}
return qtyDisplay;
}
internal static ExpirationDate earliest_expiration(this SecurityImmutable security) =>
security.normal_expirations().Min();