Fix status IsFinished/IsOpen/IsForMetrics + use redis for markets on gmx.tsx instead of inmemory cache
This commit is contained in:
@@ -211,7 +211,7 @@ public class AgentGrain : Grain, IAgentGrain
|
||||
usdcWalletValue += usdcBalance;
|
||||
}
|
||||
|
||||
foreach (var position in positions.Where(p => !p.IsFinished()))
|
||||
foreach (var position in positions.Where(p => p.IsOpen()))
|
||||
{
|
||||
var positionUsd = position.Open.Price * position.Open.Quantity;
|
||||
var realized = position.ProfitAndLoss?.Realized ?? 0;
|
||||
|
||||
@@ -925,14 +925,14 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
||||
if (_tradingBot == null)
|
||||
{
|
||||
// For non-running bots, check grain state positions
|
||||
var hasOpenPositions = _state.State.Positions?.Values.Any(p => !p.IsFinished()) ?? false;
|
||||
var hasOpenPositions = _state.State.Positions?.Values.Any(p => p.IsOpen()) ?? false;
|
||||
_logger.LogDebug("Bot {GrainId} has open positions: {HasOpenPositions} (from grain state)",
|
||||
this.GetPrimaryKey(), hasOpenPositions);
|
||||
return Task.FromResult(hasOpenPositions);
|
||||
}
|
||||
|
||||
// For running bots, check live positions
|
||||
var hasLiveOpenPositions = _tradingBot.Positions?.Values.Any(p => !p.IsFinished()) ?? false;
|
||||
var hasLiveOpenPositions = _tradingBot.Positions?.Values.Any(p => p.IsOpen()) ?? false;
|
||||
_logger.LogDebug("Bot {GrainId} has open positions: {HasOpenPositions} (from live data)",
|
||||
this.GetPrimaryKey(), hasLiveOpenPositions);
|
||||
return Task.FromResult(hasLiveOpenPositions);
|
||||
@@ -958,13 +958,13 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
||||
_scopeFactory,
|
||||
async tradingService => await tradingService.GetPositionsByInitiatorIdentifierAsync(botId));
|
||||
|
||||
var hasOpenPositions = positions?.Any(p => !p.IsFinished()) ?? false;
|
||||
var hasOpenPositions = positions?.Any(p => p.IsOpen()) ?? false;
|
||||
_logger.LogDebug("Bot {GrainId} has open positions in database: {HasOpenPositions}",
|
||||
botId, hasOpenPositions);
|
||||
|
||||
if (hasOpenPositions)
|
||||
{
|
||||
var openPositions = positions?.Where(p => !p.IsFinished()).ToList() ?? new List<Position>();
|
||||
var openPositions = positions?.Where(p => p.IsOpen()).ToList() ?? new List<Position>();
|
||||
_logger.LogWarning(
|
||||
"Bot {GrainId} cannot be stopped - has {Count} open positions in database: {Positions}",
|
||||
botId, openPositions.Count, string.Join(", ", openPositions.Select(p => p.Identifier)));
|
||||
|
||||
@@ -193,7 +193,7 @@ public class TradingBotBase : ITradingBot
|
||||
public async Task UpdateSignals(HashSet<Candle> candles = null)
|
||||
{
|
||||
// If position open and not flipped, do not update signals
|
||||
if (!Config.FlipPosition && Positions.Any(p => !p.Value.IsFinished())) return;
|
||||
if (!Config.FlipPosition && Positions.Any(p => p.Value.IsOpen())) return;
|
||||
|
||||
// Check if we're in cooldown period for any direction
|
||||
if (await IsInCooldownPeriodAsync())
|
||||
@@ -277,7 +277,7 @@ public class TradingBotBase : ITradingBot
|
||||
private async Task ManagePositions()
|
||||
{
|
||||
// First, process all existing positions that are not finished
|
||||
foreach (var position in Positions.Values.Where(p => !p.IsFinished()))
|
||||
foreach (var position in Positions.Values.Where(p => p.IsOpen()))
|
||||
{
|
||||
var signalForPosition = Signals[position.SignalIdentifier];
|
||||
if (signalForPosition == null)
|
||||
@@ -306,6 +306,31 @@ public class TradingBotBase : ITradingBot
|
||||
await UpdatePosition(signalForPosition, position);
|
||||
}
|
||||
|
||||
// Second, process all finished positions to ensure they are updated in the database
|
||||
// This should be removed in the future, when we have a better way to handle positions
|
||||
foreach (var position in Positions.Values.Where(p => p.IsFinished()))
|
||||
{
|
||||
try
|
||||
{
|
||||
var positionInDatabase = await ServiceScopeHelpers.WithScopedService<ITradingService, Position>(_scopeFactory, async tradingService =>
|
||||
{
|
||||
return await tradingService.GetPositionByIdentifierAsync(position.Identifier);
|
||||
});
|
||||
|
||||
if (positionInDatabase != null && positionInDatabase.Status != position.Status)
|
||||
{
|
||||
await UpdatePositionDatabase(position);
|
||||
await LogInformation(
|
||||
$"💾 Database Update\nPosition: `{position.Identifier}`\nStatus: `{position.Status}`\nUpdated in database");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await LogWarning($"Failed to update finished position {position.Identifier} in database: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Then, open positions for signals waiting for a position open
|
||||
// But first, check if we already have a position for any of these signals
|
||||
var signalsWaitingForPosition = Signals.Values.Where(s => s.Status == SignalStatus.WaitingForPosition);
|
||||
@@ -830,7 +855,7 @@ public class TradingBotBase : ITradingBot
|
||||
|
||||
// Check for any existing open position (not finished) for this ticker
|
||||
var openedPosition =
|
||||
Positions.Values.FirstOrDefault(p => !p.IsFinished() && p.SignalIdentifier != signal.Identifier);
|
||||
Positions.Values.FirstOrDefault(p => p.IsOpen() && p.SignalIdentifier != signal.Identifier);
|
||||
|
||||
decimal lastPrice = await ServiceScopeHelpers.WithScopedService<IExchangeService, decimal>(_scopeFactory,
|
||||
async exchangeService =>
|
||||
@@ -1708,8 +1733,8 @@ public class TradingBotBase : ITradingBot
|
||||
|
||||
public int GetWinRate()
|
||||
{
|
||||
var succeededPositions = Positions.Values.Where(p => p.IsFinished()).Count(p => p.ProfitAndLoss?.Realized > 0);
|
||||
var total = Positions.Values.Where(p => p.IsFinished()).Count();
|
||||
var succeededPositions = Positions.Values.Where(p => p.IsValidForMetrics()).Count(p => p.IsInProfit());
|
||||
var total = Positions.Values.Where(p => p.IsValidForMetrics()).Count();
|
||||
|
||||
if (total == 0)
|
||||
return 0;
|
||||
|
||||
@@ -484,7 +484,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
||||
{
|
||||
// Check if position was active at this hour
|
||||
var wasActiveAtThisHour = position.Date <= hourDateTime &&
|
||||
(!position.IsFinished() ||
|
||||
(position.IsOpen() ||
|
||||
(position.StopLoss.Status == TradeStatus.Filled &&
|
||||
position.StopLoss.Date > hourDateTime) ||
|
||||
(position.TakeProfit1.Status == TradeStatus.Filled &&
|
||||
@@ -527,7 +527,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
||||
}
|
||||
|
||||
// Add closing volume if position was closed on or before this day
|
||||
if (position.IsFinished())
|
||||
if (position.IsValidForMetrics())
|
||||
{
|
||||
if (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate)
|
||||
{
|
||||
@@ -559,7 +559,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
||||
}
|
||||
|
||||
// Calculate CUMULATIVE fees and PnL for positions closed on or before this day
|
||||
var wasClosedOnOrBeforeThisDay = position.IsFinished() && (
|
||||
var wasClosedOnOrBeforeThisDay = position.IsValidForMetrics() && (
|
||||
(position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) ||
|
||||
(position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) ||
|
||||
(position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled &&
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace Managing.Application.ManageBot
|
||||
|
||||
// Calculate ROI based on total investment
|
||||
var totalInvestment = botData.Positions.Values
|
||||
.Where(p => p.IsFinished())
|
||||
.Where(p => p.IsValidForMetrics())
|
||||
.Sum(p => p.Open.Quantity * p.Open.Price);
|
||||
var netPnl = pnl - fees;
|
||||
var roi = totalInvestment > 0 ? (netPnl / totalInvestment) * 100 : 0;
|
||||
|
||||
Reference in New Issue
Block a user