Files
ARM64/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs
2025-04-24 19:33:20 -04:00

700 lines
39 KiB
C#
Executable File

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);
}
}
}