Files
marketdata/MarketDataLib/Generator/MovingAverage/ExponentialMovingAverageCrossover.cs

213 lines
12 KiB
C#

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);
}
/// <summary>
/// GetMovingAverageCrossovers
/// Prices will be in descending order with the most recent price at index[0] and the least recent price at index[count-1]
/// </summary>
/// <param name="prices"></param>
/// <param name="fastCrossOverDays">The fast crossover. Tom Basso uses 9</param>
/// <param name="slowCrossoverDays">The slow crosser. Tom Basso uses 41</param>
/// <param name="threshholdPercent">The threshhold percent. 5% would be .05</param>
/// <returns></returns>
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<MovingAverageCrossover>).Where(x => x.IsChangeInTrend==true).OrderByDescending(x => x.EventDate).ToList());
return movingAverageCrossovers;
}
/// <summary>
/// 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
/// </summary>
/// <param name="fastDMAPrices"></param>
/// <param name="slowDMAPrices"></param>
/// <param name="movingAverageCrossovers"></param>
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<DateTime> availableDates = new List<DateTime>(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<availableDates.Count;index++)
{
DateTime analysisDate = availableDates[index];
DMAPrice slowDMAPrice = slowDMAPricesByDate[analysisDate];
DMAPrice fastDMAPrice = fastDMAPricesByDate[analysisDate];
if(index == 0) // establish an initial orientation of the fast and slow moving averages. We need to create a new record to have a current trend
{
if(fastDMAPrice.AVGPrice > 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;
}
}
}
}
}