Crypto · 10/29/2025
Exécuteur (boucle principale)

Espace publicitaire (in-article 1)
// src/executor.ts
import 'dotenv/config'
import pino from 'pino'
import crypto from 'node:crypto'
import { CcxtAdapter } from './broker/ccxt-adapter'
import { generateSignal, Candle } from './strategy'
import { positionSize, withinExposure } from './risk'
import { appendOrder } from './journal'
const log = pino({ level: 'info' })
function cid(prefix='ORD'): string {
return `${prefix}-${Date.now()}-${crypto.randomBytes(3).toString('hex')}`
}
async function main() {
const exId = process.env.EXCHANGE || 'binanceusdm'
const symbol = process.env.SYMBOL || 'BTC/USDT'
const timeframe = process.env.TIMEFRAME || '1h'
const fast = Number(process.env.SMA_FAST || 20)
const slow = Number(process.env.SMA_SLOW || 50)
const riskPct = Number(process.env.RISK_MAX_PCT || 0.01)
const maxExposure = Number(process.env.MAX_EXPOSURE_PCT || 0.2)
const ex = new CcxtAdapter({
id: exId,
apiKey: process.env.EXCHANGE_API_KEY || '',
secret: process.env.EXCHANGE_API_SECRET || '',
testnet: String(process.env.EXCHANGE_TESTNET).toLowerCase() === 'true',
})
const raw = await ex.fetchOHLCV(symbol, timeframe, 400)
const candles: Candle[] = raw.map(([t,o,h,l,c,v]) => ({ time:t, open:o, high:h, low:l, close:c, volume:v }))
const signal = generateSignal(candles, fast, slow)
log.info({ symbol, signal }, 'Signal')
if (signal === 'HOLD') return
// Exposition et taille
const balance = await ex.balanceUSDT()
const last = candles[candles.length-1].close
const size = positionSize(balance, last, riskPct)
const exposure = (size * last) / Math.max(balance, 1)
if (!withinExposure(exposure, maxExposure)) {
return log.warn({ exposure, maxExposure }, 'Exposition trop élevée, pas d’ordre')
}
const side = signal === 'BUY' ? 'buy' : 'sell'
const orderId = cid()
const order = await ex.createOrder({ symbol, side, type:'market', amount: size, clientOrderId: orderId })
appendOrder([new Date().toISOString(), symbol, side, size, last, order.id, order.status])
log.info({ order }, 'Ordre envoyé')
}
main().catch(err => {
// journal d’erreur
console.error(err)
process.exit(1)
})
Espace publicitaire (in-article 2)



