diff --git a/MarketData/MarketData/ModelHelper/CMTrendHelper.cs b/MarketData/MarketData/ModelHelper/CMTrendHelper.cs new file mode 100755 index 0000000..ff3b672 --- /dev/null +++ b/MarketData/MarketData/ModelHelper/CMTrendHelper.cs @@ -0,0 +1,202 @@ +using MarketData.Generator.CMTrend; +using MarketData.Utils; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MarketData +{ + /// + /// This model is based on Mark Minervini + /// + public static class CMTrendHelper + { + /// + /// CMTSESSION /SESSIONFILE:{pathfilename} + /// + public static void HandleCMTSession(CommandArgs commandArgs) + { + if (!commandArgs.Has("SESSIONFILE")) {MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Missing SESSIONFILE"));return;} + CMTTrendModel trendModel=new CMTTrendModel(); + trendModel.DisplaySession(commandArgs.Coalesce("SESSIONFILE")); + } + + /// + /// RUNCMTREND /MODE:DAILY|BACKTEST|RUNTRENDTEMPLATE|ENTRYTEST /SYMBOL:{for mode ANALYZE,ENTRYTEST} /TRADEDATE:{for mode DAILY,RUNTRENDTEMPLATE) /STARTDATE:(for mode BACKTEST,ENTRYTEST) /ENDDATE:(for mode BACKTEST) /INITIALCASH: /SESSIONFILE: /MAXOPENPOSITIONS: /MAXDAILYPOSITIONS: Runs Mark Minervini trend + /// VALID ARGUMENTS FOR /MODE:BACKTEST ARE /SELLATENDOFSIMULATION: /STARTDATE: /ENDDATE: /SESSIONFILE: /MAXDAILYPOSITIONS: /MAXOPENPOSITIONS: /BENCHMARKMOVINGAVERAGEDAYS: /BENCHMARKMOVINGAVERAGEHORIZON + /// VALID ARGUMENTS FOR /MODE:BACKTEST CONTD., /POSITIONRISKPERCENTDECIMAL: /ENTRYTYPE:OverExtended,MVP,PriceTrend,VolumeTrend,RVOL /USEPROFITMAXIMIZATION:{TRUE|FALSE} /USEPROFITMAXIMIZATIONEXPRESSION:{string} + /// VALID ARGUMENTS FOR /MODE:BACKTEST CONTD., /POSITIONRISKPERCENTDECIMAL: /ENTRYTYPE:OverExtended,MVP,PriceTrend,VolumeTrend,RVOL /USEPROFITMAXIMIZATION:TRUE /USEPROFITMAXIMIZATIONEXPRESSION:\"R_THRESSHOLD=4;MAX_ATR=3;MAX_R=10;FACTOR=MAX_ATR/MAX_R;MULTIPLIER=MAX_ATR;IF(RMultiple>R_THRESSHOLD){MULTIPLIER=FACTOR*RMultiple;}\" + /// VALID ARGUMENTS FOR /MODE:BACKTEST CONTD., /USEPOSITIONSIZINGSTRATEGY:{TRUE|FALSE} /USEPOSITIONSIZINGSTRATEGYSTRATEGY:{TotalRisk:EqualWeight,Formula} /USEPOSITIONSIZINGSTRATEGYFORMULA:{Formula} + /// VALID ARGUMENTS FOR /MODE:BACKTEST CONTD., /USETRADEONLYSECTORS:{TRUE|FALSE} /USETRADEONLYSECTORSSECTORS:Healthcare,Technology,Basic Materials,Consumer Defensive,Industrials + /// /BENCHMARKMOVINGAVERAGEDAYS: (i.e.) 200, 100, 55 + /// /BENCHMARKMOVINGAVERAGEHORIZON: (i.e.) 5 + /// + /// + public static void HandleRunCMTrend(CommandArgs commandArgs) + { + String mode; + + if(!commandArgs.Has("MODE")) + { + if(!commandArgs.Has("MODE")) MDTrace.WriteLine(LogLevel.DEBUG,"MODE is a required paramater."); + MDTrace.WriteLine(LogLevel.DEBUG,"RUNMMTREND /MODE:DAILY|BACKTEST|RUNTRENDTEMPLATE|ANALYZE|DISPLAY|CLOSEPOSITION /SELLDATE:{CLOSEPOSITION} /PRICE:{CLOSEPOSITION} /SYMBOL:{for mode ANALYZE,CLOSEPOSITION} /TRADEDATE:{for mode DAILY,RUNTRENDTEMPLATE,ANALYZE,CLOSEPOSITION) /STARTDATE:(for mode BACKTEST) /ENDDATE:(for mode BACKTEST) /INITIALCASH: /SESSIONFILE: MAXOPENPOSITIONS: /MAXDAILYPOSITIONS: Runs Mark Minervini trend"); + return; + } + mode=commandArgs.Get("MODE"); + if("ENTRYTEST".Equals(mode)) + { + CMTParams cmtParams=new CMTParams(); + if(!commandArgs.Has("SYMBOL")||!commandArgs.Has("STARTDATE")) + { + if(!commandArgs.Contains("SYMBOL")) MDTrace.WriteLine(LogLevel.DEBUG,"SYMBOL is a required parameter when MODE=ENTRYTEST"); + if(!commandArgs.Contains("STARTDATE")) MDTrace.WriteLine(LogLevel.DEBUG,"STARTDATE is a required parameter when MODE=ENTRYTEST"); + return; + } + CMTTrendModel trendModel=new CMTTrendModel(); + trendModel.EntryTest(commandArgs.Get("SYMBOL"),commandArgs.Get("STARTDATE")); + } + else if("CLOSEPOSITION".Equals(mode)) + { + CMTParams cmtParams=new CMTParams(); + if(!commandArgs.Has("PURCHASEDATE,SYMBOL,SESSIONFILE,PRICE,SELLDATE")) + { + if(!commandArgs.Contains("PURCHASEDATE")) MDTrace.WriteLine(LogLevel.DEBUG,"PURCHASEDATE is a required parameter when MODE=CLOSEPOSITION"); + if(!commandArgs.Contains("SYMBOL")) MDTrace.WriteLine(LogLevel.DEBUG,"SYMBOL is a required parameter when MODE=CLOSEPOSITION"); + if(!commandArgs.Contains("SESSIONFILE")) MDTrace.WriteLine(LogLevel.DEBUG,"SESSIONFILE is a required parameter when MODE=CLOSEPOSITION"); + if(!commandArgs.Contains("PRICE")) MDTrace.WriteLine(LogLevel.DEBUG,"PRICE is a required parameter when MODE=CLOSEPOSITION"); + if(!commandArgs.Contains("SELLDATE")) MDTrace.WriteLine(LogLevel.DEBUG,"SELLDATE is a required parameter when MODE=CLOSEPOSITION"); + return; + } + CMTTrendModel trendModel=new CMTTrendModel(); + trendModel.ClosePosition(commandArgs.Get("SYMBOL"),commandArgs.Get("PURCHASEDATE"),commandArgs.Get("SELLDATE"),commandArgs.Get("PRICE"),commandArgs.Get("SESSIONFILE")); + } + else if("DAILY".Equals(mode)) + { + CMTParams cmtParams=new CMTParams(); + if(!commandArgs.Has("TRADEDATE,SESSIONFILE")) + { + if(!commandArgs.Contains("TRADEDATE")) MDTrace.WriteLine(LogLevel.DEBUG,"TRADEDATE is a required parameter when MODE=DAILY"); + if(!commandArgs.Contains("SESSIONFILE")) MDTrace.WriteLine(LogLevel.DEBUG,"SESSIONFILE is a required parameter when MODE=DAILY"); + return; + } + if(commandArgs.Contains("INITIALCASH")) cmtParams.InitialCash=commandArgs.Get("INITIALCASH"); + if(commandArgs.Contains("MAXDAILYPOSITIONS")) cmtParams.MaxDailyPositions=commandArgs.Get("MAXDAILYPOSITIONS"); + if(commandArgs.Contains("MAXOPENPOSITIONS")) cmtParams.MaxOpenPositions=commandArgs.Get("MAXOPENPOSITIONS"); + + if(commandArgs.Has("ONLYTRADESYMBOLS")) cmtParams.OnlyTradeSymbols=commandArgs.Get("ONLYTRADESYMBOLS"); + + if(commandArgs.Contains("POSITIONRISKPERCENTDECIMAL")) + { + cmtParams.PositionRiskPercentDecimal=commandArgs.Get("POSITIONRISKPERCENTDECIMAL"); + } + + if(commandArgs.Contains("ENTRYTYPE")) + { + List entryTypes=Utility.ToList(commandArgs.Get("ENTRYTYPE")); + List constraints=new List { "OVEREXTENDED","MVP","NARROWRANGE","MACD","PRICETREND","VOLUMETREND" }; + bool results=entryTypes.All(i => constraints.ContainsIgnoreCase(i)); + if(!results) + { + MDTrace.WriteLine(LogLevel.DEBUG,"ENTRYTYPE must consist of one or more OVEREXTENDED, MVP, NarrowRange, MACD, PriceTrend, VolumeTrend"); + return; + } + cmtParams.EntryType=commandArgs.Get("ENTRYTYPE"); + } + CMTTrendModel trendModel=new CMTTrendModel(); + + if(commandArgs.Contains("USETRADEONLYSECTORS")) + { + cmtParams.UseTradeOnlySectors=commandArgs.Get("USETRADEONLYSECTORS"); + if(cmtParams.UseTradeOnlySectors) + { + cmtParams.UseTradeOnlySectorsSectors=commandArgs.Get("USETRADEONLYSECTORSSECTORS"); + } + } + CMTTrendModelResult result=trendModel.RunDaily(commandArgs.Get("TRADEDATE"),commandArgs.Get("SESSIONFILE"),cmtParams); + } + else if("BACKTEST".Equals(mode)) + { + CMTParams cmtParams=new CMTParams(); + bool sellAtEndOfSimulation=true; + if(!commandArgs.Has("STARTDATE,ENDDATE,SESSIONFILE")) + { + if(!commandArgs.Contains("STARTDATE")) MDTrace.WriteLine(LogLevel.DEBUG,"STARTDATE is a required parameter"); + if(!commandArgs.Contains("ENDDATE")) MDTrace.WriteLine(LogLevel.DEBUG,"ENDDATE is a required parameter"); + if(!commandArgs.Contains("SESSIONFILE")) MDTrace.WriteLine(LogLevel.DEBUG,"SESSIONFILE is a required parameter"); + return; + } + + CMTTrendModel trendModel=new CMTTrendModel(); + + if(commandArgs.Contains("USETRADEONLYSECTORS")) + { + cmtParams.UseTradeOnlySectors=commandArgs.Get("USETRADEONLYSECTORS"); + if(cmtParams.UseTradeOnlySectors) + { + cmtParams.UseTradeOnlySectorsSectors=commandArgs.Get("USETRADEONLYSECTORSSECTORS"); + } + } + + if(commandArgs.Contains("USEPROFITMAXIMIZATION")) + { + cmtParams.UseProfitMaximization=commandArgs.Get("USEPROFITMAXIMIZATION"); + if(commandArgs.Contains("USEPROFITMAXIMIZATIONEXPRESSION")) + { + cmtParams.UseProfitMaximizationExpression=commandArgs.Get("USEPROFITMAXIMIZATIONEXPRESSION"); + } + } + + if(commandArgs.Contains("MAXDAILYPOSITIONS")) cmtParams.MaxDailyPositions=commandArgs.Get("MAXDAILYPOSITIONS"); + if(commandArgs.Contains("MAXOPENPOSITIONS")) cmtParams.MaxOpenPositions=commandArgs.Get("MAXOPENPOSITIONS"); + if(commandArgs.Contains("BENCHMARKMOVINGAVERAGEDAYS")) cmtParams.BenchmarkMovingAverageDays=commandArgs.Get("BENCHMARKMOVINGAVERAGEDAYS"); + if(commandArgs.Contains("BENCHMARKMOVINGAVERAGEHORIZON")) cmtParams.BenchmarkMovingAverageHorizon=commandArgs.Get("BENCHMARKMOVINGAVERAGEHORIZON"); + + if(commandArgs.Has("ONLYTRADESYMBOLS")) cmtParams.OnlyTradeSymbols=commandArgs.Get("ONLYTRADESYMBOLS"); + + if(commandArgs.Contains("POSITIONRISKPERCENTDECIMAL")) + { + cmtParams.PositionRiskPercentDecimal=commandArgs.Get("POSITIONRISKPERCENTDECIMAL"); + } + + if(commandArgs.Contains("ENTRYTYPE")) + { + List entryTypes=Utility.ToList(commandArgs.Get("ENTRYTYPE")); + List constraints=new List { "OVEREXTENDED","MVP","NARROWRANGE","MACD","PRICETREND","VOLUMETREND" }; + bool results=entryTypes.All(i => constraints.ContainsIgnoreCase(i)); + if(!results) + { + MDTrace.WriteLine(LogLevel.DEBUG,"ENTRYTYPE must consist of one or more OVEREXTENDED, MVP, NarrowRange, MACD, PriceTrend, VolumeTrend"); + return; + } + cmtParams.EntryType=commandArgs.Get("ENTRYTYPE"); + } + if(commandArgs.Contains("SELLATENDOFSIMULATION")) + { + sellAtEndOfSimulation=commandArgs.Get("SELLATENDOFSIMULATION"); + } + CMTTrendModelResult result=trendModel.RunBacktestMode(commandArgs.Get("STARTDATE"),commandArgs.Get("ENDDATE"),sellAtEndOfSimulation,commandArgs.Get("SESSIONFILE"),cmtParams); + } + else if("DISPLAY".Equals(mode)) + { + if(!commandArgs.Contains("SESSIONFILE")) { MDTrace.WriteLine(LogLevel.DEBUG,"SESSIONFILE is a required parameter"); return; } + CMTTrendModel trendModel=new CMTTrendModel(); + trendModel.DisplaySession(commandArgs.Get("SESSIONFILE")); + } + else if("RUNTRENDTEMPLATE".Equals(mode)) + { + if(!commandArgs.Contains("TRADEDATE")) + { + MDTrace.WriteLine(LogLevel.DEBUG,"TRADEDATE is a required parameter when MODE=DAILY"); + return; + } + CMTTrendModel trendModel=new CMTTrendModel(); + trendModel.RunTrendTemplate(commandArgs.Get("TRADEDATE")); + } + else + { + MDTrace.WriteLine(LogLevel.DEBUG,"RUNCMTREND /MODE:DAILY|BACKTEST /TRADEDATE:{for mode DAILY) /STARTDATE:(for mode BACKTEST) /ENDDATE:(for mode BACKTEST) /INITIALCASH: /SESSIONFILE: /MAXPOSITIONS Runs Mark Minervini trend"); + } + return; + } + } +} diff --git a/MarketData/MarketData/Models/CM20191031.txt b/MarketData/MarketData/Models/CM20191031.TXT similarity index 100% rename from MarketData/MarketData/Models/CM20191031.txt rename to MarketData/MarketData/Models/CM20191031.TXT diff --git a/MarketData/MarketData/Models/CMT20200817.txt b/MarketData/MarketData/Models/CMT20200817.TXT similarity index 100% rename from MarketData/MarketData/Models/CMT20200817.txt rename to MarketData/MarketData/Models/CMT20200817.TXT diff --git a/MarketData/MarketData/Models/MG20180131.txt b/MarketData/MarketData/Models/MG20180131.TXT similarity index 100% rename from MarketData/MarketData/Models/MG20180131.txt rename to MarketData/MarketData/Models/MG20180131.TXT diff --git a/MarketData/MarketData/Services/MainService.cs b/MarketData/MarketData/Services/MainService.cs index 1b4dd2b..6411062 100755 --- a/MarketData/MarketData/Services/MainService.cs +++ b/MarketData/MarketData/Services/MainService.cs @@ -15,6 +15,29 @@ namespace MarketData.Services { private Dictionary> tasks = new Dictionary>(); + /// + /// DisplayUsage + /// + public static void DisplayUsage() + { + MDTrace.WriteLine(LogLevel.DEBUG,$"USAGE"); + MDTrace.WriteLine(LogLevel.DEBUG,$"LOADHEADLINESWATCHLIST"); + MDTrace.WriteLine(LogLevel.DEBUG,$"LOADPREMARKETDATA"); + MDTrace.WriteLine(LogLevel.DEBUG,"UPDATEDAILY2 /DATE:"); + MDTrace.WriteLine(LogLevel.DEBUG,"UPDATELATESTPRICEOPENPOSITIONS"); + MDTrace.WriteLine(LogLevel.DEBUG,"UPDATELATESTPRICEWATCHLIST /WATCHLIST:"); + MDTrace.WriteLine(LogLevel.DEBUG,"UPDATELATESTANALYSTRATINGS"); + MDTrace.WriteLine(LogLevel.DEBUG,"UPDATEANALYSTRATINGS"); + MDTrace.WriteLine(LogLevel.DEBUG,"UPDATESECFILINGSWATCHLIST /WATCHLIST:"); + MDTrace.WriteLine(LogLevel.DEBUG,"UPDATECOMPANYPROFILES"); + MDTrace.WriteLine(LogLevel.DEBUG,"ECHO {param1} {param2} {param(n)"); + MDTrace.WriteLine(LogLevel.DEBUG,"MGSHSESSION /SESSIONFILE:"); + MDTrace.WriteLine(LogLevel.DEBUG,"MGSHRUNBACKTEST /USEHEDGING: /HEDGEINITIALCASH: /USESTOPLIMITS: /KEEPSLOTPOSITIONS: /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{ENDDATE}: /SESSIONFILE: "); + MDTrace.WriteLine(LogLevel.DEBUG,"MGSHRUNDAILY /SESSIONFILE: /TRADEDATE:"); + MDTrace.WriteLine(LogLevel.DEBUG,"RUNCMTREND /MODE:DAILY|BACKTEST|RUNTRENDTEMPLATE|ENTRYTEST /SYMBOL:{for mode ANALYZE,ENTRYTEST} /TRADEDATE:{for mode DAILY,RUNTRENDTEMPLATE) /STARTDATE:(for mode BACKTEST,ENTRYTEST) /ENDDATE:(for mode BACKTEST) /INITIALCASH: /SESSIONFILE: /MAXOPENPOSITIONS: /MAXDAILYPOSITIONS: Runs Mark Minervini trend"); + MDTrace.WriteLine(LogLevel.DEBUG,"CMTSESSION /SESSIONFILE:{pathfilename} Runs Mark Minervini trend display session"); + } + /// /// This is the main entry point /// @@ -37,6 +60,8 @@ namespace MarketData.Services tasks.Add("MGSHSESSION",TaskMGSHSession); tasks.Add("MGSHRUNBACKTEST",TaskMGSHRunBacktest); tasks.Add("MGSHRUNDAILY",TaskMGSHRunDaily); + tasks.Add("RUNCMTREND",TaskRunCMTrend); + tasks.Add("CMTSESSION",TaskCMTSession); tasks.Add("ECHO",TaskEcho); GlobalConfig.Instance.Configuration = configuration; // This call sets up configuration stuff so it needs to be first. @@ -186,6 +211,18 @@ namespace MarketData.Services await Task.FromResult(true); } + public async Task TaskRunCMTrend(CommandArgs commandArgs) + { + CMTrendHelper.HandleRunCMTrend(commandArgs); + await Task.FromResult(true); + } + + public async Task TaskCMTSession(CommandArgs commandArgs) + { + CMTrendHelper.HandleCMTSession(commandArgs); + await Task.FromResult(true); + } + // ********************************************************************************************************************************************************* // ******************************************************************* E N D T A S K S ******************************************************************** // ********************************************************************************************************************************************************* @@ -210,28 +247,6 @@ namespace MarketData.Services return true; } -// ********************************************************************************************************************************* -// ********************************************************************************************************************************* -// ********************************************************************************************************************************* - - public static void DisplayUsage() - { - MDTrace.WriteLine(LogLevel.DEBUG,$"USAGE"); - MDTrace.WriteLine(LogLevel.DEBUG,$"LOADHEADLINESWATCHLIST"); - MDTrace.WriteLine(LogLevel.DEBUG,$"LOADPREMARKETDATA"); - MDTrace.WriteLine(LogLevel.DEBUG,"UPDATEDAILY2 /DATE:"); - MDTrace.WriteLine(LogLevel.DEBUG,"UPDATELATESTPRICEOPENPOSITIONS"); - MDTrace.WriteLine(LogLevel.DEBUG,"UPDATELATESTPRICEWATCHLIST /WATCHLIST:"); - MDTrace.WriteLine(LogLevel.DEBUG,"UPDATELATESTANALYSTRATINGS"); - MDTrace.WriteLine(LogLevel.DEBUG,"UPDATEANALYSTRATINGS"); - MDTrace.WriteLine(LogLevel.DEBUG,"UPDATESECFILINGSWATCHLIST /WATCHLIST:"); - MDTrace.WriteLine(LogLevel.DEBUG,"UPDATECOMPANYPROFILES"); - MDTrace.WriteLine(LogLevel.DEBUG,"ECHO {param1} {param2} {param(n)"); - MDTrace.WriteLine(LogLevel.DEBUG,"MGSHSESSION /SESSIONFILE:"); - MDTrace.WriteLine(LogLevel.DEBUG,"MGSHRUNBACKTEST /USEHEDGING: /HEDGEINITIALCASH: /USESTOPLIMITS: /KEEPSLOTPOSITIONS: /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{ENDDATE}: /SESSIONFILE: "); - MDTrace.WriteLine(LogLevel.DEBUG,"MGSHRUNDAILY /SESSIONFILE: /TRADEDATE:"); - } - // ********************************************************************************************************************************************** // ************************************************* U P D A T E D A I L Y 2 M E T H O D S *************************************************** // ********************************************************************************************************************************************** diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTActivePositions.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTActivePositions.cs new file mode 100755 index 0000000..41a85fc --- /dev/null +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTActivePositions.cs @@ -0,0 +1,18 @@ +using MarketData.MarketDataModel; +using MarketData.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace MarketData.Generator.CMTrend +{ + public class ActivePositions:Positions + { + public ActivePositions() + { + } + } +} diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidate.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidate.cs new file mode 100755 index 0000000..0aa45a3 --- /dev/null +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidate.cs @@ -0,0 +1,129 @@ +using MarketData.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MarketData.Generator.CMTrend +{ + public class CMTCandidates : List + { + public CMTCandidates() + { + } + public CMTCandidates(List cmtCandidates) + { + foreach(CMTCandidate cmtCandidate in cmtCandidates) Add(cmtCandidate); + } + public NVPCollections ToNVPCollections() + { + NVPCollections nvpCollections=new NVPCollections(); + foreach(CMTCandidate candidate in this) + { + nvpCollections.Add(candidate.ToNVPCollection()); + } + return nvpCollections; + } + public static CMTCandidates FromNVPCollections(NVPCollections nvpCollections) + { + CMTCandidates candidates=new CMTCandidates(); + foreach(NVPCollection nvpCollection in nvpCollections) + { + candidates.Add(CMTCandidate.FromNVPCollection(nvpCollection)); + } + return candidates; + } + public void AddFromNVPCollection(NVPCollection nvpCollection) + { + Add(CMTCandidate.FromNVPCollection(nvpCollection)); + } + public new void Sort() + { + List candidates=new CMTCandidates((from CMTCandidate mmCandidate in this select mmCandidate).OrderByDescending(x => x.Score).ToList()); + this.Clear(); + this.AddRange(candidates); + } + } + public class CMTCandidate + { + public CMTCandidate() + { + Violation=false; + } + public String Symbol { get; set; } + public DateTime AnalysisDate { get; set; } + public double EPSSlope { get; set; } + public double ProfitMarginSlope { get; set; } + public double PriceSlope { get; set; } + public double Volatility { get; set; } + public long Volume { get; set; } + public double RSquared{get;set;} + public double Beta{get;set;} + public int BetaMonths{get;set;} + public bool Violation { get; set; } + public double Slope { get; set; } + public double Score { get; set; } + public double AnnualizedReturn { get; set; } + public double SharpeRatio { get; set; } + public String Reason { get; set; } + + public virtual NVPCollection ToNVPCollection() + { + NVPCollection nvpCollection=new NVPCollection(); + nvpCollection.Add(new NVP("Symbol",Symbol.ToString())); + nvpCollection.Add(new NVP("AnalysisDate",AnalysisDate.ToString())); + if(!double.IsNaN(EPSSlope)) nvpCollection.Add(new NVP("EPSSlope",EPSSlope.ToString())); + if(!double.IsNaN(ProfitMarginSlope)) nvpCollection.Add(new NVP("ProfitMarginSlope",ProfitMarginSlope.ToString())); + if(!double.IsNaN(PriceSlope)) nvpCollection.Add(new NVP("PriceSlope",PriceSlope.ToString())); + if(!double.IsNaN(Volatility)) nvpCollection.Add(new NVP("Volatility",Volatility.ToString())); + nvpCollection.Add(new NVP("Volume",Volume.ToString())); + nvpCollection.Add(new NVP("Violation",Violation.ToString())); + if(!double.IsNaN(Slope)) nvpCollection.Add(new NVP("Slope",Slope.ToString())); + if(!double.IsNaN(Score)) nvpCollection.Add(new NVP("Score",Score.ToString())); + if(!double.IsNaN(AnnualizedReturn)) nvpCollection.Add(new NVP("AnnualizedReturn",AnnualizedReturn.ToString())); + if(!double.IsNaN(SharpeRatio)) nvpCollection.Add(new NVP("SharpeRatio",SharpeRatio.ToString())); + if(!double.IsNaN(RSquared)) nvpCollection.Add(new NVP("RSquared",RSquared.ToString())); + nvpCollection.Add(new NVP("BetaMonths",BetaMonths.ToString())); + nvpCollection.Add(new NVP("Beta",Beta.ToString())); + if(null!=Reason) nvpCollection.Add(new NVP("Reason",Reason.ToString())); + return nvpCollection; + } + public static CMTCandidate FromNVPCollection(NVPCollection nvpCollection) + { + CMTCandidate candidate=new CMTCandidate(); + NVPDictionary nvpDictionary=nvpCollection.ToDictionary(); + if(nvpDictionary.ContainsKey("Symbol")) candidate.Symbol=nvpDictionary["Symbol"].Get(); + if(nvpDictionary.ContainsKey("AnalysisDate")) candidate.AnalysisDate=nvpDictionary["AnalysisDate"].Get(); + if(nvpDictionary.ContainsKey("EPSSlope")) candidate.EPSSlope=nvpDictionary["EPSSlope"].Get(); + if(nvpDictionary.ContainsKey("ProfitMarginSlope")) candidate.ProfitMarginSlope=nvpDictionary["ProfitMarginSlope"].Get(); + if(nvpDictionary.ContainsKey("PriceSlope")) candidate.PriceSlope=nvpDictionary["PriceSlope"].Get(); + if(nvpDictionary.ContainsKey("Volatility")) candidate.Volatility=nvpDictionary["Volatility"].Get(); + if(nvpDictionary.ContainsKey("Volume")) candidate.Volume=nvpDictionary["Volume"].Get(); + if(nvpDictionary.ContainsKey("Violation")) candidate.Violation=nvpDictionary["Violation"].Get(); + if(nvpDictionary.ContainsKey("Slope")) candidate.Slope=nvpDictionary["Slope"].Get(); + if(nvpDictionary.ContainsKey("Score")) candidate.Score=nvpDictionary["Score"].Get(); + if(nvpDictionary.ContainsKey("AnnualizedReturn")) candidate.AnnualizedReturn=nvpDictionary["AnnualizedReturn"].Get(); + if(nvpDictionary.ContainsKey("SharpeRatio")) candidate.SharpeRatio=nvpDictionary["SharpeRatio"].Get(); + if(nvpDictionary.ContainsKey("RSquared")) candidate.RSquared=nvpDictionary["RSquared"].Get(); + if(nvpDictionary.ContainsKey("BetaMonths")) candidate.BetaMonths=nvpDictionary["BetaMonths"].Get(); + if(nvpDictionary.ContainsKey("Beta")) candidate.Beta=nvpDictionary["Beta"].Get(); + if(nvpDictionary.ContainsKey("Reason")) candidate.Reason=nvpDictionary["Reason"].Get(); + return candidate; + } + public static String Header() + { + StringBuilder sb=new StringBuilder(); + sb.Append("Symbol,AnalysisDate,PriceSlope,ProfitMarginSlope,EPSSlope,Volatility,Volume,Slope,Score,AnnualizedReturn,SharpeRatio,RSquared,Beta,BetaMonths"); + return sb.ToString(); + } + public override String ToString() + { + StringBuilder sb=new StringBuilder(); + sb.Append(Symbol).Append(",").Append(AnalysisDate.ToShortDateString()).Append(",").Append(PriceSlope).Append(",").Append(ProfitMarginSlope).Append(",").Append(EPSSlope).Append(",").Append(Volatility).Append(",").Append(Volume).Append(",").Append(Slope).Append(",").Append(Score).Append(",").Append(AnnualizedReturn).Append(",").Append(SharpeRatio).Append(","); + sb.Append(RSquared).Append(",").Append(Beta).Append(",").Append(BetaMonths); + return sb.ToString(); + } + } +} + diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs new file mode 100755 index 0000000..510c1c6 --- /dev/null +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTCandidateGenerator.cs @@ -0,0 +1,438 @@ +using MarketData.Cache; +using MarketData.DataAccess; +using MarketData.Generator.MovingAverage; +using MarketData.MarketDataModel; +using MarketData.Numerical; +using MarketData.Utils; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MarketData.Generator.CMTrend +{ + public class CMTCandidateGenerator + { + private static readonly int PRICING_DAYS=252; + public CMTCandidateGenerator() + { + } + // ******************************************************************************************************************************************************************************* + // ******************************************************************* G E N E R A T E C A N D I D A T E - M A R C M I N E R V I N I **************************************** + // ******************************************************************************************************************************************************************************* + public static CMTCandidate GenerateCandidate(String symbol,DateTime tradeDate,CMTParams cmtParams,List symbolsHeld=null) + { + CMTCandidate cmtCandidate=new CMTCandidate(); + + try + { +// Check MarketCap + Fundamental fundamental=FundamentalDA.GetFundamentalMaxDate(symbol,tradeDate); + if(null==fundamental) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("No fundamental for {0}.",symbol); + return cmtCandidate; + } + if(!(fundamental.MarketCap>=cmtParams.MarketCapLowerLimit)) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("MarketCapLowerLimit constraint violation for {0}.",symbol); + return cmtCandidate; + } +// Check if the symbol is held in any open positions + if(null!=symbolsHeld&&symbolsHeld.Any(x => x.Equals(symbol, StringComparison.CurrentCultureIgnoreCase))) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("{0} is already held.",symbol); + return cmtCandidate; + } +// No trade symbols + if(null!=cmtParams.NoTradeSymbolsList&&cmtParams.NoTradeSymbolsList.Any(x => x.Equals(symbol, StringComparison.CurrentCultureIgnoreCase))) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("{0} is in the No-Trade list.",symbol); + return cmtCandidate; + } +// Equity check + CompanyProfile companyProfile=CompanyProfileDA.GetCompanyProfile(symbol); + if(null==companyProfile) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("No company profile found for {0}.",symbol); + return cmtCandidate; + } + if(!companyProfile.IsEquity) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("{0} is not an equity. {1}.",symbol,companyProfile.SecurityType); + return cmtCandidate; + } +// Sector Check + if(cmtParams.UseTradeOnlySectors) + { + List validSectors=Utility.ToList(cmtParams.UseTradeOnlySectorsSectors); + if(null==companyProfile.Sector) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("Invalid sector for {0}. Found {1} expected one of {2}.",symbol,null==companyProfile.Sector?"(Null)":companyProfile.Sector,Utility.ListToString(validSectors.ToList())); + return cmtCandidate; + } + if(!validSectors.Any(x => x.Equals(companyProfile.Sector))) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("Invalid sector for {0}. Found {1} expected one of {2}.",symbol,null==companyProfile.Sector?"(Null)":companyProfile.Sector,Utility.ListToString(validSectors.ToList())); + return cmtCandidate; + } + } +// setup for trend analysis + Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,tradeDate,PRICING_DAYS); + if(null==prices||prices.Count0) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason=String.Format("Insufficient liquidity."); + return cmtCandidate; + } +// Setup for the moving averages checks + Prices prices50=new Prices(prices.Take(50).ToList()); + Prices prices15=new Prices(prices.Take(15).ToList()); + Prices prices150=new Prices(prices.Take(150).ToList()); + Prices prices200=new Prices(prices.Take(200).ToList()); + + DMAPrices dma50Prices=MovingAverageGenerator.GenerateMovingAverage(prices50,prices50.Count); + DMAPrices dma150Prices=MovingAverageGenerator.GenerateMovingAverage(prices150,prices150.Count); + DMAPrices dma200Prices=MovingAverageGenerator.GenerateMovingAverage(prices200,prices200.Count); + + double dma50Close=dma50Prices[0].AVGPrice; + double dma150Close=dma150Prices[0].AVGPrice; + double dma200Close=dma200Prices[0].AVGPrice; + double volatility=prices15.Volatility(); + +// Trend #1 check. Check that current price is greater than 150 day moving average and current price is greater than 200 day moving average + if(currentPrice.Close x.Close); + double latestClose=currentPrice.Close; + double percentReturnOver52WeekLow=((latestClose-weekLow52)/weekLow52)*100.00; + if(percentReturnOver52WeekLow x.Close); + double percentReturnProximityTo52WeekHigh=double.NaN; + if(latestClosecmtParams.MinPercentReturnProximityTo52WeekHigh) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason=String.Format("Trend#7 Violation :PercentReturnProximityTo52WeekHigh<{0}.",cmtParams.MinPercentReturnProximityTo52WeekHigh); + return cmtCandidate; + } + +// Trend #8 check. Evaluate the RSI +// generate a 14 day standard RSI with 30 days of pricing data + RSICollection rsiCollection=RSIGenerator.GenerateRSI(symbol,currentPrice.Date,30); + if(null==rsiCollection||0==rsiCollection.Count||rsiCollection[rsiCollection.Count-1].RSI dma200List=new List(); + DateTime historicalDate=dateGenerator.GenerateHistoricalDate(currentPrice.Date,cmtParams.DMA200Horizon+10); + List historicalDates=PricingDA.GetPricingDatesBetween(historicalDate,currentPrice.Date); + historicalDates=historicalDates.Take(cmtParams.DMA200Horizon).ToList(); + if(historicalDates.Count0.",cmtParams.DMA200Horizon); + return cmtCandidate; + } +// Trend check ensure that prices are trending higher + if(cmtParams.UsePriceSlopeIndicator) + { + int dayCount=cmtParams.UsePriceSlopeIndicatorDays; + Prices pricesTrend=GBPriceCache.GetInstance().GetPrices(symbol,tradeDate,dayCount); + double[] pricesLow=Numerics.ToDouble(pricesTrend.GetPricesLow()); + LeastSquaresResult lsr=LeastSquaresHelper.CalculateLeastSquares(pricesLow); + if(lsr.Slope<=0.00) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("Price trend violation {0}. The {1} pricing slope is {2}",symbol,dayCount,Utility.FormatNumber(lsr.Slope,6)); + return cmtCandidate; + } + } +// Filter penny stocks - don't trade anything less than $1.00 + if(currentPrice.Close<1.00||currentPrice.Open<1.00) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("Penny stock violation {0} Close price is {1}, Open price is ",symbol,Utility.FormatCurrency(currentPrice.Close),Utility.FormatCurrency(currentPrice.Open)); + return cmtCandidate; + } +// Capture latest Volume - we'll do a min check later on + cmtCandidate.Volume=currentPrice.Volume; + +// Daily Return Check + float[] dailyReturns=prices.GetReturns(); // First we build the returns (before we reverse the pricing direction) + if(HasReturnViolation(dailyReturns,cmtParams.DailyReturnLimit)) // Check the return stream. If any daily return exceeds DailyReturnLimit then we discard. + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("Daily return violation for {0}. A daily return exceeded {1}%.",symbol,cmtParams.DailyReturnLimit); + return cmtCandidate; + } + +// check for outliers in the return stream + if((from float value in dailyReturns where Math.Abs(value)>cmtParams.DailyReturnLimit select value).Count()>0) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("Outlier encountered in return stream for {0}. Limit {1}",symbol,cmtParams.DailyReturnLimit); + return cmtCandidate; + } + +// EBITDA screen + bool UseEBITDAScreen=true; + if(UseEBITDAScreen&&(double.IsNaN(fundamental.EBITDA)||fundamental.EBITDA<=0)) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason="Trend#14 Violation : EBITDA"; + return cmtCandidate; + } + + bool UsePEScreen=true; + if(UsePEScreen&&(double.IsNaN(fundamental.PE))||fundamental.PE<=0.00) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason="Trend#14 Violation : UsePEScreen"; + return cmtCandidate; + } + +// Setup for next tests + double profitMarginSlope=double.NaN; + DateTime minDate=DateTime.MinValue; + DateTime maxDate=DateTime.MinValue; + float[] values; +// Revenue per share screen + bool UseRevenuePerShareScreen=true; + if(UseRevenuePerShareScreen&&(double.IsNaN(fundamental.RevenuePerShare)||fundamental.RevenuePerShare<0.00)) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason="Trend#14 Violation : Revenue Per Share"; + return cmtCandidate; + } +// Trend#9 - My check Increasing EPS + double epsSlope=double.NaN; + if(companyProfile.IsEquity&&cmtParams.EPSCheck) + { + TimeSeriesCollection epsTimeSeries=FundamentalDA.GetEPS(symbol,currentPrice.Date); + if(null==epsTimeSeries||epsTimeSeries.Count<3) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason="Trend#9 Violation : No EPS time series to perform check."; + return cmtCandidate; + } + epsTimeSeries=new TimeSeriesCollection(epsTimeSeries.Take(3).ToList()); + minDate=epsTimeSeries.Min(x => x.AsOf); + maxDate=epsTimeSeries.Max(x => x.AsOf); + values=epsTimeSeries.ToFloat(); + values=Numerics.Reverse(ref values); + epsSlope=Numerics.Slope(values); + if(epsSlope<=0) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason="Trend#9 Violation : epsSlope<=0."; + return cmtCandidate; + } + } + +// Trend#10 - My check - Increasing profit margin + if(companyProfile.IsEquity&&cmtParams.ProfitMarginCheck) + { + TimeSeriesCollection profitMarginTimeSeries=IncomeStatementDA.GetProfitMarginMaxAsOf(symbol,currentPrice.Date,IncomeStatement.PeriodType.Quarterly); + if(null==profitMarginTimeSeries||profitMarginTimeSeries.Count<3) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason="Trend#10 Violation : No Profit Margin series to perform check."; + return cmtCandidate; + } + profitMarginTimeSeries=new TimeSeriesCollection(profitMarginTimeSeries.Take(3).ToList()); + minDate=profitMarginTimeSeries.Min(x => x.AsOf); + maxDate=profitMarginTimeSeries.Max(x => x.AsOf); + values=profitMarginTimeSeries.ToFloat(); + values=Numerics.Reverse(ref values); + profitMarginSlope=Numerics.Slope(values); + if(profitMarginSlope<=0) + { + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason="Trend#10 Violation : profitMarginSlope<=0."; + return cmtCandidate; + } + } + +// Calculate the Score + prices.Reverse(); // Reverse the series here. + double[] logPrices=null; + logPrices=new double[prices.Count]; + for(int index=0;indexcmtParams.MaxBeta) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("Beta for {0} exceeds maximum allowed. Candidate beta {1}, Max Beta:{2}",symbol,cmtCandidate.Beta,cmtParams.MaxBeta); + return cmtCandidate; + } + cmtCandidate.SharpeRatio=SharpeRatioGenerator.GenerateSharpeRatio(cmtCandidate.Symbol,tradeDate); + if(double.IsNaN(cmtCandidate.SharpeRatio)) + { + cmtCandidate.Violation=true; + cmtCandidate.Reason=String.Format("Unable to calculate Sharpe Ratio for {0}",symbol); + return cmtCandidate; + } + return cmtCandidate; + } + catch(Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString())); + cmtCandidate.Violation=true; + cmtCandidate.Symbol=symbol; + cmtCandidate.Reason=exception.ToString(); + return cmtCandidate; + } + } + private static bool HasReturnViolation(float[] dailyReturns,double dailyReturnLimit) + { + foreach(float dailyReturn in dailyReturns) if(Math.Abs(dailyReturn)>dailyReturnLimit) return true; + return false; + } + } +} + diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTGeneratorResult.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTGeneratorResult.cs new file mode 100755 index 0000000..43ec68f --- /dev/null +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTGeneratorResult.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MarketData.Generator.CMTrend +{ + public class CMTGeneratorResult + { + public CMTGeneratorResult() + { + Success=false; + CMTCandidates=new CMTCandidates(); + CMTCandidatesWithViolation=new CMTCandidates(); + Messages=new List(); + } + public CMTCandidates CMTCandidates { get; set; } + public CMTCandidates CMTCandidatesWithViolation { get; set; } + public bool Success { get; set; } + public List Messages { get; set; } + public String LastMessage { get { if(null==Messages||0==Messages.Count) return ""; else return Messages[0]; } } + } +} \ No newline at end of file diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTMACDIndicator.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTMACDIndicator.cs new file mode 100755 index 0000000..34d2e67 --- /dev/null +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTMACDIndicator.cs @@ -0,0 +1,31 @@ +using MarketData.Cache; +using MarketData.MarketDataModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MarketData.Generator.CMTrend +{ + public class CMTMACDIndicator + { + private CMTMACDIndicator() + { + } + public static bool IsMACDDowntrend(DateTime tradeDate,String symbol,CMTParams parameters) + { + Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,tradeDate,parameters.MACDSignalDays*3); + if(null==prices||!prices[0].Date.Date.Equals(tradeDate.Date)) return true; + MACDSetup macdSetup=new MACDSetup(parameters.MACDSetup); + MACDSignals macdSignals=MACDGenerator.GenerateMACD(prices,macdSetup); + Signals signalsMACD=SignalGenerator.GenerateSignals(macdSignals); + signalsMACD=new Signals(signalsMACD.Take(parameters.MACDSignalDays).ToList()); + int weakSellSignalCount=(from Signal signal in signalsMACD where signal.IsWeakSell() select signal).Count(); + int strongSellSignalCount=(from Signal signal in signalsMACD where signal.IsStrongSell() select signal).Count(); + if(parameters.MACDRejectWeakSells&&weakSellSignalCount>0) return true; + if(parameters.MACDRejectStrongSells&&strongSellSignalCount>0) return true; + return false; + } + } +} diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTParams.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTParams.cs new file mode 100755 index 0000000..26eb099 --- /dev/null +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTParams.cs @@ -0,0 +1,376 @@ +using MarketData.Utils; +using System; +using System.Collections.Generic; + +namespace MarketData.Generator.CMTrend +{ + public class CMTParams + { + public CMTParams() + { + SuspendTrading=false; // If this flag is set to true then no new positions will be opned. + UsePriceSlopeIndicator=true; // If this flag is set to true then enforce positive slope on prices over UserPriceSlopeIndicatorDays + UsePriceSlopeIndicatorDays=252; // The number of pricing days to use for the slope. + BetaMonths=6; // The number of months to use for Beta + AnalysisDate=DateTime.Now.Date; // The analysis date of the run + MarketCapLowerLimit=500000000; // MarketCap lower limit 50,0000,000 + TradeDate=DateTime.Now; // The current trade date + SidewaysDetection=false; // Detect stock that are going nowhere. If we've held the stock for SidewaysAfterDays AND we've never adjusted the stop AND we can break even THEN we sell. + SidewaysAfterDays=30; // Sideways detection days. + MaxDailyPositions=3; // This is the maximum number of positions to pick up per analysis date. The default is 3 + MaxOpenPositions=3; // This is the maximum number of open positions. I have tested this with 3 and had good results. + NoTradeSymbols="CLCT,PRSC,CMD,STAY,GBTC,YOKU,PNY,RFMD,ASAZY,USMO,VNR,STB,XIV,SYNT"; // ASAZY came up as candidate during 3/30 run but not available on Robinhood + OnlyTradeSymbols=""; // This should be a comma separated list of symbols which would serve as the universe of symbols to trade. If null or empty then we trade everything in security master + InitialCash=10000; // The initial cash + TotalRiskPercentDecimal=.05; // Total Risk of Initial Cash. The default is .02. I've been testing with .05 + PositionRiskPercentDecimal=.12; // Risk per position - This will determine where the stop is placed. The default is .12 + CheckOutliersInReturnStream=true; // If enabled then we check the return and ensure that no daily return exceeds DailyReturnLimit + DailyReturnLimit=.25; // If any single day return in the price stream exceeds this amount we toss it out. The default is .25 + MinRSI=70; // The minimum RSI in order to consider the candidate. Default is 70 + MinDaysBetweenReholding=30; // After selling a security we must wait this number of days before acting on a new buy signal . The default is 15 + EquityOnly=false; // If set to true then ETFs are excluded + MinPercentReturnProximityTo52WeekHigh=30; // The minimum acceptable percent proximity to the 52 week high. The default is within 30 percent (i.e.) current price can be below the 52 week high by this margin. The larger the number , the wider the margin + MinPercentReturnOver52WeekLow=80; // The minimum acceptable percent return over the 52 week low. Marc recommends 80,90,100,200,300 + ProfitMarginCheck=true; // If TRUE then profit margin time series must be present and slope must be increasing + EPSCheck=true; // If TRUE then eps time series must be present and slope must be increasing + LiquidityCheck=true; // If TRUE then the price history is checked against MinVolume + MinVolume=1000; // The minimum day volume in order to consider a security. This is used if LiquidityCheck is TRUE. Default is 5000 + DMA200Horizon=15; // Number of days to examine when evaluating the slope of 200 day moving averages. Default is 20 days of DMA200's + MinDaysBetweenStopAdjustments=30; // We won't adjust a stop if the number of days between previous adjustment is less than this. Default is 60 + MinDaysBetweenInitialStopAdjustment=5; // We won't change the initial stop until this number of days has elapsed. Default is 60. + MaxPricingExceptions=3; // This is the pricing exception limit. If we have this many of exceptions then we will sell the security at the last known good price. + UseMarketIndicator=true; // If true then we gauge the market based upon the benchmark's moving average + Benchmark="SPY"; // This is the benchmark security + BenchmarkMovingAverageDays=200; // If the latest benchmark Close is below DMA(BenchmarkMovingAverageDays) then we do not purchase new securities. I tested this with 200 and then with 55. 55 was 56% better over the period tested which was 01/06/2019-05/07/2021. so I am setting this to 100.. just because. + BenchmarkMovingAverageHorizon=5; // This is the horizon for the benchmark indicator. it must be trading above BenchmarkMovingAverageDays for BenchmarkMovingAverageHorizon days + UseMarketIndicatorVolatility=true; // If this is true then we detect volatile market by examing lower L band breaks on the ~VIX + UseMarketIndicatorVolatilityHorizon=60; // The horizon over which to examine lower L band breaks in ~VIX + UseMarketIndicatorVolatilityBenchmark="^VIX"; // The benchmark to use for volatility + UseStopLimitScaling=true; // When set to true this will scale (tighten) the stop limit as time progresses based upon an anticipated holding period of StopLimitScalingDays. + StopLimitScalingType="AverageTrueRange"; // Acceptable types are "AverageTrueRange". + StopLimitScalingVolatilityDays=30; // StopLimitScalingVolatilityDays=5 The StopLimitScaling takes volatility into account. This parameter specifies how many days of pricing to use for the volatility calculation. + EvaluateStopOnUpTrend=false; // If set to true then the stop limit is only evaluated if prices are trending up. + SellOnDMABreak=true; // If true then we look lok for breaks of all DMAs listed under DMABreak + DMABreakValues="200"; // The defaut value is 200. This can be a comma separated list. For instance 50,100,200 + DMABreakForceBreak=false; // If this flag is set to true then we will sell a security on DMA break even if it means taking a loss on the position. + EntryType="OverExtended,MVP,PriceTrend,VolumeTrend"; // The entry type indictor. NarrowRange, Swing, MACD, PriceTRend, VolumeTrend, ChannelBreakout. Default is "MVP,NarrowRange,MACD,PriceTrend,VolumeTrend". + PriceTrendDays=20; // If PriceTrend is in the EntryType list then We want to see a series of higher highs and higher lows. This is the horizon for those observations. Default is 10. + EntryHorizon=30; // For the NarrowRange entry indicator this is the number of days to consider + MACDSetup="(12,26,9)"; // setup for MACD Entry. Default is (12,26,9) + MACDSignalDays=5; // Number of signal days to consider. Default is 12 + MACDRejectStrongSells=true; // MACD reject. Default is false. **Used only if MACD is in the EntryType list + MACDRejectWeakSells=true; // MACD reject. Default is true. **Used only if MACD is in the EntryType list + VolumeTrendDays=10; // Volume trend can be used as an entry indicator. The default value is 10 days of increasing volume + CandidateExpiryDays=180; // Candidates will be kept in the candidate pool for entry search for this many days after which they will be removed and no longer available for consideration + ChannelBreakoutHorizon=40; // Horizon for ChannelBreakout entry + UseOverExtendedIndicatorDays=45; // 10 This is the number of days of history to scan for OverExtension detetction. 10 gives best results in backtest + UseOverExtendedIndicatorViolationThreshhold=1; // This is the number of items that constitute an upper band break. (i.e.) if this is set to 1 then a single band break is a violation... if 2 then >=2 band breaks are a violation etc., 1.00 gives the best results in backtest + UseOverExtendedIndicatorMarginPercent=1; // Add this in so we can control the margin. The best value is 1.00 from backtest results + MaxBeta=10.00; // Candidates with Beta that exceed this are rejected. + UseMaxBeta=false; // Utilize the MaxBeta filter + UseProfitMaximization=true; // Maximize profits when setting stop limits. What this does is to instead of using 3*ATR(20) it will use UseProfitMaximizationATRMultiplier*ATR(20) to set the stop limit for R's greater than or equal to UseProfitMaximizationRs + UseProfitMaximizationExpression="R_THRESSHOLD=4;MAX_ATR=3;MULTIPLIER=MAX_ATR;IF(RMultiple>=R_THRESSHOLD){MULTIPLIER=1.2;}"; // The default ATR multiplier is 3 which will maintain a wider gap. 1.2 will follow more closely + UseTradeOnlySectors=false; // If set to true then only consider companies in specified sectors + UseTradeOnlySectorsSectors="Healthcare,Technology,Basic Materials,Consumer Defensive,Industrials"; + } + public bool SuspendTrading{get;set;} + public bool UsePriceSlopeIndicator{get;set;} + public int UsePriceSlopeIndicatorDays{get;set;} + public bool UseMarketIndicatorVolatility{get;set;} + public int UseMarketIndicatorVolatilityHorizon{get;set;} + public string UseMarketIndicatorVolatilityBenchmark{get;set;} + public int BetaMonths { get; set; } + public DateTime AnalysisDate { get; set; } + public DateTime TradeDate { get; set; } + public double MarketCapLowerLimit { get; set; } + public bool SidewaysDetection { get; set; } + public int SidewaysAfterDays { get; set; } + public int PriceTrendDays { get; set; } + public bool CheckOutliersInReturnStream { get; set; } + public double DailyReturnLimit { get; set; } + public int MaxDailyPositions { get; set; } + public int MaxOpenPositions { get; set; } + public String NoTradeSymbols { get; set; } + public String OnlyTradeSymbols { get; set; } + public double MinRSI { get; set; } + public List NoTradeSymbolsList { get { return null==NoTradeSymbols?null:Utility.ToList(NoTradeSymbols); } } + public List OnlyTradeSymbolsList { get { return null==OnlyTradeSymbols?null:Utility.ToList(OnlyTradeSymbols); } } + public double InitialCash { get; set; } + public double TotalRiskPercentDecimal { get; set; } + public double PositionRiskPercentDecimal { get; set; } + public bool EquityOnly { get; set; } + public double MinPercentReturnProximityTo52WeekHigh { get; set; } + public double MinPercentReturnOver52WeekLow { get; set; } + public bool ProfitMarginCheck { get; set; } + public bool EPSCheck { get; set; } + public int MinDaysBetweenReholding { get; set; } + public bool LiquidityCheck { get; set; } + public int MinVolume { get; set; } + public int DMA200Horizon { get; set; } + public int MinDaysBetweenStopAdjustments { get; set; } + public int MinDaysBetweenInitialStopAdjustment { get; set; } + public int MaxPricingExceptions { get; set; } + public String MACDSetup { get; set; } + public int MACDSignalDays { get; set; } + public bool MACDRejectStrongSells { get; set; } + public bool MACDRejectWeakSells { get; set; } + public bool UseMarketIndicator { get; set; } + public String Benchmark { get; set; } + public int BenchmarkMovingAverageDays { get; set; } + public int BenchmarkMovingAverageHorizon { get; set; } + public bool UseStopLimitScaling { get; set; } + public String StopLimitScalingType { get; set; } + public int StopLimitScalingVolatilityDays { get; set; } + public bool SellOnDMABreak { get; set; } + public String DMABreakValues { get; set; } + public bool DMABreakForceBreak { get; set; } + public List DMABreakValuesCollection { get { return (!SellOnDMABreak)||(null==DMABreakValues)?new List():Utility.ToList(DMABreakValues); } } + public String EntryType { get; set; } + public List EntryTypesCollection { get { return null==EntryType?new List():Utility.ToList(EntryType); } } + public int EntryHorizon { get; set; } + public int CandidateExpiryDays { get; set; } + public int VolumeTrendDays { get; set; } + public int ChannelBreakoutHorizon { get; set; } + public int UseOverExtendedIndicatorDays { get; set; } + public int UseOverExtendedIndicatorViolationThreshhold { get; set; } + public double UseOverExtendedIndicatorMarginPercent { get; set; } + public bool UseMaxBeta { get; set; } + public double MaxBeta { get; set; } + public bool UseProfitMaximization{get;set;} + public string UseProfitMaximizationExpression{get;set;} + public bool UseTradeOnlySectors{get;set;} + public String UseTradeOnlySectorsSectors{get;set;} + public bool EvaluateStopOnUpTrend { get; set; } + + public void DisplayHeader() + { + MDTrace.WriteLine(LogLevel.DEBUG,"Setting,Value"); + } + public NVPCollection ToNVPCollection() + { + NVPCollection nvpCollection=new NVPCollection(); + nvpCollection.Add(new NVP("SuspendTrading",SuspendTrading.ToString())); + nvpCollection.Add(new NVP("UsePriceSlopeIndicator",UsePriceSlopeIndicator.ToString())); + nvpCollection.Add(new NVP("UsePriceSlopeIndicatorDays",UsePriceSlopeIndicatorDays.ToString())); + nvpCollection.Add(new NVP("AnalysisDate",AnalysisDate.ToShortDateString())); + nvpCollection.Add(new NVP("BetaMonths",BetaMonths.ToString())); + nvpCollection.Add(new NVP("TradeDate",TradeDate.ToShortDateString())); + nvpCollection.Add(new NVP("MarketCapLowerLimit",MarketCapLowerLimit.ToString())); + nvpCollection.Add(new NVP("SidewaysDetection",SidewaysDetection.ToString())); + nvpCollection.Add(new NVP("SidewaysAfterDays",SidewaysAfterDays.ToString())); + nvpCollection.Add(new NVP("PriceTrendDays",PriceTrendDays.ToString())); + nvpCollection.Add(new NVP("CheckOutliersInReturnStream",CheckOutliersInReturnStream.ToString())); + nvpCollection.Add(new NVP("DailyReturnLimit",DailyReturnLimit.ToString())); + nvpCollection.Add(new NVP("MaxDailyPositions",MaxDailyPositions.ToString())); + nvpCollection.Add(new NVP("MaxOpenPositions",MaxOpenPositions.ToString())); + nvpCollection.Add(new NVP("NoTradeSymbols",NoTradeSymbols.ToString())); + nvpCollection.Add(new NVP("OnlyTradeSymbols",OnlyTradeSymbols.ToString())); + nvpCollection.Add(new NVP("MinRSI",MinRSI.ToString())); + nvpCollection.Add(new NVP("InitialCash",InitialCash.ToString())); + nvpCollection.Add(new NVP("TotalRiskPercentDecimal",TotalRiskPercentDecimal.ToString())); + nvpCollection.Add(new NVP("PositionRiskPercentDecimal",PositionRiskPercentDecimal.ToString())); + nvpCollection.Add(new NVP("EquityOnly",EquityOnly.ToString())); + nvpCollection.Add(new NVP("MinPercentReturnProximityTo52WeekHigh",MinPercentReturnProximityTo52WeekHigh.ToString())); + nvpCollection.Add(new NVP("MinPercentReturnOver52WeekLow",MinPercentReturnOver52WeekLow.ToString())); + nvpCollection.Add(new NVP("ProfitMarginCheck",ProfitMarginCheck.ToString())); + nvpCollection.Add(new NVP("EPSCheck",EPSCheck.ToString())); + nvpCollection.Add(new NVP("MinDaysBetweenReholding",MinDaysBetweenReholding.ToString())); + nvpCollection.Add(new NVP("LiquidityCheck",LiquidityCheck.ToString())); + nvpCollection.Add(new NVP("MinVolume",MinVolume.ToString())); + nvpCollection.Add(new NVP("DMA200Horizon",DMA200Horizon.ToString())); + nvpCollection.Add(new NVP("MinDaysBetweenStopAdjustments",MinDaysBetweenStopAdjustments.ToString())); + nvpCollection.Add(new NVP("MinDaysBetweenInitialStopAdjustment",MinDaysBetweenInitialStopAdjustment.ToString())); + nvpCollection.Add(new NVP("MaxPricingExceptions",MaxPricingExceptions.ToString())); + nvpCollection.Add(new NVP("MACDSetup",MACDSetup.ToString())); + nvpCollection.Add(new NVP("MACDSignalDays",MACDSignalDays.ToString())); + nvpCollection.Add(new NVP("MACDRejectStrongSells",MACDRejectStrongSells.ToString())); + nvpCollection.Add(new NVP("MACDRejectWeakSells",MACDRejectWeakSells.ToString())); + nvpCollection.Add(new NVP("UseMarketIndicator",UseMarketIndicator.ToString())); + nvpCollection.Add(new NVP("Benchmark",Benchmark.ToString())); + nvpCollection.Add(new NVP("BenchmarkMovingAverageDays",BenchmarkMovingAverageDays.ToString())); + nvpCollection.Add(new NVP("BenchmarkMovingAverageHorizon",BenchmarkMovingAverageHorizon.ToString())); + nvpCollection.Add(new NVP("UseMarketIndicatorVolatility",UseMarketIndicatorVolatility.ToString())); + nvpCollection.Add(new NVP("UseMarketIndicatorVolatilityHorizon",UseMarketIndicatorVolatilityHorizon.ToString())); + nvpCollection.Add(new NVP("UseMarketIndicatorVolatilityBenchmark",UseMarketIndicatorVolatilityBenchmark.ToString())); + nvpCollection.Add(new NVP("UseStopLimitScaling",UseStopLimitScaling.ToString())); + nvpCollection.Add(new NVP("StopLimitScalingType",StopLimitScalingType.ToString())); + nvpCollection.Add(new NVP("StopLimitScalingVolatilityDays",StopLimitScalingVolatilityDays.ToString())); + nvpCollection.Add(new NVP("SellOnDMABreak",SellOnDMABreak.ToString())); + nvpCollection.Add(new NVP("DMABreakValues",DMABreakValues.ToString())); + nvpCollection.Add(new NVP("DMABreakForceBreak",DMABreakForceBreak.ToString())); + nvpCollection.Add(new NVP("EntryType",EntryType.ToString())); + nvpCollection.Add(new NVP("EntryHorizon",EntryHorizon.ToString())); + nvpCollection.Add(new NVP("CandidateExpiryDays",CandidateExpiryDays.ToString())); + nvpCollection.Add(new NVP("VolumeTrendDays",VolumeTrendDays.ToString())); + nvpCollection.Add(new NVP("ChannelBreakoutHorizon",ChannelBreakoutHorizon.ToString())); + nvpCollection.Add(new NVP("UseOverExtendedIndicatorDays",UseOverExtendedIndicatorDays.ToString())); + nvpCollection.Add(new NVP("UseOverExtendedIndicatorViolationThreshhold",UseOverExtendedIndicatorViolationThreshhold.ToString())); + nvpCollection.Add(new NVP("UseOverExtendedIndicatorMarginPercent",UseOverExtendedIndicatorMarginPercent.ToString())); + nvpCollection.Add(new NVP("MaxBeta",MaxBeta.ToString())); + nvpCollection.Add(new NVP("UseMaxBeta",UseMaxBeta.ToString())); + nvpCollection.Add(new NVP("UseProfitMaximization",UseProfitMaximization.ToString())); + nvpCollection.Add(new NVP("UseProfitMaximizationExpression",UseProfitMaximizationExpression.ToString())); + nvpCollection.Add(new NVP("UseTradeOnlySectors",UseTradeOnlySectors.ToString())); + nvpCollection.Add(new NVP("UseTradeOnlySectorsSectors",UseTradeOnlySectorsSectors.ToString())); + nvpCollection.Add(new NVP("EvaluateStopOnUpTrend",EvaluateStopOnUpTrend.ToString())); + return nvpCollection; + } + public static CMTParams FromNVPCollection(NVPCollection nvpCollection) + { + CMTParams cmtParams=new CMTParams(); + NVPDictionary nvpDictionary=nvpCollection.ToDictionary(); + + if(nvpDictionary.ContainsKey("SuspendTrading")) + { + cmtParams.SuspendTrading=nvpDictionary["SuspendTrading"].Get(); + } + if(nvpDictionary.ContainsKey("UsePriceSlopeIndicator")) + { + cmtParams.UsePriceSlopeIndicator=nvpDictionary["UsePriceSlopeIndicator"].Get(); + cmtParams.UsePriceSlopeIndicatorDays=nvpDictionary["UsePriceSlopeIndicatorDays"].Get(); + } + if(nvpDictionary.ContainsKey("UseMarketIndicatorVolatility")) + { + cmtParams.UseMarketIndicatorVolatility=nvpDictionary["UseMarketIndicatorVolatility"].Get(); + cmtParams.UseMarketIndicatorVolatilityHorizon=nvpDictionary["UseMarketIndicatorVolatilityHorizon"].Get(); + cmtParams.UseMarketIndicatorVolatilityBenchmark=nvpDictionary["UseMarketIndicatorVolatilityBenchmark"].Get(); + } + cmtParams.AnalysisDate=nvpDictionary["AnalysisDate"].Get(); + cmtParams.BetaMonths=nvpDictionary["BetaMonths"].Get(); + cmtParams.TradeDate=nvpDictionary["TradeDate"].Get(); + cmtParams.MarketCapLowerLimit=nvpDictionary["MarketCapLowerLimit"].Get(); + cmtParams.SidewaysDetection=nvpDictionary["SidewaysDetection"].Get(); + cmtParams.SidewaysAfterDays=nvpDictionary["SidewaysAfterDays"].Get(); + cmtParams.PriceTrendDays=nvpDictionary["PriceTrendDays"].Get(); + cmtParams.CheckOutliersInReturnStream=nvpDictionary["CheckOutliersInReturnStream"].Get(); + cmtParams.DailyReturnLimit=nvpDictionary["DailyReturnLimit"].Get(); + cmtParams.MaxDailyPositions=nvpDictionary["MaxDailyPositions"].Get(); + cmtParams.MaxOpenPositions=nvpDictionary["MaxOpenPositions"].Get(); + cmtParams.NoTradeSymbols=nvpDictionary["NoTradeSymbols"].Get(); + if(nvpDictionary.ContainsKey("OnlyTradeSymbols")) cmtParams.OnlyTradeSymbols=nvpDictionary["OnlyTradeSymbols"].Get(); + cmtParams.MinRSI=nvpDictionary["MinRSI"].Get(); + cmtParams.InitialCash=nvpDictionary["InitialCash"].Get(); + cmtParams.TotalRiskPercentDecimal=nvpDictionary["TotalRiskPercentDecimal"].Get(); + cmtParams.PositionRiskPercentDecimal=nvpDictionary["PositionRiskPercentDecimal"].Get(); + cmtParams.EquityOnly=nvpDictionary["EquityOnly"].Get(); + cmtParams.MinPercentReturnProximityTo52WeekHigh=nvpDictionary["MinPercentReturnProximityTo52WeekHigh"].Get(); + cmtParams.MinPercentReturnOver52WeekLow=nvpDictionary["MinPercentReturnOver52WeekLow"].Get(); + cmtParams.ProfitMarginCheck=nvpDictionary["ProfitMarginCheck"].Get(); + cmtParams.EPSCheck=nvpDictionary["EPSCheck"].Get(); + cmtParams.MinDaysBetweenReholding=nvpDictionary["MinDaysBetweenReholding"].Get(); + cmtParams.LiquidityCheck=nvpDictionary["LiquidityCheck"].Get(); + cmtParams.MinVolume=nvpDictionary["MinVolume"].Get(); + cmtParams.DMA200Horizon=nvpDictionary["DMA200Horizon"].Get(); + cmtParams.MinDaysBetweenStopAdjustments=nvpDictionary["MinDaysBetweenStopAdjustments"].Get(); + cmtParams.MinDaysBetweenInitialStopAdjustment=nvpDictionary["MinDaysBetweenInitialStopAdjustment"].Get(); + cmtParams.MaxPricingExceptions=nvpDictionary["MaxPricingExceptions"].Get(); + cmtParams.MACDSetup=nvpDictionary["MACDSetup"].Get(); + cmtParams.MACDSignalDays=nvpDictionary["MACDSignalDays"].Get(); + cmtParams.MACDRejectStrongSells=nvpDictionary["MACDRejectStrongSells"].Get(); + cmtParams.MACDRejectWeakSells=nvpDictionary["MACDRejectWeakSells"].Get(); + cmtParams.UseMarketIndicator=nvpDictionary["UseMarketIndicator"].Get(); + cmtParams.Benchmark=nvpDictionary["Benchmark"].Get(); + cmtParams.BenchmarkMovingAverageDays=nvpDictionary["BenchmarkMovingAverageDays"].Get(); + cmtParams.BenchmarkMovingAverageHorizon=nvpDictionary["BenchmarkMovingAverageHorizon"].Get(); + cmtParams.UseStopLimitScaling=nvpDictionary["UseStopLimitScaling"].Get(); + if(nvpDictionary.ContainsKey("StopLimitScalingType")) cmtParams.StopLimitScalingType=nvpDictionary["StopLimitScalingType"].Get(); + cmtParams.StopLimitScalingVolatilityDays=nvpDictionary["StopLimitScalingVolatilityDays"].Get(); + cmtParams.SellOnDMABreak=nvpDictionary["SellOnDMABreak"].Get(); + cmtParams.DMABreakValues=nvpDictionary["DMABreakValues"].Get(); + if(nvpDictionary.ContainsKey("DMABreakForceBreak")) cmtParams.DMABreakForceBreak=nvpDictionary["DMABreakForceBreak"].Get(); + cmtParams.EntryType=nvpDictionary["EntryType"].Get(); + cmtParams.EntryHorizon=nvpDictionary["EntryHorizon"].Get(); + cmtParams.CandidateExpiryDays=nvpDictionary["CandidateExpiryDays"].Get(); + if(nvpDictionary.ContainsKey("VolumeTrendDays")) cmtParams.VolumeTrendDays=nvpDictionary["VolumeTrendDays"].Get(); + if(nvpDictionary.ContainsKey("ChannelBreakoutHorizon")) cmtParams.ChannelBreakoutHorizon=nvpDictionary["ChannelBreakoutHorizon"].Get(); + cmtParams.UseOverExtendedIndicatorDays=nvpDictionary["UseOverExtendedIndicatorDays"].Get(); + cmtParams.UseOverExtendedIndicatorViolationThreshhold=nvpDictionary["UseOverExtendedIndicatorViolationThreshhold"].Get(); + cmtParams.UseOverExtendedIndicatorMarginPercent=nvpDictionary["UseOverExtendedIndicatorMarginPercent"].Get(); + cmtParams.UseMaxBeta=nvpDictionary["UseMaxBeta"].Get(); + cmtParams.MaxBeta=nvpDictionary["MaxBeta"].Get(); + if(nvpDictionary.ContainsKey("UseProfitMaximization")) + { + cmtParams.UseProfitMaximization=nvpDictionary["UseProfitMaximization"].Get(); + cmtParams.UseProfitMaximizationExpression=nvpDictionary["UseProfitMaximizationExpression"].Get(); + } + if(nvpDictionary.ContainsKey("UseTradeOnlySectors")) + { + cmtParams.UseTradeOnlySectors=nvpDictionary["UseTradeOnlySectors"].Get(); + cmtParams.UseTradeOnlySectorsSectors=nvpDictionary["UseTradeOnlySectorsSectors"].Get(); + } + if(nvpDictionary.ContainsKey("EvaluateStopOnUpTrend")) + { + cmtParams.EvaluateStopOnUpTrend=nvpDictionary["EvaluateStopOnUpTrend"].Get(); + } + return cmtParams; + } + public void DisplayConfiguration() + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SuspendTrading,{0}",SuspendTrading)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UsePriceSlopeIndicator,{0}",UsePriceSlopeIndicator)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UsePriceSlopeIndicatorDays,{0}",UsePriceSlopeIndicatorDays)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("AnalysisDate,{0}",AnalysisDate)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate,{0}",TradeDate)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SidewaysDetection,{0}",SidewaysDetection)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SidewaysAfterDays,{0}",SidewaysAfterDays)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("PriceTrendDays,{0}",PriceTrendDays)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxDailyPositions,{0}",MaxDailyPositions)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxOpenPositions,{0}",MaxOpenPositions)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("NoTradeSymbols,{0}",NoTradeSymbols)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("OnlyTradeSymbols,{0}",OnlyTradeSymbols)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("InitialCash,{0}",InitialCash)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TotalRiskPercentDecimal,{0}",TotalRiskPercentDecimal)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("PositionRiskPercentDecimal,{0}",PositionRiskPercentDecimal)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CheckOutliersInReturnStream,{0}",CheckOutliersInReturnStream)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DailyReturnLimit,{0}",DailyReturnLimit)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinRSI,{0}",MinRSI)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinDaysBetweenReholding,{0}",MinDaysBetweenReholding)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("EquityOnly,{0}",EquityOnly)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinPercentReturnProximityTo52WeekHigh,{0}",MinPercentReturnProximityTo52WeekHigh)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinPercentReturnOver52WeekLow,{0}",MinPercentReturnOver52WeekLow)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("ProfitMarginCheck,{0}",ProfitMarginCheck)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("EPSCheck,{0}",EPSCheck)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("LiquidityCheck,{0}",LiquidityCheck)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinVolume,{0}",MinVolume)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMA200Horizon,{0}",DMA200Horizon)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinDaysBetweenStopAdjustments,{0}",MinDaysBetweenStopAdjustments)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MinDaysBetweenInitialStopAdjustment,{0}",MinDaysBetweenInitialStopAdjustment)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxPricingExceptions,{0}",MaxPricingExceptions)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MACDSetup,{0}",MACDSetup)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MACDSignalDays,{0}",MACDSignalDays)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MACDRejectStrongSells,{0}",MACDRejectStrongSells)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MACDRejectWeakSells,{0}",MACDRejectWeakSells)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMarketIndicator,{0}",UseMarketIndicator)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Benchmark,{0}",Benchmark)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BenchmarkMovingAverageDays,{0}",BenchmarkMovingAverageDays)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BenchmarkMovingAverageHorizon,{0}",BenchmarkMovingAverageHorizon)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMarketIndicatorVolatility,{0}",UseMarketIndicatorVolatility)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMarketIndicatorVolatilityHorizon,{0}",UseMarketIndicatorVolatilityHorizon)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMarketIndicatorVolatilityBenchmark,{0}",UseMarketIndicatorVolatilityBenchmark)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseStopLimitScaling,{0}",UseStopLimitScaling)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StopLimitScalingType,{0}",StopLimitScalingType)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StopLimitScalingVolatilityDays,{0}",StopLimitScalingVolatilityDays)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SellOnDMABreak,{0}",SellOnDMABreak)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMABreakValues,{0}",DMABreakValues)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("DMABreakForceBreak,{0}",DMABreakForceBreak)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("EntryType,{0}",EntryType)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("EntryHorizon,{0}",EntryHorizon)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CandidateExpiryDays,{0}",CandidateExpiryDays)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("VolumeTrendDays,{0}",VolumeTrendDays)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("ChannelBreakoutHorizon,{0}",ChannelBreakoutHorizon)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseOverExtendedIndicatorDays,{0}",UseOverExtendedIndicatorDays.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseOverExtendedIndicatorViolationThreshhold,{0}",UseOverExtendedIndicatorViolationThreshhold.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseOverExtendedIndicatorMarginPercent,{0}",UseOverExtendedIndicatorMarginPercent.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMaxBeta,{0}",UseMaxBeta.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxBeta,{0}",MaxBeta.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MarketCapLowerLimit,{0}",MarketCapLowerLimit.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BetaMonths,{0}",BetaMonths.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseProfitMaximization,{0}",UseProfitMaximization.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseProfitMaximizationExpression,{0}",UseProfitMaximizationExpression.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseTradeOnlySectors,{0}",UseTradeOnlySectors.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseTradeOnlySectorsSectors,{0}",UseTradeOnlySectorsSectors.ToString())); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("EvaluateStopOnUpTrend,{0}",EvaluateStopOnUpTrend.ToString())); + } + } +} + diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTPositions.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTPositions.cs new file mode 100755 index 0000000..66b7bb7 --- /dev/null +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTPositions.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using MarketData.MarketDataModel; +using MarketData.Utils; +using System.Linq; +using MarketData.Generator.Interface; + +namespace MarketData.Generator.CMTrend +{ + public class Position : IPosition + { + public Position() + { + CurrentPrice=double.NaN; + } + public Position(Position position) + { + Symbol=position.Symbol; + PurchaseDate=position.PurchaseDate; + SellDate=position.SellDate; + Shares=position.Shares; + PurchasePrice=position.PurchasePrice; + CurrentPrice=position.CurrentPrice; + R=position.R; + C=position.C; + PositionRiskPercentDecimal=position.PositionRiskPercentDecimal; + InitialStopLimit=position.InitialStopLimit; + TrailingStopLimit=position.TrailingStopLimit; + Volume=position.Volume; + Volatility=position.Volatility; + LastStopAdjustment=position.LastStopAdjustment; + Comment=position.Comment; + } + public static Position Clone(Position position) + { + Position clonedPosition=new Position(); + clonedPosition.Symbol=position.Symbol; + clonedPosition.PurchaseDate=position.PurchaseDate; + clonedPosition.SellDate=position.SellDate; + clonedPosition.Shares=position.Shares; + clonedPosition.PurchasePrice=position.PurchasePrice; + clonedPosition.CurrentPrice=position.CurrentPrice; + clonedPosition.R=position.R; + clonedPosition.C=position.C; + clonedPosition.PositionRiskPercentDecimal=position.PositionRiskPercentDecimal; + clonedPosition.InitialStopLimit=position.InitialStopLimit; + clonedPosition.TrailingStopLimit=position.TrailingStopLimit; + clonedPosition.Volume=position.Volume; + clonedPosition.Volatility=position.Volatility; + clonedPosition.LastStopAdjustment=position.LastStopAdjustment; + clonedPosition.Comment=position.Comment; + return clonedPosition; + } + public String Symbol { get; set; } + public DateTime PurchaseDate { get; set; } + public DateTime SellDate { get; set; } + public double Shares { get; set; } // This is actual shares (round down P to the nearest whole number) + public double PurchasePrice { get; set; } + public double CurrentPrice { get; set; } // When the position is sold the current price will hold the sell price + public double Exposure { get { return Shares*PurchasePrice; } } // Derived + public double MarketValue { get { return Shares*CurrentPrice; } } // Derived + public double GainLoss { get { return MarketValue-Exposure; } } // Derived + public double GetGainLoss(Price currentPrice) { return (Shares*currentPrice.Close)-Exposure; } // get the gain loss based upon the given price + public double GainLossPcnt { get { return (MarketValue-Exposure)/Exposure; } } // Derived + public double PositionRiskPercentDecimal { get; set; } // (i.e.) .06 = 6% + public double R { get; set; } // PositionRiskPercentDecimal*PurchasePrice + public double C { get; set; } // AvailableCash * TotalRiskPercentDecimal + public double P { get { return C/R; } } // Derived. This is the number of shares to buy according to risk limits + public double TrailingStopLimit { get; set; } // This is the trailing stop limit. + public double InitialStopLimit { get; set; } // This is the initial stop limit. + public DateTime LastStopAdjustment { get; set; } + public double TotalRiskExposure { get { return R*Shares; } } // This is the total risk in dollars that we are exposed to + public double RMultiple { get { return (CurrentPrice-PurchasePrice)/R; } } // Derived. + public String RMultipleAsString + { + get + { + return Utility.FormatNumber(RMultiple,2,false)+"R"; + } + } // Derived. + public double Volatility { get; set; } + public long Volume { get; set; } + public String Comment { get; set; } + + public virtual NVPCollection ToNVPCollection() + { + NVPCollection nvpCollection=new NVPCollection(); + nvpCollection.Add(new NVP("Symbol",Symbol.ToString())); + nvpCollection.Add(new NVP("PurchaseDate",PurchaseDate.ToString())); + nvpCollection.Add(new NVP("SellDate",SellDate.ToString())); + nvpCollection.Add(new NVP("Shares",Shares.ToString())); + nvpCollection.Add(new NVP("PurchasePrice",PurchasePrice.ToString())); + if(!double.IsNaN(CurrentPrice)) nvpCollection.Add(new NVP("CurrentPrice",CurrentPrice.ToString())); + if(!double.IsNaN(Exposure)) nvpCollection.Add(new NVP("Exposure",Exposure.ToString())); + if(!double.IsNaN(MarketValue)) nvpCollection.Add(new NVP("MarketValue",MarketValue.ToString())); + if(!double.IsNaN(GainLoss)) nvpCollection.Add(new NVP("GainLoss",GainLoss.ToString())); + if(!double.IsNaN(GainLossPcnt)) nvpCollection.Add(new NVP("GainLossPcnt",GainLossPcnt.ToString())); + if(!double.IsNaN(PositionRiskPercentDecimal)) nvpCollection.Add(new NVP("PositionRiskDecimal",PositionRiskPercentDecimal.ToString())); + if(!double.IsNaN(R)) nvpCollection.Add(new NVP("R",R.ToString())); + if(!double.IsNaN(C)) nvpCollection.Add(new NVP("C",C.ToString())); + if(!double.IsNaN(C)) nvpCollection.Add(new NVP("P",P.ToString())); + if(!double.IsNaN(InitialStopLimit)) nvpCollection.Add(new NVP("InitialStopLimit",InitialStopLimit.ToString())); + if(!double.IsNaN(TrailingStopLimit)) nvpCollection.Add(new NVP("TrailingStopLimit",TrailingStopLimit.ToString())); + if(!double.IsNaN(TotalRiskExposure)) nvpCollection.Add(new NVP("TotalRiskExposure",TotalRiskExposure.ToString())); + if(!double.IsNaN(RMultiple)) nvpCollection.Add(new NVP("RMultiple",RMultipleAsString)); + if(!double.IsNaN(Volatility)) nvpCollection.Add(new NVP("Volatility",Volatility.ToString())); + nvpCollection.Add(new NVP("Volume",Volume.ToString())); + nvpCollection.Add(new NVP("LastStopAdjustment",LastStopAdjustment.ToString())); + if(null!=Comment) nvpCollection.Add(new NVP("Comment",Comment)); + return nvpCollection; + } + public static Position FromNVPCollection(NVPCollection nvpCollection) + { + Position position=new Position(); + NVPDictionary nvpDictionary=nvpCollection.ToDictionary(); + position.Symbol=nvpDictionary["Symbol"].Get(); + position.PurchaseDate=nvpDictionary["PurchaseDate"].Get(); + position.SellDate=nvpDictionary["SellDate"].Get(); + position.Shares=nvpDictionary["Shares"].Get(); + position.PurchasePrice=nvpDictionary["PurchasePrice"].Get(); + if(nvpDictionary.ContainsKey("CurrentPrice")) position.CurrentPrice=nvpDictionary["CurrentPrice"].Get(); + if(nvpDictionary.ContainsKey("R")) position.R=nvpDictionary["R"].Get(); + if(nvpDictionary.ContainsKey("C")) position.C=nvpDictionary["C"].Get(); + if(nvpDictionary.ContainsKey("PositionRiskDecimal")) position.PositionRiskPercentDecimal=nvpDictionary["PositionRiskDecimal"].Get(); + if(nvpDictionary.ContainsKey("InitialStopLimit")) position.InitialStopLimit=nvpDictionary["InitialStopLimit"].Get(); + if(nvpDictionary.ContainsKey("TrailingStopLimit")) position.TrailingStopLimit=nvpDictionary["TrailingStopLimit"].Get(); + if(nvpDictionary.ContainsKey("Volatility")) position.Volatility=nvpDictionary["Volatility"].Get(); + if(nvpDictionary.ContainsKey("Volume")) position.Volume=nvpDictionary["Volume"].Get(); + if(nvpDictionary.ContainsKey("LastStopAdjustment")) position.LastStopAdjustment=nvpDictionary["LastStopAdjustment"].Get(); + if(nvpDictionary.ContainsKey("Comment")) position.Comment=nvpDictionary["Comment"].Get(); + return position; + } + public void Display() + { + if(Utility.IsEpoch(SellDate)&&double.IsNaN(CurrentPrice)) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21}", + Symbol, + Shares, + Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate), + Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)), + Utility.AddQuotes(Utility.FormatCurrency(InitialStopLimit)), + Utility.AddQuotes(Utility.FormatCurrency(TrailingStopLimit)), + Utility.AddQuotes(Utility.DateTimeToStringMMHDDHYYYY(LastStopAdjustment)), + Utility.AddQuotes(Utility.FormatNumber(Volatility,2)), + Utility.AddQuotes(Utility.FormatNumber(Volume,0)), + Utility.AddQuotes(Utility.FormatCurrency(R)), + Utility.AddQuotes(Utility.FormatNumber(PositionRiskPercentDecimal,3)), + Utility.AddQuotes(Utility.FormatCurrency(C)), + Utility.AddQuotes(Utility.FormatCurrency(TotalRiskExposure)), + Utility.AddQuotes(Utility.FormatNumber(RMultiple,3)), + Utility.AddQuotes(RMultipleAsString), + Utility.IsEpoch(SellDate)?"N/A":Utility.DateTimeToStringMMHDDHYYYY(SellDate), + "N/A", + Utility.AddQuotes(Utility.FormatCurrency(Exposure)), + "N/A", + "N/A", + "N/A", + null==Comment?"":Comment + )); + } + else + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21}", + Symbol, + Shares, + Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate), + Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)), + Utility.AddQuotes(Utility.FormatCurrency(InitialStopLimit)), + Utility.AddQuotes(Utility.FormatCurrency(TrailingStopLimit)), + Utility.IsEpoch(LastStopAdjustment)?"N/A":Utility.DateTimeToStringMMHDDHYYYY(LastStopAdjustment), + Utility.AddQuotes(Utility.FormatNumber(Volatility,2)), + Utility.AddQuotes(Utility.FormatNumber(Volume,0)), + Utility.AddQuotes(Utility.FormatCurrency(R)), + Utility.AddQuotes(Utility.FormatNumber(PositionRiskPercentDecimal,3)), + Utility.AddQuotes(Utility.FormatCurrency(C)), + Utility.AddQuotes(Utility.FormatCurrency(TotalRiskExposure)), + Utility.AddQuotes(Utility.FormatNumber(RMultiple,3)), + Utility.AddQuotes(RMultipleAsString), + Utility.IsEpoch(SellDate)?"N/A":Utility.DateTimeToStringMMHDDHYYYY(SellDate), + Utility.AddQuotes(Utility.FormatCurrency(CurrentPrice)), + Utility.AddQuotes(Utility.FormatCurrency(Exposure)), + Utility.AddQuotes(Utility.FormatCurrency(MarketValue)), + Utility.AddQuotes(Utility.FormatCurrency(GainLoss)), + Utility.FormatPercent(GainLossPcnt), + null==Comment?"":Comment + )); + } + } + public static void DisplayHeader() + { + MDTrace.WriteLine(LogLevel.DEBUG,"Symbol,Shares,Purchase Date,Purchase Price,Initial Stop Limit,Trailing Stop Limit,Last Stop Adjustment,Volatility,Volume,R,PositionRiskPercentDecimal,C,TotalRiskExposure,RMultiple,RMultipleString,Sell Date,Sell Price,Exposure,Market Value,Gain Loss,Gain Loss(%),Comment"); + } + } + // **************************************************************************************************************************************************************** + public class Positions:List + { + public Positions() + { + } + public Positions(Positions positions) + { + if(null==positions) return; + foreach(Position position in positions) Add(position); + } + public Positions(List positions) + { + if(null==positions) return; + foreach(Position position in positions) Add(position); + } + public Positions(Position position) + { + if(null==position) return; + Add(position); + } + public void Add(Positions positions) + { + if(null==positions) return; + foreach(Position position in positions) Add(position); + } + public int PositionsOn(DateTime purchaseDate) + { + return this.Count(x => x.PurchaseDate.Equals(purchaseDate)); + } + public double GetExposure() + { + Positions openPositions=new Positions(this.Where(x => Utility.IsEpoch(x.SellDate)).ToList()); + return (from Position position in openPositions select position.PurchasePrice*position.Shares).Sum(); + } + public double GetMarketValue() + { + Positions closedPositions=new Positions(this.Where(x => !Utility.IsEpoch(x.SellDate)).ToList()); + if(null==closedPositions||0==closedPositions.Count) return 0.00; + return closedPositions.Select(x => x.CurrentPrice*x.Shares).Sum(); + } + public List GetSymbols() + { + return (from Position position in this select position.Symbol).Distinct().ToList(); + } + public double GetGainLoss() + { + Positions closedPositions=new Positions(this.Where(x => !Utility.IsEpoch(x.SellDate)).ToList()); + if(null==closedPositions||0==closedPositions.Count) return 0.00; + double totalMarketValue=closedPositions.Select(x => x.Shares*x.CurrentPrice).Sum(); + double totalExposure=closedPositions.Select(x => x.Shares*x.PurchasePrice).Sum(); + return totalMarketValue-totalExposure; + } + public double GetGainLossPercent() + { + Positions closedPositions=new Positions(this.Where(x => !Utility.IsEpoch(x.SellDate)).ToList()); + if(null==closedPositions||0==closedPositions.Count) return 0.00; + double totalMarketValue=closedPositions.Select(x => x.Shares*x.CurrentPrice).Sum(); + double totalExposure=closedPositions.Select(x => x.Shares*x.PurchasePrice).Sum(); + return (totalMarketValue-totalExposure)/totalExposure; + } + public NVPCollections ToNVPCollections() + { + NVPCollections nvpCollections=new NVPCollections(); + foreach(Position position in this) + { + nvpCollections.Add(position.ToNVPCollection()); + } + return nvpCollections; + } + public static Positions FromNVPCollections(NVPCollections nvpCollections) + { + Positions positions=new Positions(); + foreach(NVPCollection nvpCollection in nvpCollections) + { + positions.Add(Position.FromNVPCollection(nvpCollection)); + } + return positions; + } + public void AddFromNVPCollection(NVPCollection nvpCollection) + { + Add(Position.FromNVPCollection(nvpCollection)); + } + public void DisplayTop(int count=10) + { + Positions positions=new Positions(this.OrderByDescending(x => x.GainLossPcnt).Take(count).ToList()); + Position.DisplayHeader(); + for(int index=0;index x.GainLossPcnt).Take(count).ToList()); + Position.DisplayHeader(); + for(int index=0;index + { + public NVPCollections ToNVPCollections() + { + NVPCollections nvpCollections=new NVPCollections(); + foreach(CMTPricingException pricingException in this) + { + nvpCollections.Add(pricingException.ToNVPCollection()); + } + return nvpCollections; + } + public static CMTPricingExceptions FromNVPCollections(NVPCollections nvpCollections) + { + CMTPricingExceptions pricingExcpetions=new CMTPricingExceptions(); + foreach(NVPCollection nvpCollection in nvpCollections) + { + pricingExcpetions.Add(CMTPricingException.FromNVPCollection(nvpCollection)); + } + return pricingExcpetions; + } + public void AddFromNVPCollection(NVPCollection nvpCollection) + { + Add(CMTPricingException.FromNVPCollection(nvpCollection)); + } + } + public class CMTPricingException + { + public CMTPricingException() + { + } + public CMTPricingException(CMTPricingException pricingException) + { + this.Symbol=pricingException.Symbol; + this.ExceptionCount=pricingException.ExceptionCount; + } + public CMTPricingException(String symbol,int exceptionCount) + { + this.Symbol=symbol; + this.ExceptionCount=exceptionCount; + } + public String Symbol { get; set; } + public int ExceptionCount { get; set; } + public virtual NVPCollection ToNVPCollection() + { + NVPCollection nvpCollection=new NVPCollection(); + nvpCollection.Add(new NVP("Symbol",Symbol.ToString())); + nvpCollection.Add(new NVP("ExceptionCount",ExceptionCount.ToString())); + return nvpCollection; + } + public static CMTPricingException FromNVPCollection(NVPCollection nvpCollection) + { + CMTPricingException pricingException=new CMTPricingException(); + + NVPDictionary nvpDictionary=nvpCollection.ToDictionary(); + pricingException.Symbol=nvpDictionary["Symbol"].Get(); + pricingException.ExceptionCount=nvpDictionary["ExceptionCount"].Get(); + return pricingException; + } + } +} + diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTSessionManager.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTSessionManager.cs new file mode 100755 index 0000000..7a9253a --- /dev/null +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTSessionManager.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using MarketData.Utils; +using MarketData.Generator.Model; + +namespace MarketData.Generator.CMTrend +{ + public class CMTSessionParams + { + public DateTime LastUpdated { get; set; } + public DateTime TradeDate { get; set; } + public DateTime StartDate { get; set; } + public DateTime AnalysisDate { get; set; } + public CMTParams CMTParams { get; set; } + public ActivePositions ActivePositions { get; set; } + public Positions AllPositions { get; set; } + public CMTCandidates Candidates { get; set; } + public CMTPricingExceptions PricingExceptions { get; set; } + public StopLimits StopLimits { get; set; } + public double CashBalance { get; set; } + public double NonTradeableCash{ get; set; } + public double InitialCash { get; set; } + // This gets AllPositions+Positions + public Positions GetCombinedPositions() + { + Positions positions=new Positions(); + foreach(Position position in AllPositions) positions.Add(position); + foreach(Position position in ActivePositions) positions.Add(position); + return positions; + } + } + // ***************************************************************************** + public static class CMTSessionManager + { + public static bool SaveSession(CMTSessionParams sessionParams,String pathSessionFile) + { + try + { + if(null==pathSessionFile) return false; + pathSessionFile=GetSessionFileName(pathSessionFile); + FileStream outStream=new FileStream(pathSessionFile,FileMode.Create); + StreamWriter streamWriter=new StreamWriter(outStream); + streamWriter.WriteLine("CMTSESSIONv1.00"); + streamWriter.WriteLine((new NVP("LastUpdated",sessionParams.LastUpdated.ToString())).ToString()); + streamWriter.WriteLine((new NVP("TradeDate",sessionParams.TradeDate.ToShortDateString())).ToString()); + streamWriter.WriteLine((new NVP("StartDate",sessionParams.StartDate.ToShortDateString())).ToString()); + streamWriter.WriteLine((new NVP("AnalysisDate",sessionParams.AnalysisDate.ToShortDateString())).ToString()); + streamWriter.WriteLine((new NVP("CashBalance",sessionParams.CashBalance.ToString())).ToString()); + streamWriter.WriteLine((new NVP("NonTradeableCash",sessionParams.NonTradeableCash.ToString())).ToString()); + NVPCollection configurationCollection=sessionParams.CMTParams.ToNVPCollection(); + streamWriter.WriteLine(configurationCollection.ToString()); + + NVPCollections nvpPricingExceptionCollections=sessionParams.PricingExceptions.ToNVPCollections(); + List nvpPricingExceptionCollectionsList=nvpPricingExceptionCollections.ToList(); + streamWriter.WriteLine((new NVP("PricingExceptions",nvpPricingExceptionCollectionsList.Count.ToString())).ToString()); + foreach(String str in nvpPricingExceptionCollectionsList) streamWriter.WriteLine(str); + + NVPCollections nvpCollections=sessionParams.ActivePositions.ToNVPCollections(); + List nvpActivePositionsStringList=nvpCollections.ToList(); + streamWriter.WriteLine((new NVP("TotalActivePositions",nvpActivePositionsStringList.Count.ToString())).ToString()); + foreach(String str in nvpActivePositionsStringList) streamWriter.WriteLine(str); + + NVPCollections allPositionsCollections=sessionParams.AllPositions.ToNVPCollections(); + List nvpAllCollectionsStringList=allPositionsCollections.ToList(); + streamWriter.WriteLine((new NVP("TotalPositions",nvpAllCollectionsStringList.Count.ToString())).ToString()); + foreach(String str in nvpAllCollectionsStringList) streamWriter.WriteLine(str); + + NVPCollections allCandidatesCollections=sessionParams.Candidates.ToNVPCollections(); + List nvpSetupsCollectionsStringList=allCandidatesCollections.ToList(); + streamWriter.WriteLine((new NVP("TotalCandidates",nvpSetupsCollectionsStringList.Count.ToString())).ToString()); + foreach(String str in nvpSetupsCollectionsStringList) streamWriter.WriteLine(str); + + NVPCollections allStopLimitsCollections=sessionParams.StopLimits.ToNVPCollections(); + List nvpStopLimitsCollectionsStringList=allStopLimitsCollections.ToList(); + streamWriter.WriteLine((new NVP("TotalStopLimits",nvpStopLimitsCollectionsStringList.Count.ToString())).ToString()); + foreach(String str in nvpStopLimitsCollectionsStringList) streamWriter.WriteLine(str); + + streamWriter.Flush(); + outStream.Flush(); + streamWriter.Close(); + streamWriter.Dispose(); + outStream.Close(); + outStream.Dispose(); + return true; + } + catch(Exception exception) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception.ToString())); + return false; + } + } + public static CMTSessionParams RestoreSession(String pathSessionFile) + { + FileStream inStream=null; + StreamReader streamReader=null; + try + { + pathSessionFile=GetSessionFileName(pathSessionFile); + if(!SessionAvailable(pathSessionFile)) return null; + CMTSessionParams sessionParams=new CMTSessionParams(); + inStream=new FileStream(pathSessionFile,FileMode.Open); + streamReader=new StreamReader(inStream); + String versionInfo=streamReader.ReadLine(); + if(!versionInfo.StartsWith("CMTSESSION")) return null; + double version=double.Parse(Utility.BetweenString(versionInfo,"v",null)); + if(1.00!=version) return null; + NVP lastUpdated=new NVP(streamReader.ReadLine()); + NVP tradeDate=new NVP(streamReader.ReadLine()); + NVP startDate=new NVP(streamReader.ReadLine()); + NVP analysisDate=new NVP(streamReader.ReadLine()); + NVP cashBalance=new NVP(streamReader.ReadLine()); + NVP nonTradeableCash=new NVP(streamReader.ReadLine()); + sessionParams.LastUpdated=lastUpdated.Get(); + sessionParams.TradeDate=tradeDate.Get(); + sessionParams.StartDate=startDate.Get(); + sessionParams.AnalysisDate=analysisDate.Get(); + sessionParams.CashBalance=cashBalance.Get(); + sessionParams.NonTradeableCash=nonTradeableCash.Get(); + NVPCollection configurationCollection=new NVPCollection(streamReader.ReadLine()); + sessionParams.CMTParams=CMTParams.FromNVPCollection(configurationCollection); + + int totalPricingExceptions=(new NVP(streamReader.ReadLine())).Get(); + sessionParams.PricingExceptions=new CMTPricingExceptions(); + for(int pricingExceptionIndex=0;pricingExceptionIndex(); + sessionParams.ActivePositions=new ActivePositions(); + for(int positionIndex=0;positionIndex(); + NVPCollections nvpCollections=new NVPCollections(); + for(int positionIndex=0;positionIndex(); + sessionParams.Candidates=new CMTCandidates(); + for(int setupIndex=0;setupIndex(); + sessionParams.StopLimits=new StopLimits(); + for(int stopLimitIndex=0;stopLimitIndex symbolsHeld) + { + CMTGeneratorResult cmtGeneratorResult=new CMTGeneratorResult(); + try + { + List symbols=PricingDA.GetSymbols(); + for(int index=0;index 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 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; +// if(HolidayDA.IsMarketHoliday(currentDate)) continue; + 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())); + 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 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 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 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); + 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 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 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); + 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 symbols=PricingDA.GetSymbols(); + CMTParams cmtParams=new CMTParams(); + List violations=new List(); + List candidates=new List(); + + for(int index=0;index violationTypes=violations.Select(x => x.Reason).Distinct().ToList(); + violationTypes.Sort(); + foreach(String violationType in violationTypes) + { + List 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; + List closedPositions=new List(); + 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.Lowposition.PurchasePrice) // If the Low price is above our purchase price then re-evaluate the stop + { + EvaluateStopPrice(tradeDate,price,position); + } + } + if(0!=closedPositions.Count) + { + 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 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 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 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 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 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("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 symbolsHeld=new List(); + 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 candidatesToRemove=new List(); + 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[] 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 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].Close0||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