Compare commits

...
4 Commits
Author SHA1 Message Date
John Colvin d24becd052 fix: Ensure LadderGridHost's price_comparer isn't null
Initialize it w/ reasonable default when LadderGridHost is constructed.

Bugs (fixed):
  * Sometimes the OrderBook's ladder panel throws a null ref ex when the
    orders collection changes

  Cause: The orders collection changed callback references the ladder's
  price_comparer, which may be null since it isn't initialized until the
  DataContextChanged event fires (the relative order of these events is
  non-deterministic)
2023-11-02 14:15:58 -04:00
John Colvin 13251f5c06 fix: Add special-case tick logic for [non-perp] futs at Deribit
Unlike any other securities supported by our system, these use
TickFactors of 25/10 (e.g., 10.0, 12.5, 15.0, etc.)
2023-11-02 13:55:36 -04:00
John Colvin 03b9aaa0e8 fix: Dispose BBOVM's md poll loop
Bugs (fixed):
  * Memory Leak! The md poll loop (instance var) in BBOVM is never
    disposed
2023-11-02 12:47:58 -04:00
Mohammad Arham 32d127fd84 added log to chec user input 2023-10-18 09:34:00 -07:00
6 changed files with 73 additions and 6 deletions

No files matched your search

@@ -70,7 +70,7 @@ namespace mbts.analytics {
}
public void set_vol_data(VolSurfaceInput vs_input) {
Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_vol_data vs_id: {_vs_id}");
Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_vol_data vs_id: {_vs_id} precomputed_theo_handles?: {string.Join(", ", (vs_input?.surface_settings?.advanced_settings?.precomputed_theo_handles).empty_if_null())}");
_vs_input = vs_input != null ? VolSurfaceUtilityFunctions.clone_proto(vs_input) : null;
_compute_and_fire(InputType.Vol);
}
@@ -87,7 +87,7 @@ namespace mbts.spark {
length = _initial_row_count,
};
LadderPriceList _pending_price_list = null;
IComparer<double> _price_comparer;
IComparer<double> _price_comparer = MathUtils.epsilon_comparer.reverse();
double _last_origin_price;
readonly MarketBreakLines _mkt_break_lines;
NativePriceSize _last_trade;
@@ -14,6 +14,9 @@ namespace mbts.spark {
uint _bid_size, _ask_size;
ulong _time;
public BBOVM() =>
_dispose = Disposable.Create(() => Disposable.dispose(ref _md_poll));
public SecurityImmutable? security => _security_md?.security;
public SecurityMD? security_md {
get => _security_md;
@@ -63,7 +63,11 @@ namespace mbts {
if (ticks_per_base.is_int()) {
return true;
}
return tick.tick_unit > tick.tick_base && tick.tick_base == 1;
// The Log10 check accounts for [non-perp] futures at Deribit, which use TickFactors 25/10
// (e.g., 10.0, 12.5, 15.0, etc.). It was added 11/02/2023, when it replaced the
// commented out line below.
// return tick.tick_unit > tick.tick_base && tick.tick_base == 1;
return tick.tick_unit > tick.tick_base && Math.Log10(tick.tick_base).is_int();
}
public static bool is_valid(this RegionalTickFactors regional_tick) =>
@@ -667,7 +671,8 @@ namespace mbts {
if (negate) {
value = -value;
}
var text = core_impl();
var text = tick.tick_unit == 25 && tick.tick_base == 10 ?
deribit_future_impl() : core_impl();
if (!settings.show_zero_integer) {
// Hide the integer if its zero. This could be better optimized,
// but speed shouldn't be an issue, so it doesn't seem worth the time/effort.
@@ -721,6 +726,37 @@ namespace mbts {
return "Overflow";
}
}
string deribit_future_impl() {
try {
// TODO: Replace this hack with a suitable long term solution
//
// The hack: [non-perp] futures at Deribit tick in units of '2.5' (e.g., 10.0, 12.5,
// 15.0, etc.). However, this battle-tested impl is predicated on the tick base being
// a multiple of the tick unit. So, to avoid changing key battle-tested logic, let's
// do the following:
// 1) Round the value to the nearest multiple of '2.5' (the tick size)
// 2) Format the rounded value with normal tick factors (unit 5, base 10),
// (ticks of the sort this method was designed to expect).
var tuf = tick.tick_unit_frac<T>();
var total_ticks = value / tuf;
long total_ticks_l = NumericCastConverter<T, long>.convert(value / tuf);
var rounded_total_ticks = NumericCastConverter<long, T>.convert(total_ticks_l);
var rem = value - (rounded_total_ticks * tuf);
var unit_rem = rem / tuf;
Log.assert(unit_rem <= T.One && unit_rem >= T.Zero);
// Check if unit_rem closer to T.One, or T.Zero. Round accordingly
if (T.One - unit_rem <= unit_rem - T.Zero) {
// round up
rounded_total_ticks++;
}
var rounded_val = tuf * rounded_total_ticks;
return _format_tick(new TickFactors(10, 5), rounded_val, settings);
} catch (OverflowException ex) {
Log.Ui.warn($"Arithmetic overflow exception in format_tick (tick:{tick}, value:{value}):{ex}");
return "Overflow";
}
}
}
static T _round_tick<T>(T tick_unit_frac, T input) where T : IFloatingPointIeee754<T> =>
@@ -134,8 +134,20 @@ namespace bts.data.ticks {
static TickFactors _inflate_to_base(TickFactors tick_factors, double new_base) =>
new TickFactors(new_base, tick_factors.tick_unit * new_base / tick_factors.tick_base);
static bool _check_tick_size(double tick_size) =>
tick_size > 0 && (tick_size <= 0.5 || MathUtils.is_int(tick_size));
static bool _check_tick_size(double tick_size) {
if (!tick_size.is_real_positive()) {
return false;
}
if (tick_size <= 0.5 || tick_size.is_int()) {
return true;
}
// Check if tick size represents quarters (i.e., `tick_size=0.25 * 10^c` for some int c).
// Note: this check is designed specifically to support (non-perp) futures at Deribit
if (Math.Log10(tick_size / 0.25).is_int()) {
return true;
}
return false;
}
struct SpecKey : IEquatable<SpecKey> {
public readonly uint denominator, display_digits;
@@ -209,6 +209,17 @@ namespace mbts.spark {
}
public static decimal round_to_tick(decimal tick_size, decimal value) {
if (tick_size == (decimal)2.5) {
// This is a special case for [non-perp] futures at Deribit, which tick in 25/10
// (unit/base). While this code looks harmless enough, and should work for all ticks in
// theory, let's be extra safe and only apply the change to a specific case (when
// tick_size == 2.5).
// Note: Making the ticks code work for such a wide range of tick factors was no easy
// task, and involved overcoming a plethora of obscure rounding errors along the way.
var n_ticks = value / tick_size;
var simple_round = tick_size * n_ticks;
return simple_round;
}
decimal intpart = (long)value;
decimal floatpart = value % 1;
floatpart = ((int)(floatpart / tick_size)) * tick_size;
@@ -293,6 +304,11 @@ namespace mbts.spark {
if (!tf.low_tick.Equals(tf.high_tick) || !tf.is_valid() || tf.low_tick.is_integer())
return null;
var ticks_per_dollar = (uint)(tf.low_tick.tick_base / tf.low_tick.tick_unit);
// Note: this check was introduced specifically to account for [non-perp] Futures at
// Deribit, which use TickFactors 25/10 (e.g., 10.0, 12.5, 15.0, etc.).
if (ticks_per_dollar == 0) {
return null;
}
// if ticks_per_row is greater than ticks_per_dollar, round to a multiple of ticks_per_dollar
if (ticks_per_row > ticks_per_dollar) {
return (uint)(ticks_per_dollar * Math.Floor((double)(ticks_per_row / ticks_per_dollar)));