- ago
Greetings WealthLabbers: I am currently iterating on the development of a WealthLabClientExtension. I have run the extension against ~60% of the datasets provided by WealthLab with no issue. I do have a subscription to NorgateData and some of those datasets are where the issue arises. Specifically, any of the datasets appended with "Current & Past" cause the exception "Could not obtain Benchmark Data for Symbol: SPY" to be raised. Unfortunately, the datasets appended with "Current & Past" are also the most interesting to me ;-)

As you can see in the code fragment below, I specifically load the BenchmarkSymbol and BenchmarkData prior to the call to RunBacktest(). I can also attest that the WLHOST.Instance.GetHistory() call successfully returned a BarHistory for for the StrategyRunner. Could you take a look at the code below and see if you see something blatantly obvious? I find it suspicious that it is only Norgate datasets with "Current & Past" which cause the exception.

CODE:
private async void RunBtn_Click(object sender, RoutedEventArgs e) { // --- Validate inputs ------------------------------------------------ string stratName = StrategyBox.Text.Trim(); if (string.IsNullOrEmpty(stratName)) { MessageBox.Show("Please enter a strategy name.", "Monte Carlo Lab", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (!double.TryParse(CapitalBox.Text, out double capital) || capital <= 0) { MessageBox.Show("Please enter a valid starting capital.", "Monte Carlo Lab", MessageBoxButton.OK, MessageBoxImage.Warning); return; } int numRuns = int.TryParse(RunsBox.Text, out int rv) && rv >= 10 ? rv : 1000; int years = int.TryParse(YearsBox.Text, out int yv) && yv >= 1 ? yv : 10; // --- Lock UI -------------------------------------------------------- RunBtn.IsEnabled = false; CancelBtn.IsEnabled = true; _cts = new CancellationTokenSource(); ProgressBar.Maximum = numRuns; ProgressBar.Value = 0; StatusText.Text = "Running base backtest…"; _result = null; StatsGrid.ItemsSource = null; ChartCanvas.Children.Clear(); try { // ── 1. Base backtest ───────────────────────────────────────────── var sr = new StrategyRunner(); sr.PositionSize.StartingCapital = capital; sr.DataRange = new DataRange(DataRangeType.YearRange, 0, DateTime.Now.Date.AddYears(-10), DateTime.Now.Date); // sr.DataRange.DataRangeType = DataRangeTypes.RecentYears; sr.DataRange.RecentValue = years; if (DatasetCombo.SelectedItem is DataSet ds) sr.DataSet = ds; // Explicitly pre-load the benchmark so StrategyRunner doesn't have // to fetch it internally, avoiding the intermittent SPY data error. StatusText.Text = "Loading benchmark data…"; DateTime benchStart = DateTime.Now.Date.AddYears(-years); DateTime benchEnd = DateTime.Now.Date; BarHistory? benchmark = WLHost.Instance.GetHistory( "SPY", HistoryScale.Daily, benchStart, benchEnd, years * 265, null); if (benchmark != null) { StatusText.Text = "Benchmark data loaded: " + benchmark.Count + " bars."; sr.BenchmarkData = benchmark; MessageBox.Show( $"Benchmark data for SPY loaded successfully.\n\n" + $"Bars: {benchmark.Count}\n" + $"Date range: {benchmark.StartDate:d} to {benchmark.EndDate:d}", "Monte Carlo Lab", MessageBoxButton.OK, MessageBoxImage.Information); } else { StatusText.Text = "Failed to load benchmark data."; MessageBox.Show( "Failed to load benchmark data for SPY.\n\n" + "Please verify your dataset selection and internet connection.", "Monte Carlo Lab", MessageBoxButton.OK, MessageBoxImage.Warning); } // StrategyRunner.RunBacktest is thread-safe and may be called from // a background thread (WL8 uses this for optimization runs). StatusText.Text = "Running base backtest…"; Backtester bt = await Task.Run( () => sr.RunBacktest(stratName), _cts.Token); if (bt is null || bt.Positions.Count == 0) { StatusText.Text = "No trades found."; MessageBox.Show( "The backtest produced no closed trades.\n\n" + "Please verify:\n" + " • The strategy name exactly matches a saved strategy\n" + " • The selected dataset and date range are appropriate\n" + " • The strategy generates signals over the selected period", "Monte Carlo Lab", MessageBoxButton.OK, MessageBoxImage.Warning); return; } // ── 2. Extract trade data ──────────────────────────────────────── var positions = new List<Position>(bt.Positions.Count); foreach (Position p in bt.Positions) positions.Add(p); StatusText.Text = $"Base backtest: {positions.Count} trades. Starting {numRuns:N0} MC runs…"; var profits = positions.Select(p => p.Profit).ToList(); DateTime t0 = positions.Min(p => p.EntryDate); DateTime t1 = positions.Max(p => p.ExitDate); double startCap = bt.StartingCapital > 0 ? bt.StartingCapital : capital; // Read baseline metrics. WL8 Metrics is a dynamic property; individual // metrics are only present if the matching ScoreCard is installed. double baseNetPnlPct = 0, baseMaxDD = 0, baseAPR = 0; try { baseNetPnlPct = (double)bt.Metrics.NetProfit / startCap * 100.0; } catch { /* ScoreCard not installed */ } try { baseMaxDD = (double)bt.Metrics.MaxDrawdownPct; } catch { } try { baseAPR = (double)bt.Metrics.APR; } catch { } // ── 3. Monte Carlo simulation ──────────────────────────────────── var progress = new Progress<int>(v => { ProgressBar.Value = v; StatusText.Text = $"Monte Carlo: {v:N0} / {numRuns:N0}"; }); _result = await Task.Run(() => new MonteCarloEngine().Run( profits, startCap, numRuns, t0, t1, progress, _cts.Token)); _result.BaseNetPnlPct = baseNetPnlPct; _result.BaseMaxDDPct = baseMaxDD; _result.BaseAPR = baseAPR; // ── 4. Display ─────────────────────────────────────────────────── DisplayResults(_result); StatusText.Text = $"Done — {numRuns:N0} runs · {positions.Count} trades · " + $"{_result.Years:F1} yrs · {_result.ProbabilityOfProfit:F1}% profitable"; } catch (OperationCanceledException) { StatusText.Text = "Cancelled."; } catch (Exception ex) { StatusText.Text = "Error — see details."; MessageBox.Show( $"Error running Monte Carlo:\n\n{ex.Message}", "Monte Carlo Lab", MessageBoxButton.OK, MessageBoxImage.Error); } finally { RunBtn.IsEnabled = true; CancelBtn.IsEnabled = false; _cts?.Dispose(); _cts = null; } }
0
60
1 Replies

Reply

Bookmark

Sort
Cone8
 ( 12.18% )
- ago
#1
Probably you're testing delisted symbols whose End date is before SPY's Start date. Instead use Norgate's $SPX (or Y!'s ^GSPC) for the benchmark that will have more history.

Sometimes you just need to believe what the message is telling you ;)
1

Reply

Bookmark

Sort