From 537480ad127c7595963375d4f520936890a94aa3 Mon Sep 17 00:00:00 2001 From: Paulo Date: Wed, 29 Apr 2026 19:18:04 -0400 Subject: [PATCH 01/33] Phase A: Kalshi-specific UI tweaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - vmOrderBook.cs: lock cumulative-depth X axes to 0-100 cents with title 'YES price (¢)' (binary-contract probability scale) - ucDepth1.xaml: rename 'Bids' → 'YES Bid', 'Asks' → 'YES Ask' headers (asks come from reflected NO bids per the plugin's mapping rule) - Disable VPIN, OTT_Ratio, MarketResilience studies — designed for continuous markets (informed-flow / toxic-trade detection); not meaningful on binary contracts. LOBImbalance kept (transfers cleanly). - Removed disabled study DLLs from runtime bin ScatterPoint patch from earlier session (oxyplot fork) carried forward. --- View/ucDepth1.xaml | 4 ++-- ViewModel/vmOrderBook.cs | 12 ++++++++++-- VisualHFT.csproj | 6 ++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/View/ucDepth1.xaml b/View/ucDepth1.xaml index 104a2e4f..e778d7f6 100644 --- a/View/ucDepth1.xaml +++ b/View/ucDepth1.xaml @@ -48,7 +48,7 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/View/KalshiStrikeLadderWindow.xaml.cs b/View/KalshiStrikeLadderWindow.xaml.cs new file mode 100644 index 00000000..f8944afc --- /dev/null +++ b/View/KalshiStrikeLadderWindow.xaml.cs @@ -0,0 +1,22 @@ +using System.Windows; +using VisualHFT.ViewModel; + +namespace VisualHFT.View +{ + /// + /// Standalone window showing live Kalshi top-of-book per strike, + /// grouped by event. Wired in Phase B of the Kalshi customization track. + /// + public partial class KalshiStrikeLadderWindow : Window + { + public KalshiStrikeLadderWindow() + { + InitializeComponent(); + DataContext = new vmKalshiStrikeLadder(); + this.Closed += (_, _) => + { + if (DataContext is vmKalshiStrikeLadder vm) vm.Dispose(); + }; + } + } +} diff --git a/ViewModel/vmKalshiStrikeLadder.cs b/ViewModel/vmKalshiStrikeLadder.cs new file mode 100644 index 00000000..b19d545e --- /dev/null +++ b/ViewModel/vmKalshiStrikeLadder.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Windows; +using VisualHFT.Helpers; +using VisualHFT.Model; + +namespace VisualHFT.ViewModel +{ + /// + /// One row in the Kalshi strike-ladder grid — top-of-book + derived + /// implied-probability fields for a single binary contract. + /// + public class KalshiStrikeRow : INotifyPropertyChanged + { + public string Ticker { get; set; } = ""; + public string EventTicker { get; set; } = ""; + public string Strike { get; set; } = ""; + + private double _yesBid; + public double YesBid + { + get => _yesBid; + set { _yesBid = value; Notify(nameof(YesBid)); Notify(nameof(Spread)); Notify(nameof(MidProb)); } + } + + private double _yesAsk; + public double YesAsk + { + get => _yesAsk; + set { _yesAsk = value; Notify(nameof(YesAsk)); Notify(nameof(Spread)); Notify(nameof(MidProb)); } + } + + public double Spread => Math.Max(0, YesAsk - YesBid); + public double MidProb => (YesBid > 0 && YesAsk > 0) ? Math.Round((YesBid + YesAsk) / 2.0, 1) : 0; + + private DateTime _lastUpdate; + public DateTime LastUpdate + { + get => _lastUpdate; + set { _lastUpdate = value; Notify(nameof(LastUpdate)); } + } + + public event PropertyChangedEventHandler? PropertyChanged; + private void Notify(string p) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(p)); + } + + /// + /// View-model for the Kalshi strike-ladder window. + /// Subscribes to every order-book update from any provider, filters to + /// Kalshi tickers (prefix 'KX'), and groups by event ticker into rows. + /// + public sealed class vmKalshiStrikeLadder : INotifyPropertyChanged, IDisposable + { + public ObservableCollection Strikes { get; } = new(); + private readonly ConcurrentDictionary _byTicker = new(); + private readonly Action _handler; + + public vmKalshiStrikeLadder() + { + _handler = OnBook; + HelperOrderBook.Instance.Subscribe(_handler); + } + + private void OnBook(OrderBook ob) + { + if (ob is null) return; + var symbol = ob.Symbol; + if (!IsKalshiTicker(symbol)) return; + + // Snapshot top-of-book outside the UI thread + var bids = ob.Bids; + var asks = ob.Asks; + double topBid = bids.Count() > 0 ? (bids[0].Price ?? 0) : 0; + double topAsk = asks.Count() > 0 ? (asks[0].Price ?? 0) : 0; + + var row = _byTicker.GetOrAdd(symbol, CreateRow); + + Application.Current?.Dispatcher.BeginInvoke(() => + { + if (!Strikes.Contains(row)) + { + InsertSorted(row); + } + row.YesBid = topBid; + row.YesAsk = topAsk; + row.LastUpdate = DateTime.Now; + }); + } + + private void InsertSorted(KalshiStrikeRow row) + { + // Insert keeping rows grouped by EventTicker, then sorted by strike text + var idx = Strikes.ToList() + .FindIndex(r => string.Compare(r.EventTicker + r.Strike, row.EventTicker + row.Strike, StringComparison.Ordinal) > 0); + if (idx < 0) Strikes.Add(row); + else Strikes.Insert(idx, row); + } + + private static KalshiStrikeRow CreateRow(string ticker) + { + // KXHIGHTATL-26APR29-B82.5 → event="KXHIGHTATL-26APR29", strike="B82.5" + int lastDash = ticker.LastIndexOf('-'); + string evt = lastDash > 0 ? ticker.Substring(0, lastDash) : ticker; + string strk = lastDash > 0 ? ticker.Substring(lastDash + 1) : ""; + return new KalshiStrikeRow { Ticker = ticker, EventTicker = evt, Strike = strk }; + } + + private static bool IsKalshiTicker(string s) => + !string.IsNullOrEmpty(s) && s.StartsWith("KX", StringComparison.OrdinalIgnoreCase); + + public event PropertyChangedEventHandler? PropertyChanged; + + public void Dispose() => HelperOrderBook.Instance.Unsubscribe(_handler); + } +} From 733b2ad51a609d47ff427183c400da7daec30ff8 Mon Sep 17 00:00:00 2001 From: Paulo Date: Wed, 29 Apr 2026 19:29:45 -0400 Subject: [PATCH 03/33] Fix Phase A/B regressions surfaced by user screenshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Revert hard-lock of cumulative-depth X axes to 0-100; auto-scale restored. Hard-lock broke non-Kalshi venues (KuCoin chart axes pinned to YES-price scale even when viewing BTC/USD etc). Conditional per-symbol locking deferred to Phase D. - Re-add 'Kalshi Strikes' button to Dashboard.xaml (previous edit did not persist — empty git diff for this file at Phase B commit time; handler in Dashboard.xaml.cs was already there). Companion change in visualhft-kalshi (separate repo): KalshiPlugin sets OrderBook.ProviderID=100, ProviderName='Kalshi' so the venue appears in VisualHFT's Providers' Status panel and is selectable as a venue throughout the app. --- View/Dashboard.xaml | 1 + ViewModel/vmOrderBook.cs | 15 +++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/View/Dashboard.xaml b/View/Dashboard.xaml index 8b97922a..d43f1c07 100644 --- a/View/Dashboard.xaml +++ b/View/Dashboard.xaml @@ -141,6 +141,7 @@