UNPKG

@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
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 };