diff --git a/MarketDataLib/CNNProcessing/DataProcessor.cs b/MarketDataLib/CNNProcessing/DataProcessor.cs
index 76b49e1..5ed95a1 100644
--- a/MarketDataLib/CNNProcessing/DataProcessor.cs
+++ b/MarketDataLib/CNNProcessing/DataProcessor.cs
@@ -11,6 +11,7 @@ using MarketData.MarketDataModel;
using MarketData.Generator;
using MarketData.Utils;
using System.Drawing.Drawing2D;
+using MarketData.Generator.MovingAverage;
namespace MarketData.CNNProcessing
{
diff --git a/MarketDataLib/Generator/BollingerBandGenerator.cs b/MarketDataLib/Generator/BollingerBandGenerator.cs
index 1c40a4f..d591a67 100644
--- a/MarketDataLib/Generator/BollingerBandGenerator.cs
+++ b/MarketDataLib/Generator/BollingerBandGenerator.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
+using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
// Filename: BollingerBandGenerator.cs
// Author:Sean Kessler
diff --git a/MarketDataLib/Generator/CMMomentum/CMCandidateGenerator.cs b/MarketDataLib/Generator/CMMomentum/CMCandidateGenerator.cs
index 57bf8d7..60309d4 100644
--- a/MarketDataLib/Generator/CMMomentum/CMCandidateGenerator.cs
+++ b/MarketDataLib/Generator/CMMomentum/CMCandidateGenerator.cs
@@ -1,5 +1,6 @@
using MarketData.DataAccess;
using MarketData.Generator.Indicators;
+using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
using MarketData.Numerical;
using MarketData.Utils;
diff --git a/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs b/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs
index 41058ba..48e7e73 100644
--- a/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs
+++ b/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs
@@ -1,5 +1,6 @@
using MarketData.CNNProcessing;
using MarketData.DataAccess;
+using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
diff --git a/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs b/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs
index 823be3f..a740cec 100644
--- a/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs
+++ b/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs
@@ -1,6 +1,7 @@
using MarketData.Cache;
using MarketData.DataAccess;
using MarketData.Generator.Indicators;
+using MarketData.Generator.MovingAverage;
using MarketData.Helper;
using MarketData.MarketDataModel;
using MarketData.Numerical;
diff --git a/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs b/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs
index 5cab01c..8587d04 100644
--- a/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs
+++ b/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs
@@ -18,6 +18,7 @@ using StopLimit=MarketData.Generator.Model.StopLimit;
using StopLimits=MarketData.Generator.Model.StopLimits;
using MarketData.Generator.ModelGenerators;
using Axiom.Interpreter;
+using MarketData.Generator.MovingAverage;
namespace MarketData.Generator.CMTrend
{
diff --git a/MarketDataLib/Generator/ModelGenerators/VolatilityGenerator.cs b/MarketDataLib/Generator/ModelGenerators/VolatilityGenerator.cs
index 56c1e48..e19aa8b 100644
--- a/MarketDataLib/Generator/ModelGenerators/VolatilityGenerator.cs
+++ b/MarketDataLib/Generator/ModelGenerators/VolatilityGenerator.cs
@@ -1,4 +1,5 @@
-using MarketData.MarketDataModel;
+using MarketData.Generator.MovingAverage;
+using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
diff --git a/MarketDataLib/Generator/MovingAverage/ExponentialMovingAverageCrossover.cs b/MarketDataLib/Generator/MovingAverage/ExponentialMovingAverageCrossover.cs
new file mode 100644
index 0000000..4585179
--- /dev/null
+++ b/MarketDataLib/Generator/MovingAverage/ExponentialMovingAverageCrossover.cs
@@ -0,0 +1,212 @@
+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;
+ }
+ }
+ }
+ }
+}
diff --git a/MarketDataLib/Generator/MovingAverage/MovingAverageCrossover.cs b/MarketDataLib/Generator/MovingAverage/MovingAverageCrossover.cs
new file mode 100644
index 0000000..802bb78
--- /dev/null
+++ b/MarketDataLib/Generator/MovingAverage/MovingAverageCrossover.cs
@@ -0,0 +1,47 @@
+using MarketData.MarketDataModel;
+using MarketData.Utils;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MarketData.Generator.MovingAverage
+{
+ public class MovingAverageCrossovers : List
+ {
+ public MovingAverageCrossovers()
+ {
+ }
+
+ public MovingAverageCrossovers(List movingAverageCrossovers)
+ {
+ foreach(MovingAverageCrossover movingAverageCrossover in movingAverageCrossovers)
+ {
+ Add(movingAverageCrossover);
+ }
+ }
+ }
+
+ public class MovingAverageCrossover
+ {
+ public enum CrossoverDirectionEnum{UpCross, DownCross, UpStart, DownStart, NeutralStart};
+ public String Symbol { get; set; }
+ public DateTime EventDate { get; set; }
+ public CrossoverDirectionEnum CrossOverDirection {get; set;}
+ public bool IsChangeInTrend { get; set; }
+ public double ChangePercent { get; set; }
+ public DMAPrice SlowDMAPrice { get; set; } // the slow DMA price associated with this crossover
+ public DMAPrice FastDMAPrice { get; set; } // the fast DMA price associated with this crossover
+ public double CurrentPrice {get{return SlowDMAPrice.CurrentPrice;}} // it doesn't matter if sourced from slow or fast since they both refer to the same underlying price
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("Symbol:").Append(Symbol);
+ sb.Append(" Date:").Append(EventDate.ToShortDateString());
+ sb.Append(" Direction:").Append(CrossOverDirection.ToString());
+ sb.Append(" IsChangeInTrend:").Append(IsChangeInTrend.ToString());
+ sb.Append(" ChangePercent:").Append(Utility.FormatNumber(ChangePercent,2));
+ sb.Append(" ClosePrice:").Append(Utility.FormatCurrency(CurrentPrice));
+ return sb.ToString();
+ }
+ }
+}
diff --git a/MarketDataLib/Generator/MovingAverageGenerator.cs b/MarketDataLib/Generator/MovingAverage/MovingAverageGenerator.cs
similarity index 99%
rename from MarketDataLib/Generator/MovingAverageGenerator.cs
rename to MarketDataLib/Generator/MovingAverage/MovingAverageGenerator.cs
index d0f6a7f..cc2a13f 100644
--- a/MarketDataLib/Generator/MovingAverageGenerator.cs
+++ b/MarketDataLib/Generator/MovingAverage/MovingAverageGenerator.cs
@@ -1,11 +1,11 @@
-using System;
+using System;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Numerical;
using MarketData.Utils;
-namespace MarketData.Generator
+namespace MarketData.Generator.MovingAverage
{
public class MovingAverageGenerator
{
diff --git a/MarketDataLib/Generator/StochasticsGenerator.cs b/MarketDataLib/Generator/StochasticsGenerator.cs
index 48d813e..9e16071 100644
--- a/MarketDataLib/Generator/StochasticsGenerator.cs
+++ b/MarketDataLib/Generator/StochasticsGenerator.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
// Filename: StochasticsGenerator.cs
diff --git a/MarketDataLib/Helper/MovingAverageHelperSheet.cs b/MarketDataLib/Helper/MovingAverageHelperSheet.cs
index fa476a6..16ceb45 100644
--- a/MarketDataLib/Helper/MovingAverageHelperSheet.cs
+++ b/MarketDataLib/Helper/MovingAverageHelperSheet.cs
@@ -6,6 +6,7 @@ using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Generator;
using MarketData.Utils;
+using MarketData.Generator.MovingAverage;
namespace MarketData.Helper
{
diff --git a/MarketDataLib/MarketDataLib.csproj b/MarketDataLib/MarketDataLib.csproj
index c779179..747cd1e 100644
--- a/MarketDataLib/MarketDataLib.csproj
+++ b/MarketDataLib/MarketDataLib.csproj
@@ -162,6 +162,9 @@
+
+
+
@@ -300,7 +303,6 @@
-
diff --git a/MarketDataLib/MarketDataModel/YieldCurve.cs b/MarketDataLib/MarketDataModel/YieldCurve.cs
index d7edfa3..493bb05 100644
--- a/MarketDataLib/MarketDataModel/YieldCurve.cs
+++ b/MarketDataLib/MarketDataModel/YieldCurve.cs
@@ -4,6 +4,7 @@ using System.Text;
using System.Linq;
using MarketData.Generator;
using MarketData.Utils;
+using MarketData.Generator.MovingAverage;
namespace MarketData.MarketDataModel
{
diff --git a/Program.cs b/Program.cs
index 31ea698..5fbd5a9 100644
--- a/Program.cs
+++ b/Program.cs
@@ -23,6 +23,8 @@ using MarketData.Generator.CMTrend;
using Axiom.Interpreter;
using System.Data;
using MarketData.CNNProcessing;
+using MySql.Data.MySqlClient;
+using MarketData.Generator.MovingAverage;
namespace MarketData
{
@@ -769,13 +771,19 @@ namespace MarketData
Trace.Listeners.Add(new TextWriterTraceListener(strLogFile));
DateTime currentDate=DateTime.Now;
-// Price price = MarketDataHelper.GetLatestPriceYahoo("^GSPC");
+ Prices prices = new Prices();
+ prices.Add(new Price(){Date=DateTime.Parse("1/28/2025"),Open=10,High=10,Low=10,Close=10});
+ prices.Add(new Price(){Date=DateTime.Parse("1/29/2025"),Open=15,High=15,Low=15,Close=15});
+ prices.Add(new Price(){Date=DateTime.Parse("1/30/2025"),Open=5,High=5,Low=5,Close=5});
+ prices.Add(new Price(){Date=DateTime.Parse("1/31/2025"),Open=20,High=20,Low=20,Close=20});
+ prices.Add(new Price(){Date=DateTime.Parse("2/1/2025"),Open=10,High=10,Low=10,Close=10});
+ prices.Add(new Price(){Date=DateTime.Parse("2/2/2025"),Open=25,High=25,Low=25,Close=25});
+ prices.Add(new Price(){Date=DateTime.Parse("2/3/2025"),Open=30,High=30,Low=30,Close=30});
+
+ DMAPrices dmaPrices = MovingAverageGenerator.GenerateMovingAverage(prices, 2);
+ DMAPrices emaPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, 2);
-// GetObservations(90,30);
-// CompressObservations("Observations.txt");
-// Console.WriteLine(CNNClient.Model.inception.ToString());
-// CNNClient.Model model = (CNNClient.Model)Enum.Parse(typeof(CNNClient.Model),"inception",true);
DateTime maxHolidayDate =HolidayDA.GetMaxHolidayDate();
if(currentDate>maxHolidayDate)