Files
trading_bot_v4/tests/integration/position-manager/decision-helpers.test.ts
copilot-swe-agent[bot] 1b6297b1e2 chore: Fix .gitignore - remove test file exclusions, add coverage folder
- Removed incorrect exclusion of *.test.ts and *.test.js files
- Added coverage/ folder to .gitignore
- Removed accidentally committed coverage files

Co-authored-by: mindesbunister <32161838+mindesbunister@users.noreply.github.com>
2025-12-05 00:16:50 +00:00

309 lines
11 KiB
TypeScript

/**
* 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
})
})
})