Система триггеров (Triggers)
TriggerService управляет системой триггеров, которые автоматически выполняют действия при наступлении определенных условий. Триггеры интегрированы с системой хранения и могут восстанавливаться после перезапуска стратегии.
⚠️ Важно: Способы регистрации callback'ов
Существует два способа передачи исполняемой функции в триггер:
1. Прямая передача callback (стрелочная функция)
// ✅ Правильно - стрелочная функция
trigger.addTask({
name: 'myTask',
triggerPrice: 50000,
callback: async (args) => {
log('PriceTrigger', 'Триггер сработал!', { args }, true);
}
});
2. Регистрация callback по имени (обычная функция)
// ✅ Правильно - регистрация обычной функции
class Script extends BaseScript {
async onInit() {
// Регистрируем функцию по имени
this.triggerService.registerHandler('myTask', this.onPriceTrigger, this);
// Добавляем задачу без callback
trigger.addTask({
name: 'myTask',
triggerPrice: 50000,
canReStore: true // ⚠️ ВАЖНО: только для зарегистрированных функций!
});
}
// Обычная функция (не стрелочная!)
async onPriceTrigger(args: any) {
log('PriceTrigger', 'Триггер сработал!', { args }, true);
}
}
// ❌ НЕПРАВИЛЬНО - стрелочные функции нельзя регистрировать
class Script extends BaseScript {
async onInit() {
// ❌ Ошибка! Стрелочные функции не поддерживаются для регистрации
this.triggerService.registerHandler('myTask', (args) => {
log('PriceTrigger', 'Это не сработает!', { args }, true);
}, this);
}
}
🔄 Восстановление триггеров при перезагрузке
Ключевое различие: Только зарегистрированные callback'ы могут быть восстановлены при перезагрузке скрипта!
- ✅ Зарегистрированные функции (
canReStore: true
) — восстанавливаются автоматически - ❌ Стрелочные функции — НЕ восстанавливаются при перезагрузке
Архитектура триггеров
- Централизованное управление - все триггеры управляются через
TriggerService
- Автоматическое хранение - состояние триггеров сохраняется в Storage
- Восстановление после перезапуска - триггеры автоматически восстанавливают свое состояние
- Интеграция с событиями - триггеры подписываются на соответствующие события
Типы триггеров
Order Triggers - Триггеры по ордерам
OrderTrigger выполняет действия при изменении статуса ордеров.
Основные возможности:
- Реакция на изменение статуса ордера (открыт, исполнен, отменен)
- Поддержка как
orderId
, так иclientOrderId
- Автоматическое управление жизненным циклом задач
- Интеграция с системой хранения
Примеры использования:
// Регистрация обработчика
globals.triggers.registerOrderHandler('createSlTp', this.createSlTp, this);
// Создание задачи
globals.triggers.addTaskByOrder({
name: 'createSlTp',
orderId: '12345',
status: 'closed',
canReStore: true
});
Price Triggers - Триггеры по ценам
PriceTrigger выполняет действия при достижении определенных ценовых уровней.
Основные возможности:
- Поддержка направлений срабатывания (UpToDown, DownToUp)
- Автоматическое определение направления на основе текущей цены
- Оптимизация производительности через минимальные/максимальные цены
- Поддержка множественных символов
Направления срабатывания:
UpToDown
- срабатывает когда цена падает до указанного уровняDownToUp
- срабатывает когда цена растет до указанного уровня
Автоматический выбор направления: Если направление не указано, система автоматически определяет его на основе текущей цены:
- Если текущая цена выше триггерной → выбирается
UpToDown
- Если текущая цена ниже триггерной → выбирается
DownToUp
Примеры использования:
import { PriceTriggerDirection } from 'jt-lib';
// 1. Автоматический выбор направления
globals.triggers.addTaskByPrice({
symbol: 'BTC/USDT',
name: 'autoDirection',
triggerPrice: 50000, // Направление выберется автоматически
canReStore: true
});
// 2. Явное указание направления - сработает при росте цены
globals.triggers.addTaskByPrice({
symbol: 'BTC/USDT',
name: 'sellOnRise',
triggerPrice: 55000,
direction: PriceTriggerDirection.DownToUp, // Сработает при росте
canReStore: true
});
// 3. Явное указание направления - сработает при падении цены
globals.triggers.addTaskByPrice({
symbol: 'BTC/USDT',
name: 'buyOnFall',
triggerPrice: 45000,
direction: PriceTriggerDirection.UpToDown, // Сработает при падении
canReStore: true
});
// Регистрация обработчиков
globals.triggers.registerPriceHandler('BTC/USDT', 'sellOnRise', this.onPriceRise, this);
globals.triggers.registerPriceHandler('BTC/USDT', 'buyOnFall', this.onPriceFall, this);
Time Triggers - Триггеры по времени
TimeTrigger выполняет действия в определенное время или с заданным интервалом.
Основные возможности:
- Выполнение задач в конкретное время
- Повторяющиеся задачи с заданным интервалом
- Автоматическая валидация времени срабатывания
- Предупреждения о некорректных временных параметрах
Примеры использования:
// Регистрация обработчика
globals.triggers.registerTimeHandler('dailyReport', this.generateDailyReport, this);
// Создание задачи на определенное время
globals.triggers.addTaskByTime({
name: 'dailyReport',
triggerTime: Date.now() + 24 * 60 * 60 * 1000, // завтра
canReStore: true
});
// Повторяющаяся задача
globals.triggers.addTaskByTime({
name: 'hourlyCheck',
triggerTime: Date.now() + 60 * 60 * 1000, // через час
interval: 60 * 60 * 1000, // повторять каждый час
canReStore: true
});
Система хранения триггеров
Все триггеры поддерживают автоматическое сохранение и восстановление:
canReStore: true
- триггер будет сохранен в Storage- Автоматическое восстановление - при перезапуске стратегии триггеры восстанавливают свое состояние
- Персистентность - задачи продолжают работать после перезапуска
Управление триггерами
Регистрация обработчиков
// Регистрация обработчиков для всех типов триггеров
globals.triggers.registerOrderHandler('taskName', this.handler, this);
globals.triggers.registerPriceHandler('BTC/USDT', 'taskName', this.handler, this);
globals.triggers.registerTimeHandler('taskName', this.handler, this);
Создание задач
// Создание задач для каждого типа триггера
const orderTaskId = globals.triggers.addTaskByOrder({...});
const priceTaskId = globals.triggers.addTaskByPrice({...});
const timeTaskId = globals.triggers.addTaskByTime({...});
Отмена задач
// Отмена конкретной задачи
globals.triggers.cancelOrderTask(orderTaskId);
globals.triggers.cancelPriceTask(priceTaskId);
globals.triggers.cancelTimeTask(timeTaskId);
Практические примеры
Автоматическое управление Stop Loss и Take Profit
class Script extends BaseScript {
async onInit() {
// Регистрируем обработчик для создания SL/TP
globals.triggers.registerOrderHandler('createSlTp', this.createSlTp, this);
this.basket = new OrdersBasket({
symbol: this.symbols[0],
connectionName: this.connectionName
});
await this.basket.init();
}
async onTick() {
const price = this.basket.close();
// Создаем ордер с автоматическим SL/TP
const order = await this.basket.buyMarket(
this.basket.getContractsAmount(100, price),
0, // SL будет установлен автоматически
0 // TP будет установлен автоматически
);
// Создаем задачу для автоматического создания SL/TP
globals.triggers.addTaskByOrder({
name: 'createSlTp',
orderId: order.id,
status: 'closed', // выполнится когда ордер исполнится
canReStore: true
});
}
async createSlTp(orderId: string) {
// Получаем информацию об ордере
const order = await this.basket.getOrder(orderId);
if (order && order.status === 'closed') {
const currentPrice = this.basket.close();
const slPrice = currentPrice * 0.95; // SL на 5% ниже
const tpPrice = currentPrice * 1.1; // TP на 10% выше
// Создаем SL и TP ордера
await this.basket.createStopLossOrder('buy', order.amount, slPrice);
await this.basket.createTakeProfitOrder('buy', order.amount, tpPrice);
}
}
}
Ценовые триггеры для входа в позицию
class Script extends BaseScript {
async onInit() {
// Регистрируем обработчик для входа в позицию
globals.triggers.registerPriceHandler('BTC/USDT', 'enterLong', this.enterLong, this);
globals.triggers.registerPriceHandler('BTC/USDT', 'enterShort', this.enterShort, this);
this.basket = new OrdersBasket({
symbol: this.symbols[0],
connectionName: this.connectionName
});
await this.basket.init();
const currentPrice = this.basket.close();
// Создаем триггеры для входа в позицию
globals.triggers.addTaskByPrice({
symbol: 'BTC/USDT',
name: 'enterLong',
triggerPrice: currentPrice * 0.98, // вход на 2% ниже текущей цены
direction: 'DownToUp',
canReStore: true
});
globals.triggers.addTaskByPrice({
symbol: 'BTC/USDT',
name: 'enterShort',
triggerPrice: currentPrice * 1.02, // вход на 2% выше текущей цены
direction: 'UpToDown',
canReStore: true
});
}
async enterLong() {
const price = this.basket.close();
const contracts = this.basket.getContractsAmount(100, price);
await this.basket.buyMarket(contracts, price * 0.95, price * 1.1);
log('PositionManager', 'Long position opened', { price, contracts }, true);
}
async enterShort() {
const price = this.basket.close();
const contracts = this.basket.getContractsAmount(100, price);
await this.basket.sellMarket(contracts, price * 1.05, price * 0.9);
log('PositionManager', 'Short position opened', { price, contracts }, true);
}
}
Временные триггеры для отчетов
class Script extends BaseScript {
async onInit() {
// Регистрируем обработчик для ежедневных отчетов
globals.triggers.registerTimeHandler('dailyReport', this.generateDailyReport, this);
globals.triggers.registerTimeHandler('hourlyCheck', this.hourlyCheck, this);
this.basket = new OrdersBasket({
symbol: this.symbols[0],
connectionName: this.connectionName
});
await this.basket.init();
// Создаем ежедневный отчет на 00:00
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(0, 0, 0, 0);
globals.triggers.addTaskByTime({
name: 'dailyReport',
triggerTime: tomorrow.getTime(),
interval: 24 * 60 * 60 * 1000, // повторять каждый день
canReStore: true
});
// Создаем проверку каждый час
globals.triggers.addTaskByTime({
name: 'hourlyCheck',
triggerTime: Date.now() + 60 * 60 * 1000, // через час
interval: 60 * 60 * 1000, // повторять каждый час
canReStore: true
});
}
async generateDailyReport() {
const positions = await this.basket.getPositions();
const orders = await this.basket.getOrders();
log('DailyReport', '=== Daily Report ===', {}, true);
log('DailyReport', 'Positions count', { count: positions.length }, true);
log('DailyReport', 'Orders count', { count: orders.length }, true);
log('DailyReport', 'Current P&L', { pnl: positions.reduce((sum, pos) => sum + pos.unrealizedPnl, 0) }, true);
}
async hourlyCheck() {
const price = this.basket.close();
log('HourlyCheck', 'Hourly check - Current price', { price }, true);
// Проверяем условия для торговли
if (price > 50000) {
log('HourlyCheck', 'Price above 50k - consider taking profit', { price }, true);
}
}
}
Интеграция с другими компонентами
TriggerService + EventEmitter
- TriggerService подписывается на события через EventEmitter
- Автоматическая синхронизация жизненных циклов
- Полная типизация всех обработчиков
Storage + Triggers
- Триггеры автоматически сохраняют свое состояние
- При перезапуске стратегии состояние восстанавливается
- Обеспечивается непрерывность работы автоматизации
BaseScript + Triggers
- Стратегия регистрирует обработчики триггеров
- Автоматическое управление подписками при уничтожении
- Интеграция с системой событий для автоматизации
Следующие шаги
- Система событий - EventEmitter для управления событиями
- Торговые скрипты - Базовый класс для торговых скриптов
- Работа с биржей - OrdersBasket для торговых операций