diff --git a/index.ts b/index.ts index 09734d3..bd43888 100644 --- a/index.ts +++ b/index.ts @@ -46,5 +46,6 @@ export { Ticker } from "./src/models/ticker/Ticker"; export { TickerStatistics } from "./src/models/ticker/TickerStatistics"; export { Trade } from "./src/models/trade/Trade"; export { TradeUpdate } from "./src/models/websocket/trade/TradeUpdate"; +export { RawTradeUpdate } from "./src/models/websocket/trade/RawTradeUpdate"; export { BinanceApiClient } from "./src/BinanceApiClient"; \ No newline at end of file diff --git a/src/BinanceApiClient.ts b/src/BinanceApiClient.ts index 23adfde..70dd0cc 100644 --- a/src/BinanceApiClient.ts +++ b/src/BinanceApiClient.ts @@ -26,6 +26,7 @@ import * as WebSocket from "ws"; import { OrderBookUpdate } from "./models/websocket/depth/OrderBookUpdate"; import { CandlestickUpdate } from "./models/websocket/candlestick/CandlestickUpdate"; import { TradeUpdate } from "./models/websocket/trade/TradeUpdate"; +import { RawTradeUpdate } from "./models/websocket/trade/RawTradeUpdate"; import { AccountUpdate } from "./models/websocket/account/AccountUpdate"; import { OrderUpdate } from "./models/websocket/order/OrderUpdate"; import { ExchangeInfo } from "./models/misc/ExchangeInfo"; @@ -39,6 +40,7 @@ import { HeartbeatHandler } from "websocket-heartbeats"; */ export class BinanceApiClient { + private static readonly COMBINED_WS_BASE_URL: string = "wss://stream.binance.com:9443/stream?streams="; private static readonly WS_BASE_URL: string = "wss://stream.binance.com:9443/ws/"; private static readonly DEFAULT_WS_TIMEOUT: number = 60000; @@ -257,6 +259,9 @@ export class BinanceApiClient { icebergQuantity?: number, responseType?: ResponseType ): Promise< OrderAcknowledgement | OrderResult | OrderFull > { + + const priceAsString: string = price.toFixed(8); + console.log("BinanceApiClient says the price is" + priceAsString); let jsonResponse: any = await this.makeRequest( HttpMethod.POST, ApiVersion.V3, @@ -272,7 +277,7 @@ export class BinanceApiClient { [ "quantity", quantity ], [ "price", - type === OrderType.MARKET || type === OrderType.STOP_LOSS ? null : price + (type === OrderType.MARKET || type === OrderType.STOP_LOSS) ? null : priceAsString ], [ "newClientOrderId", clientOrderId ], [ "stopPrice", stopPrice ], @@ -587,6 +592,49 @@ export class BinanceApiClient { } + /** + * Initializes a web socket data stream that gives us information about a combined + * stream of an array of symbol's order book updates. + * + * @param symbols The symbols of which we want to get the order book updates. + * @param onUpdate A function to be called when a new update is received. + * @param connectionTimeout Timeout based on which the web socket connection is + * considered to be broken based on a heartbeat monitor. + * @param onLostConnection A callback to be invoked when the web socket connection + * is detected as broken. + */ + public monitorOrderBookCombined( + symbol: string[], + onUpdate: ( update: OrderBookUpdate ) => any, + connectionTimeout: number, + onLostConnection: () => any ): void { + + let url: string = ""; + url = BinanceApiClient.COMBINED_WS_BASE_URL; + for ( let s of symbol ) { + url += s.toLowerCase() + "@depth" + "/"; + } + // Trim the final slash + url.slice( 0, -1 ); + const websocket: WebSocket = new WebSocket( + url, + { perMessageDeflate: false } + ); + + new HeartbeatHandler( + websocket, + isNullOrUndefined( connectionTimeout ) ? BinanceApiClient.DEFAULT_WS_TIMEOUT : connectionTimeout, + onLostConnection + ).handle(); + + websocket.on( "message", ( data: any ) => { + // For a combined stream the data is wrapped in an object with the + // streamname and the raw data. + const rawData = JSON.parse( data ); + onUpdate( new OrderBookUpdate( rawData.data ) ); + } ); + + } /** * Initializes a web socket data stream that gives us information about a * single symbol's order book updates. Stream keepalive is performed through @@ -625,6 +673,50 @@ export class BinanceApiClient { /** * Initializes a web socket data stream that gives us information about + * Kline/candlestick updates for a number of symbols on the one socket. + * + * @param symbols The symbols of which we want to get the candlestick updates. + * @param interval The interval to which the requested candlestick updates + * refer to. + * @param onUpdate A function to be called when a new update is received. + * @param connectionTimeout Timeout based on which the web socket connection is + * considered to be broken based on a heartbeat monitor. + * @param onLostConnection A callback to be invoked when the web socket connection + * is detected as broken. + */ + public async monitorCandlesticksCombined( + symbols: string[], + interval: CandlestickInterval, + onUpdate: ( update: CandlestickUpdate ) => any, + connectionTimeout?: number, + onLostConnection?: () => any ): Promise< void > { + + let url: string = ""; + url = BinanceApiClient.COMBINED_WS_BASE_URL; + for ( let s of symbols ) { + url += s.toLowerCase() + "@kline_" + interval + "/"; + } + // Trim the final slash + url.slice( 0, -1 ); + const websocket: WebSocket = new WebSocket( + url, + { perMessageDeflate: false } + ); + + new HeartbeatHandler( + websocket, + isNullOrUndefined( connectionTimeout ) ? BinanceApiClient.DEFAULT_WS_TIMEOUT : connectionTimeout, + onLostConnection + ).handle(); + + websocket.on( "message", ( data: any ) => { + const rawData = JSON.parse( data ); + onUpdate( new CandlestickUpdate( rawData.data ) ); + } ); + + } + + /** * Kline/candlestick updates. Stream keepalive is performed through * [[keepAliveUserStream]] following the rules described * [here](https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md) @@ -662,6 +754,89 @@ export class BinanceApiClient { } + + /** + * Initializes a web socket data stream that gives us information about + * trade updates for a number of symbols on the one socket. + * + * @param symbols The symbols of which we want to get the trade updates. + * @param onUpdate A function to be called when a new update is received. + * @param connectionTimeout Timeout based on which the web socket connection is + * considered to be broken based on a heartbeat monitor. + * @param onLostConnection A callback to be invoked when the web socket connection + * is detected as broken. + */ + public monitorRawTradesCombined( + symbols: string[], + onUpdate: ( update: RawTradeUpdate ) => any, + connectionTimeout: number, + onLostConnection: () => any ): void { + let url: string = ""; + url = BinanceApiClient.COMBINED_WS_BASE_URL; + for ( let s of symbols ) { + url += s.toLowerCase() + "@trade" + "/"; + } + // Trim the final slash + url.slice( 0, -1 ); + const websocket: WebSocket = new WebSocket( + url, + { perMessageDeflate: false } + ); + + new HeartbeatHandler( + websocket, + isNullOrUndefined( connectionTimeout ) ? BinanceApiClient.DEFAULT_WS_TIMEOUT : connectionTimeout, + onLostConnection + ).handle(); + + websocket.on( "message", ( data: any ) => { + const rawData = JSON.parse( data ); + onUpdate( new RawTradeUpdate( rawData.data ) ); + } ); + + } + + /** + * Initializes a web socket data stream that gives us information about + * trade updates for a number of symbols on the one socket. + * + * @param symbols The symbols of which we want to get the trade updates. + * @param onUpdate A function to be called when a new update is received. + * @param connectionTimeout Timeout based on which the web socket connection is + * considered to be broken based on a heartbeat monitor. + * @param onLostConnection A callback to be invoked when the web socket connection + * is detected as broken. + */ + public monitorTradesCombined( + symbols: string[], + onUpdate: ( update: TradeUpdate ) => any, + connectionTimeout: number, + onLostConnection: () => any ): void { + let url: string = ""; + url = BinanceApiClient.COMBINED_WS_BASE_URL; + for ( let s of symbols ) { + url += s.toLowerCase() + "@aggTrade" + "/"; + } + // Trim the final slash + url.slice( 0, -1 ); + const websocket: WebSocket = new WebSocket( + url, + { perMessageDeflate: false } + ); + + new HeartbeatHandler( + websocket, + isNullOrUndefined( connectionTimeout ) ? BinanceApiClient.DEFAULT_WS_TIMEOUT : connectionTimeout, + onLostConnection + ).handle(); + + websocket.on( "message", ( data: any ) => { + const rawData = JSON.parse( data ); + onUpdate( new TradeUpdate( rawData.data ) ); + } ); + + } + /** * Initializes a web socket data stream that gives us information about * trade updates. Stream keepalive is performed through @@ -790,7 +965,7 @@ export class BinanceApiClient { this.setupAuthentication( httpMethod, apiUrl, requiredAuthentication ); try { - + return await request( { method: HttpMethod[ httpMethod ], diff --git a/src/models/account/Balance.ts b/src/models/account/Balance.ts index 6d6efcc..4d67461 100644 --- a/src/models/account/Balance.ts +++ b/src/models/account/Balance.ts @@ -11,8 +11,8 @@ export class Balance { constructor( json: any ) { this._asset = json.asset || json.a; - this._available = json.free || json.available || json.f; - this._locked = json.locked || json.l; + this._available = parseFloat(json.free || json.available || json.f); + this._locked = parseFloat(json.locked || json.l); } diff --git a/src/models/candlestick/Candlestick.ts b/src/models/candlestick/Candlestick.ts index e6e3bcf..5036cef 100644 --- a/src/models/candlestick/Candlestick.ts +++ b/src/models/candlestick/Candlestick.ts @@ -18,16 +18,16 @@ export class Candlestick { constructor( json: any ) { this._openingTime = new Date( json[ 0 ] ); - this._openingPrice = json[ 1 ]; - this._highestPrice = json[ 2 ]; - this._lowestPrice = json[ 3 ]; - this._closurePrice = json[ 4 ]; - this._baseAssetVolume = json[ 5 ]; + this._openingPrice = parseFloat(json[ 1 ]); + this._highestPrice = parseFloat(json[ 2 ]); + this._lowestPrice = parseFloat(json[ 3 ]); + this._closurePrice = parseFloat(json[ 4 ]); + this._baseAssetVolume = parseFloat(json[ 5 ]); this._closureTime = new Date( json[ 6 ] ); - this._quoteAssetVolume = json[ 7 ]; + this._quoteAssetVolume = parseFloat(json[ 7 ]); this._tradesCount = json[ 8 ]; - this._boughtBaseAssetVolume = json[ 9 ]; - this._boughtQuoteAssetVolume = json[ 10 ]; + this._boughtBaseAssetVolume = parseFloat(json[ 9 ]); + this._boughtQuoteAssetVolume = parseFloat(json[ 10 ]); } diff --git a/src/models/depth/LatestPrice.ts b/src/models/depth/LatestPrice.ts index 609a99b..707b259 100644 --- a/src/models/depth/LatestPrice.ts +++ b/src/models/depth/LatestPrice.ts @@ -8,7 +8,7 @@ export class LatestPrice { constructor( json: any ) { this._symbol = json.symbol; - this._price = json.price; + this._price = parseFloat(json.price); } get symbol(): string { diff --git a/src/models/filter/LotSizeFilter.ts b/src/models/filter/LotSizeFilter.ts index dd8c3cb..78daa71 100644 --- a/src/models/filter/LotSizeFilter.ts +++ b/src/models/filter/LotSizeFilter.ts @@ -11,9 +11,9 @@ export class LotSizeFilter implements SymbolFilter { constructor( json: any ) { - this._minimumQuantity = json.minQty; - this._maximumQuantity = json.maxQty; - this._stepSize = json.stepSize; + this._minimumQuantity = parseFloat(json.minQty); + this._maximumQuantity = parseFloat(json.maxQty); + this._stepSize = parseFloat(json.stepSize); } diff --git a/src/models/filter/MinimumNotionalFilter.ts b/src/models/filter/MinimumNotionalFilter.ts index e6776d8..c034208 100644 --- a/src/models/filter/MinimumNotionalFilter.ts +++ b/src/models/filter/MinimumNotionalFilter.ts @@ -8,7 +8,7 @@ export class MinimumNotionalFilter implements SymbolFilter { private _value: number; constructor( json: any ) { - this._value = json.minNotional; + this._value = parseFloat(json.minNotional); } get value(): number { diff --git a/src/models/filter/PriceFilter.ts b/src/models/filter/PriceFilter.ts index 4da83c4..67c94d4 100644 --- a/src/models/filter/PriceFilter.ts +++ b/src/models/filter/PriceFilter.ts @@ -11,9 +11,9 @@ export class PriceFilter implements SymbolFilter { constructor( json: any ) { - this._minimumPrice = json.minPrice; - this._maximumPrice = json.maxPrice; - this._tickSize = json.tickSize; + this._minimumPrice = parseFloat(json.minPrice); + this._maximumPrice = parseFloat(json.maxPrice); + this._tickSize = parseFloat(json.tickSize); } diff --git a/src/models/order/Order.ts b/src/models/order/Order.ts index 7567d80..fba0d7a 100644 --- a/src/models/order/Order.ts +++ b/src/models/order/Order.ts @@ -28,15 +28,15 @@ export class Order { this._symbol = json.symbol; this._id = json.orderId; this._clientId = json.clientOrderId; - this._price = json.price; - this._originalQuantity = json.origQty; - this._executedQuantity = json.executedQty; + this._price = parseFloat(json.price); + this._originalQuantity = parseFloat(json.origQty); + this._executedQuantity = parseFloat(json.executedQty); this._status = OrderStatus[ json.status as keyof typeof OrderStatus ]; this._timeInForce = TimeInForce[ json.timeInForce as keyof typeof TimeInForce ]; this._type = OrderType[ json.type as keyof typeof OrderType ]; this._side = OrderSide[ json.side as keyof typeof OrderSide ]; - this._stopPrice = json.stopPrice; - this._icebergQuantity = json.icebergQty; + this._stopPrice = parseFloat(json.stopPrice); + this._icebergQuantity = parseFloat(json.icebergQty); this._timestamp = new Date( json.time ); } diff --git a/src/models/order/OrderResult.ts b/src/models/order/OrderResult.ts index 4fb0513..8009d94 100644 --- a/src/models/order/OrderResult.ts +++ b/src/models/order/OrderResult.ts @@ -10,9 +10,9 @@ export class OrderResult { private _orderId: number; private _clientOrderId: string; private _timestamp: Date; - private _price: string; - private _originalQuantity: string; - private _executedQuantity: string; + private _price: number; + private _originalQuantity: number; + private _executedQuantity: number; private _status: OrderStatus; private _timeInForce: TimeInForce; private _type: OrderType; @@ -24,9 +24,9 @@ export class OrderResult { this._orderId = json.orderId; this._clientOrderId = json.clientOrderId; this._timestamp = new Date( json.transactTime ); - this._price = json.price; - this._originalQuantity = json.origQty; - this._executedQuantity = json.executedQty; + this._price = parseFloat(json.price); + this._originalQuantity = parseFloat(json.origQty); + this._executedQuantity = parseFloat(json.executedQty); this._status = OrderStatus[ json.status as keyof typeof OrderStatus ]; this._timeInForce = TimeInForce[ json.timeInForce as keyof typeof TimeInForce ]; this._type = OrderType[ json.type as keyof typeof OrderType ]; @@ -66,27 +66,27 @@ export class OrderResult { this._timestamp = value; } - get price(): string { + get price(): number { return this._price; } - set price( value: string ) { + set price( value: number ) { this._price = value; } - get originalQuantity(): string { + get originalQuantity(): number { return this._originalQuantity; } - set originalQuantity( value: string ) { + set originalQuantity( value: number ) { this._originalQuantity = value; } - get executedQuantity(): string { + get executedQuantity(): number { return this._executedQuantity; } - set executedQuantity( value: string ) { + set executedQuantity( value: number ) { this._executedQuantity = value; } diff --git a/src/models/order/PlacedOrder.ts b/src/models/order/PlacedOrder.ts index f1dfb2d..7820cc7 100644 --- a/src/models/order/PlacedOrder.ts +++ b/src/models/order/PlacedOrder.ts @@ -7,8 +7,8 @@ export class PlacedOrder { private _quantity: number; constructor( json: any ) { - this._price = json[ 0 ]; - this._quantity = json[ 1 ]; + this._price = parseFloat(json[ 0 ]); + this._quantity = parseFloat(json[ 1 ]); } get price(): number { diff --git a/src/models/order/PlacedOrderFill.ts b/src/models/order/PlacedOrderFill.ts index 6d597b3..a85dbc2 100644 --- a/src/models/order/PlacedOrderFill.ts +++ b/src/models/order/PlacedOrderFill.ts @@ -10,9 +10,9 @@ export class PlacedOrderFill { constructor( json: any ) { - this._quantity = json.qty; - this._price = json.price; - this._commission = json.commission; + this._quantity = parseFloat(json.qty); + this._price = parseFloat(json.price); + this._commission = parseFloat(json.commission); this._commissionAsset = json.commissionAsset; } diff --git a/src/models/ticker/Ticker.ts b/src/models/ticker/Ticker.ts index 8283600..cdda9c0 100644 --- a/src/models/ticker/Ticker.ts +++ b/src/models/ticker/Ticker.ts @@ -12,10 +12,10 @@ export class Ticker { constructor( json: any ) { this._symbol = json.symbol; - this._bidPrice = json.bidPrice; - this._bidQuantity = json.bidQty; - this._askPrice = json.askPrice; - this._askQuantity = json.askQty; + this._bidPrice = parseFloat(json.bidPrice); + this._bidQuantity = parseFloat(json.bidQty); + this._askPrice = parseFloat(json.askPrice); + this._askQuantity = parseFloat(json.askQty); } diff --git a/src/models/ticker/TickerStatistics.ts b/src/models/ticker/TickerStatistics.ts index b5f9707..7a126e9 100644 --- a/src/models/ticker/TickerStatistics.ts +++ b/src/models/ticker/TickerStatistics.ts @@ -22,17 +22,17 @@ export class TickerStatistics { constructor( json: any ) { - this._priceChange = json.priceChange; - this._priceChangePercentage = json.priceChangePercent; - this._weightedAveragePrice = json.weightedAvgPrice; - this._previousClosurePrice = json.prevClosePrice; - this._lastPrice = json.lastPrice; - this._bidPrice = json.bidPrice; - this._askPrice = json.askPrice; - this._openingPrice = json.openPrice; - this._highestPrice = json.highPrice; - this._lowestPrice = json.lowPrice; - this._volume = json.volume; + this._priceChange = parseFloat(json.priceChange); + this._priceChangePercentage = parseFloat(json.priceChangePercent); + this._weightedAveragePrice = parseFloat(json.weightedAvgPrice); + this._previousClosurePrice = parseFloat(json.prevClosePrice); + this._lastPrice = parseFloat(json.lastPrice); + this._bidPrice = parseFloat(json.bidPrice); + this._askPrice = parseFloat(json.askPrice); + this._openingPrice = parseFloat(json.openPrice); + this._highestPrice = parseFloat(json.highPrice); + this._lowestPrice = parseFloat(json.lowPrice); + this._volume = parseFloat(json.volume); this._openingTime = new Date( json.openTime ); this._closureTime = new Date( json.closeTime ); this._firstTradeId = json.firstId; @@ -169,4 +169,4 @@ export class TickerStatistics { this._tradesCount = value; } -} \ No newline at end of file +} diff --git a/src/models/trade/Trade.ts b/src/models/trade/Trade.ts index b0f6ef2..dc408b9 100644 --- a/src/models/trade/Trade.ts +++ b/src/models/trade/Trade.ts @@ -18,9 +18,9 @@ export class Trade { this._id = json.id; this._orderId = json.orderId; - this._price = json.price; - this._quantity = json.qty; - this._commission = json.commission; + this._price = parseFloat(json.price); + this._quantity = parseFloat(json.qty); + this._commission = parseFloat(json.commission); this._commissionAsset = json.commissionAsset; this._timestamp = new Date( json.time ); this._buyer = json.isBuyer; diff --git a/src/models/websocket/candlestick/CandlestickUpdate.ts b/src/models/websocket/candlestick/CandlestickUpdate.ts index 3d81007..c5451da 100644 --- a/src/models/websocket/candlestick/CandlestickUpdate.ts +++ b/src/models/websocket/candlestick/CandlestickUpdate.ts @@ -29,21 +29,21 @@ export class CandlestickUpdate { this._timestamp = new Date( json.E ); this._symbol = json.s; this._openingTime = new Date( json.k.t ); - this._openingPrice = json.k.o; - this._highestPrice = json.k.h; - this._lowestPrice = json.k.l; - this._closurePrice = json.k.c; - this._baseAssetVolume = json.k.v; + this._openingPrice = parseFloat(json.k.o); + this._highestPrice = parseFloat(json.k.h); + this._lowestPrice = parseFloat(json.k.l); + this._closurePrice = parseFloat(json.k.c); + this._baseAssetVolume = parseFloat(json.k.v); this._closureTime = new Date( json.k.T ); - this._quoteAssetVolume = json.k.q; + this._quoteAssetVolume = parseFloat(json.k.q); this._tradesCount = json.k.n; - this._boughtBaseAssetVolume = json.k.V; - this._boughtQuoteAssetVolume = json.k.Q; + this._boughtBaseAssetVolume = parseFloat(json.k.V); + this._boughtQuoteAssetVolume = parseFloat(json.k.Q); this._firstTradeId = json.k.f; this._lastTradeId = json.k.L; this._interval = json.k.i; this._final = json.k.x; - this._activeBuyVolume = json.k.V; + this._activeBuyVolume = parseFloat(json.k.V); } diff --git a/src/models/websocket/trade/RawTradeUpdate.ts b/src/models/websocket/trade/RawTradeUpdate.ts new file mode 100644 index 0000000..4c230a2 --- /dev/null +++ b/src/models/websocket/trade/RawTradeUpdate.ts @@ -0,0 +1,102 @@ +/** + * Represents a single raw trade update. + */ +export class RawTradeUpdate { + + private _timestamp: Date; + private _symbol: string; + private _tradeId: number; + private _price: number; + private _quantity: number; + private _buyerOrderId: number; + private _sellerOrderId: number; + private _placedAt: Date; + private _buyerMaker: boolean; + + constructor( json: any ) { + + this._timestamp = new Date( json.E ); + this._symbol = json.s; + this._tradeId = json.t; + this._price = parseFloat(json.p); + this._quantity = parseFloat(json.q); + this._buyerOrderId = json.b; + this._sellerOrderId = json.a; + this._placedAt = new Date( json.T ); + this._buyerMaker = json.m; + + } + + get timestamp(): Date { + return this._timestamp; + } + + set timestamp( value: Date ) { + this._timestamp = value; + } + + get symbol(): string { + return this._symbol; + } + + set symbol( value: string ) { + this._symbol = value; + } + + get tradeId(): number { + return this._tradeId; + } + + set tradeId( value: number ) { + this._tradeId = value; + } + + get price(): number { + return this._price; + } + + set price( value: number ) { + this._price = value; + } + + get quantity(): number { + return this._quantity; + } + + set quantity( value: number ) { + this._quantity = value; + } + + get buyerOrderId(): number { + return this._buyerOrderId; + } + + set buyerOrderId( value: number ) { + this._buyerOrderId = value; + } + + get sellerOrderId(): number { + return this._sellerOrderId; + } + + set sellerOrderId( value: number ) { + this._sellerOrderId = value; + } + + get placedAt(): Date { + return this._placedAt; + } + + set placedAt( value: Date ) { + this._placedAt = value; + } + + get buyerMaker(): boolean { + return this._buyerMaker; + } + + set buyerMaker( value: boolean ) { + this._buyerMaker = value; + } + +} diff --git a/src/models/websocket/trade/TradeUpdate.ts b/src/models/websocket/trade/TradeUpdate.ts index 30a66dc..f173c5d 100644 --- a/src/models/websocket/trade/TradeUpdate.ts +++ b/src/models/websocket/trade/TradeUpdate.ts @@ -18,8 +18,8 @@ export class TradeUpdate { this._timestamp = new Date( json.E ); this._symbol = json.s; this._aggregatedId = json.a; - this._price = json.p; - this._quantity = json.q; + this._price = parseFloat(json.p); + this._quantity = parseFloat(json.q); this._firstBreakdownId = json.f; this._lastBreakdownId = json.l; this._placedAt = new Date( json.T );