465 lines
11 KiB
C#
465 lines
11 KiB
C#
using FluentAssertions;
|
|
using Managing.Domain.Accounts;
|
|
using Managing.Domain.Shared.Helpers;
|
|
using Managing.Domain.Statistics;
|
|
using Xunit;
|
|
|
|
namespace Managing.Domain.Tests;
|
|
|
|
/// <summary>
|
|
/// Tests for trader analysis methods in TradingBox.
|
|
/// Covers trader evaluation, filtering, and analysis methods.
|
|
/// </summary>
|
|
public class TraderAnalysisTests : TradingBoxTests
|
|
{
|
|
[Fact]
|
|
public void IsAGoodTrader_WithAllCriteriaMet_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 35, // > 30
|
|
TradeCount = 10, // > 8
|
|
AverageWin = 100m,
|
|
AverageLoss = -50m, // |AverageLoss| < AverageWin
|
|
Pnl = 250m // > 0
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsAGoodTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsAGoodTrader_WithLowWinrate_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 25, // < 30
|
|
TradeCount = 10,
|
|
AverageWin = 100m,
|
|
AverageLoss = -50m,
|
|
Pnl = 250m
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsAGoodTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsAGoodTrader_WithInsufficientTrades_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 35,
|
|
TradeCount = 5, // < 8
|
|
AverageWin = 100m,
|
|
AverageLoss = -50m,
|
|
Pnl = 250m
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsAGoodTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsAGoodTrader_WithPoorRiskReward_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 35,
|
|
TradeCount = 10,
|
|
AverageWin = 50m,
|
|
AverageLoss = -100m, // |AverageLoss| > AverageWin
|
|
Pnl = 250m
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsAGoodTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsAGoodTrader_WithNegativePnL_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 35,
|
|
TradeCount = 10,
|
|
AverageWin = 100m,
|
|
AverageLoss = -50m,
|
|
Pnl = -50m // < 0
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsAGoodTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsABadTrader_WithAllCriteriaMet_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 25, // < 30
|
|
TradeCount = 10, // > 8
|
|
AverageWin = 50m,
|
|
AverageLoss = -200m, // |AverageLoss| * 3 > AverageWin
|
|
Pnl = -500m // < 0
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsABadTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsABadTrader_WithGoodWinrate_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 35, // > 30
|
|
TradeCount = 10,
|
|
AverageWin = 50m,
|
|
AverageLoss = -200m,
|
|
Pnl = -500m
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsABadTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsABadTrader_WithInsufficientTrades_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 25,
|
|
TradeCount = 5, // < 8
|
|
AverageWin = 50m,
|
|
AverageLoss = -200m,
|
|
Pnl = -500m
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsABadTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsABadTrader_WithGoodRiskReward_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 25,
|
|
TradeCount = 10,
|
|
AverageWin = 200m, // AverageWin > |AverageLoss| * 3
|
|
AverageLoss = -50m,
|
|
Pnl = -500m
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsABadTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsABadTrader_WithPositivePnL_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 25,
|
|
TradeCount = 10,
|
|
AverageWin = 50m,
|
|
AverageLoss = -200m,
|
|
Pnl = 100m // > 0
|
|
};
|
|
|
|
// Act
|
|
var result = TradingBox.IsABadTrader(trader);
|
|
|
|
// Assert
|
|
result.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void FindBadTrader_WithEmptyList_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
var traders = new List<Trader>();
|
|
|
|
// Act
|
|
var result = traders.FindBadTrader();
|
|
|
|
// Assert
|
|
result.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void FindBadTrader_WithOnlyGoodTraders_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
var traders = new List<Trader>
|
|
{
|
|
new Trader { Winrate = 35, TradeCount = 10, AverageWin = 100m, AverageLoss = -50m, Pnl = 250m },
|
|
new Trader { Winrate = 40, TradeCount = 15, AverageWin = 150m, AverageLoss = -75m, Pnl = 500m }
|
|
};
|
|
|
|
// Act
|
|
var result = traders.FindBadTrader();
|
|
|
|
// Assert
|
|
result.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void FindBadTrader_WithMixedTraders_ReturnsOnlyBadTraders()
|
|
{
|
|
// Arrange
|
|
var goodTrader = new Trader
|
|
{
|
|
Winrate = 35,
|
|
TradeCount = 10,
|
|
AverageWin = 100m,
|
|
AverageLoss = -50m,
|
|
Pnl = 250m
|
|
};
|
|
|
|
var badTrader1 = new Trader
|
|
{
|
|
Winrate = 25,
|
|
TradeCount = 10,
|
|
AverageWin = 50m,
|
|
AverageLoss = -200m,
|
|
Pnl = -500m
|
|
};
|
|
|
|
var badTrader2 = new Trader
|
|
{
|
|
Winrate = 20,
|
|
TradeCount = 12,
|
|
AverageWin = 30m,
|
|
AverageLoss = -150m,
|
|
Pnl = -300m
|
|
};
|
|
|
|
var traders = new List<Trader> { goodTrader, badTrader1, badTrader2 };
|
|
|
|
// Act
|
|
var result = traders.FindBadTrader();
|
|
|
|
// Assert
|
|
result.Should().HaveCount(2);
|
|
result.Should().Contain(badTrader1);
|
|
result.Should().Contain(badTrader2);
|
|
result.Should().NotContain(goodTrader);
|
|
}
|
|
|
|
[Fact]
|
|
public void FindGoodTrader_WithEmptyList_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
var traders = new List<Trader>();
|
|
|
|
// Act
|
|
var result = traders.FindGoodTrader();
|
|
|
|
// Assert
|
|
result.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void FindGoodTrader_WithOnlyBadTraders_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
var traders = new List<Trader>
|
|
{
|
|
new Trader { Winrate = 25, TradeCount = 10, AverageWin = 50m, AverageLoss = -200m, Pnl = -500m },
|
|
new Trader { Winrate = 20, TradeCount = 12, AverageWin = 30m, AverageLoss = -150m, Pnl = -300m }
|
|
};
|
|
|
|
// Act
|
|
var result = traders.FindGoodTrader();
|
|
|
|
// Assert
|
|
result.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void FindGoodTrader_WithMixedTraders_ReturnsOnlyGoodTraders()
|
|
{
|
|
// Arrange
|
|
var badTrader = new Trader
|
|
{
|
|
Winrate = 25,
|
|
TradeCount = 10,
|
|
AverageWin = 50m,
|
|
AverageLoss = -200m,
|
|
Pnl = -500m
|
|
};
|
|
|
|
var goodTrader1 = new Trader
|
|
{
|
|
Winrate = 35,
|
|
TradeCount = 10,
|
|
AverageWin = 100m,
|
|
AverageLoss = -50m,
|
|
Pnl = 250m
|
|
};
|
|
|
|
var goodTrader2 = new Trader
|
|
{
|
|
Winrate = 40,
|
|
TradeCount = 15,
|
|
AverageWin = 150m,
|
|
AverageLoss = -75m,
|
|
Pnl = 500m
|
|
};
|
|
|
|
var traders = new List<Trader> { badTrader, goodTrader1, goodTrader2 };
|
|
|
|
// Act
|
|
var result = traders.FindGoodTrader();
|
|
|
|
// Assert
|
|
result.Should().HaveCount(2);
|
|
result.Should().Contain(goodTrader1);
|
|
result.Should().Contain(goodTrader2);
|
|
result.Should().NotContain(badTrader);
|
|
}
|
|
|
|
[Fact]
|
|
public void MapToTraders_WithAccounts_CreatesTradersWithCorrectAddresses()
|
|
{
|
|
// Arrange
|
|
var account1 = new Account { Key = "0x123", Name = "Account1" };
|
|
var account2 = new Account { Key = "0x456", Name = "Account2" };
|
|
var accounts = new List<Account> { account1, account2 };
|
|
|
|
// Act
|
|
var result = accounts.MapToTraders();
|
|
|
|
// Assert
|
|
result.Should().HaveCount(2);
|
|
result[0].Address.Should().Be("0x123");
|
|
result[1].Address.Should().Be("0x456");
|
|
}
|
|
|
|
[Fact]
|
|
public void MapToTraders_WithEmptyList_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
var accounts = new List<Account>();
|
|
|
|
// Act
|
|
var result = accounts.MapToTraders();
|
|
|
|
// Assert
|
|
result.Should().BeEmpty();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(35, 10, 100, -50, 250, true)] // Good trader
|
|
[InlineData(25, 10, 50, -200, -500, false)] // Bad trader
|
|
[InlineData(29, 9, 50, -150, -100, false)] // Boundary bad trader
|
|
[InlineData(32, 7, 100, -50, 200, false)] // Insufficient trades
|
|
[InlineData(28, 10, 200, -50, -100, false)] // Good RR but low winrate
|
|
public void TraderEvaluation_TheoryTests(int winrate, int tradeCount, decimal avgWin, decimal avgLoss, decimal pnl,
|
|
bool expectedGood)
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = winrate,
|
|
TradeCount = tradeCount,
|
|
AverageWin = avgWin,
|
|
AverageLoss = avgLoss,
|
|
Pnl = pnl
|
|
};
|
|
|
|
// Act & Assert
|
|
if (expectedGood)
|
|
{
|
|
TradingBox.IsAGoodTrader(trader).Should().BeTrue();
|
|
TradingBox.IsABadTrader(trader).Should().BeFalse();
|
|
}
|
|
else
|
|
{
|
|
TradingBox.IsAGoodTrader(trader).Should().BeFalse();
|
|
// Bad trader evaluation depends on multiple criteria
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void TraderAnalysis_MethodsAreConsistent()
|
|
{
|
|
// Arrange
|
|
var trader = new Trader
|
|
{
|
|
Winrate = 35,
|
|
TradeCount = 10,
|
|
AverageWin = 100m,
|
|
AverageLoss = -50m,
|
|
Pnl = 250m
|
|
};
|
|
|
|
// Act
|
|
var isGood = TradingBox.IsAGoodTrader(trader);
|
|
var isBad = TradingBox.IsABadTrader(trader);
|
|
|
|
// Assert
|
|
// A trader cannot be both good and bad
|
|
(isGood && isBad).Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void FindMethods_WorkTogetherCorrectly()
|
|
{
|
|
// Arrange
|
|
var traders = new List<Trader>
|
|
{
|
|
new Trader { Winrate = 35, TradeCount = 10, AverageWin = 100m, AverageLoss = -50m, Pnl = 250m }, // Good
|
|
new Trader { Winrate = 25, TradeCount = 10, AverageWin = 50m, AverageLoss = -200m, Pnl = -500m }, // Bad
|
|
new Trader { Winrate = 32, TradeCount = 5, AverageWin = 75m, AverageLoss = -75m, Pnl = 0m } // Neither
|
|
};
|
|
|
|
// Act
|
|
var goodTraders = traders.FindGoodTrader();
|
|
var badTraders = traders.FindBadTrader();
|
|
|
|
// Assert
|
|
goodTraders.Should().HaveCount(1);
|
|
badTraders.Should().HaveCount(1);
|
|
goodTraders.First().Should().NotBe(badTraders.First());
|
|
}
|
|
} |