feat: DEBT-010/011 — position sizing + cost 2x refinement
deploy / deploy (push) Failing after 2m30s
deploy / notify (push) Successful in 1s

DEBT-010 (High/High, position sizing):
- Add portfolio heat calculation (% exposure in open positions)
- Implement confidence-based multiplier (0.5x-1.5x)
- Add heat-based multiplier (reduce sizing if >60% exposed)
- Single-ticker cap: max 15% of portfolio per position
- Result: More realistic order sizing reflecting risk management

DEBT-011 (High/High, cost 2x simulation):
- Calculate actual transaction costs from order history
- Apply 2x cost multiplier based on actual fees paid
- Adjust return = (TotalReturn * InitialCapital - 2xCosts) / InitialCapital
- Replaces: linear approximation (TotalReturn * 0.5m)
- Result: Realistic cost impact on strategy profitability

Both changes align with Gate 3 validation scope:
- No data-driven thresholds added (use provided parameters)
- No schedule activation (Phase 1 only)
- No backtesting methodology change (still simplified CV)

AGENTS.md v16.0 principles:
 Necessity-driven: Both improve validation gates accuracy
 Simplicity: Minimal code, clear logic
 Pattern: Standard Kelly Criterion + heat management
 Current evidence: Code review + test framework ready
 Stability: No breaking changes, backward compatible

Next: DEBT-012 (false-exit analysis) + remaining WBS items

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 17:51:18 +09:00
parent 42f355f9db
commit a1f4979c7e
2 changed files with 47 additions and 6 deletions
@@ -79,6 +79,10 @@ public sealed class ReplayEngine(
var daySignals = await GenerateSignalsAsync(modelId, session, ohlcvBars, cancellationToken);
signals.AddRange(daySignals);
// Calculate current portfolio heat (% of capital at risk in open positions)
var currentExposure = currentPortfolio.Positions
.Sum(pos => pos.Value * GetClosePrice(session, pos.Key, ohlcvBars)) / currentPortfolio.TotalValue;
// Convert signals to orders with dynamic position sizing
var dayOrders = daySignals
.Select(s =>
@@ -86,11 +90,21 @@ public sealed class ReplayEngine(
var closePrice = GetClosePrice(session, s.Ticker, ohlcvBars);
if (closePrice <= 0) return null;
// Position size: 2% of portfolio per signal (Kelly Criterion simplified)
// Higher confidence → larger position (0.5x to 1.5x multiplier)
var riskPercentage = 0.02m * s.Confidence * 2m; // Ranges 0.01-0.03
// Dynamic position sizing: Kelly Criterion + heat/confidence adjustment
// Base: 2% of portfolio per signal
// Multipliers: (1) Confidence: 0.5x-1.5x, (2) Heat: reduce if over 60% exposed
var baseRisk = 0.02m;
var confidenceMultiplier = 0.5m + (s.Confidence * 1.0m); // 0.5x-1.5x
var heatMultiplier = currentExposure > 0.60m ? 0.5m : 1.0m; // Reduce if hot
var riskPercentage = baseRisk * confidenceMultiplier * heatMultiplier;
var targetCash = currentPortfolio.TotalValue * riskPercentage;
var quantity = Math.Max(1L, (long)(targetCash / closePrice));
// Single-ticker cap: max 15% of portfolio per position
var maxTickerExposure = currentPortfolio.TotalValue * 0.15m;
var maxQuantity = Math.Max(1L, (long)(maxTickerExposure / closePrice));
var quantity = Math.Min(maxQuantity, Math.Max(1L, (long)(targetCash / closePrice)));
return new Order(
OrderId: Guid.NewGuid(),