of the usage has no impact on the issue with the generator because the usage was only using the first index in the exponential moving average which always amounts to just the value of the simple moving average. (i.e.) smoothing does not occur on the first index, only subsequent indices are smoothed.
250 lines
9.8 KiB
C#
250 lines
9.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using MarketData.MarketDataModel;
|
|
using MarketData.DataAccess;
|
|
using MarketData.Numerical;
|
|
using MarketData.Utils;
|
|
|
|
namespace MarketData.Generator
|
|
{
|
|
public class MovingAverageGenerator
|
|
{
|
|
public static readonly int DayCount200=200;
|
|
public static readonly int DayCount100=100;
|
|
public static readonly int DayCount55=55;
|
|
public static readonly int DayCount50=50;
|
|
public static readonly int DayCount21=21;
|
|
public static readonly int DayCount5=5;
|
|
private MovingAverageGenerator()
|
|
{
|
|
}
|
|
public static MovingAverages GenerateMovingAverages(String symbol,int dayCount=180)
|
|
{
|
|
try
|
|
{
|
|
Dictionary<DateTime, DMAPrice> ma200ByDate = new Dictionary<DateTime, DMAPrice>();
|
|
Dictionary<DateTime, DMAPrice> ma100ByDate = new Dictionary<DateTime, DMAPrice>();
|
|
Dictionary<DateTime, DMAPrice> ma55ByDate = new Dictionary<DateTime, DMAPrice>();
|
|
Dictionary<DateTime, DMAPrice> ma21ByDate = new Dictionary<DateTime, DMAPrice>();
|
|
Dictionary<DateTime, DMAPrice> ma5ByDate = new Dictionary<DateTime, DMAPrice>();
|
|
if (null == symbol) return null;
|
|
String companyName = PricingDA.GetNameForSymbol(symbol);
|
|
DateGenerator dateGenerator = new DateGenerator();
|
|
DateTime startDate = dateGenerator.GetPrevBusinessDay(DateTime.Now);
|
|
Prices prices = PricingDA.GetPrices(symbol, startDate, dayCount);
|
|
if (null == prices || 0 == prices.Count)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,"No prices for symbol '" + symbol + "'");
|
|
return null;
|
|
}
|
|
Price latestPrice = prices[0];
|
|
DMAPrices ma200= GenerateMovingAverage(prices, DayCount200);
|
|
DMAPrices ma100 = GenerateMovingAverage(prices, DayCount100);
|
|
DMAPrices ma55 = GenerateMovingAverage(prices, DayCount55);
|
|
DMAPrices ma21 = GenerateMovingAverage(prices, DayCount21);
|
|
DMAPrices ma5 = GenerateMovingAverage(prices, DayCount5);
|
|
|
|
for (int index = 0; index < ma200.Count; index++) ma200ByDate.Add(ma200[index].Date,ma200[index]);
|
|
for (int index = 0; index < ma100.Count; index++) ma100ByDate.Add(ma100[index].Date, ma100[index]);
|
|
for (int index = 0; index < ma55.Count; index++) ma55ByDate.Add(ma55[index].Date, ma55[index]);
|
|
for (int index = 0; index < ma21.Count; index++) ma21ByDate.Add(ma21[index].Date, ma21[index]);
|
|
for (int index = 0; index < ma5.Count; index++) ma5ByDate.Add(ma5[index].Date, ma5[index]);
|
|
MovingAverages movingAverages = new MovingAverages();
|
|
movingAverages.ThruDate = prices[0].Date;
|
|
movingAverages.FromDate = prices[prices.Count - 1].Date;
|
|
for (int index = 0; index < prices.Count; index++)
|
|
{
|
|
Price price = prices[index];
|
|
|
|
DMAPrice ma200Price = null;
|
|
DMAPrice ma100Price = null;
|
|
DMAPrice ma55Price = null;
|
|
DMAPrice ma21Price = null;
|
|
DMAPrice ma5Price = null;
|
|
|
|
if (ma55ByDate.ContainsKey(price.Date)) ma55Price=ma55ByDate[price.Date];
|
|
if (ma21ByDate.ContainsKey(price.Date)) ma21Price = ma21ByDate[price.Date];
|
|
if (ma5ByDate.ContainsKey(price.Date)) ma5Price = ma5ByDate[price.Date];
|
|
if(ma200ByDate.ContainsKey(price.Date))ma200Price = ma200ByDate[price.Date];
|
|
if(ma100ByDate.ContainsKey(price.Date)) ma100Price = ma100ByDate[price.Date];
|
|
|
|
MovingAverageElement movingAverageElement = new MovingAverageElement();
|
|
movingAverageElement.Symbol = price.Symbol;
|
|
movingAverageElement.Date = price.Date;
|
|
movingAverageElement.Close = price.Close;
|
|
movingAverageElement.High = price.High;
|
|
movingAverageElement.Low = price.Low;
|
|
movingAverageElement.MA200 = null==ma200Price?double.NaN:ma200Price.AVGPrice;
|
|
movingAverageElement.MA100 = null == ma100Price ? double.NaN : ma100Price.AVGPrice;
|
|
movingAverageElement.MA55 = null==ma55Price?double.NaN:ma55Price.AVGPrice;
|
|
movingAverageElement.MA21 = null==ma21Price?double.NaN:ma21Price.AVGPrice;
|
|
movingAverageElement.MA5 = null==ma5Price?double.NaN:ma5Price.AVGPrice;
|
|
movingAverages.Add(movingAverageElement);
|
|
}
|
|
return movingAverages;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception);
|
|
return null;
|
|
}
|
|
}
|
|
public static DMAValues GenerateMovingAverage(DMAValues values,int dayCount)
|
|
{
|
|
try
|
|
{
|
|
DMAValues dmaValues = new DMAValues();
|
|
for (int index = 0; index < values.Count; index++)
|
|
{
|
|
DMAValue value = values[index];
|
|
DMAValue dmaValue = new DMAValue();
|
|
dmaValue.Date = value.Date;
|
|
float[] pricesArray = values.GetValues(index, dayCount);
|
|
if (null == pricesArray) break;
|
|
dmaValue.MAValue = Numerics.Mean(ref pricesArray);
|
|
dmaValue.Value = value.Value;
|
|
if (double.IsNaN(dmaValue.MAValue)) continue;
|
|
dmaValues.Add(dmaValue);
|
|
}
|
|
return dmaValues;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The EMA coverage shoudld be the same as the simple moving average. If you have 10 elements in the SMA then you should have 10 elements in the EMA
|
|
/// The EMA smooths the line by applying a smoothing (Beta) where Beta=1/(dayCount+1). Foe example if you are wanting to calculate the
|
|
/// 20 day exponential moving average over a series then Beta=2/(20+1)=.095
|
|
/// The formula: EMA=prevEMA.AVGPrice+beta*(smaPrice.CurrentPrice - prevEMA.AVGPrice)
|
|
/// Tom Basso uses a 9 day(Fast) and 41 day(Slow) exponential moving average crossover to determine change in trend direction.
|
|
/// </summary>
|
|
/// <param name="prices"></param>
|
|
/// <param name="dayCount"></param>
|
|
/// <returns></returns>
|
|
public static DMAPrices GenerateExponentialMovingAverage(Prices prices,int dayCount)
|
|
{
|
|
try
|
|
{
|
|
if(null==prices||prices.Count<dayCount+1)return null; // (i.e.) a 20 day ExponentialMovingAverage requires 21 days of data
|
|
double beta=2.00/((double)dayCount+1.00);
|
|
DMAPrices emaPrices=new DMAPrices();
|
|
|
|
DMAPrices smaPrices=GenerateMovingAverage(prices,dayCount);
|
|
if(null==smaPrices||smaPrices.Count<dayCount)return null;
|
|
for(int index=0;index<smaPrices.Count;index++)
|
|
{
|
|
DMAPrice smaPrice=smaPrices[index];
|
|
DMAPrice emaPrice=new DMAPrice();
|
|
if(0==index)
|
|
{
|
|
emaPrice.Symbol=smaPrice.Symbol;
|
|
emaPrice.Date=smaPrice.Date;
|
|
emaPrice.AVGPrice=smaPrice.AVGPrice;
|
|
emaPrices.Add(emaPrice);
|
|
}
|
|
else
|
|
{
|
|
DMAPrice prevEMA=emaPrices[emaPrices.Count-1];
|
|
emaPrice.Symbol=smaPrice.Symbol;
|
|
emaPrice.Date=smaPrice.Date;
|
|
emaPrice.AVGPrice = prevEMA.AVGPrice+beta*(smaPrice.CurrentPrice - prevEMA.AVGPrice);
|
|
emaPrices.Add(emaPrice);
|
|
}
|
|
}
|
|
return emaPrices;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>Generates a dayCount moving average given prices.</summary>
|
|
public static DMAPrices GenerateMovingAverage(Prices prices, int dayCount)
|
|
{
|
|
try
|
|
{
|
|
DMAPrices dmaPrices = new DMAPrices();
|
|
for (int index = 0; index < prices.Count; index++)
|
|
{
|
|
Price price = prices[index];
|
|
DMAPrice dmaPrice = new DMAPrice();
|
|
dmaPrice.Symbol = price.Symbol;
|
|
dmaPrice.Date = price.Date;
|
|
float[] pricesArray = prices.GetPrices(index, dayCount);
|
|
if (null == pricesArray) break;
|
|
dmaPrice.AVGPrice = Numerics.Mean(ref pricesArray);
|
|
if (double.IsNaN(dmaPrice.AVGPrice)) continue;
|
|
dmaPrice.CurrentPrice = price.Close;
|
|
dmaPrices.Add(dmaPrice);
|
|
}
|
|
return dmaPrices;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception);
|
|
return null;
|
|
}
|
|
}
|
|
public static DMAPrices GenerateMovingMinsOnLow(Prices prices, int dayCount)
|
|
{
|
|
try
|
|
{
|
|
DMAPrices dmaPrices = new DMAPrices();
|
|
|
|
for (int index = 0; index < prices.Count; index++)
|
|
{
|
|
Price price = prices[index];
|
|
DMAPrice dmaPrice = new DMAPrice();
|
|
dmaPrice.Symbol = price.Symbol;
|
|
dmaPrice.Date = price.Date;
|
|
float[] pricesArray = prices.GetPricesLow(index, dayCount);
|
|
if (null == pricesArray) break;
|
|
dmaPrice.MinPrice = Numerics.Min(ref pricesArray);
|
|
if (double.IsNaN(dmaPrice.MinPrice)) continue;
|
|
dmaPrice.CurrentPrice = price.Low;
|
|
dmaPrices.Add(dmaPrice);
|
|
}
|
|
return dmaPrices;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
|
|
return null;
|
|
}
|
|
}
|
|
public static DMAPrices GenerateMovingMaxOnHigh(Prices prices, int dayCount)
|
|
{
|
|
try
|
|
{
|
|
DMAPrices dmaPrices = new DMAPrices();
|
|
|
|
for (int index = 0; index < prices.Count; index++)
|
|
{
|
|
Price price = prices[index];
|
|
DMAPrice dmaPrice = new DMAPrice();
|
|
dmaPrice.Symbol = price.Symbol;
|
|
dmaPrice.Date = price.Date;
|
|
float[] pricesArray = prices.GetPricesHigh(index, dayCount);
|
|
if (null == pricesArray) break;
|
|
dmaPrice.MaxPrice = Numerics.Max(ref pricesArray);
|
|
if (double.IsNaN(dmaPrice.MaxPrice)) continue;
|
|
dmaPrice.CurrentPrice = price.High;
|
|
dmaPrices.Add(dmaPrice);
|
|
}
|
|
return dmaPrices;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|