From 6d966c4f28d7cdd7ddb566d53998879cdb4726df Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 19 Feb 2026 09:13:15 -0500 Subject: [PATCH] Additions --- .gitignore | 3 + Database/DividendCorrections202050609.xlsx | Bin 0 -> 13035 bytes Database/Transaction_History.xlsm | Bin 0 -> 29375 bytes Database/VSTCX_Contributions.xlsx | Bin 0 -> 14456 bytes Database/users.sql | 12 ++ .../MGSHMomentum/MGSHPricingException.cs | 66 +++++++ .../Generator/Model/RealtimeGainLoss.cs | 16 ++ MarketDataLib/Generator/RSIGenerator.cs.bak | 125 ++++++++++++ ModelHelper/CMMomentumHelper.cs | 135 +++++++++++++ ModelHelper/CMTrendHelper.cs | 185 ++++++++++++++++++ ModelHelper/MGMomentumHelper.cs | 137 +++++++++++++ ModelHelper/MGSHMomentumHelper.cs | 170 ++++++++++++++++ 12 files changed, 849 insertions(+) create mode 100644 Database/DividendCorrections202050609.xlsx create mode 100644 Database/Transaction_History.xlsm create mode 100644 Database/VSTCX_Contributions.xlsx create mode 100644 Database/users.sql create mode 100644 MarketDataLib/Generator/MGSHMomentum/MGSHPricingException.cs create mode 100644 MarketDataLib/Generator/Model/RealtimeGainLoss.cs create mode 100644 MarketDataLib/Generator/RSIGenerator.cs.bak create mode 100644 ModelHelper/CMMomentumHelper.cs create mode 100644 ModelHelper/CMTrendHelper.cs create mode 100644 ModelHelper/MGMomentumHelper.cs create mode 100644 ModelHelper/MGSHMomentumHelper.cs diff --git a/.gitignore b/.gitignore index 2b81057..eda5b4e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ obj /bin/Debug/*.log /bin /.vs +/gitdiff.bat +/HedgeCashBalanceMarginStrategy +/Scraps diff --git a/Database/DividendCorrections202050609.xlsx b/Database/DividendCorrections202050609.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c87f30e5f51401876106a40c5677267a6c4c1d6f GIT binary patch literal 13035 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 literal 0 HcmV?d00001 diff --git a/Database/VSTCX_Contributions.xlsx b/Database/VSTCX_Contributions.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..61d2447f8f75960f7a6de493d1c0077559dc1686 GIT binary patch literal 14456 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 + { + public NVPCollections ToNVPCollections() + { + NVPCollections nvpCollections=new NVPCollections(); + foreach(MGSHPricingException pricingException in this) + { + nvpCollections.Add(pricingException.ToNVPCollection()); + } + return nvpCollections; + } + public static MGSHPricingExceptions FromNVPCollections(NVPCollections nvpCollections) + { + MGSHPricingExceptions pricingExcpetions=new MGSHPricingExceptions(); + foreach(NVPCollection nvpCollection in nvpCollections) + { + pricingExcpetions.Add(MGSHPricingException.FromNVPCollection(nvpCollection)); + } + return pricingExcpetions; + } + public void AddFromNVPCollection(NVPCollection nvpCollection) + { + Add(MGSHPricingException.FromNVPCollection(nvpCollection)); + } + } + public class MGSHPricingException + { + public MGSHPricingException() + { + } + public MGSHPricingException(MGSHPricingException pricingException) + { + this.Symbol=pricingException.Symbol; + this.ExceptionCount=pricingException.ExceptionCount; + } + public MGSHPricingException(String symbol,int exceptionCount) + { + this.Symbol=symbol; + this.ExceptionCount=exceptionCount; + } + public String Symbol { get; set; } + public int ExceptionCount { get; set; } + public virtual NVPCollection ToNVPCollection() + { + NVPCollection nvpCollection=new NVPCollection(); + nvpCollection.Add(new NVP("Symbol",Symbol.ToString())); + nvpCollection.Add(new NVP("ExceptionCount",ExceptionCount.ToString())); + return nvpCollection; + } + public static MGSHPricingException FromNVPCollection(NVPCollection nvpCollection) + { + MGSHPricingException pricingException=new MGSHPricingException(); + + NVPDictionary nvpDictionary=nvpCollection.ToDictionary(); + pricingException.Symbol=nvpDictionary["Symbol"].Get(); + pricingException.ExceptionCount=nvpDictionary["ExceptionCount"].Get(); + return pricingException; + } + } +} diff --git a/MarketDataLib/Generator/Model/RealtimeGainLoss.cs b/MarketDataLib/Generator/Model/RealtimeGainLoss.cs new file mode 100644 index 0000000..07f80d1 --- /dev/null +++ b/MarketDataLib/Generator/Model/RealtimeGainLoss.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MarketData.Generator.Model +{ + public class RealtimeGainLoss + { + public double Exposure{get;set;} + public double MarketValue{get;set;} + public double GainLoss{get{return MarketValue-Exposure;}} + public double GainLossPercent{get{return Exposure==0?0:(MarketValue-Exposure)/Exposure;}} + } +} diff --git a/MarketDataLib/Generator/RSIGenerator.cs.bak b/MarketDataLib/Generator/RSIGenerator.cs.bak new file mode 100644 index 0000000..5637b17 --- /dev/null +++ b/MarketDataLib/Generator/RSIGenerator.cs.bak @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using MarketData.MarketDataModel; +using MarketData.Utils; +using MarketData.DataAccess; + +namespace MarketData.Generator +{ + public class RSIGenerator + { + private RSIGenerator() + { + } + public static RSICollection GenerateRSI(String symbol,int priceCount,int rsiDayCount=14) + { + if(priceCount=0;index--) + { + Price price=prices[index]; + RSIElement rsiElement=new RSIElement(); + rsiElement.Symbol=price.Symbol; + rsiElement.RSIDays=rsiDayCount; + rsiElement.Date=price.Date; + rsiElement.Close=price.Close; + if(index==prices.Count-1)continue; + rsiElement.Change=price.Close-prices[index+1].Close; + if(rsiElement.Change<0){rsiElement.Loss=Math.Abs(rsiElement.Change);rsiElement.Gain=0.00;} + else if(rsiElement.Change>0){rsiElement.Gain=rsiElement.Change;rsiElement.Loss=0.00;} + else{rsiElement.Loss=0.00;rsiElement.Gain=0.00;} + rsiCollection.Add(rsiElement); + } + RSICollection topCollection=rsiCollection.Top(rsiDayCount,1); + rsiCollection[rsiDayCount].AverageGain=topCollection.AverageGain(); + rsiCollection[rsiDayCount].AverageLoss=topCollection.AverageLoss(); + if(0.00==rsiCollection[rsiDayCount].AverageLoss) + { + rsiCollection[rsiDayCount].RS=0.00; + rsiCollection[rsiDayCount].RSI=100.00; + } + else + { + rsiCollection[rsiDayCount].RS=rsiCollection[rsiDayCount].AverageGain/rsiCollection[rsiDayCount].AverageLoss; + rsiCollection[rsiDayCount].RSI=100.00-(100.00/(1.00+rsiCollection[rsiDayCount].RS)); + } + for(int index=rsiDayCount+1;index=0;index--) + { + Price price=prices[index]; + RSIElement rsiElement=new RSIElement(); + rsiElement.Symbol=price.Symbol; + rsiElement.RSIDays=rsiDayCount; + rsiElement.Date=price.Date; + rsiElement.Close=price.Close; + if(index==prices.Count-1)continue; + rsiElement.Change=price.Close-prices[index+1].Close; + if(rsiElement.Change<0){rsiElement.Loss=Math.Abs(rsiElement.Change);rsiElement.Gain=0.00;} + else if(rsiElement.Change>0){rsiElement.Gain=rsiElement.Change;rsiElement.Loss=0.00;} + else{rsiElement.Loss=0.00;rsiElement.Gain=0.00;} + rsiCollection.Add(rsiElement); + } + RSICollection topCollection=rsiCollection.Top(rsiDayCount,1); + rsiCollection[rsiDayCount].AverageGain=topCollection.AverageGain(); + rsiCollection[rsiDayCount].AverageLoss=topCollection.AverageLoss(); + if(0.00==rsiCollection[rsiDayCount].AverageLoss) + { + rsiCollection[rsiDayCount].RS=0.00; + rsiCollection[rsiDayCount].RSI=100.00; + } + else + { + rsiCollection[rsiDayCount].RS=rsiCollection[rsiDayCount].AverageGain/rsiCollection[rsiDayCount].AverageLoss; + rsiCollection[rsiDayCount].RSI=100.00-(100.00/(1.00+rsiCollection[rsiDayCount].RS)); + } + for(int index=rsiDayCount+1;index + /// CMCANDIDATELASTRESORT /TRADEDATE: + /// + /// + public static void RunCMCandidateLastResort(String[] args) + { + CMParams cmParams = new CMParams(); + List candidates = Utility.ToList(cmParams.FallbackCandidateBestOf); + CommandArgs commandArgs = new CommandArgs(args); + if (!commandArgs.Has("TRADEDATE")) { MDTrace.WriteLine(LogLevel.DEBUG, "TRADEDATE required"); return; } + CMCandidate cmCandidate = CMCandidateGenerator.GetFallbackCandidateOfLastResort(candidates, commandArgs.Coalesce("TRADEDATE"), commandArgs.Coalesce("TRADEDATE"), cmParams); + if (null == cmCandidate) { MDTrace.WriteLine(LogLevel.DEBUG, "Unable to determine candidate of last resort."); return; } + foreach (String candidate in candidates) + { + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Candidate examined..{0}", candidate)); + } + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Best candidate is {0}", cmCandidate.Symbol)); + } + + /// + /// CMGAINLOSS /SESSIONFILE:{PATHSESSIONFILE} (i.e.) CMGAINLOSS /SESSIONFILE:C:\boneyard\marketdata\bin\Debug\saferun\CM20191031.txt"); + /// + /// + public static void RunCMGainLoss(String[] args) + { + CommandArgs commandArgs=new CommandArgs(args); + if(!commandArgs.Has("SESSIONFILE")) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Missing SESSIONFILE")); return; } + CMMomentumBacktest cmBacktest=new CMMomentumBacktest(); + cmBacktest.DisplayGainLoss(commandArgs.Coalesce("SESSIONFILE")); + } + + /// + /// CMSESSION /SESSIONFILE: + /// + /// + public static void RunCMSession(String[] args) + { + CommandArgs commandArgs = new CommandArgs(args); + if (!commandArgs.Has("SESSIONFILE")) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Missing SESSIONFILE")); + return; + } + CMMomentumBacktest cmBacktest = new CMMomentumBacktest(); + cmBacktest.DisplaySession(commandArgs.Coalesce("SESSIONFILE")); + } + + /// + /// RUNCMBACKTEST /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{USEBINBASEDPOSITIONSIZING}: /{USEBINBASEDPOSITIONSIZINGNUMBINS}: /{TARGETBETA}: /{ENDDATE}: /SESSIONFILE: /{USECNN}: /{USECNNHOST}: /{USECNNDAYCOUNT}: + /// + /// + public static void RunCMMomentum(String[] args) + { + CommandArgs commandArgs=new CommandArgs(args); + if(!commandArgs.Has("STARTDATE,MAXPOSITIONS,INITIALCASH,HOLDINGPERIOD")) + { + if(!commandArgs.Has("STARTDATE")) MDTrace.WriteLine(LogLevel.DEBUG,"Missing STARTDATE"); + if(!commandArgs.Has("MAXPOSITIONS")) MDTrace.WriteLine(LogLevel.DEBUG,"Missing MAXPOSITIONS"); + if(!commandArgs.Has("INITIALCASH")) MDTrace.WriteLine(LogLevel.DEBUG,"Missing INITIALCASH"); + if(!commandArgs.Has("HOLDINGPERIOD")) MDTrace.WriteLine(LogLevel.DEBUG,"Missing HOLDINGPERIOD"); + return; + } + + CMParams cmParams=new CMParams(); + cmParams.AnalysisDate=commandArgs.Get("STARTDATE"); + cmParams.MaxPositions=commandArgs.Get("MAXPOSITIONS"); + cmParams.InitialCash=commandArgs.Get("INITIALCASH"); + cmParams.HoldingPeriod=commandArgs.Get("HOLDINGPERIOD"); + + if(commandArgs.Has("USECNN")) + { + if(!commandArgs.Has("USECNNCLIENT,USECNNDAYCOUNT")) + { + MDTrace.WriteLine(LogLevel.DEBUG,"Missing USECNNCLIENT, USECNNDAYCOUNT"); + return; + } + cmParams.UseCNN=true; + cmParams.UseCNNHost=commandArgs.Get("USECNNHOST"); + cmParams.UseCNNDayCount=commandArgs.Get("USECNNDAYCOUNT"); + if(commandArgs.Has("USECNNREWARDPERCENTDECIMAL"))cmParams.UseCNNRewardPercentDecimal=commandArgs.Get("USECNNREWARDPERCENTDECIMAL"); + } + + if(commandArgs.Has("USEOVEREXTENDEDINDICATOR")) + { + if(!commandArgs.Has("USEOVEREXTENDEDINDICATORDAYS,USEOVEREXTENDEDINDICATORVIOLATIONTHRESHHOLD,USEOVEREXTENDEDINDICATORMARGINPERCENT")) + { + MDTrace.WriteLine(LogLevel.DEBUG,"Missing USEOVEREXTENDEDINDICATORDAYS, USEOVEREXTENDEDINDICATORVIOLATIONTHRESHHOLD, USEOVEREXTENDEDINDICATORMARGINPERCENT"); + return; + } + cmParams.UseOverExtendedIndicator=commandArgs.Get("USEOVEREXTENDEDINDICATOR"); + cmParams.UseOverExtendedIndicatorDays=commandArgs.Get("USEOVEREXTENDEDINDICATORDAYS"); + cmParams.UseOverExtendedIndicatorViolationThreshhold=commandArgs.Get("USEOVEREXTENDEDINDICATORVIOLATIONTHRESHHOLD"); + cmParams.UseOverExtendedIndicatorMarginPercent=commandArgs.Get("USEOVEREXTENDEDINDICATORMARGINPERCENT"); + } + + + if(commandArgs.Has("USEMAXPOSITIONBUCKETWEIGHT")) // UseMaxPositionBucketWeight + { + if(!commandArgs.Has("USEMAXPOSITIONBUCKETWEIGHTMAXWEIGHT")) // UseMaxPositionBucketWeightMaxWeight + { + MDTrace.WriteLine(LogLevel.DEBUG,"Missing USEMAXPOSITIONBUCKETWEIGHTMAXWEIGHT"); + return; + } + cmParams.UseMaxPositionBucketWeight=commandArgs.Get("USEMAXPOSITIONBUCKETWEIGHT"); + cmParams.UseMaxPositionBucketWeightMaxWeight=commandArgs.Get("USEMAXPOSITIONBUCKETWEIGHTMAXWEIGHT"); + } + else + { + cmParams.UseMaxPositionBucketWeight=false; + cmParams.UseMaxPositionBucketWeightMaxWeight=0; + } + + if(commandArgs.Has("TARGETBETA"))cmParams.TargetBeta=commandArgs.Get("TARGETBETA"); + else cmParams.TargetBeta=1.00; + + DateTime endDate=DateTime.Now; + String pathSessionFileName=commandArgs.Coalesce("SESSIONFILE",null); + if(null!=pathSessionFileName) pathSessionFileName=pathSessionFileName.Trim(); + cmParams.DisplayHeader(); + + CMMomentumBacktest backtestMomentum=new CMMomentumBacktest(); + List results=new List(); + results.Add(backtestMomentum.PerformBacktest(cmParams.AnalysisDate,endDate,pathSessionFileName,cmParams)); + } + } +} diff --git a/ModelHelper/CMTrendHelper.cs b/ModelHelper/CMTrendHelper.cs new file mode 100644 index 0000000..43cbc7f --- /dev/null +++ b/ModelHelper/CMTrendHelper.cs @@ -0,0 +1,185 @@ +using MarketData.Generator.CMTrend; +using MarketData.Utils; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MarketData.ModelHelper +{ + public static class CMTrendHelper + { + 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")); + } + + 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/ModelHelper/MGMomentumHelper.cs b/ModelHelper/MGMomentumHelper.cs new file mode 100644 index 0000000..73f7b06 --- /dev/null +++ b/ModelHelper/MGMomentumHelper.cs @@ -0,0 +1,137 @@ +using MarketData.Generator.Momentum; +using System; +using System.Collections.Generic; + +namespace MarketData.ModelHelper +{ + public static class MGMomentumHelper + { + /// + /// RUNMOMENTUM /STARTDATE: /MAXPOSITIONS: + /// + /// + public static void RunMomentum(CommandArgs commandArgs) + { + if (!commandArgs.Has("STARTDATE,MAXPOSITIONS")) return; + DateTime analysisDate = commandArgs.Coalesce("STARTDATE"); + int maxPositions = commandArgs.Coalesce("MAXPOSITIONS"); + MGConfiguration config = new MGConfiguration(); + MomentumCandidates momentumCandidates = MomentumGenerator.GenerateMomentum(analysisDate, config); + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0}", MomentumCandidate.Header())); + for (int index = 0; index < momentumCandidates.Count; index++) + { + MomentumCandidate momentumCandidate = momentumCandidates[index]; + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0}", momentumCandidate.ToString())); + } + } + + /// + /// MGLIQUIDATE /SESSIONFILE: /TRADEDATE: + /// + /// + public static void RunMGLiquidate(CommandArgs commandArgs) + { + DateTime? tradeDate = null; + if (!commandArgs.Has("SESSIONFILE")) return; + if (commandArgs.Has("TRADEDATE")) tradeDate = commandArgs.Coalesce("TRADEDATE"); + MomentumBacktest momentumBacktest = new MomentumBacktest(); + momentumBacktest.MGLiquididate(commandArgs.Coalesce("SESSIONFILE"), tradeDate); + } + + /// + /// MGGAINLOSS /SESSIONFILE:{PATHSESSIONFILE} (i.e.) MGGAINLOSS /SESSIONFILE:C:\boneyard\marketdata\bin\Debug\saferun\MG20180131.txt"); + /// + /// + public static void RunMGGainLoss(CommandArgs commandArgs) + { + if(!commandArgs.Has("SESSIONFILE")) { MDTrace.WriteLine(LogLevel.DEBUG,"Missing SESSIONFILE"); return; } + MomentumBacktest momentumBacktest = new MomentumBacktest(); + MomentumBacktest.DisplayGainLoss(commandArgs.Coalesce("SESSIONFILE")); + } + /// + /// MGSESSION /SESSIONFILE: + /// + /// + public static void RunMGSession(CommandArgs commandArgs) + { + if(!commandArgs.Has("SESSIONFILE")) { MDTrace.WriteLine(LogLevel.DEBUG,"Missing SESSIONFILE"); return; } + MomentumBacktest momentumBacktest = new MomentumBacktest(); + momentumBacktest.DisplaySession(commandArgs.Coalesce("SESSIONFILE")); + } + + /// + /// RUNBACKTEST /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{ENDDATE}: /{SESSIONFILE}: + /// + /// + public static void RunBacktest(CommandArgs commandArgs) + { + MGConfiguration mgParams=new MGConfiguration(); + if (!commandArgs.Has("STARTDATE,MAXPOSITIONS,INITIALCASH,HOLDINGPERIOD")) + { + if (!commandArgs.Has("STARTDATE")) MDTrace.WriteLine(LogLevel.DEBUG, "Missing STARTDATE"); + if (!commandArgs.Has("MAXPOSITIONS")) MDTrace.WriteLine(LogLevel.DEBUG, "Missing MAXPOSITIONS"); + if (!commandArgs.Has("INITIALCASH")) MDTrace.WriteLine(LogLevel.DEBUG, "Missing INITIALCASH"); + if (!commandArgs.Has("HOLDINGPERIOD")) MDTrace.WriteLine(LogLevel.DEBUG, "Missing HOLDINGPERIOD"); + return; + } + mgParams.MaxPositions=commandArgs.Coalesce("MAXPOSITIONS"); + mgParams.InitialCash=commandArgs.Coalesce("INITIALCASH"); + mgParams.HoldingPeriod=commandArgs.Coalesce("HOLDINGPERIOD"); + + if(commandArgs.Has("INCLUDETRADEMASTERFORSYMBOLSHELD")) + { + mgParams.IncludeTradeMasterForSymbolsHeld=commandArgs.Get("INCLUDETRADEMASTERFORSYMBOLSHELD"); + } + + if(commandArgs.Has("USESTOCHASTICS")) + { + mgParams.UseStochastics=commandArgs.Coalesce("USESTOCHASTICS",true); + } + + if(commandArgs.Has("USECALCBETA")) + { + mgParams.UseCalcBeta=commandArgs.Coalesce("USECALCBETA",true); + } + +// ** M A C D + if(commandArgs.Has("USEMACD")) + { + mgParams.UseMACD=commandArgs.Coalesce("USEMACD",true); + } + if(commandArgs.Has("MACDREJECTSTRONGSELLSIGNALS")) + { + mgParams.MACDRejectStrongSellSignals=commandArgs.Coalesce("MACDREJECTSTRONGSELLSIGNALS",true); + } + if(commandArgs.Has("MACDREJECTWEAKSELLSIGNALS")) + { + mgParams.MACDRejectWeakSellSignals=commandArgs.Coalesce("MACDREJECTWEAKSELLSIGNALS",true); + } + if(commandArgs.Has("MACDSIGNALDAYS")) + { + mgParams.MACDSignalDays=commandArgs.Coalesce("MACDSIGNALDAYS",mgParams.MACDSignalDays); + } + if(commandArgs.Has("MACDSETUP")) + { + mgParams.MACDSetup=commandArgs.Coalesce("MACDSETUP",mgParams.MACDSetup); + } +// ** + QualityIndicator qualityIndicator=new QualityIndicator(QualityIndicator.QualityType.IDIndicator); + if(commandArgs.Has("QUALITYINDICATORTYPE")) qualityIndicator.Quality=QualityIndicator.ToQuality(commandArgs.Coalesce("QUALITYINDICATORTYPE","IDINDICATOR")); + mgParams.QualityIndicatorType=qualityIndicator.ToString(); + + mgParams.UseLowSlopeBetaCheck=true; + if(commandArgs.Has("USELOWSLOPEBETACHECK")) mgParams.UseLowSlopeBetaCheck=commandArgs.Coalesce("USELOWSLOPEBETACHECK",true); + + DateTime startDate = commandArgs.Coalesce("STARTDATE"); + DateTime endDate=commandArgs.Coalesce("ENDDATE",new DateTime()); + + String pathSessionFileName = commandArgs.Coalesce("SESSIONFILE", null); + if(null!=pathSessionFileName)pathSessionFileName=pathSessionFileName.Trim(); + + mgParams.DisplayHeader(); + List results=new List(); + MomentumBacktest backtestMomentum=new MomentumBacktest(); + results.Add(backtestMomentum.PerformBacktest(startDate,endDate,pathSessionFileName,mgParams)); + } + } +} diff --git a/ModelHelper/MGSHMomentumHelper.cs b/ModelHelper/MGSHMomentumHelper.cs new file mode 100644 index 0000000..7f68d12 --- /dev/null +++ b/ModelHelper/MGSHMomentumHelper.cs @@ -0,0 +1,170 @@ +using MarketData.Generator.MGSHMomentum; +using MarketData.Utils; +using System; +using System.Collections.Generic; +using System.IO; + +namespace MarketData.ModelHelper +{ + public static class MGSHMomentumHelper + { + public static void HandleMGSHSession(String[] args) + { + CommandArgs commandArgs = new CommandArgs(args); + if(!commandArgs.Has("SESSIONFILE")) + { + MDTrace.WriteLine(LogLevel.DEBUG,"Missing SESSIONFILE"); + return; + } + MGSHMomentumBacktest momentumBacktest = new MGSHMomentumBacktest(); + momentumBacktest.DisplaySession(commandArgs.Coalesce("SESSIONFILE")); + } + + public static void HandleMGSHRunDaily(String[] args) + { + DateGenerator dateGenerator = new DateGenerator(); + CommandArgs commandArgs = new CommandArgs(args); + if(!commandArgs.Has("SESSIONFILE,TRADEDATE")) + { + MDTrace.WriteLine(LogLevel.DEBUG,"SESSIONFILE and TRADEDATE are required parameters."); + return; + } + DateTime tradeDate = commandArgs.Get("TRADEDATE"); + + if(!dateGenerator.IsMarketOpen(tradeDate)) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"TRADEDATE {tradeDate.ToShortDateString()} is not a trading date."); + return; + } + + DateTime endDate = dateGenerator.FindNextBusinessDay(tradeDate); // UpdateDaily will not process the endDate (i.e.) while(tradeDate("SESSIONFILE"); + pathSessionFile = pathSessionFile.Trim(); + if(!File.Exists(pathSessionFile)) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"The specified file '{pathSessionFile}' does not exist."); + return; + } + + MGSHMomentumBacktest momentumBacktest = new MGSHMomentumBacktest(); + + if(!dateGenerator.IsMarketOpen(tradeDate)) + { + Console.WriteLine(String.Format("The market is closed today, please confirm Y/N:{0}?",tradeDate.ToShortDateString())); + String result=Console.ReadLine(); + if(null==result||!(result.ToUpper().Equals("Y")||result.ToUpper().Equals("YES")))return; + } + + MGSHBacktestResult backtestResult = momentumBacktest.UpdateDaily(tradeDate, endDate, DateTime.Now, pathSessionFile); + } + + public static void HandleMGSHRunBacktest(String[] args) + { + CommandArgs commandArgs = new CommandArgs(args); + MGSHConfiguration mgParams=new MGSHConfiguration(); + if (!commandArgs.Has("STARTDATE,MAXPOSITIONS,INITIALCASH,HOLDINGPERIOD")) + { + if (!commandArgs.Has("STARTDATE")) MDTrace.WriteLine(LogLevel.DEBUG, "Missing STARTDATE"); + if (!commandArgs.Has("MAXPOSITIONS")) MDTrace.WriteLine(LogLevel.DEBUG, "Missing MAXPOSITIONS"); + if (!commandArgs.Has("INITIALCASH")) MDTrace.WriteLine(LogLevel.DEBUG, "Missing INITIALCASH"); + if (!commandArgs.Has("HOLDINGPERIOD")) MDTrace.WriteLine(LogLevel.DEBUG, "Missing HOLDINGPERIOD"); + return; + } + mgParams.MaxPositions=commandArgs.Coalesce("MAXPOSITIONS"); + mgParams.InitialCash=commandArgs.Coalesce("INITIALCASH"); + mgParams.HoldingPeriod=commandArgs.Coalesce("HOLDINGPERIOD"); + + if(commandArgs.Has("INCLUDETRADEMASTERFORSYMBOLSHELD")) + { + mgParams.IncludeTradeMasterForSymbolsHeld=commandArgs.Get("INCLUDETRADEMASTERFORSYMBOLSHELD"); + } + + if(commandArgs.Has("USESTOCHASTICS")) + { + mgParams.UseStochastics=commandArgs.Coalesce("USESTOCHASTICS",true); + } + + if(commandArgs.Has("USESTOPLIMITS")) + { + mgParams.UseStopLimits=commandArgs.Coalesce("USESTOPLIMITS",false); + } + + if(commandArgs.Has("STOPLIMITRISKPERCENTDECIMAL")) + { + mgParams.StopLimitRiskPercentDecimal=commandArgs.Coalesce("STOPLIMITRISKPERCENTDECIMAL",.12); + } + + if(commandArgs.Has("USEHEDGING")) + { + mgParams.UseHedging=commandArgs.Coalesce("USEHEDGING",false); + if(commandArgs.Has("INITIALHEDGECASH")) + { + mgParams.HedgeInitialCash=commandArgs.Get("INITIALHEDGECASH"); + } + } + + if(commandArgs.Has("KEEPSLOTPOSITIONS")) + { + mgParams.KeepSlotPositions=commandArgs.Get("KEEPSLOTPOSITIONS"); + } + +// ** M A C D + if(commandArgs.Has("USEMACD")) + { + mgParams.UseMACD=commandArgs.Coalesce("USEMACD",true); + } + if(commandArgs.Has("MACDREJECTSTRONGSELLSIGNALS")) + { + mgParams.MACDRejectStrongSellSignals=commandArgs.Coalesce("MACDREJECTSTRONGSELLSIGNALS",true); + } + if(commandArgs.Has("MACDREJECTWEAKSELLSIGNALS")) + { + mgParams.MACDRejectWeakSellSignals=commandArgs.Coalesce("MACDREJECTWEAKSELLSIGNALS",true); + } + if(commandArgs.Has("MACDSIGNALDAYS")) + { + mgParams.MACDSignalDays=commandArgs.Coalesce("MACDSIGNALDAYS",mgParams.MACDSignalDays); + } + if(commandArgs.Has("MACDSETUP")) + { + mgParams.MACDSetup=commandArgs.Coalesce("MACDSETUP",mgParams.MACDSetup); + } +// ** + MGSHQualityIndicator qualityIndicator=new MGSHQualityIndicator(MGSHQualityIndicator.QualityType.IDIndicator); + if(commandArgs.Has("QUALITYINDICATORTYPE")) qualityIndicator.Quality=MGSHQualityIndicator.ToQuality(commandArgs.Coalesce("QUALITYINDICATORTYPE","IDINDICATOR")); + mgParams.QualityIndicatorType=qualityIndicator.ToString(); + + mgParams.UseLowSlopeBetaCheck=true; + if(commandArgs.Has("USELOWSLOPEBETACHECK")) mgParams.UseLowSlopeBetaCheck=commandArgs.Coalesce("USELOWSLOPEBETACHECK",true); + + DateTime startDate = commandArgs.Coalesce("STARTDATE"); + DateTime endDate=commandArgs.Coalesce("ENDDATE",new DateTime()); + + DateGenerator dateGenerator = new DateGenerator(); + if(!dateGenerator.IsMarketOpen(startDate)) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"STARTDATE {startDate.ToShortDateString()} is not a trading date."); + return; + } + if(!dateGenerator.IsMarketOpen(endDate)) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"ENDDATE {endDate.ToShortDateString()} is not a trading date."); + return; + } + + if(!commandArgs.Has("SESSIONFILE")) + { + MDTrace.WriteLine(LogLevel.DEBUG,$"SESSIONFILE is a required parameter."); + return; + } + + String pathSessionFile = commandArgs.Get("SESSIONFILE"); + pathSessionFile = pathSessionFile.Trim(); + + List results=new List(); + MGSHMomentumBacktest backtestMomentum=new MGSHMomentumBacktest(); + results.Add(backtestMomentum.PerformBacktest(startDate, endDate, pathSessionFile, mgParams)); + } + } +}