From 40b0d701d5c69a223ee5d34e9a6d6ac7bfddcc23 Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 11:21:50 +0300 Subject: [PATCH 1/3] Initial commit with task details for issue #231 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/linksplatform/Bot/issues/231 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..eddc24ac --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/linksplatform/Bot/issues/231 +Your prepared branch: issue-231-8ba0884f +Your prepared working directory: /tmp/gh-issue-solver-1757578907475 + +Proceed. \ No newline at end of file From 2eecc418ec88eb82e7ecdf9d048c160cfc03a5c7 Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 11:22:07 +0300 Subject: [PATCH 2/3] Remove CLAUDE.md - PR created successfully --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index eddc24ac..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/linksplatform/Bot/issues/231 -Your prepared branch: issue-231-8ba0884f -Your prepared working directory: /tmp/gh-issue-solver-1757578907475 - -Proceed. \ No newline at end of file From c8faeb49d1e969b142675d99c7609b561838131d Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 11:30:28 +0300 Subject: [PATCH 3/3] Implement Kelly Criterion for optimal position sizing in TraderBot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implementation adds the Kelly Criterion algorithm to maximize long-term profit while managing risk through optimal capital allocation. ## Key Features - Kelly Criterion calculator with mathematical accuracy - Dynamic learning from trade history (10+ trades minimum) - Configurable risk limits and safety parameters - Integration with existing trading strategy - Comprehensive test suite with real-world scenarios ## Configuration - UseKellyCriterion: Enable/disable Kelly position sizing - WinProbability: Initial win rate estimate (0.0-1.0) - ProfitLossRatio: Initial profit/loss ratio estimate - KellyFractionLimit: Maximum risk fraction (default 0.25) ## Formula: f = (bp - q) / b Where f = fraction of capital, b = profit/loss ratio, p = win probability, q = loss probability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- csharp/TraderBot/KellyCriterion.cs | 79 +++++++++++ csharp/TraderBot/TradingService.cs | 67 ++++++++- csharp/TraderBot/TradingSettings.cs | 4 + csharp/TraderBot/appsettings.TMON.json | 6 +- csharp/TraderBot/appsettings.TRUR.json | 6 +- examples/KELLY_CRITERION_README.md | 182 +++++++++++++++++++++++++ examples/KellyCriterionTests.cs | 141 +++++++++++++++++++ examples/KellyTestProject.csproj | 14 ++ examples/KellyTestRunner.cs | 48 +++++++ 9 files changed, 542 insertions(+), 5 deletions(-) create mode 100644 csharp/TraderBot/KellyCriterion.cs create mode 100644 examples/KELLY_CRITERION_README.md create mode 100644 examples/KellyCriterionTests.cs create mode 100644 examples/KellyTestProject.csproj create mode 100644 examples/KellyTestRunner.cs diff --git a/csharp/TraderBot/KellyCriterion.cs b/csharp/TraderBot/KellyCriterion.cs new file mode 100644 index 00000000..50030a28 --- /dev/null +++ b/csharp/TraderBot/KellyCriterion.cs @@ -0,0 +1,79 @@ +namespace TraderBot; + +public static class KellyCriterion +{ + /// + /// Calculates the optimal bet size fraction using the Kelly Criterion formula. + /// Formula: f = (bp - q) / b + /// Where: + /// - f = fraction of capital to bet + /// - b = profit/loss ratio (odds) + /// - p = probability of winning + /// - q = probability of losing (1-p) + /// + /// Probability of winning (0.0 to 1.0) + /// The ratio of profit to loss (e.g., 2.0 means profit is 2x the loss) + /// Maximum fraction to limit risk (default 0.25) + /// The optimal fraction of capital to bet (0.0 to maxFraction) + public static double CalculateOptimalBetSize(double winProbability, double profitLossRatio, double maxFraction = 0.25) + { + if (winProbability < 0 || winProbability > 1) + throw new ArgumentException("Win probability must be between 0 and 1", nameof(winProbability)); + + if (profitLossRatio <= 0) + throw new ArgumentException("Profit/loss ratio must be positive", nameof(profitLossRatio)); + + if (maxFraction <= 0 || maxFraction > 1) + throw new ArgumentException("Max fraction must be between 0 and 1", nameof(maxFraction)); + + double lossProbability = 1.0 - winProbability; + + // Kelly Criterion formula: f = (bp - q) / b + double kellyFraction = (profitLossRatio * winProbability - lossProbability) / profitLossRatio; + + // Return 0 if Kelly suggests negative betting (negative expected value) + if (kellyFraction <= 0) + return 0.0; + + // Cap at maximum fraction to limit risk + return Math.Min(kellyFraction, maxFraction); + } + + /// + /// Calculates the win probability and profit/loss ratio from historical operations + /// + /// List of completed operations + /// Tuple containing (winProbability, profitLossRatio) + public static (double WinProbability, double ProfitLossRatio) CalculateHistoricalMetrics( + IEnumerable<(DateTime Date, decimal BuyPrice, decimal SellPrice)> operations) + { + var operationsList = operations.ToList(); + if (operationsList.Count < 10) // Need minimum historical data + return (0.5, 1.0); // Default conservative values + + var wins = 0; + var totalProfit = 0.0m; + var totalLoss = 0.0m; + + foreach (var op in operationsList) + { + var profit = op.SellPrice - op.BuyPrice; + if (profit > 0) + { + wins++; + totalProfit += profit; + } + else if (profit < 0) + { + totalLoss += Math.Abs(profit); + } + } + + var winProbability = (double)wins / operationsList.Count; + var avgProfit = wins > 0 ? (double)(totalProfit / wins) : 0.0; + var avgLoss = (operationsList.Count - wins) > 0 ? (double)(totalLoss / (operationsList.Count - wins)) : 1.0; + var profitLossRatio = avgLoss > 0 ? avgProfit / avgLoss : 1.0; + + return (winProbability, Math.Max(profitLossRatio, 0.1)); // Minimum ratio to avoid division issues + } +} \ No newline at end of file diff --git a/csharp/TraderBot/TradingService.cs b/csharp/TraderBot/TradingService.cs index 0302809b..013b238a 100644 --- a/csharp/TraderBot/TradingService.cs +++ b/csharp/TraderBot/TradingService.cs @@ -39,6 +39,7 @@ public class TradingService : BackgroundService protected readonly ConcurrentDictionary ActiveSellOrders; protected readonly ConcurrentDictionary LotsSets; protected readonly ConcurrentDictionary ActiveSellOrderSourcePrice; + protected readonly List<(DateTime Date, decimal BuyPrice, decimal SellPrice)> CompletedOperations; public TradingService(ILogger logger, InvestApiClient investApi, IHostApplicationLifetime lifetime, TradingSettings settings) { @@ -111,6 +112,7 @@ public TradingService(ILogger logger, InvestApiClient investApi, ActiveSellOrders = new ConcurrentDictionary(); LotsSets = new ConcurrentDictionary(); ActiveSellOrderSourcePrice = new ConcurrentDictionary(); + CompletedOperations = new List<(DateTime, decimal, decimal)>(); LastOperationsCheckpoint = settings.LoadOperationsFrom; } @@ -290,7 +292,15 @@ protected void TrySubtractTradesFromOrder(ConcurrentDictionary o.Price == bestBid)?.Quantity ?? 0; Logger.LogInformation($"marketLotsAtTargetPrice: {marketLotsAtTargetPrice}"); var response = await PlaceBuyOrder(lots, bestBid); @@ -544,7 +554,7 @@ await marketDataStream.RequestStream.WriteAsync(new MarketDataRequest var lotPrice = bestBid * LotSize; if (cashBalance > lotPrice) { - var lots = (long)(cashBalance / lotPrice); + var lots = CalculateOptimalLotSize(cashBalance, lotPrice); var marketLotsAtTargetPrice = orderBook.Bids.FirstOrDefault(o => o.Price == bestBid)?.Quantity ?? 0; Logger.LogInformation($"marketLotsAtTargetPrice: {marketLotsAtTargetPrice}"); var response = await PlaceBuyOrder(lots, bestBid); @@ -652,6 +662,57 @@ private bool IsTimeToBuy() { var currentTime = DateTime.UtcNow.TimeOfDay; return currentTime > MinimumTimeToBuy && currentTime < MaximumTimeToBuy; + } + + private long CalculateOptimalLotSize(decimal cashBalance, decimal lotPrice) + { + if (!Settings.UseKellyCriterion) + { + // Use traditional sizing: all available cash + return (long)(cashBalance / lotPrice); + } + + double winProbability = Settings.WinProbability; + double profitLossRatio = Settings.ProfitLossRatio; + + // If we have enough historical data, calculate metrics dynamically + if (CompletedOperations.Count >= 10) + { + var (historicalWinProb, historicalRatio) = KellyCriterion.CalculateHistoricalMetrics(CompletedOperations); + winProbability = historicalWinProb; + profitLossRatio = historicalRatio; + Logger.LogInformation($"Using historical metrics - Win Probability: {winProbability:F3}, Profit/Loss Ratio: {profitLossRatio:F3}"); + } + else + { + Logger.LogInformation($"Using configured metrics - Win Probability: {winProbability:F3}, Profit/Loss Ratio: {profitLossRatio:F3}"); + } + + var kellyFraction = KellyCriterion.CalculateOptimalBetSize(winProbability, profitLossRatio, Settings.KellyFractionLimit); + var optimalCashToUse = cashBalance * (decimal)kellyFraction; + var lots = (long)Math.Max(1, optimalCashToUse / lotPrice); // Ensure at least 1 lot + + Logger.LogInformation($"Kelly Criterion: Fraction={kellyFraction:F3}, OptimalCash={optimalCashToUse:F2}, Lots={lots}"); + + return lots; + } + + private void TrackCompletedOperation(decimal buyPrice, decimal sellPrice) + { + lock (CompletedOperations) + { + CompletedOperations.Add((DateTime.UtcNow, buyPrice, sellPrice)); + + // Keep only last 100 operations to prevent memory growth + if (CompletedOperations.Count > 100) + { + CompletedOperations.RemoveAt(0); + } + } + + var profit = sellPrice - buyPrice; + var profitPercent = (profit / buyPrice) * 100; + Logger.LogInformation($"Operation completed: Buy={buyPrice}, Sell={sellPrice}, Profit={profit:F4} ({profitPercent:F2}%)"); } private async Task<(decimal, decimal)> GetCashBalance(bool forceRemote = false) diff --git a/csharp/TraderBot/TradingSettings.cs b/csharp/TraderBot/TradingSettings.cs index 884a25df..5f3404a8 100644 --- a/csharp/TraderBot/TradingSettings.cs +++ b/csharp/TraderBot/TradingSettings.cs @@ -17,4 +17,8 @@ public class TradingSettings public long EarlySellOwnedLotsDelta { get; set; } public decimal EarlySellOwnedLotsMultiplier { get; set; } public DateTime LoadOperationsFrom { get; set; } + public bool UseKellyCriterion { get; set; } + public double WinProbability { get; set; } + public double ProfitLossRatio { get; set; } + public double KellyFractionLimit { get; set; } = 0.25; } \ No newline at end of file diff --git a/csharp/TraderBot/appsettings.TMON.json b/csharp/TraderBot/appsettings.TMON.json index c7b66d7a..6d583a41 100644 --- a/csharp/TraderBot/appsettings.TMON.json +++ b/csharp/TraderBot/appsettings.TMON.json @@ -24,6 +24,10 @@ "MaximumTimeToBuy": "23:59:59", "EarlySellOwnedLotsDelta": 300000, "EarlySellOwnedLotsMultiplier": 0, - "LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z" + "LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z", + "UseKellyCriterion": true, + "WinProbability": 0.55, + "ProfitLossRatio": 1.2, + "KellyFractionLimit": 0.25 } } diff --git a/csharp/TraderBot/appsettings.TRUR.json b/csharp/TraderBot/appsettings.TRUR.json index 1dc848e6..e873e199 100644 --- a/csharp/TraderBot/appsettings.TRUR.json +++ b/csharp/TraderBot/appsettings.TRUR.json @@ -24,6 +24,10 @@ "MaximumTimeToBuy": "14:45:00", "EarlySellOwnedLotsDelta": 300000, "EarlySellOwnedLotsMultiplier": 0, - "LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z" + "LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z", + "UseKellyCriterion": true, + "WinProbability": 0.52, + "ProfitLossRatio": 1.1, + "KellyFractionLimit": 0.2 } } diff --git a/examples/KELLY_CRITERION_README.md b/examples/KELLY_CRITERION_README.md new file mode 100644 index 00000000..ec9067e0 --- /dev/null +++ b/examples/KELLY_CRITERION_README.md @@ -0,0 +1,182 @@ +# Kelly Criterion Implementation for TraderBot + +## Overview + +This implementation adds Kelly Criterion position sizing to the TraderBot, enabling optimal capital allocation to maximize long-term profit while managing risk. The Kelly Criterion was developed by John Kelly at Bell Labs in 1956 and is widely used by professional traders and investors. + +## Mathematical Formula + +The Kelly Criterion calculates the optimal fraction of capital to risk using: + +``` +f = (bp - q) / b +``` + +Where: +- `f` = fraction of capital to bet/invest +- `b` = profit/loss ratio (odds received) +- `p` = probability of winning +- `q` = probability of losing (1-p) + +## Key Features + +### 1. Optimal Position Sizing +- Calculates optimal lot size based on historical performance or configured parameters +- Prevents over-betting and under-betting +- Maximizes long-term geometric growth rate + +### 2. Dynamic Learning +- Automatically calculates win probability and profit/loss ratio from completed trades +- Adapts position sizing based on actual performance +- Falls back to configured values when insufficient historical data + +### 3. Risk Management +- Configurable maximum fraction limit (default 25%) to prevent excessive risk +- Returns 0% allocation for negative expected value strategies +- Built-in safeguards against calculation errors + +### 4. Comprehensive Configuration +- Toggle Kelly Criterion on/off per trading instrument +- Configure initial win probability and profit/loss ratio estimates +- Set maximum risk fraction limits + +## Configuration Parameters + +Add these parameters to your `appsettings.json` under `TradingSettings`: + +```json +{ + "TradingSettings": { + // ... existing settings ... + "UseKellyCriterion": true, + "WinProbability": 0.55, + "ProfitLossRatio": 1.2, + "KellyFractionLimit": 0.25 + } +} +``` + +### Parameter Details + +- **UseKellyCriterion**: Enable/disable Kelly position sizing (default: false) +- **WinProbability**: Initial estimate of win rate (0.0 to 1.0) +- **ProfitLossRatio**: Initial estimate of average profit to average loss ratio +- **KellyFractionLimit**: Maximum fraction of capital to risk (0.0 to 1.0, default: 0.25) + +## Real-World Examples + +### Conservative Trading (Example 1) +- Win Rate: 52% +- Profit/Loss Ratio: 1.1:1 +- Max Risk: 10% +- **Result: 8.4% of capital per trade** + +### Aggressive Trading (Example 2) +- Win Rate: 65% +- Profit/Loss Ratio: 1.5:1 +- Max Risk: 50% +- **Result: 41.7% of capital per trade** + +### High Win Rate, Low Profit (Example 3) +- Win Rate: 80% +- Profit/Loss Ratio: 0.8:1 +- Max Risk: 25% +- **Result: 25.0% of capital per trade (capped)** + +### Breakeven Strategy (Example 4) +- Win Rate: 50% +- Profit/Loss Ratio: 1.0:1 +- Max Risk: 25% +- **Result: 0.0% of capital per trade (negative expected value)** + +## Implementation Details + +### Core Components + +1. **KellyCriterion.cs**: Static calculation methods +2. **TradingSettings.cs**: Configuration parameters +3. **TradingService.cs**: Integration with existing trading logic + +### Key Methods + +- `CalculateOptimalBetSize()`: Main Kelly calculation +- `CalculateHistoricalMetrics()`: Dynamic learning from trade history +- `CalculateOptimalLotSize()`: Integration with existing lot sizing +- `TrackCompletedOperation()`: Trade history tracking + +### Behavior Changes + +When Kelly Criterion is enabled: +1. **Position Sizing**: Uses Kelly formula instead of "all available cash" +2. **Risk Management**: Automatically reduces position size for poor-performing strategies +3. **Learning**: Adapts to actual performance over time +4. **Logging**: Provides detailed Kelly calculation information + +When Kelly Criterion is disabled: +- Falls back to original position sizing logic +- No behavior changes to existing functionality + +## Testing + +Comprehensive test suite includes: +- Mathematical accuracy tests +- Edge case handling +- Historical metrics calculation +- Real-world scenario simulations + +Run tests with: +```bash +cd examples +dotnet run +``` + +## Risk Considerations + +### Important Warnings + +1. **Accurate Probabilities Required**: Kelly Criterion requires accurate estimates of win probability and profit/loss ratios. Overestimating leads to excessive risk. + +2. **Volatility**: Kelly sizing can be volatile. Consider using fractional Kelly (e.g., 50% of calculated size) for smoother equity curves. + +3. **Historical Data**: Algorithm needs minimum 10 completed trades for dynamic learning. Uses configured values otherwise. + +4. **Market Conditions**: Kelly assumes consistent market conditions. Performance may vary during regime changes. + +### Best Practices + +- Start with conservative estimates (lower win rates, profit/loss ratios) +- Use fractional Kelly (25% or less) to reduce volatility +- Monitor performance and adjust parameters based on actual results +- Maintain diverse trading strategies to spread risk + +## Advanced Usage + +### Fractional Kelly + +Many professional traders use fractional Kelly to reduce volatility: +- Full Kelly: Use calculated fraction +- Half Kelly: Use 50% of calculated fraction +- Quarter Kelly: Use 25% of calculated fraction + +Set `KellyFractionLimit` to implement fractional Kelly. + +### Dynamic Adjustment + +The system automatically switches to historical metrics after 10+ completed trades: +- Improves accuracy over time +- Adapts to changing market conditions +- Provides more reliable position sizing + +## References + +- Kelly, J. L. (1956). "A New Interpretation of Information Rate" +- Thorp, E. O. (2006). "The Kelly Capital Growth Investment Criterion" +- MacLean, L. C., Thorp, E. O., & Ziemba, W. T. (2011). "The Kelly Capital Growth Investment Criterion: Theory and Practice" + +## Support + +For questions or issues related to Kelly Criterion implementation: +1. Review configuration parameters +2. Check log output for Kelly calculation details +3. Run test suite to verify functionality +4. Ensure minimum trade history for dynamic learning \ No newline at end of file diff --git a/examples/KellyCriterionTests.cs b/examples/KellyCriterionTests.cs new file mode 100644 index 00000000..fea8ef8e --- /dev/null +++ b/examples/KellyCriterionTests.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TraderBot; + +namespace TraderBot.Examples +{ + public class KellyCriterionTests + { + public static void RunAllTests() + { + Console.WriteLine("=== Kelly Criterion Tests ==="); + + TestBasicKellyCalculation(); + TestNegativeExpectedValue(); + TestMaximumFractionLimit(); + TestEdgeCases(); + TestHistoricalMetrics(); + + Console.WriteLine("=== All tests completed ==="); + } + + private static void TestBasicKellyCalculation() + { + Console.WriteLine("\n--- Test: Basic Kelly Calculation ---"); + + // Test case: 60% win rate, 2:1 profit/loss ratio + var winProbability = 0.6; + var profitLossRatio = 2.0; + var result = KellyCriterion.CalculateOptimalBetSize(winProbability, profitLossRatio, 1.0); + + // Expected: (2 * 0.6 - 0.4) / 2 = 0.4 + var expected = 0.4; + Console.WriteLine($"Win Rate: {winProbability}, P/L Ratio: {profitLossRatio}"); + Console.WriteLine($"Expected: {expected:F3}, Actual: {result:F3}"); + + if (Math.Abs(result - expected) < 0.001) + Console.WriteLine("✓ PASS"); + else + Console.WriteLine("✗ FAIL"); + } + + private static void TestNegativeExpectedValue() + { + Console.WriteLine("\n--- Test: Negative Expected Value ---"); + + // Test case: 40% win rate, 1:1 profit/loss ratio (negative expected value) + var winProbability = 0.4; + var profitLossRatio = 1.0; + var result = KellyCriterion.CalculateOptimalBetSize(winProbability, profitLossRatio); + + Console.WriteLine($"Win Rate: {winProbability}, P/L Ratio: {profitLossRatio}"); + Console.WriteLine($"Result: {result:F3}"); + + if (result == 0.0) + Console.WriteLine("✓ PASS - Correctly returns 0 for negative expected value"); + else + Console.WriteLine("✗ FAIL - Should return 0 for negative expected value"); + } + + private static void TestMaximumFractionLimit() + { + Console.WriteLine("\n--- Test: Maximum Fraction Limit ---"); + + // Test case: Very high Kelly fraction that should be capped + var winProbability = 0.9; + var profitLossRatio = 10.0; + var maxFraction = 0.25; + var result = KellyCriterion.CalculateOptimalBetSize(winProbability, profitLossRatio, maxFraction); + + Console.WriteLine($"Win Rate: {winProbability}, P/L Ratio: {profitLossRatio}, Max: {maxFraction}"); + Console.WriteLine($"Result: {result:F3}"); + + if (result <= maxFraction) + Console.WriteLine("✓ PASS - Correctly capped at maximum fraction"); + else + Console.WriteLine("✗ FAIL - Should be capped at maximum fraction"); + } + + private static void TestEdgeCases() + { + Console.WriteLine("\n--- Test: Edge Cases ---"); + + try + { + // Test invalid win probability + KellyCriterion.CalculateOptimalBetSize(-0.1, 1.0); + Console.WriteLine("✗ FAIL - Should throw exception for negative win probability"); + } + catch (ArgumentException) + { + Console.WriteLine("✓ PASS - Correctly throws exception for negative win probability"); + } + + try + { + // Test invalid profit/loss ratio + KellyCriterion.CalculateOptimalBetSize(0.5, -1.0); + Console.WriteLine("✗ FAIL - Should throw exception for negative profit/loss ratio"); + } + catch (ArgumentException) + { + Console.WriteLine("✓ PASS - Correctly throws exception for negative profit/loss ratio"); + } + } + + private static void TestHistoricalMetrics() + { + Console.WriteLine("\n--- Test: Historical Metrics Calculation ---"); + + var operations = new List<(DateTime, decimal, decimal)> + { + (DateTime.Now.AddDays(-10), 100m, 110m), // Win: +10 + (DateTime.Now.AddDays(-9), 100m, 95m), // Loss: -5 + (DateTime.Now.AddDays(-8), 100m, 108m), // Win: +8 + (DateTime.Now.AddDays(-7), 100m, 92m), // Loss: -8 + (DateTime.Now.AddDays(-6), 100m, 105m), // Win: +5 + (DateTime.Now.AddDays(-5), 100m, 98m), // Loss: -2 + (DateTime.Now.AddDays(-4), 100m, 112m), // Win: +12 + (DateTime.Now.AddDays(-3), 100m, 97m), // Loss: -3 + (DateTime.Now.AddDays(-2), 100m, 106m), // Win: +6 + (DateTime.Now.AddDays(-1), 100m, 104m), // Win: +4 + }; + + var (winProb, profitLossRatio) = KellyCriterion.CalculateHistoricalMetrics(operations); + + // Expected: 6 wins out of 10 = 60% win rate + // Average win: (10+8+5+12+6+4)/6 = 7.5 + // Average loss: (5+8+2+3)/4 = 4.5 + // Profit/Loss ratio: 7.5/4.5 = 1.67 + + Console.WriteLine($"Win Probability: {winProb:F3} (expected ~0.600)"); + Console.WriteLine($"Profit/Loss Ratio: {profitLossRatio:F3} (expected ~1.667)"); + + if (Math.Abs(winProb - 0.6) < 0.001 && Math.Abs(profitLossRatio - 1.667) < 0.01) + Console.WriteLine("✓ PASS - Historical metrics calculated correctly"); + else + Console.WriteLine("✗ FAIL - Historical metrics calculation error"); + } + } +} \ No newline at end of file diff --git a/examples/KellyTestProject.csproj b/examples/KellyTestProject.csproj new file mode 100644 index 00000000..706bad2a --- /dev/null +++ b/examples/KellyTestProject.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8 + enable + enable + + + + + + + \ No newline at end of file diff --git a/examples/KellyTestRunner.cs b/examples/KellyTestRunner.cs new file mode 100644 index 00000000..561a08bd --- /dev/null +++ b/examples/KellyTestRunner.cs @@ -0,0 +1,48 @@ +using System; +using TraderBot.Examples; + +namespace TraderBot.Examples +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Kelly Criterion Test Suite"); + Console.WriteLine("=========================="); + + try + { + KellyCriterionTests.RunAllTests(); + + Console.WriteLine("\n=== Real-world Examples ==="); + DemonstrateRealWorldScenarios(); + } + catch (Exception ex) + { + Console.WriteLine($"Test execution failed: {ex.Message}"); + Console.WriteLine($"Stack trace: {ex.StackTrace}"); + } + + Console.WriteLine("\n=== Tests completed ==="); + } + + private static void DemonstrateRealWorldScenarios() + { + Console.WriteLine("\n--- Scenario 1: Conservative Trading ---"); + var result1 = KellyCriterion.CalculateOptimalBetSize(0.52, 1.1, 0.1); + Console.WriteLine($"52% win rate, 1.1:1 ratio, max 10%: {result1:P1} of capital"); + + Console.WriteLine("\n--- Scenario 2: Aggressive Trading ---"); + var result2 = KellyCriterion.CalculateOptimalBetSize(0.65, 1.5, 0.5); + Console.WriteLine($"65% win rate, 1.5:1 ratio, max 50%: {result2:P1} of capital"); + + Console.WriteLine("\n--- Scenario 3: High Win Rate, Low Profit ---"); + var result3 = KellyCriterion.CalculateOptimalBetSize(0.8, 0.8, 0.25); + Console.WriteLine($"80% win rate, 0.8:1 ratio, max 25%: {result3:P1} of capital"); + + Console.WriteLine("\n--- Scenario 4: Breakeven Strategy ---"); + var result4 = KellyCriterion.CalculateOptimalBetSize(0.5, 1.0, 0.25); + Console.WriteLine($"50% win rate, 1.0:1 ratio, max 25%: {result4:P1} of capital"); + } + } +} \ No newline at end of file