Fix test for trading metrics
This commit is contained in:
199
.cursor/commands/update-test-todo.md
Normal file
199
.cursor/commands/update-test-todo.md
Normal file
@@ -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
|
||||||
|
```
|
||||||
84
TODO.md
84
TODO.md
@@ -1,9 +1,9 @@
|
|||||||
# TradingBox Unit Tests - Business Logic Issues Analysis
|
# TradingBox Unit Tests - Business Logic Issues Analysis
|
||||||
|
|
||||||
## Test Results Summary
|
## Test Results Summary
|
||||||
**Total Tests:** 140
|
**Total Tests:** 160
|
||||||
- **Passed:** 135 ✅ (TradingMetricsTests: 40/40 passing - added mixed time-based filtering test)
|
- **Passed:** 140 ✅ (TradingMetricsTests: 40/40, ProfitLossTests: 21/21 ✅ FIXED)
|
||||||
- **Failed:** 5 ❌ (mostly remaining MoneyManagement and SignalProcessing tests)
|
- **Failed:** 20 ❌ (MoneyManagement: 8, SignalProcessing: 9, TraderAnalysis: 3)
|
||||||
|
|
||||||
## Failed Test Categories & Potential Business Logic Issues
|
## Failed Test Categories & Potential Business Logic Issues
|
||||||
|
|
||||||
@@ -60,18 +60,20 @@
|
|||||||
**Business Logic Fix:**
|
**Business Logic Fix:**
|
||||||
- Updated `TradingBox.GetWinRate()` to only consider `PositionStatus.Finished` positions
|
- Updated `TradingBox.GetWinRate()` to only consider `PositionStatus.Finished` positions
|
||||||
- Win rate should only count closed positions, not open positions with unrealized P&L
|
- 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:**
|
**Resolution:**
|
||||||
- Modified GetWinRate method: `if (position.Status == PositionStatus.Finished)` instead of `if (position.IsValidForMetrics())`
|
- 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
|
- Updated test to expect only closed positions in win rate calculation
|
||||||
- Win rate: 1 win out of 2 closed positions = 50% (integer division)
|
- Win rate: 1 win out of 2 closed positions = 50% (integer division)
|
||||||
|
|
||||||
**Possible Business Logic Problem:**
|
**Important Distinction:**
|
||||||
- `IsValidForMetrics()` method may be rejecting test positions
|
- **General Metrics** (P&L, fees, volume): Use `IsValidForMetrics()` to include open + closed positions
|
||||||
- `IsInProfit()` method logic may be incorrect
|
- **Win Rate**: Use `Status == Finished` to include ONLY closed positions
|
||||||
- Position validation criteria may be too restrictive
|
|
||||||
|
|
||||||
**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)
|
### 5. Money Management Calculations (MoneyManagementTests)
|
||||||
**Failed Tests:**
|
**Failed Tests:**
|
||||||
@@ -117,32 +119,39 @@
|
|||||||
|
|
||||||
## Business Logic Issues Identified
|
## Business Logic Issues Identified
|
||||||
|
|
||||||
### Critical Issues (High Priority)
|
### Critical Issues (High Priority) ✅ MOSTLY RESOLVED
|
||||||
1. **Volume Under-Reporting**: Trading volume metrics are significantly under-reported
|
1. **Volume Calculations**: ✅ FIXED - All TradingMetrics volume calculations working correctly
|
||||||
2. **Fee Calculation Failure**: No fees are being calculated, affecting cost analysis
|
2. **Fee Calculations**: ✅ FIXED - All TradingMetrics fee calculations working correctly
|
||||||
3. **P&L Calculation Failure**: Profit/Loss calculations are not working
|
3. **P&L Calculations**: ✅ FIXED - All TradingMetrics P&L calculations working correctly
|
||||||
4. **Win Rate Calculation Failure**: Key performance metric is broken
|
4. **Win Rate Calculations**: ✅ FIXED - Win rate now correctly excludes open positions
|
||||||
|
|
||||||
### Medium Priority Issues
|
### High Priority - Next Focus
|
||||||
5. **Money Management Optimization**: SL/TP calculations have incorrect logic
|
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
|
6. **Signal Processing Logic**: Confidence filtering and signal generation may be too permissive (9 failing tests)
|
||||||
7. **Position Validation**: Too restrictive validation may exclude valid positions
|
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
|
## Recommended Actions
|
||||||
|
|
||||||
### Immediate Actions
|
### Immediate Actions ✅ MOSTLY COMPLETED
|
||||||
1. **Fix Volume Calculations**: Ensure all trade volumes (entry + exit) are included
|
1. **Volume Calculations**: ✅ FIXED - All TradingMetrics volume calculations working correctly
|
||||||
2. **Debug Fee Logic**: Investigate why fees return 0 for valid positions
|
2. **Fee Calculations**: ✅ FIXED - All TradingMetrics fee calculations working correctly
|
||||||
3. **Fix P&L Calculations**: Ensure ProfitAndLoss objects are properly created
|
3. **P&L Calculations**: ✅ FIXED - All TradingMetrics P&L calculations working correctly
|
||||||
4. **Review Win Rate Logic**: Check position validation and profit detection
|
4. **Win Rate Logic**: ✅ FIXED - Win rate now correctly excludes open positions
|
||||||
|
|
||||||
### Investigation Steps
|
### Next Priority Actions - Money Management Tests
|
||||||
1. **Debug TradingBox.GetTotalVolumeTraded()** - Add logging to see what's being calculated
|
1. **Debug Money Management Logic**: Fix SL/TP optimization calculations (8 failing tests)
|
||||||
2. **Debug TradingBox.GetTotalFees()** - Check fee calculation conditions
|
2. **Fix GetBestSltpForPosition()**: Correct price movement calculations and candle processing
|
||||||
3. **Debug TradingBox.GetTotalRealizedPnL()** - Verify ProfitAndLoss object creation
|
3. **Fix GetBestMoneyManagement()**: Ensure proper averaging of SL/TP values
|
||||||
4. **Debug TradingBox.GetWinRate()** - Check IsValidForMetrics() and IsInProfit() logic
|
4. **Debug Candle Range Logic**: Verify next position limiting works correctly
|
||||||
5. **Debug TradingBox.ComputeSignals()** - Check confidence filtering and signal generation logic
|
|
||||||
6. **Debug LightIndicator initialization** - Ensure proper parameter setup in ScenarioHelpers
|
### 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
|
### Test Updates Needed
|
||||||
1. **Update Fee Expectations**: Align test expectations with actual UI fee rates
|
1. **Update Fee Expectations**: Align test expectations with actual UI fee rates
|
||||||
@@ -158,12 +167,19 @@
|
|||||||
- **Low Risk**: Money management optimization affects risk control
|
- **Low Risk**: Money management optimization affects risk control
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
1. Debug and fix the 4 critical calculation issues
|
1. **HIGH PRIORITY**: Fix Money Management tests (8 failing) - SL/TP optimization is core trading logic
|
||||||
2. Debug signal processing confidence filtering and LightIndicator initialization
|
2. Debug and fix Signal Processing tests (9 failing) - confidence filtering and signal generation
|
||||||
3. Update unit tests to match corrected business logic
|
3. Fix Trader Analysis tests (3 failing) - trader evaluation logic
|
||||||
4. Add integration tests to verify end-to-end calculations
|
4. ✅ **COMPLETED**: ProfitLoss tests fixed - Win rate now correctly considers only Finished positions
|
||||||
5. Review money management logic for edge cases
|
5. Add integration tests to verify end-to-end calculations
|
||||||
6. Consider adding more comprehensive test scenarios
|
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*
|
*Generated from unit test results analysis - Tests reveal potential business logic issues in TradingBox implementation*
|
||||||
|
|||||||
@@ -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 <host> -U <user> -d <database> -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;
|
|
||||||
|
|
||||||
@@ -1,14 +1,5 @@
|
|||||||
using FluentAssertions;
|
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.Shared.Helpers;
|
||||||
using Managing.Domain.Statistics;
|
|
||||||
using Managing.Domain.Strategies;
|
|
||||||
using Managing.Domain.Strategies.Base;
|
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using static Managing.Common.Enums;
|
using static Managing.Common.Enums;
|
||||||
@@ -63,7 +54,7 @@ public class ProfitLossTests : TradingBoxTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var validPosition = CreateTestPosition();
|
var validPosition = CreateTestPosition();
|
||||||
var invalidPosition = 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 };
|
validPosition.ProfitAndLoss = new ProfitAndLoss { Realized = 100m };
|
||||||
invalidPosition.ProfitAndLoss = new ProfitAndLoss { Realized = -50m };
|
invalidPosition.ProfitAndLoss = new ProfitAndLoss { Realized = -50m };
|
||||||
@@ -156,19 +147,23 @@ public class ProfitLossTests : TradingBoxTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void GetWinRate_WithMixedResults_CalculatesCorrectPercentage()
|
public void GetWinRate_WithMixedResults_CalculatesCorrectPercentage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange - Win rate only considers Finished positions (closed trades)
|
||||||
var winningPosition1 = CreateTestPosition();
|
var winningPosition1 = CreateFinishedPosition();
|
||||||
var winningPosition2 = CreateTestPosition();
|
var winningPosition2 = CreateFinishedPosition();
|
||||||
var losingPosition1 = CreateTestPosition();
|
var losingPosition1 = CreateFinishedPosition();
|
||||||
var losingPosition2 = CreateTestPosition();
|
var losingPosition2 = CreateFinishedPosition();
|
||||||
var invalidPosition = CreateTestPosition();
|
var openFilledPosition = CreateFilledPosition(); // Open position (Filled status) - should NOT count towards win rate
|
||||||
invalidPosition.Status = Enums.PositionStatus.New; // Invalid for metrics
|
openFilledPosition.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
|
{ Realized = 100m, Net = 100m }; // Has unrealized P&L but should not count
|
||||||
|
|
||||||
winningPosition1.ProfitAndLoss = new ProfitAndLoss { Realized = 50m };
|
winningPosition1.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
winningPosition2.ProfitAndLoss = new ProfitAndLoss { Realized = 25m };
|
{ Realized = 50m, Net = 50m };
|
||||||
losingPosition1.ProfitAndLoss = new ProfitAndLoss { Realized = -30m };
|
winningPosition2.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
losingPosition2.ProfitAndLoss = new ProfitAndLoss { Realized = -10m };
|
{ Realized = 25m, Net = 25m };
|
||||||
invalidPosition.ProfitAndLoss = new ProfitAndLoss { Realized = 100m };
|
losingPosition1.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
|
{ Realized = -30m, Net = -30m };
|
||||||
|
losingPosition2.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
|
{ Realized = -10m, Net = -10m };
|
||||||
|
|
||||||
var positions = new Dictionary<Guid, Position>
|
var positions = new Dictionary<Guid, Position>
|
||||||
{
|
{
|
||||||
@@ -176,25 +171,27 @@ public class ProfitLossTests : TradingBoxTests
|
|||||||
{ winningPosition2.Identifier, winningPosition2 },
|
{ winningPosition2.Identifier, winningPosition2 },
|
||||||
{ losingPosition1.Identifier, losingPosition1 },
|
{ losingPosition1.Identifier, losingPosition1 },
|
||||||
{ losingPosition2.Identifier, losingPosition2 },
|
{ losingPosition2.Identifier, losingPosition2 },
|
||||||
{ invalidPosition.Identifier, invalidPosition }
|
{ openFilledPosition.Identifier, openFilledPosition } // Open position excluded from win rate
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = TradingBox.GetWinRate(positions);
|
var result = TradingBox.GetWinRate(positions);
|
||||||
|
|
||||||
// Assert
|
// 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]
|
[Fact]
|
||||||
public void GetWinRate_WithAllWinningPositions_Returns100()
|
public void GetWinRate_WithAllWinningPositions_Returns100()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange - Win rate only considers Finished positions (closed trades)
|
||||||
var position1 = CreateTestPosition();
|
var position1 = CreateFinishedPosition();
|
||||||
var position2 = CreateTestPosition();
|
var position2 = CreateFinishedPosition();
|
||||||
|
|
||||||
position1.ProfitAndLoss = new ProfitAndLoss { Realized = 50m };
|
position1.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
position2.ProfitAndLoss = new ProfitAndLoss { Realized = 25m };
|
{ Realized = 50m, Net = 50m };
|
||||||
|
position2.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
|
{ Realized = 25m, Net = 25m };
|
||||||
|
|
||||||
var positions = new Dictionary<Guid, Position>
|
var positions = new Dictionary<Guid, Position>
|
||||||
{
|
{
|
||||||
@@ -206,18 +203,20 @@ public class ProfitLossTests : TradingBoxTests
|
|||||||
var result = TradingBox.GetWinRate(positions);
|
var result = TradingBox.GetWinRate(positions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().Be(100);
|
result.Should().Be(100); // 2 wins out of 2 finished positions = 100%
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetWinRate_WithAllLosingPositions_Returns0()
|
public void GetWinRate_WithAllLosingPositions_Returns0()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange - Win rate only considers Finished positions (closed trades)
|
||||||
var position1 = CreateTestPosition();
|
var position1 = CreateFinishedPosition();
|
||||||
var position2 = CreateTestPosition();
|
var position2 = CreateFinishedPosition();
|
||||||
|
|
||||||
position1.ProfitAndLoss = new ProfitAndLoss { Realized = -50m };
|
position1.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
position2.ProfitAndLoss = new ProfitAndLoss { Realized = -25m };
|
{ Realized = -50m, Net = -50m };
|
||||||
|
position2.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
|
{ Realized = -25m, Net = -25m };
|
||||||
|
|
||||||
var positions = new Dictionary<Guid, Position>
|
var positions = new Dictionary<Guid, Position>
|
||||||
{
|
{
|
||||||
@@ -229,7 +228,7 @@ public class ProfitLossTests : TradingBoxTests
|
|||||||
var result = TradingBox.GetWinRate(positions);
|
var result = TradingBox.GetWinRate(positions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().Be(0);
|
result.Should().Be(0); // 0 wins out of 2 finished positions = 0%
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -309,7 +308,7 @@ public class ProfitLossTests : TradingBoxTests
|
|||||||
public void GetProfitAndLoss_CalculatesLongPositionCorrectly()
|
public void GetProfitAndLoss_CalculatesLongPositionCorrectly()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: Enums.TradeDirection.Long,
|
var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: TradeDirection.Long,
|
||||||
leverage: 1m);
|
leverage: 1m);
|
||||||
var quantity = 1m;
|
var quantity = 1m;
|
||||||
var closePrice = 110m; // 10% profit
|
var closePrice = 110m; // 10% profit
|
||||||
@@ -328,7 +327,7 @@ public class ProfitLossTests : TradingBoxTests
|
|||||||
public void GetProfitAndLoss_CalculatesShortPositionCorrectly()
|
public void GetProfitAndLoss_CalculatesShortPositionCorrectly()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: Enums.TradeDirection.Short,
|
var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: TradeDirection.Short,
|
||||||
leverage: 1m);
|
leverage: 1m);
|
||||||
var quantity = 1m;
|
var quantity = 1m;
|
||||||
var closePrice = 90m; // 10% profit
|
var closePrice = 90m; // 10% profit
|
||||||
@@ -347,7 +346,7 @@ public class ProfitLossTests : TradingBoxTests
|
|||||||
public void GetProfitAndLoss_WithLeverage_AppliesLeverageMultiplier()
|
public void GetProfitAndLoss_WithLeverage_AppliesLeverageMultiplier()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: Enums.TradeDirection.Long,
|
var position = CreateTestPosition(openPrice: 100m, quantity: 1m, direction: TradeDirection.Long,
|
||||||
leverage: 1m);
|
leverage: 1m);
|
||||||
var quantity = 1m;
|
var quantity = 1m;
|
||||||
var closePrice = 105m; // 5% profit
|
var closePrice = 105m; // 5% profit
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Managing.Common;
|
using Managing.Common;
|
||||||
using Managing.Domain.Accounts;
|
|
||||||
using Managing.Domain.Candles;
|
using Managing.Domain.Candles;
|
||||||
using Managing.Domain.Indicators;
|
|
||||||
using Managing.Domain.MoneyManagements;
|
|
||||||
using Managing.Domain.Scenarios;
|
|
||||||
using Managing.Domain.Shared.Helpers;
|
using Managing.Domain.Shared.Helpers;
|
||||||
using Managing.Domain.Statistics;
|
|
||||||
using Managing.Domain.Strategies;
|
|
||||||
using Managing.Domain.Strategies.Base;
|
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using static Managing.Common.Enums;
|
using static Managing.Common.Enums;
|
||||||
@@ -513,6 +506,7 @@ public class TradingMetricsTests : TradingBoxTests
|
|||||||
public void GetTotalVolumeTraded_WithMixedPositionStatuses_IncludesOnlyValidPositions()
|
public void GetTotalVolumeTraded_WithMixedPositionStatuses_IncludesOnlyValidPositions()
|
||||||
{
|
{
|
||||||
// Arrange - Mix of different position statuses
|
// 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 finishedPosition = CreateFinishedPosition(openPrice: 50000m, quantity: 0.001m, leverage: 1m);
|
||||||
var filledPosition = CreateFilledPosition(openPrice: 60000m, quantity: 0.002m, leverage: 1m);
|
var filledPosition = CreateFilledPosition(openPrice: 60000m, quantity: 0.002m, leverage: 1m);
|
||||||
var newPosition = CreateNewPosition(openPrice: 40000m, quantity: 0.001m, leverage: 1m);
|
var newPosition = CreateNewPosition(openPrice: 40000m, quantity: 0.001m, leverage: 1m);
|
||||||
@@ -523,13 +517,13 @@ public class TradingMetricsTests : TradingBoxTests
|
|||||||
// Act
|
// Act
|
||||||
var result = TradingBox.GetTotalVolumeTraded(positions);
|
var result = TradingBox.GetTotalVolumeTraded(positions);
|
||||||
|
|
||||||
// Assert - Should include finished + filled positions, exclude new + canceled
|
// Assert - GetTotalVolumeTraded only includes valid positions (uses IsValidForMetrics)
|
||||||
// Finished: 50000 * 0.001 * 1 + 52000 * 0.001 * 1 = 102
|
// Finished (valid): 50000 * 0.001 * 1 (open) + 52000 * 0.001 * 1 (TP1) = 102
|
||||||
// Filled: 60000 * 0.002 * 1 = 120
|
// Filled (valid): 60000 * 0.002 * 1 = 120
|
||||||
// New: excluded
|
// New (EXCLUDED - not valid for metrics): excluded
|
||||||
// Canceled: excluded
|
// Canceled (EXCLUDED - not valid for metrics): excluded
|
||||||
// Total: 102 + 120 = 222
|
// Total: 102 + 120 = 222
|
||||||
result.Should().Be(317m); // Actual calculation gives 317
|
result.Should().Be(222m);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -568,11 +562,11 @@ public class TradingMetricsTests : TradingBoxTests
|
|||||||
// Arrange - Mix of positions with different statuses and outcomes
|
// Arrange - Mix of positions with different statuses and outcomes
|
||||||
var winningFinished = CreateFinishedPosition();
|
var winningFinished = CreateFinishedPosition();
|
||||||
winningFinished.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
winningFinished.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
{ Net = 100m };
|
{ Net = 100m };
|
||||||
|
|
||||||
var losingFinished = CreateFinishedPosition();
|
var losingFinished = CreateFinishedPosition();
|
||||||
losingFinished.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
losingFinished.ProfitAndLoss = new ProfitAndLoss(new List<Tuple<decimal, decimal>>(), TradeDirection.Long)
|
||||||
{ Net = -50m };
|
{ Net = -50m };
|
||||||
|
|
||||||
var openFilled = CreateFilledPosition(); // Open position - should not count towards win rate
|
var openFilled = CreateFilledPosition(); // Open position - should not count towards win rate
|
||||||
var newPosition = CreateNewPosition(); // Not valid for metrics
|
var newPosition = CreateNewPosition(); // Not valid for metrics
|
||||||
|
|||||||
@@ -487,6 +487,7 @@ public static class TradingBox
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the total volume traded across all positions
|
/// Calculates the total volume traded across all positions
|
||||||
|
/// Only includes valid positions (Filled, Finished, Flipped) - excludes New, Canceled, Rejected
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="positions">List of positions to analyze</param>
|
/// <param name="positions">List of positions to analyze</param>
|
||||||
/// <returns>The total volume traded in decimal</returns>
|
/// <returns>The total volume traded in decimal</returns>
|
||||||
@@ -496,6 +497,12 @@ public static class TradingBox
|
|||||||
|
|
||||||
foreach (var position in positions)
|
foreach (var position in positions)
|
||||||
{
|
{
|
||||||
|
// Only count volume for valid positions (Filled, Finished, Flipped)
|
||||||
|
if (!position.IsValidForMetrics())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Add entry volume
|
// Add entry volume
|
||||||
totalVolume += position.Open.Quantity * position.Open.Price * position.Open.Leverage;
|
totalVolume += position.Open.Quantity * position.Open.Price * position.Open.Leverage;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user