internal void ToSerie(StockSerie stockSerie, StockSerie referenceSerie = null) { float open = 0.0f; float high = 0.0f; float low = 0.0f; float close = 0.0f; int volume = 1; float cash = TotalDeposit; Dictionary<string, PositionValues> stockPositionDico = new Dictionary<string, PositionValues>(); // Statistics int nbTrades = 0; int nbWinTrades = 0; float maxDrawdown = float.MaxValue; float maxGain = float.MinValue; float maxLoss = float.MinValue; if (referenceSerie == null) { referenceSerie = StockDictionary.StockDictionarySingleton["CAC40"]; } referenceSerie.Initialise(); foreach (DateTime date in referenceSerie.GetValues(StockSerie.StockBarDuration.Daily).Where(d => d.DATE.Year > 2014).Select(v => v.DATE.Date)) { // Calculate open value // Retrieve orders for this date/time var orderList = this.OrderList.FindAll(order => order.ExecutionDate.Date == date).OrderBy(o => o.ID); // Manage new orders foreach (StockOrder stockOrder in orderList) { int numberOfShare = stockOrder.IsShortOrder ? -stockOrder.Number : stockOrder.Number; if (stockOrder.IsBuyOrder()) // Buy position { cash -= stockOrder.TotalCost; if (stockPositionDico.ContainsKey(stockOrder.StockName)) { stockPositionDico[stockOrder.StockName].Position += numberOfShare; stockPositionDico[stockOrder.StockName].OpenValue = (stockPositionDico[stockOrder.StockName].Position * stockPositionDico[stockOrder.StockName].OpenValue + numberOfShare * stockOrder.Value) / (stockPositionDico[stockOrder.StockName].Position + numberOfShare); } else { if (stockDictionary.ContainsKey(stockOrder.StockName) && stockDictionary[stockOrder.StockName].Initialise()) { stockPositionDico.Add(stockOrder.StockName, new PositionValues(numberOfShare, stockOrder.Value, stockDictionary[stockOrder.StockName].GetValues(StockSerie.StockBarDuration.Daily))); } else { StockLog.Write("Initialisation failed: " + stockOrder.StockName); stockPositionDico.Add(stockOrder.StockName, new PositionValues(numberOfShare, stockOrder.Value, null)); } } } else // Closing Position { if (stockPositionDico.ContainsKey(stockOrder.StockName)) { cash += stockOrder.TotalCost; PositionValues position = stockPositionDico[stockOrder.StockName]; if (position.Position == numberOfShare) { maxDrawdown = Math.Min(maxDrawdown, position.MaxDrawdown); stockPositionDico.Remove(stockOrder.StockName); nbTrades++; } else { position.Position -= numberOfShare; } if (stockOrder.IsShortOrder) { if (position.OpenValue > stockOrder.Value) { nbWinTrades++; maxGain = Math.Max(maxGain, (position.OpenValue - stockOrder.Value) / position.OpenValue); } else { maxLoss = Math.Max(maxLoss, -(position.OpenValue - stockOrder.Value) / position.OpenValue); } } else { if (position.OpenValue < stockOrder.Value) { nbWinTrades++; maxGain = Math.Max(maxGain, -(position.OpenValue - stockOrder.Value) / position.OpenValue); } else { maxLoss = Math.Max(maxLoss, (position.OpenValue - stockOrder.Value) / position.OpenValue); } } } else { // Open short position cash += stockOrder.TotalCost; if (stockDictionary.ContainsKey(stockOrder.StockName) && stockDictionary[stockOrder.StockName].Initialise()) { stockPositionDico.Add(stockOrder.StockName, new PositionValues(-numberOfShare, stockOrder.Value, stockDictionary[stockOrder.StockName].GetValues(StockSerie.StockBarDuration.Daily))); } else { StockLog.Write("Initialisation failed: " + stockOrder.StockName); stockPositionDico.Add(stockOrder.StockName, new PositionValues(-numberOfShare, stockOrder.Value, null)); } //throw new System.Exception("Sell order found on non bought stock " + stockOrder.StockName + " in " + this.Name); // @@@@ Need to have proper error manegement otherwise the applications crashes. //return referenceSerie; } } } // Calculate new value after taking into account the orders. low = cash; high = cash; close = cash; open = cash; if (stockPositionDico.Count != 0) { foreach (PositionValues position in stockPositionDico.Values) { StockDailyValue currentValue = position.AtDate(date); if (currentValue == null) { // Position on stock not in dico if (position.Position > 0) { close += position.OpenValue * position.Position; open += position.OpenValue * position.Position; low += position.OpenValue * position.Position; high += position.OpenValue * position.Position; } } else { // Position on stock in dico close += currentValue.CLOSE * position.Position; open += currentValue.OPEN * position.Position; if (position.Position > 0) { position.MaxValue = Math.Max(position.MaxValue, currentValue.HIGH); position.MinValue = Math.Min(position.MinValue, currentValue.LOW); low += currentValue.LOW * position.Position; high += currentValue.HIGH * position.Position; } else { // We are facing a short order, everything is reversed low += currentValue.HIGH * position.Position; high += currentValue.LOW * position.Position; position.MaxValue = Math.Max(position.MaxValue, currentValue.LOW); position.MinValue = Math.Min(position.MinValue, currentValue.HIGH); } } } } StockDailyValue dailyValue = new StockDailyValue(stockSerie.StockName, open, high, low, close, volume, date); stockSerie.Add(date, dailyValue); dailyValue.Serie = stockSerie; } StockLog.Write("Statistics for " + stockSerie.StockName); StockLog.Write("NbTrades: " + nbTrades); StockLog.Write("Win %: " + ((float)nbWinTrades / (float)nbTrades).ToString("P2")); StockLog.Write("MaxDrowdown: " + maxDrawdown.ToString("P2")); StockLog.Write("MaxGain: " + maxGain.ToString("P2")); StockLog.Write("MaxLoss: " + maxLoss.ToString("P2")); }
public override bool LoadIntradayDurationArchiveData(string rootFolder, StockSerie serie, StockSerie.StockBarDuration duration) { StockLog.Write("LoadIntradayDurationArchiveData Name:" + serie.StockName + " duration:" + duration); string durationFileName = rootFolder + ARCHIVE_FOLDER + "\\" + duration + "\\" + serie.ShortName.Replace(':', '_') + "_" + serie.StockName + "_" + serie.StockGroup.ToString() + ".txt"; if (File.Exists(durationFileName)) { var values = serie.GetValues(duration); if (values == null) StockLog.Write("LoadIntradayDurationArchiveData Cache File Found, current size is: 0"); else StockLog.Write("LoadIntradayDurationArchiveData Cache File Found, current size is: " + values.Count); serie.ReadFromCSVFile(durationFileName, duration); StockLog.Write("LoadIntradayDurationArchiveData New serie size is: " + serie.GetValues(duration).Count); if (serie.GetValues(duration).Count > 0) { StockLog.Write("LoadIntradayDurationArchiveData First bar: " + serie.GetValues(duration).First().ToString()); StockLog.Write("LoadIntradayDurationArchiveData Last bar: " + serie.GetValues(duration).Last().ToString()); } else { return false; } } else { return false; } return true; }