From fef1bbee18df3003f781b66787fbb19f143f9c61 Mon Sep 17 00:00:00 2001 From: mkm279 Date: Sun, 8 Aug 2021 14:25:18 +0530 Subject: [PATCH 1/5] added check for kite key and token in incoming request --- lib/session.ts | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/session.ts b/lib/session.ts index c26edfd8..d50ccdcf 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -1,13 +1,38 @@ // this file is a wrapper with defaults to be used in both API routes and `getServerSideProps` functions import { withIronSession } from 'next-iron-session' +import { KiteConnect } from 'kiteconnect' // NB: not the best place to require these // ideally these should live in their own file that gets included as a middleware import './queue-processor' import './exit-strategies' import './watchers' -export default function withSession (handler) { - return withIronSession(handler, { +const withAdminCheck = (handler) => { + return async function withAdminWrapper(req, res) { + const kiteKey = req.headers['signalx-kite-key']; + const kiteToken = req.headers['signalx-kite-token']; + if (kiteKey && kiteToken) { + try { + const kc = new KiteConnect({ + api_key: kiteKey, + access_token: kiteToken + }); + + const kiteProfile = await kc.getProfile(); + const user = { isLoggedIn: true, session: { access_token: kiteToken, ...kiteProfile } } + req.session.set('user', user) + await req.session.save() + } catch (error) { + console.log(error) + return res.status(403).send('Forbidden. Unaithorized kry or token provided') + } + } + return handler(req, res) + } +} + +export default function withSession(handler) { + return withIronSession(withAdminCheck(handler), { password: process.env.SECRET_COOKIE_PASSWORD!, cookieName: 'khaching/kite/session', cookieOptions: { From 0f97f0b57d7c8cf972a76ffa957277803768990f Mon Sep 17 00:00:00 2001 From: mkm279 Date: Tue, 10 Aug 2021 15:22:58 +0530 Subject: [PATCH 2/5] changes for remote admin api access --- lib/session.ts | 4 +++- pages/api/trades_day.ts | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/session.ts b/lib/session.ts index d50ccdcf..c6a44829 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -12,6 +12,7 @@ const withAdminCheck = (handler) => { const kiteKey = req.headers['signalx-kite-key']; const kiteToken = req.headers['signalx-kite-token']; if (kiteKey && kiteToken) { + console.log('key and token found in headers. ateempting to connect kite and save session') try { const kc = new KiteConnect({ api_key: kiteKey, @@ -22,9 +23,10 @@ const withAdminCheck = (handler) => { const user = { isLoggedIn: true, session: { access_token: kiteToken, ...kiteProfile } } req.session.set('user', user) await req.session.save() + console.log('session generated') } catch (error) { console.log(error) - return res.status(403).send('Forbidden. Unaithorized kry or token provided') + return res.status(403).send('Forbidden. Unauthorized kry or token provided') } } return handler(req, res) diff --git a/pages/api/trades_day.ts b/pages/api/trades_day.ts index a22b980e..4656a7b3 100644 --- a/pages/api/trades_day.ts +++ b/pages/api/trades_day.ts @@ -21,9 +21,9 @@ import { SignalXUser } from '../../types/misc' const nanoid = customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 8) -async function createJob ( +async function createJob( { jobData, user }: - { jobData: SUPPORTED_TRADE_CONFIG, user: SignalXUser} + { jobData: SUPPORTED_TRADE_CONFIG, user: SignalXUser } ) { const { runAt, @@ -73,7 +73,7 @@ async function createJob ( ) } -async function deleteJob (id) { +async function deleteJob(id) { try { if (id.includes('repeat')) { await tradingQueue.removeRepeatableByKey(id) @@ -152,6 +152,7 @@ export default withSession(async (req, res) => { } if (req.method === 'DELETE') { + console.log("delete request recieved for: ", req.body._id) try { const { data } = await axios(`${endpoint}/${req.body._id as string}`) if (data.queue?.id) { From 8db1c1097cc7b0ddbd1dcfa67507560d9daf5dea Mon Sep 17 00:00:00 2001 From: mkm279 Date: Fri, 27 Aug 2021 00:37:31 +0530 Subject: [PATCH 3/5] session set code moved to single method --- lib/session.ts | 5 ++-- lib/utils.ts | 55 +++++++++++++++++++--------------- pages/api/redirect_url_kite.ts | 6 ++-- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/lib/session.ts b/lib/session.ts index c6a44829..524d4bf6 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -6,6 +6,7 @@ import { KiteConnect } from 'kiteconnect' import './queue-processor' import './exit-strategies' import './watchers' +import { setUserSession } from './utils' const withAdminCheck = (handler) => { return async function withAdminWrapper(req, res) { @@ -20,9 +21,7 @@ const withAdminCheck = (handler) => { }); const kiteProfile = await kc.getProfile(); - const user = { isLoggedIn: true, session: { access_token: kiteToken, ...kiteProfile } } - req.session.set('user', user) - await req.session.save() + await setUserSession(req, { access_token: kiteToken, ...kiteProfile }); console.log('session generated') } catch (error) { console.log(error) diff --git a/lib/utils.ts b/lib/utils.ts index ac9d0e82..1591297a 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -10,7 +10,7 @@ import { ERROR_STRINGS, EXIT_STRATEGIES, STRATEGIES, USER_OVERRIDE } from './con // export const memoizer = require('redis-memoizer')(redisClient); import { COMPLETED_ORDER_RESPONSE } from './strategies/mockData/orderResponse' import { SignalXUser } from '../types/misc' -import { KiteOrder } from '../types/kite' +import { KiteOrder, KiteProfile } from '../types/kite' Promise.config({ cancellation: true, warnings: true }) const isSameOrBefore = require('dayjs/plugin/isSameOrBefore') @@ -43,7 +43,7 @@ const asyncGetIndexInstruments = (exchange = 'NFO') => const jsonArray = await csv().fromFile(filename) // sometimes 0d returns 200 status code but 502 gateway error in file if (Object.keys(jsonArray[0]).length === 12) { - fs.unlink(filename, (e) => {}) + fs.unlink(filename, (e) => { }) const indexesData = exchange === 'NFO' ? jsonArray.filter( @@ -55,7 +55,7 @@ const asyncGetIndexInstruments = (exchange = 'NFO') => return resolve(indexesData) } // retry if that's the case - fs.unlink(filename, (e) => {}) + fs.unlink(filename, (e) => { }) console.log('🔴 Failed downloading instruments file! Retrying...') // resolve this promise with a recursive promise fn call resolve(asyncGetIndexInstruments()) @@ -129,18 +129,18 @@ export const getCurrentExpiryTradingSymbol = async ({ } } -export function getPercentageChange (price1: number, price2: number, mode: string = 'AGGRESIVE') { +export function getPercentageChange(price1: number, price2: number, mode: string = 'AGGRESIVE') { const denominator = mode === 'AGGRESIVE' ? ((price1 + price2) / 2) : Math.min(price1, price2) return Math.floor((Math.abs(price1 - price2) / denominator) * 100) } -export async function getInstrumentPrice (kite, underlying: string, exchange: string) { +export async function getInstrumentPrice(kite, underlying: string, exchange: string) { const instrumentString = `${exchange}:${underlying}` const underlyingRes = await kite.getLTP(instrumentString) return Number(underlyingRes[instrumentString].last_price) } -export async function getSkew (kite, instrument1, instrument2, exchange) { +export async function getSkew(kite, instrument1, instrument2, exchange) { const [price1, price2] = await Promise.all([ getInstrumentPrice(kite, instrument1, exchange), getInstrumentPrice(kite, instrument2, exchange) @@ -154,7 +154,7 @@ export async function getSkew (kite, instrument1, instrument2, exchange) { } } -export function syncGetKiteInstance (user) { +export function syncGetKiteInstance(user) { const accessToken = user?.session?.access_token if (!accessToken) { throw new Error('missing access_token in `user` object, or `user` is undefined') @@ -165,12 +165,12 @@ export function syncGetKiteInstance (user) { }) } -export async function getCompletedOrderFromOrderHistoryById (kite, orderId) { +export async function getCompletedOrderFromOrderHistoryById(kite, orderId) { const orders = await kite.getOrderHistory(orderId) return orders.find((odr) => odr.status === 'COMPLETE') } -export async function getAllOrNoneCompletedOrdersByKiteResponse (kite, rawKiteOrdersResponse) { +export async function getAllOrNoneCompletedOrdersByKiteResponse(kite, rawKiteOrdersResponse) { if (MOCK_ORDERS) { return [...new Array(rawKiteOrdersResponse.length)].fill(COMPLETED_ORDER_RESPONSE) } @@ -229,10 +229,10 @@ export const getBackoffStrategy = ({ strategy }) => { export const getCustomBackoffStrategies = () => { return { - backOffToNearest5thMinute () { + backOffToNearest5thMinute() { return dayjs(getNextNthMinute(5 * 60 * 1000)).diff(dayjs()) }, - backOffToNearestMinute () { + backOffToNearestMinute() { return dayjs(getNextNthMinute(1 * 60 * 1000)).diff(dayjs()) } } @@ -368,9 +368,8 @@ export const getLastOpenDateSince = (from: Dayjs) => { } export const checkHasSameAccessToken = async (accessToken: string) => { - const ACCESS_TOKEN_URL = `${withoutFwdSlash(DATABASE_HOST_URL as string)}/pvt_${ - DATABASE_USER_KEY as string - }/tokens?limit=1` + const ACCESS_TOKEN_URL = `${withoutFwdSlash(DATABASE_HOST_URL as string)}/pvt_${DATABASE_USER_KEY as string + }/tokens?limit=1` try { const { data: [token] } = await axios(ACCESS_TOKEN_URL) const { access_token: dbAccessToken } = token @@ -382,9 +381,8 @@ export const checkHasSameAccessToken = async (accessToken: string) => { } export const storeAccessTokenRemotely = async (accessToken) => { - const ACCESS_TOKEN_URL = `${withoutFwdSlash(DATABASE_HOST_URL as string)}/pvt_${ - DATABASE_USER_KEY as string - }/tokens` + const ACCESS_TOKEN_URL = `${withoutFwdSlash(DATABASE_HOST_URL as string)}/pvt_${DATABASE_USER_KEY as string + }/tokens` try { await axios.post( ACCESS_TOKEN_URL, @@ -454,14 +452,14 @@ export const isMarketOpen = (time = dayjs()) => { return time.isAfter(startTime) && time.isBefore(endTime) } -export function randomIntFromInterval (min: number, max: number) { +export function randomIntFromInterval(min: number, max: number) { // min and max included return Math.floor(Math.random() * (max - min + 1) + min) } -interface LTP_TYPE {tradingsymbol: string, strike: string, last_price: number} +interface LTP_TYPE { tradingsymbol: string, strike: string, last_price: number } -export function closest (needle: number, haystack: Array, haystackKey: string, greaterThanEqualToPrice: boolean) { +export function closest(needle: number, haystack: Array, haystackKey: string, greaterThanEqualToPrice: boolean) { const filtered = haystack.filter((item) => { if (greaterThanEqualToPrice) { return item[haystackKey] >= needle @@ -542,14 +540,14 @@ export const getTradingSymbolsByOptionPrice = async ({ return closest(price, formattedPrices, 'last_price', greaterThanEqualToPrice) } -export function withoutFwdSlash (url: string): string { +export function withoutFwdSlash(url: string): string { if (url.endsWith('/')) { return url.slice(0, url.length - 1) } return url } -export async function premiumAuthCheck (): Promise { +export async function premiumAuthCheck(): Promise { if (!process.env.SIGNALX_API_KEY) { return false } @@ -833,7 +831,7 @@ export const remoteOrderSuccessEnsurer = async (args: { } // patches and returns stale data -export const patchDbTrade = async ({ _id, patchProps }: {_id: string, patchProps: object}): Promise => { +export const patchDbTrade = async ({ _id, patchProps }: { _id: string, patchProps: object }): Promise => { const endpoint = `${baseTradeUrl}/${_id}` const { data } = await axios(endpoint) await axios.put(endpoint, { @@ -878,7 +876,7 @@ export const attemptBrokerOrders = async (ordersPr: Array>): Promis export const getHedgeForStrike = async ( { strike, distance, type, nfoSymbol }: - {strike: number, distance: number, type: string, nfoSymbol: string} + { strike: number, distance: number, type: string, nfoSymbol: string } ): Promise => { const hedgeStrike = strike + distance * (type === 'PE' ? -1 : 1) @@ -924,3 +922,12 @@ export const getStrikeByDelta = ( callStrike } } + +export const setUserSession = async (req, userData: KiteProfile) => { + const user: SignalXUser = { + isLoggedIn: true, + session: userData + }; + req.session.set('user', user) + await req.session.save() +} diff --git a/pages/api/redirect_url_kite.ts b/pages/api/redirect_url_kite.ts index 7938a521..ac208a64 100644 --- a/pages/api/redirect_url_kite.ts +++ b/pages/api/redirect_url_kite.ts @@ -3,7 +3,7 @@ import { KiteConnect } from 'kiteconnect' import { cleanupQueues } from '../../lib/queue' import withSession from '../../lib/session' -import { getIndexInstruments, premiumAuthCheck, storeAccessTokenRemotely, checkHasSameAccessToken } from '../../lib/utils' +import { getIndexInstruments, premiumAuthCheck, storeAccessTokenRemotely, checkHasSameAccessToken, setUserSession } from '../../lib/utils' import { KiteProfile } from '../../types/kite' import { SignalXUser } from '../../types/misc' @@ -22,9 +22,7 @@ export default withSession(async (req, res) => { try { const sessionData: KiteProfile = await kc.generateSession(requestToken, kiteSecret) - const user: SignalXUser = { isLoggedIn: true, session: sessionData } - req.session.set('user', user) - await req.session.save() + await setUserSession(req, sessionData); // prepare the day // fire and forget From ff52c64d32903909620cc9863f63742669fd8cd1 Mon Sep 17 00:00:00 2001 From: Manish Mishra Date: Fri, 22 Oct 2021 17:58:15 +0530 Subject: [PATCH 4/5] added multiple expiry option to choose from when setting up trade (#60) * added multiple expiry option to choose from when seting up trade * formatted changed files * ft: enable in DOS as well Co-authored-by: Aakash Goel --- components/lib/ExpiryTypeComponent.tsx | 50 +++++ components/lib/commonDetailsRows.tsx | 5 +- .../trades/atmStraddle/TradeSetupForm.tsx | 3 + .../trades/atmStrangle/TradeSetupForm.tsx | 3 + .../TradeSetupForm.tsx | 3 + lib/browserUtils.ts | 12 +- lib/constants.ts | 30 +++ .../optionEntryWatcher.js | 5 +- lib/queue-processor/tradingQueue.ts | 1 + lib/strategies/atmStraddle.ts | 18 +- lib/strategies/directionalOptionSelling.ts | 26 ++- lib/strategies/optionSellerStrategy.js | 13 +- lib/strategies/strangle.ts | 39 ++-- lib/utils.ts | 201 ++++++++++++++++-- pages/api/get_orders.js | 52 ++--- types/plans.ts | 4 +- 16 files changed, 375 insertions(+), 90 deletions(-) create mode 100644 components/lib/ExpiryTypeComponent.tsx diff --git a/components/lib/ExpiryTypeComponent.tsx b/components/lib/ExpiryTypeComponent.tsx new file mode 100644 index 00000000..87d139e1 --- /dev/null +++ b/components/lib/ExpiryTypeComponent.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import { + FormControl, + FormControlLabel, + Typography, + FormLabel, + RadioGroup, + Radio, + Grid +} from '@material-ui/core' +import { EXPIRY_TYPE, EXPIRY_TYPE_HUMAN } from '../../lib/constants' + +const ExpiryTypeComponent = ({ state, onChange }) => { + const expiryTypes = [ + EXPIRY_TYPE.CURRENT, + EXPIRY_TYPE.NEXT, + EXPIRY_TYPE.MONTHLY + ] + return ( + + + Option Expiry + + onChange({ expiryType: e.target.value as EXPIRY_TYPE }) + } + row + > + {expiryTypes.map(expiryType => ( + } + label={ + + {EXPIRY_TYPE_HUMAN[expiryType]} + + } + /> + ))} + + + + ) +} + +export default ExpiryTypeComponent diff --git a/components/lib/commonDetailsRows.tsx b/components/lib/commonDetailsRows.tsx index a9ec44d8..4f213a07 100644 --- a/components/lib/commonDetailsRows.tsx +++ b/components/lib/commonDetailsRows.tsx @@ -20,7 +20,8 @@ const CommonDetailsRows = ({ volatilityType, liveTrailingSl, lastTrailingSlSetAt, - lastHeartbeatAt + lastHeartbeatAt, + expiryType }) => { const getAlgoStatus = useCallback( () => @@ -55,6 +56,8 @@ const CommonDetailsRows = ({ return [ volatilityType ? [{ value: 'Vol type' }, { value: volatilityType }] : null, productType ? [{ value: 'Product' }, { value: productType }] : null, + expiryType ? [{ value: 'Expiry' }, { value: expiryType }] : null, + [ { value: 'Exit Strategy' }, { value: EXIT_STRATEGIES_DETAILS[exitStrategy].label } diff --git a/components/trades/atmStraddle/TradeSetupForm.tsx b/components/trades/atmStraddle/TradeSetupForm.tsx index d4411ac8..39d98733 100644 --- a/components/trades/atmStraddle/TradeSetupForm.tsx +++ b/components/trades/atmStraddle/TradeSetupForm.tsx @@ -34,6 +34,7 @@ import ProductTypeComponent from '../../lib/ProductTypeComponent' import VolatilityTypeComponent from '../../lib/VolatilityTypeComponent' import RollbackComponent from '../../lib/RollbackComponent' import SlManagerComponent from '../../lib/SlManagerComponent' +import ExpiryTypeComponent from '../../lib/ExpiryTypeComponent' interface ATMStraddleTradeSetupFormProps { formHeading?: string @@ -113,6 +114,8 @@ const TradeSetupForm = ({ + + + + Entry strategy diff --git a/components/trades/directionalOptionSelling/TradeSetupForm.tsx b/components/trades/directionalOptionSelling/TradeSetupForm.tsx index f13ef2be..156acac3 100644 --- a/components/trades/directionalOptionSelling/TradeSetupForm.tsx +++ b/components/trades/directionalOptionSelling/TradeSetupForm.tsx @@ -38,6 +38,7 @@ import HedgeComponent from '../../lib/HedgeComponent' import RollbackComponent from '../../lib/RollbackComponent' import ProductTypeComponent from '../../lib/ProductTypeComponent' import SlManagerComponent from '../../lib/SlManagerComponent' +import ExpiryTypeComponent from '../../lib/ExpiryTypeComponent' interface DOSTradeSetupFormProps { state: Partial @@ -114,6 +115,8 @@ const TradeSetupForm = ({ + + = { [INSTRUMENTS.NIFTY]: { lotSize: 50, @@ -86,6 +101,18 @@ export enum VOLATILITY_TYPE { SHORT = 'SHORT' } +export enum EXPIRY_TYPE { + CURRENT = 'CURRENT', + NEXT = 'NEXT', + MONTHLY = 'MONTHLY' +} + +export const EXPIRY_TYPE_HUMAN = { + [EXPIRY_TYPE.CURRENT]: 'Current weekly', + [EXPIRY_TYPE.NEXT]: 'Next weekly', + [EXPIRY_TYPE.MONTHLY]: 'Current Monthly' +} + export enum STRANGLE_ENTRY_STRATEGIES { DISTANCE_FROM_ATM = 'DISTANCE_FROM_ATM', DELTA_STIKES = 'DELTA_STIKES' @@ -136,6 +163,7 @@ export const STRATEGIES_DETAILS = { trailingSlPercent: NEXT_PUBLIC_DEFAULT_SLM_PERCENT, productType: PRODUCT_TYPE.MIS, volatilityType: VOLATILITY_TYPE.SHORT, + expiryType: EXPIRY_TYPE.CURRENT, runNow: false, expireIfUnsuccessfulInMins: 10, exitStrategy: EXIT_STRATEGIES.INDIVIDUAL_LEG_SLM_1X, @@ -173,6 +201,7 @@ export const STRATEGIES_DETAILS = { deltaStrikes: 20, productType: PRODUCT_TYPE.MIS, volatilityType: VOLATILITY_TYPE.SHORT, + expiryType: EXPIRY_TYPE.CURRENT, runNow: false, exitStrategy: EXIT_STRATEGIES.INDIVIDUAL_LEG_SLM_1X, slOrderType: SL_ORDER_TYPE.SLL, @@ -214,6 +243,7 @@ export const STRATEGIES_DETAILS = { martingaleIncrementSize: 1, isHedgeEnabled: true, productType: PRODUCT_TYPE.MIS, + expiryType: EXPIRY_TYPE.CURRENT, hedgeDistance: 2000, entryStrategy: DOS_ENTRY_STRATEGIES.FIXED_TIME, exitStrategy: EXIT_STRATEGIES.MIN_XPERCENT_OR_SUPERTREND, diff --git a/lib/queue-processor/optionSellerStrategy/optionEntryWatcher.js b/lib/queue-processor/optionSellerStrategy/optionEntryWatcher.js index cfe5f22a..6051c2c9 100644 --- a/lib/queue-processor/optionSellerStrategy/optionEntryWatcher.js +++ b/lib/queue-processor/optionSellerStrategy/optionEntryWatcher.js @@ -18,7 +18,7 @@ const optionSellerEntryWatcher = async ({ addHedge }) => { try { - const { user, orderTag, instrument } = initialJobData + const { user, orderTag, instrument, expiryType } = initialJobData const kite = syncGetKiteInstance(user) const orderHistory = await kite.getOrderHistory(limitOrderAckId) const revOrderHistory = orderHistory.reverse() @@ -81,7 +81,8 @@ const optionSellerEntryWatcher = async ({ completedOrder.tradingsymbol.length - 2, completedOrder.tradingsymbol.length - 1 ), - user + user, + expiry: expiryType }) const hedgeOrder = { diff --git a/lib/queue-processor/tradingQueue.ts b/lib/queue-processor/tradingQueue.ts index eeed26f2..381e6539 100644 --- a/lib/queue-processor/tradingQueue.ts +++ b/lib/queue-processor/tradingQueue.ts @@ -21,6 +21,7 @@ async function processJob (job: Job) { data, data: { strategy } } = job + console.log(`[job processing] Beginning job processing for ${strategy}`) switch (strategy) { case STRATEGIES.ATM_STRADDLE: { return atmStraddle(data) diff --git a/lib/strategies/atmStraddle.ts b/lib/strategies/atmStraddle.ts index 294806e4..1fb39ebe 100644 --- a/lib/strategies/atmStraddle.ts +++ b/lib/strategies/atmStraddle.ts @@ -4,6 +4,7 @@ import { SignalXUser } from '../../types/misc' import { ATM_STRADDLE_TRADE } from '../../types/trade' import { + EXPIRY_TYPE, INSTRUMENT_DETAILS, INSTRUMENT_PROPERTIES, PRODUCT_TYPE, @@ -16,7 +17,7 @@ import { attemptBrokerOrders, delay, ensureMarginForBasketOrder, - getCurrentExpiryTradingSymbol, + getExpiryTradingSymbol, getHedgeForStrike, getIndexInstruments, getInstrumentPrice, @@ -58,6 +59,7 @@ export async function getATMStraddle ( thresholdSkewPercent, takeTradeIrrespectiveSkew, expiresAt, + expiryType, attempt = 0 } = args try { @@ -98,11 +100,12 @@ export async function getATMStraddle ( const atmStrike = Math.round(underlyingLTP / strikeStepSize!) * strikeStepSize! - const { PE_STRING, CE_STRING } = (await getCurrentExpiryTradingSymbol({ + const { PE_STRING, CE_STRING } = (await getExpiryTradingSymbol({ nfoSymbol, - strike: atmStrike + strike: atmStrike, + expiry: expiryType })) as StrikeInterface - + console.log(`Expiry ${expiryType} strikes: ${PE_STRING} & ${CE_STRING}`) // if time has expired if (timeExpired) { console.log( @@ -205,6 +208,7 @@ async function atmStraddle ({ hedgeDistance, productType = PRODUCT_TYPE.MIS, volatilityType = VOLATILITY_TYPE.SHORT, + expiryType = EXPIRY_TYPE.CURRENT, _nextTradingQueue = EXIT_TRADING_Q_NAME }: ATM_STRADDLE_TRADE): Promise< | { @@ -240,7 +244,8 @@ async function atmStraddle ({ maxSkewPercent, thresholdSkewPercent, takeTradeIrrespectiveSkew, - expiresAt + expiresAt, + expiryType }) const { PE_STRING, CE_STRING, atmStrike } = straddle @@ -256,7 +261,8 @@ async function atmStraddle ({ strike: atmStrike, distance: hedgeDistance!, type, - nfoSymbol + nfoSymbol, + expiryType }) ) ) diff --git a/lib/strategies/directionalOptionSelling.ts b/lib/strategies/directionalOptionSelling.ts index 851fecce..d0089e61 100644 --- a/lib/strategies/directionalOptionSelling.ts +++ b/lib/strategies/directionalOptionSelling.ts @@ -20,8 +20,8 @@ import { TRADING_Q_NAME } from '../queue' import { + getExpiryTradingSymbol, attemptBrokerOrders, - getCurrentExpiryTradingSymbol, getInstrumentPrice, getLastOpenDateSince, getNearestCandleTime, @@ -94,7 +94,8 @@ export default async function directionalOptionSelling ( .ENTRY_STRATEGIES.FIXED_TIME, lastTrend, lastTradeOrders, - user + user, + expiryType } = initialJobData if (getTimeLeftInMarketClosingMs() < 40 * 60 * 1000) { @@ -105,9 +106,10 @@ export default async function directionalOptionSelling ( const { instrument_token: futInstrumentToken - } = (await getCurrentExpiryTradingSymbol({ + } = (await getExpiryTradingSymbol({ nfoSymbol, - instrumentType: 'FUT' + instrumentType: 'FUT', + expiry: expiryType })) as TradingSymbolInterface const DATE_FORMAT = 'YYYY-MM-DD' @@ -249,7 +251,8 @@ async function punchOrders ( rollback, productType = PRODUCT_TYPE.MIS, isHedgeEnabled = false, - hedgeDistance = 1700 + hedgeDistance = 1700, + expiryType } = initialJobData const strikeByPriceNumber = strikeByPrice ? Number(strikeByPrice) : null const kite = _kite || syncGetKiteInstance(user) @@ -271,13 +274,15 @@ async function punchOrders ( price: strikeByPriceNumber, pivotStrike: atmStrike, instrumentType, - user: user! + user: user!, + expiry: expiryType }) ) - : await getCurrentExpiryTradingSymbol({ + : await getExpiryTradingSymbol({ nfoSymbol, strike: superTrendStrike, - instrumentType + instrumentType, + expiry: expiryType }) const ltp = await withRemoteRetry(async () => @@ -297,10 +302,11 @@ async function punchOrders ( Number(optionStrike) + Number(hedgeDistance) * (instrumentType === 'PE' ? -1 : 1) - const hedgeStrikeData = (await getCurrentExpiryTradingSymbol({ + const hedgeStrikeData = (await getExpiryTradingSymbol({ nfoSymbol, strike: hedgeStrike, - instrumentType + instrumentType, + expiry: expiryType })) as TradingSymbolInterface if (hedgeStrikeData) { diff --git a/lib/strategies/optionSellerStrategy.js b/lib/strategies/optionSellerStrategy.js index e30951d8..1e196082 100644 --- a/lib/strategies/optionSellerStrategy.js +++ b/lib/strategies/optionSellerStrategy.js @@ -15,6 +15,7 @@ import { import { delay, getCurrentExpiryTradingSymbol, + getExpiryTradingSymbol, getIndexInstruments, getInstrumentPrice, getLastOpenDateSince, @@ -56,7 +57,7 @@ export const fetchHistoricalPrice = async instrumentToken => { export default async initialJobData => { try { - const { instrument } = initialJobData + const { instrument, expiryType } = initialJobData const { underlyingSymbol, strikeStepSize, nfoSymbol } = INSTRUMENT_DETAILS[ instrument @@ -85,18 +86,20 @@ export default async initialJobData => { const nfoInstruments = await getIndexInstruments('NFO') - const callStrike = getCurrentExpiryTradingSymbol({ + const callStrike = await getExpiryTradingSymbol({ sourceData: nfoInstruments, nfoSymbol, strike: callOptionStrike, - instrumentType: 'CE' + instrumentType: 'CE', + expiry: expiryType }) - const putStrike = getCurrentExpiryTradingSymbol({ + const putStrike = await getExpiryTradingSymbol({ sourceData: nfoInstruments, nfoSymbol, strike: putOptionStrike, - instrumentType: 'PE' + instrumentType: 'PE', + expiry: expiryType }) // return { diff --git a/lib/strategies/strangle.ts b/lib/strategies/strangle.ts index 32e04dd7..dbdcf680 100644 --- a/lib/strategies/strangle.ts +++ b/lib/strategies/strangle.ts @@ -1,6 +1,7 @@ import { ATM_STRANGLE_TRADE } from '../../types/trade' import { ERROR_STRINGS, + EXPIRY_TYPE, INSTRUMENTS, INSTRUMENT_DETAILS, PRODUCT_TYPE, @@ -13,7 +14,7 @@ import { apiResponseObject, attemptBrokerOrders, ensureMarginForBasketOrder, - getCurrentExpiryTradingSymbol, + getExpiryTradingSymbol, getHedgeForStrike, getIndexInstruments, getStrikeByDelta, @@ -54,7 +55,8 @@ const getStrangleStrikes = async ({ inverted = false, entryStrategy, distanceFromAtm = 1, - deltaStrikes + deltaStrikes, + expiryType }: { atmStrike: number instrument: INSTRUMENTS @@ -62,6 +64,7 @@ const getStrangleStrikes = async ({ entryStrategy: STRANGLE_ENTRY_STRATEGIES distanceFromAtm?: number deltaStrikes?: number + expiryType?: EXPIRY_TYPE }) => { const { nfoSymbol, strikeStepSize } = INSTRUMENT_DETAILS[instrument] @@ -106,21 +109,21 @@ const getStrangleStrikes = async ({ higherLegCEStrike = atmStrike + distanceFromAtm * strikeStepSize } - const { - tradingsymbol: LOWER_LEG_PE_STRING - } = (await getCurrentExpiryTradingSymbol({ + const { tradingsymbol: LOWER_LEG_PE_STRING } = (await getExpiryTradingSymbol({ nfoSymbol, strike: lowerLegPEStrike, - instrumentType: 'PE' + instrumentType: 'PE', + expiry: expiryType })) as TradingSymbolInterface - const { - tradingsymbol: HIGHER_LEG_CE_STRING - } = (await getCurrentExpiryTradingSymbol({ - nfoSymbol, - strike: higherLegCEStrike, - instrumentType: 'CE' - })) as TradingSymbolInterface + const { tradingsymbol: HIGHER_LEG_CE_STRING } = (await getExpiryTradingSymbol( + { + nfoSymbol, + strike: higherLegCEStrike, + instrumentType: 'CE', + expiry: expiryType + } + )) as TradingSymbolInterface const PE_STRING = !inverted ? LOWER_LEG_PE_STRING @@ -153,6 +156,7 @@ async function atmStrangle (args: ATM_STRANGLE_TRADE) { distanceFromAtm = 1, productType = PRODUCT_TYPE.MIS, volatilityType = VOLATILITY_TYPE.SHORT, + expiryType, _nextTradingQueue = EXIT_TRADING_Q_NAME } = args const { @@ -176,7 +180,8 @@ async function atmStrangle (args: ATM_STRANGLE_TRADE) { underlyingSymbol, exchange, nfoSymbol, - strikeStepSize + strikeStepSize, + expiryType } as any) const { @@ -190,7 +195,8 @@ async function atmStrangle (args: ATM_STRANGLE_TRADE) { inverted, distanceFromAtm, entryStrategy, - deltaStrikes + deltaStrikes, + expiryType }) const kite = syncGetKiteInstance(user) @@ -210,7 +216,8 @@ async function atmStrangle (args: ATM_STRANGLE_TRADE) { strike, distance: hedgeDistance!, type, - nfoSymbol + nfoSymbol, + expiryType }) ) ) diff --git a/lib/utils.ts b/lib/utils.ts index 919778d7..f8528946 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -8,8 +8,10 @@ import { allSettled, allSettledInterface } from './es6-promise' import { ERROR_STRINGS, EXIT_STRATEGIES, + EXPIRY_TYPE, INSTRUMENTS, INSTRUMENT_DETAILS, + KITE_INSTRUMENT_INFO, STRATEGIES, USER_OVERRIDE } from './constants' @@ -41,7 +43,9 @@ export const logDeep = object => console.log(JSON.stringify(object, null, 2)) export const ms = seconds => seconds * 1000 -const asyncGetIndexInstruments = (exchange = 'NFO') => +const asyncGetIndexInstruments = ( + exchange = 'NFO' +): Promise => new Promise((resolve, reject) => { const filename = `instrument_${new Date().getTime()}.csv` const file = fs.createWriteStream(filename) @@ -104,17 +108,13 @@ export const getMisOrderLastSquareOffTime = () => .set('seconds', 0) .format() -export interface TradingSymbolInterface { - tradingsymbol: string - instrument_token: string - strike: string -} +export type TradingSymbolInterface = KITE_INSTRUMENT_INFO export interface StrikeInterface { PE_STRING: string CE_STRING: string } -export const getCurrentExpiryTradingSymbol = async ({ +const getSortedMatchingIntrumentsData = async ({ nfoSymbol, strike, instrumentType, @@ -124,9 +124,9 @@ export const getCurrentExpiryTradingSymbol = async ({ strike?: number instrumentType?: string tradingsymbol?: string -}): Promise => { +}): Promise => { const instrumentsData = await getIndexInstruments() - const rows = instrumentsData + const rows: KITE_INSTRUMENT_INFO[] = instrumentsData .filter( item => (nfoSymbol ? item.name === nfoSymbol : true) && @@ -137,17 +137,172 @@ export const getCurrentExpiryTradingSymbol = async ({ .sort((row1, row2) => dayjs(row1.expiry).isSameOrBefore(dayjs(row2.expiry)) ? -1 : 1 ) + return rows +} + +export const getExpiryTradingSymbol = async ({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol, + expiry = EXPIRY_TYPE.CURRENT +}: { + nfoSymbol?: string + strike?: number + instrumentType?: string + tradingsymbol?: string + expiry?: EXPIRY_TYPE +}): Promise => { + console.log('Fetching trading symbol for expiry type: ', expiry) + switch (expiry) { + case EXPIRY_TYPE.MONTHLY: + return getMonthlyExpiryTradingSymbol({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol + }) + + case EXPIRY_TYPE.NEXT: + return getNextExpiryTradingSymbol({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol + }) + + default: + return getCurrentExpiryTradingSymbol({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol + }) + } +} + +export const getCurrentExpiryTradingSymbol = async ({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol +}: { + nfoSymbol?: string + strike?: number + instrumentType?: string + tradingsymbol?: string +}): Promise => { + const rows = await getSortedMatchingIntrumentsData({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol + }) if (instrumentType) { return rows.length ? rows[0] : null } - + // get first two entries for current expiry const relevantRows = rows.slice(0, 2) - const peStrike = relevantRows.find(item => item.instrument_type === 'PE') - .tradingsymbol - const ceStrike = relevantRows.find(item => item.instrument_type === 'CE') - .tradingsymbol + const peStrike = relevantRows?.find(item => item.instrument_type === 'PE') + ?.tradingsymbol + const ceStrike = relevantRows?.find(item => item.instrument_type === 'CE') + ?.tradingsymbol + + if (!peStrike || !ceStrike) return null + + return { + PE_STRING: peStrike, + CE_STRING: ceStrike + } +} + +export const getNextExpiryTradingSymbol = async ({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol +}: { + nfoSymbol?: string + strike?: number + instrumentType?: string + tradingsymbol?: string +}): Promise => { + const rows = await getSortedMatchingIntrumentsData({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol + }) + + if (instrumentType) { + return rows.length ? rows[1] : null + } + // first two entries are CE and PE for current week. So taking the next two items here + const relevantRows = rows.slice(2, 4) + + const peStrike = relevantRows?.find(item => item.instrument_type === 'PE') + ?.tradingsymbol + const ceStrike = relevantRows?.find(item => item.instrument_type === 'CE') + ?.tradingsymbol + + if (!peStrike || !ceStrike) return null + + return { + PE_STRING: peStrike, + CE_STRING: ceStrike + } +} + +export const getMonthlyExpiryTradingSymbol = async ({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol +}: { + nfoSymbol?: string + strike?: number + instrumentType?: string + tradingsymbol?: string +}): Promise => { + const instrumentsData = await getSortedMatchingIntrumentsData({ + nfoSymbol, + strike, + instrumentType, + tradingsymbol + }) + + // get current calendar month expiries + let rows = instrumentsData.filter( + item => dayjs().get('month') === dayjs(item.expiry).get('month') + ) + + // // get next calendar month expiries + if (!rows.length) { + const month = dayjs().get('month') === 11 ? 0 : dayjs().get('month') // to handle December current year & Jan next year cases + rows = instrumentsData.filter( + item => dayjs(item.expiry).get('month') === month + ) + } + rows = rows.sort((row1, row2) => + dayjs(row1.expiry).isSameOrBefore(dayjs(row2.expiry)) ? -1 : 1 + ) + + const rowsLength = rows.length + + if (instrumentType) { + return rows.length ? rows[rowsLength - 1] : null + } + // get last two entries for monthly expiry + const relevantRows = rows.slice(rowsLength - 2, rowsLength) + + const peStrike = relevantRows?.find(item => item.instrument_type === 'PE') + ?.tradingsymbol + const ceStrike = relevantRows?.find(item => item.instrument_type === 'CE') + ?.tradingsymbol + + if (!peStrike || !ceStrike) return null return { PE_STRING: peStrike, @@ -559,6 +714,7 @@ interface TRADING_SYMBOL_BY_OPTION_PRICE_TYPE { pivotStrike: number user: SignalXUser greaterThanEqualToPrice?: boolean + expiry?: EXPIRY_TYPE } interface GET_LTP_ARGS { @@ -621,7 +777,8 @@ export const getTradingSymbolsByOptionPrice = async ({ instrumentType, pivotStrike, user, - greaterThanEqualToPrice = false + greaterThanEqualToPrice = false, + expiry = EXPIRY_TYPE.CURRENT }: TRADING_SYMBOL_BY_OPTION_PRICE_TYPE) => { const kite = syncGetKiteInstance(user) const totalStrikes = 31 // pivot and 15 on each side @@ -638,10 +795,11 @@ export const getTradingSymbolsByOptionPrice = async ({ .sort((a, b) => a - b) const instruments = await Promise.map(strikes, async strike => { - const { tradingsymbol } = (await getCurrentExpiryTradingSymbol({ + const { tradingsymbol } = (await getExpiryTradingSymbol({ nfoSymbol, strike, - instrumentType + instrumentType, + expiry })) as TradingSymbolInterface return { @@ -1166,19 +1324,22 @@ export const getHedgeForStrike = async ({ strike, distance, type, - nfoSymbol + nfoSymbol, + expiryType = EXPIRY_TYPE.CURRENT }: { strike: number distance: number type: string nfoSymbol: string + expiryType: EXPIRY_TYPE }): Promise => { const hedgeStrike = strike + distance * (type === 'PE' ? -1 : 1) - const { tradingsymbol } = (await getCurrentExpiryTradingSymbol({ + const { tradingsymbol } = (await getExpiryTradingSymbol({ nfoSymbol, strike: hedgeStrike, - instrumentType: type + instrumentType: type, + expiry: expiryType })) as TradingSymbolInterface return tradingsymbol diff --git a/pages/api/get_orders.js b/pages/api/get_orders.js index 7c910855..0a35f547 100644 --- a/pages/api/get_orders.js +++ b/pages/api/get_orders.js @@ -47,34 +47,34 @@ export default withSession(async (req, res) => { : -1 ) - const getHumanTradingSymbol = async ({ tradingsymbol }) => { - const instrumentType = tradingsymbol.substr(tradingsymbol.length - 2, 2) - const expiryData = await getCurrentExpiryTradingSymbol({ - tradingsymbol, - instrumentType - }) - if (!expiryData) { - return null - } - const { expiry, name, strike } = expiryData - const dateString = dayjs(expiry) - .format('Do MMM') - .split(' ') - .map((str, idx) => (idx === 1 ? str.toUpperCase() : str)) - .join(' ') - return `${name} ${dateString} ${strike} ${instrumentType}` - } + // const getHumanTradingSymbol = async ({ tradingsymbol }) => { + // const instrumentType = tradingsymbol.substr(tradingsymbol.length - 2, 2) + // const expiryData = await getCurrentExpiryTradingSymbol({ + // tradingsymbol, + // instrumentType + // }) + // if (!expiryData) { + // return null + // } + // const { expiry, name, strike } = expiryData + // const dateString = dayjs(expiry) + // .format('Do MMM') + // .split(' ') + // .map((str, idx) => (idx === 1 ? str.toUpperCase() : str)) + // .join(' ') + // return `${name} ${dateString} ${strike} ${instrumentType}` + // } - const humanOrders = await Promise.map(orders, async order => { - return { - ...order, - humanTradingSymbol: await getHumanTradingSymbol({ - tradingsymbol: order.tradingsymbol - }) - } - }) + // const humanOrders = await Promise.map(orders, async order => { + // return { + // ...order, + // humanTradingSymbol: await getHumanTradingSymbol({ + // tradingsymbol: order.tradingsymbol + // }) + // } + // }) - res.json(humanOrders) + res.json(orders) } catch (e) { res.status(500).send(e) } diff --git a/types/plans.ts b/types/plans.ts index c857ec18..440448f2 100644 --- a/types/plans.ts +++ b/types/plans.ts @@ -5,11 +5,13 @@ import { STRATEGIES, STRANGLE_ENTRY_STRATEGIES, PRODUCT_TYPE, - VOLATILITY_TYPE + VOLATILITY_TYPE, + EXPIRY_TYPE } from '../lib/constants' interface COMMON_TRADE_PROPS { productType: PRODUCT_TYPE + expiryType: EXPIRY_TYPE } export interface SavedPlanMeta extends COMMON_TRADE_PROPS { From 0333e73fd67c87d8a992b28e62101c80e11e6068 Mon Sep 17 00:00:00 2001 From: Aakash Goel Date: Sat, 23 Oct 2021 12:27:20 +0530 Subject: [PATCH 5/5] ft: enable admin work auth --- lib/session.ts | 63 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/lib/session.ts b/lib/session.ts index 524d4bf6..e3bf1254 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -6,33 +6,64 @@ import { KiteConnect } from 'kiteconnect' import './queue-processor' import './exit-strategies' import './watchers' -import { setUserSession } from './utils' +import { setUserSession, premiumAuthCheck } from './utils' +import { KiteProfile } from '../types/kite' -const withAdminCheck = (handler) => { - return async function withAdminWrapper(req, res) { - const kiteKey = req.headers['signalx-kite-key']; - const kiteToken = req.headers['signalx-kite-token']; - if (kiteKey && kiteToken) { - console.log('key and token found in headers. ateempting to connect kite and save session') +const withAdminCheck = handler => { + return async function withAdminWrapper (req, res) { + const sxApiKey = req.headers['signalx-api-key'] + const kiteApiKey = req.headers['signalx-kite-key'] + const kiteAccessToken = req.headers['signalx-kite-token'] + + if (!sxApiKey && !kiteApiKey) { + // UI based flow + return handler(req, res) + } + + // for premium and club users + let validSxUser = false + try { + validSxUser = await premiumAuthCheck() + } catch (e) { + validSxUser = false + } + + // block intrusion + if (!validSxUser) { + return handler(req, res) + } + + if (!kiteAccessToken) { + // sx admin work, no broker authorization + await setUserSession(req, {} as KiteProfile) + } else { + console.log( + 'key and token found in headers. attempting to connect kite and save session' + ) try { const kc = new KiteConnect({ - api_key: kiteKey, - access_token: kiteToken - }); + api_key: kiteApiKey, + access_token: kiteAccessToken + }) - const kiteProfile = await kc.getProfile(); - await setUserSession(req, { access_token: kiteToken, ...kiteProfile }); - console.log('session generated') + const kiteProfile = await kc.getProfile() + await setUserSession(req, { + access_token: kiteAccessToken, + ...kiteProfile + }) + console.log('session generated from headers') + return handler(req, res) } catch (error) { console.log(error) - return res.status(403).send('Forbidden. Unauthorized kry or token provided') + return res + .status(403) + .send('Forbidden. Unauthorized key or token provided') } } - return handler(req, res) } } -export default function withSession(handler) { +export default function withSession (handler) { return withIronSession(withAdminCheck(handler), { password: process.env.SECRET_COOKIE_PASSWORD!, cookieName: 'khaching/kite/session',