static void Main(string[] args) { CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; var(sec, thresholds) = ParseArgs(args); var date = DateTime.Now; Console.WriteLine($"Now: {date:s}"); date = date.Date.AddDays(date < date.Date.AddHours(20) ? -1 : 0); //date = new DateTime(2018, 10, 1); Console.WriteLine($"Trade date: {date:s}"); var history = MoexApi.DownloadHistory(sec, date.AddDays(-60), date).ToArray(); if (history.Last().Date != date) { if (MoexApi.HasCandles(sec, date)) { throw new Exception($"No history entry for {date:yyyy-MM-dd}"); } Console.WriteLine($"No trades on {date:yyyy-MM-dd}"); return; } var indicator = RocEma.Calculate(history); var candidate = new TurnCandidate(indicator, indicator.Count - 2); Console.WriteLine(candidate); if (candidate.IsDip(thresholds.Low)) { Notify($"📈 '{sec.ID}' DIP on {date:yyyy-MM-dd}"); } if (candidate.IsPeak(thresholds.High)) { Notify($"📉 '{sec.ID}' PEAK on {date:yyyy-MM-dd}"); } Console.WriteLine(); }
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)); }