UNPKG

gdax-sim

Version:

Simulator used to help unit test and back test various Coinbase-Pro (gdax) interactions.

462 lines (448 loc) 15.4 kB
const ApiSim = require("../Lib/ApiSim"); const assert = require("assert"); const marketSellPerams = { product_id: "LTC-USD", size: 1, type: "market", }; const marketBuyPerams = { product_id: "LTC-USD", size: 2, type: "market", }; const badMarketSellPerams = { product_id: "LTC-USD", size: 101, type: "market", }; const badMarketBuyPerams = { product_id: "LTC-USD", size: 500, type: "market", }; const testCandleHighToLow = { time: "Tue Nov 27 2018 03:44:00 GMT-0500 (Eastern Standard Time)", open: 29.35, high: 29.41, low: 29.28, close: 29.3, volume: 58.25595405999999, }; const testCandleLowToHigh = { time: "Tue Nov 27 2018 03:45:00 GMT-0500 (Eastern Standard Time)", open: 29.25, high: 29.38, low: 29.22, close: 29.26, volume: 41.767021490000005, }; const twoCandleArray = [testCandleHighToLow, testCandleLowToHigh]; const limitSellParams = { price: 29.3, size: 0.1, product_id: "LTC-USD", }; const limitBuyPerams = { price: 29.23, size: 0.1, product_id: "LTC-USD", }; describe("#ApiSim Market Orders", () => { describe("#sell", () => { it("does not add 'funds' value to market sells that use size", () => { let Gdax = new ApiSim(); Gdax.sell(marketSellPerams, (err, res, data) => { assert.strictEqual(data.funds, undefined); }); }); it("saves the market order to the user.marketOrders.openSells array", () => { let Gdax = new ApiSim(); Gdax.sell(marketSellPerams, (err, res, data) => { assert.strictEqual(Gdax.user.marketOrders.openSells[0].id, data.id); }); }); it("runs the callback after", (done) => { let Gdax = new ApiSim(); Gdax.sell(marketSellPerams, done); }); it("returns the order in the 'data' attribute of the callback", () => { let Gdax = new ApiSim(); Gdax.sell(marketSellPerams, (err, res, data) => { assert.strictEqual(Gdax.user.marketOrders.openSells[0], data); }); }); it("rejects the order if the user lacks the crypto", () => { let Gdax = new ApiSim(); Gdax.sell(badMarketSellPerams, (err, res, data) => { assert.strictEqual(data.status, "rejected"); }); }); it("deducts the value of the order (size) from the crypto balance", () => { let Gdax = new ApiSim(); Gdax.sell(marketSellPerams, (err, res, data) => { assert.strictEqual(Gdax.user.cryptoBalance, 99); }); }); }); describe("#buy", () => { it("adds the funds value to the market order", () => { let Gdax = new ApiSim(); Gdax.buy(marketBuyPerams, (err, res, data) => { assert.strictEqual(data.funds, Gdax.user.fiatBalance.toString()); }); }); it("saves the market order to the user.marketOrders.openBuys array", () => { let Gdax = new ApiSim(); Gdax.buy(marketBuyPerams, (err, res, data) => { assert.strictEqual(Gdax.user.marketOrders.openBuys[0].id, data.id); }); }); it("runs the callback after", (done) => { let Gdax = new ApiSim(); Gdax.sell(marketBuyPerams, done); }); it("returns the order in the 'data' attribute of the callback", () => { let Gdax = new ApiSim(); Gdax.buy(marketSellPerams, (err, res, data) => { assert.strictEqual(Gdax.user.marketOrders.openBuys[0], data); }); }); it("rejects the order if the user lacks the fiat", () => { let Gdax = new ApiSim(100, 100); Gdax.currentPrice = 35; Gdax.buy(badMarketBuyPerams, (err, res, data) => { assert.strictEqual(data.status, "rejected"); }); }); it("deducts the value of the order (size) from the fiat balance", () => { let Gdax = new ApiSim(); let baseBalance = Gdax.user.fiatBalance; Gdax.buy(marketBuyPerams, (err, res, data) => { assert.strictEqual( Gdax.user.fiatBalance, baseBalance - marketBuyPerams.size * Gdax.currentPrice, ); }); }); }); describe("#fillOrder", () => { let time = "2018-12-06T01:46:01.162000Z"; it("has the proper adjustmets made to the json object", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 2; Gdax.sell(marketSellPerams, (err, res, data) => { Gdax.fillOrder(data.id, data.size, time); let order = Gdax.user.orders[0]; assert.strictEqual(order.settled, true); assert.strictEqual(order.status, "done"); assert.strictEqual( order.executed_value, (marketSellPerams.size * Gdax.currentPrice).toString(), ); assert.strictEqual(order.filled_size, marketSellPerams.size.toString()); assert.strictEqual( order.fill_fees, ( marketSellPerams.size * Gdax.currentPrice * Gdax.taker_fee ).toString(), ); assert.strictEqual(order.done_reason, "filled"); assert.strictEqual(order.done_at, Gdax.currentTime); assert.strictEqual(order.stp, undefined); }); }); it("has the proper adjustmets made to the json object", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 2; Gdax.buy(marketBuyPerams, (err, res, data) => { Gdax.fillOrder(data.id, data.size, time); let order = Gdax.user.orders[0]; assert.strictEqual(order.settled, true); assert.strictEqual(order.status, "done"); assert.strictEqual( order.executed_value, (marketBuyPerams.size * Gdax.currentPrice).toString(), ); assert.strictEqual(order.filled_size, marketBuyPerams.size.toString()); assert.strictEqual( order.fill_fees, ( marketBuyPerams.size * Gdax.currentPrice * Gdax.taker_fee ).toString(), ); assert.strictEqual(order.done_reason, "filled"); assert.strictEqual(order.done_at, Gdax.currentTime); assert.strictEqual(order.stp, undefined); }); }); it("adds the (size * price) - fee of a market sell order to the fiat account", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 2; let target = Gdax.user.fiatBalance + marketSellPerams.size * Gdax.currentPrice * (1 - Gdax.taker_fee); Gdax.sell(marketSellPerams, (err, res, data) => { Gdax.fillOrder(data.id, data.size, time); assert.strictEqual(Gdax.user.fiatBalance, target); }); }); it("adds the (size/price) of a market buy order to the crypto account", () => { let Gdax = new ApiSim(1000, 0); Gdax.currentPrice = 35; let target = Gdax.user.cryptoBalance + marketBuyPerams.size; Gdax.buy(marketBuyPerams, (err, res, data) => { Gdax.fillOrder(data.id, data.size, time); assert.strictEqual(Gdax.user.cryptoBalance, target); }); }); it("subtracts the fee from the fiat account for a market buy", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 35; let target = Gdax.user.fiatBalance - marketBuyPerams.size * Gdax.currentPrice * (1 + Gdax.taker_fee); Gdax.buy(marketBuyPerams, (err, res, data) => { Gdax.fillOrder(data.id, data.size, time); assert.strictEqual(Gdax.user.fiatBalance.toFixed(2), target.toFixed(2)); }); }); it("sell: returns the match first", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 35; Gdax.sell(marketSellPerams, (err, res, data) => { let ret = Gdax.fillOrder(data.id, data.size, time); assert.strictEqual(ret[0].type, "match"); assert.strictEqual(ret[0].taker_order_id, data.id); assert.strictEqual(ret[0].side, data.side); }); }); it("buy: returns the match first", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 35; Gdax.buy(marketBuyPerams, (err, res, data) => { let ret = Gdax.fillOrder(data.id, data.size, time); assert.strictEqual(ret[0].type, "match"); assert.strictEqual(ret[0].taker_order_id, data.id); assert.strictEqual(ret[0].side, data.side); }); }); it("sell: returns the 'done' last", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 35; Gdax.sell(marketSellPerams, (err, res, data) => { let ret = Gdax.fillOrder(data.id, data.size, time); assert.strictEqual(ret[ret.length - 1].type, "done"); }); }); it("buy: returns the 'done' last", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 35; Gdax.buy(marketBuyPerams, (err, res, data) => { let ret = Gdax.fillOrder(data.id, data.size, time); assert.strictEqual(ret[ret.length - 1].type, "done"); }); }); it("sell: the returned 'done' has the proper data", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 35; Gdax.sell(marketSellPerams, (err, res, data) => { let ret = Gdax.fillOrder(data.id, data.size, time); let done = ret[ret.length - 1]; assert.strictEqual(done.side, "sell"); assert(done.order_id, data.id); }); }); it("buy: the returned 'done' has the proper data", () => { let Gdax = new ApiSim(); Gdax.currentPrice = 35; Gdax.buy(marketBuyPerams, (err, res, data) => { let ret = Gdax.fillOrder(data.id, data.size, time); let done = ret[ret.length - 1]; assert.strictEqual(done.side, "buy"); assert(done.order_id, data.id); }); }); it("does nothing if the market order is allready completed", () => { let Gdax = new ApiSim(); Gdax.buy(marketBuyPerams, (err, res, data) => { Gdax.user.orders[0].status = "done"; let ret = Gdax.fillOrder(data.id, data.size, time); assert.strictEqual(ret.length, 0); }); }); }); describe("#backtest", () => { it("sell: completes all market orders at each match", () => { let Gdax = new ApiSim(); Gdax.sell(marketSellPerams); Gdax.websocketClient.on("message", (message) => { assert.strictEqual( Gdax.user.orders.map((x) => x.status).indexOf("pending"), -1, ); }); Gdax.backtest(twoCandleArray); }); it("buy: completes all market orders at each match", () => { let Gdax = new ApiSim(); Gdax.buy(marketBuyPerams); Gdax.websocketClient.on("message", (message) => { assert.strictEqual( Gdax.user.orders.map((x) => x.status).indexOf("pending"), -1, ); }); Gdax.backtest(twoCandleArray); }); it("sell: the price of the market order is the price of the most recent match", () => { let Gdax = new ApiSim(); let order; let checked = false; Gdax.sell(marketSellPerams, (err, res, data) => { order = data.id; }); Gdax.websocketClient.on("message", (message) => { if (message.taker_order_id === order) { if (parseFloat(message.price) === Gdax.currentPrice) { checked = true; } } }); Gdax.backtest(twoCandleArray); assert(checked); }); it("buy: the price of the market order is the price of the most recent match", () => { let Gdax = new ApiSim(); let order; let checked = false; Gdax.buy(marketBuyPerams, (err, res, data) => { order = data.id; }); Gdax.websocketClient.on("message", (message) => { if (message.taker_order_id === order) { if (parseFloat(message.price) === Gdax.currentPrice) { checked = true; } } }); Gdax.backtest(twoCandleArray); assert(checked); }); it("sell: market info is disbatched", () => { let count = 0; countOfMatch = 0; let Gdax = new ApiSim(); Gdax.sell(marketSellPerams, (err, res, data) => { order = data.id; }); Gdax.websocketClient.on("message", (message) => { if (message.taker_order_id === order) { countOfMatch = count; } if (message.order_id === order) { assert.strictEqual(count - 1, countOfMatch); } count++; }); Gdax.backtest(twoCandleArray); }); it("buy: market info is disbatched", () => { let count = 0; let tested1 = false; let tested2 = false; countOfMatch = 0; let Gdax = new ApiSim(); Gdax.buy(marketBuyPerams, (err, res, data) => { order = data.id; }); Gdax.websocketClient.on("message", (message) => { if (message.taker_order_id === order) { tested1 = true; countOfMatch = count; } if (message.order_id === order) { tested2 = true; assert.strictEqual(count - 1, countOfMatch); } count++; }); Gdax.backtest(twoCandleArray); assert(tested1 === true && tested2 === true); }); it("sell: market order info is disbatched before limit order info", () => { let Gdax = new ApiSim(); let count = 0; let limitOrder, marketOrder; let tests = 0; function placeOrder() { Gdax.sell(limitSellParams, (err, res, data) => { limitOrder = data; }); Gdax.sell( { size: 0.1, product_id: "LTC-USD", type: "market", }, (err, res, data) => { marketOrder = data; }, ); } Gdax.websocketClient.on("message", (message) => { if (count === 2) { placeOrder(); } if (count > 2) { if (message.taker_order_id === marketOrder.id) { assert.strictEqual(count, 4); tests++; } if (message.maker_order_id === limitOrder.id) { assert.strictEqual(count, 8); tests++; } } count++; }); Gdax.backtest(twoCandleArray); assert.strictEqual(tests, 2); }); it("buy: market order info is disbatched before limit order info", () => { let Gdax = new ApiSim(); let count = 0; let limitOrder, marketOrder; let tests = 0; function placeOrder() { Gdax.sell(limitSellParams, (err, res, data) => { limitOrder = data; }); Gdax.buy(marketBuyPerams, (err, res, data) => { marketOrder = data; }); } Gdax.websocketClient.on("message", (message) => { if (count === 2) { placeOrder(); } if (count > 2) { if (message.taker_order_id === marketOrder.id) { assert.strictEqual(count, 4); tests++; } if (message.maker_order_id === limitOrder.id) { assert.strictEqual(count, 8); tests++; } } count++; }); Gdax.backtest(twoCandleArray); assert.strictEqual(tests, 2); }); }); });