Refactor BotController and BotService for improved bot management

- Cleaned up constructor parameters in BotController for better readability.
- Enhanced StartCopyTradingCommand handling with improved formatting.
- Updated bot deletion logic in BotService to delete associated positions and trigger agent summary updates.
- Added new method in TradingService for deleting positions by initiator identifier.
- Implemented error handling in StopBotCommandHandler to ensure agent summary updates do not disrupt bot stop operations.
This commit is contained in:
2025-11-23 15:30:11 +07:00
parent 9c8ab71736
commit 411fc41bef
8 changed files with 558 additions and 13 deletions

View File

@@ -20,7 +20,6 @@ using MediatR;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using static Managing.Common.Enums; using static Managing.Common.Enums;
namespace Managing.Api.Controllers; namespace Managing.Api.Controllers;
@@ -62,7 +61,8 @@ public class BotController : BaseController
public BotController(ILogger<BotController> logger, IMediator mediator, IHubContext<BotHub> hubContext, public BotController(ILogger<BotController> logger, IMediator mediator, IHubContext<BotHub> hubContext,
IBacktester backtester, IBotService botService, IUserService userService, IBacktester backtester, IBotService botService, IUserService userService,
IAccountService accountService, IMoneyManagementService moneyManagementService, IAccountService accountService, IMoneyManagementService moneyManagementService,
IServiceScopeFactory scopeFactory, IAdminConfigurationService adminService, IConfiguration configuration) : base(userService) IServiceScopeFactory scopeFactory, IAdminConfigurationService adminService,
IConfiguration configuration) : base(userService)
{ {
_logger = logger; _logger = logger;
_mediator = mediator; _mediator = mediator;
@@ -172,7 +172,9 @@ public class BotController : BaseController
return Unauthorized("User not found"); return Unauthorized("User not found");
} }
var result = await _mediator.Send(new StartCopyTradingCommand(request.MasterBotIdentifier, request.BotTradingBalance, user)); var result =
await _mediator.Send(new StartCopyTradingCommand(request.MasterBotIdentifier, request.BotTradingBalance,
user));
await NotifyBotSubscriberAsync(); await NotifyBotSubscriberAsync();
return Ok(result); return Ok(result);
@@ -306,7 +308,8 @@ public class BotController : BaseController
var result = await _botService.DeleteBot(identifier); var result = await _botService.DeleteBot(identifier);
await NotifyBotSubscriberAsync(); await NotifyBotSubscriberAsync();
return Ok(result);
return result ? Ok(result) : Problem($"Failed to delete bot with identifier {identifier}");
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -27,6 +27,7 @@ public interface ITradingRepository
Task<IEnumerable<Position>> GetPositionsByStatusAsync(PositionStatus positionStatus); Task<IEnumerable<Position>> GetPositionsByStatusAsync(PositionStatus positionStatus);
Task<IEnumerable<Position>> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier); Task<IEnumerable<Position>> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier);
Task<IEnumerable<Position>> GetPositionsByInitiatorIdentifiersAsync(IEnumerable<Guid> initiatorIdentifiers); Task<IEnumerable<Position>> GetPositionsByInitiatorIdentifiersAsync(IEnumerable<Guid> initiatorIdentifiers);
Task DeletePositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier);
Task<IEnumerable<Position>> GetAllPositionsAsync(); Task<IEnumerable<Position>> GetAllPositionsAsync();
Task<decimal> GetGlobalPnLFromPositionsAsync(); Task<decimal> GetGlobalPnLFromPositionsAsync();

View File

@@ -36,6 +36,7 @@ public interface ITradingService
Task<IEnumerable<Position>> GetAllDatabasePositionsAsync(); Task<IEnumerable<Position>> GetAllDatabasePositionsAsync();
Task<IEnumerable<Position>> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier); Task<IEnumerable<Position>> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier);
Task<IEnumerable<Position>> GetPositionsByInitiatorIdentifiersAsync(IEnumerable<Guid> initiatorIdentifiers); Task<IEnumerable<Position>> GetPositionsByInitiatorIdentifiersAsync(IEnumerable<Guid> initiatorIdentifiers);
Task DeletePositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier);
Task<decimal> GetGlobalPnLFromPositionsAsync(); Task<decimal> GetGlobalPnLFromPositionsAsync();
Task<PrivyInitAddressResponse> InitPrivyWallet(string publicAddress, TradingExchanges tradingExchange); Task<PrivyInitAddressResponse> InitPrivyWallet(string publicAddress, TradingExchanges tradingExchange);

View File

@@ -74,6 +74,11 @@ namespace Managing.Application.ManageBot
var account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>( var account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>(
_scopeFactory, _scopeFactory,
async accountService => await accountService.GetAccount(config.AccountName, true, false)); async accountService => await accountService.GetAccount(config.AccountName, true, false));
// Delete all positions for this bot from the database
await _tradingService.DeletePositionsByInitiatorIdentifierAsync(identifier);
_tradingBotLogger.LogInformation("Deleted all positions for bot {BotId}", identifier);
await grain.StopAsync("Deleting bot"); await grain.StopAsync("Deleting bot");
await _botRepository.DeleteBot(identifier); await _botRepository.DeleteBot(identifier);
await grain.DeleteAsync(); await grain.DeleteAsync();
@@ -85,12 +90,17 @@ namespace Managing.Application.ManageBot
$"⚠️ Bot has been permanently deleted and all data removed"; $"⚠️ Bot has been permanently deleted and all data removed";
await _messengerService.SendTradeMessage(deleteMessage, false, account.User); await _messengerService.SendTradeMessage(deleteMessage, false, account.User);
// Trigger agent summary update after bot deletion
var agentGrain = _grainFactory.GetGrain<IAgentGrain>(account.User.Id);
await agentGrain.ForceUpdateSummary();
return true; return true;
} }
catch (Exception e) catch (Exception e)
{ {
_tradingBotLogger.LogError(e, "Error deleting bot {Identifier}", identifier); _tradingBotLogger.LogError(e, "Error deleting bot {Identifier}", identifier);
return false; throw;
} }
} }
@@ -111,13 +121,13 @@ namespace Managing.Application.ManageBot
// Check balances for EVM/GMX V2 bots before starting/restarting // Check balances for EVM/GMX V2 bots before starting/restarting
var botConfig = await botGrain.GetConfiguration(); var botConfig = await botGrain.GetConfiguration();
Account account; Account account;
if (string.IsNullOrEmpty(botConfig.AccountName)) if (string.IsNullOrEmpty(botConfig.AccountName))
{ {
// Fallback: Get the first account for the user // Fallback: Get the first account for the user
var user = await botGrain.GetUserAsync(); var user = await botGrain.GetUserAsync();
account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>( account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>(
_scopeFactory, _scopeFactory,
async accountService => async accountService =>
@@ -128,11 +138,13 @@ namespace Managing.Application.ManageBot
{ {
throw new InvalidOperationException($"User '{user.Name}' has no accounts configured."); throw new InvalidOperationException($"User '{user.Name}' has no accounts configured.");
} }
return firstAccount; return firstAccount;
}); });
botConfig.AccountName = account.Name; botConfig.AccountName = account.Name;
_tradingBotLogger.LogInformation("Bot '{BotName}' (ID: {BotId}) using fallback account '{AccountName}' for user '{UserName}'", _tradingBotLogger.LogInformation(
"Bot '{BotName}' (ID: {BotId}) using fallback account '{AccountName}' for user '{UserName}'",
botConfig.Name, identifier, account.Name, user.Name); botConfig.Name, identifier, account.Name, user.Name);
} }
else else
@@ -421,7 +433,7 @@ namespace Managing.Application.ManageBot
} }
var usdcValue = usdcBalance?.Amount ?? 0m; var usdcValue = usdcBalance?.Amount ?? 0m;
// Get positions for the user and add their USDC value (similar to AgentGrain.UpdateSummary) // Get positions for the user and add their USDC value (similar to AgentGrain.UpdateSummary)
try try
{ {
@@ -437,7 +449,9 @@ namespace Managing.Application.ManageBot
} }
catch (Exception ex) catch (Exception ex)
{ {
_tradingBotLogger.LogError(ex, "Error calculating position values for available allocation for user {UserId}", account.User.Id); _tradingBotLogger.LogError(ex,
"Error calculating position values for available allocation for user {UserId}",
account.User.Id);
// Continue with calculation even if position retrieval fails // Continue with calculation even if position retrieval fails
} }

View File

@@ -1,6 +1,11 @@
using Managing.Application.Abstractions; using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.ManageBot.Commands; using Managing.Application.ManageBot.Commands;
using Managing.Core;
using Managing.Domain.Accounts;
using MediatR; using MediatR;
using Microsoft.Extensions.DependencyInjection;
using static Managing.Common.Enums; using static Managing.Common.Enums;
namespace Managing.Application.ManageBot namespace Managing.Application.ManageBot
@@ -8,15 +13,46 @@ namespace Managing.Application.ManageBot
public class StopBotCommandHandler : IRequestHandler<StopBotCommand, BotStatus> public class StopBotCommandHandler : IRequestHandler<StopBotCommand, BotStatus>
{ {
private readonly IBotService _botService; private readonly IBotService _botService;
private readonly IServiceScopeFactory _scopeFactory;
private readonly IGrainFactory _grainFactory;
public StopBotCommandHandler(IBotService botService) public StopBotCommandHandler(IBotService botService, IServiceScopeFactory scopeFactory, IGrainFactory grainFactory)
{ {
_botService = botService; _botService = botService;
_scopeFactory = scopeFactory;
_grainFactory = grainFactory;
} }
public async Task<BotStatus> Handle(StopBotCommand request, CancellationToken cancellationToken) public async Task<BotStatus> Handle(StopBotCommand request, CancellationToken cancellationToken)
{ {
return await _botService.StopBot(request.Identifier); var result = await _botService.StopBot(request.Identifier);
try
{
// Get bot configuration to find the user for agent summary update
var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(request.Identifier);
var config = await grain.GetConfiguration();
// Get account to find user ID for agent grain
var account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>(
_scopeFactory,
async accountService => await accountService.GetAccount(config.AccountName, true, false));
if (account?.User != null)
{
// Trigger agent summary update after stopping the bot
var agentGrain = _grainFactory.GetGrain<IAgentGrain>(account.User.Id);
await agentGrain.ForceUpdateSummary();
}
}
catch (Exception ex)
{
// Log the error but don't fail the stop operation
// The bot was successfully stopped, we just couldn't update the summary
Console.WriteLine($"Failed to update agent summary after stopping bot {request.Identifier}: {ex.Message}");
}
return result;
} }
} }
} }

View File

@@ -256,6 +256,11 @@ public class TradingService : ITradingService
return await _tradingRepository.GetPositionsByInitiatorIdentifiersAsync(initiatorIdentifiers); return await _tradingRepository.GetPositionsByInitiatorIdentifiersAsync(initiatorIdentifiers);
} }
public async Task DeletePositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier)
{
await _tradingRepository.DeletePositionsByInitiatorIdentifierAsync(initiatorIdentifier);
}
public async Task<decimal> GetGlobalPnLFromPositionsAsync() public async Task<decimal> GetGlobalPnLFromPositionsAsync()
{ {
return await _tradingRepository.GetGlobalPnLFromPositionsAsync(); return await _tradingRepository.GetGlobalPnLFromPositionsAsync();

View File

@@ -0,0 +1,407 @@
# Indicators Documentation
## Understanding Indicator Types
This documentation covers three main types of indicators used in the strategy combination system:
### Signal Indicators
**Purpose**: Generate precise entry/exit signals when specific market conditions are met
- **When to use**: For identifying high-probability trade entries and exits
- **Behavior**: Generate signals infrequently, only when patterns are detected
- **Examples**: RSI Divergence, MACD Cross, Chandelier Exit
- **Lookback Period Usage**: Determines how far back to analyze for pattern recognition (e.g., divergence detection over 5-10 candles)
### Trend Indicators
**Purpose**: Identify overall market direction and momentum
- **When to use**: For confirming market direction and avoiding counter-trend trades
- **Behavior**: Provide continuous trend assessment (Long/Short/Neutral) for each candlestick
- **Examples**: EMA Trend, Stochastic RSI Trend
- **Lookback Period Usage**: Sets the timeframe for trend calculation (e.g., 20-50 periods for reliable trend signals)
### Context Indicators
**Purpose**: Provide market context to prevent trades in unfavorable conditions
- **When to use**: For risk management and avoiding trades in poor market conditions
- **Behavior**: Act as filters with veto power - can block otherwise valid signals
- **Examples**: Volatility filters, market condition assessments
- **Lookback Period Usage**: Determines volatility measurement window for context assessment
## Using Lookback Periods for Different Scenarios
### Short-term Trading (Scalping/Day Trading)
- **Signal Indicators**: Lookback 3-5 periods - quick pattern recognition
- **Trend Indicators**: Lookback 10-20 periods - responsive to recent price action
- **Context Indicators**: Lookback 10-20 periods - capture short-term volatility spikes
### Medium-term Trading (Swing Trading)
- **Signal Indicators**: Lookback 5-10 periods - balanced pattern detection
- **Trend Indicators**: Lookback 20-50 periods - reliable trend identification
- **Context Indicators**: Lookback 20-30 periods - meaningful volatility assessment
### Long-term Trading (Position Trading)
- **Signal Indicators**: Lookback 10-15 periods - comprehensive pattern analysis
- **Trend Indicators**: Lookback 50-100 periods - stable trend confirmation
- **Context Indicators**: Lookback 30-50 periods - long-term volatility context
### High Volatility Markets
- **Signal Indicators**: Shorter lookback periods - avoid noise from extreme moves
- **Trend Indicators**: Standard periods - trends still need confirmation
- **Context Indicators**: Longer lookback periods - better volatility normalization
### Low Volatility Markets
- **Signal Indicators**: Longer lookback periods - capture subtle patterns
- **Trend Indicators**: Standard periods - maintain trend sensitivity
- **Context Indicators**: Shorter lookback periods - detect volatility contraction
## Indicator Categories
- **ChandelierExitIndicatorBase** - Chandelier Exit trailing stop indicator
- **DualEmaCrossIndicatorBase** - Dual EMA crossover signals
- **EmaCrossIndicator** - Exponential Moving Average crossover signals
- **EmaCrossIndicatorBase** - Base class for EMA cross indicators
- **LaggingSTC** - Lagging Schaff Trend Cycle indicator
- **MacdCrossIndicatorBase** - MACD crossover signals
- **RsiDivergenceConfirmIndicatorBase** - RSI divergence confirmation signals
- **RsiDivergenceIndicatorBase** - RSI divergence detection signals
- **StcIndicatorBase** - Schaff Trend Cycle indicator base
- **SuperTrendCrossEma** - SuperTrend with EMA crossover signals
- **SuperTrendIndicatorBase** - SuperTrend indicator base
- **ThreeWhiteSoldiersIndicatorBase** - Three White Soldiers candlestick pattern
### Trend Indicators
Trend indicators identify the direction and strength of market trends.
- **EmaTrendIndicatorBase** - Exponential Moving Average trend detection
- **StochRsiTrendIndicatorBase** - Stochastic RSI trend indicator
### Context Indicators
Context indicators provide market context and act as filters to prevent trades in unfavorable conditions. They work with confidence levels rather than simple allow/block decisions, providing veto power in the strategy combination system.
**Confidence Levels (Veto System):**
- **High Confidence**: Ideal market conditions (e.g., low volatility) - optimal for trading
- **Medium Confidence**: Normal market conditions - standard trading environment
- **Low Confidence**: Elevated risk conditions - trade with caution or reduced position size
- **None Confidence**: Poor conditions - blocks trading entirely
**Behavior in Strategy Combinations:**
- All context indicators must meet minimum confidence thresholds for trades to proceed
- Context indicators have veto power - they can block otherwise valid signals
- Used for risk management and market condition filtering
- **StDevContext** - Standard deviation context for volatility analysis using Z-score measurements
---
## RSI Divergence
**Type:** Signal
**Category:** RSI
**Description:**
Detects RSI divergence patterns where price and RSI move in opposite directions. This powerful reversal indicator identifies when momentum disagrees with price action, often signaling potential trend changes before they become obvious to other indicators.
**Trigger a Short when**
• Previous candle close < current candle close (price going up)
• Current RSI < maximum RSI in lookback period (RSI making lower high)
• Current close > maximum price in lookback period (price making higher high)
• Bearish divergence confirmed
**Trigger a Long when**
• Previous candle close > current candle close (price going down)
• Current RSI > minimum RSI in lookback period (RSI making higher low)
• Current close < minimum price in lookback period (price making lower low)
• Bullish divergence confirmed
**Parameters**
• Period (recommended: 14) - RSI calculation period, typically 14 periods
• Lookback Period (recommended: 5-10) - Number of candles to analyze for divergence patterns
---
## EMA Cross
**Type:** Signal
**Category:** EMA
**Description:**
Generates precise entry/exit signals when price crosses the Exponential Moving Average line. EMA gives more weight to recent prices, making it responsive to current market conditions while filtering out noise. Crossovers indicate momentum shifts and potential trend changes.
**Trigger a Short when**
• Previous candle close > EMA value
• Current candle close < EMA value
• Price crosses from above to below EMA line
**Trigger a Long when**
• Previous candle close < EMA value
• Current candle close > EMA value
• Price crosses from below to above EMA line
**Parameters**
• Period (recommended: 2050) - Number of periods for EMA calculation. Shorter periods (20-30) are more responsive but noisier, longer periods (40-50) are smoother but slower
---
## Dual EMA Cross
**Type:** Signal
**Category:** EMA
**Description:**
Uses two Exponential Moving Averages of different speeds to generate trend-following signals. The fast EMA responds quickly to price changes while the slow EMA provides trend confirmation, creating reliable crossover signals that identify major trend shifts.
**Trigger a Short when**
• Previous candle fast EMA > slow EMA
• Current candle fast EMA < slow EMA
• Fast EMA crosses from above to below slow EMA
**Trigger a Long when**
• Previous candle fast EMA < slow EMA
• Current candle fast EMA > slow EMA
• Fast EMA crosses from below to above slow EMA
**Parameters**
• Fast Period (recommended: 912) - Short-term EMA for quick signals, typically 9-12 periods
• Slow Period (recommended: 2126) - Long-term EMA for trend confirmation, typically 21-26 periods
---
## MACD Cross
**Type:** Signal
**Category:** MACD
**Description:**
Generates signals when the MACD line crosses the signal line, indicating changes in momentum. MACD (Moving Average Convergence Divergence) combines trend-following and momentum oscillators to identify when buying/selling pressure shifts, often providing early signals of trend changes.
**Trigger a Short when**
• Previous candle MACD > signal line
• Current candle MACD < signal line
• MACD line crosses from above to below signal line
**Trigger a Long when**
• Previous candle MACD < signal line
• Current candle MACD > signal line
• MACD line crosses from below to above signal line
**Parameters**
• Fast Period (recommended: 12) - Fast EMA period for MACD calculation, typically 12 periods
• Slow Period (recommended: 26) - Slow EMA period for MACD calculation, typically 26 periods
• Signal Period (recommended: 9) - EMA smoothing period for signal line, typically 9 periods
---
## SuperTrend
**Type:** Signal
**Category:** Trend Following
**Description:**
Uses ATR-based trailing stop levels to identify trend direction and generate signals. Combines volatility measurement with trend following to create dynamic support/resistance levels that adapt to market conditions, providing clear entry/exit points with built-in risk management.
**Trigger a Short when**
• Current candle close < SuperTrend value
• Price is below the SuperTrend trailing stop line
**Trigger a Long when**
• Current candle close > SuperTrend value
• Price is above the SuperTrend trailing stop line
**Parameters**
• Period (recommended: 1014) - ATR calculation period for volatility measurement, typically 10-14 periods
• Multiplier (recommended: 2.03.0) - ATR multiplier for stop distance, higher values create wider stops
---
## Chandelier Exit
**Type:** Signal
**Category:** Volatility
**Description:** ATR-based trailing stop that adjusts based on recent volatility, using separate calculations for long and short positions
**Trigger a Short when →**
• Current close < previous Chandelier exit level
• Previous close > previous Chandelier exit level
• Current close < previous candle open price
• Using Short Chandelier type calculation
**Trigger a Long when →**
• Current close > previous Chandelier exit level
• Previous close < previous Chandelier exit level
• Current close > current candle open price
• Using Long Chandelier type calculation
**Parameters:**
- Period (recommended: 22) - ATR calculation period for volatility measurement
- Multiplier (recommended: 3.0) - ATR multiplier for trailing stop distance
---
## STC (Schaff Trend Cycle)
**Type:** Signal
**Category:** STC
**Description:**
Advanced cycle oscillator that combines MACD with Stochastic calculations to identify trend cycles. Oscillates between 0-100 with double-smoothing to reduce noise, providing clear overbought/oversold signals that often precede major trend changes in cyclic markets.
**Trigger a Short when**
• Previous candle STC > 75
• Current candle STC ≤ 75
• STC crosses from above 75 to 75 or below
**Trigger a Long when**
• Previous candle STC < 25
• Current candle STC ≥ 25
• STC crosses from below 25 to 25 or above
**Parameters**
• Cycle Period (recommended: 10) - Primary cycle length for double-smoothing, typically 10 periods
• Fast Period (recommended: 23) - Fast EMA period for MACD calculation, typically 23 periods
• Slow Period (recommended: 50) - Slow EMA period for MACD calculation, typically 50 periods
---
## SuperTrend Cross EMA
**Type:** Signal
**Category:** EMA
**Description:** Combines SuperTrend with EMA crossover for enhanced trend confirmation
**Trigger a Short when →** SuperTrend confirms downtrend and price below EMA
**Trigger a Long when →** SuperTrend confirms uptrend and price above EMA
**Parameters:**
- SuperTrend Period (recommended: 10-14)
- SuperTrend Multiplier (recommended: 2.0-3.0)
- EMA Period (recommended: 20-50)
---
## Three White Soldiers
**Type:** Signal
**Category:** Candlestick
**Description:** Bullish reversal pattern consisting of three consecutive white candles
**Trigger a Short when →** N/A (bullish only pattern)
**Trigger a Long when →** Three consecutive higher closes forming white candles
**Parameters:**
- Lookback Period (recommended: 3-5 candles)
---
## RSI Divergence Confirm
**Type:** Signal
**Category:** RSI
**Description:** Detects RSI divergences and confirms them with additional price action validation for higher reliability
**Divergence Detection Process:**
1. **Identifies divergence**: Same logic as RSI Divergence - detects when price and RSI move in opposite directions
2. **Initial signal**: Creates low-confidence signal (Confidence.None) when divergence is spotted
3. **Confirmation phase**: Monitors subsequent price action to validate the divergence
**Confirmation Logic:**
**Long confirmation**: Divergence signal is confirmed when current close price breaks above the divergence signal's open price
**Short confirmation**: Divergence signal is confirmed when current close price breaks below the divergence signal's open price
• When confirmed, generates High confidence signal and expires the original low-confidence signal
**Trigger a Short when →** Bearish divergence confirmed by price breaking below signal candle's open
**Trigger a Long when →** Bullish divergence confirmed by price breaking above signal candle's open
**Parameters:**
- RSI Period (recommended: 14) - RSI calculation period for divergence detection
---
## EMA Trend
**Type:** Trend
**Category:** EMA
**Description:**
Provides continuous trend context by comparing price position relative to Exponential Moving Average. Unlike crossover signals, this indicator gives ongoing trend direction information, useful for trend filtering and position management in trending markets.
**Trigger a Short when**
• Current candle close < EMA value
• Price is below the EMA line (bearish trend context)
**Trigger a Long when**
• Current candle close > EMA value
• Price is above the EMA line (bullish trend context)
**Parameters**
• Period (recommended: 2050) - EMA calculation period, longer periods provide more reliable trend signals
• Slope Threshold (recommended: 0.10.5%) - Minimum EMA slope change to confirm trend strength
---
## Stochastic RSI Trend
**Type:** Trend
**Category:** RSI
**Description:**
Combines RSI momentum with Stochastic overbought/oversold levels for enhanced trend analysis. Applies Stochastic oscillator to RSI values instead of price, reducing false signals while providing clear trend context based on momentum extremes.
**Trigger a Short when**
• Current candle Stochastic RSI signal > 80
• Indicates overbought conditions (bearish trend context)
**Trigger a Long when**
• Current candle Stochastic RSI signal < 20
• Indicates oversold conditions (bullish trend context)
**Parameters**
• RSI Period (recommended: 14) - RSI calculation period, typically 14 periods
• Stochastic Period (recommended: 14) - Lookback period for Stochastic calculation, typically 14 periods
• Signal Period (recommended: 3) - Smoothing period for signal line, typically 3 periods
• Smooth Period (recommended: 3) - Additional smoothing period, typically 3 periods
---
## Standard Deviation Context
**Type:** Context
**Category:** Volatility
**Description:** Provides volatility context using standard deviation and Z-score analysis to assess market conditions and trading suitability
**Trigger a Short when →** N/A (context indicator - no directional signals)
**Trigger a Long when →** N/A (context indicator - no directional signals)
**Context Confidence Levels (Veto System):**
**High Confidence** (|Z-score| ≤ 0.5) - Very low volatility, ideal trading conditions
**Medium Confidence** (|Z-score| ≤ 1.0) - Normal volatility, good trading conditions
**Low Confidence** (|Z-score| ≤ 1.5) - Elevated volatility, use caution
**None Confidence** (|Z-score| > 1.5) - High volatility, trading not recommended
**Parameters:**
- Period (recommended: 20-50) - Standard deviation calculation period for volatility measurement
---

View File

@@ -529,6 +529,84 @@ public class PostgreSqlTradingRepository : BaseRepositoryWithLogging, ITradingRe
return PostgreSqlMappers.Map(positions); return PostgreSqlMappers.Map(positions);
} }
public async Task DeletePositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier)
{
await ExecuteWithLoggingAsync(async () =>
{
try
{
await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
// First, collect all trade IDs that need to be deleted using separate queries for each trade type
var openTradeIds = await _context.Positions
.AsNoTracking()
.Where(p => p.InitiatorIdentifier == initiatorIdentifier && p.OpenTradeId.HasValue)
.Select(p => p.OpenTradeId.Value)
.ToListAsync()
.ConfigureAwait(false);
var stopLossTradeIds = await _context.Positions
.AsNoTracking()
.Where(p => p.InitiatorIdentifier == initiatorIdentifier && p.StopLossTradeId.HasValue)
.Select(p => p.StopLossTradeId.Value)
.ToListAsync()
.ConfigureAwait(false);
var takeProfit1TradeIds = await _context.Positions
.AsNoTracking()
.Where(p => p.InitiatorIdentifier == initiatorIdentifier && p.TakeProfit1TradeId.HasValue)
.Select(p => p.TakeProfit1TradeId.Value)
.ToListAsync()
.ConfigureAwait(false);
var takeProfit2TradeIds = await _context.Positions
.AsNoTracking()
.Where(p => p.InitiatorIdentifier == initiatorIdentifier && p.TakeProfit2TradeId.HasValue)
.Select(p => p.TakeProfit2TradeId.Value)
.ToListAsync()
.ConfigureAwait(false);
// Combine all trade IDs and remove duplicates
var tradeIdsToDelete = openTradeIds
.Concat(stopLossTradeIds)
.Concat(takeProfit1TradeIds)
.Concat(takeProfit2TradeIds)
.Distinct()
.ToList();
// Delete trades first (due to foreign key constraints)
if (tradeIdsToDelete.Any())
{
var tradesToDelete = await _context.Trades
.AsTracking()
.Where(t => tradeIdsToDelete.Contains(t.Id))
.ToListAsync()
.ConfigureAwait(false);
_context.Trades.RemoveRange(tradesToDelete);
}
// Then delete all positions with the given initiator identifier
var positionsToDelete = await _context.Positions
.AsTracking()
.Where(p => p.InitiatorIdentifier == initiatorIdentifier)
.ToListAsync()
.ConfigureAwait(false);
if (positionsToDelete.Any())
{
_context.Positions.RemoveRange(positionsToDelete);
}
await _context.SaveChangesAsync();
}
finally
{
await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
}
}, nameof(DeletePositionsByInitiatorIdentifierAsync), ("initiatorIdentifier", initiatorIdentifier));
}
public async Task<IEnumerable<Position>> GetAllPositionsAsync() public async Task<IEnumerable<Position>> GetAllPositionsAsync()
{ {
var positions = await _context.Positions var positions = await _context.Positions