Initial commit
This commit is contained in:
281
mocks/ranger.js
Normal file
281
mocks/ranger.js
Normal file
@@ -0,0 +1,281 @@
|
||||
const WebSocket = require('ws');
|
||||
const Helpers = require('./helpers')
|
||||
|
||||
const isSubscribed = (streams, routingKey) => {
|
||||
for (const i in streams) {
|
||||
const stream = streams[i];
|
||||
if (routingKey.startsWith(stream) || routingKey.endsWith(stream)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const sendEvent = (ws, routingKey, event) => {
|
||||
try {
|
||||
if (isSubscribed(ws.streams, routingKey)) {
|
||||
const payload = {};
|
||||
payload[routingKey] = event;
|
||||
ws.send(JSON.stringify(payload));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`failed to send ranger message: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
const tickersMock = (ws, markets) => () => {
|
||||
sendEvent(ws, "global.tickers", Helpers.getTickers(markets));
|
||||
};
|
||||
|
||||
const balancesMock = (ws) => () => {
|
||||
sendEvent(ws, "balances", Helpers.getBalances());
|
||||
};
|
||||
|
||||
/*
|
||||
Example: ["btcusd.update",{"asks":[["1000.0","0.1"]],"bids":[]}]
|
||||
*/
|
||||
const orderBookUpdateMock = (ws, marketId) => () => {
|
||||
sendEvent(ws, `${marketId}.update`, Helpers.getDepth(0));
|
||||
};
|
||||
|
||||
// Inremental orderbook support
|
||||
const orderBookSnapshotMock = (ws, marketId) => () => {
|
||||
ws.sequences[marketId] = 1;
|
||||
console.log(`orderBookSnapshotMock called: ${marketId}`);
|
||||
try {
|
||||
if (isSubscribed(ws.streams, `${marketId}.ob-inc`)) {
|
||||
console.log(`orderBookSnapshotMock sending: ${marketId}`);
|
||||
const payload = {};
|
||||
payload[`${marketId}.ob-snap`] = Helpers.getDepth(ws.sequences[marketId]);
|
||||
ws.send(JSON.stringify(payload));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`failed to send ranger message: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
const orderBookIncrementMock = (ws, marketId) => () => {
|
||||
var event = Helpers.getDepthIncrement();
|
||||
event.sequence = ++ws.sequences[marketId];
|
||||
sendEvent(ws, `${marketId}.ob-inc`, event);
|
||||
};
|
||||
|
||||
/*
|
||||
Success order scenario:
|
||||
* Private messages:
|
||||
["order",{"id":758,"at":1546605232,"market":"macarstc","kind":"bid","price":"1.17","state":"wait","remaining_volume":"0.1","origin_volume":"0.1"}]
|
||||
["order",{"id":758,"at":1546605232,"market":"macarstc","kind":"bid","price":"1.17","state":"done","remaining_volume":"0.0","origin_volume":"0.1"}]
|
||||
["trade",{"id":312,"kind":"bid","at":1546605232,"price":"1.17","remaining_volume":"0.1","ask_id":651,"bid_id":758,"market":"macarstc"}]
|
||||
|
||||
* Public messages:
|
||||
["macarstc.trades",{"trades":[{"tid":312,"type":"buy","date":1546605232,"price":"1.17","amount":"0.1"}]}]
|
||||
*/
|
||||
|
||||
// Those functions are the same used in k-line mocked API
|
||||
const minDay = 6;
|
||||
const maxDay = 10;
|
||||
const fakePeriod = 86400;
|
||||
|
||||
const timeToPrice = (time) => {
|
||||
return minDay + (maxDay - minDay) / 2 * (1 + Math.cos((time / fakePeriod) * 2 * Math.PI));
|
||||
};
|
||||
|
||||
const timeToVolume = (time, periodInSeconds) => {
|
||||
return maxDay * 10 / 2 * (1 + Math.cos((time / fakePeriod) * 2 * Math.PI));
|
||||
};
|
||||
|
||||
const kLine = (time, period) => {
|
||||
const periodInSeconds = parseInt(period * 60);
|
||||
const roundedTime = parseInt(time / periodInSeconds) * periodInSeconds;
|
||||
const open = timeToPrice(time);
|
||||
const close = timeToPrice(time + periodInSeconds);
|
||||
const delta = (maxDay - minDay) / fakePeriod * periodInSeconds * 2;
|
||||
const high = Math.max(open, close) + delta;
|
||||
const low = Math.min(open, close) - delta;
|
||||
const volume = timeToVolume(time, periodInSeconds);
|
||||
|
||||
return [roundedTime, open, high, low, close, volume].map(String);
|
||||
}
|
||||
|
||||
let tradeIndex = 100000;
|
||||
let orderIndex = 100;
|
||||
|
||||
const matchedTradesMock = (ws, marketId) => {
|
||||
let kind = "bid";
|
||||
let price = 0.1;
|
||||
let volume = 1000;
|
||||
|
||||
return function () {
|
||||
const shouldPushOrder = Math.random() < 0.1;
|
||||
const orderId = orderIndex++;
|
||||
const tradeId = tradeIndex++;
|
||||
kind = kind == "bid" ? "ask" : "bid";
|
||||
const takerType = Math.random() < 0.5 ? "buy" : "sell";
|
||||
const orderType = Math.random() < 0.5 ? "limit" : "market";
|
||||
price += price > 0.01 ? Math.random() < 0.5 ? +0.01 : -0.01 : 0.01;
|
||||
volume += 50;
|
||||
let bidId = kind == "bid" ? orderId : orderId - 10;
|
||||
let askId = kind == "ask" ? orderId : orderId - 10;
|
||||
let at = parseInt(Date.now() / 1000);
|
||||
let remainingVolume = volume;
|
||||
const executedVolume = volume - remainingVolume;
|
||||
|
||||
const order = {
|
||||
"uuid": orderId + 1000000,
|
||||
"id": orderId,
|
||||
"at": at + 1,
|
||||
"market": marketId,
|
||||
"kind": kind,
|
||||
"price": price,
|
||||
"avg_price": price,
|
||||
"state": "wait",
|
||||
"remaining_volume": volume,
|
||||
"origin_volume": volume,
|
||||
"executed_volume": executedVolume,
|
||||
"side": takerType,
|
||||
"created_at": at,
|
||||
"updated_at": at + 1,
|
||||
"order_type": orderType,
|
||||
"trades_count": 0
|
||||
};
|
||||
|
||||
const privateTrade = {
|
||||
"uuid": orderId + 1000000,
|
||||
"id": tradeId,
|
||||
"price": price,
|
||||
"total": (volume * price).toFixed(4),
|
||||
"amount": volume,
|
||||
"market": marketId,
|
||||
"at": at + 1,
|
||||
"created_at": at,
|
||||
"taker_type": takerType
|
||||
};
|
||||
|
||||
const publicTrade = {
|
||||
"tid": tradeId,
|
||||
"date": at,
|
||||
"taker_type": takerType,
|
||||
"price": price,
|
||||
"amount": volume
|
||||
};
|
||||
|
||||
if (ws.authenticated && shouldPushOrder) {
|
||||
sendEvent(ws, "order", order);
|
||||
|
||||
setTimeout(() => {
|
||||
remainingVolume = volume / (Math.random() + 2);
|
||||
sendEvent(ws, "order", { ...order, "remaining_volume": String(remainingVolume) });
|
||||
|
||||
setTimeout(() => {
|
||||
sendEvent(ws, "order", { ...order, "state": "done", "remaining_volume": "0.0" });
|
||||
sendEvent(ws, "trade", privateTrade);
|
||||
}, 10000);
|
||||
}, 5000);
|
||||
}
|
||||
sendEvent(ws, `${marketId}.trades`, { "trades": [publicTrade] });
|
||||
}
|
||||
};
|
||||
|
||||
const klinesMock = (ws, marketId) => () => {
|
||||
let at = parseInt(Date.now() / 1000);
|
||||
|
||||
sendEvent(ws, `${marketId}.kline-1m`, kLine(at, 1));
|
||||
sendEvent(ws, `${marketId}.kline-5m`, kLine(at, 5));
|
||||
sendEvent(ws, `${marketId}.kline-15m`, kLine(at, 15));
|
||||
sendEvent(ws, `${marketId}.kline-30m`, kLine(at, 30));
|
||||
sendEvent(ws, `${marketId}.kline-1h`, kLine(at, 60));
|
||||
sendEvent(ws, `${marketId}.kline-2h`, kLine(at, 120));
|
||||
sendEvent(ws, `${marketId}.kline-4h`, kLine(at, 240));
|
||||
sendEvent(ws, `${marketId}.kline-6h`, kLine(at, 360));
|
||||
sendEvent(ws, `${marketId}.kline-12h`, kLine(at, 720));
|
||||
sendEvent(ws, `${marketId}.kline-1d`, kLine(at, 1440));
|
||||
sendEvent(ws, `${marketId}.kline-3d`, kLine(at, 4320));
|
||||
sendEvent(ws, `${marketId}.kline-1w`, kLine(at, 10080));
|
||||
};
|
||||
class RangerMock {
|
||||
constructor(port, markets) {
|
||||
this.markets = markets;
|
||||
this.port = port;
|
||||
this.start();
|
||||
}
|
||||
start() {
|
||||
this.wss = new WebSocket.Server({ port: this.port });
|
||||
const url = `ws://0.0.0.0:${this.port}`.green
|
||||
console.log(`Ranger: listening on ${url}`);
|
||||
const ranger = this;
|
||||
this.wss.on('connection', function connection(ws, request) {
|
||||
ranger.initConnection(ws, request);
|
||||
ws.on('message', (message) => ranger.onMessage(ws, message));
|
||||
ws.on('close', () => ranger.closeConnection(ws));
|
||||
});
|
||||
}
|
||||
close() {
|
||||
this.wss.close();
|
||||
}
|
||||
initConnection(ws, request) {
|
||||
ws.authenticated = true;
|
||||
ws.timers = [];
|
||||
ws.streams = [];
|
||||
ws.sequences = {};
|
||||
|
||||
console.log(`Ranger: connection accepted, url: ${request.url}`);
|
||||
this.subscribe(ws, Helpers.getStreamsFromUrl(request.url));
|
||||
ws.timers.push(setInterval(tickersMock(ws, this.markets), 3000));
|
||||
ws.timers.push(setInterval(balancesMock(ws), 3000));
|
||||
this.markets.forEach((name) => {
|
||||
let { baseUnit, quoteUnit, marketId } = Helpers.getMarketInfos(name);
|
||||
ws.timers.push(setInterval(orderBookIncrementMock(ws, marketId), 200));
|
||||
ws.timers.push(setInterval(orderBookUpdateMock(ws, marketId), 2000));
|
||||
ws.timers.push(setInterval(matchedTradesMock(ws, marketId), 10000));
|
||||
ws.timers.push(setInterval(klinesMock(ws, marketId), 2500));
|
||||
});
|
||||
ws.timers.push(setTimeout(() => {sendEvent(ws, "deposit_address", { address: "a4E49HU6CTHyYMmsYt3F1ar1q5W89t3hfQ?dt=1", currencies:["xrp"], type:"create" })}, 15000));
|
||||
}
|
||||
closeConnection() {
|
||||
console.log('Ranger: connection closed');
|
||||
}
|
||||
onMessage(ws, message) {
|
||||
if (message.length === 0)
|
||||
return;
|
||||
try {
|
||||
console.log('Ranger: received message: %s', message);
|
||||
var payload = JSON.parse(message);
|
||||
|
||||
if ("jwt" in payload) {
|
||||
if (payload["jwt"] === "Bearer null") {
|
||||
ws.send(JSON.stringify({ "error": { "message": "Authentication failed." } }));
|
||||
} else {
|
||||
ws.send(JSON.stringify({ "success": { "message": "Authenticated." } }));
|
||||
ws.authenticated = true;
|
||||
}
|
||||
}
|
||||
switch (payload["event"]) {
|
||||
case "subscribe":
|
||||
this.subscribe(ws, payload["streams"]);
|
||||
break;
|
||||
|
||||
case "unsubscribe":
|
||||
this.unsubscribe(ws, payload["streams"]);
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`Ranger: Something went wrong: ${err} (message: ${message})`);
|
||||
}
|
||||
|
||||
}
|
||||
subscribe(ws, streams) {
|
||||
ws.streams = Helpers.unique(ws.streams.concat(streams));
|
||||
console.log(`subcribed to : ${ws.streams}`)
|
||||
this.markets.forEach((name) => {
|
||||
let { marketId } = Helpers.getMarketInfos(name);
|
||||
orderBookSnapshotMock(ws, marketId)();
|
||||
});
|
||||
ws.send(JSON.stringify({ "success": { "message": "subscribed", "streams": ws.streams } }))
|
||||
}
|
||||
unsubscribe(ws, streams) {
|
||||
ws.streams = ws.streams.filter((value) => streams.indexOf(value) === -1);
|
||||
ws.send(JSON.stringify({ "success": { "message": "unsubscribed", "streams": ws.streams } }))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RangerMock;
|
||||
Reference in New Issue
Block a user