diff --git a/docs/api.md b/docs/api.md
index e8f0bddad..b89e95b5a 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -36,6 +36,8 @@
- [ListPairsResponse](#xudrpc.ListPairsResponse)
- [ListPeersRequest](#xudrpc.ListPeersRequest)
- [ListPeersResponse](#xudrpc.ListPeersResponse)
+ - [ListTradesRequest](#xudrpc.ListTradesRequest)
+ - [ListTradesResponse](#xudrpc.ListTradesResponse)
- [LndChannels](#xudrpc.LndChannels)
- [LndInfo](#xudrpc.LndInfo)
- [OpenChannelRequest](#xudrpc.OpenChannelRequest)
@@ -63,6 +65,7 @@
- [SubscribeSwapsRequest](#xudrpc.SubscribeSwapsRequest)
- [SwapFailure](#xudrpc.SwapFailure)
- [SwapSuccess](#xudrpc.SwapSuccess)
+ - [Trade](#xudrpc.Trade)
- [UnbanRequest](#xudrpc.UnbanRequest)
- [UnbanResponse](#xudrpc.UnbanResponse)
- [UnlockNodeRequest](#xudrpc.UnlockNodeRequest)
@@ -552,6 +555,36 @@
+
+
+### ListTradesRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| limit | [int32](#int32) | | The maximum number of trades to return |
+
+
+
+
+
+
+
+
+### ListTradesResponse
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| trades | [Trade](#xudrpc.Trade) | repeated | |
+
+
+
+
+
+
### LndChannels
@@ -996,6 +1029,25 @@
+
+
+### Trade
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| maker_order | [Order](#xudrpc.Order) | | The maker order involved in this trade. |
+| taker_order | [Order](#xudrpc.Order) | | The taker order involved in this trade. |
+| r_hash | [string](#string) | | The payment hash involved in this trade. |
+| quantity | [int64](#int64) | | The quantity transacted in this trade. |
+| pair_id | [string](#string) | | The trading pair for this trade. |
+
+
+
+
+
+
### UnbanRequest
@@ -1116,6 +1168,7 @@
| ListCurrencies | [ListCurrenciesRequest](#xudrpc.ListCurrenciesRequest) | [ListCurrenciesResponse](#xudrpc.ListCurrenciesResponse) | Gets a list of this node's supported currencies. |
| ListPairs | [ListPairsRequest](#xudrpc.ListPairsRequest) | [ListPairsResponse](#xudrpc.ListPairsResponse) | Gets a list of this nodes suported trading pairs. |
| ListPeers | [ListPeersRequest](#xudrpc.ListPeersRequest) | [ListPeersResponse](#xudrpc.ListPeersResponse) | Gets a list of connected peers. |
+| ListTrades | [ListTradesRequest](#xudrpc.ListTradesRequest) | [ListTradesResponse](#xudrpc.ListTradesResponse) | |
| PlaceOrder | [PlaceOrderRequest](#xudrpc.PlaceOrderRequest) | [PlaceOrderEvent](#xudrpc.PlaceOrderEvent) stream | Adds an order to the order book. If price is zero or unspecified a market order will get added. |
| PlaceOrderSync | [PlaceOrderRequest](#xudrpc.PlaceOrderRequest) | [PlaceOrderResponse](#xudrpc.PlaceOrderResponse) | The synchronous non-streaming version of PlaceOrder. |
| ExecuteSwap | [ExecuteSwapRequest](#xudrpc.ExecuteSwapRequest) | [SwapSuccess](#xudrpc.SwapSuccess) | Execute a swap on a maker peer order |
diff --git a/lib/cli/commands/listtrades.ts b/lib/cli/commands/listtrades.ts
new file mode 100644
index 000000000..82020865c
--- /dev/null
+++ b/lib/cli/commands/listtrades.ts
@@ -0,0 +1,53 @@
+import { Arguments } from 'yargs';
+import { callback, loadXudClient } from '../command';
+import { ListTradesRequest, ListTradesResponse, Trade } from '../../proto/xudrpc_pb';
+import Table, { HorizontalTable } from 'cli-table3';
+import colors from 'colors/safe';
+import { satsToCoinsStr } from '../utils';
+
+const HEADERS = [
+ colors.blue('Trading Pair'),
+ colors.blue('Trade Quantity'),
+ colors.blue('Price'),
+ colors.blue('Order Type'),
+];
+
+const displayTrades = (trades: ListTradesResponse.AsObject) => {
+ const table = new Table({ head: HEADERS }) as HorizontalTable;
+ trades.tradesList.forEach((trade: Trade.AsObject) => {
+ const type = trade.makerOrder ? 'maker' : 'taker';
+ let price = 0;
+ if (trade.makerOrder) {
+ price = trade.makerOrder.price;
+ } else if (trade.takerOrder) {
+ price = trade.takerOrder.price;
+ }
+
+ table.push([
+ trade.pairId,
+ satsToCoinsStr(trade.quantity),
+ parseFloat(price.toFixed(5)),
+ type,
+ ]);
+ });
+ console.log(colors.underline(colors.bold('\Trades:')));
+ console.log(table.toString());
+};
+
+export const command = 'listtrades [limit]';
+
+export const describe = 'list completed trades';
+
+export const builder = {
+ limit: {
+ description: 'the maximum number of trades to display',
+ type: 'number',
+ default: 15,
+ },
+};
+
+export const handler = (argv: Arguments) => {
+ const request = new ListTradesRequest();
+ request.setLimit(argv.limit);
+ loadXudClient(argv).listTrades(request, callback(argv, displayTrades));
+};
diff --git a/lib/grpc/GrpcService.ts b/lib/grpc/GrpcService.ts
index e4c8c152c..e3b50f040 100644
--- a/lib/grpc/GrpcService.ts
+++ b/lib/grpc/GrpcService.ts
@@ -12,6 +12,7 @@ import { errorCodes as lndErrorCodes } from '../lndclient/errors';
import { LndInfo } from '../lndclient/types';
import { SwapSuccess, SwapFailure } from '../swaps/types';
import { SwapFailureReason } from '../constants/enums';
+import { TradeInstance, OrderInstance } from '../db/types';
/**
* Creates an xudrpc Order message from an [[Order]].
@@ -69,6 +70,26 @@ const createSwapFailure = (swapFailure: SwapFailure) => {
return grpcSwapFailure;
};
+/**
+ * Creates an xudrpc Order from OrderInstance.
+ */
+const getGrpcOrderFromOrderInstance = (order: OrderInstance) => {
+ const grpcOrder = new xudrpc.Order();
+ grpcOrder.setCreatedAt(order.createdAt);
+ grpcOrder.setId(order.id);
+ grpcOrder.setIsOwnOrder(order.nodeId === undefined);
+ if (order.localId) {
+ grpcOrder.setLocalId(order.localId);
+ }
+ grpcOrder.setPairId(order.pairId);
+ // TODO: set peer pub key if order.nodeId has a value
+ if (order.price) {
+ grpcOrder.setPrice(order.price);
+ }
+ grpcOrder.setSide(order.isBuy ? xudrpc.OrderSide.BUY : xudrpc.OrderSide.SELL);
+ return grpcOrder;
+};
+
/**
* Creates an xudrpc PlaceOrderResponse message from a [[PlaceOrderResult]].
*/
@@ -503,6 +524,33 @@ class GrpcService {
}
}
+ /**
+ * See [[Service.listTrades]]
+ */
+ public listTrades: grpc.handleUnaryCall = async (call, callback) => {
+ try {
+ const trades = await this.service.listTrades(call.request.toObject());
+ const response = new xudrpc.ListTradesResponse();
+ const tradesList: xudrpc.Trade[] = [];
+ await Promise.all(trades.map(async (trade: TradeInstance) => {
+ const grpcTrade = new xudrpc.Trade();
+ const makerOrder = await trade.getMakerOrder();
+ const takerOrder = await trade.getTakerOrder();
+ grpcTrade.setQuantity(trade.quantity);
+ grpcTrade.setRHash(trade.rHash ? trade.rHash : '');
+ grpcTrade.setMakerOrder(getGrpcOrderFromOrderInstance(makerOrder!));
+ grpcTrade.setTakerOrder(takerOrder ? getGrpcOrderFromOrderInstance(takerOrder) : undefined);
+ grpcTrade.setPairId(makerOrder!.pairId);
+ tradesList.push(grpcTrade);
+ }));
+
+ response.setTradesList(tradesList);
+ callback(null, response);
+ } catch (err) {
+ callback(this.getGrpcError(err), null);
+ }
+ }
+
/**
* See [[Service.listPeers]]
*/
diff --git a/lib/orderbook/OrderBook.ts b/lib/orderbook/OrderBook.ts
index fe23485a3..1e2b764f1 100644
--- a/lib/orderbook/OrderBook.ts
+++ b/lib/orderbook/OrderBook.ts
@@ -200,6 +200,14 @@ class OrderBook extends EventEmitter {
return currencyInstance ? currencyInstance.toJSON() : undefined;
}
+ /**
+ * Gets all trades or a limited number of trades from the database.
+ */
+ public getTrades = async (limit?: number) => {
+ const response = await this.repository.getTrades(limit);
+ return response;
+ }
+
/**
* Get lists of buy and sell orders of peers.
*/
diff --git a/lib/orderbook/OrderBookRepository.ts b/lib/orderbook/OrderBookRepository.ts
index cfeb2f7c7..4374a4f73 100644
--- a/lib/orderbook/OrderBookRepository.ts
+++ b/lib/orderbook/OrderBookRepository.ts
@@ -49,6 +49,14 @@ class OrderbookRepository {
public addTrade = (trade: db.TradeFactory) => {
return this.models.Trade.create(trade);
}
+
+ public getTrades = (limit?: number): Bluebird => {
+ if (limit) {
+ return this.models.Trade.findAll({ limit, order: [['createdAt', 'DESC']] });
+ } else {
+ return this.models.Trade.findAll({ order: [['createdAt', 'DESC']] });
+ }
+ }
}
export default OrderbookRepository;
diff --git a/lib/proto/xudp2p_grpc_pb.js b/lib/proto/xudp2p_grpc_pb.js
index 51b4d6959..97b3a2461 100644
--- a/lib/proto/xudp2p_grpc_pb.js
+++ b/lib/proto/xudp2p_grpc_pb.js
@@ -1 +1 @@
-// GENERATED CODE -- NO SERVICES IN PROTO
+// GENERATED CODE -- NO SERVICES IN PROTO
\ No newline at end of file
diff --git a/lib/proto/xudrpc.swagger.json b/lib/proto/xudrpc.swagger.json
index 30e53a259..b8334716d 100644
--- a/lib/proto/xudrpc.swagger.json
+++ b/lib/proto/xudrpc.swagger.json
@@ -237,6 +237,32 @@
]
}
},
+ "/v1/listtrades": {
+ "post": {
+ "operationId": "ListTrades",
+ "responses": {
+ "200": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/xudrpcListTradesResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/xudrpcListTradesRequest"
+ }
+ }
+ ],
+ "tags": [
+ "Xud"
+ ]
+ }
+ },
"/v1/nodeinfo": {
"get": {
"summary": "Gets general information about a node.",
@@ -918,6 +944,27 @@
}
}
},
+ "xudrpcListTradesRequest": {
+ "type": "object",
+ "properties": {
+ "limit": {
+ "type": "integer",
+ "format": "int32",
+ "title": "The maximum number of trades to return"
+ }
+ }
+ },
+ "xudrpcListTradesResponse": {
+ "type": "object",
+ "properties": {
+ "trades": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/xudrpcTrade"
+ }
+ }
+ }
+ },
"xudrpcLndChannels": {
"type": "object",
"properties": {
@@ -1388,6 +1435,32 @@
}
}
},
+ "xudrpcTrade": {
+ "type": "object",
+ "properties": {
+ "maker_order": {
+ "$ref": "#/definitions/xudrpcOrder",
+ "description": "The maker order involved in this trade."
+ },
+ "taker_order": {
+ "$ref": "#/definitions/xudrpcOrder",
+ "description": "The taker order involved in this trade."
+ },
+ "r_hash": {
+ "type": "string",
+ "description": "The payment hash involved in this trade."
+ },
+ "quantity": {
+ "type": "string",
+ "format": "int64",
+ "description": "The quantity transacted in this trade."
+ },
+ "pair_id": {
+ "type": "string",
+ "description": "The trading pair for this trade."
+ }
+ }
+ },
"xudrpcUnbanRequest": {
"type": "object",
"properties": {
diff --git a/lib/proto/xudrpc_grpc_pb.d.ts b/lib/proto/xudrpc_grpc_pb.d.ts
index 25eabfb05..b6cde0964 100644
--- a/lib/proto/xudrpc_grpc_pb.d.ts
+++ b/lib/proto/xudrpc_grpc_pb.d.ts
@@ -72,6 +72,7 @@ interface IXudService extends grpc.ServiceDefinition;
responseDeserialize: grpc.deserialize;
}
+interface IXudService_IListTrades extends grpc.MethodDefinition {
+ path: string; // "/xudrpc.Xud/ListTrades"
+ requestStream: boolean; // false
+ responseStream: boolean; // false
+ requestSerialize: grpc.serialize;
+ requestDeserialize: grpc.deserialize;
+ responseSerialize: grpc.serialize;
+ responseDeserialize: grpc.deserialize;
+}
interface IXudService_IPlaceOrder extends grpc.MethodDefinition {
path: string; // "/xudrpc.Xud/PlaceOrder"
requestStream: boolean; // false
@@ -318,6 +328,7 @@ export interface IXudServer {
listCurrencies: grpc.handleUnaryCall;
listPairs: grpc.handleUnaryCall;
listPeers: grpc.handleUnaryCall;
+ listTrades: grpc.handleUnaryCall;
placeOrder: grpc.handleServerStreamingCall;
placeOrderSync: grpc.handleUnaryCall;
executeSwap: grpc.handleUnaryCall;
@@ -373,6 +384,9 @@ export interface IXudClient {
listPeers(request: xudrpc_pb.ListPeersRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall;
listPeers(request: xudrpc_pb.ListPeersRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall;
listPeers(request: xudrpc_pb.ListPeersRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall;
+ listTrades(request: xudrpc_pb.ListTradesRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall;
+ listTrades(request: xudrpc_pb.ListTradesRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall;
+ listTrades(request: xudrpc_pb.ListTradesRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall;
placeOrder(request: xudrpc_pb.PlaceOrderRequest, options?: Partial): grpc.ClientReadableStream;
placeOrder(request: xudrpc_pb.PlaceOrderRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream;
placeOrderSync(request: xudrpc_pb.PlaceOrderRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.PlaceOrderResponse) => void): grpc.ClientUnaryCall;
@@ -445,6 +459,9 @@ export class XudClient extends grpc.Client implements IXudClient {
public listPeers(request: xudrpc_pb.ListPeersRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall;
public listPeers(request: xudrpc_pb.ListPeersRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall;
public listPeers(request: xudrpc_pb.ListPeersRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall;
+ public listTrades(request: xudrpc_pb.ListTradesRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall;
+ public listTrades(request: xudrpc_pb.ListTradesRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall;
+ public listTrades(request: xudrpc_pb.ListTradesRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall;
public placeOrder(request: xudrpc_pb.PlaceOrderRequest, options?: Partial): grpc.ClientReadableStream;
public placeOrder(request: xudrpc_pb.PlaceOrderRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream;
public placeOrderSync(request: xudrpc_pb.PlaceOrderRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.PlaceOrderResponse) => void): grpc.ClientUnaryCall;
diff --git a/lib/proto/xudrpc_grpc_pb.js b/lib/proto/xudrpc_grpc_pb.js
index 73547a466..3d92fe566 100644
--- a/lib/proto/xudrpc_grpc_pb.js
+++ b/lib/proto/xudrpc_grpc_pb.js
@@ -323,6 +323,28 @@ function deserialize_xudrpc_ListPeersResponse(buffer_arg) {
return xudrpc_pb.ListPeersResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
+function serialize_xudrpc_ListTradesRequest(arg) {
+ if (!(arg instanceof xudrpc_pb.ListTradesRequest)) {
+ throw new Error('Expected argument of type xudrpc.ListTradesRequest');
+ }
+ return Buffer.from(arg.serializeBinary());
+}
+
+function deserialize_xudrpc_ListTradesRequest(buffer_arg) {
+ return xudrpc_pb.ListTradesRequest.deserializeBinary(new Uint8Array(buffer_arg));
+}
+
+function serialize_xudrpc_ListTradesResponse(arg) {
+ if (!(arg instanceof xudrpc_pb.ListTradesResponse)) {
+ throw new Error('Expected argument of type xudrpc.ListTradesResponse');
+ }
+ return Buffer.from(arg.serializeBinary());
+}
+
+function deserialize_xudrpc_ListTradesResponse(buffer_arg) {
+ return xudrpc_pb.ListTradesResponse.deserializeBinary(new Uint8Array(buffer_arg));
+}
+
function serialize_xudrpc_OpenChannelRequest(arg) {
if (!(arg instanceof xudrpc_pb.OpenChannelRequest)) {
throw new Error('Expected argument of type xudrpc.OpenChannelRequest');
@@ -774,6 +796,17 @@ var XudService = exports.XudService = {
responseSerialize: serialize_xudrpc_ListPeersResponse,
responseDeserialize: deserialize_xudrpc_ListPeersResponse,
},
+ listTrades: {
+ path: '/xudrpc.Xud/ListTrades',
+ requestStream: false,
+ responseStream: false,
+ requestType: xudrpc_pb.ListTradesRequest,
+ responseType: xudrpc_pb.ListTradesResponse,
+ requestSerialize: serialize_xudrpc_ListTradesRequest,
+ requestDeserialize: deserialize_xudrpc_ListTradesRequest,
+ responseSerialize: serialize_xudrpc_ListTradesResponse,
+ responseDeserialize: deserialize_xudrpc_ListTradesResponse,
+ },
// Adds an order to the order book.
// If price is zero or unspecified a market order will get added.
placeOrder: {
diff --git a/lib/proto/xudrpc_pb.d.ts b/lib/proto/xudrpc_pb.d.ts
index 668c84412..205672850 100644
--- a/lib/proto/xudrpc_pb.d.ts
+++ b/lib/proto/xudrpc_pb.d.ts
@@ -786,6 +786,50 @@ export namespace ListPeersResponse {
}
}
+export class ListTradesRequest extends jspb.Message {
+ getLimit(): number;
+ setLimit(value: number): void;
+
+
+ serializeBinary(): Uint8Array;
+ toObject(includeInstance?: boolean): ListTradesRequest.AsObject;
+ static toObject(includeInstance: boolean, msg: ListTradesRequest): ListTradesRequest.AsObject;
+ static extensions: {[key: number]: jspb.ExtensionFieldInfo};
+ static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo};
+ static serializeBinaryToWriter(message: ListTradesRequest, writer: jspb.BinaryWriter): void;
+ static deserializeBinary(bytes: Uint8Array): ListTradesRequest;
+ static deserializeBinaryFromReader(message: ListTradesRequest, reader: jspb.BinaryReader): ListTradesRequest;
+}
+
+export namespace ListTradesRequest {
+ export type AsObject = {
+ limit: number,
+ }
+}
+
+export class ListTradesResponse extends jspb.Message {
+ clearTradesList(): void;
+ getTradesList(): Array;
+ setTradesList(value: Array): void;
+ addTrades(value?: Trade, index?: number): Trade;
+
+
+ serializeBinary(): Uint8Array;
+ toObject(includeInstance?: boolean): ListTradesResponse.AsObject;
+ static toObject(includeInstance: boolean, msg: ListTradesResponse): ListTradesResponse.AsObject;
+ static extensions: {[key: number]: jspb.ExtensionFieldInfo};
+ static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo};
+ static serializeBinaryToWriter(message: ListTradesResponse, writer: jspb.BinaryWriter): void;
+ static deserializeBinary(bytes: Uint8Array): ListTradesResponse;
+ static deserializeBinaryFromReader(message: ListTradesResponse, reader: jspb.BinaryReader): ListTradesResponse;
+}
+
+export namespace ListTradesResponse {
+ export type AsObject = {
+ tradesList: Array,
+ }
+}
+
export class LndChannels extends jspb.Message {
getActive(): number;
setActive(value: number): void;
@@ -942,6 +986,49 @@ export namespace Order {
}
+export class Trade extends jspb.Message {
+
+ hasMakerOrder(): boolean;
+ clearMakerOrder(): void;
+ getMakerOrder(): Order | undefined;
+ setMakerOrder(value?: Order): void;
+
+
+ hasTakerOrder(): boolean;
+ clearTakerOrder(): void;
+ getTakerOrder(): Order | undefined;
+ setTakerOrder(value?: Order): void;
+
+ getRHash(): string;
+ setRHash(value: string): void;
+
+ getQuantity(): number;
+ setQuantity(value: number): void;
+
+ getPairId(): string;
+ setPairId(value: string): void;
+
+
+ serializeBinary(): Uint8Array;
+ toObject(includeInstance?: boolean): Trade.AsObject;
+ static toObject(includeInstance: boolean, msg: Trade): Trade.AsObject;
+ static extensions: {[key: number]: jspb.ExtensionFieldInfo};
+ static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo};
+ static serializeBinaryToWriter(message: Trade, writer: jspb.BinaryWriter): void;
+ static deserializeBinary(bytes: Uint8Array): Trade;
+ static deserializeBinaryFromReader(message: Trade, reader: jspb.BinaryReader): Trade;
+}
+
+export namespace Trade {
+ export type AsObject = {
+ makerOrder?: Order.AsObject,
+ takerOrder?: Order.AsObject,
+ rHash: string,
+ quantity: number,
+ pairId: string,
+ }
+}
+
export class OrderUpdate extends jspb.Message {
hasOrder(): boolean;
diff --git a/lib/proto/xudrpc_pb.js b/lib/proto/xudrpc_pb.js
index acedc1606..a262a482f 100644
--- a/lib/proto/xudrpc_pb.js
+++ b/lib/proto/xudrpc_pb.js
@@ -42,6 +42,8 @@ goog.exportSymbol('proto.xudrpc.ListPairsRequest', null, global);
goog.exportSymbol('proto.xudrpc.ListPairsResponse', null, global);
goog.exportSymbol('proto.xudrpc.ListPeersRequest', null, global);
goog.exportSymbol('proto.xudrpc.ListPeersResponse', null, global);
+goog.exportSymbol('proto.xudrpc.ListTradesRequest', null, global);
+goog.exportSymbol('proto.xudrpc.ListTradesResponse', null, global);
goog.exportSymbol('proto.xudrpc.LndChannels', null, global);
goog.exportSymbol('proto.xudrpc.LndInfo', null, global);
goog.exportSymbol('proto.xudrpc.OpenChannelRequest', null, global);
@@ -70,6 +72,7 @@ goog.exportSymbol('proto.xudrpc.SubscribeSwapsRequest', null, global);
goog.exportSymbol('proto.xudrpc.SwapFailure', null, global);
goog.exportSymbol('proto.xudrpc.SwapSuccess', null, global);
goog.exportSymbol('proto.xudrpc.SwapSuccess.Role', null, global);
+goog.exportSymbol('proto.xudrpc.Trade', null, global);
goog.exportSymbol('proto.xudrpc.UnbanRequest', null, global);
goog.exportSymbol('proto.xudrpc.UnbanResponse', null, global);
goog.exportSymbol('proto.xudrpc.UnlockNodeRequest', null, global);
@@ -5342,6 +5345,316 @@ proto.xudrpc.ListPeersResponse.prototype.clearPeersList = function() {
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.xudrpc.ListTradesRequest = function(opt_data) {
+ jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.xudrpc.ListTradesRequest, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+ proto.xudrpc.ListTradesRequest.displayName = 'proto.xudrpc.ListTradesRequest';
+}
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto suitable for use in Soy templates.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * To access a reserved field use, foo.pb_, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ * for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.xudrpc.ListTradesRequest.prototype.toObject = function(opt_includeInstance) {
+ return proto.xudrpc.ListTradesRequest.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Whether to include the JSPB
+ * instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @param {!proto.xudrpc.ListTradesRequest} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.xudrpc.ListTradesRequest.toObject = function(includeInstance, msg) {
+ var f, obj = {
+ limit: jspb.Message.getFieldWithDefault(msg, 1, 0)
+ };
+
+ if (includeInstance) {
+ obj.$jspbMessageInstance = msg;
+ }
+ return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.xudrpc.ListTradesRequest}
+ */
+proto.xudrpc.ListTradesRequest.deserializeBinary = function(bytes) {
+ var reader = new jspb.BinaryReader(bytes);
+ var msg = new proto.xudrpc.ListTradesRequest;
+ return proto.xudrpc.ListTradesRequest.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.xudrpc.ListTradesRequest} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.xudrpc.ListTradesRequest}
+ */
+proto.xudrpc.ListTradesRequest.deserializeBinaryFromReader = function(msg, reader) {
+ while (reader.nextField()) {
+ if (reader.isEndGroup()) {
+ break;
+ }
+ var field = reader.getFieldNumber();
+ switch (field) {
+ case 1:
+ var value = /** @type {number} */ (reader.readInt32());
+ msg.setLimit(value);
+ break;
+ default:
+ reader.skipField();
+ break;
+ }
+ }
+ return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.xudrpc.ListTradesRequest.prototype.serializeBinary = function() {
+ var writer = new jspb.BinaryWriter();
+ proto.xudrpc.ListTradesRequest.serializeBinaryToWriter(this, writer);
+ return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.xudrpc.ListTradesRequest} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.xudrpc.ListTradesRequest.serializeBinaryToWriter = function(message, writer) {
+ var f = undefined;
+ f = message.getLimit();
+ if (f !== 0) {
+ writer.writeInt32(
+ 1,
+ f
+ );
+ }
+};
+
+
+/**
+ * optional int32 limit = 1;
+ * @return {number}
+ */
+proto.xudrpc.ListTradesRequest.prototype.getLimit = function() {
+ return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0));
+};
+
+
+/** @param {number} value */
+proto.xudrpc.ListTradesRequest.prototype.setLimit = function(value) {
+ jspb.Message.setField(this, 1, value);
+};
+
+
+
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.xudrpc.ListTradesResponse = function(opt_data) {
+ jspb.Message.initialize(this, opt_data, 0, -1, proto.xudrpc.ListTradesResponse.repeatedFields_, null);
+};
+goog.inherits(proto.xudrpc.ListTradesResponse, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+ proto.xudrpc.ListTradesResponse.displayName = 'proto.xudrpc.ListTradesResponse';
+}
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array}
+ * @const
+ */
+proto.xudrpc.ListTradesResponse.repeatedFields_ = [1];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto suitable for use in Soy templates.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * To access a reserved field use, foo.pb_, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ * for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.xudrpc.ListTradesResponse.prototype.toObject = function(opt_includeInstance) {
+ return proto.xudrpc.ListTradesResponse.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Whether to include the JSPB
+ * instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @param {!proto.xudrpc.ListTradesResponse} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.xudrpc.ListTradesResponse.toObject = function(includeInstance, msg) {
+ var f, obj = {
+ tradesList: jspb.Message.toObjectList(msg.getTradesList(),
+ proto.xudrpc.Trade.toObject, includeInstance)
+ };
+
+ if (includeInstance) {
+ obj.$jspbMessageInstance = msg;
+ }
+ return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.xudrpc.ListTradesResponse}
+ */
+proto.xudrpc.ListTradesResponse.deserializeBinary = function(bytes) {
+ var reader = new jspb.BinaryReader(bytes);
+ var msg = new proto.xudrpc.ListTradesResponse;
+ return proto.xudrpc.ListTradesResponse.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.xudrpc.ListTradesResponse} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.xudrpc.ListTradesResponse}
+ */
+proto.xudrpc.ListTradesResponse.deserializeBinaryFromReader = function(msg, reader) {
+ while (reader.nextField()) {
+ if (reader.isEndGroup()) {
+ break;
+ }
+ var field = reader.getFieldNumber();
+ switch (field) {
+ case 1:
+ var value = new proto.xudrpc.Trade;
+ reader.readMessage(value,proto.xudrpc.Trade.deserializeBinaryFromReader);
+ msg.addTrades(value);
+ break;
+ default:
+ reader.skipField();
+ break;
+ }
+ }
+ return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.xudrpc.ListTradesResponse.prototype.serializeBinary = function() {
+ var writer = new jspb.BinaryWriter();
+ proto.xudrpc.ListTradesResponse.serializeBinaryToWriter(this, writer);
+ return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.xudrpc.ListTradesResponse} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.xudrpc.ListTradesResponse.serializeBinaryToWriter = function(message, writer) {
+ var f = undefined;
+ f = message.getTradesList();
+ if (f.length > 0) {
+ writer.writeRepeatedMessage(
+ 1,
+ f,
+ proto.xudrpc.Trade.serializeBinaryToWriter
+ );
+ }
+};
+
+
+/**
+ * repeated Trade trades = 1;
+ * @return {!Array.}
+ */
+proto.xudrpc.ListTradesResponse.prototype.getTradesList = function() {
+ return /** @type{!Array.} */ (
+ jspb.Message.getRepeatedWrapperField(this, proto.xudrpc.Trade, 1));
+};
+
+
+/** @param {!Array.} value */
+proto.xudrpc.ListTradesResponse.prototype.setTradesList = function(value) {
+ jspb.Message.setRepeatedWrapperField(this, 1, value);
+};
+
+
+/**
+ * @param {!proto.xudrpc.Trade=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.xudrpc.Trade}
+ */
+proto.xudrpc.ListTradesResponse.prototype.addTrades = function(opt_value, opt_index) {
+ return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.xudrpc.Trade, opt_index);
+};
+
+
+proto.xudrpc.ListTradesResponse.prototype.clearTradesList = function() {
+ this.setTradesList([]);
+};
+
+
+
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -6340,6 +6653,290 @@ proto.xudrpc.Order.prototype.setHold = function(value) {
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.xudrpc.Trade = function(opt_data) {
+ jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.xudrpc.Trade, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+ proto.xudrpc.Trade.displayName = 'proto.xudrpc.Trade';
+}
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto suitable for use in Soy templates.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * To access a reserved field use, foo.pb_, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ * for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.xudrpc.Trade.prototype.toObject = function(opt_includeInstance) {
+ return proto.xudrpc.Trade.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Whether to include the JSPB
+ * instance for transitional soy proto support:
+ * http://goto/soy-param-migration
+ * @param {!proto.xudrpc.Trade} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.xudrpc.Trade.toObject = function(includeInstance, msg) {
+ var f, obj = {
+ makerOrder: (f = msg.getMakerOrder()) && proto.xudrpc.Order.toObject(includeInstance, f),
+ takerOrder: (f = msg.getTakerOrder()) && proto.xudrpc.Order.toObject(includeInstance, f),
+ rHash: jspb.Message.getFieldWithDefault(msg, 3, ""),
+ quantity: jspb.Message.getFieldWithDefault(msg, 4, 0),
+ pairId: jspb.Message.getFieldWithDefault(msg, 5, "")
+ };
+
+ if (includeInstance) {
+ obj.$jspbMessageInstance = msg;
+ }
+ return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.xudrpc.Trade}
+ */
+proto.xudrpc.Trade.deserializeBinary = function(bytes) {
+ var reader = new jspb.BinaryReader(bytes);
+ var msg = new proto.xudrpc.Trade;
+ return proto.xudrpc.Trade.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.xudrpc.Trade} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.xudrpc.Trade}
+ */
+proto.xudrpc.Trade.deserializeBinaryFromReader = function(msg, reader) {
+ while (reader.nextField()) {
+ if (reader.isEndGroup()) {
+ break;
+ }
+ var field = reader.getFieldNumber();
+ switch (field) {
+ case 1:
+ var value = new proto.xudrpc.Order;
+ reader.readMessage(value,proto.xudrpc.Order.deserializeBinaryFromReader);
+ msg.setMakerOrder(value);
+ break;
+ case 2:
+ var value = new proto.xudrpc.Order;
+ reader.readMessage(value,proto.xudrpc.Order.deserializeBinaryFromReader);
+ msg.setTakerOrder(value);
+ break;
+ case 3:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setRHash(value);
+ break;
+ case 4:
+ var value = /** @type {number} */ (reader.readInt64());
+ msg.setQuantity(value);
+ break;
+ case 5:
+ var value = /** @type {string} */ (reader.readString());
+ msg.setPairId(value);
+ break;
+ default:
+ reader.skipField();
+ break;
+ }
+ }
+ return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.xudrpc.Trade.prototype.serializeBinary = function() {
+ var writer = new jspb.BinaryWriter();
+ proto.xudrpc.Trade.serializeBinaryToWriter(this, writer);
+ return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.xudrpc.Trade} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.xudrpc.Trade.serializeBinaryToWriter = function(message, writer) {
+ var f = undefined;
+ f = message.getMakerOrder();
+ if (f != null) {
+ writer.writeMessage(
+ 1,
+ f,
+ proto.xudrpc.Order.serializeBinaryToWriter
+ );
+ }
+ f = message.getTakerOrder();
+ if (f != null) {
+ writer.writeMessage(
+ 2,
+ f,
+ proto.xudrpc.Order.serializeBinaryToWriter
+ );
+ }
+ f = message.getRHash();
+ if (f.length > 0) {
+ writer.writeString(
+ 3,
+ f
+ );
+ }
+ f = message.getQuantity();
+ if (f !== 0) {
+ writer.writeInt64(
+ 4,
+ f
+ );
+ }
+ f = message.getPairId();
+ if (f.length > 0) {
+ writer.writeString(
+ 5,
+ f
+ );
+ }
+};
+
+
+/**
+ * optional Order maker_order = 1;
+ * @return {?proto.xudrpc.Order}
+ */
+proto.xudrpc.Trade.prototype.getMakerOrder = function() {
+ return /** @type{?proto.xudrpc.Order} */ (
+ jspb.Message.getWrapperField(this, proto.xudrpc.Order, 1));
+};
+
+
+/** @param {?proto.xudrpc.Order|undefined} value */
+proto.xudrpc.Trade.prototype.setMakerOrder = function(value) {
+ jspb.Message.setWrapperField(this, 1, value);
+};
+
+
+proto.xudrpc.Trade.prototype.clearMakerOrder = function() {
+ this.setMakerOrder(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {!boolean}
+ */
+proto.xudrpc.Trade.prototype.hasMakerOrder = function() {
+ return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional Order taker_order = 2;
+ * @return {?proto.xudrpc.Order}
+ */
+proto.xudrpc.Trade.prototype.getTakerOrder = function() {
+ return /** @type{?proto.xudrpc.Order} */ (
+ jspb.Message.getWrapperField(this, proto.xudrpc.Order, 2));
+};
+
+
+/** @param {?proto.xudrpc.Order|undefined} value */
+proto.xudrpc.Trade.prototype.setTakerOrder = function(value) {
+ jspb.Message.setWrapperField(this, 2, value);
+};
+
+
+proto.xudrpc.Trade.prototype.clearTakerOrder = function() {
+ this.setTakerOrder(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {!boolean}
+ */
+proto.xudrpc.Trade.prototype.hasTakerOrder = function() {
+ return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional string r_hash = 3;
+ * @return {string}
+ */
+proto.xudrpc.Trade.prototype.getRHash = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/** @param {string} value */
+proto.xudrpc.Trade.prototype.setRHash = function(value) {
+ jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * optional int64 quantity = 4;
+ * @return {number}
+ */
+proto.xudrpc.Trade.prototype.getQuantity = function() {
+ return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0));
+};
+
+
+/** @param {number} value */
+proto.xudrpc.Trade.prototype.setQuantity = function(value) {
+ jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * optional string pair_id = 5;
+ * @return {string}
+ */
+proto.xudrpc.Trade.prototype.getPairId = function() {
+ return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
+};
+
+
+/** @param {string} value */
+proto.xudrpc.Trade.prototype.setPairId = function(value) {
+ jspb.Message.setField(this, 5, value);
+};
+
+
+
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
diff --git a/lib/service/Service.ts b/lib/service/Service.ts
index fef4dca0b..aa0ff861b 100644
--- a/lib/service/Service.ts
+++ b/lib/service/Service.ts
@@ -349,6 +349,18 @@ class Service {
return this.pool.listPeers();
}
+ /**
+ * Gets the list of trades.
+ */
+ public listTrades = (args: {limit: number}) => {
+ const { limit } = args;
+ if (limit === 0) {
+ return this.orderBook.getTrades();
+ } else {
+ return this.orderBook.getTrades(limit);
+ }
+ }
+
/**
* Add an order to the order book.
* If price is zero or unspecified a market order will get added.
diff --git a/proto/xudrpc.proto b/proto/xudrpc.proto
index 18279b8f8..df043079e 100644
--- a/proto/xudrpc.proto
+++ b/proto/xudrpc.proto
@@ -172,6 +172,13 @@ service Xud {
};
}
+ rpc ListTrades(ListTradesRequest) returns (ListTradesResponse) {
+ option (google.api.http) = {
+ post: "/v1/listtrades"
+ body: "*"
+ };
+ }
+
/* Adds an order to the order book.
* If price is zero or unspecified a market order will get added. */
rpc PlaceOrder(PlaceOrderRequest) returns (stream PlaceOrderEvent) {
@@ -432,6 +439,15 @@ message ListPeersResponse {
repeated Peer peers = 1 [json_name = "peers"];
}
+message ListTradesRequest {
+ // The maximum number of trades to return
+ int32 limit = 1 [json_name = "limit"];
+}
+
+message ListTradesResponse {
+ repeated Trade trades = 1 [json_name = "trades"];
+}
+
message LndChannels {
// The number of active/online channels for this lnd instance that can be used for swaps.
uint32 active = 1 [json_name = "active"];
@@ -476,6 +492,19 @@ message Order {
uint64 hold = 10 [json_name = "hold"];
}
+message Trade {
+ // The maker order involved in this trade.
+ Order maker_order = 1 [json_name = "maker_order"];
+ // The taker order involved in this trade.
+ Order taker_order = 2 [json_name = "taker_order_id"];
+ // The payment hash involved in this trade.
+ string r_hash = 3 [json_name = "r_hash"];
+ // The quantity transacted in this trade.
+ int64 quantity = 4 [json_name = "quantity"];
+ // The trading pair for this trade.
+ string pair_id = 5 [json_name = "pair_id"];
+}
+
message OrderUpdate {
oneof order_update {
// An order that was added to the order book.