using MarketData.Cache; using MarketData.MarketDataModel; using System; using System.Collections.Generic; using System.Linq; namespace MarketData.Generator.MovingAverage { public static class ExponentialMovingAverageCrossover { public static MovingAverageCrossovers GetMovingAverageCrossovers(String symbol, DateTime analysisDate,double threshholdPercentDecimal=.05, int fastCrossOverDays=9, int slowCrossoverDays=41) { Prices prices = GBPriceCache.GetInstance().GetPrices(symbol, analysisDate, slowCrossoverDays * 6); if(prices == null || 0==prices.Count)return new MovingAverageCrossovers(); return GetMovingAverageCrossovers(prices, threshholdPercentDecimal, fastCrossOverDays, slowCrossoverDays); } /// /// GetMovingAverageCrossovers /// Prices will be in descending order with the most recent price at index[0] and the least recent price at index[count-1] /// /// /// The fast crossover. Tom Basso uses 9 /// The slow crosser. Tom Basso uses 41 /// The threshhold percent. 5% would be .05 /// public static MovingAverageCrossovers GetMovingAverageCrossovers(Prices prices, double threshholdPercentDecimal=.05, int fastCrossOverDays=9, int slowCrossoverDays=41) { MovingAverageCrossovers movingAverageCrossovers = new MovingAverageCrossovers(); if(prices == null || 0==prices.Count || prices.Count<=slowCrossoverDays)return movingAverageCrossovers; if(fastCrossOverDays >= slowCrossoverDays)return movingAverageCrossovers; DMAPrices fastDMAPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, fastCrossOverDays); DMAPrices slowDMAPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, slowCrossoverDays); if(null == fastDMAPrices || 0 == fastDMAPrices.Count)return movingAverageCrossovers; if(null == slowDMAPrices || 0 == slowDMAPrices.Count)return movingAverageCrossovers; FindCrossovers(fastDMAPrices, slowDMAPrices, movingAverageCrossovers, threshholdPercentDecimal); // put the most recent item in the lowest index movingAverageCrossovers = new MovingAverageCrossovers((movingAverageCrossovers as List).Where(x => x.IsChangeInTrend==true).OrderByDescending(x => x.EventDate).ToList()); return movingAverageCrossovers; } /// /// If the sorter term (faster) crossed the longer term (slower) moving average then a direction change is noted. /// If the change carried more than 5% in that direction then consider it a successful trend /// /// /// /// private static void FindCrossovers(DMAPrices fastDMAPrices, DMAPrices slowDMAPrices,MovingAverageCrossovers movingAverageCrossovers, double threshholdPercentDecimal) { double trendChangePercentThreshhold = threshholdPercentDecimal*100.00; // if a change in direction carries more than 5% then consider is a successful trend movingAverageCrossovers.Clear(); DateTime startDate = slowDMAPrices[slowDMAPrices.Count-1].Date; MovingAverageCrossover movingAverageCrossover = default; DMAPricesByDate fastDMAPricesByDate = fastDMAPrices.GetDMAPricesByDate(); DMAPricesByDate slowDMAPricesByDate = slowDMAPrices.GetDMAPricesByDate(); List availableDates = new List(slowDMAPricesByDate.Keys); availableDates.Sort((a, b) => DateTime.Compare(a,b)); // earliest date should be in the lowest index, most recent date should be in the highest 2024[0], 2025[1], 2026[2] for example int currentDirection=0; // 1:fast DMA is above the slow, -1:fast DMA is below the slow, 0:slow DMA == fastDMA DMAPrice prevSlowDMAPrice = default; DMAPrice prevFastDMAPrice = default; for(int index=0;index slowDMAPrice.AVGPrice) { movingAverageCrossover = new MovingAverageCrossover(); movingAverageCrossover.EventDate = analysisDate; movingAverageCrossover.Symbol = slowDMAPrice.Symbol; movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpStart; movingAverageCrossover.IsChangeInTrend = false; movingAverageCrossover.SlowDMAPrice = slowDMAPrice; movingAverageCrossover.FastDMAPrice = fastDMAPrice; movingAverageCrossover.ChangePercent=0.00; movingAverageCrossovers.Add(movingAverageCrossover); currentDirection = 1; } else if(fastDMAPrice.AVGPrice < slowDMAPrice.AVGPrice) { movingAverageCrossover = new MovingAverageCrossover(); movingAverageCrossover.EventDate = analysisDate; movingAverageCrossover.Symbol = slowDMAPrice.Symbol; movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.DownStart; movingAverageCrossover.IsChangeInTrend = false; movingAverageCrossover.SlowDMAPrice = slowDMAPrice; movingAverageCrossover.FastDMAPrice = fastDMAPrice; movingAverageCrossover.ChangePercent=0.00; movingAverageCrossovers.Add(movingAverageCrossover); currentDirection = -1; } else { movingAverageCrossover = new MovingAverageCrossover(); movingAverageCrossover.EventDate = analysisDate; movingAverageCrossover.Symbol = slowDMAPrice.Symbol; movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.NeutralStart; movingAverageCrossover.IsChangeInTrend = false; movingAverageCrossover.SlowDMAPrice = slowDMAPrice; movingAverageCrossover.FastDMAPrice = fastDMAPrice; movingAverageCrossover.ChangePercent=0.00; movingAverageCrossovers.Add(movingAverageCrossover); currentDirection = 0; } prevSlowDMAPrice = slowDMAPrice; prevFastDMAPrice = fastDMAPrice; } else { if(fastDMAPrice.AVGPrice > slowDMAPrice.AVGPrice) { if(1 == currentDirection) // The fast is above the slow as it was previously { if(movingAverageCrossovers.Count>0) // check the last change record for a threshhold break { MovingAverageCrossover movingAverageCrossOver = movingAverageCrossovers[movingAverageCrossovers.Count-1]; double changePercent = ((fastDMAPrice.AVGPrice - movingAverageCrossOver.FastDMAPrice.AVGPrice)/movingAverageCrossOver.FastDMAPrice.AVGPrice)*100.00; if(changePercent > 0.00 && changePercent > trendChangePercentThreshhold) // if the threshhold is exceeded then create a new record and mark it as a trend change { movingAverageCrossover = new MovingAverageCrossover(); movingAverageCrossover.EventDate = analysisDate; movingAverageCrossover.Symbol = slowDMAPrice.Symbol; movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross; movingAverageCrossover.IsChangeInTrend = true; movingAverageCrossover.SlowDMAPrice = slowDMAPrice; movingAverageCrossover.FastDMAPrice = fastDMAPrice; movingAverageCrossover.ChangePercent=changePercent; movingAverageCrossovers.Add(movingAverageCrossover); } } } else if(-1 == currentDirection || 0 == currentDirection) // The fast is above the slow and it was below the slow previously { currentDirection = 1; // switch directions movingAverageCrossover = new MovingAverageCrossover(); movingAverageCrossover.EventDate = analysisDate; movingAverageCrossover.Symbol = slowDMAPrice.Symbol; movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross; movingAverageCrossover.SlowDMAPrice = slowDMAPrice; movingAverageCrossover.FastDMAPrice = fastDMAPrice; double changePercent = Math.Abs(((fastDMAPrice.AVGPrice - slowDMAPrice.AVGPrice)/slowDMAPrice.AVGPrice)*100.00); movingAverageCrossover.ChangePercent=changePercent; if(changePercent>trendChangePercentThreshhold)movingAverageCrossover.IsChangeInTrend = true; else movingAverageCrossover.IsChangeInTrend = false; movingAverageCrossovers.Add(movingAverageCrossover); } } else if(fastDMAPrice.AVGPrice < slowDMAPrice.AVGPrice) { if(-1 == currentDirection) // The fast is below the slow as it was previously { if(movingAverageCrossovers.Count>0) // check the last change record for a threshhold break { movingAverageCrossover = movingAverageCrossovers[movingAverageCrossovers.Count-1]; double changePercent = Math.Abs(((fastDMAPrice.AVGPrice - movingAverageCrossover.FastDMAPrice.AVGPrice)/movingAverageCrossover.FastDMAPrice.AVGPrice)*100.00); if(changePercent<0.00 && Math.Abs(changePercent)>trendChangePercentThreshhold) // if the threshhold is exceeded then create a new record and mark it as a trend change { movingAverageCrossover = new MovingAverageCrossover(); movingAverageCrossover.EventDate = analysisDate; movingAverageCrossover.Symbol = slowDMAPrice.Symbol; movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross; movingAverageCrossover.IsChangeInTrend = true; movingAverageCrossover.SlowDMAPrice = slowDMAPrice; movingAverageCrossover.FastDMAPrice = fastDMAPrice; movingAverageCrossover.ChangePercent = changePercent; movingAverageCrossovers.Add(movingAverageCrossover); } } } else if(1 == currentDirection || 0 == currentDirection) // The fast is below the slow but it was above the slow previously so we've crossed { currentDirection = -1; // switch directions movingAverageCrossover = new MovingAverageCrossover(); movingAverageCrossover.EventDate = analysisDate; movingAverageCrossover.Symbol = slowDMAPrice.Symbol; movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.DownCross; movingAverageCrossover.SlowDMAPrice = slowDMAPrice; movingAverageCrossover.FastDMAPrice = fastDMAPrice; double changePercent = Math.Abs(((fastDMAPrice.AVGPrice - slowDMAPrice.AVGPrice)/slowDMAPrice.AVGPrice)*100.00); movingAverageCrossover.ChangePercent=changePercent; if(changePercent>trendChangePercentThreshhold)movingAverageCrossover.IsChangeInTrend = true; else movingAverageCrossover.IsChangeInTrend = false; movingAverageCrossovers.Add(movingAverageCrossover); } } else // The fast and the slow are equal. There's nothing to do here { } prevSlowDMAPrice = slowDMAPrice; prevFastDMAPrice = fastDMAPrice; } } } } }