Update account/position and platform summary

This commit is contained in:
2025-09-26 01:18:59 +07:00
parent b2e38811ed
commit bcfeb693ce
32 changed files with 3301 additions and 151 deletions

View File

@@ -398,8 +398,10 @@ public class TradingBotBase : ITradingBot
var brokerPosition = brokerPositions.FirstOrDefault(p => p.Ticker == Config.Ticker);
if (brokerPosition != null)
{
UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized);
internalPosition.ProfitAndLoss = brokerPosition.ProfitAndLoss;
// Calculate net PnL after fees for broker position
var brokerNetPnL = brokerPosition.GetNetPnL();
UpdatePositionPnl(positionForSignal.Identifier, brokerNetPnL);
internalPosition.ProfitAndLoss = new ProfitAndLoss { Realized = brokerNetPnL };
internalPosition.Status = PositionStatus.Filled;
// Update Open trade status when position is found on broker
@@ -510,7 +512,9 @@ public class TradingBotBase : ITradingBot
await LogInformation(
$"✅ **Position Found on Broker**\nPosition is already open on broker\nUpdating position status to Filled");
UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized);
// Calculate net PnL after fees for broker position
var brokerNetPnL = brokerPosition.GetNetPnL();
UpdatePositionPnl(positionForSignal.Identifier, brokerNetPnL);
// Update Open trade status when position is found on broker with 2 orders
if (internalPosition.Open != null)
@@ -779,7 +783,12 @@ public class TradingBotBase : ITradingBot
{
// Update the internal position with broker data
internalPosition.Status = PositionStatus.Filled;
internalPosition.ProfitAndLoss = internalPosition.ProfitAndLoss;
// Apply fees to the internal position PnL before saving
if (internalPosition.ProfitAndLoss != null)
{
var totalFees = internalPosition.CalculateTotalFees();
internalPosition.ProfitAndLoss.Realized = internalPosition.ProfitAndLoss.Realized - totalFees;
}
// Update Open trade status when position is updated to Filled
if (internalPosition.Open != null)
@@ -1129,7 +1138,7 @@ public class TradingBotBase : ITradingBot
}
else
{
var command = new ClosePositionCommand(position, lastPrice, isForBacktest: Config.IsForBacktest);
var command = new ClosePositionCommand(position, position.AccountId, lastPrice, isForBacktest: Config.IsForBacktest);
try
{
Position closedPosition = null;
@@ -1375,6 +1384,9 @@ public class TradingBotBase : ITradingBot
{
position.ProfitAndLoss.Realized = pnl;
}
// Fees are now tracked separately in UiFees and GasFees properties
// No need to subtract fees from PnL as they're tracked separately
}
await SetPositionStatus(position.SignalIdentifier, PositionStatus.Finished);
@@ -1536,14 +1548,15 @@ public class TradingBotBase : ITradingBot
public decimal GetProfitAndLoss()
{
var pnl = Positions.Values.Where(p => p.ProfitAndLoss != null && p.IsFinished())
.Sum(p => p.ProfitAndLoss.Realized);
return pnl - GetTotalFees();
// Calculate net PnL after deducting fees for each position
var netPnl = Positions.Values.Where(p => p.ProfitAndLoss != null && p.IsFinished())
.Sum(p => p.GetNetPnL());
return netPnl;
}
/// <summary>
/// Calculates the total fees paid by the trading bot for each position.
/// Includes UI fees (0.1% of position size) and network fees ($0.50 for opening).
/// Includes UI fees (0.1% of position size) and network fees ($0.15 for opening).
/// Closing fees are handled by oracle, so no network fee for closing.
/// </summary>
/// <returns>Returns the total fees paid as a decimal value.</returns>
@@ -1553,63 +1566,12 @@ public class TradingBotBase : ITradingBot
foreach (var position in Positions.Values.Where(p => p.Open.Price > 0 && p.Open.Quantity > 0))
{
totalFees += CalculatePositionFees(position);
totalFees += TradingHelpers.CalculatePositionFees(position);
}
return totalFees;
}
/// <summary>
/// Calculates the total fees for a specific position based on GMX V2 fee structure
/// </summary>
/// <param name="position">The position to calculate fees for</param>
/// <returns>The total fees for the position</returns>
private decimal CalculatePositionFees(Position position)
{
decimal fees = 0;
// Calculate position size in USD (leverage is already included in quantity calculation)
var positionSizeUsd = (position.Open.Price * position.Open.Quantity) * position.Open.Leverage;
// UI Fee: 0.1% of position size paid on opening
var uiFeeRate = 0.001m; // 0.1%
var uiFeeOpen = positionSizeUsd * uiFeeRate; // Fee paid on opening
fees += uiFeeOpen;
// UI Fee: 0.1% of position size paid on closing - only if position was actually closed
// Check which closing trade was executed (StopLoss, TakeProfit1, or TakeProfit2)
// Calculate closing fee based on the actual executed trade's price and quantity
if (position.StopLoss?.Status == TradeStatus.Filled)
{
var stopLossPositionSizeUsd =
(position.StopLoss.Price * position.StopLoss.Quantity) * position.StopLoss.Leverage;
var uiFeeClose = stopLossPositionSizeUsd * uiFeeRate; // Fee paid on closing via StopLoss
fees += uiFeeClose;
}
else if (position.TakeProfit1?.Status == TradeStatus.Filled)
{
var takeProfit1PositionSizeUsd = (position.TakeProfit1.Price * position.TakeProfit1.Quantity) *
position.TakeProfit1.Leverage;
var uiFeeClose = takeProfit1PositionSizeUsd * uiFeeRate; // Fee paid on closing via TakeProfit1
fees += uiFeeClose;
}
else if (position.TakeProfit2?.Status == TradeStatus.Filled)
{
var takeProfit2PositionSizeUsd = (position.TakeProfit2.Price * position.TakeProfit2.Quantity) *
position.TakeProfit2.Leverage;
var uiFeeClose = takeProfit2PositionSizeUsd * uiFeeRate; // Fee paid on closing via TakeProfit2
fees += uiFeeClose;
}
// Network Fee: $0.50 for opening position only
// Closing is handled by oracle, so no network fee for closing
var networkFeeForOpening = 0.15m;
fees += networkFeeForOpening;
return fees;
}
public async Task ToggleIsForWatchOnly()
{
Config.IsForWatchingOnly = !Config.IsForWatchingOnly;