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_ac380will enter the trade;act_ac396will exit all.minTriggerruns 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