Comment écrire des revenus passifs : écrire un bot commercial de qualité en JS (partie 1)

Commençons à écrire un bot de trading qui s'exécutera sur l'échange crypto de Binance. Le bot doit être capable de :





  1. commercez par vous-même, générant une sorte de revenu





  2. devrait être pratique pour créer et déployer diverses stratégies de trading





  3. tester la stratégie sur des données historiques





Commençons par l'architecture

Nous avons un échange Binance qui a une API géniale. Par conséquent, l'architecture pourrait ressembler à ceci :





Appelez quelques méthodes "acheter moins cher" et "vendre plus cher". Mais la tâche pour nous est d'écrire un bot dans lequel un programmeur-trader conditionnel peut créer et tester de nouvelles stratégies de rentabilité. Par conséquent, il est nécessaire de séparer la logique commerciale de tout le reste. De plus, le module logique ne devrait pas se soucier de l'échange auquel il était connecté : à une API réelle ou à une pseudo-API (pour les tests). En tenant compte de tout cela, nous avons obtenu quelque chose comme cette architecture :





La base a été choisie par PostgreSQL. Il n'y a pas d'intention secrète ici. Vous pouvez utiliser n'importe lequel. 





Du fait que chaque module mérite votre attention, tout cela ne rentrera pas dans un seul article. Par conséquent, je lance une mini-série : « Écrire un bot commercial de haute qualité en JS ». Par conséquent, abonnez-vous, installez-vous confortablement - commençons





Service pour les journaux

, 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







À suivre.





Dans le prochain article, nous écrirons un noyau qui reliera toutes ces pièces et fera le commerce du bot.








All Articles