Bot & Automation
Treding Forex Ai
/root/hermes-projects/Treding Forex Ai
src/services/safetyFilter.ts
text
/**
* Safety Filter — dijalankan sebelum setiap scan auto-trading.
*
* Filter ini memastikan bot tidak entry saat kondisi pasar berbahaya:
* - Spread terlalu lebar (volatilitas ekstrem)
* - Market sedang dalam rollover window (XAUUSD: 23:00–01:00 UTC)
* - Daily loss sudah melebihi batas maksimum
* - Sudah ada open posisi untuk simbol yang sama
*/
export interface SafetyCheckResult {
safe: boolean;
reasons: string[];
}
export interface SafetyFilterConfig {
/** Maksimum spread dalam pips sebelum entry ditolak. Default: 50 */
maxSpreadPips: number;
/** Skip rollover window XAUUSD (23:00–01:00 UTC). Default: true */
skipRolloverWindow: boolean;
/** Maksimum daily loss (dalam USD) sebelum trading dihentikan */
maxDailyLossAmount: number;
/** Maksimum posisi terbuka per simbol. 0 = tidak ada batas. Default: 1 */
maxOpenPositions: number;
}
export class SafetyFilter {
public constructor(private readonly config: SafetyFilterConfig) {}
public updateConfig(newConfig: Partial<SafetyFilterConfig>): void {
Object.assign(this.config, newConfig);
}
/**
* Cek semua kondisi keamanan.
* @param symbolCode Kode simbol yang akan di-scan
* @param currentSpreadPips Spread saat ini dalam pips
* @param todayLossAmount Total kerugian hari ini (positif = rugi)
* @param openPositionCount Jumlah posisi terbuka untuk simbol ini
*/
public check(
symbolCode: string,
currentSpreadPips: number,
todayLossAmount: number,
openPositionCount: number
): SafetyCheckResult {
const reasons: string[] = [];
// 1. Spread check
if (currentSpreadPips > this.config.maxSpreadPips) {
reasons.push(
`Spread ${currentSpreadPips.toFixed(1)} pips melebihi batas ${this.config.maxSpreadPips} pips — pasar terlalu volatile.`
);
}
// 2. Rollover window check (berlaku untuk semua simbol forex/gold)
if (this.config.skipRolloverWindow && isRolloverWindow()) {
reasons.push(
`Market sedang dalam rollover window (23:00–01:00 UTC) — skip entry ${symbolCode}.`
);
}
// 3. Daily loss limit check (hanya aktif jika maxDailyLossAmount > 0)
if (this.config.maxDailyLossAmount > 0 && todayLossAmount >= this.config.maxDailyLossAmount) {
reasons.push(
`Daily loss ${todayLossAmount.toFixed(2)} sudah mencapai batas ${this.config.maxDailyLossAmount.toFixed(2)} — trading dihentikan hari ini.`
);
}
// 4. Open position limit untuk simbol ini (0 = tidak ada batas)
const maxPos = this.config.maxOpenPositions;
if (maxPos > 0 && openPositionCount >= maxPos) {
reasons.push(
`Sudah ada ${openPositionCount} posisi terbuka untuk ${symbolCode} (maks ${maxPos}) — skip entry baru.`
);
}
return {
safe: reasons.length === 0,
reasons
};
}
/**
* Estimasi spread saat ini dari data candle terakhir (ATR-based).
* Jika tidak ada data live, gunakan defaultSpreadPips dari symbol config.
* @param atr Average True Range dari candle terakhir
* @param pipSize Ukuran 1 pip untuk simbol ini
* @param defaultSpreadPips Spread default jika ATR tidak tersedia
*/
public static estimateSpreadPips(
atr: number,
pipSize: number,
defaultSpreadPips: number
): number {
if (atr <= 0 || pipSize <= 0) {
return defaultSpreadPips;
}
// ATR yang sangat tinggi biasanya terjadi saat spread melebar juga
// Gunakan 5% dari ATR sebagai estimasi spread dalam pips
const atrBasedEstimate = (atr * 0.05) / pipSize;
return Math.max(atrBasedEstimate, defaultSpreadPips);
}
}
/**
* Cek apakah sekarang adalah rollover window XAUUSD/Forex.
* Rollover terjadi antara 23:00–01:00 UTC setiap hari kerja.
*/
function isRolloverWindow(): boolean {
const now = new Date();
const hourUtc = now.getUTCHours();
const minuteUtc = now.getUTCMinutes();
const decimalHour = hourUtc + minuteUtc / 60;
// 23:00 UTC hingga 01:00 UTC (melewati tengah malam)
return decimalHour >= 23 || decimalHour < 1;
}