@@ -63,6 +63,7 @@ public TradingService(ILogger<TradingService> logger, InvestApiClient investApi,
6363 Logger . LogInformation ( $ "EarlySellOwnedLotsDelta: { settings . EarlySellOwnedLotsDelta } ") ;
6464 Logger . LogInformation ( $ "EarlySellOwnedLotsMultiplier: { settings . EarlySellOwnedLotsMultiplier } ") ;
6565 Logger . LogInformation ( $ "LoadOperationsFrom: { settings . LoadOperationsFrom } ") ;
66+ Logger . LogInformation ( $ "MaximumLossPercentage: { settings . MaximumLossPercentage } ") ;
6667
6768 var currentTime = DateTime . UtcNow . TimeOfDay ;
6869 Logger . LogInformation ( $ "Current time: { currentTime } ") ;
@@ -441,6 +442,44 @@ await marketDataStream.RequestStream.WriteAsync(new MarketDataRequest
441442
442443 // Logger.LogInformation($"bid: {bestBid}, ask: {bestAsk}.");
443444
445+ // Check for maximum loss protection
446+ if ( LotsSets . Count > 0 && ShouldTriggerMaximumLossProtection ( bestBid ) )
447+ {
448+ Logger . LogCritical ( $ "MAXIMUM LOSS PROTECTION TRIGGERED! Current market price: { bestBid } ") ;
449+
450+ // Cancel all existing orders first
451+ var allOrders = new List < string > ( ) ;
452+ allOrders . AddRange ( ActiveBuyOrders . Keys ) ;
453+ allOrders . AddRange ( ActiveSellOrders . Keys ) ;
454+
455+ foreach ( var orderId in allOrders )
456+ {
457+ await TryCancelOrder ( orderId ) ;
458+ }
459+
460+ // Clear active orders
461+ ActiveBuyOrders . Clear ( ) ;
462+ ActiveSellOrders . Clear ( ) ;
463+ ActiveSellOrderSourcePrice . Clear ( ) ;
464+
465+ // Sell all lots at market price
466+ var totalLots = LotsSets . Values . Sum ( ) ;
467+ if ( totalLots > 0 )
468+ {
469+ await PlaceMarketSellOrder ( totalLots ) ;
470+
471+ // Clear lots as they will be sold
472+ LotsSets . Clear ( ) ;
473+ }
474+
475+ // Reset cash balance
476+ SetCashBalance ( CashBalanceFree + CashBalanceLocked , 0 ) ;
477+
478+ Logger . LogCritical ( $ "MAXIMUM LOSS PROTECTION: Sold { totalLots } lots at market price to minimize further losses") ;
479+
480+ continue ;
481+ }
482+
444483 // Logger.LogInformation($"Time: {DateTime.Now}");
445484 // Logger.LogInformation($"ActiveBuyOrders.Count: {ActiveBuyOrders.Count}");
446485 // Logger.LogInformation($"ActiveSellOrders.Count: {ActiveSellOrders.Count}");
@@ -691,6 +730,51 @@ private decimal GetTargetSellPrice(decimal minimumSellPrice, decimal bestAsk)
691730 return targetSellPrice ;
692731 }
693732
733+ private decimal CalculateCurrentLossPercentage ( decimal currentMarketPrice )
734+ {
735+ if ( LotsSets . Count == 0 )
736+ {
737+ return 0 ;
738+ }
739+
740+ decimal totalCost = 0 ;
741+ decimal totalLots = 0 ;
742+
743+ foreach ( var lotsSet in LotsSets )
744+ {
745+ decimal purchasePrice = lotsSet . Key ;
746+ long lots = lotsSet . Value ;
747+ totalCost += purchasePrice * lots ;
748+ totalLots += lots ;
749+ }
750+
751+ if ( totalLots == 0 )
752+ {
753+ return 0 ;
754+ }
755+
756+ decimal averagePurchasePrice = totalCost / totalLots ;
757+ decimal currentValue = currentMarketPrice * totalLots ;
758+ decimal totalPurchaseCost = averagePurchasePrice * totalLots ;
759+
760+ decimal lossPercentage = ( ( totalPurchaseCost - currentValue ) / totalPurchaseCost ) * 100 ;
761+
762+ Logger . LogInformation ( $ "Average purchase price: { averagePurchasePrice } , Current price: { currentMarketPrice } , Loss: { lossPercentage : F2} %") ;
763+
764+ return lossPercentage ;
765+ }
766+
767+ private bool ShouldTriggerMaximumLossProtection ( decimal currentMarketPrice )
768+ {
769+ if ( ! Settings . MaximumLossPercentage . HasValue || LotsSets . Count == 0 )
770+ {
771+ return false ;
772+ }
773+
774+ decimal currentLoss = CalculateCurrentLossPercentage ( currentMarketPrice ) ;
775+ return currentLoss >= Settings . MaximumLossPercentage . Value ;
776+ }
777+
694778 protected override async Task ExecuteAsync ( CancellationToken cancellationToken )
695779 {
696780 var tasks = new [ ]
@@ -831,6 +915,22 @@ private async Task<PostOrderResponse> PlaceSellOrder(long amount, decimal price)
831915 return response ;
832916 }
833917
918+ private async Task < PostOrderResponse > PlaceMarketSellOrder ( long amount )
919+ {
920+ PostOrderRequest marketSellOrderRequest = new ( )
921+ {
922+ OrderId = Guid . NewGuid ( ) . ToString ( ) ,
923+ AccountId = CurrentAccount . Id ,
924+ Direction = OrderDirection . Sell ,
925+ OrderType = OrderType . Market ,
926+ Figi = Figi ,
927+ Quantity = amount
928+ } ;
929+ var response = await InvestApi . Orders . PostOrderAsync ( marketSellOrderRequest ) . ResponseAsync ;
930+ Logger . LogCritical ( $ "MAXIMUM LOSS PROTECTION: Market sell order placed for { amount } lots: { response } ") ;
931+ return response ;
932+ }
933+
834934 private async Task < PostOrderResponse > PlaceBuyOrder ( long amount , decimal price )
835935 {
836936 PostOrderRequest buyOrderRequest = new ( )
0 commit comments