From 835c5be11ad2cae9ce21d66c0256039e59b38f06 Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 25 Feb 2025 15:40:13 -0500 Subject: [PATCH] Add Edit/Close positions for MG and CM models. Remove the Fix for purchase date sell date fall on weekend. Dead code elimination --- .../Generator/CMMomentum/CMActivePositions.cs | 32 ++- .../Generator/CMMomentum/CMBacktest.cs | 233 ++++++++++-------- .../Generator/CMMomentum/CMPositions.cs | 67 ++++- .../Generator/CMTrend/CMTTrendModel..cs | 38 +-- .../Generator/MGSHMomentum/MGSHBacktest.cs | 95 ++----- MarketDataLib/Generator/Momentum/Backtest.cs | 153 +++--------- 6 files changed, 289 insertions(+), 329 deletions(-) diff --git a/MarketDataLib/Generator/CMMomentum/CMActivePositions.cs b/MarketDataLib/Generator/CMMomentum/CMActivePositions.cs index 27dd740..76ebd80 100644 --- a/MarketDataLib/Generator/CMMomentum/CMActivePositions.cs +++ b/MarketDataLib/Generator/CMMomentum/CMActivePositions.cs @@ -1,11 +1,7 @@ -using MarketData.MarketDataModel; -using MarketData.Utils; +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.CMMomentum { @@ -26,6 +22,25 @@ namespace MarketData.Generator.CMMomentum } return exposure; } + + public bool Remove(Position searchPosition) + { + List keys = new List(this.Keys); + for (int index = 0; index < keys.Count; index++) + { + Positions positions = this[keys[index]]; + foreach (Position slotPosition in positions) + { + if (slotPosition == searchPosition) + { + positions.Remove(searchPosition); + return true; + } + } + } + return false; + } + public Positions GetPositions() { Positions positionsCollection=new Positions(); @@ -41,6 +56,7 @@ namespace MarketData.Generator.CMMomentum } return positionsCollection; } + public List GetSymbols() { Dictionary symbols = new Dictionary(); @@ -56,6 +72,7 @@ namespace MarketData.Generator.CMMomentum } return new List(symbols.Keys); } + public double GetMarketValue() { int count = Count; @@ -68,6 +85,7 @@ namespace MarketData.Generator.CMMomentum } return marketValue; } + public double GetGainLoss() { int count = Count; @@ -82,6 +100,7 @@ namespace MarketData.Generator.CMMomentum } return marketValue - exposure; } + public double GetGainLossPercent() { double exposure = GetExposure(); @@ -89,6 +108,7 @@ namespace MarketData.Generator.CMMomentum if (0.00 == exposure) return exposure; return (marketValue - exposure) / exposure; } + public void Display() { for (int slotIndex = 0; slotIndex < Count; slotIndex++) @@ -99,6 +119,7 @@ namespace MarketData.Generator.CMMomentum positions.Display(); } } + public void AddFromNVPCollection(NVPCollection nvpCollection) { SlotPosition slotPosition = SlotPosition.FromNVPCollection(nvpCollection); @@ -108,6 +129,7 @@ namespace MarketData.Generator.CMMomentum } this[slotPosition.Slot].Add(slotPosition.ToPosition()); } + public List ToNVPCollections() { List slots = new List(Keys); diff --git a/MarketDataLib/Generator/CMMomentum/CMBacktest.cs b/MarketDataLib/Generator/CMMomentum/CMBacktest.cs index 4f30fd7..8429e67 100644 --- a/MarketDataLib/Generator/CMMomentum/CMBacktest.cs +++ b/MarketDataLib/Generator/CMMomentum/CMBacktest.cs @@ -38,7 +38,7 @@ namespace MarketData.Generator.CMMomentum private String PathSessionFileName { get; set; } // ****************************************************************************************************************************************************** -//********************************************************** U P D A T E S E S S I O N P R I C E ***************************************************** +//********************************************************** D I S P L A Y G A I N L O S S ********************************************************* // ****************************************************************************************************************************************************** public void DisplayGainLoss(String paramPathSessionFileName) { @@ -119,25 +119,25 @@ namespace MarketData.Generator.CMMomentum MarketData.Generator.CMMomentum.Positions combinedPositions=sessionParams.GetCombinedPositions(); // Fix purchase date/sell date fall on weekend - foreach(MarketData.Generator.CMMomentum.Position position in combinedPositions) - { - if(dateGenerator.IsWeekend(position.PurchaseDate)) - { - while(true) - { - position.PurchaseDate=dateGenerator.GetPrevBusinessDay(position.PurchaseDate); - if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break; - } - } - if(dateGenerator.IsWeekend(position.SellDate)) - { - while(true) - { - position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate); - if(!HolidayDA.IsMarketHoliday(position.SellDate)) break; - } - } - } + //foreach(MarketData.Generator.CMMomentum.Position position in combinedPositions) + //{ + // if(dateGenerator.IsWeekend(position.PurchaseDate)) + // { + // while(true) + // { + // position.PurchaseDate=dateGenerator.GetPrevBusinessDay(position.PurchaseDate); + // if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break; + // } + // } + // if(dateGenerator.IsWeekend(position.SellDate)) + // { + // while(true) + // { + // position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate); + // if(!HolidayDA.IsMarketHoliday(position.SellDate)) break; + // } + // } + //} // ******************************************************** DateTime minDate=combinedPositions.Min(x => x.PurchaseDate); DateTime maxDate=PricingDA.GetLatestDate(); @@ -206,48 +206,6 @@ namespace MarketData.Generator.CMMomentum } } // ****************************************************************************************************************************************************** -//********************************************************** U P D A T E S E S S I O N P R I C E ***************************************************** -// ****************************************************************************************************************************************************** - public void UpdateSessionPrice(String symbol, DateTime tradeDate, double price, String pathSessionFileName) - { - CMSessionParams sessionParams = null; - if (null == symbol || Utility.IsEpoch(tradeDate) || null == pathSessionFileName) - { - MDTrace.WriteLine(LogLevel.DEBUG, "UpdateSessionPrice. One or more parameters are invalid."); - return; - } - PathSessionFileName = pathSessionFileName; - if (null == (sessionParams = RestoreSession())) - { - MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Error loading session file {0}", pathSessionFileName)); - return; - } - List keys = new List(ActivePositions.Keys); - bool hasChanges = false; - foreach (int key in keys) - { - Positions positions = ActivePositions[key]; - foreach (Position position in positions) - { - if (!position.Symbol.Equals(symbol) || !position.PurchaseDate.Date.Equals(tradeDate.Date)) continue; - MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Changing purchase price for {0} on {1} from {2} to {3}", - position.Symbol, - Utility.DateTimeToStringMMHDDHYYYY(position.PurchaseDate), - Utility.FormatCurrency(position.PurchasePrice), - Utility.FormatCurrency(price))); - position.PurchasePrice = price; - if (!hasChanges) hasChanges = true; - } - } - if (hasChanges) SaveSession(); - 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))); - MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Next Slot:{0}", Cycle)); - } -// ****************************************************************************************************************************************************** //************************************************************** D I S P L A Y S E S S I O N ***************************************************** // ****************************************************************************************************************************************************** public void DisplaySession(String paramPathSessionFileName) @@ -299,45 +257,86 @@ namespace MarketData.Generator.CMMomentum else MDTrace.WriteLine(LogLevel.DEBUG,"There does not appear to be any trade data in this file."); } // ****************************************************************************************************************************************************** -//******************************************************* L I Q U I D A T E A L L P O S I T I O N S *********************************************** +// ****************************************************************** C L O S E ********************************************************************** // ****************************************************************************************************************************************************** - public void CMLiquidate(String pathSessionFile, DateTime? tradeDate) + public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double sellPrice,String pathSessionFile) { - if (null == pathSessionFile) return; - CMSessionParams sessionParams = null; + if(null==pathSessionFile) return false; + CMSessionParams sessionParams=null; - PathSessionFileName = pathSessionFile; - if (null == (sessionParams = RestoreSession())) + PathSessionFileName=pathSessionFile; + if(null==(sessionParams=RestoreSession())) { - MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Error loading session file {0}", pathSessionFile)); - return; + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile)); + return false; } - MDTrace.WriteLine(LogLevel.DEBUG, "************** L I Q U I D A T E P O S I T I O N S *************"); - if (null == ActivePositions || 0 == ActivePositions.Count) + if(!BackupSession()) return false; + Positions activePositions = ActivePositions.GetPositions(); + Position position=activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault(); + if(null==position) // if it is not in the active positions then the position is already closed and we are modifying either the sell date or the sell price { - MDTrace.WriteLine(LogLevel.DEBUG, String.Format("No active positions in file {0}", pathSessionFile)); - return; + position=AllPositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault(); + if(null==position) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot locate position for symbol '{0}' purchased on {1}.",symbol,purchaseDate.ToShortDateString())); + return false; + } + position.SellDate = sellDate; + CashBalance -= position.MarketValue; + position.CurrentPrice = sellPrice; + CashBalance += position.MarketValue; + SaveSession(); + return true; } - if (null == tradeDate) tradeDate = PricingDA.GetLatestDate(ActivePositions.GetSymbols()); - MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Trade date:{0}", Utility.DateTimeToStringMMHDDHYYYY(tradeDate.Value))); - if (null == (sessionParams = RestoreSession())) - { - MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Error loading session file {0}", pathSessionFile)); - return; - } - for (int slotIndex = 0; slotIndex < ActivePositions.Count; slotIndex++) - { - Positions slotPositions = ActivePositions[slotIndex]; - SellPositions(slotPositions, tradeDate.Value); - MDTrace.WriteLine(LogLevel.DEBUG, "********************* S E L L *********************"); - slotPositions.Display(); - AllPositions.Add(slotPositions); - CashBalance += slotPositions.MarketValue; - ActivePositions[slotIndex].Clear(); - } - GBPriceCache.GetInstance().Dispose(); + position.SellDate = sellDate; + position.CurrentPrice = sellPrice; + CashBalance += position.MarketValue; + ActivePositions.Remove(position); + AllPositions.Add(position); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Position for symbol '{0}' purchased on {1} is now closed.",symbol,purchaseDate.ToShortDateString())); SaveSession(); + return true; } + +// ****************************************************************************************************************************************************** +// *************************************************************************** E D I T ****************************************************************** +// ****************************************************************************************************************************************************** + public bool EditPosition(String symbol,DateTime purchaseDate,double purchasePrice,String pathSessionFile) + { + if(null==pathSessionFile) return false; + PathSessionFileName=pathSessionFile; + CMSessionParams sessionParams=null; + if(null==(sessionParams=RestoreSession())) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile)); + return false; + } + if(!BackupSession()) return false; + + Positions activePositions = ActivePositions.GetPositions(); + + Position position=activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault(); + if(null==position) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot locate position for symbol '{0}' purchased on {1}.",symbol,purchaseDate.ToShortDateString())); + return false; + } + if(!position.PurchaseDate.Equals(purchaseDate)) position.PurchaseDate=purchaseDate; + if(!position.PurchasePrice.Equals(purchasePrice)) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting Cash for Position for symbol '{0}' purchased on {1}. Original Price: {2} New Price: {3} Change in Cash: {4}", + symbol,purchaseDate.ToShortDateString(), + Utility.FormatCurrency(position.PurchasePrice), + Utility.FormatCurrency(purchasePrice), + Utility.FormatCurrency((position.PurchasePrice-purchasePrice)*position.Shares))); + CashBalance+=(position.PurchasePrice-purchasePrice)*position.Shares; + position.PurchasePrice=purchasePrice; + } + SaveSession(); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Position for symbol '{0}' purchased on {1} has been modified and saved.",symbol,purchaseDate.ToShortDateString())); + return true; + } + // ****************************************************************************************************************************************************** // ****************************************************************** B A C K T E S T ***************************************************************** // ****************************************************************************************************************************************************** @@ -356,7 +355,6 @@ namespace MarketData.Generator.CMMomentum TradeDate = paramStartDate; AnalysisDate = paramAnalysisDate; PathSessionFileName = paramPathSessionFileName; - SMSNotifications = new SMSNotifications(); CMSessionParams sessionParams = null; Cycle = 0; @@ -606,7 +604,7 @@ namespace MarketData.Generator.CMMomentum // *************************************************************************************************************************************************** // ***************************************************************** P O S I T I O N W E I G H T S ************************************************ // *************************************************************************************************************************************************** -// This is a post-stage to the above PositiionSizing. It works by examing the weights of the positions in the bucket and ensuring that no position weight is larger than UseMaxPositionBucketWeightMaxWeight. +// This is a post-stage to the above PositionSizing. It works by examing the weights of the positions in the bucket and ensuring that no position weight is larger than UseMaxPositionBucketWeightMaxWeight. // If an overweight position is located then it's exposure is reduced to UseMaxPositionBucketWeightMaxWeight and the remaining positions divide the excess exposure evenly. // This prevents any single position from eclipsing the other positions in the bucket. private Positions AdjustPositionWeights(Positions positions) @@ -712,6 +710,22 @@ namespace MarketData.Generator.CMMomentum return DateTime.Now; } // **************************************************************************************************************************************** +// ************************************************************** C N N S E R V E R *************************************************** +// **************************************************************************************************************************************** + private void CheckCNNServerStatus() + { + if(Parameters.UseCNN) // ping the server here so that we don't have to do it for each request + { + CNNClient cnnClient=new CNNClient(Parameters.UseCNNHost); + if(!cnnClient.Ping()) + { + String strMessage=String.Format("******* UseCNN=true but the server is not responding. {0} *******",Parameters.UseCNNHost); + Console.Beep(800,200); + throw new Exception(strMessage); + } + } + } +// **************************************************************************************************************************************** // **************************************************************** S E S S I O N M A N A G E M E N T *********************************** // **************************************************************************************************************************************** public CMSessionParams RestoreSession() @@ -725,6 +739,7 @@ namespace MarketData.Generator.CMMomentum TradeDate = sessionParams.TradeDate; if (TradeDate.Date < AnalysisDate.Date) TradeDate = AnalysisDate; StartDate = sessionParams.StartDate; + AnalysisDate = sessionParams.AnalysisDate; Parameters = sessionParams.CMParams; ActivePositions = sessionParams.ActivePositions; AllPositions = sessionParams.AllPositions; @@ -739,6 +754,7 @@ namespace MarketData.Generator.CMMomentum return null; } } + public void SaveSession() { MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Saving session to '{0}'", PathSessionFileName)); @@ -756,18 +772,25 @@ namespace MarketData.Generator.CMMomentum sessionParams.NonTradeableCash = NonTradeableCash; sessionManager.SaveSession(sessionParams, PathSessionFileName); } - private void CheckCNNServerStatus() + + public bool BackupSession() { - if(Parameters.UseCNN) // ping the server here so that we don't have to do it for each request - { - CNNClient cnnClient=new CNNClient(Parameters.UseCNNHost); - if(!cnnClient.Ping()) - { - String strMessage=String.Format("******* UseCNN=true but the server is not responding. {0} *******",Parameters.UseCNNHost); - Console.Beep(800,200); - throw new Exception(strMessage); - } - } + String[] parts=PathSessionFileName.Split('.'); + String backupFileName=parts[0]+"_"+Utility.DateTimeToStringYYYYMMDDMMSSTT(DateTime.Now)+".bak"; + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Saving session to '{0}'",backupFileName)); + CMSessionParams sessionParams = new CMSessionParams(); + CMSessionManager sessionManager = new CMSessionManager(); + sessionParams.LastUpdated = Today(); + sessionParams.TradeDate = TradeDate; + sessionParams.StartDate = StartDate; + sessionParams.AnalysisDate = AnalysisDate; + sessionParams.CMParams = Parameters; + sessionParams.ActivePositions = ActivePositions; + sessionParams.AllPositions = AllPositions; + sessionParams.Cycle = Cycle; + sessionParams.CashBalance = CashBalance; + sessionParams.NonTradeableCash = NonTradeableCash; + return sessionManager.SaveSession(sessionParams, backupFileName); } } } diff --git a/MarketDataLib/Generator/CMMomentum/CMPositions.cs b/MarketDataLib/Generator/CMMomentum/CMPositions.cs index 34f1790..0153e72 100644 --- a/MarketDataLib/Generator/CMMomentum/CMPositions.cs +++ b/MarketDataLib/Generator/CMMomentum/CMPositions.cs @@ -1,22 +1,19 @@ using System; using System.Collections.Generic; -using System.Text; -using MarketData.MarketDataModel; -using MarketData.DataAccess; using MarketData.Utils; using System.Linq; -using MarketData.Helper; -using MarketData.Numerical; +using MarketData.Generator.Interface; namespace MarketData.Generator.CMMomentum { - public class Position + public class Position : IPurePosition { public Position() { CurrentPrice = double.NaN; CNNPrediction=false; } + public Position(Position position) { Symbol = position.Symbol; @@ -34,32 +31,69 @@ namespace MarketData.Generator.CMMomentum TargetBetaOverBeta = position.TargetBetaOverBeta; CNNPrediction=position.CNNPrediction; } + + 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.Beta = position.Beta; + clonedPosition.BetaMonths = position.BetaMonths; + clonedPosition.SharpeRatio = position.SharpeRatio; + clonedPosition.Weight = position.Weight; + clonedPosition.RiskAdjustedWeight = position.RiskAdjustedWeight; + clonedPosition.RiskAdjustedAllocation = position.RiskAdjustedAllocation; + clonedPosition.TargetBetaOverBeta = position.TargetBetaOverBeta; + clonedPosition.CNNPrediction = position.CNNPrediction; + return clonedPosition; + } + public String Symbol { get; set; } + public DateTime PurchaseDate { get; set; } + public DateTime SellDate { get; set; } + public double Shares { get; set; } + public double PurchasePrice { get; set; } + public double CurrentPrice { get; set; } + public double Exposure { get { return Shares * PurchasePrice; } } // Derived + public double MarketValue { get { return Shares * CurrentPrice; } } // Derived + public double GainLoss { get { return MarketValue - Exposure; } } // Derived + public double GainLossPcnt { get { return (MarketValue - Exposure) / Exposure; } }// Derived + public double Beta { get; set; } + public int BetaMonths { get; set; } + public double SharpeRatio { get; set; } + public double Weight { get; set; } + public double RiskAdjustedWeight { get; set; } + public double RiskAdjustedAllocation { get; set; } + public double TargetBetaOverBeta { get; set; } + public double Score{get;set;} + public bool CNNPrediction{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("PurchaseDate", PurchaseDate.Date.ToString())); nvpCollection.Add(new NVP("SellDate", SellDate.Date.ToString())); nvpCollection.Add(new NVP("Shares", Shares.ToString())); @@ -75,13 +109,12 @@ namespace MarketData.Generator.CMMomentum nvpCollection.Add(new NVP("CNNPrediction", CNNPrediction.ToString())); 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.PurchaseDate = nvpDictionary["PurchaseDate"].Get().Date; position.SellDate = nvpDictionary["SellDate"].Get().Date; position.Shares = nvpDictionary["Shares"].Get(); @@ -97,6 +130,7 @@ namespace MarketData.Generator.CMMomentum if(nvpDictionary.ContainsKey("CNNPrediction"))position.CNNPrediction = nvpDictionary["CNNPrediction"].Get(); return position; } + public void Display() { if (Utility.IsEpoch(SellDate) && double.IsNaN(CurrentPrice)) @@ -146,6 +180,7 @@ namespace MarketData.Generator.CMMomentum )); } } + public static void DisplayHeader() { MDTrace.WriteLine(LogLevel.DEBUG, "Symbol,Shares,Purchase Date,Purchase Price,Sell Date,Sell Price,Exposure,Beta,BetaMonths,SharpeRatio,RiskAdjustedWeight,RiskAdjustedAllocation,TargetBetaOverBeta,Score,CNNPrediction,Market Value,Gain Loss,Gain Loss(%)"); @@ -154,25 +189,30 @@ namespace MarketData.Generator.CMMomentum // **************************************************************************************************************************************************************** public class Positions : List { - public Positions() + public Positions() { } + public Positions(Positions positions) { foreach (Position position in positions) Add(position); } + public Positions(List positions) { foreach (Position position in positions) Add(position); } + public Positions(Position position) { Add(position); } + public void Add(Positions positions) { foreach (Position position in positions) Add(position); } + public double Exposure { get @@ -180,6 +220,7 @@ namespace MarketData.Generator.CMMomentum return (from Position position in this select position.PurchasePrice * position.Shares).Sum(); } } + public double MarketValue { get @@ -197,6 +238,7 @@ namespace MarketData.Generator.CMMomentum } return nvpCollections; } + public static Positions FromNVPCollections(NVPCollections nvpCollections) { Positions positions = new Positions(); @@ -206,6 +248,7 @@ namespace MarketData.Generator.CMMomentum } return positions; } + public void DisplayTopFive() { Positions positions = new Positions(this.OrderByDescending(x => x.GainLossPcnt).Take(5).ToList()); @@ -216,6 +259,7 @@ namespace MarketData.Generator.CMMomentum position.Display(); } } + public void DisplayBottomFive() { Positions positions = new Positions(this.OrderBy(x => x.GainLossPcnt).Take(5).ToList()); @@ -226,6 +270,7 @@ namespace MarketData.Generator.CMMomentum position.Display(); } } + public void Display() { Position.DisplayHeader(); diff --git a/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs b/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs index e74322e..c84ea98 100644 --- a/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs +++ b/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs @@ -125,25 +125,25 @@ namespace MarketData.Generator.CMTrend MarketData.Generator.CMTrend.Positions combinedPositions=sessionParams.GetCombinedPositions(); // Fix purchase date/sell date fall on weekend - foreach(MarketData.Generator.CMTrend.Position position in combinedPositions) - { - if(dateGenerator.IsWeekend(position.PurchaseDate)) - { - while(true) - { - position.PurchaseDate=dateGenerator.GetPrevBusinessDay(position.PurchaseDate); - if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break; - } - } - if(dateGenerator.IsWeekend(position.SellDate)) - { - while(true) - { - position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate); - if(!HolidayDA.IsMarketHoliday(position.SellDate)) break; - } - } - } + //foreach(MarketData.Generator.CMTrend.Position position in combinedPositions) + //{ + // if(dateGenerator.IsWeekend(position.PurchaseDate)) + // { + // while(true) + // { + // position.PurchaseDate=dateGenerator.GetPrevBusinessDay(position.PurchaseDate); + // if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break; + // } + // } + // if(dateGenerator.IsWeekend(position.SellDate)) + // { + // while(true) + // { + // position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate); + // if(!HolidayDA.IsMarketHoliday(position.SellDate)) break; + // } + // } + //} // ******************************************************** DateTime minDate=combinedPositions.Min(x => x.PurchaseDate); DateTime maxDate=PricingDA.GetLatestDate(); diff --git a/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs b/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs index d289ab1..5f7a30d 100644 --- a/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs +++ b/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs @@ -34,50 +34,6 @@ namespace MarketData.Generator.MGSHMomentum private DateTime StartDate{get;set;} private DateTime AnalysisDate{get;set;} private String PathSessionFileName{get;set;} - -// ****************************************************************************************************************************************************** -//********************************************************** U P D A T E S E S S I O N P R I C E ***************************************************** -// ****************************************************************************************************************************************************** - public void UpdateSessionPrice(String symbol,DateTime tradeDate,double price,String pathSessionFileName) - { - MGSHSessionParams sessionParams=null; - if(null==symbol||Utility.IsEpoch(tradeDate)||null==pathSessionFileName) - { - MDTrace.WriteLine(LogLevel.DEBUG,"UpdateSessionPrice. One or more parameters are invalid."); - return; - } - PathSessionFileName=pathSessionFileName; - if(null==(sessionParams=RestoreSession())) - { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFileName)); - return; - } - List keys=new List(ActivePositions.Keys); - bool hasChanges=false; - foreach(int key in keys) - { - MGSHPositions positions=ActivePositions[key]; - foreach(MGSHPosition position in positions) - { - if(!position.Symbol.Equals(symbol)||!position.PurchaseDate.Date.Equals(tradeDate.Date))continue; - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Changing purchase price for {0} on {1} from {2} to {3}", - position.Symbol, - Utility.DateTimeToStringMMHDDHYYYY(position.PurchaseDate), - Utility.FormatCurrency(position.PurchasePrice), - Utility.FormatCurrency(price))); - position.PurchasePrice=price; - if(!hasChanges)hasChanges=true; - } - } - if(hasChanges)SaveSession(); - 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))); - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle)); - } - // ****************************************************************************************************************************************************** //************************************************************** D I S P L A Y G A I N L O S S ***************************************************** // ****************************************************************************************************************************************************** @@ -119,9 +75,9 @@ namespace MarketData.Generator.MGSHMomentum } } - // ****************************************************************************************************************************************************** - // *************************************************************************** E D I T ****************************************************************** - // ****************************************************************************************************************************************************** +// ****************************************************************************************************************************************************** +// *************************************************************************** E D I T ****************************************************************** +// ****************************************************************************************************************************************************** public bool EditPosition(String symbol,DateTime purchaseDate,double purchasePrice,double initialStop,double trailingStop,String sessionFile) { if (!MGSHSessionManager.IsValidSessionFile(sessionFile)) @@ -137,7 +93,6 @@ namespace MarketData.Generator.MGSHMomentum AnalysisDate = sessionParams.AnalysisDate; Cycle=sessionParams.Cycle; sessionParams.LastUpdated = DateTime.Now; - //sessionParams.CMTParams.AnalysisDate=DateTime.Now; StopLimits=sessionParams.StopLimits; ActivePositions=sessionParams.ActivePositions; AllPositions=sessionParams.AllPositions; @@ -170,6 +125,9 @@ namespace MarketData.Generator.MGSHMomentum return true; } +// ****************************************************************************************************************************************************** +// ************************************************************************ C L O S E ****************************************************************** +// ****************************************************************************************************************************************************** public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double price,String sessionFile) { if (!MGSHSessionManager.IsValidSessionFile(sessionFile)) @@ -186,7 +144,6 @@ namespace MarketData.Generator.MGSHMomentum AnalysisDate = sessionParams.AnalysisDate; Cycle=sessionParams.Cycle; sessionParams.LastUpdated = DateTime.Now; - //sessionParams.CMTParams.AnalysisDate=DateTime.Now; StopLimits=sessionParams.StopLimits; ActivePositions=sessionParams.ActivePositions; AllPositions=sessionParams.AllPositions; @@ -198,7 +155,7 @@ namespace MarketData.Generator.MGSHMomentum if(!BackupSession())return false; MGSHPositions activePositions = ActivePositions.GetPositions(); - MGSHPosition position=activePositions.Where(x => x.Symbol.Equals(symbol)&&x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault(); + MGSHPosition position=activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault(); if(null==position) // if it is not in the active positions then the position is already closed and we are modifying either the sell date or the sell price { position=AllPositions.Where(x => x.Symbol.Equals(symbol)&&x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault(); @@ -275,25 +232,25 @@ namespace MarketData.Generator.MGSHMomentum MGSHPositions combinedPositions=sessionParams.GetCombinedPositions(); // Fix purchase date/sell date fall on weekend - foreach(MGSHPosition position in combinedPositions) - { - if(dateGenerator.IsWeekend(position.PurchaseDate)) - { - while(true) - { - position.PurchaseDate=dateGenerator.GetPrevBusinessDay(position.PurchaseDate); - if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break; - } - } - if(dateGenerator.IsWeekend(position.SellDate)) - { - while(true) - { - position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate); - if(!HolidayDA.IsMarketHoliday(position.SellDate)) break; - } - } - } + //foreach(MGSHPosition position in combinedPositions) + //{ + // if(dateGenerator.IsWeekend(position.PurchaseDate)) + // { + // while(true) + // { + // position.PurchaseDate=dateGenerator.GetPrevBusinessDay(position.PurchaseDate); + // if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break; + // } + // } + // if(dateGenerator.IsWeekend(position.SellDate)) + // { + // while(true) + // { + // position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate); + // if(!HolidayDA.IsMarketHoliday(position.SellDate)) break; + // } + // } + //} // ******************************************************** DateTime minDate=combinedPositions.Min(x => x.PurchaseDate); DateTime maxDate=PricingDA.GetLatestDate(); diff --git a/MarketDataLib/Generator/Momentum/Backtest.cs b/MarketDataLib/Generator/Momentum/Backtest.cs index 6e5a94d..f7295ea 100644 --- a/MarketDataLib/Generator/Momentum/Backtest.cs +++ b/MarketDataLib/Generator/Momentum/Backtest.cs @@ -18,7 +18,6 @@ namespace MarketData.Generator.Momentum private int MaxPositions{get{return Configuration.MaxPositions;}} private List NoTradeSymbols{get{return Utility.ToList(Configuration.NoTradeSymbols);}} private ActivePositions ActivePositions{get;set;} - private SMSNotifications SMSNotifications{get;set;} private Positions AllPositions{get;set;} private int Cycle{get;set;} private DateTime TradeDate{get;set;} @@ -26,48 +25,6 @@ namespace MarketData.Generator.Momentum private DateTime AnalysisDate{get;set;} private String PathSessionFileName{get;set;} // ****************************************************************************************************************************************************** -//********************************************************** U P D A T E S E S S I O N P R I C E ***************************************************** -// ****************************************************************************************************************************************************** - public void UpdateSessionPrice(String symbol,DateTime tradeDate,double price,String pathSessionFileName) - { - MGSessionParams sessionParams=null; - if(null==symbol||Utility.IsEpoch(tradeDate)||null==pathSessionFileName) - { - MDTrace.WriteLine(LogLevel.DEBUG,"UpdateSessionPrice. One or more parameters are invalid."); - return; - } - PathSessionFileName=pathSessionFileName; - if(null==(sessionParams=RestoreSession())) - { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFileName)); - return; - } - List keys=new List(ActivePositions.Keys); - bool hasChanges=false; - foreach(int key in keys) - { - Positions positions=ActivePositions[key]; - foreach(Position position in positions) - { - if(!position.Symbol.Equals(symbol)||!position.PurchaseDate.Date.Equals(tradeDate.Date))continue; - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Changing purchase price for {0} on {1} from {2} to {3}", - position.Symbol, - Utility.DateTimeToStringMMHDDHYYYY(position.PurchaseDate), - Utility.FormatCurrency(position.PurchasePrice), - Utility.FormatCurrency(price))); - position.PurchasePrice=price; - if(!hasChanges)hasChanges=true; - } - } - if(hasChanges)SaveSession(); - 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))); - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle)); - } -// ****************************************************************************************************************************************************** //************************************************************** D I S P L A Y G A I N L O S S ***************************************************** // ****************************************************************************************************************************************************** public static void DisplayGainLoss(String paramPathSessionFileName) @@ -93,6 +50,7 @@ namespace MarketData.Generator.Momentum } MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Done, took {0}(ms)",profiler.End())); } + public static ModelPerformanceSeries GetModelPerformance(String paramPathSessionFileName) { try @@ -139,6 +97,7 @@ namespace MarketData.Generator.Momentum return modelStatistics; } } + public static ModelPerformanceSeries GetModelPerformance(MGSessionParams sessionParams) { Profiler profiler=new Profiler(); @@ -150,25 +109,25 @@ namespace MarketData.Generator.Momentum MarketData.Generator.Momentum.Positions combinedPositions=sessionParams.GetCombinedPositions(); // Fix purchase date/sell date fall on weekend - foreach(MarketData.Generator.Momentum.Position position in combinedPositions) - { - if(dateGenerator.IsWeekend(position.PurchaseDate)) - { - while(true) - { - position.PurchaseDate=dateGenerator.GetPrevBusinessDay(position.PurchaseDate); - if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break; - } - } - if(dateGenerator.IsWeekend(position.SellDate)) - { - while(true) - { - position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate); - if(!HolidayDA.IsMarketHoliday(position.SellDate)) break; - } - } - } + //foreach(MarketData.Generator.Momentum.Position position in combinedPositions) + //{ + // if(dateGenerator.IsWeekend(position.PurchaseDate)) + // { + // while(true) + // { + // position.PurchaseDate=dateGenerator.GetPrevBusinessDay(position.PurchaseDate); + // if(!HolidayDA.IsMarketHoliday(position.PurchaseDate)) break; + // } + // } + // if(dateGenerator.IsWeekend(position.SellDate)) + // { + // while(true) + // { + // position.SellDate=dateGenerator.GetNextBusinessDay(position.SellDate); + // if(!HolidayDA.IsMarketHoliday(position.SellDate)) break; + // } + // } + //} // ******************************************************** DateTime minDate=combinedPositions.Min(x => x.PurchaseDate); DateTime maxDate=PricingDA.GetLatestDate(); @@ -298,53 +257,8 @@ namespace MarketData.Generator.Momentum SaveSession(); } // ****************************************************************************************************************************************************** -//******************************************************* S E L L P O S I T I O N *********************************************** +// ****************************************************************** C L O S E ********************************************************************** // ****************************************************************************************************************************************************** - public void MGClosePosition(String pathSessionFile,String symbol,DateTime purchaseDate,double sellPrice,DateTime sellDate) - { - if(null==pathSessionFile) return; - MGSessionParams sessionParams=null; - - PathSessionFileName=pathSessionFile; - if(null==(sessionParams=RestoreSession())) - { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile)); - return; - } - MDTrace.WriteLine(LogLevel.DEBUG,"************** S E L L P O S I T I O N *************"); - if(null==ActivePositions||0==ActivePositions.Count) - { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No active positions in file {0}",pathSessionFile)); - return; - } - Positions activePositions=ActivePositions.GetPositions(); - - Position positionToSell=ActivePositions.FindPosition(symbol,purchaseDate); - if(null==positionToSell) - { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No active position found for {0} purchased on {1}",symbol,Utility.DateTimeToStringMMHDDHYYYY(purchaseDate))); - return; - } - int positionToSellSlot=ActivePositions.FindSlotForPosition(positionToSell); - if(-1==positionToSellSlot) - { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Could not locate slot index for position {0} purchased on {1}",symbol,Utility.DateTimeToStringMMHDDHYYYY(purchaseDate))); - return; - } - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Found position {0} purchased on {1}",symbol,Utility.DateTimeToStringMMHDDHYYYY(purchaseDate))); - positionToSell.SellDate=sellDate.Date; - positionToSell.CurrentPrice=sellPrice; - AllPositions.Add(positionToSell); - CashBalance+=positionToSell.MarketValue; - ActivePositions[positionToSellSlot].Remove(positionToSell); - DisplayBalance(); - GBPriceCache.GetInstance().Dispose(); - SaveSession(); - } - - // ****************************************************************************************************************************************************** - // ****************************************************************** C L O S E ********************************************************************** - // ****************************************************************************************************************************************************** public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double sellPrice,String pathSessionFile) { if(null==pathSessionFile) return false; @@ -383,17 +297,14 @@ namespace MarketData.Generator.Momentum SaveSession(); return true; } - - - // ****************************************************************************************************************************************************** - // *************************************************************************** E D I T ****************************************************************** - // ****************************************************************************************************************************************************** +// ****************************************************************************************************************************************************** +// *************************************************************************** E D I T ****************************************************************** +// ****************************************************************************************************************************************************** public bool EditPosition(String symbol,DateTime purchaseDate,double purchasePrice,String pathSessionFile) { if(null==pathSessionFile) return false; - MGSessionParams sessionParams=null; - PathSessionFileName=pathSessionFile; + MGSessionParams sessionParams=null; if(null==(sessionParams=RestoreSession())) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile)); @@ -443,7 +354,6 @@ namespace MarketData.Generator.Momentum TradeDate=paramStartDate; AnalysisDate=paramAnalysisDate; PathSessionFileName=paramPathSessionFileName; - SMSNotifications=new SMSNotifications(); MGSessionParams sessionParams=null; Cycle=0; @@ -481,7 +391,6 @@ namespace MarketData.Generator.Momentum { positions.Clear(); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************")); -// break; } positions.Display(); ActivePositions.Add(slotIndex,positions); @@ -543,7 +452,8 @@ namespace MarketData.Generator.Momentum backTestResult.CashBalance=CashBalance; GBPriceCache.GetInstance().Dispose(); return backTestResult; - } + } + public List SymbolsHeld(DateTime tradeDate) { List symbolsHeld=ActivePositions.SymbolsHeld(); @@ -603,6 +513,7 @@ namespace MarketData.Generator.Momentum else position.CurrentPrice=price.Close; } } + private void SellPosition(Position position,DateTime sellDate) { Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,sellDate); @@ -680,6 +591,7 @@ namespace MarketData.Generator.Momentum } return positions; } + private Positions BuyBenchmarkPositions(DateTime tradeDate, double cash) { Positions positions = new Positions(); @@ -714,6 +626,7 @@ namespace MarketData.Generator.Momentum MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL EXPOSURE: {0}",Utility.FormatCurrency(ActivePositions.GetExposure()))); MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************"); } + private void DisplayBalanceFromPositions() { MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,GAIN/LOSS,GAIN/LOSS(%),AVAILABLE CASH,TOTAL ACCOUNT"); @@ -724,6 +637,7 @@ namespace MarketData.Generator.Momentum Utility.AddQuotes(Utility.FormatCurrency(CashBalance)), Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance)))); } + private void DisplayBalance(RealtimeGainLoss gainLoss) { MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,GAIN/LOSS,GAIN/LOSS(%),AVAILABLE CASH,TOTAL ACCOUNT"); @@ -804,8 +718,7 @@ namespace MarketData.Generator.Momentum sessionParams.Cycle=Cycle; sessionParams.CashBalance=CashBalance; sessionParams.NonTradeableCash=NonTradeableCash; - sessionManager.SaveSession(sessionParams,backupFileName); - return true; + return sessionManager.SaveSession(sessionParams,backupFileName); } } }