From 8090a5e09352b62d20df98c4c71fe79e260d8a8a Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 11 Feb 2025 19:19:05 -0500 Subject: [PATCH] Add ClosePosition log and fix bug in the initial stop limit adjustment where it was possible to adjust a stop limit downwards. --- .../Generator/MGSHMomentum/MGSHBacktest.cs | 69 +++++++++++++++++-- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs b/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs index 655eb2f..dc56128 100644 --- a/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs +++ b/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs @@ -170,6 +170,61 @@ namespace MarketData.Generator.MGSHMomentum return true; } + public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double price,String sessionFile) + { + if (!MGSHSessionManager.IsValidSessionFile(sessionFile)) + { + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Invalid session file '{0}'.", sessionFile)); + return false; + } + + PathSessionFileName = sessionFile; + MGSHSessionParams sessionParams=MGSHSessionManager.RestoreSession(PathSessionFileName); + Configuration = sessionParams.Configuration; + TradeDate = sessionParams.TradeDate; + StartDate = sessionParams.StartDate; + AnalysisDate = sessionParams.AnalysisDate; + Cycle=sessionParams.Cycle; + sessionParams.LastUpdated = DateTime.Now; + //sessionParams.CMTParams.AnalysisDate=DateTime.Now; + StopLimits=sessionParams.StopLimits; + ActivePositions=sessionParams.ActivePositions; + AllPositions=sessionParams.AllPositions; + HedgePositions=sessionParams.HedgePositions; + CashBalance=sessionParams.CashBalance; + NonTradeableCash=sessionParams.NonTradeableCash; + HedgeCashBalance=sessionParams.HedgeCashBalance; + PricingExceptions=sessionParams.PricingExceptions; + if(!BackupSession())return false; + + MGSHPositions activePositions = ActivePositions.GetPositions(); + 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(); + 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; + } + // ****************************************************************************************************************************************************** // **************************************************************** S T A T I S T I C S ****************************************************************** // ****************************************************************************************************************************************************** @@ -800,15 +855,15 @@ namespace MarketData.Generator.MGSHMomentum } // This is an older position for which we never set an initial stop. We will not adjust these - if(double.IsNaN(position.R) || double.IsNaN(position.TrailingStopLimit) || double.IsNaN(position.InitialStopLimit)) + if (double.IsNaN(position.R) || double.IsNaN(position.TrailingStopLimit) || double.IsNaN(position.InitialStopLimit)) { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[UpdateStopLimitsForActivePositions] Position {0} on {1} is legacy will not adjust stop limit.",position.Symbol,analysisDate.ToShortDateString())); + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("[UpdateStopLimitsForActivePositions] Position {0} on {1} is legacy will not adjust stop limit.", position.Symbol, analysisDate.ToShortDateString())); continue; } position.CurrentPrice = price.Close; RemovePricingException(position.Symbol); - if(price.Low < position.TrailingStopLimit && !position.PurchaseDate.Equals(analysisDate)) + if (price.Low < position.TrailingStopLimit && !position.PurchaseDate.Equals(analysisDate)) { position.SellDate = analysisDate; position.CurrentPrice = position.TrailingStopLimit; @@ -868,16 +923,19 @@ namespace MarketData.Generator.MGSHMomentum { trailingStopScaled=GetStopLimitWithScalingAverageTrueRange(analysisDate,currentPrice,position,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 changed; } - if(Numerics.Round(trailingStop).Equals(Numerics.Round(position.TrailingStopLimit))) + + if(Numerics.Round(trailingStop)<=(Numerics.Round(position.TrailingStopLimit))) { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be the same as the existing stop limit of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(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 changed; } + 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, @@ -888,6 +946,7 @@ namespace MarketData.Generator.MGSHMomentum Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop), Utility.FormatNumber(position.Shares,2))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*****************************************************************************************")); + StopLimit newStopLimit=new StopLimit { StopLimitId=position.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(position.PurchaseDate),