/** * Validation script for position sizing calculations */ import { fixedRiskPositionSize, kellyPositionSize, volatilityTargetPositionSize, equalWeightPositionSize, atrBasedPositionSize, expectancyPositionSize, calculatePortfolioHeat, validatePositionSize } from '../src/calculations/position-sizing.js'; console.log('=== Position Sizing Calculation Validation ===\n'); // Test 1: Fixed Risk Position Sizing console.log('1. Fixed Risk Position Sizing'); const fixedRiskResult = fixedRiskPositionSize({ accountSize: 100000, riskPercentage: 2, entryPrice: 100, stopLoss: 95, leverage: 1 }); console.log(` Account: $100,000, Risk: 2%, Entry: $100, Stop: $95`); console.log(` Result: ${fixedRiskResult} shares`); console.log(` Expected: 400 shares (Risk: $2,000 ÷ $5 risk per share = 400)`); console.log(` ✓ ${fixedRiskResult === 400 ? 'PASS' : 'FAIL'}\n`); // Test 2: Kelly Criterion console.log('2. Kelly Criterion Position Sizing'); const kellyResult = kellyPositionSize({ winRate: 0.6, averageWin: 150, averageLoss: -100 }, 100000); console.log(` Win Rate: 60%, Avg Win: $150, Avg Loss: $100`); console.log(` Result: $${kellyResult.toFixed(0)}`); console.log(` Kelly formula with safety factor applied`); console.log(` ✓ ${kellyResult > 0 && kellyResult < 25000 ? 'PASS' : 'FAIL'}\n`); // Test 3: Volatility Target Position Sizing console.log('3. Volatility Target Position Sizing'); const volResult = volatilityTargetPositionSize({ price: 100, volatility: 0.20, targetVolatility: 0.10, lookbackDays: 30 }, 100000); console.log(` Price: $100, Asset Vol: 20%, Target Vol: 10%`); console.log(` Result: ${volResult} shares`); console.log(` Expected: 500 shares (Vol ratio 0.5 * $100k = $50k ÷ $100 = 500)`); console.log(` ✓ ${volResult === 500 ? 'PASS' : 'FAIL'}\n`); // Test 4: Equal Weight Position Sizing console.log('4. Equal Weight Position Sizing'); const equalResult = equalWeightPositionSize(100000, 5, 100); console.log(` Account: $100,000, Positions: 5, Price: $100`); console.log(` Result: ${equalResult} shares`); console.log(` Expected: 200 shares ($100k ÷ 5 = $20k ÷ $100 = 200)`); console.log(` ✓ ${equalResult === 200 ? 'PASS' : 'FAIL'}\n`); // Test 5: ATR-Based Position Sizing console.log('5. ATR-Based Position Sizing'); const atrResult = atrBasedPositionSize(100000, 2, 5, 2, 100); console.log(` Account: $100,000, Risk: 2%, ATR: $5, Multiplier: 2`); console.log(` Result: ${atrResult} shares`); console.log(` Expected: 200 shares (Risk: $2k ÷ Stop: $10 = 200)`); console.log(` ✓ ${atrResult === 200 ? 'PASS' : 'FAIL'}\n`); // Test 6: Expectancy Position Sizing console.log('6. Expectancy Position Sizing'); const expectancyResult = expectancyPositionSize(100000, 0.6, 150, -100, 5); console.log(` Win Rate: 60%, Avg Win: $150, Avg Loss: $100`); console.log(` Result: $${expectancyResult.toFixed(0)}`); console.log(` Expectancy: 0.6*150 - 0.4*100 = 50 (positive expectancy)`); console.log(` ✓ ${expectancyResult > 0 ? 'PASS' : 'FAIL'}\n`); // Test 7: Portfolio Heat Calculation console.log('7. Portfolio Heat Calculation'); const heatResult = calculatePortfolioHeat([ { value: 10000, risk: 500 }, { value: 15000, risk: 750 }, { value: 20000, risk: 1000 } ], 100000); console.log(` Positions with risks: $500, $750, $1000`); console.log(` Result: ${heatResult}%`); console.log(` Expected: 2.25% (Total risk: $2250 ÷ $100k = 2.25%)`); console.log(` ✓ ${heatResult === 2.25 ? 'PASS' : 'FAIL'}\n`); // Test 8: Position Size Validation console.log('8. Position Size Validation'); const validationResult = validatePositionSize(50, 100, 100000, 10, 2); console.log(` Position: 50 shares @ $100, Account: $100k, Max: 10%`); console.log(` Result: ${validationResult.isValid ? 'Valid' : 'Invalid'}`); console.log(` Position value: $5,000 (5% of account - within 10% limit)`); console.log(` ✓ ${validationResult.isValid ? 'PASS' : 'FAIL'}\n`); // Test edge cases console.log('=== Edge Case Testing ===\n'); // Zero/negative inputs console.log('9. Zero/Negative Input Handling'); const zeroResult = fixedRiskPositionSize({ accountSize: 0, riskPercentage: 2, entryPrice: 100, stopLoss: 95 }); console.log(` Zero account size result: ${zeroResult}`); console.log(` ✓ ${zeroResult === 0 ? 'PASS' : 'FAIL'}`); const equalStopResult = fixedRiskPositionSize({ accountSize: 100000, riskPercentage: 2, entryPrice: 100, stopLoss: 100 }); console.log(` Equal entry/stop result: ${equalStopResult}`); console.log(` ✓ ${equalStopResult === 0 ? 'PASS' : 'FAIL'}\n`); // Negative expectancy Kelly console.log('10. Negative Expectancy Kelly'); const negativeKellyResult = kellyPositionSize({ winRate: 0.3, averageWin: 100, averageLoss: -200 }, 100000); console.log(` Win Rate: 30%, Avg Win: $100, Avg Loss: $200`); console.log(` Result: $${negativeKellyResult}`); console.log(` Expected: $0 (negative expectancy)`); console.log(` ✓ ${negativeKellyResult === 0 ? 'PASS' : 'FAIL'}\n`); console.log('=== Validation Complete ==='); console.log('All position sizing calculations have been validated!'); console.log('The functions now include proper input validation, edge case handling,'); console.log('and mathematically correct implementations.');