Update bot management
This commit is contained in:
@@ -39,120 +39,183 @@ public class PositionManagerWorker : BaseWorker<PositionManagerWorker>
|
||||
protected override async Task Run(CancellationToken cancellationToken)
|
||||
{
|
||||
await ManageNewPositions();
|
||||
await ManagePartillyFilledPositions();
|
||||
await ManagePartiallyFilledPositions();
|
||||
await ManageFilledPositions();
|
||||
}
|
||||
|
||||
private async Task ManagePartillyFilledPositions()
|
||||
private async Task ManagePartiallyFilledPositions()
|
||||
{
|
||||
var positions = GetPositions(PositionStatus.PartiallyFilled);
|
||||
|
||||
_logger.LogInformation("Partilly filled positions count : {0} ", positions.Count());
|
||||
_logger.LogInformation("Processing {PartiallyFilledCount} partially filled positions", positions.Count());
|
||||
|
||||
foreach (var position in positions)
|
||||
{
|
||||
_logger.LogInformation("Managing Partilly filled position: {0} - Date: {1} - Direction: {2} - Ticker: {3}",
|
||||
position.Identifier, position.Date, position.OriginDirection, position.Ticker);
|
||||
|
||||
var account = await _accountService.GetAccount(position.AccountName, false, false);
|
||||
|
||||
try
|
||||
using (_logger.BeginScope("Position {PositionId} ({Ticker})", position.Identifier, position.Ticker))
|
||||
{
|
||||
if (position.StopLoss.Status == TradeStatus.PendingOpen)
|
||||
try
|
||||
{
|
||||
var stopLoss = await _exchangeService.OpenStopLoss(account, position.Ticker,
|
||||
position.OriginDirection,
|
||||
position.StopLoss.Price, position.StopLoss.Quantity, false, DateTime.UtcNow);
|
||||
// Lock position for processing
|
||||
position.Status = PositionStatus.Updating;
|
||||
_tradingService.UpdatePosition(position);
|
||||
|
||||
_logger.LogDebug("Processing risk orders for {Direction} position opened at {OpenDate}",
|
||||
position.OriginDirection, position.Date.ToString("o"));
|
||||
|
||||
if (stopLoss != null & (stopLoss.Status == TradeStatus.Requested))
|
||||
{
|
||||
position.StopLoss = stopLoss;
|
||||
_logger.LogInformation("|_ SL is requested");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Stop loss not requested");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"|_ SL is already handle. Current status = {position.StopLoss.Status}");
|
||||
}
|
||||
var account = await _accountService.GetAccount(position.AccountName, false, false);
|
||||
var success = true;
|
||||
|
||||
if (position.TakeProfit1.Status == TradeStatus.PendingOpen)
|
||||
{
|
||||
var takeProfit1 = await _exchangeService.OpenTakeProfit(account, position.Ticker,
|
||||
position.OriginDirection, position.TakeProfit1.Price, position.TakeProfit1.Quantity, false,
|
||||
DateTime.UtcNow);
|
||||
// Process and update trades
|
||||
var updatedSl = await ProcessTrade(account, position.StopLoss, "SL", async () =>
|
||||
await _exchangeService.OpenStopLoss(account, position.Ticker, position.OriginDirection,
|
||||
position.StopLoss.Price, position.StopLoss.Quantity, false, DateTime.UtcNow));
|
||||
|
||||
if (updatedSl != null)
|
||||
{
|
||||
position.StopLoss = updatedSl;
|
||||
success &= updatedSl.Status.IsActive();
|
||||
}
|
||||
|
||||
if (takeProfit1 != null & (takeProfit1.Status == TradeStatus.Requested))
|
||||
var updatedTp1 = await ProcessTrade(account, position.TakeProfit1, "TP1", async () =>
|
||||
await _exchangeService.OpenTakeProfit(account, position.Ticker, position.OriginDirection,
|
||||
position.TakeProfit1.Price, position.TakeProfit1.Quantity, false, DateTime.UtcNow));
|
||||
|
||||
if (updatedTp1 != null)
|
||||
{
|
||||
position.TakeProfit1 = takeProfit1;
|
||||
_logger.LogInformation("|_ TP is requested");
|
||||
position.TakeProfit1 = updatedTp1;
|
||||
success &= updatedTp1.Status.IsActive();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Take Profit 1 not requested");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"|_ TP is already handle. Current status = {position.TakeProfit1.Status}");
|
||||
}
|
||||
|
||||
if (position.TakeProfit2 != null &&
|
||||
position.TakeProfit2.Status == TradeStatus.PendingOpen)
|
||||
{
|
||||
var takeProfit2 = await _exchangeService.OpenTakeProfit(account, position.Ticker,
|
||||
position.OriginDirection, position.TakeProfit2.Price, position.TakeProfit2.Quantity, false,
|
||||
DateTime.UtcNow);
|
||||
Trade? updatedTp2 = null;
|
||||
if (position.TakeProfit2 != null)
|
||||
{
|
||||
updatedTp2 = await ProcessTrade(account, position.TakeProfit2, "TP2", async () =>
|
||||
await _exchangeService.OpenTakeProfit(account, position.Ticker, position.OriginDirection,
|
||||
position.TakeProfit2.Price, position.TakeProfit2.Quantity, false, DateTime.UtcNow));
|
||||
|
||||
if (updatedTp2 != null)
|
||||
{
|
||||
position.TakeProfit2 = updatedTp2;
|
||||
success &= updatedTp2.Status.IsActive() || updatedTp2.Status == TradeStatus.Cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
if (takeProfit2 != null & (takeProfit2.Status == TradeStatus.Requested))
|
||||
{
|
||||
position.TakeProfit2 = takeProfit2;
|
||||
_logger.LogInformation("|_ TP2 is requested");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Take Profit 2 not requested");
|
||||
}
|
||||
// Update position status based on trade states
|
||||
position.Status = success && AllTradesActive(position)
|
||||
? PositionStatus.Filled
|
||||
: PositionStatus.PartiallyFilled;
|
||||
|
||||
_logger.LogInformation("Final position status: {Status}", position.Status);
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogInformation("|_ TP2 is already handle or not required");
|
||||
_logger.LogError(ex, "Position processing failed");
|
||||
position.Status = PositionStatus.PartiallyFilled;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_tradingService.UpdatePosition(position);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"|_ Cannot fully filled position because : {ex.Message}");
|
||||
}
|
||||
|
||||
TradeStatus[] validStatus = [TradeStatus.Requested, TradeStatus.Filled];
|
||||
if (validStatus.Contains(position.StopLoss.Status)
|
||||
&& (validStatus.Contains(position.TakeProfit1.Status)))
|
||||
{
|
||||
position.Status = PositionStatus.Filled;
|
||||
_logger.LogInformation($"|_ Position is now open and SL/TP are correctly requested");
|
||||
}
|
||||
|
||||
_tradingService.UpdatePosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Trade?> ProcessTrade(Account account, Trade trade, string tradeType, Func<Task<Trade>> createTrade)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. Check existing status on exchange
|
||||
var exchangeTrade = await _exchangeService.GetTrade(account, trade.ExchangeOrderId, trade.Ticker);
|
||||
if (exchangeTrade != null && exchangeTrade.Status.IsActive())
|
||||
{
|
||||
_logger.LogInformation("{TradeType} already exists on exchange - Status: {Status}",
|
||||
tradeType, exchangeTrade.Status);
|
||||
return exchangeTrade;
|
||||
}
|
||||
|
||||
// 2. Only create new order if in pending state
|
||||
if (trade.Status != TradeStatus.PendingOpen)
|
||||
{
|
||||
_logger.LogWarning("{TradeType} creation skipped - Invalid status: {Status}",
|
||||
tradeType, trade.Status);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Create new order
|
||||
var newTrade = await createTrade();
|
||||
if (newTrade?.Status == TradeStatus.Requested)
|
||||
{
|
||||
_logger.LogInformation("{TradeType} successfully created - ExchangeID: {ExchangeOrderId}",
|
||||
tradeType, newTrade.ExchangeOrderId);
|
||||
return newTrade;
|
||||
}
|
||||
|
||||
_logger.LogError("{TradeType} creation failed - Null response or invalid status", tradeType);
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "{TradeType} processing failed", tradeType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool AllTradesActive(Position position)
|
||||
{
|
||||
return position.StopLoss.Status.IsActive() &&
|
||||
position.TakeProfit1.Status.IsActive() &&
|
||||
(position.TakeProfit2?.Status.IsActive() ?? true);
|
||||
}
|
||||
private async Task ManageFilledPositions()
|
||||
{
|
||||
var positions = GetPositions(PositionStatus.Filled);
|
||||
|
||||
_logger.LogInformation("Filled positions count : {0} ", positions.Count());
|
||||
_logger.LogInformation("Monitoring {FilledPositionCount} filled positions", positions.Count());
|
||||
|
||||
foreach (var position in positions)
|
||||
{
|
||||
_logger.LogInformation("Managing filled position: {0} - Date: {1} - Direction: {2} - Ticker: {3}",
|
||||
position.Identifier, position.Date, position.OriginDirection, position.Ticker);
|
||||
var account = await GetAccount(position.AccountName);
|
||||
using (_logger.BeginScope("Position {PositionId} ({Ticker})", position.Identifier, position.Ticker))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Acquire processing lock
|
||||
_logger.LogDebug("Acquiring position lock");
|
||||
position.Status = PositionStatus.Updating;
|
||||
_tradingService.UpdatePosition(position);
|
||||
|
||||
var updatedPosition = await _tradingService.ManagePosition(account, position);
|
||||
_tradingService.UpdatePosition(updatedPosition);
|
||||
_logger.LogInformation("Managing filled position - Direction: {Direction}, Open Since: {OpenDate}",
|
||||
position.OriginDirection, position.Date.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||
|
||||
var account = await GetAccount(position.AccountName);
|
||||
|
||||
// Perform position management
|
||||
var updatedPosition = await _tradingService.ManagePosition(account, position);
|
||||
|
||||
// Log status changes if they occurred
|
||||
if (updatedPosition.Status != position.Status)
|
||||
{
|
||||
_logger.LogInformation("Position status updated: {OldStatus} → {NewStatus}",
|
||||
position.Status, updatedPosition.Status);
|
||||
}
|
||||
|
||||
_tradingService.UpdatePosition(updatedPosition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to manage position - {ErrorMessage}", ex.Message);
|
||||
|
||||
// Reset status for retry
|
||||
position.Status = PositionStatus.Filled;
|
||||
_tradingService.UpdatePosition(position);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Ensure lock is always released
|
||||
if (position.Status == PositionStatus.Updating)
|
||||
{
|
||||
position.Status = PositionStatus.Filled;
|
||||
_tradingService.UpdatePosition(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user