From e48588c13a5a5dfba2006cd9906e9d45a8fe00eb Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 19 Mar 2026 19:49:09 -0400 Subject: [PATCH] Add changes to all models to eliminate selling and immediately buying back the same security as this is considered a Wash Trade and is illegal. --- .../Generator/CMMomentum/CMBacktest.cs | 5 +++-- .../Generator/CMTrend/CMTTrendModel.cs | 15 +++++++++------ .../Generator/MGSHMomentum/MGSHBacktest.cs | 6 +++--- .../MarketDataLib/Generator/Momentum/Backtest.cs | 3 ++- README.md | 6 +++++- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/MarketData/MarketDataLib/Generator/CMMomentum/CMBacktest.cs b/MarketData/MarketDataLib/Generator/CMMomentum/CMBacktest.cs index 7a0d55b..eed2b2b 100755 --- a/MarketData/MarketDataLib/Generator/CMMomentum/CMBacktest.cs +++ b/MarketData/MarketDataLib/Generator/CMMomentum/CMBacktest.cs @@ -388,6 +388,7 @@ namespace MarketData.Generator.CMMomentum else { Positions slotPositions = ActivePositions[slotIndex]; + List slotSymbols = slotPositions.ConvertAll(x=>x.Symbol); // capture sell symbols to exclude from purchases to eliminate wash trades SellPositions(slotPositions, TradeDate); DisplaySales(slotPositions, TradeDate); MDTrace.WriteLine(LogLevel.DEBUG, "********************* S E L L *********************"); @@ -396,8 +397,8 @@ namespace MarketData.Generator.CMMomentum CashBalance += slotPositions.MarketValue; ActivePositions[slotIndex].Clear(); DisplayBalance(); - double cashAllocation = Math.Min(CashBalance, (ActivePositions.GetExposure() + CashBalance) / HoldingPeriod); // Even out the cash allocation so that no one slot eats up all the cash - Positions positions=BuyPositions(slotIndex,TradeDate,AnalysisDate,cashAllocation,SymbolsHeld()); + double cashAllocation = Math.Min(CashBalance, (ActivePositions.GetExposure() + CashBalance) / HoldingPeriod); // Even out the cash allocation so that no one slot eats up all the cash + Positions positions=BuyPositions(slotIndex,TradeDate,AnalysisDate,cashAllocation,new List(SymbolsHeld().Concat(slotSymbols))); DisplayPurchases(positions, TradeDate); AddToWatchList(positions); MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************"); diff --git a/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendModel.cs b/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendModel.cs index 29966c4..21c46fd 100755 --- a/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendModel.cs +++ b/MarketData/MarketDataLib/Generator/CMTrend/CMTTrendModel.cs @@ -622,7 +622,7 @@ namespace MarketData.Generator.CMTrend 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); + List closedSymbols = ManageOpenPositions(TradeDate); // get any closed positions so we can avoid selling and buying (wash trades) ManageCandidates(TradeDate); // ************************************************************************************************************************************************************************ // **************************************************************************** N E W P O S I T I O N S ***************************************************************** @@ -640,7 +640,7 @@ namespace MarketData.Generator.CMTrend result.Success=true; return result; } - Positions positions=BuyCandidates(TradeDate,CashBalance,ActivePositions.GetSymbols()); + Positions positions=BuyCandidates(TradeDate,CashBalance,new List(ActivePositions.GetSymbols().Concat(closedSymbols))); if(null != positions && 0!=positions.Count) { MDTrace.WriteLine(LogLevel.DEBUG,"******************** B U Y ********************"); @@ -735,7 +735,7 @@ namespace MarketData.Generator.CMTrend 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); + List closedSymbols = ManageOpenPositions(TradeDate); ManageCandidates(TradeDate); if(ActivePositions.PositionsOn(TradeDate)>=Parameters.MaxDailyPositions) { @@ -743,7 +743,7 @@ namespace MarketData.Generator.CMTrend result.Success=true; continue; } - Positions positions=BuyCandidates(TradeDate,CashBalance,ActivePositions.GetSymbols()); + Positions positions=BuyCandidates(TradeDate,CashBalance,new List(ActivePositions.GetSymbols().Concat(closedSymbols))); if(null!=positions&&0!=positions.Count) { MDTrace.WriteLine(LogLevel.DEBUG,"******************** B U Y ********************"); @@ -885,9 +885,10 @@ namespace MarketData.Generator.CMTrend // *********************************************************************************************************************************************************************** // *********************************************************************** M A N A G E O P E N P O S I T I O N S ***************************************************** // *********************************************************************************************************************************************************************** - private void ManageOpenPositions(DateTime tradeDate) + private List ManageOpenPositions(DateTime tradeDate) { - if(0==ActivePositions.Count) return; + List closedSymbols = new List(); + if(0==ActivePositions.Count) return closedSymbols; Positions closedPositions = new Positions(); foreach(Position position in ActivePositions) { @@ -960,6 +961,8 @@ namespace MarketData.Generator.CMTrend ActivePositions.Remove(closedPosition); } } + if(closedPositions.Count>0)closedSymbols = closedPositions.ConvertAll(x => x.Symbol); + return closedSymbols; } // ********************************************************************************************************************************************************** diff --git a/MarketData/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs b/MarketData/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs index 5b270a1..edea181 100755 --- a/MarketData/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs +++ b/MarketData/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs @@ -219,10 +219,8 @@ namespace MarketData.Generator.MGSHMomentum double losingTrades=sessionParams.AllPositions.Where(x => x.GainLoss<0.00).Count(); MGSHPositions winningTradeList = new MGSHPositions(sessionParams.AllPositions.Where(x => x.GainLoss>=0.00).ToList()); double averageWinningTrade = winningTradeList.Count>0?winningTradeList.Average(x => x.GainLossPcnt)*100.00:0.00; -// double averageWinningTrade=sessionParams.AllPositions.Where(x => x.GainLoss>=0.00).Average(x => x.GainLossPcnt)*100.00; MGSHPositions losingTradeList = new MGSHPositions(sessionParams.AllPositions.Where(x => x.GainLoss<0.00).ToList()); double averageLosingTrade = losingTradeList.Count>0?losingTradeList.Average(x => x.GainLossPcnt)*100.00:0.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; @@ -520,8 +518,10 @@ namespace MarketData.Generator.MGSHMomentum public void SellAndBuySlotPositions(int slotIndex) { MGSHPositions slotPositions=ActivePositions[slotIndex]; + List closedSymbols = new List(); if(!Configuration.KeepSlotPositions) // if we are not configured to KeepSlotPositions then don't sell anything just buy to the max positions allowed for the slot { + closedSymbols = slotPositions.ConvertAll(x => x.Symbol); // capture the closed symbols so we can avoid buying right back (wash trades) SellPositions(slotPositions,TradeDate); DisplaySales(slotPositions, TradeDate); MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L *********************"); @@ -535,7 +535,7 @@ namespace MarketData.Generator.MGSHMomentum int positionsToFill = MaxPositions-slotPositions.Count; double cashAllocation = (CashBalance / ((HoldingPeriod * MaxPositions) - ActivePositions.GetCount()))*positionsToFill; // split the cash between the total positions we can own less the number of positions we have MGSHPositions positions = null; - positions=BuyPositions(slotIndex, TradeDate,AnalysisDate,cashAllocation,SymbolsHeld(TradeDate), positionsToFill); + positions=BuyPositions(slotIndex, TradeDate,AnalysisDate,cashAllocation,new List(SymbolsHeld(TradeDate).Concat(closedSymbols)), positionsToFill); if(CashBalance-positions.Exposure<=0.00) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************")); diff --git a/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs b/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs index cd71406..e2b6fe0 100755 --- a/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs +++ b/MarketData/MarketDataLib/Generator/Momentum/Backtest.cs @@ -378,6 +378,7 @@ namespace MarketData.Generator.Momentum else { Positions slotPositions=ActivePositions[slotIndex]; + List closedSymbols = slotPositions.ConvertAll(x => x.Symbol); // capture the sell symbols to avoid buying straight back (wash trades) SellPositions(slotPositions,TradeDate); MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L *********************"); slotPositions.Display(); @@ -390,7 +391,7 @@ namespace MarketData.Generator.Momentum cashAllocation = Math.Min(CashBalance, (ActivePositions.GetExposure() + CashBalance) / (double)HoldingPeriod); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CASH ALLOCATION:{0}",Utility.FormatCurrency(cashAllocation))); Positions positions = null; - positions=BuyPositions(TradeDate,AnalysisDate,cashAllocation,SymbolsHeld(TradeDate)); + positions=BuyPositions(TradeDate,AnalysisDate,cashAllocation,new List(SymbolsHeld(TradeDate).Concat(closedSymbols))); MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************"); positions.Display(); if(CashBalance-positions.Exposure<=0.00) diff --git a/README.md b/README.md index 7964580..d9e9d9b 100644 --- a/README.md +++ b/README.md @@ -240,5 +240,9 @@ Also note that 1>/dev/null is synonymous to, but more explicit than >/dev/null Slow builds and C# Dev Kit Errors ps -u $USER -o pid,cmd | grep -E 'csdevkit|visualstudio-projectsystem-buildhost|vstest.console' | grep dotnet | awk '#{print $1}' | xargs -r kill -9 || true +Kill MSBuild that don't close when exiting vscode +ps aux | grep MSBuild.dll | grep -v grep | awk '{print $2}' | xargs sudo kill -9 + + This shows where dotnet performance is -dotnet build /clp:PerformanceSummary \ No newline at end of file +dotnet build /clp:PerformanceSummary