public ExperimentResult(Thresholds thresholds, double xirr, int tradeCount, int slipCount) { Thresholds = thresholds; XIrr = xirr; TradeCount = tradeCount; SlipCount = slipCount; }
static ExperimentResult RunExperiment(IReadOnlyList <Day> history, Thresholds thresholds, Action <string> logger = null) { var tradeCount = 0; var slipCount = 0; var cash = 0m; var depo = new List <decimal>(); var flowDates = new List <DateTime>(); var flowValues = new List <decimal>(); for (var i = 2; i < history.Count; i++) { var isLastDay = i == history.Count - 1; var day = history[i]; var date = day.Date.ToString("yyyy-MM-dd"); var price = day.Close; var candidate = new TurnCandidate(history, i - 1); if (!isLastDay && candidate.IsDip(thresholds.Low)) { var buyCount = 1; var requiredCash = buyCount * price; if (cash < requiredCash) { var topup = requiredCash - cash; flowDates.Add(day.Date); flowValues.Add(topup); cash += topup; logger?.Invoke($"{date} Top-up {topup}"); } cash -= requiredCash; depo.AddRange(Enumerable.Repeat(price, buyCount)); tradeCount++; logger?.Invoke($"{date} Buy {buyCount} x {price}"); } if (isLastDay || candidate.IsPeak(thresholds.High)) { if (depo.Any()) { var sellCount = depo.Count; var investedValue = depo.Take(sellCount).Sum(); var currentValue = sellCount * price; var earning = currentValue - investedValue; if (isLastDay || earning > 0) { cash += currentValue; depo.RemoveRange(0, sellCount); tradeCount++; logger?.Invoke($"{date} Sell {sellCount} x {price}, earn {earning}"); } else { logger?.Invoke($"{date} Hold"); } } else { slipCount++; logger?.Invoke($"{date} Nothing to sell"); } } } if (depo.Any()) { throw new Exception(); } var xirr = 0d; if (flowValues.Any()) { flowDates.Add(history.Last().Date); flowValues.Add(-cash); xirr = Excel.FinancialFunctions.Financial.XIrr(flowValues.Select(Convert.ToDouble), flowDates); } return(new ExperimentResult(thresholds, xirr, tradeCount, slipCount)); }