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