Comencemos a escribir un robot comercial que se ejecutará en el intercambio de cifrado Binance. El bot debería poder:
comercia por tu cuenta, generando algún tipo de ingresos
Debería ser conveniente para crear e implementar diversas estrategias comerciales
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.