We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/FajarArrizki/mcp-technical-analysis'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
/**
* Bounce Risk Management
* calculateBounceTPTrail, calculateBounceSLOffset functions
*/
import { Signal, HistoricalDataPoint } from '../types'
export interface TechnicalIndicators {
ema8?: number | null
atr?: number | null
}
export interface BounceTPTrailResult {
tpPrice: number
isTrailing: boolean
reason: string
emaLevel?: number
}
/**
* Calculate Dynamic TP Trail for Bounce Signals
* Trails TP based on EMA8 cross detection for faster exit
*/
export function calculateBounceTPTrail(
_entryPrice: number,
signal: Signal & {
bounce_mode?: boolean
bounce_type?: 'BUY_BOUNCE' | 'SELL_BOUNCE'
},
indicators: TechnicalIndicators | null,
historicalData: HistoricalDataPoint[],
bounceTP: number
): BounceTPTrailResult {
if (!signal.bounce_mode || !indicators || !historicalData || historicalData.length < 2) {
return {
tpPrice: bounceTP,
isTrailing: false,
reason: 'Not a bounce signal or insufficient data'
}
}
const isBuyBounce = signal.bounce_type === 'BUY_BOUNCE' ||
(signal.signal === 'buy_to_enter' && signal.bounce_mode)
const isSellBounce = signal.bounce_type === 'SELL_BOUNCE' ||
(signal.signal === 'sell_to_enter' && signal.bounce_mode)
if (!isBuyBounce && !isSellBounce) {
return { tpPrice: bounceTP, isTrailing: false, reason: 'Not a bounce signal' }
}
const ema8 = indicators.ema8
if (!ema8 || ema8 === null) {
return { tpPrice: bounceTP, isTrailing: false, reason: 'EMA8 not available' }
}
const currentPrice = historicalData[historicalData.length - 1].close
const previousPrice = historicalData[historicalData.length - 2].close
// const previousEma8 = indicators.ema8 // For simplicity, use current EMA8 (in real implementation, track EMA8 history)
// For BUY bounce: TP trail = min(bounceTP, price when EMA8 crossdown occurs)
if (isBuyBounce) {
// Check if EMA8 crossdown occurred (price crossed below EMA8)
if (previousPrice >= ema8 && currentPrice < ema8) {
// EMA8 crossdown detected - use current price as trailing TP
const trailingTP = currentPrice
if (trailingTP < bounceTP) {
return {
tpPrice: trailingTP,
isTrailing: true,
reason: `EMA8 crossdown detected at $${trailingTP.toFixed(2)} (below bounce TP $${bounceTP.toFixed(2)}) - using trailing TP`,
emaLevel: ema8
}
}
}
}
// For SELL bounce: If price crosses above EMA8, pullback failed - exit at current price
// TP trail = current price when EMA8 crossup (faster exit than waiting for bounceTP)
if (isSellBounce) {
// Check if EMA8 crossup occurred (price crossed above EMA8)
if (previousPrice <= ema8 && currentPrice > ema8) {
// EMA8 crossup detected - pullback failed, exit at current price
const trailingTP = currentPrice
// For SELL: Use trailing TP if it's above entry (means we can exit with profit or smaller loss)
// This allows faster exit when momentum changes
return {
tpPrice: trailingTP,
isTrailing: true,
reason: `EMA8 crossup detected at $${trailingTP.toFixed(2)} (pullback failed, momentum changed) - using trailing TP for faster exit`,
emaLevel: ema8
}
}
}
// No trailing TP - use original bounce TP
return {
tpPrice: bounceTP,
isTrailing: false,
reason: 'No EMA8 cross detected, using original bounce TP'
}
}
/**
* Calculate Dynamic SL Offset for Bounce Signals
* ATR tinggi → SL × 1.5 (lebar untuk hindari shadow wick)
* ATR rendah → SL × 0.8 (ketat)
*/
export function calculateBounceSLOffset(
slDistance: number,
indicators: TechnicalIndicators | null,
entryPrice: number
): number {
if (!indicators || !indicators.atr || !entryPrice) {
return slDistance // Return original if no ATR data
}
const atr = indicators.atr
const atrPercent = (atr / entryPrice) * 100
// High ATR (> 3%): Use wider SL (× 1.5) to avoid shadow wick
if (atrPercent > 3.0) {
return slDistance * 1.5
}
// Low ATR (< 1.5%): Use tight SL (× 0.8)
else if (atrPercent < 1.5) {
return slDistance * 0.8
}
// Normal ATR (1.5% - 3%): Use standard SL
return slDistance
}