Как написать пассивный доход: Пишем качественного трейд бота на JS (часть 1)

от автора

Начнем писать трейдинг бота, который будет работать на криптобирже Binance. Бот должен уметь:

  1. торговать самостоятельно, принося какой-то доход

  2. должен быть удобен для создания и обкатывания различных стратегий торговли

  3. тестировать стратегию на исторических данных

Пожалуй, начнем с архитектуры

У нас есть биржа Binance, у которой есть шикарное api. Поэтому архитектура могла бы выглядеть так:

Вызвать пару методов “купи дешевле” и “продай дороже”. Но задача для нас написать такого бота, при котором условный программист-трейдер сможет создавать и тестировать на прибыльность новые стратегии. Поэтому, необходимо отделить логику торговли от всего прочего. А также модулю логики должно быть все равно к какой бирже его подключили: к реальному API или к псевдо-API (для тестирования). С учетом всего этого получилась примерно вот такая архитектура:

Базу выбрал PostgreSQL. Тут нет никакого тайного умысла. Вы можете использовать любую. 

В связи с тем, что каждый модуль стоит внимания, это все не поместится в одну статью. Поэтому я начинаю мини-сериал: «Пишем качественного трейд бота на JS». Поэтому подписывайтесь, устраивайтесь поудобней — начинаем

Сервис для логов

Простой класс, который принимает на вход префикс для логирования и имеет два метода log и error. Эти методы печатают лог с текущим временем и перфиксом:

class LoggerService {   constructor(prefix) {     this.logPrefix = prefix   }    log(...props) {     console.log(new Date().toISOString().substr(0, 19), this.logPrefix, ...props)   }    error(...props) {     console.error(new Date().toISOString().substr(0, 19), this.logPrefix, ...props)   } }

Теперь подключим биржу

yarn add node-binance-api

Добавим класс BaseApiService. Сделаем в нем инициализацию Binance SDK, а также применим сервис LoggerService. Учитывая мой опыт с Binance могу сразу сказать, что в зависимости от торговой пары мы должны слать цену и обьем с разным количеством знаков после запятой. Все эти настройки для каждой пары можно взять, сделав запрос futuresExchangeInfo(). И написать методы для получения количества знаков после запятой для цены getAssetPricePrecision и объема getAssetQuantityPrecision.

class BaseApiService {   constructor({ client, secret }) {     const { log, error } = new Logger('BaseApiService')     this.log = log     this.error = error      this.api = new NodeBinanceApi().options({       APIKEY: client,       APISECRET: secret,       hedgeMode: true,     })     this.exchangeInfo = {}   }    async init() {     try {       this.exchangeInfo = await this.api.futuresExchangeInfo()     } catch (e) {       this.error('init error', e)     }   }    getAssetQuantityPrecision(symbol) {     const { symbols = [] } = this.exchangeInfo     const s = symbols.find(s => s.symbol === symbol) || { quantityPrecision: 3 }     return s.quantityPrecision   }    getAssetPricePrecision(symbol) {     const { symbols = [] } = this.exchangeInfo     const s = symbols.find(s => s.symbol === symbol) || { pricePrecision: 2 }     return s.pricePrecision   } }

Дальше добавляем метод создания ордера, с учетом правильного количества знаков после запятой для цены и обьема:

async futuresOrder(side, symbol, qty, price, params={}) {   try {     qty = Number(qty).toFixed(this.getAssetQuantityPrecision(symbol))     price = Number(price).toFixed(this.getAssetPricePrecision(symbol))     if (!params.type) {       params.type = ORDER.TYPE.MARKET     }     const res = await this.api.futuresOrder(side, symbol, qty, price || false, params)     this.log('futuresOrder', res)     return res   } catch (e) {     console.log('futuresOrder error', e)   } }

Теперь бот умеет создавать ордера. Научим его слушать события из биржы для того, чтоб он мог отлавливать изменения статуса ордеров. Для этого создадим класс TradeService.

class TradeService {   constructor({client, secret}) {     const { log, error } = new LoggerService('TradeService')     this.log = log     this.error = error     this.api = new NodeBinanceApi().options({       APIKEY: client,       APISECRET: secret,       hedgeMode: true,     })     this.events = new EventEmitter()   }    marginCallCallback = (data) => this.log('marginCallCallback', data)    accountUpdateCallback = (data) => this.log('accountUpdateCallback', data)    orderUpdateCallback = (data) => this.emit(data)    subscribedCallback = (data) => this.log('subscribedCallback', data)    accountConfigUpdateCallback = (data) => this.log('accountConfigUpdateCallback', data)    startListening() {     this.api.websockets.userFutureData(       this.marginCallCallback,       this.accountUpdateCallback,       this.orderUpdateCallback,       this.subscribedCallback,       this.accountConfigUpdateCallback,     )   }    subscribe(cb) {     this.events.on('trade', cb)   }    emit = (data) => {     this.events.emit('trade', data)   } }

При помощи метода из SDK this.api.websockets.userFutureData подписываемся на события из биржы. Самой главный колбек для нас this.orderUpdateCallback . Он вызывается каждый раз когда меняется статус у ордера. Ловим это событие и прокидываем через EventEmitter тому, кто на это событие подписался, используя метод subscribe.

Перейдем к базе данных

Для чего она нужна? В базе будем хранить все ордера, а также всю историю торговли бота. Пользователей с их ключами к бирже и балансами. В последствии сможем считать сколько бот принес прибыли/убытка. Тут останавливаться долго не буду. Подключаю sequlize. 

yarn add sequelize-cli -D yarn add sequelize npx sequelize-cli init

Добавим docker-compose.yml файл для локальной базы:

version: '3.1'  services:   db:     image: 'postgres:12'     restart: unless-stopped     volumes:       - ./volumes/postgresql/data:/var/lib/postgresql/data     environment:       POSTGRES_USER: root       POSTGRES_PASSWORD: example       POSTGRES_DB: bot     ports:       - 5432:5432     networks:       - postgres   networks:   postgres:     driver: bridge

А также добавляю миграции и модели. User, Order

Продолжение следует.

В следующей статье будем писать ядро, которое соединит все эти части и заставит бота торговать.

ссылка на оригинал статьи https://habr.com/ru/post/562496/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *