Fix status for position and SLTP
This commit is contained in:
@@ -402,6 +402,12 @@ public class TradingBotBase : ITradingBot
|
|||||||
internalPosition.ProfitAndLoss = brokerPosition.ProfitAndLoss;
|
internalPosition.ProfitAndLoss = brokerPosition.ProfitAndLoss;
|
||||||
internalPosition.Status = PositionStatus.Filled;
|
internalPosition.Status = PositionStatus.Filled;
|
||||||
|
|
||||||
|
// Update Open trade status when position is found on broker
|
||||||
|
if (internalPosition.Open != null)
|
||||||
|
{
|
||||||
|
internalPosition.Open.SetStatus(TradeStatus.Filled);
|
||||||
|
}
|
||||||
|
|
||||||
if (internalPosition.Status.Equals(PositionStatus.New))
|
if (internalPosition.Status.Equals(PositionStatus.New))
|
||||||
{
|
{
|
||||||
await SetPositionStatus(internalPosition.SignalIdentifier, PositionStatus.Filled);
|
await SetPositionStatus(internalPosition.SignalIdentifier, PositionStatus.Filled);
|
||||||
@@ -493,6 +499,13 @@ public class TradingBotBase : ITradingBot
|
|||||||
$"✅ **Position Found on Broker**\nPosition is already open on broker\nUpdating position status to Filled");
|
$"✅ **Position Found on Broker**\nPosition is already open on broker\nUpdating position status to Filled");
|
||||||
|
|
||||||
UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized);
|
UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized);
|
||||||
|
|
||||||
|
// Update Open trade status when position is found on broker with 2 orders
|
||||||
|
if (internalPosition.Open != null)
|
||||||
|
{
|
||||||
|
internalPosition.Open.SetStatus(TradeStatus.Filled);
|
||||||
|
}
|
||||||
|
|
||||||
await SetPositionStatus(signal.Identifier, PositionStatus.Filled);
|
await SetPositionStatus(signal.Identifier, PositionStatus.Filled);
|
||||||
|
|
||||||
// Notify platform summary about the executed trade
|
// Notify platform summary about the executed trade
|
||||||
@@ -568,56 +581,130 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
if (positionForSignal.StopLoss.Price >= lastCandle.Low)
|
if (positionForSignal.StopLoss.Price >= lastCandle.Low)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
// Use actual execution price (lastCandle.Low for SL hit)
|
||||||
$"🛑 **Stop Loss Hit**\nClosing LONG position\nPrice: `${positionForSignal.StopLoss.Price}`");
|
var executionPrice = lastCandle.Low;
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
positionForSignal.StopLoss.SetPrice(executionPrice, 2);
|
||||||
positionForSignal.StopLoss.Price, true);
|
positionForSignal.StopLoss.SetDate(lastCandle.Date);
|
||||||
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel TP trades when SL is hit
|
||||||
|
if (positionForSignal.TakeProfit1 != null)
|
||||||
|
{
|
||||||
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
if (positionForSignal.TakeProfit2 != null)
|
||||||
|
{
|
||||||
|
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
await LogInformation(
|
||||||
|
$"🛑 **Stop Loss Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)");
|
||||||
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
||||||
|
executionPrice, true);
|
||||||
}
|
}
|
||||||
else if (positionForSignal.TakeProfit1.Price <= lastCandle.High &&
|
else if (positionForSignal.TakeProfit1.Price <= lastCandle.High &&
|
||||||
positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
// Use actual execution price (lastCandle.High for TP hit)
|
||||||
$"🎯 **Take Profit 1 Hit**\nClosing LONG position\nPrice: `${positionForSignal.TakeProfit1.Price}`");
|
var executionPrice = lastCandle.High;
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
positionForSignal.TakeProfit1.SetPrice(executionPrice, 2);
|
||||||
positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
|
positionForSignal.TakeProfit1.SetDate(lastCandle.Date);
|
||||||
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel SL trade when TP is hit
|
||||||
|
if (positionForSignal.StopLoss != null)
|
||||||
|
{
|
||||||
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
await LogInformation(
|
||||||
|
$"🎯 **Take Profit 1 Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
||||||
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
||||||
|
executionPrice, positionForSignal.TakeProfit2 == null);
|
||||||
}
|
}
|
||||||
else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High)
|
else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
// Use actual execution price (lastCandle.High for TP hit)
|
||||||
$"🎯 **Take Profit 2 Hit**\nClosing LONG position\nPrice: `${positionForSignal.TakeProfit2.Price}`");
|
var executionPrice = lastCandle.High;
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
positionForSignal.TakeProfit2.SetPrice(executionPrice, 2);
|
||||||
positionForSignal.TakeProfit2.Price, true);
|
positionForSignal.TakeProfit2.SetDate(lastCandle.Date);
|
||||||
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel SL trade when TP is hit
|
||||||
|
if (positionForSignal.StopLoss != null)
|
||||||
|
{
|
||||||
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
await LogInformation(
|
||||||
|
$"🎯 **Take Profit 2 Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
||||||
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
||||||
|
executionPrice, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (positionForSignal.OriginDirection == TradeDirection.Short)
|
else if (positionForSignal.OriginDirection == TradeDirection.Short)
|
||||||
{
|
{
|
||||||
if (positionForSignal.StopLoss.Price <= lastCandle.High)
|
if (positionForSignal.StopLoss.Price <= lastCandle.High)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
// Use actual execution price (lastCandle.High for SL hit on SHORT)
|
||||||
$"🛑 **Stop Loss Hit**\nClosing SHORT position\nPrice: `${positionForSignal.StopLoss.Price}`");
|
var executionPrice = lastCandle.High;
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
positionForSignal.StopLoss.SetPrice(executionPrice, 2);
|
||||||
positionForSignal.StopLoss.Price, true);
|
positionForSignal.StopLoss.SetDate(lastCandle.Date);
|
||||||
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel TP trades when SL is hit
|
||||||
|
if (positionForSignal.TakeProfit1 != null)
|
||||||
|
{
|
||||||
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
if (positionForSignal.TakeProfit2 != null)
|
||||||
|
{
|
||||||
|
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
await LogInformation(
|
||||||
|
$"🛑 **Stop Loss Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)");
|
||||||
|
await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
|
||||||
|
executionPrice, true);
|
||||||
}
|
}
|
||||||
else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low &&
|
else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low &&
|
||||||
positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
// Use actual execution price (lastCandle.Low for TP hit on SHORT)
|
||||||
$"🎯 **Take Profit 1 Hit**\nClosing SHORT position\nPrice: `${positionForSignal.TakeProfit1.Price}`");
|
var executionPrice = lastCandle.Low;
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
positionForSignal.TakeProfit1.SetPrice(executionPrice, 2);
|
||||||
positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
|
positionForSignal.TakeProfit1.SetDate(lastCandle.Date);
|
||||||
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel SL trade when TP is hit
|
||||||
|
if (positionForSignal.StopLoss != null)
|
||||||
|
{
|
||||||
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
await LogInformation(
|
||||||
|
$"🎯 **Take Profit 1 Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)");
|
||||||
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
|
||||||
|
executionPrice, positionForSignal.TakeProfit2 == null);
|
||||||
}
|
}
|
||||||
else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low)
|
else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low)
|
||||||
{
|
{
|
||||||
await LogInformation(
|
// Use actual execution price (lastCandle.Low for TP hit on SHORT)
|
||||||
$"🎯 **Take Profit 2 Hit**\nClosing SHORT position\nPrice: `${positionForSignal.TakeProfit2.Price}`");
|
var executionPrice = lastCandle.Low;
|
||||||
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
positionForSignal.TakeProfit2.SetPrice(executionPrice, 2);
|
||||||
positionForSignal.TakeProfit2.Price, true);
|
positionForSignal.TakeProfit2.SetDate(lastCandle.Date);
|
||||||
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel SL trade when TP is hit
|
||||||
|
if (positionForSignal.StopLoss != null)
|
||||||
|
{
|
||||||
|
positionForSignal.StopLoss.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
await LogInformation(
|
||||||
|
$"🎯 **Take Profit 2 Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)");
|
||||||
|
await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
|
||||||
|
executionPrice, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -676,6 +763,12 @@ public class TradingBotBase : ITradingBot
|
|||||||
internalPosition.Status = PositionStatus.Filled;
|
internalPosition.Status = PositionStatus.Filled;
|
||||||
internalPosition.ProfitAndLoss = internalPosition.ProfitAndLoss;
|
internalPosition.ProfitAndLoss = internalPosition.ProfitAndLoss;
|
||||||
|
|
||||||
|
// Update Open trade status when position is updated to Filled
|
||||||
|
if (internalPosition.Open != null)
|
||||||
|
{
|
||||||
|
internalPosition.Open.SetStatus(TradeStatus.Filled);
|
||||||
|
}
|
||||||
|
|
||||||
// Save updated position to database
|
// Save updated position to database
|
||||||
await tradingService.UpdatePositionAsync(internalPosition);
|
await tradingService.UpdatePositionAsync(internalPosition);
|
||||||
});
|
});
|
||||||
@@ -1137,26 +1230,52 @@ public class TradingBotBase : ITradingBot
|
|||||||
|
|
||||||
if (wasStopLossHit)
|
if (wasStopLossHit)
|
||||||
{
|
{
|
||||||
closingPrice = position.StopLoss.Price;
|
// Use actual execution price based on direction
|
||||||
|
closingPrice = position.OriginDirection == TradeDirection.Long
|
||||||
|
? minPriceRecent // For LONG, SL hits at the low
|
||||||
|
: maxPriceRecent; // For SHORT, SL hits at the high
|
||||||
|
|
||||||
|
position.StopLoss.SetPrice(closingPrice, 2);
|
||||||
position.StopLoss.SetDate(currentCandle.Date);
|
position.StopLoss.SetDate(currentCandle.Date);
|
||||||
position.StopLoss.SetStatus(TradeStatus.Filled);
|
position.StopLoss.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel TP trades when SL is hit
|
||||||
|
if (position.TakeProfit1 != null)
|
||||||
|
{
|
||||||
|
position.TakeProfit1.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
if (position.TakeProfit2 != null)
|
||||||
|
{
|
||||||
|
position.TakeProfit2.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"🛑 **Stop Loss Execution Confirmed**\n" +
|
$"🛑 **Stop Loss Execution Confirmed**\n" +
|
||||||
$"Position: `{position.Identifier}`\n" +
|
$"Position: `{position.Identifier}`\n" +
|
||||||
$"SL Price: `${position.StopLoss.Price:F2}` was hit\n" +
|
$"SL Price: `${closingPrice:F2}` was hit (was `${position.StopLoss.Price:F2}`)\n" +
|
||||||
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
||||||
}
|
}
|
||||||
else if (wasTakeProfitHit)
|
else if (wasTakeProfitHit)
|
||||||
{
|
{
|
||||||
closingPrice = position.TakeProfit1.Price;
|
// Use actual execution price based on direction
|
||||||
|
closingPrice = position.OriginDirection == TradeDirection.Long
|
||||||
|
? maxPriceRecent // For LONG, TP hits at the high
|
||||||
|
: minPriceRecent; // For SHORT, TP hits at the low
|
||||||
|
|
||||||
|
position.TakeProfit1.SetPrice(closingPrice, 2);
|
||||||
position.TakeProfit1.SetDate(currentCandle.Date);
|
position.TakeProfit1.SetDate(currentCandle.Date);
|
||||||
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel SL trade when TP is hit
|
||||||
|
if (position.StopLoss != null)
|
||||||
|
{
|
||||||
|
position.StopLoss.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"🎯 **Take Profit Execution Confirmed**\n" +
|
$"🎯 **Take Profit Execution Confirmed**\n" +
|
||||||
$"Position: `{position.Identifier}`\n" +
|
$"Position: `{position.Identifier}`\n" +
|
||||||
$"TP Price: `${position.TakeProfit1.Price:F2}` was hit\n" +
|
$"TP Price: `${closingPrice:F2}` was hit (was `${position.TakeProfit1.Price:F2}`)\n" +
|
||||||
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1176,13 +1295,31 @@ public class TradingBotBase : ITradingBot
|
|||||||
|
|
||||||
if (isManualCloseProfitable)
|
if (isManualCloseProfitable)
|
||||||
{
|
{
|
||||||
|
position.TakeProfit1.SetPrice(closingPrice, 2);
|
||||||
position.TakeProfit1.SetDate(currentCandle.Date);
|
position.TakeProfit1.SetDate(currentCandle.Date);
|
||||||
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel SL trade when TP is used for manual close
|
||||||
|
if (position.StopLoss != null)
|
||||||
|
{
|
||||||
|
position.StopLoss.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
position.StopLoss.SetPrice(closingPrice, 2);
|
||||||
position.StopLoss.SetDate(currentCandle.Date);
|
position.StopLoss.SetDate(currentCandle.Date);
|
||||||
position.StopLoss.SetStatus(TradeStatus.Filled);
|
position.StopLoss.SetStatus(TradeStatus.Filled);
|
||||||
|
|
||||||
|
// Cancel TP trades when SL is used for manual close
|
||||||
|
if (position.TakeProfit1 != null)
|
||||||
|
{
|
||||||
|
position.TakeProfit1.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
|
if (position.TakeProfit2 != null)
|
||||||
|
{
|
||||||
|
position.TakeProfit2.SetStatus(TradeStatus.Cancelled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
|
|||||||
Reference in New Issue
Block a user