Cómo escribir ingresos pasivos: escribir un bot de comercio de calidad en JS (parte 1)

Comencemos a escribir un robot comercial que se ejecutará en el intercambio de cifrado Binance. El bot debería poder:





  1. comercia por tu cuenta, generando algún tipo de ingresos





  2. Debería ser conveniente para crear e implementar diversas estrategias comerciales





  3. probar la estrategia en datos históricos





Empecemos por la arquitectura

Tenemos un intercambio de Binance que tiene una hermosa API. Por lo tanto, la arquitectura podría verse así:





Llame a un par de métodos "comprar más barato" y "vender más caro". Pero la tarea para nosotros es escribir un bot en el que un programador-comerciante condicional pueda crear y probar nuevas estrategias de rentabilidad. Por lo tanto, es necesario separar la lógica comercial de todo lo demás. Y también al módulo lógico no debería importarle a qué intercambio estaba conectado: a una API real oa una pseudo-API (para pruebas). Teniendo todo esto en cuenta, obtuvimos algo como esta arquitectura:





La base fue elegida por PostgreSQL. No hay ninguna intención secreta aquí. Puede utilizar cualquiera. 





Debido al hecho de que cada módulo merece su atención, todo esto no cabe en un artículo. Por lo tanto, estoy comenzando una miniserie: "Escribiendo un bot comercial de alta calidad en JS". Por lo tanto, suscríbase, póngase cómodo, comencemos





Servicio para troncos

, 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







Continuará.





En el próximo artículo, escribiremos un núcleo que conectará todas estas partes y hará que el bot se comercialice.








All Articles