@bcswaper/apis
Version:
bcswaper apis
1 lines • 8.33 kB
JavaScript
import hmacSHA256 from"crypto-js/hmac-sha256";import Base64 from"crypto-js/enc-base64";import{conerr,conlog}from"../utils";import{OrderSide,OrderType,PositionSide}from"../context";import{convertResolution,OkxConstants,ChannelType,EventType,InstType,OpType}from"./okxContext";import{AccountHandler}from"./accountHandler";import{OrderbookHandler}from"./orderbookHandler";import{CandlesHandler}from"./candlesHandler";import{OkxSocketClient}from"./okxWebSocket";import{TickerHandler}from"./tickerHandler";import{OkxHttpClient}from"./okHttpClient";import{AbstractClient}from"../clients";export class OkxClient extends AbstractClient{constructor(e){super(e),this.publicEndpoint=OkxConstants.DEMO_PUBLIC_ENDPOINT,this.privateEndpoint=OkxConstants.DEMO_PRIVATE_ENDPOINT,this.businessEndpoint=OkxConstants.DEMO_BUSINESS_ENDPOINT,this.okxAccInfo=e.account,this.okxAccInfo.formally&&(this.publicEndpoint=OkxConstants.PUBLIC_ENDPOINT,this.privateEndpoint=OkxConstants.PRIVATE_ENDPOINT,this.businessEndpoint=OkxConstants.BUSINESS_ENDPOINT)}async initialize(){conlog("initializing ..."),this.accountHandler=new AccountHandler(this.symbol,this.clientCallBackFuncs),this.tickerHandler=new TickerHandler(this.symbol,this.clientCallBackFuncs),this.candlesHandler=new CandlesHandler(this.symbol,this.clientCallBackFuncs),this.orderbookHandler=new OrderbookHandler(this.symbol),this.okxHttpClient=new OkxHttpClient(this.okxAccInfo),this.initialCandle(),this.initialInstInfo(),this.createSocketClient(),super.initialize(),conlog("done.")}async initialCandle(){this.clientCandleMap?.forEach((async(e,t)=>{conlog("initial candle: ",t);const n=convertResolution(t);if(!n)return void conerr("error initial resolution: ",t);const s=n.replace("candle",""),i=await this.okxHttpClient.getCandles(this.symbol,s);this.candlesHandler.initialize(n,i)}))}async initialInstInfo(){conlog("initial instrument info: ",this.symbol);const e=await this.okxHttpClient.getInstInfo(InstType.SWAP,this.symbol);0!==e?.length&&(this.instInfo=e[0])}createSocketClient(){conlog("clientCallBackFuncs ...",this.clientCallBackFuncs),this.privateWss=new OkxSocketClient(this.privateEndpoint,(()=>{conlog(`socket ${this.privateEndpoint} opened.`),conlog("login ..."),this.login()}),(async e=>{await this.handleMessage(e)}),(()=>{conlog(`socket ${this.privateEndpoint} closed.`),this.running&&(conlog(`reconnecting ${this.privateEndpoint} ...`),setTimeout((()=>{this.privateWss?.connect()}),1e3))}),(e=>{conerr(`socket ${this.privateEndpoint} error:`,e),this.running&&(conlog(`reconnecting ${this.privateEndpoint} ...`),setTimeout((()=>{this.privateWss?.connect()}),1e3))})),this.privateWss.connect(),this.publicWss=new OkxSocketClient(this.publicEndpoint,(()=>{if(conlog(`socket ${this.publicEndpoint} opened.`),this.clientCallBackFuncs.tickFunc){conlog("subscribed ticker ...");const e={channel:"tickers",instId:this.symbol};this.publicWss?.send({op:"subscribe",args:[e]})}}),(async e=>{await this.handleMessage(e)}),(()=>{conlog(`socket ${this.publicEndpoint} closed.`),this.running&&(conlog(`reconnecting ${this.publicEndpoint} ...`),setTimeout((()=>{this.publicWss?.connect()}),1e3))}),(e=>{conerr(`socket ${this.publicEndpoint} error:`,e),this.running&&(conlog(`reconnecting ${this.publicEndpoint} ...`),setTimeout((()=>{this.publicWss?.connect()}),1e3))})),this.publicWss.connect(),this.businessWss=new OkxSocketClient(this.businessEndpoint,(()=>{conlog(`socket ${this.businessEndpoint} opened.`),this.clientCallBackFuncs.candleFunc&&(conlog("clientCandleMap ...",this.clientCandleMap),this.clientCandleMap?.forEach(((e,t)=>{conlog("subscribed candle: ",t);const n=convertResolution(t);if(!n)return void conlog("error subscribing resolution: ",t);const s={channel:n,instId:this.symbol};this.businessWss?.send({op:"subscribe",args:[s]})})))}),(async e=>{await this.handleMessage(e)}),(()=>{conlog(`socket ${this.businessEndpoint} closed.`),this.running&&(conlog(`reconnecting ${this.businessEndpoint} ...`),setTimeout((()=>{this.businessWss?.connect()}),1e3))}),(e=>{conerr(`socket ${this.businessEndpoint} error:`,e),this.running&&(conlog(`reconnecting ${this.businessEndpoint} ...`),setTimeout((()=>{this.businessWss?.connect()}),1e3))})),this.businessWss.connect()}async login(){const e=(""+Date.now()).slice(0,-3),t=`${e}GET/users/self/verify`,n=Base64.stringify(hmacSHA256(t,this.okxAccInfo.secretKey??"")),s={op:"login",args:[{apiKey:this.okxAccInfo.apiKey??"",passphrase:this.okxAccInfo.passphrase??"",timestamp:e,sign:n}]};this.privateWss.send(s)}async handleMessage(e){if("string"!=typeof e.data)return void conerr("error message received: ",e);const t=e.data;try{const e=JSON.parse(t),n=e?.event,s=e?.code;let i=!1;if("string"==typeof s?i="0"!==s:n===EventType.Error&&(i=!0),i)return void conerr("error message received: ",t);if(n){if(conlog("event message received: ",t),n===EventType.Login){conlog("subscribe to okx account/positions ...");const e={channel:"account",ccy:this.ccy,extraParams:'{\n "updateInterval": "0"\n }'},t={channel:"positions",instType:"ANY",instId:this.symbol,extraParams:'{\n "updateInterval": "0"\n }'};this.privateWss.send({op:"subscribe",args:[e,t]}),this.clientCallBackFuncs.orderBookFunc&&conlog("orderbook not supported yet ..."),this.clientCallBackFuncs.tradesFunc&&conlog("trade not supported yet ... ")}return}const o=e.data,c=e.op;if(c){switch(conlog("op message received: ",o),c){case OpType.Order:case OpType.Cancel:}return}const r=e.arg.channel,a=e.arg.uid;if(r.startsWith(ChannelType.Candle))return void await this.candlesHandler.resolve(r,o);switch(r){case ChannelType.Account:this.accountHandler.resolveAccount(a,o);break;case ChannelType.Fills:this.accountHandler.resolveFill(a,o);break;case ChannelType.Orders:this.accountHandler.resolveOrder(a,o);break;case ChannelType.Positions:this.accountHandler.resolvePostitions(a,o);break;case ChannelType.Tickers:await this.tickerHandler.resolve(o);break;default:conerr("unexpected message received: ",o)}}catch(e){conerr("error resolving websocket message:",e)}}getPrice(){return this.tickerHandler.getPrice()||0}getOrderbook(){return this.orderbookHandler.getOrderbook()}getAccountInfo(){return this.accountHandler.getAccountInfo()}getCandles(e){const t=convertResolution(e);return this.candlesHandler.getCandles(t)}async placeOrder(e,t,n,s,i,o,c,r="cross"){conlog(`place order ****** clientId: ${e}, side : ${t}, posSide:${n}, orderType:${s}, price: ${i}, size: ${o}`);const a=this.calculateSize(o);if(0===a)return void conerr(`calculated sz = ${a}, ctVal=${this.instInfo.ctVal}/${this.instInfo.ctValCcy}, reject this order request.`);const l={clOrdId:e.toString(),side:t.toLowerCase(),posSide:n.toLowerCase(),instId:this.symbol,tdMode:r,ordType:s.toLowerCase(),px:i,sz:a,reduceOnly:c},d={id:e,op:OpType.Order,args:[l]};this.privateWss.send(d)}async placeMarketOrder(e,t,n,s,i){this.placeOrder(e,t,n,OrderType.MARKET,0,s,i)}async buy(e,t,n){this.placeMarketOrder(e,OrderSide.BUY,PositionSide.LONG,n,!1)}async sell(e,t,n){this.placeMarketOrder(e,OrderSide.SELL,PositionSide.SHORT,n,!1)}async close(e,t,n){conlog("close order ... ");const s=t==OrderSide.BUY?PositionSide.SHORT:PositionSide.LONG;await this.placeMarketOrder(e,t,s,n,!0)}async placeLimitOrder(e,t,n,s,i,o){this.placeOrder(e,t,n,OrderType.LIMIT,s,i,o)}async buyLimit(e,t,n){this.placeLimitOrder(e,OrderSide.BUY,PositionSide.LONG,t,n,!1)}async sellLimit(e,t,n){this.placeLimitOrder(e,OrderSide.SELL,PositionSide.SHORT,t,n,!1)}async closeLimit(e,t,n,s){conlog("close order limit ... ");const i=t==OrderSide.BUY?PositionSide.SHORT:PositionSide.LONG;await this.placeLimitOrder(e,t,i,n,s,!0)}async cancel(e){conlog(`cancel order ****** id : ${e.id}, clientId: ${e.clientId}, type: ${e.type} `);const t={instId:e.ticker,ordId:e.id},n={id:Date.now().toString(),op:OpType.Cancel,args:[t]};this.privateWss.send(n)}calculateSize(e){const t=this.instInfo?.instType;if([InstType.SPOT,InstType.MARGIN].includes(t))return e;const n=this.instInfo?.ctVal||0,s=this.instInfo?.lotSz||0;if(0===n||0===s)return 0;const i=Math.trunc(e/s)*s;return 0===i?n:i}destroy(){conlog("okx client release ..."),this.publicWss?.close(),this.privateWss?.close(),this.businessWss?.close(),super.destroy(),conlog("okx client released.")}}