Add MGMomentum

This commit is contained in:
2025-04-24 19:33:20 -04:00
parent 51a207d690
commit 63f6f5a9f5
20 changed files with 2511 additions and 2 deletions

View File

@@ -0,0 +1,175 @@
using MarketData.Utils;
namespace MarketData.Generator.Momentum
{
public class ActivePositions : Dictionary<int,Positions>
{
public ActivePositions()
{
}
public double GetExposure()
{
double exposure=0.00;
List<int> keys = new List<int>(Keys);
for(int index=0;index<keys.Count;index++)
{
List<Position> positions=this[keys[index]];
if(null==positions||0==positions.Count)continue;
exposure+=(from Position position in positions select position.Exposure).Sum();
}
return exposure;
}
public List<String> GetSymbols()
{
return SymbolsHeld();
}
public Position FindPosition(String symbol,DateTime purchaseDate)
{
Position foundPosition=null;
List<int> keys=new List<int>(this.Keys);
for(int index=0;index<keys.Count;index++)
{
Positions positions=this[keys[index]];
foreach(Position position in positions)
{
if(position.Symbol.Equals(symbol)&&position.PurchaseDate.Date.Equals(purchaseDate.Date))
{
foundPosition=position;
break;
}
}
}
return foundPosition;
}
public int FindSlotForPosition(Position searchPosition)
{
List<int> keys=new List<int>(this.Keys);
for(int index=0;index<keys.Count;index++)
{
Positions positions=this[keys[index]];
foreach(Position position in positions)
{
if(position == searchPosition)return index;
}
}
return -1;
}
public bool Remove(Position searchPosition)
{
List<int> keys = new List<int>(this.Keys);
for (int index = 0; index < keys.Count; index++)
{
Positions positions = this[keys[index]];
foreach (Position slotPosition in positions)
{
if (slotPosition == searchPosition)
{
positions.Remove(searchPosition);
return true;
}
}
}
return false;
}
public Positions GetPositions()
{
Positions positionsCollection=new Positions();
List<int> keys=new List<int>(this.Keys);
Dictionary<String,String> symbols=new Dictionary<String,String>();
for(int index=0;index<keys.Count;index++)
{
Positions positions=this[keys[index]];
foreach(Position 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++)
{
Positions positions = this[keys[index]];
foreach (Position position in positions)
{
if (!symbols.ContainsKey(position.Symbol)) symbols.Add(position.Symbol, position.Symbol);
}
}
return new List<String>(symbols.Keys);
}
public double GetMarketValue()
{
int count=Count;
double marketValue=0.00;
List<int> keys=new List<int>(this.Keys);
for(int index=0;index<keys.Count;index++)
{
List<Position> positions=this[keys[index]];
if(null==positions||0==positions.Count)continue;
marketValue+=(from Position position in positions select position.MarketValue).Sum();
}
return marketValue;
}
public double GetGainLoss()
{
int count=Count;
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<Position> positions=this[keys[index]];
if(null==positions||0==positions.Count)continue;
exposure+=(from Position position in positions select position.Exposure).Sum();
marketValue+=(from Position 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++)
{
Positions positions=this[keys[index]];
DateTime purchaseDate=(from Position 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)
{
SlotPosition slotPosition=SlotPosition.FromNVPCollection(nvpCollection);
if(!ContainsKey(slotPosition.Slot))
{
Add(slotPosition.Slot,new Positions());
}
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++)
{
Positions positions=this[keys[index]];
SlotPositions slotPositions=new SlotPositions(keys[index],positions);
NVPCollections nvpCollections=slotPositions.ToNVPCollections();
nvpCollectionsList.Add(nvpCollections);
}
return nvpCollectionsList;
}
}
}

View File

@@ -0,0 +1,699 @@
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using MarketData.Cache;
using MarketData.Generator.Model;
namespace MarketData.Generator.Momentum
{
public class MomentumBacktest
{
private double NonTradeableCash{get;set;}
private double CashBalance{get;set;}
private MGConfiguration Configuration{get;set;}
private int HoldingPeriod{get{return Configuration.HoldingPeriod;}}
private int MaxPositions{get{return Configuration.MaxPositions;}}
private ActivePositions ActivePositions{get;set;}
private Positions AllPositions{get;set;}
private int Cycle{get;set;}
private DateTime TradeDate{get;set;}
private DateTime StartDate{get;set;}
private DateTime AnalysisDate{get;set;}
private String PathSessionFileName{get;set;}
// ******************************************************************************************************************************************************
//************************************************************** D I S P L A Y G A I N L O S S *****************************************************
// ******************************************************************************************************************************************************
public static void DisplayGainLoss(String paramPathSessionFileName)
{
Profiler profiler=new Profiler();
ModelPerformanceSeries performanceSeries=GetModelPerformance(paramPathSessionFileName);
if(null==performanceSeries)return;
MDTrace.WriteLine("Date,Exposure,MarketValue,GainLossDOD,GainLoss,CumulativeGainLoss,R,(1+R),CumProd,CumProd-1,ClosedPositions");
foreach(ModelPerformanceItem modelPerformanceItem in performanceSeries)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\",\"{9}\",\"{10}\"",
modelPerformanceItem.Date.ToShortDateString(),
Utility.FormatCurrency(modelPerformanceItem.Exposure),
Utility.FormatCurrency(modelPerformanceItem.MarketValue),
Utility.FormatCurrency(modelPerformanceItem.GainLossDOD),
Utility.FormatCurrency(modelPerformanceItem.GainLoss),
Utility.FormatCurrency(modelPerformanceItem.CumulativeGainLoss),
Utility.FormatNumber(modelPerformanceItem.R,4),
Utility.FormatNumber(modelPerformanceItem.OnePlusR,4),
Utility.FormatNumber(modelPerformanceItem.CumProd,4),
Utility.FormatNumber(modelPerformanceItem.CumProdMinusOne,4),
modelPerformanceItem.ClosedPositions));
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Done, took {0}(ms)",profiler.End()));
}
public static ModelPerformanceSeries GetModelPerformance(String paramPathSessionFileName)
{
try
{
MGSessionParams sessionParams=MGSessionManager.RestoreSession(paramPathSessionFileName);
return GetModelPerformance(sessionParams);
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
// Calculates the expectation for the model ( Percent of Winning Trades * Average Gain)/(Percent Losing Trades * Average Loss)
// The expectation should be above zero
public static ModelStatistics GetModelStatistics(MGSessionParams sessionParams)
{
ModelStatistics modelStatistics=new ModelStatistics();
try
{
if(null==sessionParams||null==sessionParams.AllPositions||0==sessionParams.AllPositions.Count) return modelStatistics;
double totalTrades=sessionParams.AllPositions.Count;
double winningTrades=sessionParams.AllPositions.Where(x => x.GainLoss>=0.00).Count();
double losingTrades=sessionParams.AllPositions.Where(x => x.GainLoss<0.00).Count();
double averageWinningTrade=sessionParams.AllPositions.Where(x => x.GainLoss>=0.00).Average(x => x.GainLossPcnt)*100.00;
double averageLosingTrade=sessionParams.AllPositions.Where(x => x.GainLoss<0.00).Average(x => x.GainLossPcnt)*100.00;
double percentWinningTrades=(winningTrades/(double)sessionParams.AllPositions.Count)*100.00;
double percentLosingTrades=(losingTrades/(double)sessionParams.AllPositions.Count)*100.00;
double expectation=(percentWinningTrades*averageWinningTrade)/(percentLosingTrades*Math.Abs(averageLosingTrade));
modelStatistics.TotalTrades=(long)totalTrades;
modelStatistics.WinningTrades=(long)winningTrades;
modelStatistics.LosingTrades=(long)losingTrades;
modelStatistics.AverageWinningTradePercentGain=averageWinningTrade;
modelStatistics.AverageLosingTradePercentLoss=averageLosingTrade;
modelStatistics.WinningTradesPercent=percentWinningTrades;
modelStatistics.LosingTradesPercent=percentLosingTrades;
modelStatistics.Expectancy=expectation;
return modelStatistics;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return modelStatistics;
}
}
public static ModelPerformanceSeries GetModelPerformance(MGSessionParams sessionParams)
{
Profiler profiler=new Profiler();
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
DateGenerator dateGenerator=new DateGenerator();
try
{
if(null==sessionParams)return null;
MarketData.Generator.Momentum.Positions combinedPositions=sessionParams.GetCombinedPositions();
DateTime minDate=combinedPositions.Min(x => x.PurchaseDate);
DateTime maxDate=PricingDA.GetLatestDate();
double prevGainLoss=double.NaN;
LocalPriceCache.GetInstance().RemoveDate(maxDate);
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
MarketData.Generator.Momentum.Positions openPositions=new MarketData.Generator.Momentum.Positions(combinedPositions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList());
MarketData.Generator.Momentum.Positions closedPositions=new MarketData.Generator.Momentum.Positions(combinedPositions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList());
if(0==openPositions.Count&&0==closedPositions.Count) continue;
double gainLoss=0.00;
double gainLossClosedPositions=0.00;
double exposure=0.00;
double marketValue=0.00;
// if(HolidayDA.IsMarketHoliday(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(MarketData.Generator.Momentum.Position openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.PurchasePrice;
if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
{
Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,PricingDA.ForwardLookingDays);
LocalPriceCache.GetInstance().Add(prices);
}
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
}
else
{
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
}
foreach(MarketData.Generator.Momentum.Position closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
performanceItem.Date=currentDate;
performanceItem.Exposure=exposure;
performanceItem.MarketValue=marketValue;
performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
performanceItem.ClosedPositions=closedPositions.Count>0?true:false;
performanceSeries.Add(performanceItem);
prevGainLoss=gainLoss;
}
performanceSeries.CalculatePerformance();
return performanceSeries;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
finally
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Done, total took {0}(ms)",profiler.End()));
}
}
// ******************************************************************************************************************************************************
//************************************************************** D I S P L A Y S E S S I O N *****************************************************
// ******************************************************************************************************************************************************
public void DisplaySession(String paramPathSessionFileName)
{
if(null==paramPathSessionFileName)return;
PathSessionFileName=paramPathSessionFileName;
MGSessionParams sessionParams=null;
if(null==(sessionParams=RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",paramPathSessionFileName));
return;
}
Console.WriteLine(String.Format("SessionFile:{0} Last Updated:{1}",paramPathSessionFileName,sessionParams.LastUpdated));
Configuration.DisplayConfiguration();
MDTrace.WriteLine(LogLevel.DEBUG,"************** A L L P O S I T I O N S *************");
AllPositions=new Positions((from Position position in AllPositions orderby position.PurchaseDate ascending select position).ToList());
AllPositions.Display();
MDTrace.WriteLine(LogLevel.DEBUG,"************** A C T I V E P O S I T I O N S *************");
ActivePositions.Display();
MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P G A I N E R S *************");
AllPositions.DisplayTopFive();
MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P L O S S E R S *************");
AllPositions.DisplayBottomFive();
DisplayBalanceFromPositions();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StartDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(StartDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("AnalysisDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle));
}
// ******************************************************************************************************************************************************
//******************************************************* L I Q U I D A T E A L L P O S I T I O N S ***********************************************
// ******************************************************************************************************************************************************
public void MGLiquididate(String pathSessionFile,DateTime? tradeDate)
{
if (null == pathSessionFile) return;
MGSessionParams sessionParams=null;
PathSessionFileName = pathSessionFile;
if (null == (sessionParams = RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Error loading session file {0}", pathSessionFile));
return;
}
MDTrace.WriteLine(LogLevel.DEBUG, "************** L I Q U I D A T E P O S I T I O N S *************");
if (null == ActivePositions || 0 == ActivePositions.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("No active positions in file {0}", pathSessionFile));
return;
}
if(null==tradeDate)tradeDate = PricingDA.GetLatestDate(ActivePositions.GetSymbols());
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Trade date:{0}", Utility.DateTimeToStringMMHDDHYYYY(tradeDate.Value)));
for (int slotIndex = 0; slotIndex < ActivePositions.Count; slotIndex++)
{
Positions slotPositions = ActivePositions[slotIndex];
SellPositions(slotPositions, tradeDate.Value);
MDTrace.WriteLine(LogLevel.DEBUG, "********************* S E L L *********************");
slotPositions.Display();
AllPositions.Add(slotPositions);
CashBalance += slotPositions.MarketValue;
ActivePositions[slotIndex].Clear();
}
GBPriceCache.GetInstance().Dispose();
SaveSession();
}
// ******************************************************************************************************************************************************
// ****************************************************************** C L O S E **********************************************************************
// ******************************************************************************************************************************************************
public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double sellPrice,String pathSessionFile)
{
if(null==pathSessionFile) return false;
MGSessionParams sessionParams=null;
PathSessionFileName=pathSessionFile;
if(null==(sessionParams=RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile));
return false;
}
if(!BackupSession()) return false;
Positions activePositions = ActivePositions.GetPositions();
Position position=activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault();
if(null==position) // if it is not in the active positions then the position is already closed and we are modifying either the sell date or the sell price
{
position=AllPositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault();
if(null==position)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot locate position for symbol '{0}' purchased on {1}.",symbol,purchaseDate.ToShortDateString()));
return false;
}
position.SellDate = sellDate;
CashBalance -= position.MarketValue;
position.CurrentPrice = sellPrice;
CashBalance += position.MarketValue;
SaveSession();
return true;
}
position.SellDate = sellDate;
position.CurrentPrice = sellPrice;
CashBalance += position.MarketValue;
ActivePositions.Remove(position);
AllPositions.Add(position);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Position for symbol '{0}' purchased on {1} is now closed.",symbol,purchaseDate.ToShortDateString()));
SaveSession();
return true;
}
// ******************************************************************************************************************************************************
// *************************************************************************** E D I T ******************************************************************
// ******************************************************************************************************************************************************
public bool EditPosition(String symbol,DateTime purchaseDate,double purchasePrice,String pathSessionFile)
{
if(null==pathSessionFile) return false;
PathSessionFileName=pathSessionFile;
MGSessionParams sessionParams=null;
if(null==(sessionParams=RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile));
return false;
}
if(!BackupSession()) return false;
Positions activePositions = ActivePositions.GetPositions();
Position position=activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault();
if(null==position)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot locate position for symbol '{0}' purchased on {1}.",symbol,purchaseDate.ToShortDateString()));
return false;
}
if(!position.PurchaseDate.Equals(purchaseDate)) position.PurchaseDate=purchaseDate;
if(!position.PurchasePrice.Equals(purchasePrice))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting Cash for Position for symbol '{0}' purchased on {1}. Original Price: {2} New Price: {3} Change in Cash: {4}",
symbol,purchaseDate.ToShortDateString(),
Utility.FormatCurrency(position.PurchasePrice),
Utility.FormatCurrency(purchasePrice),
Utility.FormatCurrency((position.PurchasePrice-purchasePrice)*position.Shares)));
CashBalance+=(position.PurchasePrice-purchasePrice)*position.Shares;
position.PurchasePrice=purchasePrice;
}
SaveSession();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Position for symbol '{0}' purchased on {1} has been modified and saved.",symbol,purchaseDate.ToShortDateString()));
return true;
}
// ******************************************************************************************************************************************************
// ****************************************************************** B A C K T E S T *****************************************************************
// ******************************************************************************************************************************************************
// Ideally, startDate should be November,February,May,August
// paramStartDate is startDate {ORIGINAL START DATE}
// paramAnalysisDate is endDate {TODAY}
public BacktestResult PerformBacktest(DateTime paramStartDate,DateTime paramAnalysisDate,String paramPathSessionFileName,MGConfiguration configuration)
{
BacktestResult backTestResult=new BacktestResult();
DateGenerator dateGenerator=new DateGenerator();
Configuration=configuration;
CashBalance=Configuration.InitialCash;
ActivePositions=new ActivePositions();
AllPositions=new Positions();
StartDate=paramStartDate;
TradeDate=paramStartDate;
AnalysisDate=paramAnalysisDate;
PathSessionFileName=paramPathSessionFileName;
MGSessionParams sessionParams=null;
Cycle=0;
if(AnalysisDate.Date>Today().Date)return backTestResult;
if(Utility.IsEpoch(AnalysisDate))AnalysisDate=Today();
TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate);
if(TradeDate>AnalysisDate)
{
int startMonth=StartDate.Month;
TimeSpan timeSpan=new TimeSpan();
if((new int[]{12,3,6,9}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(30,0,0,0);
else if((new int[]{1,4,7,10}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(60,0,0,0);
else if((new int[]{2,5,8,11}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(90,0,0,0);
StartDate=StartDate-timeSpan;
TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate);
}
if(null!=PathSessionFileName)sessionParams=RestoreSession();
if(null!=sessionParams)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using session file {0}, Last updated {1}",paramPathSessionFileName,sessionParams.LastUpdated));
}
Configuration.DisplayConfiguration();
DisplayBalance();
while(true)
{
if(TradeDate>AnalysisDate)break;
int slotIndex=(int)(((double)Cycle)%((double)(HoldingPeriod)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TRADE DATE {0} , ANALYSIS DATE {1}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate),Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate)));
if(!ActivePositions.ContainsKey(slotIndex))
{
Positions positions = null;
positions=BuyPositions(TradeDate,AnalysisDate,CashBalance/((double)HoldingPeriod-(double)ActivePositions.Count),SymbolsHeld(TradeDate));
MDTrace.WriteLine(LogLevel.DEBUG, "******************** B U Y ********************");
if(CashBalance-positions.Exposure<0.00)
{
positions.Clear();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************"));
}
positions.Display();
ActivePositions.Add(slotIndex,positions);
CashBalance-=positions.Exposure;
DisplayBalance();
}
else
{
Positions slotPositions=ActivePositions[slotIndex];
SellPositions(slotPositions,TradeDate);
MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L *********************");
slotPositions.Display();
AllPositions.Add(slotPositions);
CashBalance+=slotPositions.MarketValue;
ActivePositions[slotIndex].Clear();
DisplayBalance();
double cashAllocation=CashBalance;
cashAllocation = Math.Min(CashBalance, (ActivePositions.GetExposure() + CashBalance) / (double)HoldingPeriod);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CASH ALLOCATION:{0}",Utility.FormatCurrency(cashAllocation)));
Positions positions = null;
positions=BuyPositions(TradeDate,AnalysisDate,cashAllocation,SymbolsHeld(TradeDate));
MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************");
positions.Display();
if(CashBalance-positions.Exposure<=0.00)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************"));
break;
}
ActivePositions[slotIndex]=positions;
CashBalance-=positions.Exposure;
DisplayBalance();
}
Cycle++;
TradeDate=dateGenerator.GetNextMonthEnd(TradeDate);
if(TradeDate>AnalysisDate)break;
} // WHILE TRUE
MDTrace.WriteLine(LogLevel.DEBUG,"RUN COMPLETE.");
if(null!=PathSessionFileName)SaveSession();
for(int slotIndex=0;slotIndex<HoldingPeriod;slotIndex++)
{
if(!ActivePositions.ContainsKey(slotIndex)||0==ActivePositions[slotIndex].Count())continue;
Positions slotPositions=ActivePositions[slotIndex];
SellPositions(slotPositions,AnalysisDate);
MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L ********************");
slotPositions.Display();
AllPositions.Add(slotPositions);
CashBalance+=slotPositions.MarketValue;
ActivePositions[slotIndex].Clear();
}
MDTrace.WriteLine(LogLevel.DEBUG,"************** A L L P O S I T I O N S *************");
AllPositions=new Positions((from Position position in AllPositions orderby position.PurchaseDate ascending select position).ToList());
AllPositions.Display();
MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P G A I N E R S *************");
AllPositions.DisplayTopFive();
MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P L O S E R S *************");
AllPositions.DisplayBottomFive();
DisplayBalance();
backTestResult.Success=true;
backTestResult.CashBalance=CashBalance;
GBPriceCache.GetInstance().Dispose();
return backTestResult;
}
public List<String> SymbolsHeld(DateTime tradeDate)
{
List<String> symbolsHeld=ActivePositions.SymbolsHeld();
if(!Configuration.IncludeTradeMasterForSymbolsHeld)return symbolsHeld;
if(null == symbolsHeld)symbolsHeld=new List<String>();
PortfolioTrades portfolioTrades=PortfolioDA.GetOpenTradesAsOf(tradeDate);
if(null == portfolioTrades || 0==portfolioTrades.Count)return symbolsHeld;
symbolsHeld.AddRange(portfolioTrades.Symbols.Distinct());
return symbolsHeld;
}
// **********************************************************************************************************************************************************
// **************************************************************** G E T E X P O S U R E / M A R K E T V A L U E*****************************************
// **********************************************************************************************************************************************************
public RealtimeGainLoss GetRealtimeGainLoss(DateTime tradeDate)
{
int count=ActivePositions.Count;
double marketValue=0.00;
double exposure=0.00;
RealtimeGainLoss gainLoss=new RealtimeGainLoss();
for(int slotIndex=0;slotIndex<count;slotIndex++)
{
List<Position> positions=ActivePositions[slotIndex];
if(null==positions||0==positions.Count)continue;
foreach(Position position in positions)
{
Price price=GBPriceCache.GetInstance().GetRealtimePrice(position.Symbol);
if(null==price){MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot price {0} on {1}",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));continue;}
position.CurrentPrice=price.Close;
}
}
for(int slotIndex=0;slotIndex<count;slotIndex++)
{
List<Position> positions=ActivePositions[slotIndex];
if(null==positions||0==positions.Count)continue;
exposure+=(from Position position in positions select position.Exposure).Sum();
marketValue+=(from Position position in positions select position.MarketValue).Sum();
}
gainLoss.Exposure=exposure;
gainLoss.MarketValue=marketValue;
return gainLoss;
}
// **************************************************************************************************************************************************
// **************************************************************** S E L L P O S I T I O N S *****************************************************
// ***************************************************************************************************************************************************
private void SellPositions(Positions positions,DateTime sellDate)
{
foreach(Position position in positions)
{
Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,sellDate);
position.SellDate=sellDate;
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate)));
position.CurrentPrice=position.PurchasePrice;
}
else position.CurrentPrice=price.Close;
}
}
private void SellPosition(Position position,DateTime sellDate)
{
Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,sellDate);
position.SellDate=sellDate;
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate)));
position.CurrentPrice=position.PurchasePrice;
}
else position.CurrentPrice=price.Close;
}
// ***************************************************************************************************************************************************
// ******************************************************** B U Y P O S I T I O N S *************************************************************
// ***************************************************************************************************************************************************
private Positions BuyPositions(DateTime tradeDate, DateTime analysisDate, double cash, List<String> symbolsHeld)
{
MDTrace.WriteLine(LogLevel.DEBUG,"**BUYPOSITIONS**");
Positions positions = new Positions();
int positionCount = 0;
if (Configuration.BenchmarkMode) return BuyBenchmarkPositions(tradeDate, cash);
MomentumCandidates momentumCandidates = MomentumGenerator.GenerateMomentum(tradeDate, symbolsHeld, Configuration);
for (int index = 0; index < momentumCandidates.Count; index++)
{
MomentumCandidate momentumCandidate = momentumCandidates[index];
Price price = GBPriceCache.GetInstance().GetPrice(momentumCandidate.Symbol, tradeDate);
if (null == price)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", momentumCandidate.Symbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
continue;
}
Position position = new Position();
position.Symbol = momentumCandidate.Symbol;
position.CumReturn252 = momentumCandidate.CumReturn252;
position.IDIndicator = momentumCandidate.IDIndicator;
position.Score=momentumCandidate.Score;
position.MaxDrawdown = momentumCandidate.MaxDrawdown;
position.MaxUpside = momentumCandidate.MaxUpside;
position.PE = momentumCandidate.PE;
position.Beta = momentumCandidate.Beta;
position.ZacksRank = momentumCandidate.ZacksRank;
position.Velocity = momentumCandidate.Velocity;
position.Volume = momentumCandidate.Volume;
position.Return1D = position.Return1D;
position.PurchaseDate = tradeDate;
position.PurchasePrice = price.Close;
position.CurrentPrice = price.Close;
position.Shares = (int)Math.Floor((cash / (double)MaxPositions) / position.PurchasePrice);
if (0 == position.Shares) continue; // if not able to purchase any shares, for example, if the share price exceeds our purchasing power then move on to the next candidate
positions.Add(position);
positionCount++;
if (positionCount >= MaxPositions) break;
}
if (0 == positions.Count && Configuration.UseFallbackCandidate) // if we don't get any signals then consider the fallback candidate options as per the configuration file
{
String fallbackCandidate = Configuration.FallbackCandidate;
if (null != Configuration.FallbackCandidateBestOf && !"".Equals(Configuration.FallbackCandidateBestOf))
{
QualityIndicator qualityIndicator=new QualityIndicator(Configuration.QualityIndicatorType);
fallbackCandidate=CandidateSelector.SelectBestCandidateSymbol(qualityIndicator,Utility.ToList(Configuration.FallbackCandidateBestOf),Configuration.FallbackCandidate,tradeDate);
}
Price price = GBPriceCache.GetInstance().GetPrice(fallbackCandidate, tradeDate);
if (null == price)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", fallbackCandidate, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
return positions;
}
Position position = new Position();
position.Symbol = fallbackCandidate;
position.PurchaseDate = tradeDate;
position.PurchasePrice = price.Close;
position.CurrentPrice = price.Close;
position.Shares = (int)Math.Floor(cash / position.PurchasePrice);
if(0 == position.Shares)return positions;
positions.Add(position);
}
return positions;
}
private Positions BuyBenchmarkPositions(DateTime tradeDate, double cash)
{
Positions positions = new Positions();
Price benchmarkPrice = GBPriceCache.GetInstance().GetPrice(Configuration.BenchmarkModeSymbol, tradeDate);
if (null == benchmarkPrice)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", Configuration.BenchmarkModeSymbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
return positions;
}
Position position = new Position();
position.Symbol = Configuration.BenchmarkModeSymbol;
position.PurchaseDate = tradeDate;
position.PurchasePrice = benchmarkPrice.Close;
position.Shares = (int)Math.Floor(cash / position.PurchasePrice);
if (0 == position.Shares) return positions;
positions.Add(position);
return positions;
}
// *********************************************************************************************************************************************************************
// ************************************************************ E N D B U Y P O S I T I O N S ***********************************************
// *********************************************************************************************************************************************************************
private void DisplayBalance()
{
MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************");
MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,AVAILABLE CASH,TOTAL ACCOUNT");
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2}",Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance))));
for (int slotIndex = 0; slotIndex < ActivePositions.Count; slotIndex++)
{
Positions slotPositions = ActivePositions[slotIndex];
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SLOT:{0} EXPOSURE:{1}",slotIndex,Utility.FormatCurrency(slotPositions.Exposure)));
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL EXPOSURE: {0}",Utility.FormatCurrency(ActivePositions.GetExposure())));
MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************");
}
private void DisplayBalanceFromPositions()
{
MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,GAIN/LOSS,GAIN/LOSS(%),AVAILABLE CASH,TOTAL ACCOUNT");
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4}",
Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),
Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetGainLoss())),
Utility.AddQuotes(Utility.FormatPercent(ActivePositions.GetGainLossPercent())),
Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance))));
}
private void DisplayBalance(RealtimeGainLoss gainLoss)
{
MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,GAIN/LOSS,GAIN/LOSS(%),AVAILABLE CASH,TOTAL ACCOUNT");
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4}",
Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),
Utility.AddQuotes(Utility.FormatCurrency(gainLoss.GainLoss)),
Utility.AddQuotes(Utility.FormatPercent(gainLoss.GainLossPercent)),
Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
Utility.AddQuotes(Utility.FormatCurrency(gainLoss.MarketValue+CashBalance))));
}
// ****************************************************************************************************************************************
// ************************************************************* C O N T R O L T O D A Y ***********************************************
// ****************************************************************************************************************************************
public DateTime Today()
{
return DateTime.Now;
}
// ****************************************************************************************************************************************
// **************************************************************** S E S S I O N M A N A G E M E N T ***********************************
// ****************************************************************************************************************************************
public MGSessionParams RestoreSession()
{
try
{
MGSessionManager sessionManager=new MGSessionManager();
if(!MGSessionManager.SessionAvailable(PathSessionFileName))return null;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Restoring session from '{0}'",PathSessionFileName));
MGSessionParams sessionParams=MGSessionManager.RestoreSession(PathSessionFileName);
TradeDate=sessionParams.TradeDate;
if(TradeDate.Date<AnalysisDate.Date)TradeDate=AnalysisDate;
StartDate=sessionParams.StartDate;
Configuration=sessionParams.Configuration;
ActivePositions=sessionParams.ActivePositions;
AllPositions=sessionParams.AllPositions;
Cycle=sessionParams.Cycle;
CashBalance=sessionParams.CashBalance;
NonTradeableCash=sessionParams.NonTradeableCash;
return sessionParams;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
public void SaveSession()
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Saving session to '{0}'",PathSessionFileName));
MGSessionParams sessionParams=new MGSessionParams();
MGSessionManager sessionManager=new MGSessionManager();
sessionParams.LastUpdated=Today();
sessionParams.TradeDate=TradeDate;
sessionParams.StartDate=StartDate;
sessionParams.AnalysisDate=AnalysisDate;
sessionParams.Configuration=Configuration;
sessionParams.ActivePositions=ActivePositions;
sessionParams.AllPositions=AllPositions;
sessionParams.Cycle=Cycle;
sessionParams.CashBalance=CashBalance;
sessionParams.NonTradeableCash=NonTradeableCash;
sessionManager.SaveSession(sessionParams,PathSessionFileName);
}
public bool BackupSession()
{
String[] parts=PathSessionFileName.Split('.');
String backupFileName=parts[0]+"_"+Utility.DateTimeToStringYYYYMMDDMMSSTT(DateTime.Now)+".bak";
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Saving session to '{0}'",backupFileName));
MGSessionParams sessionParams=new MGSessionParams();
MGSessionManager sessionManager=new MGSessionManager();
sessionParams.LastUpdated=Today();
sessionParams.TradeDate=TradeDate;
sessionParams.StartDate=StartDate;
sessionParams.AnalysisDate=AnalysisDate;
sessionParams.Configuration=Configuration;
sessionParams.ActivePositions=ActivePositions;
sessionParams.AllPositions=AllPositions;
sessionParams.Cycle=Cycle;
sessionParams.CashBalance=CashBalance;
sessionParams.NonTradeableCash=NonTradeableCash;
return sessionManager.SaveSession(sessionParams,backupFileName);
}
}
}

View File

@@ -0,0 +1,13 @@

namespace MarketData.Generator.Momentum
{
public class BacktestResult
{
public BacktestResult()
{
}
public double CashBalance{get;set;}
public bool Success{get;set;}
}
}

View File

@@ -0,0 +1,92 @@
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
namespace MarketData.Generator.Momentum
{
public class CandidateSelector
{
private static int DAY_COUNT=252;
private CandidateSelector()
{
}
public static String SelectBestCandidateSymbol(QualityIndicator 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(QualityIndicator.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(QualityIndicator 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(QualityIndicator.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,19 @@

namespace MarketData.Generator.Momentum
{
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,46 @@
using System.Text;
using MarketData.Utils;
namespace MarketData.Generator.Momentum
{
public class MomentumCandidates : List<MomentumCandidate>
{
public MomentumCandidates()
{
}
public MomentumCandidates(List<MomentumCandidate> momentumCandidates)
{
foreach(MomentumCandidate momentumCandidate in momentumCandidates)Add(momentumCandidate);
}
}
public class MomentumCandidate
{
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 MaxDrawdown{get;set;}
public double MaxUpside{get;set;}
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 String ZacksRank{get;set;}
public static String Header()
{
StringBuilder sb=new StringBuilder();
sb.Append("Symbol,AnalysisDate,Return,DayCount,IDIndicator,Score,MaxDrawdown,MaxUpside");
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(","). Append(Utility.FormatPercent(MaxDrawdown)).Append(",").Append(Utility.FormatPercent(MaxUpside));
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,56 @@
using MarketData.MarketDataModel;
namespace MarketData.Generator.Momentum
{
public class QualityIndicatorCandidate
{
public QualityIndicatorCandidate()
{
}
//public QualityIndicatorCandidate(String symbol,double idIndicator,double score)
//{
// Symbol=symbol;
// IDIndicator=idIndicator;
// Score=score;
//}
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,234 @@
using MarketData.Utils;
namespace MarketData.Generator.Momentum
{
public class MGConfiguration
{
// 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;}
// 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;}
// 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 is selected as the best 252 day return
// 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 MGConfiguration()
{
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
MaxPositions=3; // 3 is the default
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;
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,AGG,ACWX"; // if set then the fallback candidate is selected as the best 252 day return in this comma seperated list (i.e.) "SHV,ACWX,AGG"
// Set the QualityIndicator type to the IDIndicator
QualityIndicatorType=QualityIndicator.ToString(QualityIndicator.QualityType.IDIndicator);
IncludeTradeMasterForSymbolsHeld=false;
}
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("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()));
return nvpCollection;
}
public static MGConfiguration FromNVPCollection(NVPCollection nvpCollection)
{
MGConfiguration mgConfiguration=new MGConfiguration();
NVPDictionary nvpDictionary=nvpCollection.ToDictionary();
mgConfiguration.Verbose=nvpDictionary["Verbose"].Get<Boolean>();
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>();
if(nvpDictionary.ContainsKey("QualityIndicatorType")) mgConfiguration.QualityIndicatorType=nvpDictionary["QualityIndicatorType"].Get<String>();
else mgConfiguration.QualityIndicatorType=QualityIndicator.ToString(QualityIndicator.QualityType.IDIndicator);
if(nvpDictionary.ContainsKey("IncludeTradeMasterForSymbolsHeld")) mgConfiguration.IncludeTradeMasterForSymbolsHeld=nvpDictionary["IncludeTradeMasterForSymbolsHeld"].Get<bool>();
else mgConfiguration.IncludeTradeMasterForSymbolsHeld=false;
return mgConfiguration;
}
public void DisplayConfiguration()
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Verbose,{0}",Verbose));
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));
}
}
}

View File

@@ -0,0 +1,156 @@
using MarketData.Utils;
namespace MarketData.Generator.Momentum
{
// *****************************************************************************
public class MGSessionManager
{
public bool SaveSession(MGSessionParams 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("SESSIONv1.00");
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());
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);
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 MGSessionParams RestoreSession(String pathSessionFile)
{
FileStream inStream =null;
StreamReader streamReader=null;
try
{
if(!SessionAvailable(pathSessionFile))return null;
MGSessionParams sessionParams=new MGSessionParams();
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)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());
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>();
NVPCollection configurationCollection=new NVPCollection(streamReader.ReadLine());
sessionParams.Configuration=MGConfiguration.FromNVPCollection(configurationCollection);
int totalActivePositions=(new NVP(streamReader.ReadLine())).Get<int>();
sessionParams.ActivePositions=new ActivePositions();
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=Positions.FromNVPCollections(nvpCollections);
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 bool IsValidSessionFile(String pathSessionFile)
{
FileStream inStream =null;
StreamReader streamReader=null;
try
{
if(null==pathSessionFile)return false;
if(!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)return false;
return true;
}
catch(Exception)
{
return false;
}
finally
{
if(null!=streamReader)streamReader.Close();
if(null!=inStream)inStream.Close();
}
}
}
}

View File

@@ -0,0 +1,27 @@

namespace MarketData.Generator.Momentum
{
public class MGSessionParams
{
public DateTime LastUpdated { get; set; }
public DateTime TradeDate { get; set; }
public DateTime StartDate { get; set; }
public DateTime AnalysisDate { get; set; }
public MGConfiguration Configuration { get; set; }
public ActivePositions ActivePositions { get; set; }
public Positions AllPositions { get; set; }
public double CashBalance { get; set; }
public double NonTradeableCash { get; set; }
public int Cycle { get; set; }
// This gets AllPositions+Positions
public Positions GetCombinedPositions()
{
Positions positions=new Positions();
foreach(Position position in AllPositions) positions.Add(position);
Positions activePositions=ActivePositions.GetPositions();
foreach(Position position in activePositions) positions.Add(position);
return positions;
}
}
}

View File

@@ -0,0 +1,356 @@
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using MarketData.Numerical;
using MarketData.Cache;
// Filename: MomentumGenerator.cs
// Author:Sean Kessler
// Date:01/2018
namespace MarketData.Generator.Momentum
{
/// <summary>Generate momentum selections - </summary>
public class MomentumGenerator
{
public enum MomentumGeneratorConstants{DayCount=252}; // Trading days in one year
private MomentumGenerator()
{
}
// These two interfaces are used by the UI so that it can capture the fallback candidates
public static MomentumCandidates GenerateMomentum(DateTime tradeDate,MGConfiguration config)
{
List<String> symbolsHeld=new List<String>();
return new MomentumCandidates(GenerateMomentum(tradeDate,symbolsHeld,config).Take(config.MaxPositions).ToList());
}
public static MomentumCandidates GenerateMomentumWithFallback(DateTime tradeDate,MGConfiguration config)
{
List<String> symbolsHeld=new List<String>();
MomentumCandidates momentumCandidates=GenerateMomentum(tradeDate,symbolsHeld,config);
QualityIndicator qualityIndicator=new QualityIndicator(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)
{
ZacksRank zacksRank=ZacksRankDA.GetZacksRankOnOrBefore(bestCandidate.Symbol,tradeDate);
MomentumCandidate momentumCandidate=new MomentumCandidate();
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;
if(null!=zacksRank)momentumCandidate.ZacksRank=zacksRank.Rank;
momentumCandidates=new MomentumCandidates();
momentumCandidates.Add(momentumCandidate);
}
}
}
return momentumCandidates;
}
// This interface is called by the Backtest
public static MomentumCandidates GenerateMomentum(DateTime tradeDate,List<String> symbolsHeld,MGConfiguration config)
{
DateGenerator dateGenerator=new DateGenerator();
List<String> symbols=PricingDA.GetSymbols();
MomentumCandidates momentumCandidates=new MomentumCandidates();
MomentumCandidates highPECandidates=new MomentumCandidates();
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)MomentumGenerator.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;
// 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 && fundamental.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;
}
// Zacks Rank. This is for informational purposes for now but may further it's use in the future.
ZacksRank zacksRank=ZacksRankDA.GetZacksRankOnOrBefore(symbol,tradeDate);
// 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."));
MomentumCandidate highPECandidate=new MomentumCandidate();
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.MaxDrawdown=prices.MaxDrawdown();
highPECandidate.MaxUpside=prices.MaxUpside();
highPECandidate.PE=fundamental.PE;
highPECandidate.Beta=fundamental.Beta;
highPECandidate.Velocity=velocity;
highPECandidate.Volume=price.Volume;
highPECandidate.Return1D=return1D;
if(null!=zacksRank)highPECandidate.ZacksRank=zacksRank.Rank;
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
MomentumCandidate momentumCandidate=new MomentumCandidate();
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.MaxDrawdown=prices.MaxDrawdown();
momentumCandidate.MaxUpside=prices.MaxUpside();
momentumCandidate.PE=fundamental.PE;
momentumCandidate.Beta=fundamental.Beta;
momentumCandidate.Velocity=velocity;
momentumCandidate.Volume=price.Volume;
momentumCandidate.Return1D=return1D;
if(null!=zacksRank)momentumCandidate.ZacksRank=zacksRank.Rank;
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 MomentumCandidates(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 MomentumCandidate momentumCandidate in highPECandidates select momentumCandidate.Symbol).ToList())));
}
QualityIndicator qualityIndicator=new QualityIndicator(config.QualityIndicatorType);
if(qualityIndicator.Quality.Equals(QualityIndicator.QualityType.IDIndicator))
{
momentumCandidates=new MomentumCandidates((from MomentumCandidate momentumCandidate in momentumCandidates orderby momentumCandidate.IDIndicator ascending, momentumCandidate.CumReturn252 descending, momentumCandidate.Return1D descending, momentumCandidate.Volume descending select momentumCandidate).ToList());
}
else
{
momentumCandidates=new MomentumCandidates((from MomentumCandidate 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,19 @@

namespace MarketData.Generator.Momentum
{
public class SMSNotifications : Dictionary<String,SMSNotification>
{
}
public class SMSNotification
{
public SMSNotification()
{
}
public DateTime NotificationTime{get;set;}
public int ElapsedTimeMinutes(DateTime currentTime)
{
TimeSpan elapsedTime=currentTime-NotificationTime;
return elapsedTime.Minutes;
}
}
}

View File

@@ -0,0 +1,303 @@
using MarketData.Utils;
using MarketData.Generator.Interface;
namespace MarketData.Generator.Momentum
{
public class Position : IPurePosition
{
public Position()
{
CurrentPrice=double.NaN;
}
public Position(Position 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;
ZacksRank = position.ZacksRank;
CumReturn252 = position.CumReturn252;
IDIndicator = position.IDIndicator;
Score=position.Score;
MaxDrawdown = position.MaxDrawdown;
MaxUpside = position.MaxUpside;
Velocity = position.Velocity;
PE = position.PE;
Beta = position.Beta;
SharpeRatio = position.SharpeRatio;
}
public static Position Clone(Position position)
{
Position clonedPosition = new Position();
clonedPosition.Symbol = position.Symbol;
clonedPosition.PurchaseDate = position.PurchaseDate;
clonedPosition.SellDate = position.SellDate;
clonedPosition.Shares = position.Shares;
clonedPosition.PurchasePrice = position.PurchasePrice;
clonedPosition.CurrentPrice = position.CurrentPrice;
clonedPosition.Volume = position.Volume;
clonedPosition.Return1D = position.Return1D;
clonedPosition.ZacksRank = position.ZacksRank;
clonedPosition.CumReturn252 = position.CumReturn252;
clonedPosition.IDIndicator = position.IDIndicator;
clonedPosition.Score = position.Score;
clonedPosition.MaxDrawdown = position.MaxDrawdown;
clonedPosition.MaxUpside = position.MaxUpside;
clonedPosition.Velocity = position.Velocity;
clonedPosition.PE = position.PE;
clonedPosition.Beta = position.Beta;
clonedPosition.SharpeRatio = position.SharpeRatio;
return clonedPosition;
}
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 String ZacksRank{get;set;}
public double CumReturn252{get;set;}
public double IDIndicator{get;set;}
public double Score{get;set;}
public double MaxDrawdown{get;set;}
public double MaxUpside{get;set;}
public double Velocity{get;set;}
public double PE{get;set;}
public double Beta{get;set;}
public double SharpeRatio { 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("ZacksRank",ZacksRank));
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("MaxDrawdown",MaxDrawdown.ToString()));
nvpCollection.Add(new NVP("MaxUpside",MaxUpside.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("SharpeRatio", SharpeRatio.ToString()));
return nvpCollection;
}
public static Position FromNVPCollection(NVPCollection nvpCollection)
{
Position position=new Position();
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>();
if(nvpDictionary.ContainsKey("ZacksRank"))position.ZacksRank=nvpDictionary["ZacksRank"].Get<String>();
position.CumReturn252=nvpDictionary["CumReturn252"].Get<long>();
position.IDIndicator=nvpDictionary["IDIndicator"].Get<double>();
if(nvpDictionary.ContainsKey("MaxDrawdown"))position.MaxDrawdown=nvpDictionary["MaxDrawdown"].Get<double>();
if(nvpDictionary.ContainsKey("MaxUpside"))position.MaxUpside=nvpDictionary["MaxUpside"].Get<double>();
position.Velocity=nvpDictionary["Velocity"].Get<double>();
position.PE=nvpDictionary["PE"].Get<double>();
position.Beta=nvpDictionary["Beta"].Get<double>();
if (nvpDictionary.ContainsKey("SharpeRatio")) position.SharpeRatio = nvpDictionary["SharpeRatio"].Get<double>();
else position.SharpeRatio = double.NaN;
if(nvpDictionary.ContainsKey("Score")) position.Score=nvpDictionary["Score"].Get<double>();
else position.Score=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");
}
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}",
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(MaxDrawdown)),
Utility.AddQuotes(Utility.FormatPercent(MaxUpside)),
Utility.AddQuotes(Utility.FormatPercent(Velocity)),
Utility.AddQuotes(Utility.FormatNumber(PE)),
Utility.AddQuotes(Utility.FormatNumber(Beta)),
Utility.AddQuotes(Utility.FormatNumber(SharpeRatio))
));
}
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}",
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(MaxDrawdown)),
Utility.AddQuotes(Utility.FormatPercent(MaxUpside)),
Utility.AddQuotes(Utility.FormatPercent(Velocity)),
Utility.AddQuotes(Utility.FormatNumber(PE)),
Utility.AddQuotes(Utility.FormatNumber(Beta)),
Utility.AddQuotes(Utility.FormatNumber(SharpeRatio))
));
}
}
}
// ****************************************************************************************************************************************************************
public class Positions : List<Position>
{
public Positions()
{
}
public Positions(Positions positions)
{
foreach(Position position in positions)Add(position);
}
public Positions(List<Position> positions)
{
foreach(Position position in positions)Add(position);
}
public Positions(Position position)
{
Add(position);
}
public void Add(Positions positions)
{
foreach(Position position in positions)Add(position);
}
public double Exposure
{
get
{
return (from Position position in this select position.PurchasePrice*position.Shares).Sum();
}
}
public double MarketValue
{
get
{
return (from Position position in this select position.CurrentPrice*position.Shares).Sum();
}
}
public NVPCollections ToNVPCollections()
{
NVPCollections nvpCollections=new NVPCollections();
foreach(Position position in this)
{
nvpCollections.Add(position.ToNVPCollection());
}
return nvpCollections;
}
public static Positions FromNVPCollections(NVPCollections nvpCollections)
{
Positions positions=new Positions();
foreach(NVPCollection nvpCollection in nvpCollections)
{
positions.Add(Position.FromNVPCollection(nvpCollection));
}
return positions;
}
public void DisplayTopFive()
{
Positions positions = new Positions(this.OrderByDescending(x => x.GainLossPcnt).Take(5).ToList());
Position.DisplayHeader();
for (int index = 0; index < positions.Count; index++)
{
Position position = positions[index];
position.Display();
}
}
public void DisplayBottomFive()
{
Positions positions = new Positions(this.OrderBy(x => x.GainLossPcnt).Take(5).ToList());
Position.DisplayHeader();
for (int index = 0; index < positions.Count; index++)
{
Position position = positions[index];
position.Display();
}
}
public void Display()
{
Position.DisplayHeader();
for(int index=0;index<Count;index++)
{
Position 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,48 @@

namespace MarketData.Generator.Momentum
{
public class QualityIndicator
{
public enum QualityType{IDIndicator=0,ScoreIndicator=1};
private QualityType qualityType;
public QualityIndicator()
{
qualityType=QualityType.IDIndicator;
}
public QualityIndicator(QualityType qualityType)
{
this.qualityType=qualityType;
}
public QualityIndicator(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(QualityIndicator.QualityType qualityType)
{
if(qualityType.Equals(QualityType.ScoreIndicator))return "SCOREINDICATOR";
return "IDINDICATOR";
}
public static QualityType ToQuality(String strQualityType)
{
if(strQualityType.Equals("SCOREINDICATOR")) return QualityIndicator.QualityType.ScoreIndicator;
return QualityIndicator.QualityType.IDIndicator;
}
public QualityType Quality
{
get{return qualityType;}
set{qualityType=value;}
}
}
}

View File

@@ -0,0 +1,32 @@
using MarketData.MarketDataModel;
using MarketData.Numerical;
namespace MarketData.Generator.Momentum
{
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,69 @@
using MarketData.Utils;
namespace MarketData.Generator.Momentum
{
public class SlotPositions : List<SlotPosition>
{
public SlotPositions()
{
}
public SlotPositions(int slot,Positions positions)
{
for(int index=0;index<positions.Count;index++)
{
Add(new SlotPosition(slot,positions[index]));
}
}
public NVPCollections ToNVPCollections()
{
NVPCollections nvpCollections=new NVPCollections();
foreach(SlotPosition position in this)
{
nvpCollections.Add(position.ToNVPCollection());
}
return nvpCollections;
}
public static SlotPositions FromNVPCollections(NVPCollections nvpCollections)
{
SlotPositions positions=new SlotPositions();
foreach(NVPCollection nvpCollection in nvpCollections)
{
positions.Add(SlotPosition.FromNVPCollection(nvpCollection));
}
return positions;
}
}
public class SlotPosition : Position
{
public SlotPosition()
{
}
public SlotPosition(int slot,Position position) : base(position)
{
Slot=slot;
}
public SlotPosition(Position position) : base(position)
{
}
public int Slot{get;set;}
public Position ToPosition()
{
Position position = new Position(this);
return position;
}
public override NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection=base.ToNVPCollection();
nvpCollection.Insert(0,new NVP("Slot",Slot.ToString()));
return nvpCollection;
}
public static new SlotPosition FromNVPCollection(NVPCollection nvpCollection)
{
NVPDictionary nvpDictionary=nvpCollection.ToDictionary();
Position position=Position.FromNVPCollection(nvpCollection);
SlotPosition slotPosition=new SlotPosition(position);
slotPosition.Slot=nvpDictionary["Slot"].Get<int>();
return slotPosition;
}
}
}