diff --git a/.cursor/commands/update-test-todo.md b/.cursor/commands/update-test-todo.md new file mode 100644 index 00000000..05df67ec --- /dev/null +++ b/.cursor/commands/update-test-todo.md @@ -0,0 +1,199 @@ +# update-test-todo + +## When to Use + +Use this command when you need to: +- Update TODO.md with current test results from a test project +- Analyze test failures and identify business logic issues +- Set priorities for fixing failing tests +- Track progress on unit test development and bug fixes + +## Prerequisites + +- Test project exists and is runnable +- TODO.md file exists in project root +- Tests can be executed with `dotnet test` + +## Execution Steps + +### Step 1: Run Tests and Capture Results + +**Run the test project:** +```bash +cd src/YourTestProject +dotnet test --verbosity minimal | tail -20 +``` + +**Expected output format:** +``` +Passed! - Failed: X, Passed: Y, Skipped: Z, Total: T, Duration: D ms +``` + +### Step 2: Analyze Test Results + +**Count by category:** +- Identify which test classes have the most failures +- Group failures by business logic area (Trading, P&L, Signals, etc.) +- Determine if failures indicate business logic bugs vs incorrect test expectations + +**Example analysis:** +``` +MoneyManagementTests: 8 failures +SignalProcessingTests: 9 failures +TraderAnalysisTests: 3 failures +TradingMetricsTests: 0 failures (✅ working) +``` + +### Step 3: Update TODO.md Structure + +**Update test summary:** +```markdown +## Test Results Summary +**Total Tests:** T +- **Passed:** Y ✅ +- **Failed:** X ❌ (Category1: A, Category2: B, ...) +``` + +**Update priority sections:** +- Mark completed items as ✅ FIXED +- Move next priority items to "High Priority - Next Focus" +- Update investigation steps for current priority + +**Example:** +```markdown +### Critical Issues (High Priority) ✅ MOSTLY RESOLVED +1. **Volume Calculations**: ✅ FIXED - All TradingMetrics volume calculations working correctly + +### High Priority - Next Focus +5. **Money Management Optimization**: SL/TP calculations have incorrect logic (8 failing tests) +``` + +### Step 4: Set Next Priority + +**Choose next focus based on:** +- Business impact (trading logic > display logic) +- Number of failing tests +- Core vs peripheral functionality + +**Priority order example:** +1. **Money Management** (8 fails) - Core trading strategy logic +2. **Signal Processing** (9 fails) - Trading signal generation +3. **Trader Analysis** (3 fails) - Performance evaluation +4. **P&L Tests** (2 fails) - Profit calculation edge cases + +### Step 5: Update Investigation Steps + +**For current priority, add specific debugging steps:** +```markdown +### Investigation Steps for [Current Priority] +1. **Debug [MethodName]()** - Check [specific logic area] +2. **Debug [Calculation]** - Verify [expected behavior] +3. **Debug [Edge Case]** - Ensure [boundary condition] +4. **Debug [Integration]** - Check [component interaction] +``` + +## Best Practices + +### Test Status Tracking +- ✅ **Passed**: All tests in category working +- 🔄 **In Progress**: Currently being fixed +- ⏳ **Pending**: Known issues, not yet addressed +- ❌ **Failed**: Tests failing, investigation needed + +### Priority Setting +- **Critical**: Core trading calculations (P&L, volume, fees) +- **High**: Trading strategy logic (signals, money management) +- **Medium**: Performance evaluation (win rates, trader analysis) +- **Low**: Edge cases and display logic + +### Business Logic vs Test Issues +- **Business Logic Bug**: Tests fail because implementation is wrong +- **Test Expectation Issue**: Tests fail because expectations don't match actual (correct) behavior +- **Test Setup Issue**: Tests fail due to incorrect test data or mocking + +## Common Patterns + +### Trading Logic Priority +1. **Money Management** - SL/TP optimization affects risk control +2. **Signal Processing** - Signal generation affects trade entries +3. **Volume/P&L Calculations** - Core metrics for performance analysis +4. **Fee Calculations** - Cost analysis for profitability + +### Investigation Framework +``` +1. **Reproduce Issue**: Run failing test individually +2. **Debug Implementation**: Add logging to understand actual behavior +3. **Compare Expectations**: Verify test assumptions vs business requirements +4. **Fix Implementation**: Update code if business logic is wrong +5. **Fix Tests**: Update test expectations if implementation is correct +6. **Add Edge Cases**: Ensure comprehensive coverage +``` + +## Example Execution + +**User input:** Update TODO.md for Managing.Domain.Tests + +**AI execution:** + +1. **Run tests:** + ```bash + cd src/Managing.Domain.Tests + dotnet test --verbosity minimal | tail -5 + ``` + Output: `Failed: 22, Passed: 138, Skipped: 0, Total: 160` + +2. **Analyze failures:** + - MoneyManagementTests: 8 fails + - SignalProcessingTests: 9 fails + - TraderAnalysisTests: 3 fails + - ProfitLossTests: 2 fails + +3. **Update TODO.md:** + ```markdown + ## Test Results Summary + **Total Tests:** 160 + - **Passed:** 138 ✅ + - **Failed:** 22 ❌ (MoneyManagement: 8, SignalProcessing: 9, TraderAnalysis: 3, ProfitLoss: 2) + + ### High Priority - Next Focus + 5. **Money Management Optimization**: SL/TP calculations have incorrect logic (8 failing tests) + ``` + +4. **Set investigation steps:** + ```markdown + ### Investigation Steps for Money Management + 1. **Debug GetBestSltpForPosition()** - Check candle filtering logic with next position + 2. **Debug Price Movement Calculations** - Verify min/max price detection for SL/TP + 3. **Debug Percentage Calculations** - Ensure GetPercentageFromEntry() works correctly + 4. **Debug Averaging Logic** - Check how multiple positions are averaged + ``` + +## Important Notes + +- 📊 **Track Progress**: Update TODO.md after each significant fix +- 🎯 **Prioritize Impact**: Focus on core trading logic first +- 🔍 **Debug Thoroughly**: Understand root cause before fixing +- ✅ **Verify Fixes**: Ensure fixes don't break other tests +- 📈 **Comprehensive Coverage**: Add tests for edge cases found during debugging + +## Quick Commands + +**Update test results:** +```bash +cd src/YourTestProject && dotnet test --verbosity minimal | tail -5 +``` + +**Run specific test category:** +```bash +dotnet test --filter "CategoryName" --verbosity normal +``` + +**Debug individual test:** +```bash +dotnet test --filter "FullyQualifiedTestName" --verbosity normal +``` + +**Generate coverage report:** +```bash +dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura +``` diff --git a/TODO.md b/TODO.md index 276a83be..3b7d4985 100644 --- a/TODO.md +++ b/TODO.md @@ -1,9 +1,9 @@ # TradingBox Unit Tests - Business Logic Issues Analysis ## Test Results Summary -**Total Tests:** 140 -- **Passed:** 135 ✅ (TradingMetricsTests: 40/40 passing - added mixed time-based filtering test) -- **Failed:** 5 ❌ (mostly remaining MoneyManagement and SignalProcessing tests) +**Total Tests:** 160 +- **Passed:** 140 ✅ (TradingMetricsTests: 40/40, ProfitLossTests: 21/21 ✅ FIXED) +- **Failed:** 20 ❌ (MoneyManagement: 8, SignalProcessing: 9, TraderAnalysis: 3) ## Failed Test Categories & Potential Business Logic Issues @@ -60,18 +60,20 @@ **Business Logic Fix:** - Updated `TradingBox.GetWinRate()` to only consider `PositionStatus.Finished` positions - Win rate should only count closed positions, not open positions with unrealized P&L +- Other metrics (P&L, fees, volume) correctly use `IsValidForMetrics()` to include both open and closed positions **Resolution:** - Modified GetWinRate method: `if (position.Status == PositionStatus.Finished)` instead of `if (position.IsValidForMetrics())` +- `IsValidForMetrics()` includes: Filled (open), Finished (closed), and Flipped positions +- Win rate is special - only considers completed trades (Finished status) - Updated test to expect only closed positions in win rate calculation - Win rate: 1 win out of 2 closed positions = 50% (integer division) -**Possible Business Logic Problem:** -- `IsValidForMetrics()` method may be rejecting test positions -- `IsInProfit()` method logic may be incorrect -- Position validation criteria may be too restrictive +**Important Distinction:** +- **General Metrics** (P&L, fees, volume): Use `IsValidForMetrics()` to include open + closed positions +- **Win Rate**: Use `Status == Finished` to include ONLY closed positions -**Impact:** Win rate is a key performance indicator for trading strategies. +**Impact:** Win rate is a key performance indicator for trading strategies and should reflect completed trades only. ### 5. Money Management Calculations (MoneyManagementTests) **Failed Tests:** @@ -117,32 +119,39 @@ ## Business Logic Issues Identified -### Critical Issues (High Priority) -1. **Volume Under-Reporting**: Trading volume metrics are significantly under-reported -2. **Fee Calculation Failure**: No fees are being calculated, affecting cost analysis -3. **P&L Calculation Failure**: Profit/Loss calculations are not working -4. **Win Rate Calculation Failure**: Key performance metric is broken +### Critical Issues (High Priority) ✅ MOSTLY RESOLVED +1. **Volume Calculations**: ✅ FIXED - All TradingMetrics volume calculations working correctly +2. **Fee Calculations**: ✅ FIXED - All TradingMetrics fee calculations working correctly +3. **P&L Calculations**: ✅ FIXED - All TradingMetrics P&L calculations working correctly +4. **Win Rate Calculations**: ✅ FIXED - Win rate now correctly excludes open positions -### Medium Priority Issues -5. **Money Management Optimization**: SL/TP calculations have incorrect logic -6. **Signal Processing Logic**: Confidence filtering and signal generation may be too permissive -7. **Position Validation**: Too restrictive validation may exclude valid positions +### High Priority - Next Focus +5. **Money Management Optimization**: SL/TP calculations have incorrect logic (8 failing tests) +6. **Signal Processing Logic**: Confidence filtering and signal generation may be too permissive (9 failing tests) +7. **Trader Analysis**: Trader evaluation logic issues (3 failing tests) + +### Resolved Issues +8. **Profit/Loss Tests**: ✅ FIXED (21/21 passing) - Win rate now correctly considers only Finished positions ## Recommended Actions -### Immediate Actions -1. **Fix Volume Calculations**: Ensure all trade volumes (entry + exit) are included -2. **Debug Fee Logic**: Investigate why fees return 0 for valid positions -3. **Fix P&L Calculations**: Ensure ProfitAndLoss objects are properly created -4. **Review Win Rate Logic**: Check position validation and profit detection +### Immediate Actions ✅ MOSTLY COMPLETED +1. **Volume Calculations**: ✅ FIXED - All TradingMetrics volume calculations working correctly +2. **Fee Calculations**: ✅ FIXED - All TradingMetrics fee calculations working correctly +3. **P&L Calculations**: ✅ FIXED - All TradingMetrics P&L calculations working correctly +4. **Win Rate Logic**: ✅ FIXED - Win rate now correctly excludes open positions -### Investigation Steps -1. **Debug TradingBox.GetTotalVolumeTraded()** - Add logging to see what's being calculated -2. **Debug TradingBox.GetTotalFees()** - Check fee calculation conditions -3. **Debug TradingBox.GetTotalRealizedPnL()** - Verify ProfitAndLoss object creation -4. **Debug TradingBox.GetWinRate()** - Check IsValidForMetrics() and IsInProfit() logic -5. **Debug TradingBox.ComputeSignals()** - Check confidence filtering and signal generation logic -6. **Debug LightIndicator initialization** - Ensure proper parameter setup in ScenarioHelpers +### Next Priority Actions - Money Management Tests +1. **Debug Money Management Logic**: Fix SL/TP optimization calculations (8 failing tests) +2. **Fix GetBestSltpForPosition()**: Correct price movement calculations and candle processing +3. **Fix GetBestMoneyManagement()**: Ensure proper averaging of SL/TP values +4. **Debug Candle Range Logic**: Verify next position limiting works correctly + +### Investigation Steps for Money Management +1. **Debug GetBestSltpForPosition()** - Check candle filtering logic with next position +2. **Debug Price Movement Calculations** - Verify min/max price detection for SL/TP +3. **Debug Percentage Calculations** - Ensure GetPercentageFromEntry() works correctly +4. **Debug Averaging Logic** - Check how multiple positions are averaged ### Test Updates Needed 1. **Update Fee Expectations**: Align test expectations with actual UI fee rates @@ -158,12 +167,19 @@ - **Low Risk**: Money management optimization affects risk control ## Next Steps -1. Debug and fix the 4 critical calculation issues -2. Debug signal processing confidence filtering and LightIndicator initialization -3. Update unit tests to match corrected business logic -4. Add integration tests to verify end-to-end calculations -5. Review money management logic for edge cases +1. **HIGH PRIORITY**: Fix Money Management tests (8 failing) - SL/TP optimization is core trading logic +2. Debug and fix Signal Processing tests (9 failing) - confidence filtering and signal generation +3. Fix Trader Analysis tests (3 failing) - trader evaluation logic +4. ✅ **COMPLETED**: ProfitLoss tests fixed - Win rate now correctly considers only Finished positions +5. Add integration tests to verify end-to-end calculations 6. Consider adding more comprehensive test scenarios +## Current Status +- ✅ **TradingMetricsTests**: 40/40 passing - comprehensive trading metrics coverage complete +- ✅ **ProfitLossTests**: 21/21 passing - All P&L and win rate tests fixed +- 🔄 **MoneyManagementTests**: Next priority - 8 failing tests need investigation +- ⏳ **SignalProcessingTests**: 9 failing tests - confidence filtering issues +- ⏳ **TraderAnalysisTests**: 3 failing tests - evaluation logic issues + --- *Generated from unit test results analysis - Tests reveal potential business logic issues in TradingBox implementation* diff --git a/scripts/fix-backtest-finalpnl.sql b/scripts/fix-backtest-finalpnl.sql deleted file mode 100644 index edbee8ba..00000000 --- a/scripts/fix-backtest-finalpnl.sql +++ /dev/null @@ -1,69 +0,0 @@ --- Fix Backtest FinalPnl where it incorrectly equals NetPnl --- This script updates records where FinalPnl was incorrectly set to the same value as NetPnl --- Correct formula: FinalPnl = NetPnl + Fees (since NetPnl = FinalPnl - Fees) --- --- IMPORTANT: Run this script in a transaction and verify results before committing! --- Usage: psql -h -U -d -f fix-backtest-finalpnl.sql - -BEGIN; - --- First, let's see how many records will be affected -SELECT - COUNT(*) as affected_records, - SUM("Fees") as total_fees_to_add, - AVG("Fees") as avg_fees -FROM "Backtests" -WHERE "FinalPnl" = "NetPnl" - AND "Fees" > 0; - --- Show sample of records that will be updated (for verification) -SELECT - "Id", - "Identifier", - "FinalPnl" as current_final_pnl, - "NetPnl", - "Fees", - ("NetPnl" + "Fees") as new_final_pnl, - ("NetPnl" + "Fees" - "FinalPnl") as change_amount -FROM "Backtests" -WHERE "FinalPnl" = "NetPnl" - AND "Fees" > 0 -ORDER BY "Id" -LIMIT 10; - --- Update the records where FinalPnl equals NetPnl --- Only update if Fees > 0 to avoid incorrect updates -UPDATE "Backtests" -SET - "FinalPnl" = "NetPnl" + "Fees", - "UpdatedAt" = NOW() -WHERE "FinalPnl" = "NetPnl" - AND "Fees" > 0; - --- Verify the update - should return 0 -SELECT - COUNT(*) as remaining_incorrect_records -FROM "Backtests" -WHERE "FinalPnl" = "NetPnl" - AND "Fees" > 0; - --- Show a sample of updated records to verify -SELECT - "Id", - "Identifier", - "FinalPnl" as new_final_pnl, - "NetPnl", - "Fees", - ("FinalPnl" - "NetPnl") as difference_should_equal_fees -FROM "Backtests" -WHERE "FinalPnl" != "NetPnl" - AND "Fees" > 0 -ORDER BY "UpdatedAt" DESC -LIMIT 10; - --- If everything looks correct, uncomment the COMMIT line below --- COMMIT; - --- If something is wrong, run ROLLBACK instead --- ROLLBACK; - diff --git a/src/Managing.Domain.Tests/ProfitLossTests.cs b/src/Managing.Domain.Tests/ProfitLossTests.cs index bb3787e5..210bfced 100644 --- a/src/Managing.Domain.Tests/ProfitLossTests.cs +++ b/src/Managing.Domain.Tests/ProfitLossTests.cs @@ -1,14 +1,5 @@ using FluentAssertions; -using Managing.Common; -using Managing.Domain.Accounts; -using Managing.Domain.Candles; -using Managing.Domain.Indicators; -using Managing.Domain.MoneyManagements; -using Managing.Domain.Scenarios; using Managing.Domain.Shared.Helpers; -using Managing.Domain.Statistics; -using Managing.Domain.Strategies; -using Managing.Domain.Strategies.Base; using Managing.Domain.Trades; using Xunit; using static Managing.Common.Enums; @@ -63,7 +54,7 @@ public class ProfitLossTests : TradingBoxTests // Arrange var validPosition = CreateTestPosition(); var invalidPosition = CreateTestPosition(); - invalidPosition.Status = Enums.PositionStatus.New; // Invalid for metrics + invalidPosition.Status = PositionStatus.New; // Invalid for metrics validPosition.ProfitAndLoss = new ProfitAndLoss { Realized = 100m }; invalidPosition.ProfitAndLoss = new ProfitAndLoss { Realized = -50m }; @@ -156,19 +147,23 @@ public class ProfitLossTests : TradingBoxTests [Fact] public void GetWinRate_WithMixedResults_CalculatesCorrectPercentage() { - // Arrange - var winningPosition1 = CreateTestPosition(); - var winningPosition2 = CreateTestPosition(); - var losingPosition1 = CreateTestPosition(); - var losingPosition2 = CreateTestPosition(); - var invalidPosition = CreateTestPosition(); - invalidPosition.Status = Enums.PositionStatus.New; // Invalid for metrics + // Arrange - Win rate only considers Finished positions (closed trades) + var winningPosition1 = CreateFinishedPosition(); + var winningPosition2 = CreateFinishedPosition(); + var losingPosition1 = CreateFinishedPosition(); + var losingPosition2 = CreateFinishedPosition(); + var openFilledPosition = CreateFilledPosition(); // Open position (Filled status) - should NOT count towards win rate + openFilledPosition.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = 100m, Net = 100m }; // Has unrealized P&L but should not count - winningPosition1.ProfitAndLoss = new ProfitAndLoss { Realized = 50m }; - winningPosition2.ProfitAndLoss = new ProfitAndLoss { Realized = 25m }; - losingPosition1.ProfitAndLoss = new ProfitAndLoss { Realized = -30m }; - losingPosition2.ProfitAndLoss = new ProfitAndLoss { Realized = -10m }; - invalidPosition.ProfitAndLoss = new ProfitAndLoss { Realized = 100m }; + winningPosition1.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = 50m, Net = 50m }; + winningPosition2.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = 25m, Net = 25m }; + losingPosition1.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = -30m, Net = -30m }; + losingPosition2.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = -10m, Net = -10m }; var positions = new Dictionary { @@ -176,25 +171,27 @@ public class ProfitLossTests : TradingBoxTests { winningPosition2.Identifier, winningPosition2 }, { losingPosition1.Identifier, losingPosition1 }, { losingPosition2.Identifier, losingPosition2 }, - { invalidPosition.Identifier, invalidPosition } + { openFilledPosition.Identifier, openFilledPosition } // Open position excluded from win rate }; // Act var result = TradingBox.GetWinRate(positions); // Assert - result.Should().Be(50); // 2 wins out of 4 valid positions = 50% + result.Should().Be(50); // 2 wins out of 4 finished positions = 50% (open Filled position excluded) } [Fact] public void GetWinRate_WithAllWinningPositions_Returns100() { - // Arrange - var position1 = CreateTestPosition(); - var position2 = CreateTestPosition(); + // Arrange - Win rate only considers Finished positions (closed trades) + var position1 = CreateFinishedPosition(); + var position2 = CreateFinishedPosition(); - position1.ProfitAndLoss = new ProfitAndLoss { Realized = 50m }; - position2.ProfitAndLoss = new ProfitAndLoss { Realized = 25m }; + position1.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = 50m, Net = 50m }; + position2.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = 25m, Net = 25m }; var positions = new Dictionary { @@ -206,18 +203,20 @@ public class ProfitLossTests : TradingBoxTests var result = TradingBox.GetWinRate(positions); // Assert - result.Should().Be(100); + result.Should().Be(100); // 2 wins out of 2 finished positions = 100% } [Fact] public void GetWinRate_WithAllLosingPositions_Returns0() { - // Arrange - var position1 = CreateTestPosition(); - var position2 = CreateTestPosition(); + // Arrange - Win rate only considers Finished positions (closed trades) + var position1 = CreateFinishedPosition(); + var position2 = CreateFinishedPosition(); - position1.ProfitAndLoss = new ProfitAndLoss { Realized = -50m }; - position2.ProfitAndLoss = new ProfitAndLoss { Realized = -25m }; + position1.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = -50m, Net = -50m }; + position2.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) + { Realized = -25m, Net = -25m }; var positions = new Dictionary { @@ -229,7 +228,7 @@ public class ProfitLossTests : TradingBoxTests var result = TradingBox.GetWinRate(positions); // Assert - result.Should().Be(0); + result.Should().Be(0); // 0 wins out of 2 finished positions = 0% } [Fact] @@ -309,7 +308,7 @@ public class ProfitLossTests : TradingBoxTests public void GetProfitAndLoss_CalculatesLongPositionCorrectly() { // Arrange - var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: Enums.TradeDirection.Long, + var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: TradeDirection.Long, leverage: 1m); var quantity = 1m; var closePrice = 110m; // 10% profit @@ -328,7 +327,7 @@ public class ProfitLossTests : TradingBoxTests public void GetProfitAndLoss_CalculatesShortPositionCorrectly() { // Arrange - var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: Enums.TradeDirection.Short, + var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: TradeDirection.Short, leverage: 1m); var quantity = 1m; var closePrice = 90m; // 10% profit @@ -347,7 +346,7 @@ public class ProfitLossTests : TradingBoxTests public void GetProfitAndLoss_WithLeverage_AppliesLeverageMultiplier() { // Arrange - var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: Enums.TradeDirection.Long, + var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: TradeDirection.Long, leverage: 1m); var quantity = 1m; var closePrice = 105m; // 5% profit diff --git a/src/Managing.Domain.Tests/TradingMetricsTests.cs b/src/Managing.Domain.Tests/TradingMetricsTests.cs index 360d961b..f0c84797 100644 --- a/src/Managing.Domain.Tests/TradingMetricsTests.cs +++ b/src/Managing.Domain.Tests/TradingMetricsTests.cs @@ -1,14 +1,7 @@ using FluentAssertions; using Managing.Common; -using Managing.Domain.Accounts; using Managing.Domain.Candles; -using Managing.Domain.Indicators; -using Managing.Domain.MoneyManagements; -using Managing.Domain.Scenarios; using Managing.Domain.Shared.Helpers; -using Managing.Domain.Statistics; -using Managing.Domain.Strategies; -using Managing.Domain.Strategies.Base; using Managing.Domain.Trades; using Xunit; using static Managing.Common.Enums; @@ -513,6 +506,7 @@ public class TradingMetricsTests : TradingBoxTests public void GetTotalVolumeTraded_WithMixedPositionStatuses_IncludesOnlyValidPositions() { // Arrange - Mix of different position statuses + // IsValidForMetrics() returns true for: Filled (open), Finished (closed), and Flipped positions var finishedPosition = CreateFinishedPosition(openPrice: 50000m, quantity: 0.001m, leverage: 1m); var filledPosition = CreateFilledPosition(openPrice: 60000m, quantity: 0.002m, leverage: 1m); var newPosition = CreateNewPosition(openPrice: 40000m, quantity: 0.001m, leverage: 1m); @@ -523,13 +517,13 @@ public class TradingMetricsTests : TradingBoxTests // Act var result = TradingBox.GetTotalVolumeTraded(positions); - // Assert - Should include finished + filled positions, exclude new + canceled - // Finished: 50000 * 0.001 * 1 + 52000 * 0.001 * 1 = 102 - // Filled: 60000 * 0.002 * 1 = 120 - // New: excluded - // Canceled: excluded + // Assert - GetTotalVolumeTraded only includes valid positions (uses IsValidForMetrics) + // Finished (valid): 50000 * 0.001 * 1 (open) + 52000 * 0.001 * 1 (TP1) = 102 + // Filled (valid): 60000 * 0.002 * 1 = 120 + // New (EXCLUDED - not valid for metrics): excluded + // Canceled (EXCLUDED - not valid for metrics): excluded // Total: 102 + 120 = 222 - result.Should().Be(317m); // Actual calculation gives 317 + result.Should().Be(222m); } [Fact] @@ -568,11 +562,11 @@ public class TradingMetricsTests : TradingBoxTests // Arrange - Mix of positions with different statuses and outcomes var winningFinished = CreateFinishedPosition(); winningFinished.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) - { Net = 100m }; + { Net = 100m }; var losingFinished = CreateFinishedPosition(); losingFinished.ProfitAndLoss = new ProfitAndLoss(new List>(), TradeDirection.Long) - { Net = -50m }; + { Net = -50m }; var openFilled = CreateFilledPosition(); // Open position - should not count towards win rate var newPosition = CreateNewPosition(); // Not valid for metrics diff --git a/src/Managing.Domain/Shared/Helpers/TradingBox.cs b/src/Managing.Domain/Shared/Helpers/TradingBox.cs index af226642..b335cabb 100644 --- a/src/Managing.Domain/Shared/Helpers/TradingBox.cs +++ b/src/Managing.Domain/Shared/Helpers/TradingBox.cs @@ -487,6 +487,7 @@ public static class TradingBox /// /// Calculates the total volume traded across all positions + /// Only includes valid positions (Filled, Finished, Flipped) - excludes New, Canceled, Rejected /// /// List of positions to analyze /// The total volume traded in decimal @@ -496,6 +497,12 @@ public static class TradingBox foreach (var position in positions) { + // Only count volume for valid positions (Filled, Finished, Flipped) + if (!position.IsValidForMetrics()) + { + continue; + } + // Add entry volume totalVolume += position.Open.Quantity * position.Open.Price * position.Open.Leverage;