Add MGSHMomentum Model Processing and Model File

This commit is contained in:
2025-04-08 18:05:56 -04:00
parent d4944607ab
commit eec147b974
26 changed files with 5065 additions and 9 deletions

View File

@@ -13,5 +13,5 @@ namespace MarketData.Extensions
}
return Encryption.VerifyPassword(password, user.Salt, user.Hash);
}
}
}
}

View File

@@ -0,0 +1,95 @@
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MarketData.Generator.MGSHMomentum
{
public class CandidateSelector
{
private static int DAY_COUNT=252;
private CandidateSelector()
{
}
public static String SelectBestCandidateSymbol(MGSHQualityIndicator qualityIndicator,List<String> candidates,String defaultCandidate,DateTime analysisDate)
{
String bestCandidate=defaultCandidate;
try
{
if(null==candidates||0==candidates.Count||Utility.IsEpoch(analysisDate)||null==defaultCandidate)return bestCandidate;
List<QualityIndicatorCandidate> qualityIndicatorCandidates=new List<QualityIndicatorCandidate>();
foreach(String candidate in candidates)
{
Prices prices=PricingDA.GetPrices(candidate,analysisDate,DAY_COUNT);
if(null==prices||0==prices.Count)continue;
QualityIndicatorCandidate qualityIndicatorCandidate=new QualityIndicatorCandidate();
qualityIndicatorCandidate.Symbol=candidate;
qualityIndicatorCandidate.IDIndicator=IDIndicator.Calculate(prices);
qualityIndicatorCandidate.Score=ScoreIndicator.Calculate(prices);
qualityIndicatorCandidates.Add(qualityIndicatorCandidate);
}
if(0==qualityIndicatorCandidates.Count)return bestCandidate;
if(qualityIndicator.Quality.Equals(MGSHQualityIndicator.QualityType.IDIndicator))
{
qualityIndicatorCandidates=(from QualityIndicatorCandidate qualityIndicatorCandidate in qualityIndicatorCandidates orderby qualityIndicatorCandidate.IDIndicator ascending select qualityIndicatorCandidate).ToList();
}
else
{
qualityIndicatorCandidates=(from QualityIndicatorCandidate qualityIndicatorCandidate in qualityIndicatorCandidates orderby qualityIndicatorCandidate.Score descending select qualityIndicatorCandidate).ToList();
}
return qualityIndicatorCandidates.Take(1).FirstOrDefault().Symbol;
}
catch(Exception exception)
{
MDTrace.WriteLine(String.Format("{0}",exception.ToString()));
return bestCandidate;
}
}
public static QualityIndicatorCandidate SelectBestCandidate(MGSHQualityIndicator qualityIndicator,List<String> candidates,String defaultCandidate,DateTime analysisDate)
{
QualityIndicatorCandidate bestCandidate=new QualityIndicatorCandidate();
try
{
if(null==candidates||0==candidates.Count||Utility.IsEpoch(analysisDate)||null==defaultCandidate)return null;
List<QualityIndicatorCandidate> qualityIndicatorCandidates=new List<QualityIndicatorCandidate>();
foreach(String candidate in candidates)
{
Prices prices=PricingDA.GetPrices(candidate,analysisDate,DAY_COUNT);
Fundamental fundamental=FundamentalDA.GetFundamentalMaxDate(candidate,analysisDate);
if(null==prices||0==prices.Count)continue;
QualityIndicatorCandidate qualityIndicatorCandidate=new QualityIndicatorCandidate();
qualityIndicatorCandidate.Symbol=candidate;
qualityIndicatorCandidate.IDIndicator=IDIndicator.Calculate(prices);
qualityIndicatorCandidate.Score=ScoreIndicator.Calculate(prices);
qualityIndicatorCandidate.CumReturn252=prices.GetCumulativeReturn();
qualityIndicatorCandidate.DayCount=DAY_COUNT;
qualityIndicatorCandidate.Return1D=prices.GetReturn1D();
if(null!=fundamental)
{
qualityIndicatorCandidate.PE=fundamental.PE;
qualityIndicatorCandidate.Beta=fundamental.Beta;
}
qualityIndicatorCandidates.Add(qualityIndicatorCandidate);
}
if(0==qualityIndicatorCandidates.Count)return bestCandidate;
if(qualityIndicator.Quality.Equals(MGSHQualityIndicator.QualityType.IDIndicator))
{
qualityIndicatorCandidates=(from QualityIndicatorCandidate qualityIndicatorCandidate in qualityIndicatorCandidates orderby qualityIndicatorCandidate.IDIndicator ascending select qualityIndicatorCandidate).ToList();
}
else
{
qualityIndicatorCandidates=(from QualityIndicatorCandidate qualityIndicatorCandidate in qualityIndicatorCandidates orderby qualityIndicatorCandidate.Score descending select qualityIndicatorCandidate).ToList();
}
return qualityIndicatorCandidates.Take(1).FirstOrDefault();
}
catch(Exception exception)
{
MDTrace.WriteLine(String.Format("{0}",exception.ToString()));
return bestCandidate;
}
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
namespace MarketData.Generator.MGSHMomentum
{
public class CandidateViolation
{
public CandidateViolation(String symbol, String reasonCategory)
{
Symbol = symbol;
ReasonCategory = reasonCategory;
}
public String Symbol {get; set;}
public String ReasonCategory {get; set;}
}
public class CandidateViolations : List<CandidateViolation>
{
}
}

View File

@@ -0,0 +1,435 @@
using System;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.Cache;
using MarketData.Numerical;
using MarketData.Generator.ModelGenerators;
using StopLimit=MarketData.Generator.Model.StopLimit;
namespace MarketData.Generator.MGSHMomentum
{
public class HedgeManager
{
private static int PRICING_DAYS = 120;
public HedgeManager()
{
}
public String HedgeShortSymbol { get; set; } = "SH";
public bool Verbose {get;set;} = false;
private void Display(String message)
{
if(Verbose)
{
MDTrace.WriteLine(LogLevel.DEBUG,message);
}
}
public bool IsOpenHedgeIndicator(DateTime analysisDate, int hedgeCloseGreaterSMANDays=10, int hedgeBandBreakCheckDays=3)
{
Prices prices=GBPriceCache.GetInstance().GetPrices(HedgeShortSymbol, analysisDate, PRICING_DAYS);
if(null==prices || !prices[0].Date.Date.Equals(analysisDate.Date))
{
Display(String.Format("Cannot evaluate IsOpenHedgeIndicator for candidate {0} due to lack of current price on {1}",
HedgeShortSymbol,analysisDate.ToShortDateString()));
return false;
}
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices); // K is upper band, KL1 below that. L is lower band, LP1 above that
BollingerBandElementsByDate bollingerBandElementsByDate = bollingerBands.GetBollingerBandElementsByDate();
if(!bollingerBandElementsByDate.ContainsKey(analysisDate))
{
Display(String.Format("Cannot evaluate IsOpenHedgeIndicator for candidate {0} due to lack of current price on {1}",HedgeShortSymbol,analysisDate.ToShortDateString()));
return false;
}
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[analysisDate];
if(bollingerBandElement.Close < bollingerBandElement.L )
{
Display(String.Format($"IsOpenHedgeIndicator AnalysisDate:{analysisDate.ToShortDateString()}"));
SlopeManager slopeManager = new SlopeManager(bollingerBandElementsByDate, analysisDate){Verbose=Verbose};
slopeManager.DisplaySlopes();
if(!CloseGreaterSMAN(analysisDate, bollingerBandElementsByDate, hedgeCloseGreaterSMANDays))
{
return false;
}
if(!BandBreakCheck(analysisDate, bollingerBandElementsByDate, hedgeBandBreakCheckDays)) // 3
{
return false;
}
int bucketDays=5;
int closeLessL=GetCloseLessL(analysisDate, bollingerBandElementsByDate, bucketDays);
int closeBetweenLAndLP1=GetCloseBetweenLAndLP1(analysisDate, bollingerBandElementsByDate, bucketDays);
int closeBetweenLP1AndSMAN=GetCloseBetweenLP1AndSMAN(analysisDate, bollingerBandElementsByDate, bucketDays);
int closeBetweenSMANAndKL1=GetCloseBetweenSMANAndKL1(analysisDate, bollingerBandElementsByDate, bucketDays);
int closeBetweenKL1AndK=GetCloseBetweenKL1AndK(analysisDate, bollingerBandElementsByDate, bucketDays);
int closeGreaterK=GetCloseGreaterK(analysisDate, bollingerBandElementsByDate, bucketDays);
Display($"Close buckets <L:{closeLessL} L/LP1:{closeBetweenLAndLP1} LP1/SMAN:{closeBetweenLP1AndSMAN} SMAN/KL1:{closeBetweenSMANAndKL1} KL1/K:{closeBetweenKL1AndK} >K:{closeGreaterK} KLSpread:{slopeManager.GetKLSpread()}");
// if the close is spending a lot of time between L and LP1 then make sure the overall volatility spread is widening sufficiently in the 5, 10, 30 day terms
if(closeBetweenLAndLP1 >= bucketDays && !slopeManager.IsMatchKLSpread("??+++"))
{
return false;
}
Display(String.Format("Lower band break detected for {0} on {1} Price Close:{2} LP1:{3} ",
HedgeShortSymbol,
analysisDate.ToShortDateString(),
Utility.FormatCurrency(bollingerBandElement.Close),
Utility.FormatCurrency(bollingerBandElement.L)
));
Display($"TRUE");
return true;
}
return false;
}
/// <summary>
/// Mainains and adjusts the stop limits for teh hedge position
/// </summary>
/// <param name="analysisDate"></param>
/// <param name="position"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public StopLimit EvaluateStopPriceHedge(DateTime analysisDate, MGSHPosition position, MGSHConfiguration configuration)
{
DateGenerator dateGenerator=new DateGenerator();
Price currentPrice=GBPriceCache.GetInstance().GetPrice(position.Symbol,analysisDate);
double trailingStop=position.InitialStopLimit+Math.Floor((currentPrice.Low-position.PurchasePrice)/position.R)*position.R;
double trailingStopScaled=trailingStop;
double daysHeld=Math.Abs(dateGenerator.DaysBetweenActual(position.PurchaseDate,analysisDate));
if(Utility.IsEpoch(position.LastStopAdjustment)) // we've never adjusted the stop price
{
BollingerBandElementsByDate bollingerBandElementsByDate = GetBollingerBandElementsByDate(analysisDate);
if(null == bollingerBandElementsByDate)return null;
trailingStopScaled=GetHedgeStopLimitWithScalingAverageTrueRange(analysisDate,currentPrice,position,trailingStop, configuration.StopLimitScalingVolatilityDays, configuration.HedgeATRMultiplier);
trailingStop=Math.Max(trailingStop,trailingStopScaled);
if(trailingStop >= currentPrice.Low || trailingStop >= currentPrice.Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be higher than the Low/Close of {2}.",
position.Symbol,
Utility.FormatCurrency(trailingStop),
Utility.FormatCurrency(currentPrice.Low)));
return null;
}
if(Numerics.Round(trailingStop) <= (Numerics.Round(position.TrailingStopLimit)))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be less than or equal to the existing stop limit of {2}.",
position.Symbol,
Utility.FormatCurrency(trailingStop),
Utility.FormatCurrency(position.TrailingStopLimit)));
return null;
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************** A D J U S T S T O P L I M I T ************************"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting StopLimit on {0} after {1} days for {2} from {3} to {4}. Purchase Price: {5} Current Price:{6} Spread:{7} Shares:{8}",
analysisDate.ToShortDateString(),
daysHeld,
position.Symbol,
Utility.FormatCurrency(position.TrailingStopLimit),
Utility.FormatCurrency(trailingStop),
Utility.FormatCurrency(position.PurchasePrice),
Utility.FormatCurrency(currentPrice.Close),
Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop),
Utility.FormatNumber(position.Shares,2)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*****************************************************************************************"));
StopLimit newStopLimit=new StopLimit
{
StopLimitId=position.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(position.PurchaseDate),
Symbol=position.Symbol,
AnalysisDate=analysisDate,
PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit,
NewStop=trailingStop,
CurrentPriceLow=currentPrice.Low,
CurrentPriceClose=currentPrice.Close,
PriceTrendIndicatorSlope=0.00
};
position.TrailingStopLimit=trailingStop;
position.LastStopAdjustment=analysisDate;
return newStopLimit;
}
else // we have already made prior stop adjustments
{
int daysSinceLastStopAdjustment=Math.Abs(dateGenerator.DaysBetweenActual(position.LastStopAdjustment,analysisDate));
if(daysSinceLastStopAdjustment>=configuration.HedgeMinDaysBetweenStopAdjustments)
{
trailingStopScaled=GetHedgeStopLimitWithScalingAverageTrueRange(analysisDate,currentPrice,position,trailingStop,daysSinceLastStopAdjustment,configuration.HedgeATRMultiplier);
trailingStop=Math.Max(trailingStop,trailingStopScaled);
if(trailingStop>=currentPrice.Low||trailingStop>=currentPrice.Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be higher than the Low/Close of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(currentPrice.Low)));
return null;
}
if(Numerics.Round(position.TrailingStopLimit) < Numerics.Round(trailingStop)) // round the stop limits to fractionals don't look like differences
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************** A D J U S T S T O P L I M I T ************************"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting StopLimit on {0} after {1} days for {2} from {3} to {4}. Purchase Price: {5} Current Price:{6} Spread:{7} Shares:{8}",
analysisDate.ToShortDateString(),
daysHeld,
position.Symbol,
Utility.FormatCurrency(position.TrailingStopLimit),
Utility.FormatCurrency(trailingStop),
Utility.FormatCurrency(position.PurchasePrice),
Utility.FormatCurrency(currentPrice.Close),
Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop),
Utility.FormatNumber(position.Shares,2)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************************************************************************"));
StopLimit newStopLimit=new StopLimit
{
StopLimitId=position.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(position.PurchaseDate),
Symbol=position.Symbol,
AnalysisDate=analysisDate,
PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit,
NewStop=trailingStop,
CurrentPriceLow=currentPrice.Low,
CurrentPriceClose=currentPrice.Close,
PriceTrendIndicatorSlope=0.00
};
position.TrailingStopLimit=trailingStop;
position.LastStopAdjustment=analysisDate;
return newStopLimit;
}
else
{
double currentTrailingStopLimit=Numerics.Round(position.TrailingStopLimit);
double suggestedTrailingStopLimit=Numerics.Round(trailingStop);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Not Adjusting Stop Limit for {0} because the new trailing stop limit would be less than or equal to the current trailing stop limit. Current Trailing Stop Limit:{1}, Calculated Trailing Stop Limit:{2}.",
position.Symbol,
Utility.FormatCurrency(currentTrailingStopLimit),
Utility.FormatCurrency(suggestedTrailingStopLimit)));
return null;
}
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The trailing stop for {0} was adjusted {1} days ago. HedgeMinDaysBetweenStopAdjustments is {2}",
position.Symbol,
daysSinceLastStopAdjustment,
configuration.HedgeMinDaysBetweenStopAdjustments));
return null;
}
}
}
private int GetCloseLessL(DateTime analysisDate,BollingerBandElementsByDate bollingerBandElementsByDate, int days)
{
int count=0;
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> dates = dateGenerator.GenerateHistoricalDates(analysisDate, days +1);
dates.RemoveAt(0);
foreach(DateTime date in dates)
{
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[date];
if(bollingerBandElement.Close < bollingerBandElement.L)count++;
}
return count;
}
private int GetCloseBetweenLAndLP1(DateTime analysisDate,BollingerBandElementsByDate bollingerBandElementsByDate, int days)
{
int count=0;
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> dates = dateGenerator.GenerateHistoricalDates(analysisDate, days +1);
dates.RemoveAt(0);
foreach(DateTime date in dates)
{
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[date];
if(bollingerBandElement.Close >= bollingerBandElement.L && bollingerBandElement.Close<bollingerBandElement.LP1 )count++;
}
return count;
}
private int GetCloseBetweenLP1AndSMAN(DateTime analysisDate,BollingerBandElementsByDate bollingerBandElementsByDate, int days)
{
int count=0;
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> dates = dateGenerator.GenerateHistoricalDates(analysisDate, days +1);
dates.RemoveAt(0);
foreach(DateTime date in dates)
{
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[date];
if(bollingerBandElement.Close >= bollingerBandElement.LP1 && bollingerBandElement.Close<bollingerBandElement.SMAN )count++;
}
return count;
}
private int GetCloseBetweenSMANAndKL1(DateTime analysisDate,BollingerBandElementsByDate bollingerBandElementsByDate, int days)
{
int count=0;
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> dates = dateGenerator.GenerateHistoricalDates(analysisDate, days +1);
dates.RemoveAt(0);
foreach(DateTime date in dates)
{
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[date];
if(bollingerBandElement.Close >= bollingerBandElement.SMAN && bollingerBandElement.Close<bollingerBandElement.KL1 )count++;
}
return count;
}
private int GetCloseBetweenKL1AndK(DateTime analysisDate,BollingerBandElementsByDate bollingerBandElementsByDate, int days)
{
int count=0;
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> dates = dateGenerator.GenerateHistoricalDates(analysisDate, days +1);
dates.RemoveAt(0);
foreach(DateTime date in dates)
{
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[date];
if(bollingerBandElement.Close >= bollingerBandElement.KL1 && bollingerBandElement.Close<bollingerBandElement.K )count++;
}
return count;
}
private int GetCloseGreaterK(DateTime analysisDate,BollingerBandElementsByDate bollingerBandElementsByDate, int days)
{
int count=0;
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> dates = dateGenerator.GenerateHistoricalDates(analysisDate, days +1);
dates.RemoveAt(0);
foreach(DateTime date in dates)
{
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[date];
if(bollingerBandElement.Close > bollingerBandElement.K )count++;
}
return count;
}
private bool BandBreakCheck(DateTime analysisDate,BollingerBandElementsByDate bollingerBandElementsByDate, int days)
{
int lowLessLP1=0;
int closeLessLP1=0;
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> dates = dateGenerator.GenerateHistoricalDates(analysisDate, days +1);
dates.RemoveAt(0);
foreach(DateTime date in dates)
{
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[date];
if(bollingerBandElement.Low <= bollingerBandElement.LP1)lowLessLP1++;
if(bollingerBandElement.Close <= bollingerBandElement.LP1)closeLessLP1++;
}
return (lowLessLP1 >= days) && (closeLessLP1 >= days);
}
private bool CloseGreaterSMAN(DateTime analysisDate, BollingerBandElementsByDate bollingerBandElementsByDate, int days)
{
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> dates = dateGenerator.GenerateHistoricalDates(analysisDate, days + 1);
dates.RemoveAt(0);
foreach (DateTime date in dates)
{
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[date];
if (bollingerBandElement.Close > bollingerBandElement.SMAN) return true;
}
return false;
}
/// <summary>
/// This is used to trigger an adjustment to the stop price.
/// </summary>
/// <param name="analysisDate"></param>
/// <param name="position"></param>
/// <returns></returns>
public bool IsLowerBandBreakIndicator(DateTime analysisDate, MGSHPosition position)
{
BollingerBandElementsByDate bollingerBandElementsByDate = GetBollingerBandElementsByDate(analysisDate);
if (null == bollingerBandElementsByDate) return false;
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[analysisDate];
if (bollingerBandElement.High > bollingerBandElement.K)
{
Display(String.Format("+++ [1] Upper band break detected for {0} on {1} Purchase Date:{2} Purchase Price:{3} Price Close:{4} Price High:{5} Price High(hF):{6} KL1:{7}",
HedgeShortSymbol,
analysisDate.ToShortDateString(),
position.PurchaseDate.ToShortDateString(),
Utility.FormatCurrency(position.PurchasePrice),
Utility.FormatCurrency(bollingerBandElement.Close),
Utility.FormatCurrency(bollingerBandElement.High),
Utility.FormatCurrency(bollingerBandElement.High),
Utility.FormatCurrency(bollingerBandElement.K)
));
return true;
}
return false;
}
private double GetHedgeStopLimitWithScalingAverageTrueRange(DateTime analysisDate,Price currentPrice,MGSHPosition position,double stopLimitNonScaled, int stopLimitScalingVolatilityDays, double hedgeATRMultiplier)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetHedgeStopLimitWithScalingAverageTrueRange: Symbol:{0}",position.Symbol));
double volatility=double.NaN;
volatility=VolatilityGenerator.CalculateVolatility(position.Symbol,analysisDate,stopLimitScalingVolatilityDays, hedgeATRMultiplier);
if(double.IsNaN(volatility))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Unable to calculate AverageTrueRange for {0} on {1}. Using non-scaled stop limit.",position.Symbol,analysisDate.ToShortDateString()));
return stopLimitNonScaled;
}
double stopLimit = ((currentPrice.High + currentPrice.Low)/2.00) - volatility; // We base the stop off of the midpoint of the high and the low
return stopLimit;
}
private BollingerBandElementsByDate GetBollingerBandElementsByDate(DateTime analysisDate)
{
Prices prices=GBPriceCache.GetInstance().GetPrices(HedgeShortSymbol, analysisDate, PRICING_DAYS);
if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
{
Display(String.Format("Cannot evaluate IsHedgeCloseIndicatorOn for candidate {0} due to lack of current price on {1}",
HedgeShortSymbol,analysisDate.ToShortDateString()));
return null;
}
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices); // K is upper band, KL1 below that. L is lower band, LP1 above that
BollingerBandElementsByDate bollingerBandElementsByDate = bollingerBands.GetBollingerBandElementsByDate();
if(!bollingerBandElementsByDate.ContainsKey(analysisDate))
{
Display(String.Format("Cannot evaluate IsHedgeCloseIndicatorOn for candidate {0} due to lack of current price on {1}",
HedgeShortSymbol,analysisDate.ToShortDateString()));
return null;
}
return bollingerBandElementsByDate;
}
public void DisplayBandSlopes(DateTime analysisDate)
{
Display($"***** B A N D S L O P E S {analysisDate.ToShortDateString()} *****");
Prices prices=GBPriceCache.GetInstance().GetPrices(HedgeShortSymbol, analysisDate, PRICING_DAYS);
if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
{
Display(String.Format("Cannot evaluate IsHedgeCloseIndicatorOn for candidate {0} due to lack of current price on {1}",
HedgeShortSymbol,analysisDate.ToShortDateString()));
return;
}
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices); // K is upper band, KL1 below that. L is lower band, LP1 above that
BollingerBandElementsByDate bollingerBandElementsByDate = bollingerBands.GetBollingerBandElementsByDate();
if(!bollingerBandElementsByDate.ContainsKey(analysisDate))
{
Display(String.Format("Cannot evaluate IsHedgeCloseIndicatorOn for candidate {0} due to lack of current price on {1}",
HedgeShortSymbol,analysisDate.ToShortDateString()));
return;
}
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[analysisDate];
SlopeManager slopeManager = new SlopeManager(bollingerBandElementsByDate, analysisDate){Verbose=true};
Display("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
slopeManager.DisplaySlopes();
Display("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
slopeManager.DisplaySlopesNumeric();
Display("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
}
}

View File

@@ -0,0 +1,52 @@
using System;
using MarketData.MarketDataModel;
using System.Linq;
namespace MarketData.Generator.MGSHMomentum
{
public class QualityIndicatorCandidate
{
public QualityIndicatorCandidate()
{
}
public String Symbol{get;set;}
public double CumReturn252{get;set;}
public double IDIndicator{get;set;}
public double Score{get;set;}
public int DayCount{get;set;}
public double PE{get;set;}
public double Beta{get;set;}
public double Return1D{get;set;}
}
// *********************************************************************************************************
public class IDIndicator
{
private IDIndicator()
{
}
//CountOf CountOf
//Negative Positive Return Sign IDIndicator
// 50 202 0.2 1 -60.31746032
// 100 152 0.2 1 -20.63492063
// 0 252 0.1 1 -100
// 0 252 0.2 1 -100
// The lower the IDIndicator the better
// This calculator deviates from the original by using an exponential decay on the stream of returns such that weigh distant returns a bit less than current returns
// and thereby attempt to give a more accurate picture of the quality of current returns.
public static double Calculate(Prices prices)
{
double idIndicator=0.00;
double cumulativeReturn=0.00;
float[] returns=prices.GetReturns();
ExponentialDecay exponentialDecay=new ExponentialDecay();
exponentialDecay.Prime(returns,2.00);
for(int index=0;index<returns.Length;index++)returns[index]=returns[index]*(float)exponentialDecay[index];
double positiveCount=(from float value in returns where value>0.00 select value).Count();
double negativeCount=(from float value in returns where value<0.00 select value).Count();
for(int index=0;index<returns.Length;index++)cumulativeReturn+=(double)returns[index];
double sign=cumulativeReturn>0.00?1.00:-1.00;
idIndicator=sign*((negativeCount/returns.Length)*100.00-(positiveCount/returns.Length)*100.00);
return idIndicator;
}
}
}

View File

@@ -0,0 +1,196 @@
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHActivePositions : Dictionary<int,MGSHPositions>
{
public MGSHActivePositions()
{
}
public int GetMaxSlotNumber()
{
List<int> keys = new List<int>(Keys);
if(0 == keys.Count)
{
return -1; // if there are no slots then return -1
}
return keys.Max();
}
public int GetCount()
{
int positionCount=0;
List<int> keys = new List<int>(Keys);
for(int index=0;index<keys.Count;index++)
{
List<MGSHPosition> positions=this[keys[index]];
if(null==positions||0==positions.Count)continue;
positionCount+=positions.Count;
}
return positionCount;
}
public double GetExposure()
{
double exposure=0.00;
List<int> keys = new List<int>(Keys);
for(int index=0;index<keys.Count;index++)
{
List<MGSHPosition> positions=this[keys[index]];
if(null==positions||0==positions.Count)continue;
exposure+=(from MGSHPosition position in positions select position.Exposure).Sum();
}
return exposure;
}
public List<String> GetSymbols()
{
return SymbolsHeld();
}
public MGSHPosition FindPosition(String symbol,DateTime purchaseDate)
{
MGSHPosition foundPosition=null;
List<int> keys=new List<int>(this.Keys);
for(int index=0;index<keys.Count;index++)
{
MGSHPositions positions=this[keys[index]];
foreach(MGSHPosition position in positions)
{
if(position.Symbol.Equals(symbol)&&position.PurchaseDate.Date.Equals(purchaseDate.Date))
{
foundPosition=position;
break;
}
}
}
return foundPosition;
}
public int FindSlotForPosition(MGSHPosition searchPosition)
{
List<int> keys=new List<int>(this.Keys);
for(int index=0;index<keys.Count;index++)
{
MGSHPositions positions=this[keys[index]];
foreach(MGSHPosition position in positions)
{
if(position == searchPosition)return index;
}
}
return -1;
}
public bool Remove(MGSHPosition searchPosition) {
List<int> keys = new List<int>(this.Keys);
for (int index = 0; index < keys.Count; index++) {
MGSHPositions positions = this[keys[index]];
foreach (MGSHPosition slotPosition in positions) {
if (slotPosition == searchPosition) {
positions.Remove(searchPosition);
return true;
}
}
}
return false;
}
public MGSHPositions GetPositions()
{
MGSHPositions positionsCollection=new MGSHPositions();
List<int> keys=new List<int>(this.Keys);
Dictionary<String,String> symbols=new Dictionary<String,String>();
for(int index=0;index<keys.Count;index++)
{
MGSHPositions positions=this[keys[index]];
foreach(MGSHPosition position in positions)
{
positionsCollection.Add(position);
}
}
return positionsCollection;
}
public List<String> SymbolsHeld()
{
List<int> keys = new List<int>(this.Keys);
Dictionary<String, String> symbols = new Dictionary<String, String>();
for (int index = 0; index < keys.Count; index++)
{
MGSHPositions positions = this[keys[index]];
foreach (MGSHPosition position in positions)
{
if (!symbols.ContainsKey(position.Symbol)) symbols.Add(position.Symbol, position.Symbol);
}
}
return new List<String>(symbols.Keys);
}
public double GetMarketValue()
{
double marketValue=0.00;
List<int> keys=new List<int>(this.Keys);
for(int index=0;index<keys.Count;index++)
{
List<MGSHPosition> positions=this[keys[index]];
if(null==positions||0==positions.Count)continue;
marketValue+=(from MGSHPosition position in positions select position.MarketValue).Sum();
}
return marketValue;
}
public double GetGainLoss()
{
double marketValue=0.00;
double exposure=0.00;
List<int> keys = new List<int>(this.Keys);
for(int index=0;index<keys.Count;index++)
{
List<MGSHPosition> positions=this[keys[index]];
if(null==positions||0==positions.Count)continue;
exposure+=(from MGSHPosition position in positions select position.Exposure).Sum();
marketValue+=(from MGSHPosition position in positions select position.MarketValue).Sum();
}
return marketValue-exposure;
}
public double GetGainLossPercent()
{
double exposure=GetExposure();
double marketValue=GetMarketValue();
if(0.00==exposure)return exposure;
return (marketValue-exposure)/exposure;
}
public void Display()
{
List<int> keys = new List<int>(this.Keys);
for(int index=0;index<keys.Count;index++)
{
MGSHPositions positions=this[keys[index]];
DateTime purchaseDate=(from MGSHPosition position in positions select position.PurchaseDate).Distinct().FirstOrDefault();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("******************* B U Y S F O R {0} *****************",Utility.DateTimeToStringMMHDDHYYYY(purchaseDate)));
positions.Display();
}
}
public void AddFromNVPCollection(NVPCollection nvpCollection)
{
MGSHSlotPosition slotPosition=MGSHSlotPosition.FromNVPCollection(nvpCollection);
if(!ContainsKey(slotPosition.Slot))
{
Add(slotPosition.Slot,new MGSHPositions());
}
this[slotPosition.Slot].Add(slotPosition.ToPosition());
}
public List<NVPCollections> ToNVPCollections()
{
List<int> keys=new List<int>(Keys);
List<NVPCollections> nvpCollectionsList=new List<NVPCollections>();
for(int index=0;index<keys.Count;index++)
{
MGSHPositions positions=this[keys[index]];
MGSHSlotPositions slotPositions=new MGSHSlotPositions(keys[index],positions);
NVPCollections nvpCollections=slotPositions.ToNVPCollections();
nvpCollectionsList.Add(nvpCollections);
}
return nvpCollectionsList;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
using MarketData.Utils;
using System.Text;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHBacktestResult
{
public MGSHBacktestResult()
{
}
public MGSHBacktestResult(bool success, double cashBalance)
{
Success=success;
CashBalance=cashBalance;
}
public double CashBalance{get;set;}
public bool Success{get;set;}
public void Display()
{
StringBuilder sb = new StringBuilder();
MDTrace.WriteLine(LogLevel.DEBUG,$"RESULT:{Success.ToString()} CASH BALANCE:{Utility.FormatCurrency(CashBalance)}");
}
}
}

View File

@@ -0,0 +1,392 @@
using System;
using System.Runtime.InteropServices;
using MarketData.Utils;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHConfiguration
{
// Operational Settings
public bool Verbose{get;set;}
// Basic settings
public int HoldingPeriod{get;set;}
public int MaxPositions{get;set;}
public String NoTradeSymbols{get;set;}
public String NoTradeFinancialSymbols{get;set;}
public double InitialCash{get;set;} // There is no defaullt for this value. It is supplied to the model at launch time.
// Stop Limit Functionality
public bool UseStopLimits{get;set;}
public double StopLimitRiskPercentDecimal{get;set;}
public int StopLimitScalingVolatilityDays{get;set;}
public int MinDaysBetweenStopAdjustments{get;set;}
public int MinDaysBetweenInitialStopAdjustment{get;set;}
public int StopLimitPriceTrendDays{get;set;}
public double StopLimitATRMultiplier{get;set;}
// Hedging Strategy
public bool UseHedging{get;set;}
public String HedgeBenchmarkSymbol{get;set;}
public String HedgeShortSymbol{get;set;}
public double HedgeRiskPercentDecimal{get;set;}
public int HedgeMinDaysBetweenStopAdjustments;
public double HedgeInitialCash{get;set;} // There is no default for this value. It is supplied to the model at launch time
public int HedgeCloseAboveSMANDays{get;set;}
public int HedgeBandBreakCheckDays{get;set;}
public double HedgeATRMultiplier{get;set;}
// Manage buying and selling
public bool KeepSlotPositions{get;set;} // if this setting is true then we never sell slot positions, allowing the trailing stop to eventually invoke a sell.
// Pricing Exceptions
public int MaxPricingExceptions{get;set;} // This is the pricing exception limit. If we have this many of exceptions then we will sell the security at the last known good price.
// Fundamental screenings
public double MarketCapLowerLimit{get;set;}
public bool UsePEScreen{get;set;} // If set this filter will ignore any security that is either missing a PE or if the PE is present but less than 0
public bool UseMaxPEScreen{get;set;} // control Max PE range check
public double MaxPE{get;set;} // if UseMaxPECheck is set this setting will ignore any security who's PE is greater. If PE is missing candidate will be accepted
public bool StrictMaxPE{get;set;} // if TRUE then UseMaxPEScreen is STRICT otherwise UseMaxPEScreen is SOFT RULE where >MaxPE is AVOIDED but used to fill the quota. The quota is filled by ranking highPECandidates by PE and taking the lower PE's up to the quota. Soft rule yields best result
// EBITDA screen
public bool UseEBITDAScreen{get;set;}
public bool UseRevenuePerShareScreen{get;set;}
// BETA
public bool UseBetaGenerator{get;set;} // If set then the model will use the internal beta generator. Otherwise it will use Beta from Fundamental data
public int UseBetaGeneratorMonths{get;set;} // If UseBetaGenerator=true then UseBetaGeneratorMonths will prescribe the number of months over which to calculate the monthly beta
// If slope Beta Check is true then we compare the fundamental Beta to the threshhold value. If Beta>Threshhold AND the slope from the Benchmark LowPrice over BetaDays is <0 we reject
public String Benchmark{get;set;}
public bool UseLowSlopeBetaCheck{get;set;}
public int LowSlopeBetaDays{get;set;}
public double LowSlopeBetaThreshhold{get;set;}
// MACD Settings : If MACD is being used then the process configures the MACD as per setup and eliminates candidates with a weak sell/strong sell signal in the signal days setting.
public bool UseMACD{get;set;}
public String MACDSetup="(12,26,9)"; // (8,17,9) is another alternative
public int MACDSignalDays{get;set;}
public bool MACDRejectWeakSellSignals{get;set;}
public bool MACDRejectStrongSellSignals{get;set;}
// Stochastics Settings : If Stochastics is being used then the process eliminates candidates with a weak sell/strong sell in the signal days setting.
public bool UseStochastics{get;set;}
public int StochasticsSignalDays{get;set;}
public bool StochasticsRejectWeakSells{get;set;}
public bool StochasticsRejectStrongSells{get;set;}
// FallbackCandidate : If this setting is true then the FallbackCandidate is purchased if there are no candidates that qualify in a given cycle
public bool UseFallbackCandidate{get;set;}
public String FallbackCandidate{get;set;}
public String FallbackCandidateBestOf{get;set;} // if this is set then the fallback candidate with the best 252 day return is selected
// Benchmark mode : If this is set to true then purchases the benchmark symbol instead of the momentum candidates
public bool BenchmarkMode{get;set;}
public String BenchmarkModeSymbol{get;set;}
// QualityIndicator
public String QualityIndicatorType{get;set;} // this can be either IDINDICATOR or SCOREINDICATOR. The SCOREINDICATOR was adopted from CMMomentum model
// IncludeTradeMasterForSymbolsHeld :
public bool IncludeTradeMasterForSymbolsHeld{get;set;} // if this is set to true then use both ActivePositions within the model as well as the master trades table to determine held positions.
public MGSHConfiguration()
{
Verbose=true; // user verbose output
BenchmarkMode=false; // set this to true if you want to run the model using just the benchmark symbol to buy.
BenchmarkModeSymbol="SPY"; // SPY is the default symbol to buy when testing
HoldingPeriod=3; // 3 is the default. Hold for three months
MaxPositions=3; // 3 is the default. Max positions per slot
NoTradeSymbols="GBTC,YOKU,PNY,RFMD,ASAZY"; // ASAZY came up as candidate during 3/30 run but not available on Robinhood
NoTradeFinancialSymbols="U.S. Private Equity,U.S. Financials,U.S. Financial Services,U.S. Banking and Investment Services,Trading-Miscellaneous,Trading--Miscellaneous,Trading--Leveraged Equity,Trading--Leveraged Debt,Trading--Leveraged Commodities,Trading--Inverse Equity,Trading--Inverse Commodities,Tactical Allocation,Specialty Finance,Japan Financials,Savings & Cooperative Banks,Option Writing,Insurance Brokers,Insurance - Specialty,Insurance - Reinsurance,Insurance - Property & Casualty,Insurance - Life,Insurance - Diversified,Global Private Equity,Global Financials,Financial Services,Financial Exchanges,Financial,China Financials,Banks - Regional - US,Banks - Regional - Latin America,Banks - Global,Asset Management,Credit Services";
Benchmark="SPY"; // SPY is the default
// Candidate selection settings
MarketCapLowerLimit=1000000000; // 1B is the default
// PEScreen is off by default
UsePEScreen=false; // false is the default. This setting yields the most optimal performance in backtests. Checks for existance of PE and if exists ensures >0
// UseMaxPEScreen, MaxPE, and StrictPEExclusion
UseMaxPEScreen=true;
MaxPE=40;
StrictMaxPE=false;
// Beta
UseBetaGenerator=true; // The default setting is to calculate our own beta rather than use the beta from the fundamental data
UseBetaGeneratorMonths=24; // The default number of months over which to calculate our monthly return stream for the beta generator
// Stop Limits
UseStopLimits=false; // Flag to control the use of stop limits
StopLimitRiskPercentDecimal=.17; // The per share risk to take when setting the initial stop. This was the best setting after running a suite of tests
StopLimitScalingVolatilityDays=30; // This is the number of days to use in the average true range calculation
MinDaysBetweenInitialStopAdjustment=30; // 30 Number of days that must elapse before attempting to adjust the stop limit. This is the best setting
MinDaysBetweenStopAdjustments=30; // 30 Number of days between stop adjustments. This is after the initial stop is set... of course
StopLimitPriceTrendDays=20; // The number of days for which we want to see upward trend before adjusting a subsequent stop limit
StopLimitATRMultiplier=3.00; // the ATR multiplier for setting stop limits on regular positions
// Hedging Strategy
UseHedging=false; // Flag to control the use of hedging strategy
HedgeBenchmarkSymbol="SPY"; // The benchmark symbol for the hedging strategy
HedgeShortSymbol="SH"; // The symbol that is used to go short
HedgeRiskPercentDecimal=.12; // This will be the risk to assume with the hedge position.
HedgeMinDaysBetweenStopAdjustments=1; // We use a single day for hedge positions
HedgeCloseAboveSMANDays=10; // Part of open hedge indicator if Close is not above Bollinger SMAN for this many days then reject
HedgeBandBreakCheckDays=3; // Number of days that low and close must be <= LP1 in order to open hedge. If >= number of days then reject
HedgeATRMultiplier=1.00; // 1.00 produces best results for the hedging stops. The default ATR multiplier in the code Volatility Calculator is 3 which gives a good spread when adjusting stop prices.
// Manage buying and selling
KeepSlotPositions=true; // The default is true to retain legacy functionality
MaxPricingExceptions=3; // The maximum number of pricing exceptions. If exceeded then the security will be sold at the last known good price
// Other settings
UseEBITDAScreen=true; // true is the default
UseRevenuePerShareScreen=true; // true is the default
UseLowSlopeBetaCheck=true; // true is the default. this yields the most optimal performance in backtests
LowSlopeBetaDays=15; // 15 is the default. This yields the most optimal performance in backtests
LowSlopeBetaThreshhold=1.00; // (1.00) is the default This yields the most optimal performance in backtests
UseMACD=true; // true is the default
MACDSetup="(12,26,9)"; // (12,26,9)
MACDSignalDays=12; // 12 is the default
MACDRejectStrongSellSignals=false; // false is the default
MACDRejectWeakSellSignals=true; // true is the default
UseStochastics=true; // true is the default
StochasticsSignalDays=3; // 3 is the default
StochasticsRejectStrongSells=true; // true is the default
StochasticsRejectWeakSells=true; // true is the default
// Fallback candidate settings
UseFallbackCandidate=true; // True is the default
FallbackCandidate="SHV"; // "SHV" ICE U.S. Treasury Short Bond Index, "AGG" Barclays U.S. Aggregate Bond Index - AGG can have a slighty better return but is more volatile
FallbackCandidateBestOf="SHV,NEAR,BIL,GSY,AGG,ACWX,GSY,SCHF,IXUS,DBEF,IEFA,TLT"; // if set then the fallback candidate is selected as the best 252 day return in this comma seperated list (i.e.) "SHV,ACWX,AGG,SHV,NEAR,BIL,GSY,AGG,ACWX,GSY,SCHF,IXUS,DBEF,IEFA
// Set the QualityIndicator type to the ScoreIndicator by default.
QualityIndicatorType=MGSHQualityIndicator.ToString(MGSHQualityIndicator.QualityType.ScoreIndicator);
IncludeTradeMasterForSymbolsHeld=false; // If this is set to true then the model takes into account any open positions in other models etc.,
}
public void DisplayHeader()
{
MDTrace.WriteLine(LogLevel.DEBUG,"Setting,Value");
}
public NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection=new NVPCollection();
nvpCollection.Add(new NVP("Verbose",Verbose.ToString()));
nvpCollection.Add(new NVP("KeepSlotPositions",KeepSlotPositions.ToString()));
nvpCollection.Add(new NVP("BenchmarkMode",BenchmarkMode.ToString()));
nvpCollection.Add(new NVP("BenchmarkModeSymbol",BenchmarkModeSymbol.ToString()));
nvpCollection.Add(new NVP("HoldingPeriod",HoldingPeriod.ToString()));
nvpCollection.Add(new NVP("MaxPositions",MaxPositions.ToString()));
nvpCollection.Add(new NVP("NoTradeSymbols",NoTradeSymbols.ToString()));
nvpCollection.Add(new NVP("NoTradeFinancialSymbols",NoTradeFinancialSymbols.ToString()));
nvpCollection.Add(new NVP("Benchmark",Benchmark.ToString()));
nvpCollection.Add(new NVP("MarketCapLowerLimit",MarketCapLowerLimit.ToString()));
nvpCollection.Add(new NVP("UsePEScreen",UsePEScreen.ToString()));
nvpCollection.Add(new NVP("UseEBITDAScreen",UseEBITDAScreen.ToString()));
nvpCollection.Add(new NVP("UseRevenuePerShareScreen",UseRevenuePerShareScreen.ToString()));
nvpCollection.Add(new NVP("UseLowSlopeBetaCheck",UseLowSlopeBetaCheck.ToString()));
nvpCollection.Add(new NVP("LowSlopeBetaDays",LowSlopeBetaDays.ToString()));
nvpCollection.Add(new NVP("LowSlopeBetaThreshhold",LowSlopeBetaThreshhold.ToString()));
nvpCollection.Add(new NVP("UseMACD",UseMACD.ToString()));
nvpCollection.Add(new NVP("MACDSetup",MACDSetup.ToString()));
nvpCollection.Add(new NVP("MACDSignalDays",MACDSignalDays.ToString()));
nvpCollection.Add(new NVP("MACDRejectStrongSellSignals",MACDRejectStrongSellSignals.ToString()));
nvpCollection.Add(new NVP("MACDRejectWeakSellSignals",MACDRejectWeakSellSignals.ToString()));
nvpCollection.Add(new NVP("UseStochastics",UseStochastics.ToString()));
nvpCollection.Add(new NVP("StochasticsSignalDays",StochasticsSignalDays.ToString()));
nvpCollection.Add(new NVP("StochasticsRejectStrongSells",StochasticsRejectStrongSells.ToString()));
nvpCollection.Add(new NVP("StochasticsRejectWeakSells",StochasticsRejectWeakSells.ToString()));
nvpCollection.Add(new NVP("UseFallbackCandidate",UseFallbackCandidate.ToString()));
nvpCollection.Add(new NVP("FallbackCandidate",FallbackCandidate.ToString()));
nvpCollection.Add(new NVP("FallbackCandidateBestOf",FallbackCandidateBestOf.ToString()));
nvpCollection.Add(new NVP("UseMaxPEScreen",UseMaxPEScreen.ToString()));
nvpCollection.Add(new NVP("MaxPE",MaxPE.ToString()));
nvpCollection.Add(new NVP("StrictMaxPE",StrictMaxPE.ToString()));
nvpCollection.Add(new NVP("QualityIndicatorType",QualityIndicatorType.ToString()));
nvpCollection.Add(new NVP("IncludeTradeMasterForSymbolsHeld",IncludeTradeMasterForSymbolsHeld.ToString()));
nvpCollection.Add(new NVP("UseStopLimits",UseStopLimits.ToString()));
nvpCollection.Add(new NVP("StopLimitRiskPercentDecimal",StopLimitRiskPercentDecimal.ToString()));
nvpCollection.Add(new NVP("StopLimitScalingVolatilityDays",StopLimitScalingVolatilityDays.ToString()));
nvpCollection.Add(new NVP("MinDaysBetweenInitialStopAdjustment",MinDaysBetweenInitialStopAdjustment.ToString()));
nvpCollection.Add(new NVP("MinDaysBetweenStopAdjustments",MinDaysBetweenStopAdjustments.ToString()));
nvpCollection.Add(new NVP("StopLimitPriceTrendDays",StopLimitPriceTrendDays.ToString()));
nvpCollection.Add(new NVP("StopLimitATRMultiplier",StopLimitATRMultiplier.ToString()));
nvpCollection.Add(new NVP("UseHedging",UseHedging.ToString()));
nvpCollection.Add(new NVP("HedgeBenchmarkSymbol",HedgeBenchmarkSymbol.ToString()));
nvpCollection.Add(new NVP("HedgeShortSymbol",HedgeShortSymbol.ToString()));
nvpCollection.Add(new NVP("HedgeRiskPercentDecimal",HedgeRiskPercentDecimal.ToString()));
nvpCollection.Add(new NVP("HedgeMinDaysBetweenStopAdjustments",HedgeMinDaysBetweenStopAdjustments.ToString()));
nvpCollection.Add(new NVP("HedgeInitialCash",HedgeInitialCash.ToString()));
nvpCollection.Add(new NVP("HedgeCloseAboveSMANDays",HedgeCloseAboveSMANDays.ToString()));
nvpCollection.Add(new NVP("HedgeBandBreakCheckDays",HedgeBandBreakCheckDays.ToString()));
nvpCollection.Add(new NVP("HedgeATRMultiplier",HedgeATRMultiplier.ToString()));
nvpCollection.Add(new NVP("MaxPricingExceptions",MaxPricingExceptions.ToString()));
nvpCollection.Add(new NVP("UseBetaGenerator",UseBetaGenerator.ToString()));
nvpCollection.Add(new NVP("UseBetaGeneratorMonths",UseBetaGeneratorMonths.ToString()));
return nvpCollection;
}
public static MGSHConfiguration FromNVPCollection(NVPCollection nvpCollection)
{
MGSHConfiguration mgConfiguration=new MGSHConfiguration();
NVPDictionary nvpDictionary=nvpCollection.ToDictionary();
mgConfiguration.Verbose=nvpDictionary["Verbose"].Get<Boolean>();
if(nvpDictionary.ContainsKey("KeepSlotPositions")) mgConfiguration.KeepSlotPositions=nvpDictionary["KeepSlotPositions"].Get<bool>();
mgConfiguration.BenchmarkMode=nvpDictionary["BenchmarkMode"].Get<Boolean>();
mgConfiguration.BenchmarkModeSymbol=nvpDictionary["BenchmarkModeSymbol"].Get<String>();
mgConfiguration.HoldingPeriod=nvpDictionary["HoldingPeriod"].Get<int>();
mgConfiguration.MaxPositions=nvpDictionary["MaxPositions"].Get<int>();
mgConfiguration.NoTradeSymbols=nvpDictionary["NoTradeSymbols"].Get<String>();
mgConfiguration.NoTradeFinancialSymbols=nvpDictionary["NoTradeFinancialSymbols"].Get<String>();
mgConfiguration.Benchmark=nvpDictionary["Benchmark"].Get<String>();
mgConfiguration.MarketCapLowerLimit=nvpDictionary["MarketCapLowerLimit"].Get<double>();
mgConfiguration.UsePEScreen=nvpDictionary["UsePEScreen"].Get<Boolean>();
mgConfiguration.UseMaxPEScreen=nvpDictionary["UseMaxPEScreen"].Get<Boolean>();
mgConfiguration.MaxPE=nvpDictionary["MaxPE"].Get<double>();
mgConfiguration.StrictMaxPE=nvpDictionary["StrictMaxPE"].Get<Boolean>();
mgConfiguration.UseEBITDAScreen=nvpDictionary["UseEBITDAScreen"].Get<Boolean>();
mgConfiguration.UseRevenuePerShareScreen=nvpDictionary["UseRevenuePerShareScreen"].Get<Boolean>();
mgConfiguration.UseLowSlopeBetaCheck=nvpDictionary["UseLowSlopeBetaCheck"].Get<Boolean>();
mgConfiguration.LowSlopeBetaDays=nvpDictionary["LowSlopeBetaDays"].Get<int>();
mgConfiguration.LowSlopeBetaThreshhold=nvpDictionary["LowSlopeBetaThreshhold"].Get<double>();
mgConfiguration.UseMACD=nvpDictionary["UseMACD"].Get<Boolean>();
mgConfiguration.MACDSetup=nvpDictionary["MACDSetup"].Get<String>();
mgConfiguration.MACDSignalDays=nvpDictionary["MACDSignalDays"].Get<int>();
mgConfiguration.MACDRejectStrongSellSignals=nvpDictionary["MACDRejectStrongSellSignals"].Get<Boolean>();
mgConfiguration.MACDRejectWeakSellSignals=nvpDictionary["MACDRejectWeakSellSignals"].Get<Boolean>();
mgConfiguration.UseStochastics=nvpDictionary["UseStochastics"].Get<Boolean>();
mgConfiguration.StochasticsSignalDays=nvpDictionary["StochasticsSignalDays"].Get<int>();
mgConfiguration.StochasticsRejectStrongSells=nvpDictionary["StochasticsRejectStrongSells"].Get<Boolean>();
mgConfiguration.StochasticsRejectWeakSells=nvpDictionary["StochasticsRejectWeakSells"].Get<Boolean>();
mgConfiguration.UseFallbackCandidate=nvpDictionary["UseFallbackCandidate"].Get<Boolean>();
mgConfiguration.FallbackCandidate=nvpDictionary["FallbackCandidate"].Get<String>();
mgConfiguration.FallbackCandidateBestOf=nvpDictionary["FallbackCandidateBestOf"].Get<String>();
mgConfiguration.MaxPricingExceptions=nvpDictionary["MaxPricingExceptions"].Get<int>();
if(nvpDictionary.ContainsKey("QualityIndicatorType")) mgConfiguration.QualityIndicatorType=nvpDictionary["QualityIndicatorType"].Get<String>();
else mgConfiguration.QualityIndicatorType=MGSHQualityIndicator.ToString(MGSHQualityIndicator.QualityType.IDIndicator);
if(nvpDictionary.ContainsKey("IncludeTradeMasterForSymbolsHeld")) mgConfiguration.IncludeTradeMasterForSymbolsHeld=nvpDictionary["IncludeTradeMasterForSymbolsHeld"].Get<bool>();
else mgConfiguration.IncludeTradeMasterForSymbolsHeld=false;
// Stop Limits
if(nvpDictionary.ContainsKey("UseStopLimits"))
{
mgConfiguration.UseStopLimits = nvpDictionary["UseStopLimits"].Get<bool>();
mgConfiguration.StopLimitRiskPercentDecimal = nvpDictionary["StopLimitRiskPercentDecimal"].Get<double>();
mgConfiguration.StopLimitScalingVolatilityDays = nvpDictionary["StopLimitScalingVolatilityDays"].Get<int>();
mgConfiguration.MinDaysBetweenInitialStopAdjustment = nvpDictionary["MinDaysBetweenInitialStopAdjustment"].Get<int>();
mgConfiguration.MinDaysBetweenStopAdjustments = nvpDictionary["MinDaysBetweenStopAdjustments"].Get<int>();
mgConfiguration.StopLimitPriceTrendDays = nvpDictionary["StopLimitPriceTrendDays"].Get<int>();
if(nvpDictionary.ContainsKey("StopLimitATRMultiplier"))
{
mgConfiguration.StopLimitATRMultiplier = nvpDictionary["StopLimitATRMultiplier"].Get<double>();
}
}
else
{
mgConfiguration.UseStopLimits = false;
}
// Hedging
if(nvpDictionary.ContainsKey("UseHedging"))
{
mgConfiguration.UseHedging = nvpDictionary["UseHedging"].Get<bool>();
mgConfiguration.HedgeBenchmarkSymbol = nvpDictionary["HedgeBenchmarkSymbol"].Get<String>();
mgConfiguration.HedgeShortSymbol = nvpDictionary["HedgeShortSymbol"].Get<string>();
mgConfiguration.HedgeRiskPercentDecimal = nvpDictionary["HedgeRiskPercentDecimal"].Get<double>();
mgConfiguration.HedgeMinDaysBetweenStopAdjustments = nvpDictionary["HedgeMinDaysBetweenStopAdjustments"].Get<int>();
mgConfiguration.HedgeInitialCash = nvpDictionary["HedgeInitialCash"].Get<int>();
mgConfiguration.HedgeCloseAboveSMANDays = nvpDictionary["HedgeCloseAboveSMANDays"].Get<int>();
mgConfiguration.HedgeBandBreakCheckDays = nvpDictionary["HedgeBandBreakCheckDays"].Get<int>();
mgConfiguration.HedgeATRMultiplier = nvpDictionary["HedgeATRMultiplier"].Get<double>();
}
else
{
mgConfiguration.UseHedging=false;
}
if(nvpDictionary.ContainsKey("UseBetaGenerator"))
{
mgConfiguration.UseBetaGenerator = nvpDictionary["UseBetaGenerator"].Get<bool>();
mgConfiguration.UseBetaGeneratorMonths = nvpDictionary["UseBetaGeneratorMonths"].Get<int>();
}
return mgConfiguration;
}
public void DisplayConfiguration()
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Verbose,{0}",Verbose));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxPricingExceptions,{0}",MaxPricingExceptions));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("KeepSlotPositions,{0}",KeepSlotPositions));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Holding Period,{0}",HoldingPeriod));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxPositions,{0}",MaxPositions));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("NoTradeSymbols,{0}",NoTradeSymbols));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("NoTradeFinancialSymbols,{0}",NoTradeFinancialSymbols));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Benchmark,{0}",Benchmark));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MarketCapLowerLimit,{0}",MarketCapLowerLimit));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UsePEScreen,{0}",UsePEScreen));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMaxPEScreen,{0}",UseMaxPEScreen));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxPE,{0}",MaxPE));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StrictMaxPE,{0}",StrictMaxPE));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseEBITDAScreen,{0}",UseEBITDAScreen));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseRevenuePerShareScreen,{0}",UseRevenuePerShareScreen));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseLowSlopeBetaCheck,{0}",UseLowSlopeBetaCheck));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("LowSlopeBetaDays,{0}",LowSlopeBetaDays));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("LowSlopeBetaThreshhold,{0}",LowSlopeBetaThreshhold));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMACD,{0}",UseMACD));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MACDSetup,{0}",MACDSetup));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MACDSignalDays,{0}",MACDSignalDays));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MACDRejectStrongSellSignals,{0}",MACDRejectStrongSellSignals));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MACDRejectWeakSellSignals,{0}",MACDRejectWeakSellSignals));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseStochastics,{0}",UseStochastics));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StochasticsSignalDays,{0}",StochasticsSignalDays));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StochasticsRejectStrongSells,{0}",StochasticsRejectStrongSells));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StochasticsRejectWeakSells,{0}",StochasticsRejectWeakSells));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseFallbackCandidate,{0}",UseFallbackCandidate));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("FallbackCandidate,{0}",FallbackCandidate));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("FallbackCandidateBestOf,{0}",FallbackCandidateBestOf));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BenchmarkMode,{0}",BenchmarkMode));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BenchmarkSymbol,{0}",BenchmarkModeSymbol));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("QualityIndicatorType,{0}",QualityIndicatorType));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IncludeTradeMasterForSymbolsHeld,{0}",IncludeTradeMasterForSymbolsHeld));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseStopLimits,{0}",UseStopLimits));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StopLimitRiskPercentDecimal,{0}",StopLimitRiskPercentDecimal));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StopLimitScalingVolatilityDays,{0}",StopLimitScalingVolatilityDays));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinDaysBetweenInitialStopAdjustment,{0}",MinDaysBetweenInitialStopAdjustment));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinDaysBetweenStopAdjustments,{0}",MinDaysBetweenStopAdjustments));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StopLimitPriceTrendDays,{0}",StopLimitPriceTrendDays));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StopLimitATRMultiplier,{0}",StopLimitATRMultiplier));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseHedging,{0}",UseHedging));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("HedgeBenchmarkSymbol,{0}",HedgeBenchmarkSymbol));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("HedgeShortSymbol,{0}",HedgeShortSymbol));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("HedgeRiskPercentDecimal,{0}", HedgeRiskPercentDecimal));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("HedgeMinDaysBetweenStopAdjustments,{0}", HedgeMinDaysBetweenStopAdjustments));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("HedgeInitialCash,{0}", HedgeInitialCash));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("HedgeCloseAboveSMANDays,{0}", HedgeCloseAboveSMANDays));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("HedgeBandBreakCheckDays,{0}", HedgeBandBreakCheckDays));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("HedgeATRMultiplier,{0}", HedgeATRMultiplier));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("UseBetaGenerator,{0}", UseBetaGenerator));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("UseBetaGeneratorMonths,{0}", UseBetaGeneratorMonths));
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Text;
using MarketData.Utils;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHMomentumCandidates : List<MGSHMomentumCandidate>
{
public MGSHMomentumCandidates()
{
}
public MGSHMomentumCandidates(List<MGSHMomentumCandidate> momentumCandidates)
{
foreach(MGSHMomentumCandidate momentumCandidate in momentumCandidates)Add(momentumCandidate);
}
}
public class MGSHMomentumCandidate
{
public String Symbol{get;set;}
public DateTime AnalysisDate{get;set;}
public double CumReturn252{get;set;}
public int DayCount{get;set;}
public double IDIndicator{get;set;} // This is the IDIndicator methodology used for quality. This is the default methodology
public double Score{get;set;} // This is the Score methodology used for quality. This one is taken from Andreas Clenow Momentum
public double PE{get;set;}
public double Beta{get;set;}
public double Velocity{get;set;}
public long Volume{get;set;}
public double Return1D{get;set;}
public double SharpeRatio{get;set;}
public static String Header()
{
StringBuilder sb=new StringBuilder();
sb.Append("Symbol,AnalysisDate,Return,DayCount,IDIndicator,Score");
return sb.ToString();
}
public override String ToString()
{
StringBuilder sb=new StringBuilder();
sb.Append(Symbol).Append(",").
Append(AnalysisDate).
Append(",").
Append(Utility.FormatPercent(CumReturn252)).
Append(",").Append(DayCount).
Append(",").
Append(IDIndicator).
Append(",").
Append(Score).
Append(",");
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,368 @@
using System;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using System.Linq;
using MarketData.Numerical;
using MarketData.Cache;
using System.Configuration;
// Filename: MGSHMomentumGenerator.cs
// Author:Sean Kessler
// Date:02/2025
// Summary: This model was adapted from th MGMomentum model. This new model adds the following new functionality
// 1) Places stop limits on positions. This is configurable
// 2) Incorporates ability to buy a hedge position. This is configurable
// 3) The model does not sell all positions at month end. Instead, it holds positions until they stop out. This is configurable
//BetaGenerator.Beta(symbol,tradeDate,cmtParams.BetaMonths); // beta months is 6
namespace MarketData.Generator.MGSHMomentum
{
/// <summary>Generate momentum selections - </summary>
public class MGSHMomentumGenerator
{
public enum MomentumGeneratorConstants{DayCount=252}; // Trading days in one year
private MGSHMomentumGenerator()
{
}
// These two interfaces are used by the UI so that it can capture the fallback candidates
public static MGSHMomentumCandidates GenerateMomentum(DateTime tradeDate,MGSHConfiguration config)
{
List<String> symbolsHeld=new List<String>();
return new MGSHMomentumCandidates(GenerateMomentum(tradeDate,symbolsHeld,config).Take(config.MaxPositions).ToList());
}
public static MGSHMomentumCandidates GenerateMomentumWithFallback(DateTime tradeDate,MGSHConfiguration config)
{
List<String> symbolsHeld=new List<String>();
MGSHMomentumCandidates momentumCandidates=GenerateMomentum(tradeDate,symbolsHeld,config);
MGSHQualityIndicator qualityIndicator=new MGSHQualityIndicator(config.QualityIndicatorType);
if((null==momentumCandidates||0==momentumCandidates.Count)&&config.UseFallbackCandidate)
{
QualityIndicatorCandidate bestCandidate=null;
if(null!=config.FallbackCandidateBestOf && !"".Equals(config.FallbackCandidateBestOf))
{
bestCandidate=CandidateSelector.SelectBestCandidate(qualityIndicator,Utility.ToList(config.FallbackCandidateBestOf),config.FallbackCandidate,tradeDate);
if(null!=bestCandidate)
{
MGSHMomentumCandidate momentumCandidate=new MGSHMomentumCandidate();
momentumCandidate.Symbol=bestCandidate.Symbol;
momentumCandidate.AnalysisDate=tradeDate;
momentumCandidate.CumReturn252=bestCandidate.CumReturn252;
momentumCandidate.IDIndicator=bestCandidate.IDIndicator;
momentumCandidate.Score=bestCandidate.Score;
momentumCandidate.DayCount=bestCandidate.DayCount;
momentumCandidate.PE=bestCandidate.PE;
momentumCandidate.Beta=bestCandidate.Beta;
momentumCandidate.Return1D=bestCandidate.Return1D;
momentumCandidates=new MGSHMomentumCandidates();
momentumCandidates.Add(momentumCandidate);
}
}
}
return momentumCandidates;
}
// This interface is called by the Backtest
public static MGSHMomentumCandidates GenerateMomentum(DateTime tradeDate,List<String> symbolsHeld,MGSHConfiguration config)
{
DateGenerator dateGenerator=new DateGenerator();
List<String> symbols=PricingDA.GetSymbols();
MGSHMomentumCandidates momentumCandidates=new MGSHMomentumCandidates();
MGSHMomentumCandidates highPECandidates=new MGSHMomentumCandidates();
DateTime startDateOfReturns=dateGenerator.GetPrevMonthEnd(tradeDate,2);
List<String> noTradeSymbols=Utility.ToList(config.NoTradeSymbols);
List<String> noTradeFinancialSymbols=Utility.ToList(config.NoTradeFinancialSymbols);
CandidateViolations candidateViolations = new CandidateViolations();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Generate momentum.. examining candidates"));
// Go through the universe of stocks
for(int index=0;index<symbols.Count;index++)
{
String symbol=symbols[index];
if(0==(index%500))Console.WriteLine("Processing item {0} of {1}",index+1,symbols.Count);
// Check if the symbol is held in any open positions
if(symbolsHeld.Any(x=>x.Equals(symbol, StringComparison.CurrentCultureIgnoreCase)))
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate already held."));
continue;
}
// Check if the symbol is in the no trade list (i.e.) Bitcoin etc.,
if(noTradeSymbols.Any(x=>x.Equals(symbol, StringComparison.CurrentCultureIgnoreCase)))
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate in NoTradeSymbol."));
continue;
}
// Check MarketCap, EBITDA, PE, and Revenue Per Share
Fundamental fundamental=FundamentalDA.GetFundamentalMaxDate(symbol,tradeDate);
if(null==fundamental)
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate no fundamental."));
continue;
}
if(!(fundamental.MarketCap>=config.MarketCapLowerLimit))
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate MarketCapLimit."));
continue;
}
if(config.UseEBITDAScreen && (double.IsNaN(fundamental.EBITDA)||fundamental.EBITDA<=0))
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate EBITDA violation."));
continue;
}
if(config.UseRevenuePerShareScreen && (double.IsNaN(fundamental.RevenuePerShare)||fundamental.RevenuePerShare<0.00))
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate RevenuePerShare violation."));
continue;
}
// Initial PE screening. This screen checks for existance of PE and if it is availabe it must be >0.00 . There is another PE based on limits further below
if(config.UsePEScreen && (double.IsNaN(fundamental.PE)||fundamental.PE<=0.00))
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate PE violation."));
continue;
}
// Exclude any company in the "Financial" sector
CompanyProfile companyProfile=CompanyProfileDA.GetCompanyProfile(symbol);
if(null!=companyProfile&&null!=companyProfile.Sector&&noTradeFinancialSymbols.Any(x=>x.Equals(companyProfile.Sector)))
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate Financial Sector violation."));
continue;
}
// Fetch single day price
Price price=GBPriceCache.GetInstance().GetPrice(symbol,tradeDate);
if(null==price)
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate missing price on trade date."));
continue;
}
// Filter penny stocks - don't trade anything less than $1.00
if(price.Close<1.00||price.Open<1.00)
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate penny stock violation."));
continue;
}
// Retrieve prices
Prices prices=null;
prices=GBPriceCache.GetInstance().GetPrices(symbol,tradeDate,(int)MomentumGeneratorConstants.DayCount);
if(null==prices||prices.Count!=(int)MomentumGeneratorConstants.DayCount)
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate missing price history."));
continue;
}
// calculate the one day return
double return1D=prices.GetReturn1D();
// Liquidity check - if any day has volume < 10,000 then we reject it
if(((from Price xPrice in prices where xPrice.Volume<10000 select xPrice).Count())>1)
{
candidateViolations.Add(new CandidateViolation(symbol,"Liquidity violation."));
continue;
}
// Calculate velocity as a percentage range of the open price within the 252+20 day range of prices - This is used for display purposes
double velocity;
Prices velocityPrices=GBPriceCache.GetInstance().GetPrices(symbol,tradeDate,(int)MGSHMomentumGenerator.MomentumGeneratorConstants.DayCount+20);
double priceHigh=(from Price selectPrice in velocityPrices select selectPrice.Open).Max();
double priceLow=(from Price selectPrice in velocityPrices select selectPrice.Open).Min();
if(0.00==priceHigh-priceLow)velocity=0.00;
else velocity=((price.Open-priceLow)*(100/(priceHigh-priceLow)))/100.00;
// Price slopes - These are used for display purposes
double[] pricesArray=null;
LeastSquaresResult leastSquaresResult;
// Beta - first capture the fundamental beta and then determine whether we will calculate our own
double beta = fundamental.Beta;
if(config.UseBetaGenerator)
{
beta = BetaGenerator.Beta(symbol, config.UseBetaGeneratorMonths, false);
if(double.IsNaN(beta))
{
candidateViolations.Add(new CandidateViolation(symbol,"No Beta violation."));
continue;
}
}
// Get the benchmark pricing low pricing data and check the slope of previous lows; only if Beta of candidate is >= LowSlopeBetaThreshhold
// The idea behind this check is that a high beta stock will track to the benchmark. So if the benchmark lows are forming a downward pattern then we
// assume that this is a somewhat bearish condition. The config has the setting at a 15 day check and the threshold beta set to 1.00
if(config.UseLowSlopeBetaCheck && beta >= config.LowSlopeBetaThreshhold)
{
Prices benchmarkPrices=GBPriceCache.GetInstance().GetPrices(config.Benchmark,tradeDate,config.LowSlopeBetaDays);
pricesArray=Numerics.ToDouble(benchmarkPrices.GetPricesLow());
leastSquaresResult=Numerics.LeastSquares(pricesArray);
double slopeBmk=leastSquaresResult.Slope;
if(slopeBmk<0)
{
candidateViolations.Add(new CandidateViolation(symbol,"Beta threshhold violation."));
continue;
}
}
// *** MACDSignal detection
if(config.UseMACD)
{
MACDSetup macdSetup=new MACDSetup(config.MACDSetup);
MACDSignals macdSignals=MACDGenerator.GenerateMACD(prices,macdSetup);
Signals signalsMACD = SignalGenerator.GenerateSignals(macdSignals);
signalsMACD=new Signals(signalsMACD.Take(config.MACDSignalDays).ToList());
int weakSellSignals=(from Signal signal in signalsMACD where signal.IsWeakSell() select signal).Count();
int strongSellSignals=(from Signal signal in signalsMACD where signal.IsStrongSell() select signal).Count();
if(config.MACDRejectWeakSellSignals && weakSellSignals>0)
{
candidateViolations.Add(new CandidateViolation(symbol,"MACD Reject Weak Sell violation."));
continue;
}
if(config.MACDRejectStrongSellSignals && strongSellSignals>0)
{
candidateViolations.Add(new CandidateViolation(symbol,"MACD Reject Strong Sell violation."));
continue;
}
}
// *** Stochastics oscillator
if(config.UseStochastics)
{
Stochastics stochastics=StochasticsGenerator.GenerateStochastics(prices);
Signals signalsStochastics=new Signals(SignalGenerator.GenerateSignals(stochastics).OrderByDescending(x => x.SignalDate).ToList());
signalsStochastics=new Signals(signalsStochastics.Take(config.StochasticsSignalDays).ToList());
int weakSellCount=(from Signal signal in signalsStochastics where signal.IsWeakSell() select signal).Count();
int strongSellCount=(from Signal signal in signalsStochastics where signal.IsStrongSell() select signal).Count();
if(config.StochasticsRejectStrongSells&&strongSellCount>0)
{
candidateViolations.Add(new CandidateViolation(symbol,"Stochastics Oscillator Reject Strong Sell violation."));
continue;
}
if(config.StochasticsRejectWeakSells&&weakSellCount>0)
{
candidateViolations.Add(new CandidateViolation(symbol,"Stochastics Oscillator Reject Weak Sell violation."));
continue;
}
}
// Analyst Ratings - "Downgrades" that are more than a year old (252 days) are not considered. Mean reversion.... bad companies improve, good companies decline.
DateTime minRatingDate=dateGenerator.GenerateHistoricalDate(startDateOfReturns,(int)MomentumGeneratorConstants.DayCount);
AnalystRatings analystRatings=AnalystRatingsDA.GetAnalystRatingsMaxDateNoZacks(symbol,tradeDate);
analystRatings.RemoveAll(x => x.Date<minRatingDate);
AnalystRating rating=null;
if(null!=analystRatings)rating=(from AnalystRating analystRating in analystRatings where analystRating.Type.Equals("Downgrades") select analystRating).FirstOrDefault();
if(null!=rating)
{
candidateViolations.Add(new CandidateViolation(symbol,"AnalystRating Downgrade violation within set period."));
continue;
}
// The cumulative returns for the ranking skip to the previous month to eliminate short term reversal anomaly (Wesley Gray : Quantum Momentum)
prices=GBPriceCache.GetInstance().GetPrices(symbol,startDateOfReturns,(int)MomentumGeneratorConstants.DayCount);
if(null==prices||(int)MomentumGeneratorConstants.DayCount!=prices.Count)
{
candidateViolations.Add(new CandidateViolation(symbol,"Insufficient pricing, cannot determine rank."));
continue;
}
// check for outliers in the return stream
float[] returns = default;
returns=prices.GetReturns();
if((from float value in returns where Math.Abs(value)>.50 select value).Count()>0)
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate pricing contains outliers in the returns."));
continue;
}
// Cumulative return
double cumulativeReturn=prices.GetCumulativeReturn();
if(cumulativeReturn<.10)
{
candidateViolations.Add(new CandidateViolation(symbol,"Candidate cumulative returns below threshhold."));
continue;
}
// Apply the PEScreening last because there an option to permit the inclusion of the high PE candidates if we have no other available candidates.
// The idea is to try to avoid high PE stocks as they are more likey to introduce drawdowns as backtests have shown.
if(config.UseMaxPEScreen && !double.IsNaN(fundamental.PE) && fundamental.PE>config.MaxPE)
{
candidateViolations.Add(new CandidateViolation(symbol,"PE violation."));
MGSHMomentumCandidate highPECandidate=new MGSHMomentumCandidate();
highPECandidate.AnalysisDate=tradeDate;
highPECandidate.Symbol=symbol;
highPECandidate.CumReturn252=prices.GetCumulativeReturn();
highPECandidate.DayCount=(int)MomentumGeneratorConstants.DayCount;
highPECandidate.IDIndicator=IDIndicator.Calculate(prices);
highPECandidate.Score=ScoreIndicator.Calculate(prices);
highPECandidate.PE=fundamental.PE;
highPECandidate.Beta=beta;
highPECandidate.Velocity=velocity;
highPECandidate.Volume=price.Volume;
highPECandidate.Return1D=return1D;
highPECandidates.Add(highPECandidate);
continue;
}
// *********************************************************************** C A N D I D A T E A C C E P T A N C E *******************************************************
// At this point whatever remains is taken so initialize the candidate and add to list
MGSHMomentumCandidate momentumCandidate=new MGSHMomentumCandidate();
momentumCandidate.AnalysisDate=tradeDate;
momentumCandidate.Symbol=symbol;
momentumCandidate.CumReturn252=prices.GetCumulativeReturn();
momentumCandidate.DayCount=(int)MomentumGeneratorConstants.DayCount;
momentumCandidate.IDIndicator=IDIndicator.Calculate(prices);
momentumCandidate.Score=ScoreIndicator.Calculate(prices);
momentumCandidate.PE=fundamental.PE;
momentumCandidate.Beta=beta;
momentumCandidate.Velocity=velocity;
momentumCandidate.Volume=price.Volume;
momentumCandidate.Return1D=return1D;
momentumCandidates.Add(momentumCandidate);
} // for all symbols
if(0!=candidateViolations.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"**************** C A N D I D A T E S U M M A R Y ************************");
IEnumerable<Tuple<string, int>> groups = candidateViolations.GroupBy(x => x.ReasonCategory).OrderByDescending(group => group.Count()).Select(group => Tuple.Create(group.Key, group.Count()));
foreach(Tuple<string, int> group in groups)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Group: {0} Count:{1}",group.Item1, group.Item2));
}
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Considered : {momentumCandidates.Count+candidateViolations.Count}"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Disqualified : {candidateViolations.Count}"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Eligible : {momentumCandidates.Count}"));
MDTrace.WriteLine(LogLevel.DEBUG,"******************************************************************************************************");
// ********************************************************* E N D C A N D I D A T E S E L E C T I O N C R I T E R I A ****************************************
// If we wind up with less than the number of required candidates then check the StrictMaxPE
// flag and, if allowed, add the highPECandidate (that we've accumulated but skipped) to the momentumCandidates ordering them by the Lowest PE
if(!config.StrictMaxPE && momentumCandidates.Count<config.MaxPositions && highPECandidates.Count>0)
{
int takeCandidates=config.MaxPositions-momentumCandidates.Count;
highPECandidates=new MGSHMomentumCandidates(highPECandidates.OrderBy(x=>x.PE).Take(takeCandidates).ToList());
momentumCandidates.AddRange(highPECandidates);
if(config.Verbose)MDTrace.WriteLine(LogLevel.DEBUG,String.Format("High PE Candidates,{0}",Utility.FromList((from MGSHMomentumCandidate momentumCandidate in highPECandidates select momentumCandidate.Symbol).ToList())));
}
MGSHQualityIndicator qualityIndicator=new MGSHQualityIndicator(config.QualityIndicatorType);
if(qualityIndicator.Quality.Equals(MGSHQualityIndicator.QualityType.IDIndicator))
{
momentumCandidates=new MGSHMomentumCandidates((from MGSHMomentumCandidate momentumCandidate in momentumCandidates orderby momentumCandidate.IDIndicator ascending, momentumCandidate.CumReturn252 descending, momentumCandidate.Return1D descending, momentumCandidate.Volume descending select momentumCandidate).ToList());
}
else
{
momentumCandidates=new MGSHMomentumCandidates((from MGSHMomentumCandidate momentumCandidate in momentumCandidates orderby momentumCandidate.Score descending,momentumCandidate.CumReturn252 descending,momentumCandidate.Return1D descending,momentumCandidate.Volume descending select momentumCandidate).ToList());
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MomentumGenertor.GenerateMomentum:{0} candidates",momentumCandidates.Count()));
return momentumCandidates;
}
}
}

View File

@@ -0,0 +1,321 @@
using System;
using System.Collections.Generic;
using MarketData.Utils;
using MarketData.Extensions;
using System.Linq;
using MarketData.Generator.Interface;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHPosition : IPosition
{
public MGSHPosition()
{
CurrentPrice=double.NaN;
}
public MGSHPosition(MGSHPosition position)
{
Symbol = position.Symbol;
PurchaseDate = position.PurchaseDate;
SellDate = position.SellDate;
Shares = position.Shares;
PurchasePrice = position.PurchasePrice;
CurrentPrice = position.CurrentPrice;
Volume = position.Volume;
Return1D = position.Return1D;
CumReturn252 = position.CumReturn252;
IDIndicator = position.IDIndicator;
Score=position.Score;
Velocity = position.Velocity;
PE = position.PE;
Beta = position.Beta;
InitialStopLimit = position.InitialStopLimit;
TrailingStopLimit = position.TrailingStopLimit;
LastStopAdjustment = position.LastStopAdjustment;
PositionRiskPercentDecimal = position.PositionRiskPercentDecimal;
R = position.R;
if(null!=position.Comment)Comment=new String(position.Comment.ToArray());
}
public static MGSHPosition Clone(MGSHPosition positionToClone)
{
MGSHPosition position = new MGSHPosition();
position.Symbol = positionToClone.Symbol;
position.PurchaseDate = positionToClone.PurchaseDate;
position.SellDate=positionToClone.SellDate;
position.Shares=positionToClone.Shares;
position.PurchasePrice=positionToClone.PurchasePrice;
position.CurrentPrice=positionToClone.CurrentPrice;
position.Volume=positionToClone.Volume;
position.Return1D=positionToClone.Return1D;
position.CumReturn252=positionToClone.CumReturn252;
position.IDIndicator=positionToClone.IDIndicator;
position.Score=positionToClone.Score;
position.Velocity=positionToClone.Velocity;
position.PE=positionToClone.PE;
position.Beta=positionToClone.Beta;
position.InitialStopLimit=positionToClone.InitialStopLimit;
position.TrailingStopLimit=positionToClone.TrailingStopLimit;
position.LastStopAdjustment=positionToClone.LastStopAdjustment;
position.PositionRiskPercentDecimal=positionToClone.PositionRiskPercentDecimal;
position.R=positionToClone.R;
position.Comment=positionToClone.Comment;
return position;
}
public String Symbol{get;set;}
public DateTime PurchaseDate{get;set;}
public DateTime SellDate{get;set;}
public double Shares{get;set;}
public double PurchasePrice{get;set;}
public double CurrentPrice{get;set;}
public double Exposure{get{return Shares*PurchasePrice;}}
public double MarketValue{get{return Shares*CurrentPrice;}}
public long Volume{get;set;}
public double Return1D{get;set;}
public double GainLoss{get{return MarketValue-Exposure;}}
public double GainLossPcnt{get{return (MarketValue-Exposure)/Exposure;}}
public double CumReturn252{get;set;}
public double IDIndicator{get;set;}
public double Score{get;set;}
public double Velocity{get;set;}
public double PE{get;set;}
public double Beta{get;set;}
public double InitialStopLimit {get; set; }
public double TrailingStopLimit {get; set; }
public DateTime LastStopAdjustment {get; set; }
public double PositionRiskPercentDecimal {get; set; }
public double R { get; set; }
public String Comment {get; set; }
public virtual NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection=new NVPCollection();
nvpCollection.Add(new NVP("Symbol",Symbol.ToString()));
nvpCollection.Add(new NVP("PurchaseDate",PurchaseDate.ToString()));
nvpCollection.Add(new NVP("SellDate",SellDate.ToString()));
nvpCollection.Add(new NVP("Shares",Shares.ToString()));
nvpCollection.Add(new NVP("PurchasePrice",PurchasePrice.ToString()));
nvpCollection.Add(new NVP("CurrentPrice",CurrentPrice.ToString()));
nvpCollection.Add(new NVP("Volume",Volume.ToString()));
nvpCollection.Add(new NVP("Return1D",Return1D.ToString()));
nvpCollection.Add(new NVP("CumReturn252",CumReturn252.ToString()));
nvpCollection.Add(new NVP("IDIndicator",IDIndicator.ToString()));
nvpCollection.Add(new NVP("Score",Score.ToString()));
nvpCollection.Add(new NVP("Velocity",Velocity.ToString()));
nvpCollection.Add(new NVP("PE",PE.ToString()));
nvpCollection.Add(new NVP("Beta",Beta.ToString()));
nvpCollection.Add(new NVP("InitialStopLimit", InitialStopLimit.ToString()));
nvpCollection.Add(new NVP("TrailingStopLimit", TrailingStopLimit.ToString()));
nvpCollection.Add(new NVP("LastStopAdjustment", LastStopAdjustment.ToString()));
nvpCollection.Add(new NVP("PositionRiskPercentDecimal", PositionRiskPercentDecimal.ToString()));
nvpCollection.Add(new NVP("R", R.ToString()));
nvpCollection.Add(new NVP("Comment", Comment?.ToString()));
return nvpCollection;
}
public static MGSHPosition FromNVPCollection(NVPCollection nvpCollection)
{
MGSHPosition position=new MGSHPosition();
if(0 == nvpCollection.Count) return null;
NVPDictionary nvpDictionary=nvpCollection.ToDictionary();
position.Symbol=nvpDictionary["Symbol"].Get<String>();
position.PurchaseDate=nvpDictionary["PurchaseDate"].Get<DateTime>();
position.SellDate=nvpDictionary["SellDate"].Get<DateTime>();
position.Shares=nvpDictionary["Shares"].Get<double>();
position.PurchasePrice=nvpDictionary["PurchasePrice"].Get<double>();
position.CurrentPrice=nvpDictionary["CurrentPrice"].Get<double>();
position.Volume=nvpDictionary["Volume"].Get<long>();
position.Return1D=nvpDictionary["Return1D"].Get<long>();
position.CumReturn252=nvpDictionary["CumReturn252"].Get<long>();
position.IDIndicator=nvpDictionary["IDIndicator"].Get<double>();
position.Velocity=nvpDictionary["Velocity"].Get<double>();
position.PE=nvpDictionary["PE"].Get<double>();
position.Beta=nvpDictionary["Beta"].Get<double>();
if(nvpDictionary.ContainsKey("Score")) position.Score=nvpDictionary["Score"].Get<double>();
else position.Score=double.NaN;
if(nvpDictionary.ContainsKey("InitialStopLimit")) position.InitialStopLimit=nvpDictionary["InitialStopLimit"].Get<double>();
else position.InitialStopLimit=double.NaN;
if(nvpDictionary.ContainsKey("TrailingStopLimit")) position.TrailingStopLimit=nvpDictionary["TrailingStopLimit"].Get<double>();
else position.TrailingStopLimit=double.NaN;
if(nvpDictionary.ContainsKey("LastStopAdjustment")) position.LastStopAdjustment=nvpDictionary["LastStopAdjustment"].Get<DateTime>();
else position.LastStopAdjustment=Utility.Epoch;
if(nvpDictionary.ContainsKey("Comment")) position.Comment=nvpDictionary["Comment"].Get<String>();
else position.Comment=null;
if(nvpDictionary.ContainsKey("PositionRiskPercentDecimal")) position.PositionRiskPercentDecimal=nvpDictionary["PositionRiskPercentDecimal"].Get<double>();
else position.PositionRiskPercentDecimal=double.NaN;
if(nvpDictionary.ContainsKey("R")) position.R=nvpDictionary["R"].Get<double>();
else position.R=double.NaN;
return position;
}
public static void DisplayHeader()
{
MDTrace.WriteLine(LogLevel.DEBUG, "Symbol,Purchase Date,Shares,Purchase Price,Exposure,Volume,Return1D,Sell Date,Sell Price,Market Value,Gain Loss,Gain Loss(%),CumReturn252,IDIndicator,Score,MaxDrawdown,MaxUpside,Velocity,PE,Beta,SharpeRatio,InitialStopLimit,TrailingStopLimit,LastStopAdjustment,Comment");
}
public void Display()
{
if (Utility.IsEpoch(SellDate) && double.IsNaN(CurrentPrice))
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0},{1},{2},{3},{4},{5},{6},N/A,N/A,N/A,N/A,N/A,{7},{8},{9},{10},{11},{12},{13},{14},{15},{16}",
Symbol,
Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate),
Shares,
Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)),
Utility.AddQuotes(Utility.FormatCurrency(Exposure)),
Volume,
Utility.AddQuotes(Utility.FormatPercent(Return1D)),
Utility.AddQuotes(Utility.FormatPercent(CumReturn252)),
Utility.AddQuotes(Utility.FormatNumber(IDIndicator)),
Utility.AddQuotes(Utility.FormatNumber(Score)),
Utility.AddQuotes(Utility.FormatPercent(Velocity)),
Utility.AddQuotes(Utility.FormatNumber(PE)),
Utility.AddQuotes(Utility.FormatNumber(Beta)),
Utility.AddQuotes(Utility.FormatNumber(InitialStopLimit,2)),
Utility.AddQuotes(Utility.FormatNumber(TrailingStopLimit,2)),
Utility.DateTimeToStringMMHDDHYYYY(LastStopAdjustment),
Comment
));
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21}",
Symbol,
Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate),
Shares,
Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)),
Utility.AddQuotes(Utility.FormatCurrency(Exposure)),
Volume,
Utility.AddQuotes(Utility.FormatPercent(Return1D)),
Utility.DateTimeToStringMMHDDHYYYY(SellDate),
Utility.AddQuotes(Utility.FormatCurrency(CurrentPrice)),
Utility.AddQuotes(Utility.FormatCurrency(MarketValue)),
Utility.AddQuotes(Utility.FormatCurrency(GainLoss)),
Utility.FormatPercent(GainLossPcnt),
Utility.AddQuotes(Utility.FormatPercent(CumReturn252)),
Utility.AddQuotes(Utility.FormatNumber(IDIndicator)),
Utility.AddQuotes(Utility.FormatNumber(Score)),
Utility.AddQuotes(Utility.FormatPercent(Velocity)),
Utility.AddQuotes(Utility.FormatNumber(PE)),
Utility.AddQuotes(Utility.FormatNumber(Beta)),
Utility.AddQuotes(Utility.FormatNumber(InitialStopLimit,2)),
Utility.AddQuotes(Utility.FormatNumber(TrailingStopLimit,2)),
Utility.DateTimeToStringMMHDDHYYYY(LastStopAdjustment),
Comment
));
}
}
}
// ****************************************************************************************************************************************************************
public class MGSHPositions : List<MGSHPosition>
{
public MGSHPositions()
{
}
public MGSHPositions(MGSHPositions positions)
{
foreach(MGSHPosition position in positions)Add(position);
}
public MGSHPositions(List<MGSHPosition> positions)
{
foreach(MGSHPosition position in positions)Add(position);
}
public MGSHPositions(MGSHPosition position)
{
Add(position);
}
public void Add(MGSHPositions positions)
{
foreach(MGSHPosition position in positions)Add(position);
}
public double Exposure
{
get
{
return (from MGSHPosition position in this select position.PurchasePrice*position.Shares).Sum();
}
}
public double MarketValue
{
get
{
return (from MGSHPosition position in this select position.CurrentPrice*position.Shares).Sum();
}
}
public List<String> Symbols()
{
List<String> symbols = new List<string>();
foreach(MGSHPosition position in this)
{
symbols.Add(position.Symbol);
}
return symbols;
}
public NVPCollections ToNVPCollections()
{
NVPCollections nvpCollections=new NVPCollections();
foreach(MGSHPosition position in this)
{
nvpCollections.Add(position.ToNVPCollection());
}
return nvpCollections;
}
public static MGSHPositions FromNVPCollections(NVPCollections nvpCollections)
{
MGSHPositions positions=new MGSHPositions();
foreach(NVPCollection nvpCollection in nvpCollections)
{
positions.Add(MGSHPosition.FromNVPCollection(nvpCollection));
}
return positions;
}
public void DisplayTopFive()
{
MGSHPositions positions = new MGSHPositions(this.OrderByDescending(x => x.GainLossPcnt).Take(5).ToList());
MGSHPosition.DisplayHeader();
for (int index = 0; index < positions.Count; index++)
{
MGSHPosition position = positions[index];
position.Display();
}
}
public void DisplayBottomFive()
{
MGSHPositions positions = new MGSHPositions(this.OrderBy(x => x.GainLossPcnt).Take(5).ToList());
MGSHPosition.DisplayHeader();
for (int index = 0; index < positions.Count; index++)
{
MGSHPosition position = positions[index];
position.Display();
}
}
public void Display()
{
MGSHPosition.DisplayHeader();
for(int index=0;index<Count;index++)
{
MGSHPosition position=this[index];
position.Display();
}
if(this.Any(x=>Utility.IsEpoch(x.SellDate)||double.IsNaN(x.CurrentPrice)))
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("EXPOSURE:{0}", Utility.FormatCurrency(this.Exposure)));
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("GAIN/LOSS {0}", Utility.FormatCurrency(this.Sum(x => x.GainLoss))));
}
MDTrace.WriteLine(LogLevel.DEBUG,"****************************************************************************************************************************");
}
}
}

View File

@@ -0,0 +1,66 @@
using MarketData.Utils;
using System;
using System.Collections.Generic;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHPricingExceptions:List<MGSHPricingException>
{
public NVPCollections ToNVPCollections()
{
NVPCollections nvpCollections=new NVPCollections();
foreach(MGSHPricingException pricingException in this)
{
nvpCollections.Add(pricingException.ToNVPCollection());
}
return nvpCollections;
}
public static MGSHPricingExceptions FromNVPCollections(NVPCollections nvpCollections)
{
MGSHPricingExceptions pricingExcpetions=new MGSHPricingExceptions();
foreach(NVPCollection nvpCollection in nvpCollections)
{
pricingExcpetions.Add(MGSHPricingException.FromNVPCollection(nvpCollection));
}
return pricingExcpetions;
}
public void AddFromNVPCollection(NVPCollection nvpCollection)
{
Add(MGSHPricingException.FromNVPCollection(nvpCollection));
}
}
public class MGSHPricingException
{
public MGSHPricingException()
{
}
public MGSHPricingException(MGSHPricingException pricingException)
{
this.Symbol=pricingException.Symbol;
this.ExceptionCount=pricingException.ExceptionCount;
}
public MGSHPricingException(String symbol,int exceptionCount)
{
this.Symbol=symbol;
this.ExceptionCount=exceptionCount;
}
public String Symbol { get; set; }
public int ExceptionCount { get; set; }
public virtual NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection=new NVPCollection();
nvpCollection.Add(new NVP("Symbol",Symbol.ToString()));
nvpCollection.Add(new NVP("ExceptionCount",ExceptionCount.ToString()));
return nvpCollection;
}
public static MGSHPricingException FromNVPCollection(NVPCollection nvpCollection)
{
MGSHPricingException pricingException=new MGSHPricingException();
NVPDictionary nvpDictionary=nvpCollection.ToDictionary();
pricingException.Symbol=nvpDictionary["Symbol"].Get<String>();
pricingException.ExceptionCount=nvpDictionary["ExceptionCount"].Get<int>();
return pricingException;
}
}
}

View File

@@ -0,0 +1,49 @@
using System;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHQualityIndicator
{
public enum QualityType{IDIndicator=0,ScoreIndicator=1};
private QualityType qualityType;
public MGSHQualityIndicator()
{
qualityType=QualityType.IDIndicator;
}
public MGSHQualityIndicator(QualityType qualityType)
{
this.qualityType=qualityType;
}
public MGSHQualityIndicator(String strQualityType)
{
if(null==strQualityType)
{
qualityType=QualityType.IDIndicator;
return;
}
strQualityType=strQualityType.ToUpper();
if("SCOREINDICATOR".Equals(strQualityType))qualityType=QualityType.ScoreIndicator;
else qualityType=QualityType.IDIndicator;
}
public override String ToString()
{
if(qualityType.Equals(QualityType.ScoreIndicator))return "SCOREINDICATOR";
return "IDINDICATOR";
}
public static String ToString(MGSHQualityIndicator.QualityType qualityType)
{
if(qualityType.Equals(QualityType.ScoreIndicator))return "SCOREINDICATOR";
return "IDINDICATOR";
}
public static QualityType ToQuality(String strQualityType)
{
if(strQualityType.Equals("SCOREINDICATOR")) return MGSHQualityIndicator.QualityType.ScoreIndicator;
return MGSHQualityIndicator.QualityType.IDIndicator;
}
public QualityType Quality
{
get{return qualityType;}
set{qualityType=value;}
}
}
}

View File

@@ -0,0 +1,247 @@
using System;
using System.Collections.Generic;
using System.IO;
using MarketData.Utils;
using StopLimits=MarketData.Generator.Model.StopLimits;
namespace MarketData.Generator.MGSHMomentum
{
// *****************************************************************************
public static class MGSHSessionManager
{
private const String SIGNATURE="MGSHSESSIONv2.00";
public static bool SaveSession(MGSHSessionParams sessionParams,String pathSessionFile)
{
try
{
if(null==pathSessionFile)return false;
pathSessionFile=GetSessionFileName(pathSessionFile);
FileStream outStream=new FileStream(pathSessionFile,FileMode.Create);
StreamWriter streamWriter=new StreamWriter(outStream);
streamWriter.WriteLine(SIGNATURE);
streamWriter.WriteLine((new NVP("LastUpdated",sessionParams.LastUpdated.ToString())).ToString());
streamWriter.WriteLine((new NVP("TradeDate",sessionParams.TradeDate.ToShortDateString())).ToString());
streamWriter.WriteLine((new NVP("StartDate",sessionParams.StartDate.ToShortDateString())).ToString());
streamWriter.WriteLine((new NVP("AnalysisDate",sessionParams.AnalysisDate.ToShortDateString())).ToString());
streamWriter.WriteLine((new NVP("Cycle",sessionParams.Cycle.ToString())).ToString());
streamWriter.WriteLine((new NVP("CashBalance",sessionParams.CashBalance.ToString())).ToString());
streamWriter.WriteLine((new NVP("NonTradeableCash",sessionParams.NonTradeableCash.ToString())).ToString());
streamWriter.WriteLine((new NVP("HedgeCashBalance",sessionParams.HedgeCashBalance.ToString())).ToString());
NVPCollection configurationCollection=sessionParams.Configuration.ToNVPCollection();
streamWriter.WriteLine(configurationCollection.ToString());
List<NVPCollections> nvpCollectionsList=sessionParams.ActivePositions.ToNVPCollections();
int totalPositions=0;
foreach(NVPCollections nvpCollections in nvpCollectionsList)
{
List<String> nvpCollectionsStringList=nvpCollections.ToList();
totalPositions+=nvpCollectionsStringList.Count;
}
streamWriter.WriteLine((new NVP("TotalActivePositions",totalPositions.ToString())).ToString());
foreach(NVPCollections nvpCollections in nvpCollectionsList)
{
List<String> nvpCollectionsStringList=nvpCollections.ToList();
foreach(String str in nvpCollectionsStringList)
{
streamWriter.WriteLine(str);
}
}
NVPCollections allPositionsCollections=sessionParams.AllPositions.ToNVPCollections();
List<String> nvpAllCollectionsStringList=allPositionsCollections.ToList();
streamWriter.WriteLine((new NVP("TotalPositions",nvpAllCollectionsStringList.Count.ToString())).ToString());
foreach(String str in nvpAllCollectionsStringList)streamWriter.WriteLine(str);
if(null == sessionParams.StopLimits)sessionParams.StopLimits = new StopLimits();
NVPCollections allStopLimitsCollections=sessionParams.StopLimits.ToNVPCollections();
List<String> nvpStopLimitsCollectionsStringList=allStopLimitsCollections.ToList();
streamWriter.WriteLine((new NVP("TotalStopLimits",nvpStopLimitsCollectionsStringList.Count.ToString())).ToString());
foreach(String str in nvpStopLimitsCollectionsStringList) streamWriter.WriteLine(str);
if(null == sessionParams.HedgePositions)sessionParams.HedgePositions = new MGSHPositions();
NVPCollections hedgePositionsCollections=sessionParams.HedgePositions.ToNVPCollections();
List<String> nvpHedgePositionsStringList=hedgePositionsCollections.ToList();
streamWriter.WriteLine((new NVP("TotalHedgePositions",nvpHedgePositionsStringList.Count.ToString())).ToString());
foreach(String str in nvpHedgePositionsStringList)streamWriter.WriteLine(str);
NVPCollections pricingExceptionsCollections=sessionParams.PricingExceptions.ToNVPCollections();
List<String> nvpPricingExceptionsCollectionsStringList=pricingExceptionsCollections.ToList();
streamWriter.WriteLine((new NVP("TotalPricingExceptions",nvpPricingExceptionsCollectionsStringList.Count.ToString())).ToString());
foreach(String str in nvpPricingExceptionsCollectionsStringList)streamWriter.WriteLine(str);
streamWriter.Flush();
outStream.Flush();
streamWriter.Close();
outStream.Close();
outStream.Dispose();
return true;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception.ToString()));
return false;
}
}
public static MGSHSessionParams RestoreSession(String pathSessionFile)
{
FileStream inStream =null;
StreamReader streamReader=null;
try
{
if(!SessionAvailable(pathSessionFile))return null;
MGSHSessionParams sessionParams=new MGSHSessionParams();
inStream =new FileStream(pathSessionFile,FileMode.Open);
streamReader=new StreamReader(inStream);
String versionInfo=streamReader.ReadLine();
double version=double.Parse(Utility.BetweenString(versionInfo,"v",null));
if(version>2.00)return null;
NVP lastUpdated=new NVP(streamReader.ReadLine());
NVP tradeDate=new NVP(streamReader.ReadLine());
NVP startDate=new NVP(streamReader.ReadLine());
NVP analysisDate=new NVP(streamReader.ReadLine());
NVP cycle=new NVP(streamReader.ReadLine());
NVP cashBalance=new NVP(streamReader.ReadLine());
NVP nonTradeableCash=new NVP(streamReader.ReadLine());
NVP hedgeCashBalance=new NVP(streamReader.ReadLine());
sessionParams.LastUpdated=lastUpdated.Get<DateTime>();
sessionParams.TradeDate=tradeDate.Get<DateTime>();
sessionParams.StartDate=startDate.Get<DateTime>();
sessionParams.AnalysisDate=analysisDate.Get<DateTime>();
sessionParams.Cycle=cycle.Get<int>();
sessionParams.CashBalance=cashBalance.Get<double>();
sessionParams.NonTradeableCash=nonTradeableCash.Get<double>();
if(2.00==version)sessionParams.HedgeCashBalance=hedgeCashBalance.Get<double>();
NVPCollection configurationCollection=new NVPCollection(streamReader.ReadLine());
sessionParams.Configuration=MGSHConfiguration.FromNVPCollection(configurationCollection);
int totalActivePositions=(new NVP(streamReader.ReadLine())).Get<int>();
sessionParams.ActivePositions=new MGSHActivePositions();
for(int positionIndex=0;positionIndex<totalActivePositions;positionIndex++)
{
NVPCollection nvpCollection=new NVPCollection(streamReader.ReadLine());
sessionParams.ActivePositions.AddFromNVPCollection(nvpCollection);
}
int totalPositions=(new NVP(streamReader.ReadLine())).Get<int>();
NVPCollections nvpCollections=new NVPCollections();
for(int positionIndex=0;positionIndex<totalPositions;positionIndex++)
{
NVPCollection nvpCollection=new NVPCollection(streamReader.ReadLine());
nvpCollections.Add(nvpCollection);
}
sessionParams.AllPositions=MGSHPositions.FromNVPCollections(nvpCollections);
if(2.00==version)
{
// stop limits
int totalStopLimits=(new NVP(streamReader.ReadLine())).Get<int>();
sessionParams.StopLimits=new StopLimits();
for(int stopLimitIndex=0;stopLimitIndex<totalStopLimits;stopLimitIndex++)
{
NVPCollection nvpCollection=new NVPCollection(streamReader.ReadLine());
sessionParams.StopLimits.AddFromNVPCollection(nvpCollection);
}
// hedges
int totalHedgePositions = (new NVP(streamReader.ReadLine())).Get<int>();
NVPCollections nvpHedgeCollections=new NVPCollections();
for(int hedgePositionIndex=0;hedgePositionIndex<totalHedgePositions;hedgePositionIndex++)
{
NVPCollection nvpCollection=new NVPCollection(streamReader.ReadLine());
nvpHedgeCollections.Add(nvpCollection);
}
sessionParams.HedgePositions=MGSHPositions.FromNVPCollections(nvpHedgeCollections);
// pricing exceptions
int totalPricingExceptions = (new NVP(streamReader.ReadLine())).Get<int>();
NVPCollections nvpPricingExceptionsCollections=new NVPCollections();
for(int pricingExceptionIndex=0;pricingExceptionIndex<totalPricingExceptions;pricingExceptionIndex++)
{
NVPCollection nvpCollection=new NVPCollection(streamReader.ReadLine());
nvpPricingExceptionsCollections.Add(nvpCollection);
}
sessionParams.PricingExceptions=MGSHPricingExceptions.FromNVPCollections(nvpPricingExceptionsCollections);
} // version
inStream.Close();
inStream.Dispose();
inStream = null;
streamReader.Close();
streamReader.Dispose();
streamReader = null;
return sessionParams;
}
finally
{
if(null!=streamReader)streamReader.Close();
if(null!=inStream)inStream.Close();
}
}
public static String GetSessionFileName(String pathSessionFile)
{
if (null == pathSessionFile) return null;
String directory = Path.GetDirectoryName(pathSessionFile);
if ("".Equals(directory)) directory = Directory.GetCurrentDirectory();
return directory + "/" + Path.GetFileNameWithoutExtension(pathSessionFile) + ".TXT";
}
public static bool SessionAvailable(String pathSessionFile)
{
return IsValidSessionFile(pathSessionFile);
}
public static double GetSessionFileVersion(String pathSessionFile)
{
FileStream inStream =null;
StreamReader streamReader=null;
try
{
if(null==pathSessionFile)return 0.00;
// pathSessionFile=pathSessionFile.ToUpper();
// if(!pathSessionFile.EndsWith(".TXT")&&!pathSessionFile.EndsWith(".txt"))pathSessionFile+=".txt";
if(!File.Exists(pathSessionFile))return 0.00;
inStream =new FileStream(pathSessionFile,FileMode.Open);
streamReader=new StreamReader(inStream);
String versionInfo=streamReader.ReadLine();
double version=double.Parse(Utility.BetweenString(versionInfo,"v",null));
return version;
}
catch(Exception)
{
return 0.00;
}
finally
{
if(null!=streamReader)streamReader.Close();
if(null!=inStream)inStream.Close();
}
}
public static bool IsValidSessionFile(String pathSessionFile)
{
FileStream inStream =null;
StreamReader streamReader=null;
try
{
if(null==pathSessionFile)return false;
// pathSessionFile=pathSessionFile.ToUpper();
// if(!pathSessionFile.EndsWith(".TXT")&&!pathSessionFile.EndsWith(".txt"))pathSessionFile+=".txt";
if(!File.Exists(pathSessionFile))return false;
inStream =new FileStream(pathSessionFile,FileMode.Open);
streamReader=new StreamReader(inStream);
String versionInfo=streamReader.ReadLine();
double version=double.Parse(Utility.BetweenString(versionInfo,"v",null));
if(1.00!=version && 2.00!=version)return false;
return true;
}
catch(Exception)
{
return false;
}
finally
{
if(null!=streamReader)streamReader.Close();
if(null!=inStream)inStream.Close();
}
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
using StopLimits=MarketData.Generator.Model.StopLimits;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHSessionParams
{
public DateTime LastUpdated { get; set; }
public DateTime TradeDate { get; set; }
public DateTime StartDate { get; set; }
public DateTime AnalysisDate { get; set; }
public MGSHConfiguration Configuration { get; set; }
public MGSHActivePositions ActivePositions { get; set; }
public MGSHPositions AllPositions { get; set; }
public double CashBalance { get; set; }
public double HedgeCashBalance { get; set; }
public double NonTradeableCash { get; set; }
public StopLimits StopLimits { get; set;}
public MGSHPositions HedgePositions { get; set; }
public MGSHPricingExceptions PricingExceptions{ get; set; }
public int Cycle { get; set; }
// This gets AllPositions+Positions+HedgePositions
public MGSHPositions GetCombinedPositions()
{
MGSHPositions positions=new MGSHPositions();
positions.AddRange(AllPositions);
positions.AddRange(ActivePositions.GetPositions());
positions.AddRange(HedgePositions);
return positions;
}
}
}

View File

@@ -0,0 +1,70 @@
using System.Collections.Generic;
using MarketData.Utils;
namespace MarketData.Generator.MGSHMomentum
{
public class MGSHSlotPositions : List<MGSHSlotPosition>
{
public MGSHSlotPositions()
{
}
public MGSHSlotPositions(int slot,MGSHPositions positions)
{
for(int index=0;index<positions.Count;index++)
{
Add(new MGSHSlotPosition(slot,positions[index]));
}
}
public NVPCollections ToNVPCollections()
{
NVPCollections nvpCollections=new NVPCollections();
foreach(MGSHSlotPosition position in this)
{
nvpCollections.Add(position.ToNVPCollection());
}
return nvpCollections;
}
public static MGSHSlotPositions FromNVPCollections(NVPCollections nvpCollections)
{
MGSHSlotPositions positions=new MGSHSlotPositions();
foreach(NVPCollection nvpCollection in nvpCollections)
{
positions.Add(MGSHSlotPosition.FromNVPCollection(nvpCollection));
}
return positions;
}
}
public class MGSHSlotPosition : MGSHPosition
{
public MGSHSlotPosition()
{
}
public MGSHSlotPosition(int slot,MGSHPosition position) : base(position)
{
Slot=slot;
}
public MGSHSlotPosition(MGSHPosition position) : base(position)
{
}
public int Slot{get;set;}
public MGSHPosition ToPosition()
{
MGSHPosition position = new MGSHPosition(this);
return position;
}
public override NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection=base.ToNVPCollection();
nvpCollection.Insert(0,new NVP("Slot",Slot.ToString()));
return nvpCollection;
}
public static new MGSHSlotPosition FromNVPCollection(NVPCollection nvpCollection)
{
NVPDictionary nvpDictionary=nvpCollection.ToDictionary();
MGSHPosition position=MGSHPosition.FromNVPCollection(nvpCollection);
MGSHSlotPosition slotPosition=new MGSHSlotPosition(position);
slotPosition.Slot=nvpDictionary["Slot"].Get<int>();
return slotPosition;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace MarketData.Generator.MGSHMomentum
{
public class RealtimeGainLoss
{
public double Exposure{get;set;}
public double MarketValue{get;set;}
public double GainLoss{get{return MarketValue-Exposure;}}
public double GainLossPercent{get{return Exposure==0?0:(MarketValue-Exposure)/Exposure;}}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Numerical;
using MarketData.Utils;
using System.Linq;
using MarketData.Helper;
namespace MarketData.Generator.MGSHMomentum
{
public class ScoreIndicator
{
private ScoreIndicator()
{
}
// This uses the same scoring mechanism as is used in Clenow Momentum which is based upon the log of returns.
// The higher the score the better the rank. The logPrices array gets filled with the most distant price in the lower indices. Hence the backward walk through the prices.
public static double Calculate(Prices prices)
{
double score=double.NaN;
if(null==prices || 0==prices.Count)return double.NaN;
double[] logPrices=new double[prices.Count];
for(int index=prices.Count-1;index>=0;index--)
{
Price price=prices[index];
logPrices[(prices.Count-index)-1]=Math.Log(price.Close);
}
LeastSquaresResultWithR2 leastSquaresResult=LeastSquaresHelper.CalculateLeastSquaresWithR2(logPrices);
double slope=leastSquaresResult.Slope;
double annualizedReturn=Math.Pow(Math.Exp(slope),252);
if(slope<0.00)annualizedReturn*=-1.00;
score=leastSquaresResult.RSquared*annualizedReturn;
return score;
}
}
}

View File

@@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System.Linq;
using MarketData.Numerical;
using System.Text;
namespace MarketData.Generator.MGSHMomentum
{
public class SlopeManager
{
private double bandSlopeSpreadKL5;
private double bandSlopeSpreadKL10;
private double bandSlopeSpreadKL30;
private double bandSlopeSpreadKL60;
private double bandSlopeSpreadKL90;
public SlopeManager(BollingerBandElementsByDate bollingerBandElementsByDate, DateTime analysisDate)
{
bandSlopeSpreadKL5 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 5);
bandSlopeSpreadKL10 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 10);
bandSlopeSpreadKL30 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 30);
bandSlopeSpreadKL60 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 60);
bandSlopeSpreadKL90 = GetKLBandSpreadSlope(bollingerBandElementsByDate, analysisDate, 90);
}
public bool Verbose { get; set; } = true;
/// <summary>
/// The pattern follows.. 90, 60, 30, 10 ,5
/// </summary>
/// <param name="bandStringKLSpread">++0-0</param>
/// <returns></returns>
public bool IsMatchKLSpread(String bandStringKLSpread)
{
StringBuilder sbKLSPread = new StringBuilder();
sbKLSPread.Append(GetValue(bandSlopeSpreadKL90)).Append(GetValue(bandSlopeSpreadKL60)).Append(GetValue(bandSlopeSpreadKL30)).Append(GetValue(bandSlopeSpreadKL10)).Append(GetValue(bandSlopeSpreadKL5));
return AreEqual(bandStringKLSpread, sbKLSPread.ToString());
}
public String GetKLSpread()
{
StringBuilder sbKLSPread = new StringBuilder();
sbKLSPread.Append(GetValue(bandSlopeSpreadKL90)).Append(GetValue(bandSlopeSpreadKL60)).Append(GetValue(bandSlopeSpreadKL30)).Append(GetValue(bandSlopeSpreadKL10)).Append(GetValue(bandSlopeSpreadKL5));
return sbKLSPread.ToString();
}
public int FindFirstMatchRtoL(String bandString, char match)
{
for (int index = bandString.Length - 1; index >= 0; index--)
{
if (bandString[index].Equals(match)) {
switch (index) {
case 0:
return 90;
case 1:
return 60;
case 2:
return 30;
case 3:
return 10;
case 4:
return 5;
default:
return 10;
}
}
}
return 10;
}
/// <summary>
/// Where str1 may have a wild card '?'
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
private bool AreEqual(String str1,String str2)
{
if(str1.Length!=str2.Length)return false;
for(int index=0;index<str1.Length;index++)
{
char ch1 = str1[index];
char ch2 = str2[index];
if(ch1.Equals('?'))continue;
if(!ch1.Equals(ch2))return false;
}
return true;
}
private String GetValue(double value)
{
if(Math.Abs(value)<.0001)value=0.00;
if(value==0.00)return "0";
else if(value>0.00)return "+";
return "-";
}
private double GetKLBandSpreadSlope(BollingerBandElementsByDate bollingerBandElementsByDate, DateTime fromDate, int dayCount)
{
List<BollingerBandElement> bollingerBandElements = new List<BollingerBandElement>();
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> historicalDates = dateGenerator.GenerateHistoricalDates(fromDate, dayCount);
foreach (DateTime historicalDate in historicalDates) {
if (!bollingerBandElementsByDate.ContainsKey(historicalDate)) {
continue;
}
bollingerBandElements.Add(bollingerBandElementsByDate[historicalDate]);
}
List<double> spreadElements = new List<double>();
foreach (BollingerBandElement bollingerBandElement in bollingerBandElements) {
spreadElements.Add(bollingerBandElement.K - bollingerBandElement.L);
}
double[] elements = spreadElements.ToArray<double>();
elements = Numerics.Reverse(ref elements);
double[] logElements = new double[elements.Length];
for (int index = 0; index < elements.Length; index++) {
logElements[index] = Math.Log(elements[index]);
}
LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logElements);
return leastSquaresResult.Slope;
}
private double GetKBandSlope(BollingerBandElementsByDate bollingerBandElementsByDate,DateTime fromDate, int dayCount)
{
List<BollingerBandElement> bollingerBandElements = new List<BollingerBandElement>();
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> historicalDates = dateGenerator.GenerateHistoricalDates(fromDate, dayCount);
foreach(DateTime historicalDate in historicalDates)
{
if(!bollingerBandElementsByDate.ContainsKey(historicalDate))
{
continue;
}
bollingerBandElements.Add(bollingerBandElementsByDate[historicalDate]);
}
List<double> bandElements = new List<double>();
foreach(BollingerBandElement bollingerBandElement in bollingerBandElements)
{
bandElements.Add(bollingerBandElement.K);
}
double[] elements = bandElements.ToArray<double>();
elements = Numerics.Reverse(ref elements);
double[] logElements = new double[elements.Length];
for(int index=0;index<elements.Length;index++)
{
logElements[index] = Math.Log(elements[index]);
}
LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logElements);
return leastSquaresResult.Slope;
}
private double GetLBandSlope(BollingerBandElementsByDate bollingerBandElementsByDate,DateTime fromDate, int dayCount)
{
List<BollingerBandElement> bollingerBandElements = new List<BollingerBandElement>();
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> historicalDates = dateGenerator.GenerateHistoricalDates(fromDate, dayCount);
foreach(DateTime historicalDate in historicalDates)
{
if(!bollingerBandElementsByDate.ContainsKey(historicalDate))
{
continue;
}
bollingerBandElements.Add(bollingerBandElementsByDate[historicalDate]);
}
List<double> bandElements = new List<double>();
foreach(BollingerBandElement bollingerBandElement in bollingerBandElements)
{
bandElements.Add(bollingerBandElement.L);
}
double[] elements = bandElements.ToArray<double>();
elements = Numerics.Reverse(ref elements);
double[] logElements = new double[elements.Length];
for(int index=0;index<elements.Length;index++)
{
logElements[index] = Math.Log(elements[index]);
}
LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logElements);
return leastSquaresResult.Slope;
}
public void DisplaySlopes()
{
Display($" 90 60 30 10 5");
//Display($"K {GetValue(bandSlopeK90)} {GetValue(bandSlopeK60)} {GetValue(bandSlopeK30)} {GetValue(bandSlopeK10)} {GetValue(bandSlopeK5)} ");
//Display($"L {GetValue(bandSlopeL90)} {GetValue(bandSlopeL60)} {GetValue(bandSlopeL30)} {GetValue(bandSlopeL10)} {GetValue(bandSlopeL5)} ");
Display($"K-L {GetValue(bandSlopeSpreadKL90)} {GetValue(bandSlopeSpreadKL60)} {GetValue(bandSlopeSpreadKL30)} {GetValue(bandSlopeSpreadKL10)} {GetValue(bandSlopeSpreadKL5)} ");
}
public void DisplaySlopesNumeric()
{
Display($" 90 60 30 10 5");
//Display($"K {Utility.FormatNumber(bandSlopeK90,10)} {Utility.FormatNumber(bandSlopeK60,10)} {Utility.FormatNumber(bandSlopeK30,10)} {Utility.FormatNumber(bandSlopeK10,10)} {Utility.FormatNumber(bandSlopeK5,10)} ");
//Display($"L {Utility.FormatNumber(bandSlopeL90,10)} {Utility.FormatNumber(bandSlopeL60,10)} {Utility.FormatNumber(bandSlopeL30,10)} {Utility.FormatNumber(bandSlopeL10,10)} {Utility.FormatNumber(bandSlopeL5,10)} ");
Display($"L-L {Utility.FormatNumber(bandSlopeSpreadKL90,10)} {Utility.FormatNumber(bandSlopeSpreadKL60,10)} {Utility.FormatNumber(bandSlopeSpreadKL30,10)} {Utility.FormatNumber(bandSlopeSpreadKL10,10)} {Utility.FormatNumber(bandSlopeSpreadKL5,10)} ");
}
private void Display(String message)
{
if(Verbose)
{
MDTrace.WriteLine(LogLevel.DEBUG,message);
}
}
}
}