368 lines
18 KiB
JavaScript
368 lines
18 KiB
JavaScript
// Source: https://github.com/coss-exchange/trading-api-wrapper-node-js/blob/master/index.js
|
|
// Documentation: https://api.coss.io/v1/spec
|
|
|
|
const MODULE_NAME = "COSS Client"
|
|
const request = require('superagent');
|
|
const querystring = require('querystring');
|
|
const HttpMethod = { POST: 1, GET: 2, DELETE: 3 };
|
|
const util = require('util');
|
|
|
|
const HOST_URL = "https://trade.coss.io/c/api/v1";
|
|
const HOST_URL2 = "https://engine.coss.io/api/v1";
|
|
const EXCHANGE_HOST = "https://exchange.coss.io/api";
|
|
|
|
//WRAPPER STARTS HERE (Commenting out stuff that coss wrote which should be part of bot.js (the actual trading bot) rather than index.js (the wrapper core functionality))
|
|
let Coss = (keyVaultAPI, logger) => {
|
|
|
|
//HTTPS Private Request
|
|
async function transmit_encrypted_request(method, url, payload) {
|
|
var payload_json_str = method == HttpMethod.GET ? querystring.stringify(payload) : JSON.stringify(payload)
|
|
var sign = await transmit_payload_signature_get(payload_json_str)
|
|
var req_headers = transmit_signed_header_get(sign)
|
|
|
|
logger.write(MODULE_NAME, `sending private message to ${url}`)
|
|
|
|
if (method == HttpMethod.POST) {
|
|
let response = await request.post(url).set(req_headers).send(payload_json_str)
|
|
return (response.body);
|
|
|
|
} else if (method == HttpMethod.DELETE) {
|
|
let response = await request.delete(url).set(req_headers).send(payload_json_str)
|
|
return (response.body);
|
|
|
|
} else if (method == HttpMethod.GET) {
|
|
let response = await request.get(url).set(req_headers).query(payload_json_str)
|
|
return (response.body);
|
|
}
|
|
}
|
|
|
|
function transmit_signed_header_get(sign) {
|
|
var req_headers = {
|
|
'Content-Type': 'application/json',
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'Authorization': sign.Key,
|
|
'Signature': sign.Sign,
|
|
}
|
|
return req_headers
|
|
}
|
|
|
|
//AUTHBUILDER
|
|
async function transmit_payload_signature_get(payload_json_str) {
|
|
keyVaultAPI.signTransaction[util.promisify.custom] = n => new Promise((resolve, reject) => {
|
|
keyVaultAPI.signTransaction(payload_json_str, (result, details) => {
|
|
if (result === undefined) {
|
|
logger.write(MODULE_NAME, details)
|
|
reject(global.DEFAULT_FAIL_RESPONSE)
|
|
} else {
|
|
resolve(result)
|
|
}
|
|
})
|
|
})
|
|
const signTransaction = util.promisify(keyVaultAPI.signTransaction)
|
|
return await signTransaction(payload_json_str)
|
|
}
|
|
|
|
//HTTPS Public Request
|
|
async function publicRequest(url) {
|
|
let response = await request.get(url)
|
|
return response.body;
|
|
}
|
|
|
|
|
|
return {
|
|
//PLACE LIMIT ORDER
|
|
placeLimitOrder: async (params = {}) => {
|
|
if (!params.Symbol) {
|
|
return Promise.reject("placeLimitOrder(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("placeLimitOrder(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
} else if (!params.Side) {
|
|
return Promise.reject("placeLimitOrder(), You must supply a valid Side, e.g. 'BUY' or 'SELL'!");
|
|
} else if (params.Side && params.Side !== 'Buy' && params.Side !== 'Sell') {
|
|
return Promise.reject("placeLimitOrder(), You must supply a valid Side, e.g. 'BUY' or 'SELL'!");
|
|
} else if (!params.Price || !params.Amount) {
|
|
return Promise.reject("placeLimitOrder(), You must supply a valid Price and Amount, e.g. Price: '0.00000034' or Amount: '123.00000000'!");
|
|
//} else if (typeof params.Price !== 'number' || typeof params.Amount !== 'number') {
|
|
//return Promise.reject("placeLimitOrder(), You must supply a valid Price and Amount as a number, e.g. Price: '0.00000034' or Amount: '123.00000000'!");
|
|
}
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'order_symbol': params.Symbol,
|
|
'order_price': params.Price.toString(),
|
|
'order_side': params.Side,
|
|
'order_size': params.Amount.toString(),
|
|
'type': 'limit',
|
|
'timestamp': now,
|
|
'recvWindow': 5000
|
|
}
|
|
let url = HOST_URL + "/order/add";
|
|
return transmit_encrypted_request(HttpMethod.POST, url, query_params)
|
|
},
|
|
|
|
//PLACE MARKET ORDER
|
|
placeMarketOrder: async (params = {}) => {
|
|
if (!params.Symbol) {
|
|
return Promise.reject("placeMarketOrder(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("placeMarketOrder(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
} else if (!params.Side) {
|
|
return Promise.reject("placeMarketOrder(), You must supply a valid Side, e.g. 'BUY' or 'SELL'!");
|
|
} else if (params.Side && params.Side !== 'Buy' && params.Side !== 'Sell') {
|
|
return Promise.reject("placeMarketOrder(), You must supply a valid Side, e.g. 'BUY' or 'SELL'!");
|
|
} else if (!params.Amount) {
|
|
return Promise.reject("placeMarketOrder(), You must supply a valid Amount, e.g. Amount: '123.00000000'!");
|
|
} else if (typeof params.Amount !== 'number') {
|
|
return Promise.reject("placeMarketOrder(), You must supply a valid Amount as a number, e.g. Amount: '123.00000000'!");
|
|
}
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'order_symbol': params.Symbol,
|
|
'order_side': params.Side,
|
|
'order_size': params.Amount.toString(),
|
|
'type': 'market',
|
|
'timestamp': now,
|
|
'recvWindow': 5000
|
|
}
|
|
let url = HOST_URL + "/order/add";
|
|
return transmit_encrypted_request(HttpMethod.POST, url, query_params)
|
|
},
|
|
|
|
//Get Account Balances
|
|
getAccountBalances: async (params = {}) => {
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'recvWindow': 5000,
|
|
'timestamp': now
|
|
}
|
|
let url = HOST_URL + "/account/balances";
|
|
return transmit_encrypted_request(HttpMethod.GET, url, query_params)
|
|
},
|
|
|
|
//Get Account Details
|
|
getAccountDetails: async (params = {}) => {
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'recvWindow': 5000,
|
|
'timestamp': now
|
|
}
|
|
let url = HOST_URL + "/account/details";
|
|
return transmit_encrypted_request(HttpMethod.GET, url, query_params)
|
|
},
|
|
|
|
//CANCEL ORDER BY ID #
|
|
cancelOrder: async (params = {}) => {
|
|
if (!params.ID) {
|
|
return Promise.reject("cancelOrder(), You must supply a valid order_ID to cancel, e.g. '9e5ae4dd-3369-401d-81f5-dff985e1c4e7'!");
|
|
} else if (params.ID && typeof params.ID !== 'string') {
|
|
return Promise.reject("cancelOrder(), You must supply a valid order_ID to cancel as a string, e.g. '9e5ae4dd-3369-401d-81f5-dff985e1c4e7'!");
|
|
} else if (!params.Symbol) {
|
|
return Promise.reject("cancelOrder(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("cancelOrder(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
}
|
|
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'order_id': params.ID,
|
|
'order_symbol': params.Symbol,
|
|
'timestamp': now,
|
|
'recvWindow': 5000
|
|
}
|
|
let url = HOST_URL + "/order/cancel";
|
|
return transmit_encrypted_request(HttpMethod.DELETE, url, query_params)
|
|
},
|
|
|
|
//GET ORDER DETAILS
|
|
getOrderDetails: async (params = {}) => {
|
|
if (!params.ID) {
|
|
return Promise.reject("getOrderDetails(), You must supply a valid order_ID to cancel, e.g. '9e5ae4dd-3369-401d-81f5-dff985e1c4e7'!");
|
|
} else if (params.ID && typeof params.ID !== 'string') {
|
|
return Promise.reject("getOrderDetails(), You must supply a valid order_ID to cancel as a string, e.g. '9e5ae4dd-3369-401d-81f5-dff985e1c4e7'!");
|
|
}
|
|
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'order_id': params.ID,
|
|
'timestamp': now,
|
|
'recvWindow': 5000
|
|
}
|
|
let url = HOST_URL + "/order/details";
|
|
return transmit_encrypted_request(HttpMethod.POST, url, query_params)
|
|
},
|
|
|
|
//GET TRADE DETAILS
|
|
getTradeDetails: async (params = {}) => {
|
|
if (!params.ID) {
|
|
return Promise.reject("getTradeDetails(), You must supply a valid order_ID to getTradeDetails, e.g. '9e5ae4dd-3369-401d-81f5-dff985e1c4e7'!");
|
|
} else if (params.ID && typeof params.ID !== 'string') {
|
|
return Promise.reject("getTradeDetails(), You must supply a valid order_ID to getTradeDetails as a string, e.g. '9e5ae4dd-3369-401d-81f5-dff985e1c4e7'!");
|
|
}
|
|
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'order_id': params.ID,
|
|
'timestamp': now,
|
|
'recvWindow': 5000
|
|
}
|
|
let url = HOST_URL + "/order/trade-detail";
|
|
return transmit_encrypted_request(HttpMethod.POST, url, query_params)
|
|
},
|
|
|
|
//GET OPEN ORDERS
|
|
getOpenOrders: async (params = {}) => {
|
|
if (!params.Limit) {
|
|
return Promise.reject("getOpenOrders(), You must supply a valid Limit for your open orders, e.g. '10'!");
|
|
} else if (params.Limit && typeof params.Limit !== 'number') {
|
|
return Promise.reject("getOpenOrders(), You must supply a valid Limit as a number for your open orders, e.g. '10'!");
|
|
} else if (!params.Symbol) {
|
|
return Promise.reject("getOpenOrders(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("getOpenOrders(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
}
|
|
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'limit': params.Limit.toString(),
|
|
'page': 0,
|
|
'symbol': params.Symbol,
|
|
'timestamp': now,
|
|
'recvWindow': 5000
|
|
}
|
|
let url = HOST_URL + "/order/list/open";
|
|
return transmit_encrypted_request(HttpMethod.POST, url, query_params)
|
|
},
|
|
|
|
//GET COMPLETED ORDERS
|
|
getCompletedOrders: async (params = {}) => {
|
|
if (!params.Limit) {
|
|
return Promise.reject("getCompletedOrders(), You must supply a valid Limit for your open orders, e.g. '10'!");
|
|
} else if (params.Limit && typeof params.Limit !== 'number') {
|
|
return Promise.reject("getCompletedOrders(), You must supply a valid Limit as a number for your open orders, e.g. '10'!");
|
|
} else if (!params.Symbol) {
|
|
return Promise.reject("getCompletedOrders(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("getCompletedOrders(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
}
|
|
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'limit': params.Limit.toString(),
|
|
'page': 0,
|
|
'symbol': params.Symbol,
|
|
'timestamp': now,
|
|
'recvWindow': 5000
|
|
}
|
|
let url = HOST_URL + "/order/list/completed";
|
|
return transmit_encrypted_request(HttpMethod.POST, url, query_params)
|
|
},
|
|
|
|
//GET ALL USER ORDERS
|
|
getAllOrders: async (params = {}) => {
|
|
if (!params.Limit) {
|
|
return Promise.reject("getAllOrders(), You must supply a valid Limit for your open orders, e.g. '10'!");
|
|
} else if (params.Limit && typeof params.Limit !== 'number') {
|
|
return Promise.reject("getAllOrders(), You must supply a valid Limit as a number for your open orders, e.g. '10'!");
|
|
} else if (!params.Symbol) {
|
|
return Promise.reject("getAllOrders(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("getAllOrders(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
} else if (!params.ID) {
|
|
return Promise.reject("getAllOrders(), You must supply a valid account ID");
|
|
} else if (params.ID && typeof params.ID !== 'string') {
|
|
return Promise.reject("getAllOrders(), You must supply a valid account ID as a string");
|
|
}
|
|
|
|
var now = new Date().getTime()
|
|
var query_params = {
|
|
'symbol': params.Symbol,
|
|
'from_id': params.ACCT_ID,
|
|
'limit': params.Limit,
|
|
'timestamp': now,
|
|
'recvWindow': 5000
|
|
}
|
|
let url = HOST_URL + "/order/list/all";
|
|
return transmit_encrypted_request(HttpMethod.POST, url, query_params)
|
|
},
|
|
|
|
|
|
//Public API Calls (don't need to be signed)
|
|
//get Market Price
|
|
getMarketPrice: async (params = {}) => {
|
|
if (!params.Symbol) {
|
|
return Promise.reject("getMarketPrice(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("getMarketPrice(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
}
|
|
|
|
let url = HOST_URL + "/market-price/" + '?symbol=' + params.Symbol;
|
|
|
|
|
|
return publicRequest(url);
|
|
},
|
|
|
|
//get Market Price
|
|
getPairDepth: async (params = {}) => {
|
|
if (!params.Symbol) {
|
|
return Promise.reject("getPairDepth(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("getPairDepth(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
}
|
|
logger.write(MODULE_NAME, HOST_URL2 + "/dp" + '?symbol=' + params.Symbol);
|
|
let url = HOST_URL2 + "/dp" + '?symbol=' + params.Symbol;
|
|
|
|
|
|
return publicRequest(url);
|
|
},
|
|
|
|
//get Market Price
|
|
getMarketSides: async (params = {}) => {
|
|
if (!params.Symbol) {
|
|
return Promise.reject("getMarketSides(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("getMarketSides(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
}
|
|
logger.write(MODULE_NAME, HOST_URL2 + "/dp" + '?symbol=' + params.Symbol);
|
|
let url = HOST_URL2 + "/dp" + '?symbol=' + params.Symbol;
|
|
|
|
let pairDepth = await publicRequest(url);
|
|
let marketSides = [[pairDepth.bids[0][0], pairDepth.bids[0][1]], [pairDepth.asks[0][0], pairDepth.asks[0][1]]];
|
|
|
|
return (marketSides)
|
|
},
|
|
|
|
//get Exchange Info
|
|
getExchangeInfo: async () => {
|
|
logger.write(MODULE_NAME, HOST_URL + "/exchange-info");
|
|
let url = HOST_URL + "/exchange-info";
|
|
|
|
return publicRequest(url);
|
|
},
|
|
|
|
//get Market Summary (NOT WORKING ATM)
|
|
getMarketSummary: async (params = {}) => {
|
|
if (!params.Symbol) {
|
|
return Promise.reject("getMarketSummary(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("getMarketSummary(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
}
|
|
logger.write(MODULE_NAME, EXCHANGE_HOST + "/getmarketsummaries/" + '?symbol=' + params.Symbol);
|
|
let url = EXCHANGE_HOST + "/getmarketsummaries/" + '?symbol=' + params.Symbol;
|
|
|
|
return publicRequest(url);
|
|
},
|
|
|
|
getTradeHistory: async (params = {}) => {
|
|
if (!params.Symbol) {
|
|
return Promise.reject("getTradeHistory(), You must supply a valid trading pair Symbol, e.g. 'COSS_ETH'!");
|
|
} else if (params.Symbol && typeof params.Symbol !== 'string') {
|
|
return Promise.reject("getTradeHistory(), You must supply a valid trading pair Symbol as a string, e.g. 'COSS_ETH'!");
|
|
}
|
|
logger.write(MODULE_NAME, HOST_URL2 + "/ht" + '?symbol=' + params.Symbol);
|
|
let url = HOST_URL2 + "/ht" + '?symbol=' + params.Symbol;
|
|
|
|
return publicRequest(url);
|
|
}
|
|
}
|
|
};
|
|
module.exports = exports = Coss;
|