В backtest-kit модуль volume-anomaly используется как источник в графе сигналов - параллельно с GARCH. Если GARCH отвечает на вопрос «достаточно ли ожидаемое движение», то volume-anomaly отвечает на вопрос «является ли прямо сейчас статистически необычным моментом в микроструктуре рынка».
Пример кода
import { sourceNode, outputNode } from '@backtest-kit/graph';
import { predict } from 'volume-anomaly';
import { getCandles } from 'backtest-kit';
const ANOMALY_CONFIDENCE = 0.75;
const N_TRAIN = 1200; // обучающее окно — должно быть без аномалий
const N_DETECT = 200; // окно детекции
const reversalSource = sourceNode(
async (symbol) => {
// Важно: recent не должен пересекаться с historical
const all = await getAggregatedTrades(symbol, N_TRAIN + N_DETECT);
const historical = all.slice(0, N_TRAIN); // старые сделки — baseline
const recent = all.slice(N_TRAIN); // новые — без overlap
return predict(historical, recent, ANOMALY_CONFIDENCE);
// {
// anomaly: true,
// confidence: 0.81,
// direction: 'long' | 'short' | 'neutral',
// imbalance: 0.61,
// }
},
);
const entrySignal = outputNode(
async ([reversal, ...]) => {
if (!reversal.anomaly) return null;
if (reversal.direction === 'neutral') return null;
const position = reversal.direction; // 'long' | 'short'
return {
id: randomString(),
position,
priceTakeProfit: ...
priceStopLoss: ...
minuteEstimatedTime: 60,
};
},
reversalSource,
...
);
Ключевые детали
Hawkes Process - кластеризация ордеров
CUSUM- сдвиг buy/sell дисбаланса относительно исторической нормы
BOCPD- смена режима: момент когда распределение дисбаланса само меняется
Как использовать
Классическая проблема DCA - ты усредняешься в падающий нож. Цена идёт против, ты докупаешь, а она продолжает падать. volume-anomaly заточен именно под это: докупать не по расписанию или по сетке уровней, а только когда ордерфлоу показывает разворот агрессии.