Files
marketdata/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs

1826 lines
106 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using System.Linq;
using MarketData.Helper;
using MarketData.Numerical;
using MarketData.Cache;
using MarketData.Generator.Indicators;
using MarketData.Generator.Model;
using StopLimit=MarketData.Generator.Model.StopLimit;
using StopLimits=MarketData.Generator.Model.StopLimits;
using MarketData.Generator.ModelGenerators;
using Axiom.Interpreter;
using MarketData.Generator.MovingAverage;
namespace MarketData.Generator.CMTrend
{
public class CMTTrendModel
{
public enum RunType { BacktestResult, Daily };
private double CashBalance { get; set; }
private double NonTradeableCash{get;set;}
public CMTParams Parameters { get; set; }
private int MaxDailyPositions { get { return Parameters.MaxDailyPositions; } }
private int MaxOpenPositions { get { return Parameters.MaxOpenPositions; } }
private int MaxPricingExceptions { get { return Parameters.MaxPricingExceptions; } }
private ActivePositions ActivePositions { get; set; }
private Positions AllPositions { get; set; }
private CMTCandidates Candidates { get; set; }
private CMTPricingExceptions PricingExceptions { get; set; }
private StopLimits StopLimits { 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();
if(null!=paramPathSessionFileName) paramPathSessionFileName=paramPathSessionFileName.Trim();
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
{
CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(paramPathSessionFileName);
return GetModelPerformance(sessionParams);
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
// Calcualtes 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(CMTSessionParams 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(CMTSessionParams sessionParams)
{
Profiler profiler=new Profiler();
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
DateGenerator dateGenerator=new DateGenerator();
try
{
if(null==sessionParams) return null;
MarketData.Generator.CMTrend.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.CMTrend.Positions openPositions=new MarketData.Generator.CMTrend.Positions(combinedPositions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList());
MarketData.Generator.CMTrend.Positions closedPositions=new MarketData.Generator.CMTrend.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;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(MarketData.Generator.CMTrend.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()));
continue;
// return performanceSeries;
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(MarketData.Generator.CMTrend.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;
CMTSessionParams 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));
Parameters.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,"************** T O P G A I N E R S *************");
AllPositions.DisplayTop(10);
MDTrace.WriteLine(LogLevel.DEBUG,"************** T O P L O S E R S *************");
AllPositions.DisplayBottom(10);
MDTrace.WriteLine(LogLevel.DEBUG,"************** A C T I V E P O S I T I O N S *************");
ActivePositions.Display();
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)));
double gainLoss=0.00;
double winners=0.00;
double losers=0.00;
double averageWinner=0.00;
double averageLoser=0.00;
double totalTrades=0.00;
winners=ActivePositions.Where(x => x.GainLoss>=0).Count()+AllPositions.Where(x => x.GainLoss>=0).Count();
losers=ActivePositions.Where(x => x.GainLoss<0).Count()+AllPositions.Where(x => x.GainLoss<0).Count();
averageWinner=(ActivePositions.Count>0&&ActivePositions.Where(x => x.GainLoss>=0).Count()>0?ActivePositions.Where(x => x.GainLoss>=0).Select(x => x.GainLoss).Average():0+AllPositions.Count>0&&AllPositions.Where(x => x.GainLoss>=0).Count()>0?AllPositions.Where(x => x.GainLoss>=0).Select(x => x.GainLoss).Average():0)/2.00;
averageLoser=(ActivePositions.Count>0&&ActivePositions.Where(x => x.GainLoss<0).Count()>0?ActivePositions.Where(x => x.GainLoss<0).Select(x => x.GainLoss).Average():0+AllPositions.Count>0&&AllPositions.Where(x => x.GainLoss<0).Count()>0?AllPositions.Where(x => x.GainLoss<0).Select(x => x.GainLoss).Average():0)/2.00;
totalTrades=AllPositions.Count+ActivePositions.Count;
double netProfit;
double totalRWinners=ActivePositions.Count>0?ActivePositions.Where(x => x.GainLoss>=0).Select(x => x.RMultiple).Sum():0+AllPositions.Count>0?AllPositions.Where(x => x.GainLoss>=0).Select(x => x.RMultiple).Sum():0;
double totalRLosers=ActivePositions.Count>0?ActivePositions.Where(x => x.GainLoss<0).Select(x => x.RMultiple).Sum():0+AllPositions.Count>0?AllPositions.Where(x => x.GainLoss<0).Select(x => x.RMultiple).Sum():0;
if(0==ActivePositions.Count&&0==AllPositions.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"There does not appear to be any trade data in this file.");
}
if(null!=Candidates&&0!=Candidates.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"*********************************** C A N D I D A T E S *********************************");
MDTrace.WriteLine(LogLevel.DEBUG,CMTCandidate.Header());
foreach(CMTCandidate candidate in Candidates) MDTrace.WriteLine(LogLevel.DEBUG,candidate.ToString());
}
// ********************************************************************************************************************************************************************************
// ************************************************************************* S T O P L I M I T S A N D P R I C I N G *********************************************************
// ********************************************************************************************************************************************************************************
if(null!=AllPositions&&0!=AllPositions.Count)
{
DateGenerator dateGenerator=new DateGenerator();
foreach(Position position in AllPositions)
{
double stopPrice=position.InitialStopLimit;
List<DateTime> dates=dateGenerator.GenerateHistoricalDates(position.SellDate,position.PurchaseDate);
StringBuilder sb=new StringBuilder();
sb.Append("Symbol,Date,StopLimit,High,Low,Close,Gain/Loss,Gain/Loss (%),PriceTrend,Slope,Purchase");
MDTrace.WriteLine(LogLevel.DEBUG,sb.ToString());
double stopLimitValue=0.00;
double priceTrendIndicatorSlope=double.NaN;
for(int dateIndex=dates.Count-1;dateIndex>=0;dateIndex--)
{
DateTime date=dates[dateIndex];
Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,date);
if(null==price) continue;
if(dateIndex==dates.Count-1) stopLimitValue=position.InitialStopLimit;
double gainLossItem=0.00;
double gainLossPercentItem=0.00;
if(date.Equals(position.SellDate))
{
gainLossItem=(position.CurrentPrice*position.Shares)-(position.PurchasePrice*position.Shares);
gainLossPercentItem=((position.CurrentPrice-position.PurchasePrice)/position.PurchasePrice)*100.00;
}
else
{
gainLossItem=(price.Close*position.Shares)-(position.PurchasePrice*position.Shares);
gainLossPercentItem=((price.Close-position.PurchasePrice)/position.PurchasePrice)*100.00;
}
if(null!=StopLimits&&0!=StopLimits.Count)
{
StopLimit stopLimit=StopLimits.Where(x => x.Symbol.Equals(price.Symbol)&&x.AnalysisDate.Equals(price.Date)).FirstOrDefault();
if(null!=stopLimit)
{
stopLimitValue=stopLimit.NewStop;
priceTrendIndicatorSlope=stopLimit.PriceTrendIndicatorSlope;
}
}
sb=new StringBuilder();
sb.Append(price.Symbol).Append(",");
sb.Append(price.Date.ToShortDateString()).Append(",");
sb.Append(Utility.FormatCurrency(stopLimitValue)).Append(",");
sb.Append(Utility.FormatCurrency(price.High)).Append(",");
sb.Append(Utility.FormatCurrency(price.Low)).Append(",");
sb.Append(Utility.FormatCurrency(price.Close)).Append(",");
sb.Append(Utility.FormatCurrency(gainLossItem)).Append(",");
sb.Append(Utility.FormatNumber(gainLossPercentItem,2,false)).Append(",");
sb.Append(Utility.FormatNumber(priceTrendIndicatorSlope,4,false)).Append(",");
sb.Append(Utility.FormatCurrency(position.PurchasePrice));
MDTrace.WriteLine(LogLevel.DEBUG,sb.ToString());
}
}
}
// ********************************************************************************************************************************************************************************
// ********************************************************************************************************************************************************************************
// ********************************************************************************************************************************************************************************
if(ActivePositions.Count > 0 && AllPositions.Count > 0)
{
double realtimeGainLoss=GetRealtimeGainLoss(PricingDA.GetLatestDate());
gainLoss=AllPositions.Sum(x => x.GainLoss);
if(!double.IsNaN(realtimeGainLoss)) gainLoss+=realtimeGainLoss;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total Gain/Loss {0}",Utility.FormatCurrency(gainLoss)));
}
else if(ActivePositions.Count>0)
{
double realtimeGainLoss=GetRealtimeGainLoss(PricingDA.GetLatestDate());
if(!double.IsNaN(realtimeGainLoss)) gainLoss=realtimeGainLoss;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total Active Gain/Loss {0}",Utility.FormatCurrency(gainLoss)));
}
else if(AllPositions.Count>0)
{
gainLoss=AllPositions.Sum(x => x.GainLoss);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total Gain/Loss {0}",Utility.FormatCurrency(gainLoss)));
}
netProfit=gainLoss/totalTrades;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Winners to losers {0}:{1} Hit Ratio:{2}",(int)winners,(int)losers,Utility.FormatPercent((winners/(winners+losers)))));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Average winner {0} Average Loser:{1}",Utility.FormatCurrency(averageWinner),Utility.FormatCurrency(averageLoser)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total Trades:{0}",Utility.FormatNumber(totalTrades,0,true)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total RWinners:{0}",Utility.FormatNumber(totalRWinners,0,true)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Total RLosers:{0}",Utility.FormatNumber(totalRLosers,0,true)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Average Profit {0}/Trade",Utility.FormatCurrency(netProfit)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Average RProfit {0}R per trade (expectancy)",Utility.FormatNumber((totalRWinners+totalRLosers)/totalTrades,2,false)));
GBPriceCache.GetInstance().Dispose();
}
// ******************************************************************************************************************************************************
// ****************************************************************** E N T R Y T E S T *****************************************************************
// ******************************************************************************************************************************************************
// This is for standalone testing.
public void EntryTest(String symbol,DateTime analysisDate)
{
try
{
CMTParams cmtParams=new CMTParams();
Parameters=cmtParams;
// DMA200 check : Candidates must remain above the 200 day moving average.
Prices prices200=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,MovingAverageGenerator.DayCount200+10);
Price currentPrice=GBPriceCache.GetInstance().GetPrice(symbol,analysisDate);
if(null==prices200||prices200.Count<MovingAverageGenerator.DayCount200)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA200 for candidate {0} due to insufficient pricng data",symbol));
return;
}
if(null==currentPrice)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA200 for candidate {0} due to lack of pricng data for {1}",symbol,Utility.DateTimeToStringMMHDDHYYYY(analysisDate)));
return;
}
prices200=new Prices(prices200.Take(MovingAverageGenerator.DayCount200).ToList());
DMAPrices dma200Prices=MovingAverageGenerator.GenerateMovingAverage(prices200,prices200.Count);
double dma200Close=dma200Prices[0].AVGPrice;
if(currentPrice.Close<dma200Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA200 Violation for candidate {0}. The closing price is less than the 200 day moving average {1} < {2} on {3}",symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(dma200Close),analysisDate.ToShortDateString()));
return;
}
// DMA50 check : The 50 day moving average must remain above the 200 day moving average
Prices prices50=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,MovingAverageGenerator.DayCount50+10);
if(null==prices50 || prices50.Count < MovingAverageGenerator.DayCount50)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA50 for candidate {0} due to insufficient pricng data",symbol));
return;
}
if(null==currentPrice)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA50 for candidate {0} due to lack of pricng data for {1}",symbol,Utility.DateTimeToStringMMHDDHYYYY(analysisDate)));
return;
}
prices50=new Prices(prices50.Take(MovingAverageGenerator.DayCount50).ToList());
DMAPrices dma50Prices=MovingAverageGenerator.GenerateMovingAverage(prices50,prices50.Count);
double dma50Close=dma50Prices[0].AVGPrice;
// CHECK THAT THE CURRENT PRICE IS ABOVE THE 50 DAY MOVING AVERAGE
if(currentPrice.Close <= dma50Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA50 Violation for candidate {0}. The closing price is less than the 50 day moving average {1} < {2} on {3}",symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(dma50Close),analysisDate.ToShortDateString()));
return;
}
// CHECK THAT THE 50 DAY MOVING AVERAGE IS ABOVE THE 200 DAY MOVING AVERAGE
if(dma50Close <= dma200Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA50 Violation for candidate {0}. The 50 DMA must remain about 200 DMA. {1} < {2} on {3}",symbol,Utility.FormatCurrency(dma50Close),Utility.FormatCurrency(dma200Close),analysisDate.ToShortDateString()));
return;
}
// Price trend check. Ensure that prices are trending higher UsePriceTrendSlope UsePriceTrendSlopeDayCount
if(Parameters.UsePriceSlopeIndicator)
{
int dayCount=Parameters.UsePriceSlopeIndicatorDays;
Prices pricesTrend=PricingDA.GetPrices(symbol,dayCount);
double[] pricesLow=Numerics.ToDouble(pricesTrend.GetPricesLow());
LeastSquaresResult leastSquaresResult=LeastSquaresHelper.CalculateLeastSquares(pricesLow);
if(leastSquaresResult.Slope<=0.00)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Price trend violation {0}. The {1} pricing slope is {2}",symbol,dayCount,Utility.FormatNumber(leastSquaresResult.Slope,6)));
return;
}
}
// NarrowRange check
Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.EntryHorizon+5);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Checking for NarrowRange entry for symbol {0} on {1}",symbol,analysisDate.ToShortDateString()));
if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Narrow Range entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
return;
}
if(NarrowRangeIndicator.IsNarrowRangeEntry(analysisDate,prices,Parameters.EntryHorizon)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on NarrowRange",symbol));
// Preparation for SwingEntry check
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Checking for Swing entry for symbol {0} on {1}",symbol,analysisDate.ToShortDateString()));
prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.EntryHorizon<40?40:Parameters.EntryHorizon);
if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Swing entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
return;
}
DMAPrices dmaPrices=MovingAverageGenerator.GenerateMovingAverage(prices,Parameters.EntryHorizon);
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);
// SwingTrade check
SwingTrades swingTrades=SwingTradeHelper.FindSwingTrades(prices,dmaPrices.GetDMAPricesByDate(),bollingerBands.GetBollingerBandElementsByDate());
if(null!=swingTrades&&swingTrades.HasEntryOn(analysisDate)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on Swing on {1}",symbol,analysisDate.ToShortDateString()));
// MACD check
if(!CMTMACDIndicator.IsMACDDowntrend(analysisDate,symbol,Parameters)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on MACD{1}",symbol,Parameters.MACDSetup));
// PriceTrend check
prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.PriceTrendDays+10);
if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Price Trend entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
return;
}
if(PriceTrendIndicator.IsUptrend(prices,Parameters.PriceTrendDays).IsUpTrend) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on PriceTrend on {1}",symbol,analysisDate.ToShortDateString()));
// VolumeTrend check
prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.VolumeTrendDays+10);
if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Volume Trend entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
return;
}
if(VolumeTrendIndicator.IsUptrend(prices,Parameters.VolumeTrendDays)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on VolumeTrend on {1}",symbol,analysisDate.ToShortDateString()));
// ChannelBreakout
prices=GBPriceCache.GetInstance().GetPrices(symbol,analysisDate,Parameters.ChannelBreakoutHorizon);
if(null==prices||!prices[0].Date.Date.Equals(analysisDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate ChannelBreakout entry for candidate {0} due to lack of current price on {1}",symbol,analysisDate.ToShortDateString()));
return;
}
if(ChannelBreakoutIndicator.IsChannelBreakOut(TradeDate,prices,Parameters.ChannelBreakoutHorizon)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on ChannelBreakout on {1}",symbol,analysisDate.ToShortDateString()));
}
finally
{
GBPriceCache.GetInstance().Dispose();
}
}
// ******************************************************************************************************************************************************
// ****************************************************************** C L O S E **********************************************************************
// ******************************************************************************************************************************************************
public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double price,String sessionFile)
{
if(!CMTSessionManager.IsValidSessionFile(sessionFile))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Invalid session file '{0}'.",sessionFile));
return false;
}
PathSessionFileName=sessionFile;
CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(PathSessionFileName);
Parameters=sessionParams.CMTParams;
TradeDate=sessionParams.TradeDate;
AnalysisDate=DateTime.Now;
sessionParams.CMTParams.AnalysisDate=DateTime.Now;
sessionParams.LastUpdated=DateTime.Now;
sessionParams.AnalysisDate=DateTime.Now;
Candidates=sessionParams.Candidates;
StopLimits=sessionParams.StopLimits;
PricingExceptions=sessionParams.PricingExceptions;
ActivePositions=sessionParams.ActivePositions;
AllPositions=sessionParams.AllPositions;
CashBalance=sessionParams.CashBalance;
NonTradeableCash=sessionParams.NonTradeableCash;
if(!BackupSession()) return false;
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=price;
CashBalance+=position.MarketValue;
SaveSession();
return true;
}
position.SellDate=sellDate;
position.CurrentPrice=price;
position.Comment="Manual close.";
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,double initialStop,double trailingStop,String sessionFile)
{
if(!CMTSessionManager.IsValidSessionFile(sessionFile))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Invalid session file '{0}'.",sessionFile));
return false;
}
PathSessionFileName=sessionFile;
CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(PathSessionFileName);
Parameters=sessionParams.CMTParams;
TradeDate=sessionParams.TradeDate;
AnalysisDate=DateTime.Now;
sessionParams.CMTParams.AnalysisDate=DateTime.Now;
sessionParams.LastUpdated=DateTime.Now;
sessionParams.AnalysisDate=DateTime.Now;
Candidates=sessionParams.Candidates;
StopLimits=sessionParams.StopLimits;
PricingExceptions=sessionParams.PricingExceptions;
ActivePositions=sessionParams.ActivePositions;
AllPositions=sessionParams.AllPositions;
CashBalance=sessionParams.CashBalance;
NonTradeableCash=sessionParams.NonTradeableCash;
if(!BackupSession()) return false;
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.TrailingStopLimit.Equals(trailingStop)) position.TrailingStopLimit=trailingStop;
if(!position.InitialStopLimit.Equals(initialStop)) position.InitialStopLimit=initialStop;
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)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting R for Position for symbol '{0}' purchased on {1}. Original R: {2} New R: {3} ",symbol,purchaseDate.ToShortDateString(),Utility.FormatNumber(position.R,2),Utility.FormatNumber(position.PositionRiskPercentDecimal*purchasePrice)));
CashBalance+=(position.PurchasePrice-purchasePrice)*position.Shares;
position.Comment=(null==position.Comment?"":position.Comment+".")+String.Format("Price changed on {0} from {1} to {2}",DateTime.Now.ToShortDateString(),Utility.FormatCurrency(position.PurchasePrice),Utility.FormatCurrency(purchasePrice));
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;
}
// ******************************************************************************************************************************************************
// ****************************************************************** D A I L Y *****************************************************************
// ******************************************************************************************************************************************************
public CMTTrendModelResult RunDaily(DateTime tradeDate,String paramPathSessionFileName,CMTParams cmtParams)
{
CMTTrendModelResult result=new CMTTrendModelResult();
try
{
DateTime now=DateTime.Now;
Parameters=cmtParams;
CashBalance=Parameters.InitialCash;
ActivePositions=new ActivePositions();
AllPositions=new Positions();
Candidates=new CMTCandidates();
PricingExceptions=new CMTPricingExceptions();
StopLimits=new StopLimits();
TradeDate=tradeDate;
PathSessionFileName=paramPathSessionFileName;
if(CMTSessionManager.IsValidSessionFile(paramPathSessionFileName))
{
CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(paramPathSessionFileName);
result=CheckSequentialTradeDate(tradeDate,sessionParams.TradeDate);
if(!result.Success)
{
MDTrace.WriteLine(LogLevel.DEBUG,result.Message);
return result;
}
Parameters=sessionParams.CMTParams;
cmtParams=sessionParams.CMTParams;
sessionParams.TradeDate=TradeDate;
sessionParams.CMTParams.TradeDate=tradeDate;
sessionParams.CMTParams.AnalysisDate=now;
sessionParams.LastUpdated=now;
sessionParams.AnalysisDate=now;
AnalysisDate=sessionParams.AnalysisDate;
Candidates=sessionParams.Candidates;
StopLimits=sessionParams.StopLimits;
StartDate=sessionParams.StartDate;
PricingExceptions=sessionParams.PricingExceptions;
ActivePositions=sessionParams.ActivePositions;
AllPositions=sessionParams.AllPositions;
CashBalance=sessionParams.CashBalance;
NonTradeableCash=sessionParams.NonTradeableCash;
}
else Parameters=cmtParams;
DisplayRealtimeBlotter(TradeDate);
if(0==PricingDA.GetPriceCountOn(TradeDate))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No prices on {0}, skipping date.",TradeDate.ToShortDateString()));
return result;
}
if(0!=ActivePositions.Count&&TradeDate<ActivePositions.Select(x => x.PurchaseDate).Max())
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate ({0}) must be greater than or equal to the max position date ({1}) in the trade file.",TradeDate.ToShortDateString(),ActivePositions.Select(x => x.PurchaseDate).Max().ToShortDateString()));
return result;
}
ManageOpenPositions(TradeDate);
ManageCandidates(TradeDate);
// ************************************************************************************************************************************************************************
// **************************************************************************** N E W P O S I T I O N S *****************************************************************
// ************************************************************************************************************************************************************************
if(null!=PathSessionFileName) SaveSession(); // SAVE THE SESSION FILE POST POSITION AND CANDIDATE MANAGEMENT
if(Parameters.SuspendTrading)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Trading has been suspended for {0}. SuspendTrading flag={1} ",TradeDate.ToShortDateString(),Parameters.SuspendTrading));
result.Success=true;
return result;
}
if(ActivePositions.PositionsOn(TradeDate)>=Parameters.MaxDailyPositions)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Number of trades for {0} is already at maximum {1}",TradeDate.ToShortDateString(),Parameters.MaxDailyPositions));
result.Success=true;
return result;
}
Positions positions=BuyCandidates(TradeDate,CashBalance,ActivePositions.GetSymbols());
if(null != positions && 0!=positions.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"******************** B U Y ********************");
positions.Display();
if(CashBalance-positions.GetExposure()<0.00)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases (1). Available cash: {0}. Requested Exposure: {1}",Utility.FormatCurrency(CashBalance),Utility.FormatCurrency(positions.GetExposure())));
result.Success=false;
return result;
}
ActivePositions.Add(positions);
DisplayPurchases(positions, TradeDate);
CashBalance-=positions.GetExposure();
}
DisplayRealtimeBlotter(TradeDate);
result.Success=true;
result.CashBalance=CashBalance;
if(null!=PathSessionFileName) SaveSession();
return result;
}
catch(Exception exception)
{
result.Success=false;
result.Message=exception.ToString();
return result;
}
finally
{
GBPriceCache.GetInstance().Dispose();
}
}
// ******************************************************************************************************************************************************
// ****************************************************************** B A C K T E S T *****************************************************************
// ******************************************************************************************************************************************************
public CMTTrendModelResult RunBacktestMode(DateTime startDate,DateTime endDate,bool sellAtEndOfSimulation,String paramPathSessionFileName,CMTParams cmtParams)
{
CMTTrendModelResult result=new CMTTrendModelResult();
try
{
Parameters=cmtParams;
CashBalance=Parameters.InitialCash;
ActivePositions=new ActivePositions();
AllPositions=new Positions();
Candidates=new CMTCandidates();
PricingExceptions=new CMTPricingExceptions();
StopLimits=new StopLimits();
DateGenerator dateGenerator=new DateGenerator();
List<DateTime> runDates;
runDates=dateGenerator.GenerateHistoricalDates(startDate,endDate);
PathSessionFileName=paramPathSessionFileName;
if(CMTSessionManager.IsValidSessionFile(paramPathSessionFileName))
{
CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(paramPathSessionFileName);
Parameters=sessionParams.CMTParams;
cmtParams=sessionParams.CMTParams;
sessionParams.TradeDate=startDate;
sessionParams.LastUpdated=DateTime.Now;
sessionParams.AnalysisDate=DateTime.Now;
ActivePositions=sessionParams.ActivePositions;
AllPositions=sessionParams.AllPositions;
CashBalance=sessionParams.CashBalance;
}
else Parameters=cmtParams;
Parameters.DisplayConfiguration();
StartDate=startDate;
AnalysisDate=Today();
int currentYear=-1;
foreach(DateTime tradeDate in runDates)
{
TradeDate=tradeDate;
AnalysisDate=TradeDate;
if(-1==currentYear) currentYear=TradeDate.Year;
if(TradeDate.Year!=currentYear)
{
GBPriceCache.GetInstance().ClearCacheOnOrBefore(TradeDate,true);
currentYear=TradeDate.Year;
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("PROCESSING TRADE DATE {0} WITH AVAILABLE CASH OF {1}",tradeDate.ToShortDateString(),Utility.FormatCurrency(CashBalance)));
DisplayRealtimeBlotter(TradeDate);
if(0==PricingDA.GetPriceCountOn(TradeDate))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No prices on {0}, skipping date.",TradeDate.ToShortDateString()));
continue;
}
if(0!=ActivePositions.Count&&TradeDate<ActivePositions.Select(x => x.PurchaseDate).Max())
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate ({0}) must be greater than or equal to the max position date ({1}) in the trade file.",TradeDate.ToShortDateString(),ActivePositions.Select(x => x.PurchaseDate).Max().ToShortDateString()));
return result;
}
ManageOpenPositions(TradeDate);
ManageCandidates(TradeDate);
if(ActivePositions.PositionsOn(TradeDate)>=Parameters.MaxDailyPositions)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Number of trades for {0} is already at maximum {1}",TradeDate.ToShortDateString(),Parameters.MaxDailyPositions));
result.Success=true;
continue;
}
Positions positions=BuyCandidates(TradeDate,CashBalance,ActivePositions.GetSymbols());
if(null!=positions&&0!=positions.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"******************** B U Y ********************");
positions.Display();
if(CashBalance-positions.GetExposure()<0.00)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases (1). Available cash: {0}. Requested Exposure: {1}",Utility.FormatCurrency(CashBalance),Utility.FormatCurrency(positions.GetExposure())));
result.Success=false;
return result;
}
ActivePositions.Add(positions);
DisplayPurchases(positions, TradeDate);
CashBalance-=positions.GetExposure();
}
MDTrace.WriteLine(LogLevel.DEBUG,"\n");
}
// ***********************************************************************************************************************************************************************
// ********************************************************* S E L L A L L A T E N D O F S I M U L A T I O N ***************************************************
if(sellAtEndOfSimulation)
{
SellPositions(ActivePositions,TradeDate,"Closed due to end of simulation.");
CashBalance+=ActivePositions.GetMarketValue();
AllPositions.Add(ActivePositions);
ActivePositions.Clear();
}
// ***********************************************************************************************************************************************************************
MDTrace.WriteLine(LogLevel.DEBUG,"******************************************** C L O S E D P O S I T I O N S ***********************************************");
AllPositions.Display();
DisplayBalanceFromAllPositions(); // Aggregate the balance from the closed positions
result.Success=true;
result.CashBalance=CashBalance;
GBPriceCache.GetInstance().Dispose();
if(null!=PathSessionFileName) SaveSession();
return result;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
result.Success=false;
result.Message=exception.ToString();
return result;
}
finally
{
GBPriceCache.GetInstance().Dispose();
}
}
public void RunTrendTemplate(DateTime? analysisDate=null)
{
try
{
List<String> symbols=PricingDA.GetSymbols();
CMTParams cmtParams=new CMTParams();
List<CMTCandidate> violations=new List<CMTCandidate>();
List<CMTCandidate> candidates=new List<CMTCandidate>();
// Filter out symbols where we do not have a price on trade date
Profiler profiler = new Profiler();
Dictionary<String,DateTime> latestDates = PricingDA.GetLatestDates(symbols);
symbols=symbols.Where(x => latestDates.ContainsKey(x) && latestDates[x].Date>=analysisDate.Value.Date).ToList();
MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded pricing dates in {Utility.FormatNumber(profiler.End(),2)} (ms)");
// Prefetch a subset of fundamentals where each fundamental.asof is no greater than tradeDate
profiler.Reset();
FundamentalsV2 fundamentals = FundamentalDA.GetFundamentalsMaxDateV2(analysisDate.Value);
MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded fundamentals in {Utility.FormatNumber(profiler.End(),2)} (ms)");
// Prefetch the company profiles
profiler.Reset();
Dictionary<String,CompanyProfile> companyProfiles = CompanyProfileDA.GetCompanyProfiles(symbols);
MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded company profiles in {Utility.FormatNumber(profiler.End(),2)} (ms)");
// Prefetch the pricing dates required for 200 day moving average.
profiler.Reset();
DateGenerator dateGenerator = new DateGenerator();
DateTime historicalDate=dateGenerator.GenerateHistoricalDate(analysisDate.Value,cmtParams.DMA200Horizon+10);
List<DateTime> historicalDates=PricingDA.GetPricingDatesBetween(historicalDate,analysisDate.Value);
MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded moving average dates in {Utility.FormatNumber(profiler.End(),2)} (ms)");
// Prefetch the EPS time series
profiler.Reset();
Dictionary<String,TimeSeriesCollection> epsTimeSeriesCollectionDictionary = FundamentalDA.GetEPS(symbols,analysisDate.Value,3);
MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded EPS Time Series in {Utility.FormatNumber(profiler.End(),2)} (ms)");
// Prefetch the profit margin time series
profiler.Reset();
Dictionary<String,TimeSeriesCollection> profitMarginTimeSeriesCollectionDictionary = IncomeStatementDA.GetProfitMarginMaxAsOf(symbols,analysisDate.Value,3,IncomeStatement.PeriodType.Quarterly);
MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded Profit Margin Time Series in {Utility.FormatNumber(profiler.End(),2)} (ms)");
for(int index=0;index<symbols.Count;index++)
{
String symbol=symbols[index];
if(0==(index%500)) Console.WriteLine("GenerateCMTCandidates processing item {0} of {1}",index+1,symbols.Count);
FundamentalV2 fundamental = default;
if(fundamentals.ContainsKey(symbol))fundamental=fundamentals[symbol];
CompanyProfile companyProfile = default;
if(companyProfiles.ContainsKey(symbol))companyProfile = companyProfiles[symbol];
TimeSeriesCollection epsTimeSeriesCollection = default;
if(epsTimeSeriesCollectionDictionary.ContainsKey(symbol))epsTimeSeriesCollection=epsTimeSeriesCollectionDictionary[symbol];
TimeSeriesCollection profitMarginTimeSeriesCollection = default;
if(profitMarginTimeSeriesCollectionDictionary.ContainsKey(symbol))profitMarginTimeSeriesCollection=profitMarginTimeSeriesCollectionDictionary[symbol];
CMTCandidate cmtCandidate=CMTCandidateGenerator.GenerateCandidate(symbol,analysisDate.Value,cmtParams,fundamental,companyProfile,historicalDates,epsTimeSeriesCollection,profitMarginTimeSeriesCollection);
if(cmtCandidate.Violation) violations.Add(cmtCandidate);
else candidates.Add(cmtCandidate);
}
List<String> violationTypes=violations.Select(x => x.Reason).Distinct().ToList();
violationTypes.Sort();
foreach(String violationType in violationTypes)
{
List<CMTCandidate> candidatesInViolation=violations.Where(x => x.Reason.Equals(violationType)).ToList();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Violation:{0} Count:{1} Percent:{2}",violationType,candidatesInViolation.Count,Utility.FormatPercent((double)candidatesInViolation.Count/(double)violations.Count)));
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Found {0} qualifying candidates",candidates.Count));
foreach(CMTCandidate candidate in candidates)
{
Console.WriteLine("_____________________________________________");
String companyName=PricingDA.GetNameForSymbol(candidate.Symbol);
Console.WriteLine(String.Format("Symbol:{0} Company Name:{1}",candidate.Symbol,companyName));
}
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
finally
{
GBPriceCache.GetInstance().Dispose();
}
}
// ***********************************************************************************************************************************************************************
// *********************************************************************** M A N A G E O P E N P O S I T I O N S *****************************************************
// ***********************************************************************************************************************************************************************
private void ManageOpenPositions(DateTime tradeDate)
{
if(0==ActivePositions.Count) return;
Positions closedPositions = new Positions();
// List<Position> closedPositions=new List<Position>();
foreach(Position position in ActivePositions)
{
Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,tradeDate);
if(null==price)
{
int exceptionCount=AddPricingException(position.Symbol);
if(exceptionCount>MaxPricingExceptions)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[ManageOpenPositions] Selling {0} on {1} because price exceptions exceeds maximum of {2}",position.Symbol,tradeDate.ToShortDateString(),MaxPricingExceptions));
price=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(position.Symbol,tradeDate);
position.SellDate=tradeDate;
position.CurrentPrice=price.Close;
position.Comment="Close due to pricing exceptions.";
CashBalance+=position.MarketValue;
AllPositions.Add(position);
closedPositions.Add(position);
}
else MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[ManageOpenPositions] Cannot determine price for {0} on {1}",position.Symbol,tradeDate.ToShortDateString()));
continue;
}
position.CurrentPrice=price.Close; // Intraday the Close will be the current price
RemovePricingException(position.Symbol);
if(Parameters.SidewaysDetection&&Utility.IsEpoch(position.LastStopAdjustment)&&price.Close>position.PurchasePrice)
{
DateGenerator dateGenerator=new DateGenerator();
int daysHeld=Math.Abs(dateGenerator.DaysBetweenActual(position.PurchaseDate,tradeDate));
if(daysHeld>Parameters.SidewaysAfterDays)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[ManageOpenPositions] Selling {0} on {1} due to sideways motion for {2} days",position.Symbol,tradeDate.ToShortDateString(),daysHeld));
position.SellDate=tradeDate;
position.CurrentPrice=position.TrailingStopLimit;
position.Comment="Closed due to sideways motion.";
CashBalance+=position.MarketValue;
AllPositions.Add(position);
closedPositions.Add(position);
continue;
}
}
if(price.Low<position.TrailingStopLimit&&!position.PurchaseDate.Equals(tradeDate)) // This will miss StopLimit violations that occur on the date of purchase. Needs to be watched manually because we don't have tick data
{
position.SellDate=tradeDate;
position.CurrentPrice=position.TrailingStopLimit;
position.Comment="Closed due to trailing stop.";
CashBalance+=position.MarketValue;
AllPositions.Add(position);
closedPositions.Add(position);
}
else if(HasDMABreak(position,tradeDate,price))
{
position.SellDate=tradeDate;
position.CurrentPrice=price.Close;
position.Comment="Closed due to DMA break";
CashBalance+=position.MarketValue;
AllPositions.Add(position);
closedPositions.Add(position);
}
else if(price.Low>position.PurchasePrice) // If the Low price is above our purchase price then re-evaluate the stop
{
EvaluateStopPrice(tradeDate,price,position);
}
}
if(0!=closedPositions.Count)
{
DisplaySales(closedPositions, TradeDate);
MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L *********************");
foreach(Position closedPosition in closedPositions)
{
closedPosition.Display();
ActivePositions.Remove(closedPosition);
}
}
}
// **********************************************************************************************************************************************************
// ***************************************************** M O V I N G A V E R A G E B R E A K C H E C K *************************************************
// **********************************************************************************************************************************************************
private bool HasDMABreak(Position position,DateTime tradeDate,Price currentPrice)
{
if(!Parameters.SellOnDMABreak) return false;
List<String> dmaBreakValues=Parameters.DMABreakValuesCollection;
foreach(String strDMABreakValue in dmaBreakValues)
{
int dmaBreakValue=int.Parse(strDMABreakValue);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Evaluating DMA({0}) for {1} on {2}",dmaBreakValue,position.Symbol,tradeDate.ToShortDateString()));
Prices prices=GBPriceCache.GetInstance().GetPrices(position.Symbol,tradeDate,dmaBreakValue+5);
if(null==prices||prices.Count<dmaBreakValue)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA({0}) for {1} on {2}",dmaBreakValue,position.Symbol,tradeDate.ToShortDateString()));
return true;
}
prices=new Prices(prices.Take(dmaBreakValue).ToList());
DMAPrices dmaPrices=MovingAverageGenerator.GenerateMovingAverage(prices,prices.Count);
if(currentPrice.Close<dmaPrices[0].AVGPrice)
{
bool isLoss=position.GetGainLoss(currentPrice)<0.00;
if(isLoss)
{
if(Parameters.DMABreakForceBreak)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Moving average violation detected for {0} on {1}. Price:{2} DMA({3}):{4} : Sell triggered.",position.Symbol,tradeDate.ToShortDateString(),Utility.FormatCurrency(currentPrice.Close),dmaBreakValue,Utility.FormatCurrency(dmaPrices[0].AVGPrice)));
return true;
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Moving average violation detected for {0} on {1}. Price:{2} DMA({3}):{4} : DMABreakForceBreak=False, holding security.",position.Symbol,tradeDate.ToShortDateString(),Utility.FormatCurrency(currentPrice.Close),dmaBreakValue,Utility.FormatCurrency(dmaPrices[0].AVGPrice)));
return false;
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Moving average violation detected for {0} on {1}. Price:{2} DMA({3}):{4} : Sell triggered.",position.Symbol,tradeDate.ToShortDateString(),Utility.FormatCurrency(currentPrice.Close),dmaBreakValue,Utility.FormatCurrency(dmaPrices[0].AVGPrice)));
return true;
}
}
}
return false;
}
// **********************************************************************************************************************************************************
// ***************************************************** S T O P L I M I T C O L L E C T I O N M A I N T E N A N C E ***********************************
// **********************************************************************************************************************************************************
private void AddStopLimit(StopLimit stopLimit)
{
StopLimits.Add(stopLimit);
}
// **********************************************************************************************************************************************************
// *********************************************** P R I C I N G E X C E P T I O N C O L L E C T I O N M A I N T E N A N C E ***************************
// **********************************************************************************************************************************************************
private int AddPricingException(String symbol)
{
CMTPricingException pricingException=PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault();
if(null==pricingException) { pricingException=new CMTPricingException(symbol,1); PricingExceptions.Add(pricingException); PricingExceptions.Add(pricingException); }
else pricingException.ExceptionCount++;
return pricingException.ExceptionCount;
}
private void RemovePricingException(String symbol)
{
CMTPricingException pricingException=PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault();
if(null==pricingException) return;
PricingExceptions.Remove(pricingException);
}
private bool HasPricingException(String symbol)
{
CMTPricingException pricingException=PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault();
if(null==pricingException) return false;
return true;
}
// ***************************************************************************************************************************************************
// **************************************************************** S E L L P O S I T I O N S *****************************************************
// ***************************************************************************************************************************************************
private void SellPositions(Positions positions,DateTime sellDate,String comment)
{
DateGenerator dateGenerator=new DateGenerator();
foreach(Position position in positions)
{
SellPosition(position,sellDate,comment);
}
}
private void SellPosition(Position position,DateTime sellDate,String comment)
{
position.SellDate=sellDate;
position.Comment=comment;
Price price=GetPrice(position.Symbol,sellDate);
if(null==price)
{
DateTime latestPricingDate=PricingDA.GetLatestDate(position.Symbol);
price=PricingDA.GetPrice(position.Symbol,latestPricingDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[SellPosition] **********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate)));
position.CurrentPrice=position.PurchasePrice;
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Database does not contain a price for {0} on {1}. Latest pricing date for this security is {2}",position.Symbol,sellDate.ToShortDateString(),latestPricingDate.ToShortDateString()));
position.CurrentPrice=price.Close;
}
}
else position.CurrentPrice=price.Close;
}
// ***************************************************************************************************************************************************
// **************************************************************** B U Y C A N D I D A T E S *****************************************************
// ***************************************************************************************************************************************************
private Positions BuyCandidates(DateTime tradeDate,double cash,List<String> symbolsHeld)
{
try
{
DateGenerator dateGenerator=new DateGenerator();
Positions positions=new Positions();
int positionCount=0;
if(ActivePositions.Count==MaxOpenPositions)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The maximum number of active positions has been reached. MaxOpenPositions={0}",MaxOpenPositions));
return positions;
}
if(null==Candidates||0==Candidates.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"There are no candidates in the candidate pool to search for entry points.");
return positions;
}
if(!IsTradeableMarket(tradeDate,Parameters))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The market is not tradeable on {0} due to one or more violations.",tradeDate));
return positions;
}
Candidates.Sort();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format(""));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SEARCHING FOR ENTRY POINTS (TradeDate:{0} Available Cash:{1})",tradeDate.ToShortDateString(),Utility.FormatCurrency(cash)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",CMTCandidate.Header()));
for(int index=0;index<Candidates.Count;index++)
{
bool skipChecks=false;
CMTCandidate mmCandidate=Candidates[index];
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",mmCandidate.ToString()));
// Check NoTradeSymbol
if(Parameters.NoTradeSymbolsList.Any(x => x.Equals(mmCandidate.Symbol)))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Candidate {0} is in the NoTrade list",mmCandidate.Symbol));
RemoveCandidate(mmCandidate);
continue;
}
// Check if already held
if(ActivePositions.Any(x => x.Symbol.Equals(mmCandidate.Symbol)))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Candidate {0} is already held in active positions",mmCandidate.Symbol));
RemoveCandidate(mmCandidate);
continue;
}
// Check to make sure the candidate is still above the 200DMA. Candidates must remain above the 200 day moving average. (MMarc Minervini & Paul Tudor Jones)
Prices prices200=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,MovingAverageGenerator.DayCount200+10);
Price currentPrice=GBPriceCache.GetInstance().GetPrice(mmCandidate.Symbol,tradeDate);
if(null==prices200||prices200.Count<MovingAverageGenerator.DayCount200)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA200 for candidate {0} due to insufficient pricing data",mmCandidate.Symbol));
continue;
}
if(null==currentPrice)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA200 for candidate {0} due to lack of pricng data for {1}",mmCandidate.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
continue;
}
prices200=new Prices(prices200.Take(MovingAverageGenerator.DayCount200).ToList());
DMAPrices dma200Prices=MovingAverageGenerator.GenerateMovingAverage(prices200,prices200.Count);
double dma200Close=dma200Prices[0].AVGPrice;
if(currentPrice.Close<dma200Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA200 Violation for candidate {0}. The closing price is less than the 200 day moving average {1} < {2} on {3}",mmCandidate.Symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(dma200Close),tradeDate.ToShortDateString()));
continue;
}
// DMA50 check : The 50 day moving average must remain above the 200 day moving average
Prices prices50=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,MovingAverageGenerator.DayCount50+10);
if(null==prices50||prices50.Count<MovingAverageGenerator.DayCount50)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA50 for candidate {0} due to insufficient pricng data",mmCandidate.Symbol));
continue;
}
if(null==currentPrice)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate DMA50 for candidate {0} due to lack of pricng data for {1}",mmCandidate.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
continue;
}
prices50=new Prices(prices50.Take(MovingAverageGenerator.DayCount50).ToList());
DMAPrices dma50Prices=MovingAverageGenerator.GenerateMovingAverage(prices50,prices50.Count);
double dma50Close=dma50Prices[0].AVGPrice;
// CHECK THAT THE CURRENT PRICE IS ABOVE THE 50 DAY MOVING AVERAGE
if(currentPrice.Close <= dma50Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA50 Violation for candidate {0}. The closing price is less than the 50 day moving average {1} < {2} on {3}",mmCandidate.Symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(dma50Close),tradeDate.ToShortDateString()));
continue;
}
// CHECK THAT THE 50 DAY MOVING AVERAGE IS ABOVE THE 200 DAY MOVING AVERAGE
if(dma50Close <= dma200Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA50 Violation for candidate {0}. The 50 DMA must remain about 200 DMA. {1} < {2} on {3}",mmCandidate.Symbol,Utility.FormatCurrency(dma50Close),Utility.FormatCurrency(dma200Close),tradeDate.ToShortDateString()));
continue;
}
// Trend check ensure that prices are trending higher
if(Parameters.UsePriceSlopeIndicator)
{
int dayCount=Parameters.UsePriceSlopeIndicatorDays;
Prices pricesTrend=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,dayCount);
double[] pricesLow=Numerics.ToDouble(pricesTrend.GetPricesLow());
LeastSquaresResult leastSquaresResult=LeastSquaresHelper.CalculateLeastSquares(pricesLow);
if(leastSquaresResult.Slope<=0.00)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Price trend violation {0}. The {1} pricing slope is {2}",mmCandidate.Symbol,dayCount,Utility.FormatNumber(leastSquaresResult.Slope,6)));
continue;
}
}
// check for entries
if(Parameters.EntryTypesCollection.Any(x => x.Equals("OverExtended",StringComparison.InvariantCultureIgnoreCase)))
{
bool? result=OverExtendedIndicator.IsOverextended(mmCandidate.Symbol,tradeDate,Parameters.UseOverExtendedIndicatorDays,Parameters.UseOverExtendedIndicatorViolationThreshhold,Parameters.UseOverExtendedIndicatorMarginPercent);
if(null==result || result.Value)continue;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on OverExtended",mmCandidate.Symbol));
}
if(Parameters.EntryTypesCollection.Any(x => x.Equals("MVP",StringComparison.InvariantCultureIgnoreCase)))
{
if(MVPIndicator.IsTrue(mmCandidate.Symbol,tradeDate))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on MVP",mmCandidate.Symbol));
skipChecks=true; // skip the remaining checks. Immediate buy
}
}
if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("NarrowRange",StringComparison.InvariantCultureIgnoreCase)))
{
Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.EntryHorizon+5);
if(null==prices||0==prices.Count||!prices[0].Date.Date.Equals(tradeDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Narrow Range entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
continue;
}
if(!NarrowRangeIndicator.IsNarrowRangeEntry(tradeDate,prices,Parameters.EntryHorizon)) continue;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on NarrowRange",mmCandidate.Symbol));
}
if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("Swing",StringComparison.InvariantCultureIgnoreCase)))
{
Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.EntryHorizon<40?40:Parameters.EntryHorizon);
if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Swing entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
continue;
}
DMAPrices dmaPrices=MovingAverageGenerator.GenerateMovingAverage(prices,Parameters.EntryHorizon);
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);
SwingTrades swingTrades=SwingTradeHelper.FindSwingTrades(prices,dmaPrices.GetDMAPricesByDate(),bollingerBands.GetBollingerBandElementsByDate());
if(null==swingTrades||!swingTrades.HasEntryOn(tradeDate)) continue;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on Swing",mmCandidate.Symbol));
}
// MACD
if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("MACD",StringComparison.InvariantCultureIgnoreCase)))
{
if(CMTMACDIndicator.IsMACDDowntrend(tradeDate,mmCandidate.Symbol,Parameters)) continue;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on MACD{1}",mmCandidate.Symbol,Parameters.MACDSetup));
}
// PRICETREND
if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("PriceTrend",StringComparison.InvariantCultureIgnoreCase)))
{
Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.PriceTrendDays+10);
if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Price Trend entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
continue;
}
if(!PriceTrendIndicator.IsUptrend(prices,Parameters.PriceTrendDays).IsUpTrend) continue;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on PriceTrend",mmCandidate.Symbol));
}
// VOLUMETREND
if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("VolumeTrend",StringComparison.InvariantCultureIgnoreCase)))
{
Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.VolumeTrendDays+10);
if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate Volume Trend entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
continue;
}
if(!VolumeTrendIndicator.IsUptrend(prices,Parameters.VolumeTrendDays)) continue;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on VolumeTrend",mmCandidate.Symbol));
}
// CHANNELBREAKOUT
if(!skipChecks && Parameters.EntryTypesCollection.Any(x => x.Equals("ChannelBreakout",StringComparison.InvariantCultureIgnoreCase)))
{
Prices prices=GBPriceCache.GetInstance().GetPrices(mmCandidate.Symbol,tradeDate,Parameters.ChannelBreakoutHorizon);
if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot evaluate ChannelBreakout entry for candidate {0} due to lack of current price on {1}",mmCandidate.Symbol,tradeDate.ToShortDateString()));
continue;
}
if(!ChannelBreakoutIndicator.IsChannelBreakOut(tradeDate,prices,Parameters.ChannelBreakoutHorizon)) continue;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} has entry on ChannelBreakout",mmCandidate.Symbol));
}
// end entry checks
Position prevPosition=AllPositions.Where(x => x.Symbol.Equals(mmCandidate.Symbol)).OrderByDescending(x => x.SellDate).FirstOrDefault();
if(null!=prevPosition)
{
int daysBetween=dateGenerator.DaysBetween(tradeDate,prevPosition.SellDate);
if(daysBetween<30)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Candidate was sold less than 30 days ago. Symbol: {0} Date Sold:{1}",mmCandidate.Symbol,prevPosition.SellDate));
continue;
}
}
Price price=GetPrice(mmCandidate.Symbol,tradeDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[BuyCandidates] Cannot locate a price for {0} on {1}, skipping purchase.",mmCandidate.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
continue;
}
Position position=new Position();
position.Symbol=mmCandidate.Symbol;
position.PurchaseDate=tradeDate;
position.PurchasePrice=price.Close;
position.CurrentPrice=double.NaN;
position.Volume=mmCandidate.Volume;
position.Volatility=mmCandidate.Volatility;
if(ActivePositions.Count+positions.Count>=MaxOpenPositions) break;
RemoveCandidate(mmCandidate);
positions.Add(position);
positionCount++;
if(positionCount>=MaxDailyPositions) break;
}
positions=PerformPositionSizing(positions,cash,tradeDate);
if(null==positions||0==positions.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**** NO ENTRY POINTS FOUND ON TRADE DATE:{0} ****",tradeDate.ToShortDateString()));
return positions;
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BOUGHT {0} POSITIONS, TRADE DATE:{1}, EXPOSURE:{2}",positions.Count,tradeDate.ToShortDateString(),Utility.FormatCurrency(positions.Sum(x => x.Exposure))));
return positions;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BuyPositions: Exception:{0}",exception.ToString()));
return null;
}
}
// ***************************************************************************************************************************************************
// ***************************************************************** P O S I T I O N S I Z I N G **************************************************
// ***************************************************************************************************************************************************
private Positions PerformPositionSizing(Positions positions,double availableCash,DateTime tradeDate)
{
return PerformPositionSizingTotalRisk(positions, availableCash, tradeDate);
}
private Positions PerformPositionSizingTotalRisk(Positions positions,double availableCash,DateTime tradeDate)
{
Positions acceptedPositions=new Positions();
if(null==positions||0==positions.Count) return acceptedPositions;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("PERFORM POSITION SIZING TOTAL RISK : Positions:{0} Cash:{1}",positions.Count,Utility.FormatCurrency(availableCash)));
foreach(Position position in positions)
{
position.PositionRiskPercentDecimal=Parameters.PositionRiskPercentDecimal;
position.R=position.PositionRiskPercentDecimal*position.PurchasePrice;
position.C=Parameters.TotalRiskPercentDecimal*availableCash;
position.Shares=Math.Floor(position.P); // P=SHARES; P=C/R
position.InitialStopLimit=position.PurchasePrice-position.R;
position.TrailingStopLimit=position.InitialStopLimit;
if(position.Shares<=0)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Number of shares is zero for {0}",position.Symbol));
continue;
}
if(position.Exposure>availableCash)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Insufficient funds to purchase {0} shares of {1} at {2} per share with total exposure {3}. Available cash is {4}",
position.Shares,position.Symbol,Utility.FormatCurrency(position.PurchasePrice),Utility.FormatCurrency(position.Exposure),Utility.FormatCurrency(availableCash)));
continue;
}
availableCash-=position.Exposure;
acceptedPositions.Add(position);
}
return acceptedPositions;
}
// ***************************************************************************************************************************************************
// ************************************************************** S T O P L I M I T S *************************************************************
// ***************************************************************************************************************************************************
private void EvaluateStopPrice(DateTime tradeDate,Price currentPrice,Position position)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("EvaluateStopPrice: {0} on {1}",position.Symbol,tradeDate.ToShortDateString()));
DateGenerator dateGenerator=new DateGenerator();
Prices prices = GBPriceCache.GetInstance().GetPrices(position.Symbol, tradeDate, Parameters.PriceTrendDays); // only adjust stops if we are trending up
PriceTrendIndicatorResult priceTrendIndicatorResult = PriceTrendIndicator.IsUptrend(prices, Parameters.PriceTrendDays);
if(Parameters.EvaluateStopOnUpTrend && !priceTrendIndicatorResult.IsUpTrend)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0} does not have upward price trend (higher highs and higher lows for {1} consecutive days), will not adjust stop price", position.Symbol, Parameters.PriceTrendDays));
return;
}
double trailingStop=position.InitialStopLimit+Math.Floor((currentPrice.Low-position.PurchasePrice)/position.R)*position.R; // where R = Risk Per Share in $
double trailingStopScaled=trailingStop;
double daysHeld=Math.Abs(dateGenerator.DaysBetweenActual(position.PurchaseDate,tradeDate));
if(Utility.IsEpoch(position.LastStopAdjustment)) // we've never adjusted the stop price
{
if(daysHeld>=Parameters.MinDaysBetweenInitialStopAdjustment)
{
if(Parameters.UseStopLimitScaling)
{
if(Parameters.StopLimitScalingType.Equals("AverageTrueRange")) trailingStopScaled=GetStopLimitWithScalingAverageTrueRange(tradeDate,currentPrice,position,trailingStop);
else
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Invalid StopLimitScalingType({0}). No scaling will be applied",Parameters.StopLimitScalingType));
trailingStopScaled=trailingStop;
}
}
trailingStop=Math.Max(trailingStop,trailingStopScaled);
if(trailingStop>=currentPrice.Low||trailingStop>=currentPrice.Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be higher than the Low/Close of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(currentPrice.Low)));
return;
}
if(Numerics.Round(trailingStop) <= (Numerics.Round(position.TrailingStopLimit)))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be less than or equal to the existing stop limit of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(position.TrailingStopLimit)));
return;
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************** A D J U S T S T O P L I M I T ************************"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting StopLimit after {0} days for {1} from {2} to {3}. Purchase Price: {4} Current Price:{5} Spread:{6} Shares:{7}",daysHeld,position.Symbol,Utility.FormatCurrency(position.TrailingStopLimit),Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(position.PurchasePrice),Utility.FormatCurrency(currentPrice.Close),Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop),Utility.FormatNumber(position.Shares,2)));
StopLimit newStopLimit=new StopLimit
{
Symbol=position.Symbol,
AnalysisDate=tradeDate,
PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit,
NewStop=trailingStop,
CurrentPriceLow=currentPrice.Low,
CurrentPriceClose=currentPrice.Close,
PriceTrendIndicatorSlope=priceTrendIndicatorResult.LowPriceSlope
};
AddStopLimit(newStopLimit);
position.TrailingStopLimit=trailingStop;
position.LastStopAdjustment=tradeDate;
}
else MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The initial stop for {0} will not be modified until the grace period is reached. Days Held:{1} MinDaysBetweenInitialStopAdjustment:{2} ",position.Symbol,daysHeld,Parameters.MinDaysBetweenInitialStopAdjustment));
}
else // we have already made prior stop adjustments
{
int daysSinceLastStopAdjustment=Math.Abs(dateGenerator.DaysBetweenActual(position.LastStopAdjustment,tradeDate));
if(daysSinceLastStopAdjustment>=Parameters.MinDaysBetweenStopAdjustments)
{
if(Parameters.UseStopLimitScaling)
{
if(Parameters.StopLimitScalingType.Equals("AverageTrueRange")) trailingStopScaled=GetStopLimitWithScalingAverageTrueRange(tradeDate,currentPrice,position,trailingStop);
else
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Invalid StopLimitScalingType({0}). No scaling will be applied",Parameters.StopLimitScalingType));
trailingStopScaled=trailingStop;
}
}
trailingStop=Math.Max(trailingStop,trailingStopScaled);
if(trailingStop>=currentPrice.Low||trailingStop>=currentPrice.Close)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be higher than the Low/Close of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(currentPrice.Low)));
return;
}
if(Numerics.Round(position.TrailingStopLimit) < Numerics.Round(trailingStop)) // round the stop limits to fractionals don't look like differences
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************** A D J U S T S T O P L I M I T ************************"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting StopLimit after {0} days for {1} from {2} to {3}. Purchase Price: {4} Current Price:{5} Spread:{6} Shares:{7}",daysHeld,position.Symbol,Utility.FormatCurrency(position.TrailingStopLimit),Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(position.PurchasePrice),Utility.FormatCurrency(currentPrice.Close),Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop),Utility.FormatNumber(position.Shares,2)));
StopLimit newStopLimit=new StopLimit
{
Symbol=position.Symbol,
AnalysisDate=tradeDate,
PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit,
NewStop=trailingStop,
CurrentPriceLow=currentPrice.Low,
CurrentPriceClose=currentPrice.Close,
PriceTrendIndicatorSlope=priceTrendIndicatorResult.LowPriceSlope
};
AddStopLimit(newStopLimit);
position.TrailingStopLimit=trailingStop;
position.LastStopAdjustment=tradeDate;
}
else
{
double currentTrailingStopLimit=Numerics.Round(position.TrailingStopLimit);
double suggestedTrailingStopLimit=Numerics.Round(trailingStop);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Not Adjusting Stop Limit for {0} because the new trailing stop limit would be less than or equal to the current trailing stop limit. Current Trailing Stop Limit:{1}, Calculated Trailing Stop Limit:{2}.",position.Symbol,Utility.FormatCurrency(currentTrailingStopLimit),Utility.FormatCurrency(suggestedTrailingStopLimit)));
}
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The trailing stop for {0} was adjusted {1} days ago. MinDaysBetweenStopAdjustments is {2}",position.Symbol,daysSinceLastStopAdjustment,Parameters.MinDaysBetweenStopAdjustments));
}
}
}
private double GetStopLimitWithScalingAverageTrueRange(DateTime tradeDate,Price currentPrice,Position position,double stopLimitNonScaled)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetStopLimitWithScalingAverageTrueRange: Symbol:{0} RMultiple={1}",position.Symbol,position.RMultiple));
double volatility=double.NaN;
if(Parameters.UseProfitMaximization)
{
CodeRunner codeRunner=new CodeRunner();
SymbolTable symbolTable=codeRunner.SymbolTable;
symbolTable.AddObjects(new Object[]{position}.ToList());
codeRunner.Execute(Parameters.UseProfitMaximizationExpression);
double multiplier=codeRunner.GetValue<double>("MULTIPLIER");
volatility=VolatilityGenerator.CalculateVolatility(position.Symbol,tradeDate,Parameters.StopLimitScalingVolatilityDays,multiplier);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using profit maximization strategy for {0} which has RMultiple={1}. Multiplier:{2}",position.Symbol,position.RMultiple,Utility.FormatNumber(multiplier,3)));
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using standard ATR multiplier for {0} which has RMultiple={1}.",position.Symbol,position.RMultiple));
volatility=VolatilityGenerator.CalculateVolatility(position.Symbol,tradeDate,Parameters.StopLimitScalingVolatilityDays);
}
if(double.IsNaN(volatility))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Unable to calculate StopLimit for AverageTrueRange for {0} on {1}. Using non-scaled stop limit.",position.Symbol,tradeDate.ToShortDateString()));
return stopLimitNonScaled;
}
double stopLimit=currentPrice.Low-volatility; // We base the stop off of the low in order to give a bit more breathing room in the stop in the event that we have a wide spread between the close and the low. Backtested currentPrice.Close vs currentPrice.Low and basing off the low yields better results.
return stopLimit;
}
// ***************************************************************************************************************************************************
// ************************************************************* M A N A G E S E T U P S ***********************************************************
// ***************************************************************************************************************************************************
private void ManageCandidates(DateTime tradeDate)
{
List<String> symbolsHeld=new List<String>();
ExpireCandidates(tradeDate);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate:{0}. There are {1} candidates in the candidate pool.",tradeDate.ToShortDateString(),Candidates.Count));
CMTGeneratorResult cmtGeneratorResult=CMTTrendGenerator.GenerateCMTCandidates(tradeDate,Parameters,symbolsHeld);
if(!cmtGeneratorResult.Success)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GenerateCMTCandidates failed with message {0}",cmtGeneratorResult.LastMessage));
return;
}
for(int index=0;index<cmtGeneratorResult.CMTCandidates.Count;index++)
{
CMTCandidate mmCandidate=cmtGeneratorResult.CMTCandidates[index];
AddCandidate(mmCandidate);
}
}
private void ExpireCandidates(DateTime tradeDate)
{
List<CMTCandidate> candidatesToRemove=new List<CMTCandidate>();
DateGenerator dateGenerator=new DateGenerator();
if(null==Candidates||0==Candidates.Count) return;
foreach(CMTCandidate candidate in Candidates)
{
if(Math.Abs(dateGenerator.DaysBetweenActual(tradeDate,candidate.AnalysisDate))>Parameters.CandidateExpiryDays) candidatesToRemove.Add(candidate);
}
foreach(CMTCandidate candidate in candidatesToRemove) Candidates.Remove(candidate);
}
private void AddCandidate(CMTCandidate candidate)
{
if(null==Candidates) Candidates=new CMTCandidates();
if(Candidates.Any(x => x.Symbol.Equals(candidate.Symbol))) return;
Candidates.Add(candidate);
}
private void RemoveCandidate(CMTCandidate candidate)
{
if(null==Candidates) Candidates=new CMTCandidates();
if(!Candidates.Any(x => x.Symbol.Equals(candidate.Symbol))) return;
Candidates.Remove(Candidates.Where(x => x.Symbol.Equals(candidate.Symbol)).FirstOrDefault());
}
// ***************************************************************************************************************************************************
// ************************************************************************ M A R K E T C O N D I T I O N S ***************************************
// ***************************************************************************************************************************************************
private static bool IsTradeableMarket(DateTime tradeDate,CMTParams cmtParams)
{
Func<DateTime,CMTParams,bool>[] indicators = {IsTradeableMarketIndicator, IsTradeableVolatilityEnvironment};
return indicators.All(x => x.Invoke(tradeDate, cmtParams));
}
private static bool IsTradeableMarketIndicator(DateTime tradeDate,CMTParams cmtParams)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableMarketIndicator(setting):{0}",cmtParams.UseMarketIndicator?"Active":"Disabled"));
if(!cmtParams.UseMarketIndicator) return true;
bool result=true;
DateGenerator dateGenerator=new DateGenerator();
Prices benchmarkPrices=null;
double benchmarkDMA=double.NaN;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableMarketIndicator: Benchmark:{0} Horizon:{1} Moving Average Days:{2}",cmtParams.Benchmark,cmtParams.BenchmarkMovingAverageHorizon,cmtParams.BenchmarkMovingAverageDays));
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(tradeDate,cmtParams.BenchmarkMovingAverageHorizon);
foreach(DateTime historicalDate in historicalDates)
{
benchmarkPrices=GBPriceCache.GetInstance().GetPrices(cmtParams.Benchmark,historicalDate,cmtParams.BenchmarkMovingAverageDays+20);
benchmarkPrices=new Prices(benchmarkPrices.Take(cmtParams.BenchmarkMovingAverageDays).ToList());
DMAPrices benchmarkDMAPrices=MovingAverageGenerator.GenerateMovingAverage(benchmarkPrices,benchmarkPrices.Count);
benchmarkDMA=benchmarkDMAPrices[0].AVGPrice;
if(benchmarkPrices[0].Close<benchmarkDMA)
{
result=false;
break;
}
}
if(!result)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableMarketIndicator:{0} Benchmark:{1} BenchmarkMovingAverageHorizon:{2} BenchmarkMovingAverageDays:{3} **",result,cmtParams.Benchmark,cmtParams.BenchmarkMovingAverageHorizon,cmtParams.BenchmarkMovingAverageDays));
}
return result;
}
// Determine volatility based on ^VIX bollinger L band break on the close within 60 days prior
private static bool IsTradeableVolatilityEnvironment(DateTime tradeDate,CMTParams cmtParams)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableVolatilityEnvironment(setting):{0}",cmtParams.UseMarketIndicatorVolatility?"Active":"Disabled"));
if(!cmtParams.UseMarketIndicatorVolatility) return true;
bool result=true;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableVolatilityEnvironment: Volatility Benchmark:{0} Volatility Horizon:{1}",cmtParams.UseMarketIndicatorVolatilityBenchmark,cmtParams.UseMarketIndicatorVolatilityHorizon));
DateTime bandBreakDate=BandBreakIndicator.IsLowerBandBreakClose(cmtParams.UseMarketIndicatorVolatilityBenchmark,tradeDate,cmtParams.UseMarketIndicatorVolatilityHorizon);
if(!(Utility.IsEpoch(bandBreakDate)))
{
DateGenerator dateGenerator=new DateGenerator();
int elapsedDays = dateGenerator.TradingDaysBetween(tradeDate,bandBreakDate);
int daysRemaining=cmtParams.UseMarketIndicatorVolatilityHorizon-elapsedDays;
result=false;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("IsTradeableVolatilityEnvironment:{0}, VolatilityBenchmark:{1}, Violation/Band Break Date:{2}, Elapsed Days:{3}, Days Remaining:{4}, TradeDate:{5}",
result,cmtParams.UseMarketIndicatorVolatilityBenchmark,bandBreakDate.ToShortDateString(),elapsedDays,daysRemaining,tradeDate.ToShortDateString()));
}
return result;
}
// ***************************************************************************************************************************************************
// ************************************************************************ G E T P R I C E ******************************************************
// ***************************************************************************************************************************************************
private Price GetPrice(String symbol,DateTime priceDate)
{
DateGenerator dateGenerator=new DateGenerator();
priceDate=dateGenerator.GetPrevBusinessDay(priceDate);
Price price=GBPriceCache.GetInstance().GetPrice(symbol,priceDate);
if(null==price) price=GBPriceCache.GetInstance().GetPrice(symbol,dateGenerator.FindPrevBusinessDay(priceDate));
return price;
}
// *********************************************************************************************************************************************************************
// *********************************************************************************************************************************************************************
// *********************************************************************************************************************************************************************
public void DisplayRealtimeBlotter(DateTime tradeDate)
{
if(AllPositions.Count>0||ActivePositions.Count>0)
{
MDTrace.WriteLine(LogLevel.DEBUG,"***************************************************************************************************************************");
}
if(AllPositions.Count>0)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CLOSED POSITIONS ({0})...",AllPositions.Count));
Position.DisplayHeader();
foreach(Position position in AllPositions) position.Display();
}
if(ActivePositions.Count>0)
{
foreach(Position position in ActivePositions)
{
Price price=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(position.Symbol,tradeDate);
if(null==price) continue;
position.CurrentPrice=price.Close;
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("OPEN POSITIONS ({0})...",ActivePositions.Count));
Position.DisplayHeader();
foreach(Position position in ActivePositions) position.Display();
}
if(AllPositions.Count>0||ActivePositions.Count>0)
{
double totalGainLoss=GetRealtimeGainLoss(tradeDate);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL EXPOSURE {0}",Utility.FormatCurrency(ActivePositions.GetExposure())));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL GAIN LOSS {0}",Utility.FormatCurrency(totalGainLoss)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CASH BALANCE {0}",Utility.FormatCurrency(CashBalance)));
MDTrace.WriteLine(LogLevel.DEBUG,"***************************************************************************************************************************");
}
}
public double GetRealtimeGainLoss(DateTime tradeDate)
{
int count=ActivePositions.Count;
double gainLoss=0.00;
foreach(Position position in AllPositions) gainLoss+=position.GainLoss;
foreach(Position position in ActivePositions)
{
Price price=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(position.Symbol,tradeDate);
gainLoss+=(price.Close*position.Shares)-(position.PurchasePrice*position.Shares);
}
return gainLoss;
}
private void DisplayBalance()
{
MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,AVAILABLE CASH,TOTAL ACCOUNT");
if(!double.IsNaN(ActivePositions.GetMarketValue()))
{
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))));
}
else
{
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.GetExposure()+CashBalance))));
}
}
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 DisplayBalanceFromAllPositions()
{
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(AllPositions.GetExposure())),
Utility.AddQuotes(Utility.FormatCurrency(AllPositions.GetGainLoss())),
Utility.AddQuotes(Utility.FormatPercent(AllPositions.GetGainLossPercent())),
Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
Utility.AddQuotes(Utility.FormatCurrency(CashBalance))));
}
// ****************************************************************************************************************************************
// ************************************************************* C O N T R O L T O D A Y ***********************************************
// ****************************************************************************************************************************************
public DateTime Today()
{
return DateTime.Now;
}
// Ensure that the given trade date is the next anticipated trade date given in the session file. (i.e.) TradeDate=SessionFile.TradeDate+1
private CMTTrendModelResult CheckSequentialTradeDate(DateTime tradeDate,DateTime sessionParamsTradeDate)
{
CMTTrendModelResult result=new CMTTrendModelResult();
DateGenerator dateGenerator=new DateGenerator();
DateTime nextBusinessDay=dateGenerator.FindNextBusinessDay(sessionParamsTradeDate);
if(!nextBusinessDay.Date.Equals(tradeDate.Date))
{
String strMessage=String.Format("****** The supplied trade date does not match the next business date in the trade file. Given:{0}, Expected:{1} ******",tradeDate.Date.ToShortDateString(),nextBusinessDay.ToShortDateString());
result.Message=strMessage;
result.Success=false;
return result;
}
result.Success=true;
return result;
}
// ****************************************************************************************************************************************
// **************************************************************** S E S S I O N M A N A G E M E N T ***********************************
// ****************************************************************************************************************************************
public CMTSessionParams RestoreSession()
{
try
{
if(!CMTSessionManager.SessionAvailable(PathSessionFileName)) return null;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Restoring session from '{0}'",PathSessionFileName));
CMTSessionParams sessionParams=CMTSessionManager.RestoreSession(PathSessionFileName);
TradeDate=sessionParams.TradeDate;
if(TradeDate.Date<AnalysisDate.Date) TradeDate=AnalysisDate;
StartDate=sessionParams.StartDate;
Parameters=sessionParams.CMTParams;
PricingExceptions=sessionParams.PricingExceptions;
ActivePositions=sessionParams.ActivePositions;
AllPositions=sessionParams.AllPositions;
Candidates=sessionParams.Candidates;
StopLimits=sessionParams.StopLimits;
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));
CMTSessionParams sessionParams=new CMTSessionParams();
sessionParams.LastUpdated=Today();
sessionParams.TradeDate=TradeDate;
sessionParams.StartDate=StartDate;
sessionParams.AnalysisDate=AnalysisDate;
sessionParams.CMTParams=Parameters;
sessionParams.PricingExceptions=PricingExceptions;
sessionParams.StopLimits=StopLimits;
sessionParams.ActivePositions=ActivePositions;
sessionParams.AllPositions=AllPositions;
sessionParams.Candidates=Candidates;
sessionParams.CashBalance=CashBalance;
sessionParams.NonTradeableCash=NonTradeableCash;
CMTSessionManager.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));
CMTSessionParams sessionParams=new CMTSessionParams();
sessionParams.LastUpdated=Today();
sessionParams.TradeDate=TradeDate;
sessionParams.StartDate=StartDate;
sessionParams.AnalysisDate=AnalysisDate;
sessionParams.CMTParams=Parameters;
sessionParams.PricingExceptions=PricingExceptions;
sessionParams.StopLimits=StopLimits;
sessionParams.ActivePositions=ActivePositions;
sessionParams.AllPositions=AllPositions;
sessionParams.Candidates=Candidates;
sessionParams.CashBalance=CashBalance;
sessionParams.NonTradeableCash=NonTradeableCash;
return CMTSessionManager.SaveSession(sessionParams,backupFileName);
}
/// <summary>
/// This makes for easier reading of the sales
/// </summary>
/// <param name="positions"></param>
/// <param name="tradeDate"></param>
private static void DisplaySales(Positions positions,DateTime tradeDate)
{
MDTrace.WriteLine(LogLevel.DEBUG,"********* S E L L S *********");
foreach (Position position in positions)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Sell {0} {1} @ {2} on {3}",position.Symbol,Utility.FormatNumber(position.Shares,3),Utility.FormatCurrency(position.CurrentPrice,2),tradeDate.ToShortDateString()));
}
}
/// <summary>
/// This makes for easier reading of the purchases
/// </summary>
/// <param name="positions"></param>
/// <param name="tradeDate"></param>
private static void DisplayPurchases(Positions positions, DateTime tradeDate)
{
MDTrace.WriteLine(LogLevel.DEBUG,"********* B U Y S *********");
foreach (Position position in positions)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Buy {0} {1} @ {2} on {3}",position.Symbol,Utility.FormatNumber(position.Shares,3),Utility.FormatCurrency(position.PurchasePrice,2),tradeDate.ToShortDateString()));
}
}
}
}