Skip to main content

Supertrend Crossover

A classic trend-following strategy that enters trades when the Supertrend indicator flips—signaling a potential change in momentum.

Supertrend is loved for its simplicity. It combines price and volatility in a way that visually captures trending conditions. Perfect for beginner-to-intermediate users who want to experiment with entries based on trend confirmation.

Strategy Rules​

Before diving into the code, let’s first clarify the rules that guide this strategy. At its core, this system is a trend-following, options-based strategy that uses the Supertrend indicator applied on the Futures chart.

Entry Conditions​

  • No active positions: The system only takes a trade if no trades are currently open.
  • Time filter: Trades are allowed after 9:20 AM once the market has settled a bit.
  • Supertrend confirmation: If the closing price of the Future is below the Supertrend line, the strategy sells an ATM Call Option (CE).
    • This effectively opens the short straddle setup with the assumption that bearish momentum is in play.
if (self.position == []) and \
(self._triggers == []) and \
(((self.candleTime > datetime.time(9,20,0)) and \
(self.dt_FUT['close'].iloc[-1] < self.idc_FUT_st_10_3.iloc[-1]))):
self.act_ac380()

Exit Conditions​

  • Time filter: If the clock hits 3:00 PM, all open positions are squared off.
  • Position check: Ensures the system exits only when there are active positions.
  • This prevents the strategy from holding positions into market close, which avoids overnight risk.
if (self.position != []) and \
(((self.candleTime >= datetime.time(15,00,0)))):
self.act_ac396()

Imports & Class Shell​

import datetime
import pandas as pd
from eventManager import Strategy, runEvents

class st_ST_1(Strategy):
pass

Strategy is the base class provided by the code builder.

Action Registry​

class st_ST_1(Strategy):
def init(self):
self.actions_all = {
'act_ac380': {'trigger': False, 'legs': []},
'act_ac396': {'trigger': False, 'legs': []}
}

def minTrigger(self):
# Minute-by-minute safety: square-off around 15:15 if anything is still open
if (self.currentCandle + datetime.timedelta(minutes=1)).time() == datetime.time(15, 15, 0):
self.squareoff_all()
self.finish
  • act_ac380 will enter the trade; act_ac396 will exit all.
  • minTrigger runs every minute; we add a safety square-off at 3:15 PM.

Data​

def data_init(self):
self.getCandleDataSym(
dataType='fut',
exp={'expNo': 0, 'expType': 'monthly'},
dataLength=1,
name='dt_FUT',
strike=None,
timeFrame=None,
candleType='candlestick',
opType='None',
asof='None'
)
  • We’ll fetch monthly NIFTY futures into dt_FUT.

Supertrend Setup​

Compute Supertrend (10, 3) on futures: high/low/close → idc_FUT_st_10_3.

def indicator_init(self):
self.applyIndicator(
indicatorName='supertrend',
name='idc_FUT_st_10_3',
high=lambda: self.dt_FUT['high'],
low=lambda: self.dt_FUT['low'],
close=lambda: self.dt_FUT['close'],
multiplier=3,
length=10
)

We’ll use this line as a trend filter for entries.

Entry Action​

This strategy sells only the Call (CE) when bearish per Supertrend.

def act_ac380(self):
self.actions_all['act_ac380']['trigger'] = True
leg = self.addLeg(
transactionType='sell',
optionType='CE',
strikeSelection={'strikeBy': 'premium near', 'strikeVal': 100, 'asof': 'None', 'roundoff': None},
qty=75,
expiry={'expType': 'weekly', 'expNo': 0},
target={'isTarget': False, 'targetOn': 'val', 'targetValue': 1},
SL={'isSL': True, 'SLon': '%', 'SLvalue': 50.00},
trailSL={'isTrailSL': False, 'trailSLon': 'val', 'trailSL_X': 1, 'trailSL_Y': 1},
reEntryTg={'isReEntry': False, 'reEntryOn': 'asap', 'reEntryVal': 0, 'reEntryMaxNo': 20},
reEntrySL={'isReEntry': False, 'reEntryOn': '0', 'reEntryVal': None, 'reEntryMaxNo': 20},
waitTrade={'isWT': False, 'wtOn': 'val-up', 'wtVal': 1, 'triggers': []},
segment='OPT',
entryValidity='Day',
remark='Entering Leg',
squareoff='this',
legName='lg_ac380_1',
webhook={'is': False, 'entryMessage': {}, 'exitMessage': {}}
)
self.actions_all['act_ac380']['legs'].append(leg)
  • ATM Premium-near ₹100.
  • 50% SL on the short position CE for protecting capital.

Exit Action​

Square-off all positions.

def act_ac396(self):
self.actions_all['act_ac396']['trigger'] = True
self.squareoff_all(remark='Square off all')

Daily Orchestration (onNewDay)​

Load data, apply indicators, reset per-day counters.

def onNewDay(self):
self.data_init()
self.indicator_init()
self.entries = {'cnd_ct1': {'max': 0, 'cur': 0}}

Complete Code​

import datetime
import pandas as pd
from eventManager import Strategy, runEvents

class st_ST_1(Strategy):
def init(self):
self.actions_all = {'act_ac380': {'trigger': False, 'legs': []}, 'act_ac396': {'trigger': False, 'legs': []}}

def minTrigger(self):

if (self.currentCandle + datetime.timedelta(minutes=1)).time() == datetime.time(15,15,0):
self.squareoff_all()
self.finish

def data_init(self):
self.getCandleDataSym(dataType='fut', exp={'expNo': 0, 'expType': 'monthly'}, dataLength=1, name='dt_FUT',
strike=None, timeFrame=None, candleType='candlestick',
opType = 'None', asof = 'None')

def indicator_init(self):
self.applyIndicator(indicatorName = 'supertrend', name='idc_FUT_st_10_3', high = lambda : self.dt_FUT['high'], low = lambda : self.dt_FUT['low'], close = lambda : self.dt_FUT['close'], multiplier=3, length=10)


def act_ac380(self):
self.actions_all['act_ac380']['trigger'] = True
leg = self.addLeg(transactionType='sell', optionType='CE',
strikeSelection={'strikeBy' : 'premium near', 'strikeVal':100, 'asof':'None', 'roundoff' : None},
qty=75, expiry={'expType':'weekly','expNo':0},
target={'isTarget':False,'targetOn':'val','targetValue':1},
SL={'isSL' : True, 'SLon': '%', 'SLvalue' : 50.00},
trailSL = {'isTrailSL':False, 'trailSLon':'val', 'trailSL_X': 1, 'trailSL_Y': 1},
reEntryTg = {'isReEntry' : False, 'reEntryOn' : 'asap', 'reEntryVal' : 0, 'reEntryMaxNo':20},
reEntrySL = {'isReEntry' : False, 'reEntryOn' : '0', 'reEntryVal' : None, 'reEntryMaxNo':20},
waitTrade = {'isWT' : False, 'wtOn' : 'val-up', 'wtVal' : 1, 'triggers': []},
segment = 'OPT', entryValidity = 'Day',
remark = 'Entering Leg',
squareoff = 'this', legName = 'lg_ac380_1',
webhook = {'is' : False, 'entryMessage' : {}, 'exitMessage' : {}})
self.actions_all['act_ac380']['legs'].append(leg)


def act_ac396(self):
self.actions_all['act_ac396']['trigger'] = True
## Square off
self.squareoff_all(remark='Square off all')


def onNewDay(self):
self.data_init()
self.indicator_init()
self.entries = {'cnd_ct1': {'max': 0, 'cur': 0}}

def onCandleClose(self):

## Entry Condition
if (self.position == []) and \
(self._triggers == []) and \
(((self.candleTime > datetime.time(9,20,0)) and \
(self.dt_FUT['close'].iloc[-1] < self.idc_FUT_st_10_3.iloc[-1]))):
self.act_ac380()

## Exit Condition
if (self.position != []) and \
(((self.candleTime >= datetime.time(15,00,0)))):
self.act_ac396()


def onEnd(self):
pass