Remove warning for backtest when signal is expired
This commit is contained in:
@@ -160,7 +160,7 @@ public class AccountService : IAccountService
|
|||||||
|
|
||||||
await ManagePropertiesAsync(hideSecrets, getBalance, account);
|
await ManagePropertiesAsync(hideSecrets, getBalance, account);
|
||||||
|
|
||||||
if (account.User != null)
|
if (account.User == null)
|
||||||
{
|
{
|
||||||
account.User = await _userRepository.GetUserByNameAsync(account.User.Name);
|
account.User = await _userRepository.GetUserByNameAsync(account.User.Name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -329,9 +329,10 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await LogWarning($"Failed to update finished position {position.Identifier} in database: {ex.Message}");
|
await LogWarning(
|
||||||
|
$"Failed to update finished position {position.Identifier} in database: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Then, open positions for signals waiting for a position open
|
// Then, open positions for signals waiting for a position open
|
||||||
// But first, check if we already have a position for any of these signals
|
// But first, check if we already have a position for any of these signals
|
||||||
@@ -1294,7 +1295,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
// Update the closing trade price if available
|
// Update the closing trade price if available
|
||||||
if (gmxPosition.Open != null)
|
if (gmxPosition.Open != null)
|
||||||
{
|
{
|
||||||
var closingPrice = gmxPosition.Open.Price;
|
var gmxClosingPrice = gmxPosition.Open.Price;
|
||||||
|
|
||||||
// Determine which trade was the closing trade based on profitability
|
// Determine which trade was the closing trade based on profitability
|
||||||
bool isProfitable = position.ProfitAndLoss.Realized > 0;
|
bool isProfitable = position.ProfitAndLoss.Realized > 0;
|
||||||
@@ -1303,7 +1304,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
if (position.TakeProfit1 != null)
|
if (position.TakeProfit1 != null)
|
||||||
{
|
{
|
||||||
position.TakeProfit1.SetPrice(closingPrice, 2);
|
position.TakeProfit1.SetPrice(gmxClosingPrice, 2);
|
||||||
position.TakeProfit1.SetDate(gmxPosition.Open.Date);
|
position.TakeProfit1.SetDate(gmxPosition.Open.Date);
|
||||||
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||||
}
|
}
|
||||||
@@ -1318,7 +1319,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
{
|
{
|
||||||
if (position.StopLoss != null)
|
if (position.StopLoss != null)
|
||||||
{
|
{
|
||||||
position.StopLoss.SetPrice(closingPrice, 2);
|
position.StopLoss.SetPrice(gmxClosingPrice, 2);
|
||||||
position.StopLoss.SetDate(gmxPosition.Open.Date);
|
position.StopLoss.SetDate(gmxPosition.Open.Date);
|
||||||
position.StopLoss.SetStatus(TradeStatus.Filled);
|
position.StopLoss.SetStatus(TradeStatus.Filled);
|
||||||
}
|
}
|
||||||
@@ -1338,7 +1339,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
Logger.LogInformation(
|
Logger.LogInformation(
|
||||||
$"📊 Position Reconciliation Complete\n" +
|
$"📊 Position Reconciliation Complete\n" +
|
||||||
$"Position: `{position.Identifier}`\n" +
|
$"Position: `{position.Identifier}`\n" +
|
||||||
$"Closing Price: `${closingPrice:F2}`\n" +
|
$"Closing Price: `${gmxClosingPrice:F2}`\n" +
|
||||||
$"Used: `{(isProfitable ? "Take Profit" : "Stop Loss")}`\n" +
|
$"Used: `{(isProfitable ? "Take Profit" : "Stop Loss")}`\n" +
|
||||||
$"PnL from GMX: `${position.ProfitAndLoss.Realized:F2}`");
|
$"PnL from GMX: `${position.ProfitAndLoss.Realized:F2}`");
|
||||||
}
|
}
|
||||||
@@ -1359,6 +1360,10 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate P&L for backtests even if currentCandle is null
|
||||||
|
decimal closingPrice = 0;
|
||||||
|
bool pnlCalculated = false;
|
||||||
|
|
||||||
if (currentCandle != null)
|
if (currentCandle != null)
|
||||||
{
|
{
|
||||||
List<Candle> recentCandles = null;
|
List<Candle> recentCandles = null;
|
||||||
@@ -1428,8 +1433,6 @@ public class TradingBotBase : ITradingBot
|
|||||||
wasTakeProfitHit = minPriceRecent <= position.TakeProfit1.Price;
|
wasTakeProfitHit = minPriceRecent <= position.TakeProfit1.Price;
|
||||||
}
|
}
|
||||||
|
|
||||||
decimal closingPrice;
|
|
||||||
|
|
||||||
if (wasStopLossHit)
|
if (wasStopLossHit)
|
||||||
{
|
{
|
||||||
// Use actual execution price based on direction
|
// Use actual execution price based on direction
|
||||||
@@ -1534,6 +1537,40 @@ public class TradingBotBase : ITradingBot
|
|||||||
$"Closing at market price: `${closingPrice:F2}`");
|
$"Closing at market price: `${closingPrice:F2}`");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pnlCalculated = true;
|
||||||
|
}
|
||||||
|
else if (Config.IsForBacktest)
|
||||||
|
{
|
||||||
|
// For backtests when currentCandle is null, use a fallback closing price
|
||||||
|
// This ensures P&L calculation always happens for backtests
|
||||||
|
Logger.LogWarning(
|
||||||
|
$"⚠️ Backtest: No current candle available for position {position.Identifier}. Using fallback closing price calculation.");
|
||||||
|
|
||||||
|
// Use the position's stop loss or take profit price as closing price
|
||||||
|
if (position.StopLoss != null && position.StopLoss.Price > 0)
|
||||||
|
{
|
||||||
|
closingPrice = position.StopLoss.Price;
|
||||||
|
position.StopLoss.SetStatus(TradeStatus.Filled);
|
||||||
|
}
|
||||||
|
else if (position.TakeProfit1 != null && position.TakeProfit1.Price > 0)
|
||||||
|
{
|
||||||
|
closingPrice = position.TakeProfit1.Price;
|
||||||
|
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Last resort: use entry price (no profit/loss)
|
||||||
|
closingPrice = position.Open.Price;
|
||||||
|
Logger.LogWarning(
|
||||||
|
$"⚠️ Backtest: Using entry price as closing price for position {position.Identifier}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pnlCalculated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate P&L if we have a closing price
|
||||||
|
if (pnlCalculated && closingPrice > 0)
|
||||||
|
{
|
||||||
var entryPrice = position.Open.Price;
|
var entryPrice = position.Open.Price;
|
||||||
var positionSize = position.Open.Quantity * position.Open.Leverage;
|
var positionSize = position.Open.Quantity * position.Open.Leverage;
|
||||||
|
|
||||||
@@ -1553,7 +1590,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
var netPnl = pnl - totalFees;
|
var netPnl = pnl - totalFees;
|
||||||
position.ProfitAndLoss = new ProfitAndLoss { Realized = pnl, Net = netPnl };
|
position.ProfitAndLoss = new ProfitAndLoss { Realized = pnl, Net = netPnl };
|
||||||
}
|
}
|
||||||
else if (position.ProfitAndLoss.Realized == 0)
|
else if (position.ProfitAndLoss.Realized == 0 || position.ProfitAndLoss.Net == 0)
|
||||||
{
|
{
|
||||||
var totalFees = position.GasFees + position.UiFees;
|
var totalFees = position.GasFees + position.UiFees;
|
||||||
var netPnl = pnl - totalFees;
|
var netPnl = pnl - totalFees;
|
||||||
@@ -1561,6 +1598,12 @@ public class TradingBotBase : ITradingBot
|
|||||||
position.ProfitAndLoss.Net = netPnl;
|
position.ProfitAndLoss.Net = netPnl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.LogInformation(
|
||||||
|
$"💰 P&L Calculated for Position {position.Identifier}\n" +
|
||||||
|
$"Entry: `${entryPrice:F2}` | Exit: `${closingPrice:F2}`\n" +
|
||||||
|
$"Realized P&L: `${pnl:F2}` | Net P&L (after fees): `${position.ProfitAndLoss.Net:F2}`\n" +
|
||||||
|
$"Total Fees: `${position.GasFees + position.UiFees:F2}`");
|
||||||
|
|
||||||
// Fees are now tracked separately in UiFees and GasFees properties
|
// Fees are now tracked separately in UiFees and GasFees properties
|
||||||
// No need to subtract fees from PnL as they're tracked separately
|
// No need to subtract fees from PnL as they're tracked separately
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Managing.Application.Abstractions;
|
using Managing.Application.Abstractions;
|
||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
using Managing.Application.Trading.Commands;
|
using Managing.Application.Trading.Commands;
|
||||||
|
using Managing.Common;
|
||||||
|
using Managing.Domain.Accounts;
|
||||||
using Managing.Domain.Shared.Helpers;
|
using Managing.Domain.Shared.Helpers;
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -21,12 +23,15 @@ public class ClosePositionCommandHandler(
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get Trade
|
Account account = null;
|
||||||
var account = await accountService.GetAccountById(request.AccountId, false, false);
|
if (!request.IsForBacktest)
|
||||||
if (request.Position == null)
|
|
||||||
{
|
{
|
||||||
_ = await exchangeService.CancelOrder(account, request.Position.Ticker);
|
account = await accountService.GetAccountById(request.AccountId, false, false);
|
||||||
return request.Position;
|
if (request.Position == null)
|
||||||
|
{
|
||||||
|
_ = await exchangeService.CancelOrder(account, request.Position.Ticker);
|
||||||
|
return request.Position;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isForPaperTrading = request.IsForBacktest;
|
var isForPaperTrading = request.IsForBacktest;
|
||||||
@@ -48,12 +53,14 @@ public class ClosePositionCommandHandler(
|
|||||||
request.Position.ProfitAndLoss =
|
request.Position.ProfitAndLoss =
|
||||||
TradingBox.GetProfitAndLoss(request.Position, request.Position.Open.Quantity, lastPrice,
|
TradingBox.GetProfitAndLoss(request.Position, request.Position.Open.Quantity, lastPrice,
|
||||||
request.Position.Open.Leverage);
|
request.Position.Open.Leverage);
|
||||||
|
|
||||||
// Add UI fees for closing the position (broker closed it)
|
// Add UI fees for closing the position (broker closed it)
|
||||||
var closingPositionSizeUsd = (lastPrice * request.Position.Open.Quantity) * request.Position.Open.Leverage;
|
var closingPositionSizeUsd =
|
||||||
|
(lastPrice * request.Position.Open.Quantity) * request.Position.Open.Leverage;
|
||||||
var closingUiFees = TradingHelpers.CalculateClosingUiFees(closingPositionSizeUsd);
|
var closingUiFees = TradingHelpers.CalculateClosingUiFees(closingPositionSizeUsd);
|
||||||
request.Position.AddUiFees(closingUiFees);
|
request.Position.AddUiFees(closingUiFees);
|
||||||
|
request.Position.AddGasFees(Constants.GMX.Config.GasFeePerTransaction);
|
||||||
|
|
||||||
await tradingService.UpdatePositionAsync(request.Position);
|
await tradingService.UpdatePositionAsync(request.Position);
|
||||||
return request.Position;
|
return request.Position;
|
||||||
}
|
}
|
||||||
@@ -78,6 +85,7 @@ public class ClosePositionCommandHandler(
|
|||||||
var closingPositionSizeUsd = (lastPrice * closedPosition.Quantity) * request.Position.Open.Leverage;
|
var closingPositionSizeUsd = (lastPrice * closedPosition.Quantity) * request.Position.Open.Leverage;
|
||||||
var closingUiFees = TradingHelpers.CalculateClosingUiFees(closingPositionSizeUsd);
|
var closingUiFees = TradingHelpers.CalculateClosingUiFees(closingPositionSizeUsd);
|
||||||
request.Position.AddUiFees(closingUiFees);
|
request.Position.AddUiFees(closingUiFees);
|
||||||
|
request.Position.AddGasFees(Constants.GMX.Config.GasFeePerTransaction);
|
||||||
|
|
||||||
if (!request.IsForBacktest)
|
if (!request.IsForBacktest)
|
||||||
await tradingService.UpdatePositionAsync(request.Position);
|
await tradingService.UpdatePositionAsync(request.Position);
|
||||||
|
|||||||
Reference in New Issue
Block a user