@thoshpathi/utils-smartapi-order
Version:
Utility functions for placing live and dummy orders using Angel One's SmartAPI, with helper methods for streamlined trading workflows.
283 lines (280 loc) • 9.1 kB
JavaScript
import {
findNearestPremiumData,
generateNakedTrendOptionSymbols,
getNakedScripData,
getSpreadScripData
} from "./chunk-D3LQUZFK.mjs";
import {
generateOrderId
} from "./chunk-RQBWMBFA.mjs";
// src/helpers/order_helper.ts
import { toDbDatetimeString, groupByMap } from "@thoshpathi/utils-core";
import { calculatePL, getManyScripLtpMap } from "@thoshpathi/utils-smartapi";
var OrderHelper = class {
constructor(params) {
this.smartapi = params.smartapi;
this.logger = params.logger;
this.instrumentDumpRepo = params.instrumentDumpRepo;
this.indexScripRepo = params.indexScripRepo;
this.orderRepo = params.orderRepo;
this.orderVerfiyWaitMs = params.orderVerfiyWaitMs ?? 1e3;
this.strikeRoundBy = params.strikeRoundBy ?? 100;
this.minimumPremium = params.minimumPremium ?? 10;
}
async getScripLtpMap(symbols, instrumentDumps) {
return await getManyScripLtpMap(
{ symbols, smartapi: this.smartapi },
instrumentDumps
);
}
async getOrdersLtpMap(orders) {
const symbols = orders.map((v) => v.symbol);
const instrumentDumps = orders.map((v) => {
return {
lotSize: 0,
name: v.name,
symbol: v.symbol,
symbolToken: v.symbolToken,
exchange: v.exchange,
id: 0n,
createdAt: ""
};
});
return await getManyScripLtpMap(
{ symbols, smartapi: this.smartapi },
instrumentDumps
);
}
generateOrderId() {
return generateOrderId();
}
getStrike(ltp) {
const strikeRoundBy = this.strikeRoundBy;
const strike = Math.round(ltp / strikeRoundBy) * strikeRoundBy;
return strike;
}
async getNakedFixedPremiumScrips(indexTrendData) {
const {
indexScrip: { strategy, contractPrefix, premium },
ltp,
trend
} = indexTrendData;
const strike = this.getStrike(ltp);
const spreadScripMap = /* @__PURE__ */ new Map();
const trendSymbols = generateNakedTrendOptionSymbols({
strike,
trend,
transaction: strategy,
contractPrefix
});
const instrumentDumps = await this.instrumentDumpRepo.getMany(
trendSymbols.symbols
);
const scripLtpMap = await this.getScripLtpMap(
trendSymbols.symbols,
instrumentDumps
);
const scripLtp = findNearestPremiumData(scripLtpMap, premium);
if (scripLtp != null) {
const { symbol, ltp: ltp2 } = scripLtp;
spreadScripMap.set(symbol, { symbol, trend: trendSymbols.trend, ltp: ltp2 });
}
return spreadScripMap;
}
async getStrategyScripMap(indexTrendData) {
const { indexScrip, ltp, trend } = indexTrendData;
const { strategy, leg1Gap, contractPrefix } = indexScrip;
const strike = this.getStrike(ltp);
if (strategy === "SPREAD") {
const { leg2Gap, contractPrefix: contractPrefix2, bullishSpread, bearishSpread } = indexScrip;
const spreadType = trend === "BUY" ? bullishSpread : bearishSpread;
return getSpreadScripData({
strike,
trend,
spreadType,
contractPrefix: contractPrefix2,
leg1Gap: leg1Gap.trim(),
leg2Gap: leg2Gap.trim()
});
} else {
const premium = indexScrip.premium;
if (premium != null && !isNaN(premium) && premium > this.minimumPremium) {
return await this.getNakedFixedPremiumScrips(indexTrendData);
}
return getNakedScripData({
strike,
trend,
transaction: strategy,
contractPrefix,
legGap: leg1Gap.trim()
});
}
}
async getTrendNewOrders({
candleInterval,
scripTrendMap
}) {
const newOrders = [];
for (const trendMap of scripTrendMap.values()) {
const { indexScrip } = trendMap;
const spreadScripMap = await this.getStrategyScripMap(trendMap);
if (spreadScripMap.size == 0) {
this.logger.w(indexScrip.name, "spreadScripMap empty.");
continue;
}
const symbols = Array.from(spreadScripMap.keys());
const instrumentDumps = await this.instrumentDumpRepo.getMany(symbols);
instrumentDumps.forEach((insDump) => {
const { symbol, symbolToken, name, exchange, lotSize } = insDump;
const trendData = spreadScripMap.get(symbol);
if (trendData != null)
newOrders.push({
candleInterval,
name,
symbol,
symbolToken,
exchange,
transaction: trendData.trend,
qty: lotSize * indexScrip.noOfLots,
entryOrderId: generateOrderId(),
entryPrice: trendData.ltp ?? 0,
entryAt: toDbDatetimeString(),
isClosed: false,
status: "open"
});
});
}
if (newOrders.length == 0) {
this.logger.w(candleInterval, "newOrders empty. no new orders");
return;
}
return newOrders;
}
async updateEntryPriceOfOrders(newOrders) {
const scripLtpMap = await this.getOrdersLtpMap(newOrders);
newOrders.forEach((order) => {
const ltpData = scripLtpMap.get(order.symbol);
if (ltpData != null) order.entryPrice = ltpData.ltp;
});
return newOrders;
}
async getOpenOrders(params) {
const openOrders = await this.orderRepo.getOpenOrders(params);
if (openOrders.length == 0) {
this.logger.i("no open orders exist", params.candleInterval);
return;
}
return openOrders;
}
async getOrderCloseTrackDatas() {
this.logger.d("fetch today open orders");
const openOrders = await this.getOpenOrders({});
if (openOrders == null) return;
this.logger.d("fetch active index scrips");
const indexScrips = await this.indexScripRepo.getAll({ activeOnly: true });
if (indexScrips.length == 0) {
this.logger.w("indexScrips empty. stop here");
return;
}
const allPropsDisabled = indexScrips.every((idx) => {
return (
// idx.exitPremium == 0 &&
idx.profitPoints == 0 && idx.stoplossPoints == 0
);
});
if (allPropsDisabled) {
this.logger.w("all index scrip props are disabled. stop here");
return;
}
const scripLtpMap = await this.getOrdersLtpMap(openOrders);
const openOrderMap = groupByMap(openOrders, "name");
const orderCloseTracks = [];
for (const indexScrip of indexScrips) {
const { name, strategy, profitPoints, stoplossPoints } = indexScrip;
if (!profitPoints && !stoplossPoints) continue;
const orders = openOrderMap.get(name);
if (!orders?.length) continue;
if (strategy === "SPREAD") {
const pl = orders.reduce((acc, order) => {
const { entryPrice: entry, qty, transaction: trend } = order;
const ltp = scripLtpMap.get(order.symbol)?.ltp;
if (ltp) acc += calculatePL({ entry, qty, trend, ltp });
return acc;
}, 0);
let status = void 0;
if (profitPoints && pl > profitPoints) {
status = "target";
} else if (stoplossPoints && Math.abs(pl) > stoplossPoints) {
status = "stoploss";
}
if (status != null) {
for (const order of orders) {
const ltp = scripLtpMap.get(order.symbol).ltp;
orderCloseTracks.push({ order, ltp, status });
}
}
} else {
for (const order of orders) {
const ltp = scripLtpMap.get(order.symbol)?.ltp;
if (!ltp) continue;
const { entryPrice: entry, transaction: trend, qty } = order;
let status = void 0;
const pl = calculatePL({ entry, trend, qty, ltp });
if (profitPoints && pl > profitPoints) {
status = "target";
} else if (stoplossPoints && Math.abs(pl) > stoplossPoints) {
status = "stoploss";
}
if (status != null) {
orderCloseTracks.push({
order,
ltp,
status
});
}
}
}
}
if (orderCloseTracks.length == 0) {
this.logger.w("orderCloseTracks empty. No orders to close");
return;
}
this.logger.i(
"Close orders available",
orderCloseTracks.map(({ order }) => `[${order.id}]: ${order.symbol}`)
);
return orderCloseTracks;
}
async createOrders(newOrders, candleInterval) {
try {
this.logger.i(
candleInterval,
"create new orders",
newOrders.map((v) => v.symbol)
);
const result = await this.orderRepo.createOrders(newOrders);
this.logger.i(candleInterval, "create new orders complete", result.info);
return result;
} catch (error) {
this.logger.c("create new orders error", error);
return;
}
}
async updateOrdersClose(closeOrders) {
try {
const results = await this.orderRepo.closeOpenOrders(closeOrders);
this.logger.i(
"close open orders complete",
closeOrders.map((v) => v.symbol),
results.length
);
return results;
} catch (error) {
this.logger.e("close open orders error", error);
return;
}
}
};
export {
OrderHelper
};