@suyotech-dev/shoonya-js
Version:
Unofficial shoonya api sdk for internal use developed by suytoech.com
305 lines (272 loc) • 8.63 kB
JavaScript
import axios from "axios";
import { createHash } from "crypto";
import { TOTP } from "totp-generator";
export class ShoonyaAPI {
#baseURL = "https://api.shoonya.com/NorenWClientTP";
uid = "";
#password = "";
#vc = "";
#actid = "";
#apikey = "";
#totopkey = "";
susertoken = "";
/**
* @type {import("axios").AxiosInstance}
*/
#apiClient;
/**
*
* @param {String} uid //User ID
* @param {String} password // User Password
* @param {String} vc // Vendor Code
* @param {String} apikey // Api Key
* @param {String} totpkey // Totp Key
*/
constructor(uid, password, vc, apikey, totpkey) {
this.uid = uid;
this.#actid = uid;
this.#password = password;
this.#vc = vc;
this.#apikey = apikey;
this.#totopkey = totpkey;
this.#apiClient = this.#createAxiosInstance();
}
#routes = {
login: "/QuickAuth",
userdetails: "/UserDetails",
orderBook: "/OrderBook",
tradeBook: "/TradeBook",
positionBook: "/PositionBook",
placeOrder: "/PlaceOrder",
modifyOrder: "/ModifyOrder",
cancelorder: "/CancelOrder",
chartdata: "/TPSeries",
holding: "/Holdings",
limits: "/Limits",
};
#createAxiosInstance() {
let axiosInstance = axios.create({
baseURL: this.#baseURL,
timeout: 7000,
});
axiosInstance.interceptors.request.use((config) => {
if (config.data) {
const objtostr = (obj) => {
let newObj = {};
Object.keys(obj).forEach((key) => {
newObj[key] = String(obj[key]);
});
return newObj;
};
let data = `jData=${JSON.stringify(objtostr(config.data))}`;
if (this.susertoken) {
data = data + `&jKey=${this.susertoken}`;
}
config.data = data;
}
return config;
});
axiosInstance.interceptors.response.use(
(response) => {
if (response.data.stat !== "Ok" && !Array.isArray(response.data)) {
throw new Error(`Server Error : ${response.data.emsg}`);
}
return response;
},
(error) => {
if (error.response) {
throw new Error(
`Response Error : ${error.response?.data.emsg || error.resonse?.message}`
);
} else if (error.request) {
throw new Error(`Request Error : ${error.request?.message}`);
} else {
throw new Error(`General Error : ${error?.message}`);
}
}
);
return axiosInstance;
}
#hash(value) {
return createHash("sha256").update(value).digest("hex");
}
async login() {
try {
const otp = TOTP.generate(this.#totopkey).otp;
const request_data = {
source: "API",
apkversion: "1.0.0",
uid: this.uid,
pwd: this.#hash(this.#password),
vc: this.#vc,
appkey: this.#hash(`${this.uid}|${this.#apikey}`),
factor2: otp,
imei: "123123",
};
const resp = await this.#apiClient.post(this.#routes.login, request_data);
if (resp.data.susertoken) {
this.susertoken = resp.data.susertoken;
}
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
setSessionDetails(uid, susertoken) {
this.uid = uid;
this.susertoken = susertoken;
}
async getUserDetails() {
try {
const request_data = { uid: this.uid };
const resp = await this.#apiClient.post(this.#routes.userdetails, request_data);
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
async getOderBook() {
try {
const request_data = { uid: this.uid };
const resp = await this.#apiClient.post(this.#routes.orderBook, request_data);
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
async getTradeBook() {
try {
const request_data = { uid: this.uid, actid: this.#actid };
const resp = await this.#apiClient.post(this.#routes.tradeBook, request_data);
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
async getPositionBook() {
try {
const request_data = { uid: this.uid, actid: this.#actid };
const resp = await this.#apiClient.post(this.#routes.positionBook, request_data);
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
/**
* Get Candle data
* @param {Object} params
* @param {String} params.exch Exchange
* @param {String} params.token Symbol Token
* @param {String} params.st Start Time in seconds (epoch)
* @param {String} params.et End Time in seconds (epoch)
* @returns
*/
async getCandleData(params) {
try {
const request_data = { uid: this.uid, ...params };
const resp = await this.#apiClient.post(this.#routes.chartdata, request_data);
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
/**
* Place Order
* @param {Object} params Place Order Params
* @param {String} params.exch Exchange NSE/NFO/CDS/MCX/BSE/BFO
* @param {String} params.tsym Trading Symbol
* @param {Number} params.qty Order Quantity
* @param {Number} [params.prc] Order Price
* @param {Number} [params.trgprc] Trigger Price
* @param {String} params.prd Product "C" For CNC, "M" FOR NRML, "I" FOR MIS, "B" BRACKET ORDER, "H" FOR COVER ORDER
* @param {String} params.trantype Transaction Type B -> BUY, S -> SELL
* @param {String} params.prctyp Price Type LMT / MKT / SL-LMT / SL-MKT
*/
async placeOrder(params) {
try {
if (!["exch", "tsym", "qty", "prd", "trantype", "prctyp"].every((key) => key in params)) {
throw new Error("Fields required exch,tysm,qty,prd,trantype,prctyp");
}
const request_data = {
uid: this.uid,
actid: this.#actid,
exch: params.exch,
tsym: params.tsym,
qty: params.qty || 1,
prc: params.prc || 0,
trgprc: params.trgprc || 0,
prd: params.prd || "I",
trantype: params.trantype,
prctyp: params.prctyp || "MKT",
dscqty: 0,
ret: "DAY",
ordersource: "API",
};
console.log("request ", request_data);
const resp = await this.#apiClient.post(this.#routes.placeOrder, request_data);
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
/**
* Modify Pendig Order
* @param {Object} params Place Order Params
* @param {String} params.exch Exchange NSE/NFO/CDS/MCX/BSE/BFO
* @param {String} params.norenordno Order Number
* @param {String} params.tsym Order Number
* @param {Number} params.qty Order Quantity
* @param {Number} params.prc Order Price
* @param {Number} params.trgprc Trigger Price
* @param {String} params.trantype Transaction Type B -> BUY, S -> SELL
* @param {String} params.prctyp Price Type LMT / MKT / SL-LMT / SL-MKT
*/
async modifyOrder(params) {
try {
if (
!["exch", "norenordno", "tsym", "qty", "prc", "trgprc", "prctyp"].every(
(key) => key in params
)
) {
throw new Error("Fields required exch,tysm,qty,prd,trantype,prctyp");
}
const request_data = {
uid: this.uid,
norenordno: params.norenordno,
exch: params.exch,
tsym: params.tsym,
qty: params.qty || 1,
prc: params.prc || 0,
trgprc: params.trgprc || 0,
prctyp: params.prctyp || "MKT",
ret: "DAY",
};
console.log("request ", request_data);
const resp = await this.#apiClient.post(this.#routes.modifyOrder, request_data);
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
/**
*
* @param {String} orderid Order ID
* @returns
*/
async modifyOrder(orderid) {
try {
if (!orderid) {
throw new Error("Fields required orderid");
}
const request_data = {
uid: this.uid,
norenordno: orderid,
};
console.log("request ", request_data);
const resp = await this.#apiClient.post(this.#routes.cancelorder, request_data);
return resp.data;
} catch (error) {
throw new Error(error.message);
}
}
}