/// <summary> /// Duplicate carets to remove. Happens if multiple selection are on same line /// and hitting home/end /// </summary> internal void RemoveDuplicates() { if (Selections .GroupBy(s => s.Caret.GetPoint(Snapshot).Position) .Any(s => s.Count() > 1)) { var distinctSelections = Selections .GroupBy(s => s.Caret.GetPoint(Snapshot).Position) .Select(s => s.First()).ToList(); Selections = distinctSelections; } }
/// <summary> /// Runs backtest for all Selections and numeric Parameters /// </summary> /// <param name="pushResultsToSignal">Update signal's backtest results during execution</param> /// <returns>Collection of backtest results per each selection and parameters combination</returns> public List <BacktestResults> Backtest(bool pushResultsToSignal = true) { if (_backtestNestingLevel > 2) { Alert("Won't run backtest: possible infinite recursion loop"); return(null); } var resetBusyFlag = !_isBusy; _isBusy = true; var results = new List <BacktestResults>(); if (pushResultsToSignal) { BacktestProgress = 0F; BacktestResults.Clear(); } lock (_locker) { try { _backtestNestingLevel++; //run backtest for each instrument using each possible parameters combinations var paramSpaces = GetParamCombinations(_origParameters); var paramNames = GetParamNames(_origParameters); var paramCombinations = CartesianProduct(paramSpaces); var total = paramCombinations.Count() * Selections.Select(i => i.MarketDataSlot).Distinct().Count(); results.Capacity = total; var current = 0; foreach (var slot in Selections.GroupBy(i => i.MarketDataSlot)) { foreach (var paramsCombo in paramCombinations) { while (State == SignalState.BacktestingPaused) { System.Threading.Thread.Sleep(50); } if (State == SignalState.Stopped) { return(results); } try { var allTrades = BacktestSlotItem(slot, paramsCombo); var progress = (++current / (float)total) * 100F; var result = new BacktestResults { SignalName = this.Name, Slot = slot.Key, Index = current, StartDate = BacktestSettings.StartDate, EndDate = BacktestSettings.EndDate, TotalProgress = progress }; for (var i = 0; i < paramNames.Length; i++) { result.Parameters.Add(paramNames[i] + "|" + paramsCombo.ElementAt(i).ToString()); } if (allTrades != null && allTrades.Count > 0 && State != SignalState.Stopped) { foreach (var symTrades in allTrades.GroupBy(i => new { i.Instrument.Symbol, i.Instrument.Timeframe, i.Instrument.TimeFactor })) { var key = symTrades.First().Instrument; var summary = GetBacktestSummary(key, symTrades); summary.TradesCompressed = CompressTrades(symTrades); result.Summaries.Add(summary); } } results.Add(result); if (pushResultsToSignal) { BacktestResults.Add(result); BacktestProgress = progress; } } catch (Exception e) { Alert($"Failed to run backtest for {Name} on slot #{slot.Key}: {e.Message}"); } } //^ parameters loop } //^ slots loop } catch (Exception e) { System.Diagnostics.Trace.TraceError($"Failed to run backtest for {Name}: {e.Message}"); } finally { if (pushResultsToSignal) { BacktestProgress = 100F; } if (resetBusyFlag) { _isBusy = false; } _backtestNestingLevel--; } } //^ lock return(results); }