/** * Decision Helpers Tests * * Tests for all decision helper functions in Position Manager: * - shouldEmergencyStop() - LONG and SHORT * - shouldStopLoss() - LONG and SHORT * - shouldTakeProfit1() - LONG and SHORT * - shouldTakeProfit2() - LONG and SHORT */ import { createLongTrade, createShortTrade, TEST_DEFAULTS } from '../../helpers/trade-factory' describe('Decision Helpers', () => { // Extract decision helpers from Position Manager // These are pure functions that test price against targets function shouldEmergencyStop(price: number, trade: { direction: 'long' | 'short', emergencyStopPrice: number }): boolean { if (trade.direction === 'long') { return price <= trade.emergencyStopPrice } else { return price >= trade.emergencyStopPrice } } function shouldStopLoss(price: number, trade: { direction: 'long' | 'short', stopLossPrice: number }): boolean { if (trade.direction === 'long') { return price <= trade.stopLossPrice } else { return price >= trade.stopLossPrice } } function shouldTakeProfit1(price: number, trade: { direction: 'long' | 'short', tp1Price: number }): boolean { if (trade.direction === 'long') { return price >= trade.tp1Price } else { return price <= trade.tp1Price } } function shouldTakeProfit2(price: number, trade: { direction: 'long' | 'short', tp2Price: number }): boolean { if (trade.direction === 'long') { return price >= trade.tp2Price } else { return price <= trade.tp2Price } } describe('shouldEmergencyStop()', () => { describe('LONG positions', () => { it('should trigger emergency stop when price at -2% (emergency level)', () => { const trade = createLongTrade({ entryPrice: 140 }) // Emergency stop at -2% = $137.20 expect(trade.emergencyStopPrice).toBe(TEST_DEFAULTS.long.emergencySl) expect(shouldEmergencyStop(137.20, trade)).toBe(true) }) it('should trigger emergency stop when price below emergency level', () => { const trade = createLongTrade() expect(shouldEmergencyStop(136.00, trade)).toBe(true) // Below emergency expect(shouldEmergencyStop(135.00, trade)).toBe(true) // Well below }) it('should NOT trigger emergency stop above emergency level', () => { const trade = createLongTrade() expect(shouldEmergencyStop(138.00, trade)).toBe(false) // Above emergency but below SL expect(shouldEmergencyStop(140.00, trade)).toBe(false) // At entry expect(shouldEmergencyStop(141.00, trade)).toBe(false) // In profit }) }) describe('SHORT positions', () => { it('should trigger emergency stop when price at +2% (emergency level)', () => { const trade = createShortTrade({ entryPrice: 140 }) // Emergency stop at +2% = $142.80 expect(trade.emergencyStopPrice).toBe(TEST_DEFAULTS.short.emergencySl) expect(shouldEmergencyStop(142.80, trade)).toBe(true) }) it('should trigger emergency stop when price above emergency level', () => { const trade = createShortTrade() expect(shouldEmergencyStop(143.00, trade)).toBe(true) expect(shouldEmergencyStop(145.00, trade)).toBe(true) }) it('should NOT trigger emergency stop below emergency level', () => { const trade = createShortTrade() expect(shouldEmergencyStop(142.00, trade)).toBe(false) // Below emergency but at SL expect(shouldEmergencyStop(140.00, trade)).toBe(false) // At entry expect(shouldEmergencyStop(138.00, trade)).toBe(false) // In profit }) }) }) describe('shouldStopLoss()', () => { describe('LONG positions', () => { it('should trigger SL when price at stop loss level', () => { const trade = createLongTrade({ entryPrice: 140 }) // SL at -0.92% = $138.71 expect(trade.stopLossPrice).toBe(TEST_DEFAULTS.long.sl) expect(shouldStopLoss(138.71, trade)).toBe(true) }) it('should trigger SL when price below stop loss', () => { const trade = createLongTrade() expect(shouldStopLoss(138.00, trade)).toBe(true) expect(shouldStopLoss(137.50, trade)).toBe(true) }) it('should NOT trigger SL when price above stop loss', () => { const trade = createLongTrade() expect(shouldStopLoss(139.00, trade)).toBe(false) expect(shouldStopLoss(140.00, trade)).toBe(false) expect(shouldStopLoss(141.00, trade)).toBe(false) }) it('should work with adjusted SL (breakeven after TP1)', () => { const trade = createLongTrade({ entryPrice: 140, stopLossPrice: 140.00, // Moved to breakeven tp1Hit: true }) expect(shouldStopLoss(139.99, trade)).toBe(true) // Just below breakeven expect(shouldStopLoss(140.00, trade)).toBe(true) // At breakeven expect(shouldStopLoss(140.01, trade)).toBe(false) // Above breakeven }) }) describe('SHORT positions', () => { it('should trigger SL when price at stop loss level', () => { const trade = createShortTrade({ entryPrice: 140 }) // SL at +0.92% = $141.29 expect(trade.stopLossPrice).toBe(TEST_DEFAULTS.short.sl) expect(shouldStopLoss(141.29, trade)).toBe(true) }) it('should trigger SL when price above stop loss', () => { const trade = createShortTrade() expect(shouldStopLoss(142.00, trade)).toBe(true) expect(shouldStopLoss(143.00, trade)).toBe(true) }) it('should NOT trigger SL when price below stop loss', () => { const trade = createShortTrade() expect(shouldStopLoss(141.00, trade)).toBe(false) expect(shouldStopLoss(140.00, trade)).toBe(false) expect(shouldStopLoss(138.00, trade)).toBe(false) }) it('should work with adjusted SL (breakeven after TP1)', () => { const trade = createShortTrade({ entryPrice: 140, stopLossPrice: 140.00, // Moved to breakeven tp1Hit: true }) expect(shouldStopLoss(140.01, trade)).toBe(true) // Just above breakeven expect(shouldStopLoss(140.00, trade)).toBe(true) // At breakeven expect(shouldStopLoss(139.99, trade)).toBe(false) // Below breakeven (in profit) }) }) }) describe('shouldTakeProfit1()', () => { describe('LONG positions', () => { it('should trigger TP1 when price at target', () => { const trade = createLongTrade({ entryPrice: 140 }) expect(trade.tp1Price).toBe(TEST_DEFAULTS.long.tp1) expect(shouldTakeProfit1(141.20, trade)).toBe(true) }) it('should trigger TP1 when price above target', () => { const trade = createLongTrade() expect(shouldTakeProfit1(141.50, trade)).toBe(true) expect(shouldTakeProfit1(142.00, trade)).toBe(true) }) it('should NOT trigger TP1 when price below target', () => { const trade = createLongTrade() expect(shouldTakeProfit1(141.00, trade)).toBe(false) expect(shouldTakeProfit1(140.00, trade)).toBe(false) expect(shouldTakeProfit1(139.00, trade)).toBe(false) }) }) describe('SHORT positions', () => { it('should trigger TP1 when price at target', () => { const trade = createShortTrade({ entryPrice: 140 }) expect(trade.tp1Price).toBe(TEST_DEFAULTS.short.tp1) expect(shouldTakeProfit1(138.80, trade)).toBe(true) }) it('should trigger TP1 when price below target (better for short)', () => { const trade = createShortTrade() expect(shouldTakeProfit1(138.50, trade)).toBe(true) expect(shouldTakeProfit1(138.00, trade)).toBe(true) }) it('should NOT trigger TP1 when price above target', () => { const trade = createShortTrade() expect(shouldTakeProfit1(139.00, trade)).toBe(false) expect(shouldTakeProfit1(140.00, trade)).toBe(false) expect(shouldTakeProfit1(141.00, trade)).toBe(false) }) }) }) describe('shouldTakeProfit2()', () => { describe('LONG positions', () => { it('should trigger TP2 when price at target', () => { const trade = createLongTrade({ entryPrice: 140 }) expect(trade.tp2Price).toBe(TEST_DEFAULTS.long.tp2) expect(shouldTakeProfit2(142.41, trade)).toBe(true) }) it('should trigger TP2 when price above target', () => { const trade = createLongTrade() expect(shouldTakeProfit2(143.00, trade)).toBe(true) expect(shouldTakeProfit2(145.00, trade)).toBe(true) }) it('should NOT trigger TP2 when price below target', () => { const trade = createLongTrade() expect(shouldTakeProfit2(142.00, trade)).toBe(false) expect(shouldTakeProfit2(141.00, trade)).toBe(false) expect(shouldTakeProfit2(140.00, trade)).toBe(false) }) }) describe('SHORT positions', () => { it('should trigger TP2 when price at target', () => { const trade = createShortTrade({ entryPrice: 140 }) expect(trade.tp2Price).toBe(TEST_DEFAULTS.short.tp2) expect(shouldTakeProfit2(137.59, trade)).toBe(true) }) it('should trigger TP2 when price below target (better for short)', () => { const trade = createShortTrade() expect(shouldTakeProfit2(137.00, trade)).toBe(true) expect(shouldTakeProfit2(135.00, trade)).toBe(true) }) it('should NOT trigger TP2 when price above target', () => { const trade = createShortTrade() expect(shouldTakeProfit2(138.00, trade)).toBe(false) expect(shouldTakeProfit2(140.00, trade)).toBe(false) expect(shouldTakeProfit2(141.00, trade)).toBe(false) }) }) }) describe('Decision order priority', () => { it('emergency stop should trigger before regular SL', () => { const trade = createLongTrade({ entryPrice: 140 }) const price = 136.00 // Below both emergency and SL const emergencyTriggered = shouldEmergencyStop(price, trade) const slTriggered = shouldStopLoss(price, trade) expect(emergencyTriggered).toBe(true) expect(slTriggered).toBe(true) // In Position Manager, emergency is checked first (higher priority) }) it('SL should NOT be checked if TP already hit the threshold', () => { const trade = createLongTrade({ entryPrice: 140 }) const highPrice = 143.00 // Well above TP1 and TP2 const tp1Triggered = shouldTakeProfit1(highPrice, trade) const tp2Triggered = shouldTakeProfit2(highPrice, trade) const slTriggered = shouldStopLoss(highPrice, trade) expect(tp1Triggered).toBe(true) expect(tp2Triggered).toBe(true) expect(slTriggered).toBe(false) // When price is high, TP triggers but not SL }) }) })