From ce89f1bb2c000aac80f456568a0955a4b263fbb1 Mon Sep 17 00:00:00 2001 From: mkm279 Date: Thu, 9 Sep 2021 02:54:06 +0530 Subject: [PATCH 1/3] added different exit startegy selection in case of winning and losing for combined SL --- components/lib/SlManagerComponent.tsx | 75 +++++++------ lib/browserUtils.ts | 39 ++++--- lib/constants.ts | 14 +++ .../multiLegPremiumThreshold.ts | 101 ++++++++++++------ types/plans.ts | 4 + 5 files changed, 154 insertions(+), 79 deletions(-) diff --git a/components/lib/SlManagerComponent.tsx b/components/lib/SlManagerComponent.tsx index 8375a385..3073bfaa 100644 --- a/components/lib/SlManagerComponent.tsx +++ b/components/lib/SlManagerComponent.tsx @@ -7,13 +7,16 @@ import { RadioGroup, Radio, Grid, - TextField + TextField, + Box } from '@material-ui/core' import { COMBINED_SL_EXIT_STRATEGY, SL_ORDER_TYPE } from '../../types/plans' import { COMBINED_SL_EXIT_STRATEGY_LABEL, EXIT_STRATEGIES, - EXIT_STRATEGIES_DETAILS + EXIT_STRATEGIES_DETAILS, + POSITION_STATE, + POSITION_STATE_LABEL } from '../../lib/constants' const SlManagerComponent = ({ state, onChange, exitStrategies }) => { @@ -52,33 +55,45 @@ const SlManagerComponent = ({ state, onChange, exitStrategies }) => { When Combined trailing SL triggers - - onChange({ - combinedExitStrategy: e.target - .value as COMBINED_SL_EXIT_STRATEGY - }) - } - > - {[ - COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, - COMBINED_SL_EXIT_STRATEGY.EXIT_LOSING - ].map(combinedExitStrategy => ( - } - label={ - - {COMBINED_SL_EXIT_STRATEGY_LABEL[combinedExitStrategy]} - + {[POSITION_STATE.WINNING, POSITION_STATE.LOSING].map(item => + + + {POSITION_STATE_LABEL[item]} + + + onChange(item === POSITION_STATE.WINNING ? { + combinedExitStrategyWinning: e.target + .value as COMBINED_SL_EXIT_STRATEGY + } : { + combinedExitStrategyLosing: e.target + .value as COMBINED_SL_EXIT_STRATEGY + }) } - /> - ))} - + > + {[ + COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, + COMBINED_SL_EXIT_STRATEGY.EXIT_LOSING + ].map(combinedExitStrategy => ( + } + label={ + + {COMBINED_SL_EXIT_STRATEGY_LABEL[combinedExitStrategy]} + + } + /> + ))} + + + + )} @@ -123,8 +138,8 @@ const SlManagerComponent = ({ state, onChange, exitStrategies }) => { {state.exitStrategy !== EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD || - (state.exitStrategy === EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD && - state.combinedExitStrategy === + (state.exitStrategy === EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD && + state.combinedExitStrategy === COMBINED_SL_EXIT_STRATEGY.EXIT_LOSING) ? ( diff --git a/lib/browserUtils.ts b/lib/browserUtils.ts index 0152c2db..e62f9bf3 100644 --- a/lib/browserUtils.ts +++ b/lib/browserUtils.ts @@ -30,7 +30,7 @@ export const ensureIST = date => { return datetimeInIST } -export function getScheduleableTradeTime (strategy: STRATEGIES) { +export function getScheduleableTradeTime(strategy: STRATEGIES) { const defaultDate = dayjs(STRATEGIES_DETAILS[strategy].defaultRunAt).format() if (dayjs().isAfter(dayjs(defaultDate))) { @@ -42,7 +42,7 @@ export function getScheduleableTradeTime (strategy: STRATEGIES) { return defaultDate } -export function getDefaultSquareOffTime () { +export function getDefaultSquareOffTime() { const [hours, minutes] = ( process.env.NEXT_PUBLIC_DEFAULT_SQUARE_OFF_TIME ?? '15:20' ).split(':') @@ -52,7 +52,7 @@ export function getDefaultSquareOffTime () { .format() } -export function getSchedulingStateProps (strategy: STRATEGIES) { +export function getSchedulingStateProps(strategy: STRATEGIES) { return { runNow: false, isAutoSquareOffEnabled: true, @@ -61,7 +61,7 @@ export function getSchedulingStateProps (strategy: STRATEGIES) { } } -export function commonOnChangeHandler ( +export function commonOnChangeHandler( changedProps: Partial, state: AvailablePlansConfig, setState: Dispatch @@ -93,20 +93,20 @@ const getSchedulingApiProps = ({ runAt: runNow ? dayjs().format() : dayjs(runAt) - .set('seconds', 0) - .format(), + .set('seconds', 0) + .format(), autoSquareOffProps: isAutoSquareOffEnabled ? { - time: squareOffTime, - deletePendingOrders: - exitStrategy !== EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD - } + time: squareOffTime, + deletePendingOrders: + exitStrategy !== EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD + } : undefined, expiresAt: expireIfUnsuccessfulInMins ? dayjs(runNow ? new Date() : runAt) - .add(Number(expireIfUnsuccessfulInMins), 'minutes') - .set('seconds', 0) - .format() + .add(Number(expireIfUnsuccessfulInMins), 'minutes') + .set('seconds', 0) + .format() : undefined }) @@ -117,6 +117,7 @@ export const formatFormDataForApi = ({ strategy: string data: AvailablePlansConfig }): SUPPORTED_TRADE_CONFIG | null => { + // TODO: Ask aakash what to do here? const getOnSquareOffSetAborted = ({ exitStrategy, combinedExitStrategy }) => exitStrategy === EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD && combinedExitStrategy === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL @@ -169,7 +170,9 @@ export const formatFormDataForApi = ({ trailEveryPercentageChangeValue, trailingSlPercent, exitStrategy, - combinedExitStrategy + combinedExitStrategy, + combinedExitStrategyLosing, + combinedExitStrategyWinning, } = data as ATM_STRADDLE_CONFIG const apiProps: ATM_STRADDLE_TRADE = { @@ -186,6 +189,8 @@ export const formatFormDataForApi = ({ }), maxSkewPercent: Number(maxSkewPercent), thresholdSkewPercent: Number(thresholdSkewPercent), + combinedExitStrategyLosing, + combinedExitStrategyWinning, ...getSchedulingApiProps({ isAutoSquareOffEnabled, squareOffTime, @@ -212,7 +217,9 @@ export const formatFormDataForApi = ({ trailingSlPercent, exitStrategy, expireIfUnsuccessfulInMins, - combinedExitStrategy + combinedExitStrategy, + combinedExitStrategyLosing, + combinedExitStrategyWinning, } = data as ATM_STRANGLE_CONFIG const apiProps: ATM_STRANGLE_TRADE = { @@ -228,6 +235,8 @@ export const formatFormDataForApi = ({ combinedExitStrategy }), inverted: Boolean(inverted), + combinedExitStrategyLosing, + combinedExitStrategyWinning, ...getSchedulingApiProps({ isAutoSquareOffEnabled, squareOffTime, diff --git a/lib/constants.ts b/lib/constants.ts index 0cade471..1a997ed4 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -87,6 +87,16 @@ export enum ANCILLARY_TASKS { CLEANUP_COMPLETED_JOBS = 'CLEANUP_COMPLETED_JOBS' } +export enum POSITION_STATE { + WINNING = 'WINNING', + LOSING = 'LOSING' +} + +export const POSITION_STATE_LABEL = { + [POSITION_STATE.WINNING]: 'If winning', + [POSITION_STATE.LOSING]: 'If losing' +} + export const COMBINED_SL_EXIT_STRATEGY_LABEL = { [COMBINED_SL_EXIT_STRATEGY.EXIT_ALL]: 'Exit all legs', [COMBINED_SL_EXIT_STRATEGY.EXIT_LOSING]: @@ -133,6 +143,8 @@ export const STRATEGIES_DETAILS = { slOrderType: SL_ORDER_TYPE.SLM, slLimitPricePercent: 1, combinedExitStrategy: COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, + combinedExitStrategyWinning: COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, + combinedExitStrategyLosing: COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, rollback: { onBrokenHedgeOrders: false, onBrokenPrimaryOrders: false, @@ -169,6 +181,8 @@ export const STRATEGIES_DETAILS = { slOrderType: SL_ORDER_TYPE.SLM, slLimitPricePercent: 1, combinedExitStrategy: COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, + combinedExitStrategyWinning: COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, + combinedExitStrategyLosing: COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, rollback: { onBrokenHedgeOrders: false, onBrokenPrimaryOrders: false, diff --git a/lib/exit-strategies/multiLegPremiumThreshold.ts b/lib/exit-strategies/multiLegPremiumThreshold.ts index d41b9fa4..0eef468a 100644 --- a/lib/exit-strategies/multiLegPremiumThreshold.ts +++ b/lib/exit-strategies/multiLegPremiumThreshold.ts @@ -30,7 +30,7 @@ import dayjs from 'dayjs' import { KiteOrder } from '../../types/kite' import { COMBINED_SL_EXIT_STRATEGY } from '../../types/plans' import { ATM_STRADDLE_TRADE, ATM_STRANGLE_TRADE } from '../../types/trade' -import { EXIT_STRATEGIES, USER_OVERRIDE } from '../constants' +import { EXIT_STRATEGIES, USER_OVERRIDE, VOLATILITY_TYPE } from '../constants' import console from '../logging' import { addToNextQueue, EXIT_TRADING_Q_NAME } from '../queue' import { @@ -76,7 +76,7 @@ export type CombinedPremiumJobDataInterface = ( lastTrailingSlTriggerAtPremium?: number } -async function multiLegPremiumThreshold ({ +async function multiLegPremiumThreshold({ initialJobData, rawKiteOrdersResponse, squareOffOrders @@ -99,6 +99,9 @@ async function multiLegPremiumThreshold ({ trailEveryPercentageChangeValue, lastTrailingSlTriggerAtPremium, combinedExitStrategy = COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, + combinedExitStrategyLosing, + combinedExitStrategyWinning, + volatilityType = VOLATILITY_TYPE.SHORT, _id: dbId } = initialJobData const kite = syncGetKiteInstance(user) @@ -159,8 +162,8 @@ async function multiLegPremiumThreshold ({ if (trailEveryPercentageChangeValue) { const trailingSlTotalPremium = lastTrailingSlTriggerAtPremium ? lastTrailingSlTriggerAtPremium + - ((trailingSlPercent ?? slmPercent) / 100) * - lastTrailingSlTriggerAtPremium + ((trailingSlPercent ?? slmPercent) / 100) * + lastTrailingSlTriggerAtPremium : null // 418 checkAgainstSl = trailingSlTotalPremium ?? initialSlTotalPremium // 418 @@ -175,7 +178,7 @@ async function multiLegPremiumThreshold ({ if ( changeFromLastInflectionPoint < 0 && Math.abs(changeFromLastInflectionPoint) >= - trailEveryPercentageChangeValue + trailEveryPercentageChangeValue ) { // update lastTrailingSlTriggerAtPremium // if current liveTotalPremium is X% lesser than trailEveryPercentageChangeValue @@ -233,42 +236,50 @@ async function multiLegPremiumThreshold ({ const exitMsg = `☢️ [multiLegPremiumThreshold] triggered! liveTotalPremium (${liveTotalPremium}) > threshold (${checkAgainstSl})` console.log(exitMsg) - if (combinedExitStrategy === COMBINED_SL_EXIT_STRATEGY.EXIT_LOSING) { - // get the avg entry prices - const avgSymbolPrice = legsOrders.reduce( - (accum, order) => ({ - ...accum, - [order.tradingsymbol]: order.average_price - }), - {} - ) + // get the avg entry prices + const avgSymbolPriceAndQty = legsOrders.reduce( + (accum, order) => ({ + ...accum, + [order.tradingsymbol]: { + price: order.average_price, + qty: order.quantity + } + }), + {} + ) - // console.log('avgSymbolPrice', logDeep(avgSymbolPrice)) - - // future proofing by allowing for any number of positions to be trailed together - const { losingLegs, winningLegs } = liveSymbolPrices.reduce( - (accum, leg) => { - const { lastPrice, tradingSymbol } = leg - if (avgSymbolPrice[tradingSymbol] < lastPrice) { - return { - ...accum, - losingLegs: [...accum.losingLegs, leg] - } - } + // console.log('avgSymbolPrice', logDeep(avgSymbolPrice)) + let totalLoss = 0, totalProfit = 0; + // future proofing by allowing for any number of positions to be trailed together + const { losingLegs, winningLegs } = liveSymbolPrices.reduce( + (accum, leg) => { + const { lastPrice, tradingSymbol } = leg + const isLosingLeg = volatilityType === VOLATILITY_TYPE.SHORT ? avgSymbolPriceAndQty[tradingSymbol].price < lastPrice : avgSymbolPriceAndQty[tradingSymbol].price > lastPrice + + if (isLosingLeg) { + totalLoss += Math.abs((lastPrice - avgSymbolPriceAndQty[tradingSymbol].price) * avgSymbolPriceAndQty[tradingSymbol].quantity) return { ...accum, - winningLegs: [...accum.winningLegs, leg] + losingLegs: [...accum.losingLegs, leg] } - }, - { - losingLegs: [], - winningLegs: [] } - ) - // console.log('losingLegs', logDeep(losingLegs)) - // console.log('winningLegs', logDeep(winningLegs)) + totalProfit += Math.abs((lastPrice - avgSymbolPriceAndQty[tradingSymbol].price) * avgSymbolPriceAndQty[tradingSymbol].quantity) + return { + ...accum, + winningLegs: [...accum.winningLegs, leg] + } + }, + { + losingLegs: [], + winningLegs: [] + } + ) + + // console.log('losingLegs', logDeep(losingLegs)) + // console.log('winningLegs', logDeep(winningLegs)) + const handleExitLosingLeg = async () => { const squareOffLosingLegs = losingLegs.map(losingLeg => legsOrders.find( legOrder => legOrder.tradingsymbol === losingLeg.tradingSymbol @@ -300,6 +311,28 @@ async function multiLegPremiumThreshold ({ rawKiteOrdersResponse: bringToCostOrders } ) + + } + + if (combinedExitStrategyLosing && combinedExitStrategyWinning) { + const isPositionLosing = (totalLoss - totalProfit > 0) ? true : false; + + if (isPositionLosing) { + if (combinedExitStrategyLosing === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) { + return doSquareOffPositions(squareOffOrders!, kite, initialJobData) + } + return handleExitLosingLeg(); + } else { + if (combinedExitStrategyWinning === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) { + return doSquareOffPositions(squareOffOrders!, kite, initialJobData) + } + return handleExitLosingLeg(); + } + + } + + if (combinedExitStrategy === COMBINED_SL_EXIT_STRATEGY.EXIT_LOSING) { + return handleExitLosingLeg(); } return doSquareOffPositions(squareOffOrders!, kite, initialJobData) diff --git a/types/plans.ts b/types/plans.ts index c857ec18..c1a49683 100644 --- a/types/plans.ts +++ b/types/plans.ts @@ -63,6 +63,8 @@ export interface ATM_STRADDLE_CONFIG extends SavedPlanMeta { slOrderType: SL_ORDER_TYPE slLimitPricePercent?: number combinedExitStrategy?: COMBINED_SL_EXIT_STRATEGY + combinedExitStrategyWinning?: COMBINED_SL_EXIT_STRATEGY + combinedExitStrategyLosing?: COMBINED_SL_EXIT_STRATEGY } export interface ATM_STRANGLE_CONFIG extends SavedPlanMeta { @@ -88,6 +90,8 @@ export interface ATM_STRANGLE_CONFIG extends SavedPlanMeta { slOrderType: SL_ORDER_TYPE slLimitPricePercent?: number combinedExitStrategy?: COMBINED_SL_EXIT_STRATEGY + combinedExitStrategyWinning?: COMBINED_SL_EXIT_STRATEGY + combinedExitStrategyLosing?: COMBINED_SL_EXIT_STRATEGY } export interface DIRECTIONAL_OPTION_SELLING_CONFIG extends SavedPlanMeta { From c5824011b93f363333366ddea633c590a5b92036 Mon Sep 17 00:00:00 2001 From: mkm279 Date: Fri, 10 Sep 2021 12:05:14 +0530 Subject: [PATCH 2/3] code cleanup for multilegpremiumthreshold --- lib/browserUtils.ts | 13 -- .../multiLegPremiumThreshold.ts | 142 +++++++++--------- 2 files changed, 75 insertions(+), 80 deletions(-) diff --git a/lib/browserUtils.ts b/lib/browserUtils.ts index e62f9bf3..2edbbea1 100644 --- a/lib/browserUtils.ts +++ b/lib/browserUtils.ts @@ -117,11 +117,6 @@ export const formatFormDataForApi = ({ strategy: string data: AvailablePlansConfig }): SUPPORTED_TRADE_CONFIG | null => { - // TODO: Ask aakash what to do here? - const getOnSquareOffSetAborted = ({ exitStrategy, combinedExitStrategy }) => - exitStrategy === EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD && - combinedExitStrategy === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL - switch (strategy) { case STRATEGIES.DIRECTIONAL_OPTION_SELLING: { const { @@ -183,10 +178,6 @@ export const formatFormDataForApi = ({ trailEveryPercentageChangeValue ), trailingSlPercent: Number(trailingSlPercent), - onSquareOffSetAborted: getOnSquareOffSetAborted({ - exitStrategy, - combinedExitStrategy - }), maxSkewPercent: Number(maxSkewPercent), thresholdSkewPercent: Number(thresholdSkewPercent), combinedExitStrategyLosing, @@ -230,10 +221,6 @@ export const formatFormDataForApi = ({ trailEveryPercentageChangeValue ), trailingSlPercent: Number(trailingSlPercent), - onSquareOffSetAborted: getOnSquareOffSetAborted({ - exitStrategy, - combinedExitStrategy - }), inverted: Boolean(inverted), combinedExitStrategyLosing, combinedExitStrategyWinning, diff --git a/lib/exit-strategies/multiLegPremiumThreshold.ts b/lib/exit-strategies/multiLegPremiumThreshold.ts index 0eef468a..1520d23a 100644 --- a/lib/exit-strategies/multiLegPremiumThreshold.ts +++ b/lib/exit-strategies/multiLegPremiumThreshold.ts @@ -44,6 +44,10 @@ import { import { doSquareOffPositions } from './autoSquareOff' +const isPositionLosing = (volatilityType: VOLATILITY_TYPE, currentPrice: number, buyPrice: number) => { + return volatilityType === VOLATILITY_TYPE.SHORT ? currentPrice > buyPrice : currentPrice < buyPrice; +} + const patchTradeWithTrailingSL = async ({ dbId, trailingSl }) => { try { await patchDbTrade({ @@ -69,6 +73,41 @@ const tradeHeartbeat = async dbId => { return data } +const handleExitLosingLeg = async (losingLegs: GET_LTP_RESPONSE[], winningLegs: GET_LTP_RESPONSE[], legsOrders: KiteOrder[], kite: any, initialJobData: CombinedPremiumJobDataInterface) => { + const squareOffLosingLegs = losingLegs.map(losingLeg => + legsOrders.find( + legOrder => legOrder.tradingsymbol === losingLeg.tradingSymbol + ) + ) + // console.log('squareOffLosingLegs', logDeep(squareOffLosingLegs)) + const bringToCostOrders = winningLegs.map(winningLeg => + legsOrders.find( + legOrder => legOrder.tradingsymbol === winningLeg.tradingSymbol + ) + ) + // console.log('bringToCostOrders', logDeep(bringToCostOrders)) + // 1. square off losing legs + await doSquareOffPositions( + squareOffLosingLegs as KiteOrder[], + kite, + initialJobData + ) + // 2. bring the winning legs to cost + return await addToNextQueue( + { + ...initialJobData, + // override the slmPercent and exitStrategy in initialJobData + slmPercent: 0, + exitStrategy: EXIT_STRATEGIES.INDIVIDUAL_LEG_SLM_1X + }, + { + _nextTradingQueue: EXIT_TRADING_Q_NAME, + rawKiteOrdersResponse: bringToCostOrders + } + ) + +} + export type CombinedPremiumJobDataInterface = ( | ATM_STRADDLE_TRADE | ATM_STRANGLE_TRADE @@ -98,9 +137,8 @@ async function multiLegPremiumThreshold({ user, trailEveryPercentageChangeValue, lastTrailingSlTriggerAtPremium, - combinedExitStrategy = COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, - combinedExitStrategyLosing, - combinedExitStrategyWinning, + combinedExitStrategyLosing = COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, + combinedExitStrategyWinning = COMBINED_SL_EXIT_STRATEGY.EXIT_ALL, volatilityType = VOLATILITY_TYPE.SHORT, _id: dbId } = initialJobData @@ -167,7 +205,7 @@ async function multiLegPremiumThreshold({ : null // 418 checkAgainstSl = trailingSlTotalPremium ?? initialSlTotalPremium // 418 - if (liveTotalPremium < checkAgainstSl) { + if (!isPositionLosing(volatilityType, liveTotalPremium, checkAgainstSl)) { const lastInflectionPoint = lastTrailingSlTriggerAtPremium ?? initialPremiumReceived // 380 // liveTotalPremium = 360 @@ -227,44 +265,53 @@ async function multiLegPremiumThreshold({ } } - if (liveTotalPremium < checkAgainstSl) { + /** + * SHORT + * ce = 100, pe=100, sl-10 + * initialPremium = 200 + * initialCheckAgainstSl = 220 + * + * livePremium = 180, checkAgainstSL = 198 + * + * livePremium = 190, checkAgainstSL = 198 + * + * livePremium = 200 -> sl triggered + * ce = 120, pe = 80 + * losingleg = ce, winingleg = pe + */ + + if (!isPositionLosing(volatilityType, liveTotalPremium, checkAgainstSl)) { const rejectMsg = `🟢 [multiLegPremiumThreshold] liveTotalPremium (${liveTotalPremium}) < threshold (${checkAgainstSl})` return Promise.reject(new Error(rejectMsg)) } // terminate the checker - const exitMsg = `☢️ [multiLegPremiumThreshold] triggered! liveTotalPremium (${liveTotalPremium}) > threshold (${checkAgainstSl})` + const exitMsg = `☢️ [multiLegPremiumThreshold] triggered for ${volatilityType} position! liveTotalPremium (${liveTotalPremium}) & threshold (${checkAgainstSl})` console.log(exitMsg) // get the avg entry prices - const avgSymbolPriceAndQty = legsOrders.reduce( + const avgSymbolPrice = legsOrders.reduce( (accum, order) => ({ ...accum, - [order.tradingsymbol]: { - price: order.average_price, - qty: order.quantity - } + [order.tradingsymbol]: order.average_price }), {} ) // console.log('avgSymbolPrice', logDeep(avgSymbolPrice)) - let totalLoss = 0, totalProfit = 0; // future proofing by allowing for any number of positions to be trailed together const { losingLegs, winningLegs } = liveSymbolPrices.reduce( (accum, leg) => { const { lastPrice, tradingSymbol } = leg - const isLosingLeg = volatilityType === VOLATILITY_TYPE.SHORT ? avgSymbolPriceAndQty[tradingSymbol].price < lastPrice : avgSymbolPriceAndQty[tradingSymbol].price > lastPrice + const isLosingLeg = isPositionLosing(volatilityType, lastPrice, avgSymbolPrice[tradingSymbol]) if (isLosingLeg) { - totalLoss += Math.abs((lastPrice - avgSymbolPriceAndQty[tradingSymbol].price) * avgSymbolPriceAndQty[tradingSymbol].quantity) return { ...accum, losingLegs: [...accum.losingLegs, leg] } } - totalProfit += Math.abs((lastPrice - avgSymbolPriceAndQty[tradingSymbol].price) * avgSymbolPriceAndQty[tradingSymbol].quantity) return { ...accum, winningLegs: [...accum.winningLegs, leg] @@ -279,63 +326,24 @@ async function multiLegPremiumThreshold({ // console.log('losingLegs', logDeep(losingLegs)) // console.log('winningLegs', logDeep(winningLegs)) - const handleExitLosingLeg = async () => { - const squareOffLosingLegs = losingLegs.map(losingLeg => - legsOrders.find( - legOrder => legOrder.tradingsymbol === losingLeg.tradingSymbol - ) - ) - // console.log('squareOffLosingLegs', logDeep(squareOffLosingLegs)) - const bringToCostOrders = winningLegs.map(winningLeg => - legsOrders.find( - legOrder => legOrder.tradingsymbol === winningLeg.tradingSymbol - ) - ) - // console.log('bringToCostOrders', logDeep(bringToCostOrders)) - // 1. square off losing legs - await doSquareOffPositions( - squareOffLosingLegs as KiteOrder[], - kite, - initialJobData - ) - // 2. bring the winning legs to cost - return await addToNextQueue( - { - ...initialJobData, - // override the slmPercent and exitStrategy in initialJobData - slmPercent: 0, - exitStrategy: EXIT_STRATEGIES.INDIVIDUAL_LEG_SLM_1X - }, - { - _nextTradingQueue: EXIT_TRADING_Q_NAME, - rawKiteOrdersResponse: bringToCostOrders - } - ) - - } + const isPositionInLoss = isPositionLosing(volatilityType, liveTotalPremium, initialSlTotalPremium) - if (combinedExitStrategyLosing && combinedExitStrategyWinning) { - const isPositionLosing = (totalLoss - totalProfit > 0) ? true : false; + console.log(`☢️ [multiLegPremiumThreshold] ${volatilityType} position is ${isPositionInLoss ? 'losing' : 'winning'}! liveTotalPremium (${liveTotalPremium}) & initialSlTotalPremium (${initialSlTotalPremium})`) - if (isPositionLosing) { - if (combinedExitStrategyLosing === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) { - return doSquareOffPositions(squareOffOrders!, kite, initialJobData) - } - return handleExitLosingLeg(); - } else { - if (combinedExitStrategyWinning === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) { - return doSquareOffPositions(squareOffOrders!, kite, initialJobData) - } - return handleExitLosingLeg(); + if (isPositionInLoss) { + console.log(`☢️ [multiLegPremiumThreshold] ${combinedExitStrategyLosing} trigered for losing ${volatilityType} position`) + if (combinedExitStrategyLosing === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) { + return doSquareOffPositions(squareOffOrders!, kite, { ...initialJobData, onSquareOffSetAborted: true }) } - - } - - if (combinedExitStrategy === COMBINED_SL_EXIT_STRATEGY.EXIT_LOSING) { - return handleExitLosingLeg(); + return handleExitLosingLeg(losingLegs, winningLegs, legsOrders, kite, initialJobData); + } else { + console.log(`☢️ [multiLegPremiumThreshold] ${combinedExitStrategyLosing} trigered for winning ${volatilityType} position`) + if (combinedExitStrategyWinning === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) { + return doSquareOffPositions(squareOffOrders!, kite, { ...initialJobData, onSquareOffSetAborted: true }) + } + return handleExitLosingLeg(losingLegs, winningLegs, legsOrders, kite, initialJobData); } - return doSquareOffPositions(squareOffOrders!, kite, initialJobData) } catch (e) { console.log('☢️ [multiLegPremiumThreshold] terminated', e) return Promise.resolve(e) From 9654d19ad7490171ff5c6291a1f804b9108c9d6d Mon Sep 17 00:00:00 2001 From: mkm279 Date: Mon, 13 Sep 2021 14:49:17 +0530 Subject: [PATCH 3/3] formatted changed files --- components/lib/SlManagerComponent.tsx | 41 +++++---- lib/browserUtils.ts | 30 +++---- .../multiLegPremiumThreshold.ts | 88 ++++++++++++++----- 3 files changed, 107 insertions(+), 52 deletions(-) diff --git a/components/lib/SlManagerComponent.tsx b/components/lib/SlManagerComponent.tsx index 3073bfaa..feec1f56 100644 --- a/components/lib/SlManagerComponent.tsx +++ b/components/lib/SlManagerComponent.tsx @@ -55,24 +55,32 @@ const SlManagerComponent = ({ state, onChange, exitStrategies }) => { When Combined trailing SL triggers - {[POSITION_STATE.WINNING, POSITION_STATE.LOSING].map(item => + {[POSITION_STATE.WINNING, POSITION_STATE.LOSING].map(item => ( - + {POSITION_STATE_LABEL[item]} - onChange(item === POSITION_STATE.WINNING ? { - combinedExitStrategyWinning: e.target - .value as COMBINED_SL_EXIT_STRATEGY - } : { - combinedExitStrategyLosing: e.target - .value as COMBINED_SL_EXIT_STRATEGY - }) + onChange( + item === POSITION_STATE.WINNING + ? { + combinedExitStrategyWinning: e.target + .value as COMBINED_SL_EXIT_STRATEGY + } + : { + combinedExitStrategyLosing: e.target + .value as COMBINED_SL_EXIT_STRATEGY + } + ) } > {[ @@ -85,15 +93,18 @@ const SlManagerComponent = ({ state, onChange, exitStrategies }) => { control={} label={ - {COMBINED_SL_EXIT_STRATEGY_LABEL[combinedExitStrategy]} + { + COMBINED_SL_EXIT_STRATEGY_LABEL[ + combinedExitStrategy + ] + } } /> ))} - - )} + ))} @@ -138,8 +149,8 @@ const SlManagerComponent = ({ state, onChange, exitStrategies }) => { {state.exitStrategy !== EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD || - (state.exitStrategy === EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD && - state.combinedExitStrategy === + (state.exitStrategy === EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD && + state.combinedExitStrategy === COMBINED_SL_EXIT_STRATEGY.EXIT_LOSING) ? ( diff --git a/lib/browserUtils.ts b/lib/browserUtils.ts index 2edbbea1..8d37d577 100644 --- a/lib/browserUtils.ts +++ b/lib/browserUtils.ts @@ -30,7 +30,7 @@ export const ensureIST = date => { return datetimeInIST } -export function getScheduleableTradeTime(strategy: STRATEGIES) { +export function getScheduleableTradeTime (strategy: STRATEGIES) { const defaultDate = dayjs(STRATEGIES_DETAILS[strategy].defaultRunAt).format() if (dayjs().isAfter(dayjs(defaultDate))) { @@ -42,7 +42,7 @@ export function getScheduleableTradeTime(strategy: STRATEGIES) { return defaultDate } -export function getDefaultSquareOffTime() { +export function getDefaultSquareOffTime () { const [hours, minutes] = ( process.env.NEXT_PUBLIC_DEFAULT_SQUARE_OFF_TIME ?? '15:20' ).split(':') @@ -52,7 +52,7 @@ export function getDefaultSquareOffTime() { .format() } -export function getSchedulingStateProps(strategy: STRATEGIES) { +export function getSchedulingStateProps (strategy: STRATEGIES) { return { runNow: false, isAutoSquareOffEnabled: true, @@ -61,7 +61,7 @@ export function getSchedulingStateProps(strategy: STRATEGIES) { } } -export function commonOnChangeHandler( +export function commonOnChangeHandler ( changedProps: Partial, state: AvailablePlansConfig, setState: Dispatch @@ -93,20 +93,20 @@ const getSchedulingApiProps = ({ runAt: runNow ? dayjs().format() : dayjs(runAt) - .set('seconds', 0) - .format(), + .set('seconds', 0) + .format(), autoSquareOffProps: isAutoSquareOffEnabled ? { - time: squareOffTime, - deletePendingOrders: - exitStrategy !== EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD - } + time: squareOffTime, + deletePendingOrders: + exitStrategy !== EXIT_STRATEGIES.MULTI_LEG_PREMIUM_THRESHOLD + } : undefined, expiresAt: expireIfUnsuccessfulInMins ? dayjs(runNow ? new Date() : runAt) - .add(Number(expireIfUnsuccessfulInMins), 'minutes') - .set('seconds', 0) - .format() + .add(Number(expireIfUnsuccessfulInMins), 'minutes') + .set('seconds', 0) + .format() : undefined }) @@ -167,7 +167,7 @@ export const formatFormDataForApi = ({ exitStrategy, combinedExitStrategy, combinedExitStrategyLosing, - combinedExitStrategyWinning, + combinedExitStrategyWinning } = data as ATM_STRADDLE_CONFIG const apiProps: ATM_STRADDLE_TRADE = { @@ -210,7 +210,7 @@ export const formatFormDataForApi = ({ expireIfUnsuccessfulInMins, combinedExitStrategy, combinedExitStrategyLosing, - combinedExitStrategyWinning, + combinedExitStrategyWinning } = data as ATM_STRANGLE_CONFIG const apiProps: ATM_STRANGLE_TRADE = { diff --git a/lib/exit-strategies/multiLegPremiumThreshold.ts b/lib/exit-strategies/multiLegPremiumThreshold.ts index 1520d23a..d2ecf498 100644 --- a/lib/exit-strategies/multiLegPremiumThreshold.ts +++ b/lib/exit-strategies/multiLegPremiumThreshold.ts @@ -44,8 +44,14 @@ import { import { doSquareOffPositions } from './autoSquareOff' -const isPositionLosing = (volatilityType: VOLATILITY_TYPE, currentPrice: number, buyPrice: number) => { - return volatilityType === VOLATILITY_TYPE.SHORT ? currentPrice > buyPrice : currentPrice < buyPrice; +const isPositionLosing = ( + volatilityType: VOLATILITY_TYPE, + currentPrice: number, + buyPrice: number +) => { + return volatilityType === VOLATILITY_TYPE.SHORT + ? currentPrice > buyPrice + : currentPrice < buyPrice } const patchTradeWithTrailingSL = async ({ dbId, trailingSl }) => { @@ -73,7 +79,13 @@ const tradeHeartbeat = async dbId => { return data } -const handleExitLosingLeg = async (losingLegs: GET_LTP_RESPONSE[], winningLegs: GET_LTP_RESPONSE[], legsOrders: KiteOrder[], kite: any, initialJobData: CombinedPremiumJobDataInterface) => { +const handleExitLosingLeg = async ( + losingLegs: GET_LTP_RESPONSE[], + winningLegs: GET_LTP_RESPONSE[], + legsOrders: KiteOrder[], + kite: any, + initialJobData: CombinedPremiumJobDataInterface +) => { const squareOffLosingLegs = losingLegs.map(losingLeg => legsOrders.find( legOrder => legOrder.tradingsymbol === losingLeg.tradingSymbol @@ -105,7 +117,6 @@ const handleExitLosingLeg = async (losingLegs: GET_LTP_RESPONSE[], winningLegs: rawKiteOrdersResponse: bringToCostOrders } ) - } export type CombinedPremiumJobDataInterface = ( @@ -115,7 +126,7 @@ export type CombinedPremiumJobDataInterface = ( lastTrailingSlTriggerAtPremium?: number } -async function multiLegPremiumThreshold({ +async function multiLegPremiumThreshold ({ initialJobData, rawKiteOrdersResponse, squareOffOrders @@ -200,8 +211,8 @@ async function multiLegPremiumThreshold({ if (trailEveryPercentageChangeValue) { const trailingSlTotalPremium = lastTrailingSlTriggerAtPremium ? lastTrailingSlTriggerAtPremium + - ((trailingSlPercent ?? slmPercent) / 100) * - lastTrailingSlTriggerAtPremium + ((trailingSlPercent ?? slmPercent) / 100) * + lastTrailingSlTriggerAtPremium : null // 418 checkAgainstSl = trailingSlTotalPremium ?? initialSlTotalPremium // 418 @@ -216,7 +227,7 @@ async function multiLegPremiumThreshold({ if ( changeFromLastInflectionPoint < 0 && Math.abs(changeFromLastInflectionPoint) >= - trailEveryPercentageChangeValue + trailEveryPercentageChangeValue ) { // update lastTrailingSlTriggerAtPremium // if current liveTotalPremium is X% lesser than trailEveryPercentageChangeValue @@ -266,15 +277,15 @@ async function multiLegPremiumThreshold({ } /** - * SHORT + * SHORT * ce = 100, pe=100, sl-10 * initialPremium = 200 * initialCheckAgainstSl = 220 - * + * * livePremium = 180, checkAgainstSL = 198 - * + * * livePremium = 190, checkAgainstSL = 198 - * + * * livePremium = 200 -> sl triggered * ce = 120, pe = 80 * losingleg = ce, winingleg = pe @@ -303,7 +314,11 @@ async function multiLegPremiumThreshold({ const { losingLegs, winningLegs } = liveSymbolPrices.reduce( (accum, leg) => { const { lastPrice, tradingSymbol } = leg - const isLosingLeg = isPositionLosing(volatilityType, lastPrice, avgSymbolPrice[tradingSymbol]) + const isLosingLeg = isPositionLosing( + volatilityType, + lastPrice, + avgSymbolPrice[tradingSymbol] + ) if (isLosingLeg) { return { @@ -326,24 +341,53 @@ async function multiLegPremiumThreshold({ // console.log('losingLegs', logDeep(losingLegs)) // console.log('winningLegs', logDeep(winningLegs)) - const isPositionInLoss = isPositionLosing(volatilityType, liveTotalPremium, initialSlTotalPremium) + const isPositionInLoss = isPositionLosing( + volatilityType, + liveTotalPremium, + initialSlTotalPremium + ) - console.log(`☢️ [multiLegPremiumThreshold] ${volatilityType} position is ${isPositionInLoss ? 'losing' : 'winning'}! liveTotalPremium (${liveTotalPremium}) & initialSlTotalPremium (${initialSlTotalPremium})`) + console.log( + `☢️ [multiLegPremiumThreshold] ${volatilityType} position is ${ + isPositionInLoss ? 'losing' : 'winning' + }! liveTotalPremium (${liveTotalPremium}) & initialSlTotalPremium (${initialSlTotalPremium})` + ) if (isPositionInLoss) { - console.log(`☢️ [multiLegPremiumThreshold] ${combinedExitStrategyLosing} trigered for losing ${volatilityType} position`) + console.log( + `☢️ [multiLegPremiumThreshold] ${combinedExitStrategyLosing} trigered for losing ${volatilityType} position` + ) if (combinedExitStrategyLosing === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) { - return doSquareOffPositions(squareOffOrders!, kite, { ...initialJobData, onSquareOffSetAborted: true }) + return doSquareOffPositions(squareOffOrders!, kite, { + ...initialJobData, + onSquareOffSetAborted: true + }) } - return handleExitLosingLeg(losingLegs, winningLegs, legsOrders, kite, initialJobData); + return handleExitLosingLeg( + losingLegs, + winningLegs, + legsOrders, + kite, + initialJobData + ) } else { - console.log(`☢️ [multiLegPremiumThreshold] ${combinedExitStrategyLosing} trigered for winning ${volatilityType} position`) + console.log( + `☢️ [multiLegPremiumThreshold] ${combinedExitStrategyLosing} trigered for winning ${volatilityType} position` + ) if (combinedExitStrategyWinning === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) { - return doSquareOffPositions(squareOffOrders!, kite, { ...initialJobData, onSquareOffSetAborted: true }) + return doSquareOffPositions(squareOffOrders!, kite, { + ...initialJobData, + onSquareOffSetAborted: true + }) } - return handleExitLosingLeg(losingLegs, winningLegs, legsOrders, kite, initialJobData); + return handleExitLosingLeg( + losingLegs, + winningLegs, + legsOrders, + kite, + initialJobData + ) } - } catch (e) { console.log('☢️ [multiLegPremiumThreshold] terminated', e) return Promise.resolve(e)