Futures to Options Module
The "Futures to Options Execution Module" is a highly modular and extensible Pine Script template designed for advanced TradingView users, particularly those interested in automating options trading based on futures signals. Here’s a concise overview of the template and how traders can build on it:
%20(1).BQUJP-8f.png)
Sample Template Overview
This module is structured into 8 distinct blocks, each with a specific function:
- Tradeboard API Controls: Connects to Tradeboard for live execution, allowing integration with real trading APIs.
- Backtesting & Risk Controls: Defines trading timeframes, and configurable risk parameters including Stoploss/Target (fixed, percentage, or ATR-based).
- Trading Strategy Block: Users can plug in any logic here. The default implementation includes a HalfTrend + ATR channel-based strategy.
- Signal Mapping & Intraday Filtering: Converts strategy conditions into buy/sell signals and handles intraday session logic.
- Option Symbol Generation & API Messaging: Frames CE/PE option symbols dynamically based on spot price, expiry, and strike intervals.
- Trade Execution Logic: Executes trades based on signal, handles square-off and mode control (Enable, Long-only, Short-only).
- SL/TP Visualization: Plots stoploss and target markers based on the selected risk model.
- Trading Dashboard: Displays real-time trade info (entry/exit signals) on the chart using tables.
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tradeboard
//Trading Block Description
//Block1 : Tradeboard API controls
//Block2 : Backtesting Controls & Target and Stoploss Controls
//Block3: Trading Strategy and Controls (Write your strategy Block)
//Block4: Intraday Function and Buy and Sell Signal Mapping (Signal Mapping is Required)
//Block5: Framing Option Symbols and API Message Structure
//Block6: Trade Execution Controls
//Block7: Plotting Stoploss and Target
//Block8: Plotting Trading Dashboard
//@version=6
strategy("Futures to Options Execution Module", overlay=true, fill_orders_on_standard_ohlc = true)
//Block1 : Tradeboard API controls
// Input controls
apikey = input.string("xxxxxxxxxxxx", title="Tradeboard API Key", group="Tradeboard")
strategyType = input.string("Tradingview", title="Strategy", group="Tradeboard")
Underlying = input.symbol(title = "Underlying Symbol", defval = "NSE:NIFTY", group = "Tradeboard")
Expiry = input.string(title="Expiry Date",defval="30JAN25",group = "Tradeboard")
iInterval = input.int(title='Strike Interval',defval = 50, group = "Tradeboard")
lotsize = input.int(title = "LotSize", defval = 75,group="Tradeboard")
quantity = input.int(title = "Quantity(Lots)", defval = 1,group="Tradeboard")
offsetCE = input.int(title = "OffsetCE", defval = 0,minval=-40,maxval=40, step=1, group="Tradeboard")
offsetPE = input.int(title = "OffsetPE", defval = 0,minval=-40,maxval=40, step=1, group="Tradeboard")
exchange = input.string("NFO", title="Exchange", options=["NFO", "BFO", "MCX"], group="Tradeboard")
product = input.string("MIS", title="Product Type", options=["MIS", "NRML"], group="Tradeboard")
algomode = input.string(title="Algo Mode",defval = "ENABLE",options=['ENABLE',"LONGONLY","SHORTONLY"])
// Hardcoded Price Type
pricetype = "MARKET"
//Get the Strike Interval
SpotC1 = request.security(Underlying,timeframe.period,close)
SpotC = SpotC1[1]
//calculate the ATM/OTM/ITM Options
strike = SpotC % iInterval > iInterval/2 ? SpotC - (SpotC%iInterval) + iInterval : SpotC - (SpotC%iInterval)
strikeCE = strike + (offsetCE * iInterval)
strikePE = strike - (offsetPE * iInterval)
////////////////////////Block 1 Module Ends ///////////////////////////////////////////////////////////////////////
//Block 2 : Backtesting Controls & Target and Stoploss Controls
FromMonth = input.int(defval=9, title='From Month', minval=1, maxval=12, group='Backtesting')
FromDay = input.int(defval=1, title='From Day', minval=1, maxval=31, group='Backtesting')
FromYear = input.int(defval=2018, title='From Year', minval=999, group='Backtesting')
ToMonth = input.int(defval=1, title='To Month', minval=1, maxval=12, group='Backtesting')
ToDay = input.int(defval=1, title='To Day', minval=1, maxval=31, group='Backtesting')
ToYear = input.int(defval=9999, title='To Year', minval=999, group='Backtesting')
start = timestamp(FromYear, FromMonth, FromDay, 00, 00)
finish = timestamp(ToYear, ToMonth, ToDay, 23, 59)
window() =>
time >= start and time <= finish ? true : false
highlighting = input.bool(title='Highlighter On/Off ?', defval=true, group='Intraday Controls')
barcoloring = input.bool(title='Bar Coloring On/Off ?', defval=true, group='Intraday Controls')
intraday = input.bool(title='Intraday On/Off ?', defval=false, group='Intraday Controls')
marketSession = input.session(title='Market session', defval='0915-1500', confirm=false, group='Intraday Controls')
risk = input.bool(title='Stoploss/Target On/Off', defval=false, group='Stoploss/Target Controls')
itype = input.string(title='Type', defval='FIXED', options=['FIXED', 'PERCENTAGE', 'VOLATILITY'], group='Stoploss/Target Controls')
stop = input.float(defval=10.0, title='Stoploss', group='Stoploss/Target Controls')
target = input.float(defval=20.0, title='Target', group='Stoploss/Target Controls')
TickSz = input.float(defval=0.05, title='TickSize', group='Stoploss/Target Controls')
ATRMultiplier = input.float(title='ATR Multiplier', step=0.1, defval=1.5, group='Stoploss/Target Controls')
ATRLength = input.int(title='ATR Period', defval=20, group='Stoploss/Target Controls')
iATR = ta.atr(ATRLength)
////////////////////////Block 2 Module Ends ///////////////////////////////////////////////////////////////////////
//Block 3 : Trading Strategy and Controls
amplitude = input(title = 'Amplitude', defval = 2)
channelDeviation = input(title = 'Channel Deviation', defval = 2)
//showArrows = input(title = 'Show Arrows', defval = true)
showChannels = input(title = 'Show Channels', defval = true)
var int trend = 0
var int nextTrend = 0
var float maxLowPrice = nz(low[1], low)
var float minHighPrice = nz(high[1], high)
var float up = 0.0
var float down = 0.0
float atrHigh = 0.0
float atrLow = 0.0
float arrowUp = na
float arrowDown = na
atr2 = ta.atr(100) / 2
dev = channelDeviation * atr2
highPrice = high[math.abs(ta.highestbars(amplitude))]
lowPrice = low[math.abs(ta.lowestbars(amplitude))]
highma = ta.sma(high, amplitude)
lowma = ta.sma(low, amplitude)
if nextTrend == 1
maxLowPrice := math.max(lowPrice, maxLowPrice)
if highma < maxLowPrice and close < nz(low[1], low)
trend := 1
nextTrend := 0
minHighPrice := highPrice
minHighPrice
else
minHighPrice := math.min(highPrice, minHighPrice)
if lowma > minHighPrice and close > nz(high[1], high)
trend := 0
nextTrend := 1
maxLowPrice := lowPrice
maxLowPrice
if trend == 0
if not na(trend[1]) and trend[1] != 0
up := na(down[1]) ? down : down[1]
arrowUp := up - atr2
arrowUp
else
up := na(up[1]) ? maxLowPrice : math.max(maxLowPrice, up[1])
up
atrHigh := up + dev
atrLow := up - dev
atrLow
else
if not na(trend[1]) and trend[1] != 1
down := na(up[1]) ? up : up[1]
arrowDown := down + atr2
arrowDown
else
down := na(down[1]) ? minHighPrice : math.min(minHighPrice, down[1])
down
atrHigh := down + dev
atrLow := down - dev
atrLow
ht = trend == 0 ? up : down
var color buyColor = color.blue
var color sellColor = color.red
htColor = trend == 0 ? buyColor : sellColor
htPlot = plot(ht, title = 'HalfTrend', linewidth = 2, color = htColor)
atrHighPlot = plot(showChannels ? atrHigh : na, title = 'ATR High', style = plot.style_circles, color = color.new(sellColor, 0))
atrLowPlot = plot(showChannels ? atrLow : na, title = 'ATR Low', style = plot.style_circles, color = color.new(buyColor, 0))
fill(htPlot, atrHighPlot, title = 'ATR High Ribbon', color = color.new(sellColor, 90))
fill(htPlot, atrLowPlot, title = 'ATR Low Ribbon', color = color.new(buyColor, 90))
longCondition = not na(arrowUp) and trend == 0 and trend[1] == 1
shortCondition = not na(arrowDown) and trend == 1 and trend[1] == 0
////////////////////////Block 3 Module Ends ///////////////////////////////////////////////////////////////////////
//Block 4 : Intraday Function and Buy and Sell Signal Mapping (Signal Mapping is Required)
//Remove the comments to do the long/short signal mapping
buySignal = longCondition
sellSignal = shortCondition
barInSession(sess) =>
time(timeframe.period, sess) != 0
bool intradaySession = barInSession(marketSession)
buy = buySignal
sell = sellSignal
buy1 = buy[1]
sell1 = sell[1]
////////////////////////Block 4 Module Ends ///////////////////////////////////////////////////////////////////////
//Block 5 : Framing Option Symbols and API Message Structure
TSYMce= ta.valuewhen(buy,strikeCE,0)
TSYMpe= ta.valuewhen(sell,strikePE,0)
symbolinfo = str.replace_all(Underlying,"NSE:","")
symbolinfo := str.replace_all(symbolinfo,"1!","")
symbolinfo := str.replace_all(symbolinfo,"2!","")
//Framing a Option Symbols
tradSYMCE=symbolinfo + Expiry + str.tostring(TSYMce)+ 'CE'
tradSYMPE=symbolinfo + Expiry + str.tostring(TSYMpe)+ 'PE'
//initial declarations
var islong = false
var issell = false
var symCE = ''
var symPE = ''
var exitsymCE = ''
var exitsymPE = ''
var tpslCE = ''
var tpslPE = ''
buy := not islong and buy
sell := not issell and sell
if buy
islong := true //Buy signal is continuing
issell := false //reset the sell continue
symCE := symbolinfo + Expiry + str.tostring(TSYMce)+ 'CE'
tpslCE := symCE
exitsymPE := symPE //storing the PE symbol value for exit
symPE := ''
if sell
islong :=false //Buy Signal is no more continuing
issell :=true //Sell Signal is continuing
symPE := symbolinfo + Expiry + str.tostring(TSYMpe)+ 'PE'
tpslPE := symPE
exitsymCE := symCE //storing the CE symbol value for exit
symCE := ''
LongCall = '{ "apikey":"' + apikey + '", "strategy":"' + strategyType + '", "symbol":"' + symCE + '", "action":"BUY", "exchange":"' + exchange + '", "pricetype":"MARKET", "product":"' + product + '", "quantity":"' + str.tostring(quantity * lotsize) + '" }'
ExitCall = '{ "apikey":"' + apikey + '", "strategy":"' + strategyType + '", "symbol":"' + exitsymCE + '", "action":"SELL", "exchange":"' + exchange + '", "pricetype":"MARKET", "product":"' + product + '", "quantity":"' + str.tostring(quantity * lotsize) + '" }'
SqCall = '{ "apikey":"' + apikey + '", "strategy":"' + strategyType + '", "symbol":"' + tpslCE + '", "action":"SELL", "exchange":"' + exchange + '", "pricetype":"MARKET", "product":"' + product + '", "quantity":"' + str.tostring(quantity * lotsize) + '" }'
LongPut = '{ "apikey":"' + apikey + '", "strategy":"' + strategyType + '", "symbol":"' + symPE + '", "action":"BUY", "exchange":"' + exchange + '", "pricetype":"MARKET", "product":"' + product + '", "quantity":"' + str.tostring(quantity * lotsize) + '" }'
ExitPut = '{ "apikey":"' + apikey + '", "strategy":"' + strategyType + '", "symbol":"' + exitsymPE + '", "action":"SELL", "exchange":"' + exchange + '", "pricetype":"MARKET", "product":"' + product + '", "quantity":"' + str.tostring(quantity * lotsize) + '" }'
SqPut = '{ "apikey":"' + apikey + '", "strategy":"' + strategyType + '", "symbol":"' + tpslPE + '", "action":"SELL", "exchange":"' + exchange + '", "pricetype":"MARKET", "product":"' + product + '", "quantity":"' + str.tostring(quantity * lotsize) + '" }'
//assign trading signals
if(not intraday)
buy := buy
sell := sell
if(intraday)
buy := buy and intradaySession
sell := sell and intradaySession
////////////////////////Block 5 Module Ends ///////////////////////////////////////////////////////////////////////
//Block 6 : Trade Execution Controls
if(algomode=="ENABLE")
//Buy Fresh Call Option
if buy and strategy.position_size == 0 and window()
strategy.entry('LONGCALL', strategy.long, alert_message=LongCall)
//Buy Fresh Put Option
if sell and strategy.position_size == 0 and window()
strategy.entry('LONGPUT', strategy.short, alert_message=LongPut)
//Exit Old Put Option and Enter Fresh Call Option (Buy)
if buy and strategy.position_size < 0 and window()
strategy.close('LONGPUT', alert_message=ExitPut)
strategy.entry('LONGCALL', strategy.long, alert_message=LongCall)
//Exit Old Call Option and Enter Fresh Put Option (Buy)
if sell and strategy.position_size > 0 and window()
strategy.close('LONGCALL' ,alert_message=ExitCall)
strategy.entry('LONGPUT', strategy.short, alert_message=LongPut)
if(algomode=="LONGONLY")
//Buy Fresh Call Option
if buy and strategy.position_size == 0 and window()
strategy.entry('LONGCALL', strategy.long, alert_message=LongCall)
if sell and strategy.position_size > 0 and window()
strategy.close('LONGCALL' ,alert_message=ExitCall)
if(algomode=="SHORTONLY")
if buy and strategy.position_size < 0 and window()
strategy.close('LONGPUT', alert_message=ExitPut)
//Buy Fresh Put Option
if sell and strategy.position_size == 0 and window()
strategy.entry('LONGPUT', strategy.short, alert_message=LongPut)
if(intraday)
longsquareOff = not intradaySession and strategy.position_size > 0
if(longsquareOff)
strategy.close(id='LONGCALL', comment='Square-off',alert_message=SqCall)
shortsquareOff = not intradaySession and strategy.position_size < 0
if(shortsquareOff)
strategy.close(id='LONGPUT', comment='Square-off',alert_message=SqPut)
////////////////////////Block 6 Module Ends ///////////////////////////////////////////////////////////////////////
//Block 7 : Plot Stoploss and Target
buycount = ta.barssince(buySignal)
sellcount = ta.barssince(sellSignal)
color1 = buycount[1] < sellcount[1] ? color.green : buycount[1] > sellcount[1] ? color.red : na
barcolor(barcoloring ? color1 : na)
long_stop_level = ta.valuewhen(buy1, open - stop, 0)
long_profit_level = ta.valuewhen(buy1, open + target, 0)
short_stop_level = ta.valuewhen(sell1, open + stop, 0)
short_profit_level = ta.valuewhen(sell1, open - target, 0)
if(itype=="PERCENTAGE")
long_stop_level := ta.valuewhen(buy1, open, 0) * (100-stop)/100
long_profit_level := ta.valuewhen(buy1, open, 0) * (100+target)/100
long_stop_level := TickSz * math.round(long_stop_level/TickSz)
long_profit_level := TickSz * math.round(long_profit_level/TickSz)
short_stop_level := ta.valuewhen(sell1, open, 0) * (100+stop)/100
short_profit_level := ta.valuewhen(sell1, open, 0) * (100-target)/100
short_stop_level := TickSz * math.round(short_stop_level/TickSz)
short_profit_level := TickSz * math.round(short_profit_level/TickSz)
if(itype=="VOLATILITY")
long_stop_level := ta.valuewhen(buy1, open - iATR*ATRMultiplier, 0)
long_profit_level := ta.valuewhen(buy1, open + iATR*ATRMultiplier, 0)
short_stop_level := ta.valuewhen(sell1, open + iATR*ATRMultiplier, 0)
short_profit_level := ta.valuewhen(sell1, open - iATR*ATRMultiplier, 0)
if(risk)
if(strategy.position_size>0)
strategy.exit('CALL TP/SL', 'LONGCALL', stop=long_stop_level, limit=long_profit_level,alert_message=SqCall)
if(strategy.position_size<0)
strategy.exit('PUT TP/SL', 'LONGPUT', stop=short_stop_level, limit=short_profit_level,alert_message=SqPut)
plot(strategy.position_size <= 0 or not risk ? na : long_stop_level, color=color.new(color.red, 0), style=plot.style_circles, linewidth=2)
plot(strategy.position_size <= 0 or not risk ? na : long_profit_level, color=color.new(color.green, 0), style=plot.style_circles, linewidth=2)
plot(strategy.position_size >= 0 or not risk ? na : short_stop_level, color=color.new(color.red, 0), style=plot.style_circles, linewidth=2)
plot(strategy.position_size >= 0 or not risk ? na : short_profit_level, color=color.new(color.green, 0), style=plot.style_circles, linewidth=2)
////////////////////////Block 7 Module Ends ///////////////////////////////////////////////////////////////////////
//Block8: Plotting Trading Dashboard
entrytext = ''
exittext = ''
if(islong)
entrytext := 'Long Call : '+symCE
exittext := 'Exit Put : '+exitsymPE
if(issell)
entrytext := 'Exit Call : '+exitsymCE
exittext := 'Long Put : '+symPE
//Trading Dashboard
var tLog = table.new(position = position.bottom_left, rows = 2, columns = 2, bgcolor = color.black, border_width=1)
table.cell(tLog, row = 0, column = 1, text = entrytext, text_color = color.green)
table.cell_set_text(tLog, row = 0, column = 1, text = entrytext)
table.cell(tLog, row = 1, column = 1, text = exittext, text_color = color.red)
table.cell_set_text(tLog, row = 1, column = 1, text = exittext )
////////////////////////Block 8 Module Ends ///////////////////////////////////////////////////////////////////////Ensure Position Size is handled in the Properties Section
%20(1)%20(1)%20(1).CVv75BR1.png)
Ensure Alert Message is Set
%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1).Bo37ZaxK.png)
Creating the Alert
Every block in this template builds its payload into an alert_message on the strategy.entry, strategy.close and strategy.exit calls. Nothing reaches Tradeboard until you wire that through a TradingView alert.
- Add the strategy to the chart, open the alert dialog and choose the strategy (not an indicator condition) as the alert source.
- In the Message box put exactly:
{{strategy.order.alert_message}}That placeholder is what pushes the JSON that Block 5 assembled. If you leave TradingView's default message in place, Tradeboard receives text it cannot parse and no order is placed.
- Under Notifications, tick Webhook URL and enter your Tradeboard placeorder endpoint:
https://your-tradeboard-domain/api/v1/placeorderThe template always sends a plain order for a named option symbol, so /api/v1/placeorder is the correct endpoint. Do not point it at /api/v1/placesmartorder, which expects a position_size field the template never emits.
- Set the alert to expire as late as TradingView allows, and remember it fires on the strategy's own events, so the alert's trigger condition is driven by the script rather than by a chart condition you pick.
Things to check before going live
- Your Tradeboard instance must be reachable from the internet. TradingView cannot post to
127.0.0.1. SetHOST_SERVERin.envto your tunnel or custom domain. Expiryis the Tradeboard expiry in DDMMMYY form, for example30JAN25. Update it on every rollover, because the script concatenates it into the option symbol verbatim.Quantity(Lots)is multiplied byLotSizebefore it is sent, so Tradeboard receives the total number of units. That matches what the API expects. KeepLotSizein step with the exchange's current lot size.Strike Intervalmust match the underlying, for example 50 for NIFTY and 100 for BANKNIFTY, otherwise the strike arithmetic in Block 1 produces a symbol that does not exist.- The spot price used for strike selection is taken from the previous bar (
SpotC = SpotC1[1]), which keeps the symbol stable within the bar but means a fast gap can leave the strike one interval behind. - Test with the Tradeboard API Analyzer switched on before sending real orders.
How Traders Can Use It
- Plug-and-Play Strategy Development: Insert your own logic into Block 3 to test strategies like Moving Average Crossovers, RSI signals, or ML-driven logic.
- Backtest Ready: Instantly simulate performance with adjustable historical timeframes.
- Options Execution: Automatically converts directional futures signals into options trades with customizable offsets.
- Risk Management: Activate dynamic SL/TP configurations for robust risk control.
- API Alerts: Sends alert messages structured for API consumption, ready for auto-execution platforms.
