diff --git a/components/lib/SlManagerComponent.tsx b/components/lib/SlManagerComponent.tsx
index 8375a385..feec1f56 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,56 @@ 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]}
+
+
- ))}
-
+ style={{ paddingLeft: '4px' }}
+ onChange={e =>
+ 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
+ ]
+ }
+
+ }
+ />
+ ))}
+
+
+ ))}
diff --git a/lib/browserUtils.ts b/lib/browserUtils.ts
index 0152c2db..8d37d577 100644
--- a/lib/browserUtils.ts
+++ b/lib/browserUtils.ts
@@ -117,10 +117,6 @@ export const formatFormDataForApi = ({
strategy: string
data: AvailablePlansConfig
}): SUPPORTED_TRADE_CONFIG | null => {
- 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 {
@@ -169,7 +165,9 @@ export const formatFormDataForApi = ({
trailEveryPercentageChangeValue,
trailingSlPercent,
exitStrategy,
- combinedExitStrategy
+ combinedExitStrategy,
+ combinedExitStrategyLosing,
+ combinedExitStrategyWinning
} = data as ATM_STRADDLE_CONFIG
const apiProps: ATM_STRADDLE_TRADE = {
@@ -180,12 +178,10 @@ export const formatFormDataForApi = ({
trailEveryPercentageChangeValue
),
trailingSlPercent: Number(trailingSlPercent),
- onSquareOffSetAborted: getOnSquareOffSetAborted({
- exitStrategy,
- combinedExitStrategy
- }),
maxSkewPercent: Number(maxSkewPercent),
thresholdSkewPercent: Number(thresholdSkewPercent),
+ combinedExitStrategyLosing,
+ combinedExitStrategyWinning,
...getSchedulingApiProps({
isAutoSquareOffEnabled,
squareOffTime,
@@ -212,7 +208,9 @@ export const formatFormDataForApi = ({
trailingSlPercent,
exitStrategy,
expireIfUnsuccessfulInMins,
- combinedExitStrategy
+ combinedExitStrategy,
+ combinedExitStrategyLosing,
+ combinedExitStrategyWinning
} = data as ATM_STRANGLE_CONFIG
const apiProps: ATM_STRANGLE_TRADE = {
@@ -223,11 +221,9 @@ export const formatFormDataForApi = ({
trailEveryPercentageChangeValue
),
trailingSlPercent: Number(trailingSlPercent),
- onSquareOffSetAborted: getOnSquareOffSetAborted({
- exitStrategy,
- 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..d2ecf498 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 {
@@ -44,6 +44,16 @@ 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 +79,46 @@ 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,7 +148,9 @@ async function multiLegPremiumThreshold ({
user,
trailEveryPercentageChangeValue,
lastTrailingSlTriggerAtPremium,
- combinedExitStrategy = COMBINED_SL_EXIT_STRATEGY.EXIT_ALL,
+ combinedExitStrategyLosing = COMBINED_SL_EXIT_STRATEGY.EXIT_ALL,
+ combinedExitStrategyWinning = COMBINED_SL_EXIT_STRATEGY.EXIT_ALL,
+ volatilityType = VOLATILITY_TYPE.SHORT,
_id: dbId
} = initialJobData
const kite = syncGetKiteInstance(user)
@@ -164,7 +216,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
@@ -224,85 +276,118 @@ 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)
- 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 avgSymbolPrice = legsOrders.reduce(
+ (accum, order) => ({
+ ...accum,
+ [order.tradingsymbol]: order.average_price
+ }),
+ {}
+ )
- // 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))
+ // 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 = isPositionLosing(
+ volatilityType,
+ lastPrice,
+ avgSymbolPrice[tradingSymbol]
+ )
+
+ if (isLosingLeg) {
return {
...accum,
- winningLegs: [...accum.winningLegs, leg]
+ losingLegs: [...accum.losingLegs, leg]
}
- },
- {
- losingLegs: [],
- winningLegs: []
}
- )
- // console.log('losingLegs', logDeep(losingLegs))
- // console.log('winningLegs', logDeep(winningLegs))
+ return {
+ ...accum,
+ winningLegs: [...accum.winningLegs, leg]
+ }
+ },
+ {
+ losingLegs: [],
+ winningLegs: []
+ }
+ )
+
+ // console.log('losingLegs', logDeep(losingLegs))
+ // console.log('winningLegs', logDeep(winningLegs))
- 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
- )
+ const isPositionInLoss = isPositionLosing(
+ volatilityType,
+ liveTotalPremium,
+ 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('bringToCostOrders', logDeep(bringToCostOrders))
- // 1. square off losing legs
- await doSquareOffPositions(
- squareOffLosingLegs as KiteOrder[],
+ if (combinedExitStrategyLosing === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) {
+ return doSquareOffPositions(squareOffOrders!, kite, {
+ ...initialJobData,
+ onSquareOffSetAborted: true
+ })
+ }
+ return handleExitLosingLeg(
+ losingLegs,
+ winningLegs,
+ legsOrders,
kite,
initialJobData
)
- // 2. bring the winning legs to cost
- return await addToNextQueue(
- {
+ } else {
+ console.log(
+ `☢️ [multiLegPremiumThreshold] ${combinedExitStrategyLosing} trigered for winning ${volatilityType} position`
+ )
+ if (combinedExitStrategyWinning === COMBINED_SL_EXIT_STRATEGY.EXIT_ALL) {
+ return doSquareOffPositions(squareOffOrders!, kite, {
...initialJobData,
- // override the slmPercent and exitStrategy in initialJobData
- slmPercent: 0,
- exitStrategy: EXIT_STRATEGIES.INDIVIDUAL_LEG_SLM_1X
- },
- {
- _nextTradingQueue: EXIT_TRADING_Q_NAME,
- rawKiteOrdersResponse: bringToCostOrders
- }
+ 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)
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 {