From 178db02da57e4bfa1c035a7dd254cb17fb747c34 Mon Sep 17 00:00:00 2001 From: icebox Date: Tue, 1 Nov 2016 23:04:28 +0100 Subject: [PATCH] feat(v20): support rest-v20 calls This is a massive change due to breaking changes of a new REST calls. There is something to complete (and to fix). --- src/client/app/account/account.html | 30 +++--- .../app/account/accounts-bottomsheet.html | 6 +- src/client/app/account/accounts.service.js | 11 +-- src/client/app/activity/activity.html | 4 - src/client/app/activity/activity.service.js | 15 +-- src/client/app/charts/charts.service.js | 2 - src/client/app/charts/order-dialog.js | 83 +++++++++++------ src/client/app/components/charts.directive.js | 8 +- src/client/app/exposure/exposure.js | 11 +-- src/client/app/header/header.js | 6 +- src/client/app/orders/orders.service.js | 9 +- src/client/app/positions/positions.service.js | 27 +++++- src/client/app/services/stream.service.js | 18 +++- src/client/app/trades/trades.html | 8 +- src/client/app/trades/trades.js | 4 +- src/client/app/trades/trades.service.js | 8 +- .../test/specs/accounts.service.spec.js | 43 ++++----- .../test/specs/activity.service.spec.js | 40 ++++---- src/client/test/specs/exposure.spec.js | 6 +- .../test/specs/positions.service.spec.js | 23 +++-- src/server/routes/api.js | 92 ++++++++++--------- src/server/routes/config.js | 1 - src/server/routes/stream.js | 28 +++--- 23 files changed, 264 insertions(+), 219 deletions(-) diff --git a/src/client/app/account/account.html b/src/client/app/account/account.html index 37efac13..ad43bf27 100644 --- a/src/client/app/account/account.html +++ b/src/client/app/account/account.html @@ -1,5 +1,5 @@ - - Account Summary ({{ $ctrl.account.accountCurrency }}) {{ $ctrl.account.timestamp | date:"MMM d, HH:mm:ss" }} + + Account Summary ({{ $ctrl.account.currency }}) {{ $ctrl.account.timestamp | date:"MMM d, HH:mm:ss" }} @@ -8,47 +8,39 @@ Unrealized P&L - {{ $ctrl.account.unrealizedPl | number: 2 }} + {{ $ctrl.account.unrealizedPL | number: 2 }} Unrealized P&L (%) - {{ $ctrl.account.unrealizedPlPerc | number: 2 }} + {{ $ctrl.account.unrealizedPLPercent | number: 2 }} Net Asset Value - {{ $ctrl.account.netAssetValue | number: 2 }} + {{ $ctrl.account.NAV | number: 2 }} - Realized P&L - {{ $ctrl.account.realizedPl | number: 2 }} + {{ $ctrl.account.pl | number: 2 }} Margin Used - {{ $ctrl.account.marginUsed | number: 2 }} + {{ $ctrl.account.marginCallMarginUsed | number: 2 }} Margin Available - {{ $ctrl.account.marginAvail | number: 2 }} + {{ $ctrl.account.marginAvailable | number: 2 }} - diff --git a/src/client/app/account/accounts-bottomsheet.html b/src/client/app/account/accounts-bottomsheet.html index 5a2216a3..4a2428d4 100644 --- a/src/client/app/account/accounts-bottomsheet.html +++ b/src/client/app/account/accounts-bottomsheet.html @@ -4,9 +4,9 @@ - {{ account.accountName }} - {{ account.accountId }} - {{ account.accountCurrency }} + + {{ account.id }} + diff --git a/src/client/app/account/accounts.service.js b/src/client/app/account/accounts.service.js index 5d66a11c..d4973b9d 100644 --- a/src/client/app/account/accounts.service.js +++ b/src/client/app/account/accounts.service.js @@ -49,14 +49,12 @@ } if (!accounts.length) { - angular.merge(account, response.data); + angular.merge(account, response.data.account); account.timestamp = new Date(); - account.unrealizedPlPerc = - account.unrealizedPl / account.balance * 100; - account.netAssetValue = - account.balance + account.unrealizedPl; + account.unrealizedPLPercent = + account.unrealizedPL / account.balance * 100; if (!account.instruments) { $http.post("/api/instruments", { @@ -67,7 +65,8 @@ account.instruments = instruments.data; account.pips = {}; angular.forEach(account.instruments, function (i) { - account.pips[i.instrument] = i.pip; + account.pips[i.name] = + Math.pow(10, i.pipLocation); }); }); } diff --git a/src/client/app/activity/activity.html b/src/client/app/activity/activity.html index c36e8b0e..32d448fb 100644 --- a/src/client/app/activity/activity.html +++ b/src/client/app/activity/activity.html @@ -4,7 +4,6 @@ Market Units Price - Interest Profit Balance Date/Time @@ -20,9 +19,6 @@ {{ activity.instrument }} {{ activity.units | number }} {{ activity.price }} - - {{ activity.interest }} - {{ activity.pl | number:4 }} diff --git a/src/client/app/activity/activity.service.js b/src/client/app/activity/activity.service.js index 1e84a014..7bc52e57 100644 --- a/src/client/app/activity/activity.service.js +++ b/src/client/app/activity/activity.service.js @@ -5,8 +5,9 @@ .module("argo") .factory("activityService", activityService); - activityService.$inject = ["$http", "$q", "sessionService"]; - function activityService($http, $q, sessionService) { + activityService.$inject = ["$http", "$q", + "sessionService", "accountsService"]; + function activityService($http, $q, sessionService, accountsService) { var activities = [], service = { getActivities: getActivities, @@ -16,15 +17,18 @@ return service; function getActivities() { - var deferred = $q.defer(); + var deferred = $q.defer(), + account = accountsService.getAccount(), + lastTransactionID = account.lastTransactionID; sessionService.isLogged().then(function (credentials) { $http.post("/api/transactions", { environment: credentials.environment, token: credentials.token, - accountId: credentials.accountId + accountId: credentials.accountId, + lastTransactionID: lastTransactionID }).then(function (transactions) { - activities = transactions.data; + activities = transactions.data.reverse(); deferred.resolve(activities); }); }); @@ -39,7 +43,6 @@ instrument: activity.instrument, units: activity.units, price: activity.price, - interest: activity.interest, pl: activity.pl, // PROFIT (PIPS) // PROFIT (%) diff --git a/src/client/app/charts/charts.service.js b/src/client/app/charts/charts.service.js index 1d9ff70f..ef23f2bd 100644 --- a/src/client/app/charts/charts.service.js +++ b/src/client/app/charts/charts.service.js @@ -20,7 +20,6 @@ var instrument = opt && opt.instrument || "EUR_USD", granularity = opt && opt.granularity || "M5", count = opt && opt.count || 251, - candleFormat = opt && opt.candleFormat || "midpoint", alignmentTimezone = opt && opt.alignmentTimezone || "America/New_York", dailyAlignment = opt && opt.dailyAlignment || "0"; @@ -31,7 +30,6 @@ instrument: instrument, granularity: granularity, count: count, - candleFormat: candleFormat, alignmentTimezone: alignmentTimezone, dailyAlignment: dailyAlignment }).then(function (candles) { diff --git a/src/client/app/charts/order-dialog.js b/src/client/app/charts/order-dialog.js index d06987e2..483bb791 100644 --- a/src/client/app/charts/order-dialog.js +++ b/src/client/app/charts/order-dialog.js @@ -18,7 +18,7 @@ vm.changeMarket = changeMarket; vm.changeMeasure = changeMeasure; - vm.type = "market"; + vm.type = "MARKET"; vm.side = params.side; vm.instruments = params.instruments; vm.selectedInstrument = params.selectedInstrument; @@ -56,18 +56,19 @@ return; } - fixed = (pips[vm.selectedInstrument].match(/0/g) || []).length; + fixed = ((pips[vm.selectedInstrument] + "") + .match(/0/g) || []).length; vm.measure = "price"; vm.step = parseFloat(pips[vm.selectedInstrument]); if (vm.side === "buy") { - vm.quote = price && price.ask; + vm.quote = parseFloat(price && price.ask); vm.takeProfit = parseFloat((vm.quote + vm.step * 10) .toFixed(fixed)); vm.stopLoss = parseFloat((vm.quote - vm.step * 10) .toFixed(fixed)); } else { - vm.quote = price && price.bid; + vm.quote = parseFloat(price && price.bid); vm.takeProfit = parseFloat((vm.quote - vm.step * 10) .toFixed(fixed)); vm.stopLoss = parseFloat((vm.quote + vm.step * 10) @@ -102,7 +103,10 @@ vm.answer = function (action) { var order = {}, isBuy = vm.side === "buy", - isMeasurePips = vm.measure === "pips"; + isMeasurePips = vm.measure === "pips", + fixed = ((pips[vm.selectedInstrument] + "") + .match(/0/g) || []).length; + $mdDialog.hide(action); @@ -111,63 +115,81 @@ order.instrument = vm.selectedInstrument; order.units = vm.units; + if (vm.units && !isBuy) { + order.units = "-" + order.units; + } + order.side = vm.side; order.type = vm.type; - if (order.type === "limit") { + if (order.type === "LIMIT") { order.price = vm.quote; order.expiry = new Date(Date.now() + vm.selectedExpire); } if (isMeasurePips) { if (vm.isLowerBound) { - order.lowerBound = parseFloat(vm.quote - - vm.step * vm.lowerBound); + order.priceBound = + parseFloat(vm.quote - vm.step * vm.lowerBound) + ""; } if (vm.isUpperBound) { - order.upperBound = parseFloat(vm.quote + - vm.step * vm.lowerBound); + order.priceBound = + parseFloat(vm.quote + vm.step * vm.upperBound) + ""; } if (isBuy) { if (vm.isTakeProfit) { - order.takeProfit = parseFloat(vm.quote + - vm.step * vm.takeProfit); + order.takeProfitOnFill = {}; + order.takeProfitOnFill.price = + parseFloat(vm.quote + vm.step * vm.takeProfit) + ""; } if (vm.isStopLoss) { - order.stopLoss = parseFloat(vm.quote - - vm.step * vm.stopLoss); + order.stopLossOnFill = {}; + order.order.takeProfitOnFill.price = + parseFloat(vm.quote - vm.step * vm.stopLoss) + ""; } } else { if (vm.isTakeProfit) { - order.takeProfit = parseFloat(vm.quote - - vm.step * vm.takeProfit); + order.takeProfitOnFill = {}; + order.takeProfitOnFill.price = + parseFloat(vm.quote - vm.step * vm.takeProfit) + ""; } if (vm.isStopLoss) { - order.stopLoss = parseFloat(vm.quote + - vm.step * vm.stopLoss); + order.stopLossOnFill = {}; + order.order.takeProfitOnFill.price = + parseFloat(vm.quote + vm.step * vm.stopLoss) + ""; } } } else { if (vm.isLowerBound) { - order.lowerBound = vm.lowerBound; + order.priceBound = vm.lowerBound + ""; } if (vm.isUpperBound) { - order.upperBound = vm.upperBound; + order.priceBound = vm.upperBound + ""; } if (vm.isTakeProfit) { - order.takeProfit = vm.takeProfit; + order.takeProfitOnFill = {}; + order.takeProfitOnFill.price = vm.takeProfit + ""; } if (vm.isStopLoss) { - order.stopLoss = vm.stopLoss; + order.stopLossOnFill = {}; + order.stopLossOnFill.price = vm.stopLoss + ""; } } if (vm.isTrailingStop) { - order.trailingStop = vm.trailingStop; + order.trailingStopLossOnFill = {}; + if (isBuy) { + order.trailingStopLossOnFill.distance = + (vm.quote - vm.step * vm.trailingStop).toFixed(fixed); + } else { + order.trailingStopLossOnFill.distance = + (vm.quote + vm.step * vm.trailingStop).toFixed(fixed); + } } if (action === "submit") { ordersService.putOrder(order).then(function (transaction) { var opened, + side, message; if (transaction.code && transaction.message) { @@ -175,14 +197,19 @@ transaction.code + " " + transaction.message; + toastService.show(message); + } else if (transaction.errorMessage) { + message = "ERROR " + transaction.errorMessage; + toastService.show(message); } else { - opened = transaction.tradeOpened || - transaction.orderOpened; - message = opened.side + " " + - transaction.instrument + + opened = transaction.orderFillTransaction || + transaction.orderFillTransaction; + side = opened.units > 0 ? "buy" : "sell"; + message = side + " " + + opened.instrument + " #" + opened.id + - " @" + transaction.price + + " @" + opened.price + " for " + opened.units; toastService.show(message); diff --git a/src/client/app/components/charts.directive.js b/src/client/app/components/charts.directive.js index d6da4b45..bc19b2e1 100644 --- a/src/client/app/components/charts.directive.js +++ b/src/client/app/components/charts.directive.js @@ -53,6 +53,8 @@ if (tick && data && lastHistUpdate !== nextHistUpdate) { data.shift(); + tick.bid = parseFloat(tick.bid); + tick.ask = parseFloat(tick.ask); midPrice = (tick.bid + tick.ask) / 2; feedVolume = 0; data.push({ @@ -73,6 +75,8 @@ feedVolume += 1; } + tick.bid = parseFloat(tick.bid); + tick.ask = parseFloat(tick.ask); midPrice = (tick.bid + tick.ask) / 2; lastData = data && data[data.length - 1]; @@ -332,8 +336,8 @@ svg.append("g").attr("class", "tradearrow"); myTrades = scope.trades.map(function (trade) { return { - date: new Date(trade.time), - type: trade.side, + date: new Date(trade.openTime), + type: trade.currentUnits > 0 ? "buy" : "sell", price: trade.price }; }); diff --git a/src/client/app/exposure/exposure.js b/src/client/app/exposure/exposure.js index f251b1c1..e5099595 100644 --- a/src/client/app/exposure/exposure.js +++ b/src/client/app/exposure/exposure.js @@ -26,15 +26,8 @@ exps[legs[0]] = exps[legs[0]] || 0; exps[legs[1]] = exps[legs[1]] || 0; - if (trade.side === "buy") { - exps[legs[0]] += trade.units; - exps[legs[1]] -= trade.units * trade.price; - } - if (trade.side === "sell") { - exps[legs[0]] -= trade.units; - exps[legs[1]] += trade.units * trade.price; - } - + exps[legs[0]] += parseInt(trade.currentUnits, 10); + exps[legs[1]] -= trade.currentUnits * trade.price; }); Object.keys(exps).forEach(function (exp) { diff --git a/src/client/app/header/header.js b/src/client/app/header/header.js index 1e7c5333..01badd52 100644 --- a/src/client/app/header/header.js +++ b/src/client/app/header/header.js @@ -63,7 +63,7 @@ locals: {accounts: accounts}, targetEvent: event }).then(function (accountSelected) { - vm.accountId = accountSelected.accountId; + vm.accountId = accountSelected.id; sessionService.setCredentials({ environment: vm.environment, @@ -98,8 +98,8 @@ var allInstrs = accountsService.getAccount().instruments; angular.forEach(allInstrs, function (instrument) { - if (!instrs.hasOwnProperty(instrument.instrument)) { - instrs[instrument.instrument] = false; + if (!instrs.hasOwnProperty(instrument.name)) { + instrs[instrument.name] = false; } }); diff --git a/src/client/app/orders/orders.service.js b/src/client/app/orders/orders.service.js index cce5b526..78f5b908 100644 --- a/src/client/app/orders/orders.service.js +++ b/src/client/app/orders/orders.service.js @@ -51,11 +51,10 @@ type: order.type, expiry: order.expiry, price: order.price, - lowerBound: order.lowerBound, - upperBound: order.upperBound, - stopLoss: order.stopLoss, - takeProfit: order.takeProfit, - trailingStop: order.trailingStop + priceBound: order.lowerBound || order.upperBound, + stopLossOnFill: order.stopLossOnFill, + takeProfitOnFill: order.takeProfitOnFill, + trailingStopLossOnFill: order.trailingStopLossOnFill }).then(function (trade) { deferred.resolve(trade.data); }); diff --git a/src/client/app/positions/positions.service.js b/src/client/app/positions/positions.service.js index d7d1478a..b5ebdcd3 100644 --- a/src/client/app/positions/positions.service.js +++ b/src/client/app/positions/positions.service.js @@ -22,7 +22,32 @@ token: credentials.token, accountId: credentials.accountId }).then(function (positions) { - deferred.resolve(positions.data); + var data = []; + + positions.data.forEach(function (position) { + var side, + longUnits, + shortUnits, + units, + avgPrice; + + longUnits = position.long && + parseInt(position.long.units, 10); + shortUnits = position.short && + parseInt(position.short.units, 10); + units = longUnits || shortUnits; + side = position.longUnits > 0 ? "buy" : "sell"; + avgPrice = (longUnits && position.long.averagePrice) + || (shortUnits && position.short.averagePrice); + + data.push({ + side: side, + instrument: position.instrument, + units: units, + avgPrice: avgPrice + }); + }); + deferred.resolve(data); }); }); diff --git a/src/client/app/services/stream.service.js b/src/client/app/services/stream.service.js index e17ac427..9fac6348 100644 --- a/src/client/app/services/stream.service.js +++ b/src/client/app/services/stream.service.js @@ -33,7 +33,9 @@ ws.onmessage = function (event) { var data, + isTick, tick, + isTransaction, transaction, refreshPlugins; @@ -41,17 +43,25 @@ try { data = angular.fromJson(event.data); - tick = data.tick; - transaction = data.transaction; + isTick = data.closeoutAsk && data.closeoutBid; + isTransaction = data.accountBalance; refreshPlugins = data.refreshPlugins; - if (tick) { + if (isTick) { + tick = { + time: data.time, + instrument: data.instrument, + ask: data.closeoutAsk, + bid: data.closeoutBid + }; + quotesService.updateTick(tick); tradesService.updateTrades(tick); ordersService.updateOrders(tick); } - if (transaction) { + if (isTransaction) { + transaction = data; activityService.addActivity(transaction); tradesService.refresh(); diff --git a/src/client/app/trades/trades.html b/src/client/app/trades/trades.html index beb96205..5ae18f9a 100644 --- a/src/client/app/trades/trades.html +++ b/src/client/app/trades/trades.html @@ -19,10 +19,10 @@ {{ trade.side }} {{ trade.id }} {{ trade.instrument }} - {{ trade.units | number }} - {{ trade.stopLoss }} - {{ trade.takeProfit }} - {{ trade.trailingStop }} + {{ trade.currentUnits | number }} + {{ trade.stopLossOrder.price }} + {{ trade.takeProfitOrder.price }} + {{ trade.trailingStopLossOrder.distance }} {{ trade.price }} {{ trade.current }} {{ trade.profitPips | number:1}} diff --git a/src/client/app/trades/trades.js b/src/client/app/trades/trades.js index f3fc3112..06110bf6 100644 --- a/src/client/app/trades/trades.js +++ b/src/client/app/trades/trades.js @@ -32,11 +32,11 @@ $mdDialog.show(confirm).then(function () { tradesService.closeTrade(id).then(function (trade) { var message = "Closed " + - trade.side + " " + + (trade.units > 0 ? "sell " : "buy ") + trade.instrument + " #" + trade.id + " @" + trade.price + - " P&L " + trade.profit; + " P&L " + trade.pl; toastService.show(message); }, function (err) { diff --git a/src/client/app/trades/trades.service.js b/src/client/app/trades/trades.service.js index c1577ff4..857b7cc0 100644 --- a/src/client/app/trades/trades.service.js +++ b/src/client/app/trades/trades.service.js @@ -63,16 +63,18 @@ pips = account.pips; trades.forEach(function (trade, index) { - var current; + var current, + side; if (trade.instrument === tick.instrument) { + side = trade.currentUnits > 0 ? "buy" : "sell"; - if (trade.side === "buy") { + if (side === "buy") { current = tick.bid; trades[index].profitPips = ((current - trade.price) / pips[trade.instrument]); } - if (trade.side === "sell") { + if (side === "sell") { current = tick.ask; trades[index].profitPips = ((trade.price - current) / pips[trade.instrument]); diff --git a/src/client/test/specs/accounts.service.spec.js b/src/client/test/specs/accounts.service.spec.js index 7a8d7b17..5a002c7f 100644 --- a/src/client/test/specs/accounts.service.spec.js +++ b/src/client/test/specs/accounts.service.spec.js @@ -27,17 +27,15 @@ describe("accountsService", function () { $httpBackend .when("POST", api) .respond({ - accountCurrency: "USD", - accountId: 7442890, - accountName: "Primary", - balance: 110410.5028, - marginAvail: 110394.9676, - marginRate: 0.05, - marginUsed: 18.1671, - openOrders: 0, - openTrades: 3, - realizedPl: -1983.78, - unrealizedPl: 2.6319 + account: { + currency: "USD", + accountId: 7442890, + balance: 110410.5028, + marginAvailable: 110394.9676, + marginCallMarginUsed: 18.1671, + realizedPL: -1983.78, + unrealizedPL: 2.6319 + } }); $httpBackend @@ -45,9 +43,9 @@ describe("accountsService", function () { .respond([ { displayName: "EUR/USD", - instrument: "EUR_USD", - maxTradeUnits: 10000000, - pip: "0.0001" + name: "EUR_USD", + maximumOrderUnits: "100000000", + pipLocation: -4 } ]); @@ -68,20 +66,15 @@ describe("accountsService", function () { }).then(function () { var account = accountsService.getAccount(); - assert.equal("USD", account.accountCurrency); + assert.equal("USD", account.currency); assert.equal("7442890", account.accountId); - assert.equal("Primary", account.accountName); assert.equal(110410.5028, account.balance); - assert.equal(110394.9676, account.marginAvail); - assert.equal(0.05, account.marginRate); - assert.equal(18.1671, account.marginUsed); - assert.equal(0, account.openOrders); - assert.equal(3, account.openTrades); - assert.equal(-1983.78, account.realizedPl); - assert.equal(2.6319, account.unrealizedPl); + assert.equal(110394.9676, account.marginAvailable); + assert.equal(18.1671, account.marginCallMarginUsed); + assert.equal(-1983.78, account.realizedPL); + assert.equal(2.6319, account.unrealizedPL); assert.isDefined(account.timestamp); - assert.equal(0.0023837406163863604, account.unrealizedPlPerc); - assert.equal(110413.1347, account.netAssetValue); + assert.equal(0.0023837406163863604, account.unrealizedPLPercent); }); $httpBackend.flush(); }); diff --git a/src/client/test/specs/activity.service.spec.js b/src/client/test/specs/activity.service.spec.js index 24d0dd9c..24608d83 100644 --- a/src/client/test/specs/activity.service.spec.js +++ b/src/client/test/specs/activity.service.spec.js @@ -25,27 +25,25 @@ describe("activityService", function () { $httpBackend .when("POST", api) - .respond({ - "data": [ - { + .respond([ + { + "id": 176403879, + "accountId": 6765103, + "time": "2014-04-07T18:31:05Z", + "type": "MARKET_ORDER_CREATE", + "instrument": "EUR_USD", + "units": 2, + "side": "buy", + "price": 1.25325, + "pl": 0, + "interest": 0, + "accountBalance": 100000, + "tradeOpened": { "id": 176403879, - "accountId": 6765103, - "time": "2014-04-07T18:31:05Z", - "type": "MARKET_ORDER_CREATE", - "instrument": "EUR_USD", - "units": 2, - "side": "buy", - "price": 1.25325, - "pl": 0, - "interest": 0, - "accountBalance": 100000, - "tradeOpened": { - "id": 176403879, - "units": 2 - } + "units": 2 } - ] - }); + } + ]); $httpBackend.whenGET(/^app\/.*\.html$/).respond(200); })); @@ -57,9 +55,7 @@ describe("activityService", function () { describe("getActivities", function () { it("test", function () { - activityService.getActivities().then(function (response) { - var activities = response.data; - + activityService.getActivities().then(function (activities) { assert.lengthOf(activities, 1); assert.equal("176403879", activities[0].id); diff --git a/src/client/test/specs/exposure.spec.js b/src/client/test/specs/exposure.spec.js index 6452ed4a..781b3342 100644 --- a/src/client/test/specs/exposure.spec.js +++ b/src/client/test/specs/exposure.spec.js @@ -14,14 +14,12 @@ describe("Exposure", function () { return [ { instrument: "EUR_USD", - side: "buy", - units: 100, + currentUnits: 100, price: 1.2345 }, { instrument: "GPB_USD", - side: "buy", - units: 200, + currentUnits: 200, price: 1.4678 } ]; diff --git a/src/client/test/specs/positions.service.spec.js b/src/client/test/specs/positions.service.spec.js index 831dfd34..68b7905b 100644 --- a/src/client/test/specs/positions.service.spec.js +++ b/src/client/test/specs/positions.service.spec.js @@ -28,21 +28,24 @@ describe("positionsService", function () { .respond([ { "instrument": "EUR_USD", - "units": 4741, - "side": "buy", - "avgPrice": 1.3626 + "long": { + "units": 4741, + "averagePrice": 1.3626 + } }, { "instrument": "USD_CAD", - "units": 30, - "side": "sell", - "avgPrice": 1.11563 + "short": { + "units": -30, + "averagePrice": 1.11563 + } }, { "instrument": "USD_JPY", - "units": 88, - "side": "buy", - "avgPrice": 102.455 + "long": { + "units": 88, + "averagePrice": 102.455 + } } ]); @@ -60,7 +63,7 @@ describe("positionsService", function () { assert.lengthOf(positions, 3); assert.equal("USD_CAD", positions[1].instrument); - assert.equal(30, positions[1].units); + assert.equal(-30, positions[1].units); assert.equal("sell", positions[1].side); assert.equal(1.11563, positions[1].avgPrice); }); diff --git a/src/server/routes/api.js b/src/server/routes/api.js index 2df14c1d..6a0396bc 100644 --- a/src/server/routes/api.js +++ b/src/server/routes/api.js @@ -55,7 +55,7 @@ function getAccounts(req, response) { return response.sendStatus(400); } - url = config.getUrl(req.body.environment, "api") + "/v1/accounts"; + url = config.getUrl(req.body.environment, "api") + "/v3/accounts"; throttledRequest({ "url": url, @@ -77,7 +77,7 @@ function getAccount(req, response) { credentials.accountId = req.body.accountId; throttledRequest({ - "url": config.getUrl(req.body.environment, "api") + "/v1/accounts/" + + "url": config.getUrl(req.body.environment, "api") + "/v3/accounts/" + req.body.accountId, "headers": { "Authorization": "Bearer " + req.body.token @@ -93,10 +93,8 @@ function getInstruments(req, response) { } throttledRequest({ - "url": config.getUrl(req.body.environment, "api") + "/v1/instruments", - "qs": { - accountId: req.body.accountId - }, + "url": config.getUrl(req.body.environment, "api") + "/v3/accounts/" + + req.body.accountId + "/instruments", "headers": { "Authorization": "Bearer " + req.body.token } @@ -117,12 +115,11 @@ function getCandles(req, response) { token = req.body.token || credentials.token; throttledRequest({ - "url": config.getUrl(environment, "api") + "/v1/candles", + "url": config.getUrl(environment, "api") + "/v3/instruments/" + + req.body.instrument + "/candles", "qs": { - instrument: req.body.instrument, granularity: req.body.granularity, count: req.body.count, - candleFormat: req.body.candleFormat, alignmentTimezone: req.body.alignmentTimezone, dailyAlignment: req.body.dailyAlignment }, @@ -139,10 +136,10 @@ function getCandles(req, response) { candles.forEach(function (candle) { lines += candle.time + "," + - candle.openMid + "," + - candle.highMid + "," + - candle.lowMid + "," + - candle.closeMid + "," + + candle.mid.o + "," + + candle.mid.h + "," + + candle.mid.l + "," + + candle.mid.c + "," + candle.volume + "\n"; }); @@ -165,8 +162,8 @@ function getTrades(req, response) { } throttledRequest({ - "url": config.getUrl(req.body.environment, "api") + "/v1/accounts/" + - req.body.accountId + "/trades", + "url": config.getUrl(req.body.environment, "api") + "/v3/accounts/" + + req.body.accountId + "/openTrades", "headers": { "Authorization": "Bearer " + req.body.token } @@ -181,7 +178,7 @@ function getOrders(req, response) { } throttledRequest({ - "url": config.getUrl(req.body.environment, "api") + "/v1/accounts/" + + "url": config.getUrl(req.body.environment, "api") + "/v3/accounts/" + req.body.accountId + "/orders", "headers": { "Authorization": "Bearer " + req.body.token @@ -197,8 +194,8 @@ function getPositions(req, response) { } throttledRequest({ - "url": config.getUrl(req.body.environment, "api") + "/v1/accounts/" + - req.body.accountId + "/positions", + "url": config.getUrl(req.body.environment, "api") + "/v3/accounts/" + + req.body.accountId + "/openPositions", "headers": { "Authorization": "Bearer " + req.body.token } @@ -208,13 +205,16 @@ function getPositions(req, response) { } function getTransactions(req, response) { + var id; + if (!req.body) { return response.sendStatus(400); } + id = req.body.lastTransactionID > 32 ? req.body.lastTransactionID - 32 : 0; throttledRequest({ - "url": config.getUrl(req.body.environment, "api") + "/v1/accounts/" + - req.body.accountId + "/transactions", + "url": config.getUrl(req.body.environment, "api") + "/v3/accounts/" + + req.body.accountId + "/transactions/sinceid?id=" + id, "headers": { "Authorization": "Bearer " + req.body.token } @@ -283,20 +283,23 @@ function putOrder(req, response) { throttledRequest({ "method": "POST", - "url": config.getUrl(environment, "api") + "/v1/accounts/" + + "url": config.getUrl(environment, "api") + "/v3/accounts/" + accountId + "/orders", - "form": { - instrument: req.body.instrument, - units: req.body.units, - side: req.body.side, - type: req.body.type, - expiry: req.body.expiry, - price: req.body.price, - lowerBound: req.body.lowerBound, - upperBound: req.body.upperBound, - stopLoss: req.body.stopLoss, - takeProfit: req.body.takeProfit, - trailingStop: req.body.trailingStop + "json": true, + "body": { + order: { + instrument: req.body.instrument, + units: req.body.units, + side: req.body.side, + type: req.body.type, + expiry: req.body.expiry, + price: req.body.price, + lowerBound: req.body.lowerBound, + upperBound: req.body.upperBound, + stopLossOnFill: req.body.stopLossOnFill, + takeProfitOnFill: req.body.takeProfitOnFill, + trailingStop: req.body.trailingStop + } }, "headers": { "Authorization": "Bearer " + token @@ -312,9 +315,9 @@ function closeOrder(req, response) { } throttledRequest({ - "method": "DELETE", - "url": config.getUrl(req.body.environment, "api") + "/v1/accounts/" + - req.body.accountId + "/orders/" + req.body.id, + "method": "PUT", + "url": config.getUrl(req.body.environment, "api") + "/v3/accounts/" + + req.body.accountId + "/orders/" + req.body.id + "/cancel", "headers": { "Authorization": "Bearer " + req.body.token } @@ -329,14 +332,14 @@ function closeTrade(req, response) { } throttledRequest({ - "method": "DELETE", - "url": config.getUrl(req.body.environment, "api") + "/v1/accounts/" + - req.body.accountId + "/trades/" + req.body.id, + "method": "PUT", + "url": config.getUrl(req.body.environment, "api") + "/v3/accounts/" + + req.body.accountId + "/trades/" + req.body.id + "/close", "headers": { "Authorization": "Bearer " + req.body.token } }, function (err, res, body) { - processApi("closeTrade", err, body, response); + processApi("closeTrade", err, body, response, "orderFillTransaction"); }); } @@ -362,8 +365,10 @@ function processApi(apiName, err, body, response, property) { var obj; try { - body = JSON.parse(body); - if (!err && !body.code) { + if (typeof body === "string") { + body = JSON.parse(body); + } + if (!err && !body.errorCode) { if (property) { obj = body[property]; } else { @@ -372,7 +377,8 @@ function processApi(apiName, err, body, response, property) { response.json(obj); } else { - processApiError(apiName, err, body.code, body.message, response); + processApiError(apiName, err, body.errorCode, + body.errorMessage, response); } } catch (e) { // Discard "incomplete" json diff --git a/src/server/routes/config.js b/src/server/routes/config.js index 6578c06a..3f8fb87f 100644 --- a/src/server/routes/config.js +++ b/src/server/routes/config.js @@ -13,7 +13,6 @@ exports.streamUrl = "/stream"; exports.environment = process.env.OANDA_ENVIRONMENT || "practice"; exports.accessToken = process.env.OANDA_TOKEN || "ACCESS_TOKEN"; exports.accountId = process.env.OANDA_ACCOUNTID || "1234567890"; -exports.sessionId = process.env.OANDA_SESSIONID || "1"; exports.instruments = [ "EUR_USD", diff --git a/src/server/routes/stream.js b/src/server/routes/stream.js index 68c63b17..f93ffba6 100644 --- a/src/server/routes/stream.js +++ b/src/server/routes/stream.js @@ -11,16 +11,18 @@ var WebSocket = require("faye-websocket"), var pricesStreaming, eventsStreaming, + initialSnapshots = [], ws; function start(options, callback) { var environment = options && options.environment || config.environment, accessToken = options && options.accessToken || config.accessToken, accountId = options && options.accountId || config.accountId, - sessionId = options && options.sessionId || config.sessionId, instruments = options && options.instruments || config.instruments, - pricesUrl = config.getUrl(environment, "stream") + "/v1/prices", - eventsUrl = config.getUrl(environment, "stream") + "/v1/events", + pricesUrl = config.getUrl(environment, "stream") + "/v3/accounts/" + + accountId + "/pricing/stream", + eventsUrl = config.getUrl(environment, "stream") + "/v3/accounts/" + + accountId + "/transactions/stream", authHeader = { "Authorization": "Bearer " + accessToken }; @@ -33,17 +35,12 @@ function start(options, callback) { pricesStreaming = request({ "url": pricesUrl, "qs": { - accountId: accountId, - sessionId: sessionId, instruments: instruments.join(",") }, "headers": authHeader }).on("response", function () { eventsStreaming = request({ "url": eventsUrl, - "qs": { - accountIds: accountId - }, "headers": authHeader }).on("response", function () { callback(); @@ -52,13 +49,18 @@ function start(options, callback) { } function processChunk(chunk) { - var data = chunk.toString().split("\r\n").slice(0, -1); + var data = chunk.toString(); if (ws) { - data.forEach(function (el) { - ws.send(el); - plugin.shoutStreaming(el); - }); + if (initialSnapshots.length > 0) { + initialSnapshots.forEach(function () { + ws.send(initialSnapshots.pop()); + }); + } + ws.send(data); + plugin.shoutStreaming(data); + } else { + initialSnapshots.push(data); } }