OpenScriptv0.5.0Documentation
GitHub

chart.*

The chart namespace, facts about the instrument and the chart it is drawn on, symbol, exchange, instrument type, tick size, lot size, point value, interval, timezone and the wall clock.

On this page
  1. Where the facts come from
  2. The instrument
  3. Contract arithmetic
  4. What the host supplies
  5. The chart's interval and clock
  6. Planned
  7. Related

The chart namespace answers questions about what the script is running on: which instrument, on which exchange, at what interval, in which timezone, and with what contract arithmetic (tick size, lot size, point value). A script that reads these facts instead of typing numbers in can work unchanged on an NSE stock, an NFO index future and an MCX contract, wherever the host states them.

Not every host states every fact. The /trading chart in this release passes the script only the symbol, the interval, the tick size and the timezone, so chart.exchange, chart.lotSize, chart.instrumentType, chart.hasVolume and the rest read as none there. The table under Where the facts come from shows what each part of /trading states, and each entry below says what to do where its fact is missing.

Every entry except chart.now() is a single value, fixed for the whole run. None of them has a history, so chart.tickSize[1] is OS2004: the answer could not have been different one bar ago.

Where the facts come from

Every fact here comes from the host, the application that runs the script, and any of them may be missing. A fact the host does not state reads as none, never as a guess, so a script can tell "one lot is 75 units" from "nobody said".

In this release the /trading page states these facts:

FactOn the /trading chartIn the /trading Backtest panel
chart.symbolYesYes
chart.exchangenoneYes
chart.interval, and chart.intervalMinutes and chart.isIntraday worked out from itYesnone
chart.timezoneYes, "Asia/Kolkata" unless the chart is set otherwisenone
chart.tickSizeYesYes
chart.lotSize, chart.pointValue, chart.currencynoneYes
chart.instrumentType, chart.hasVolume, chart.hasOpenInterestnonenone
chart.now()Yesnone

So test a fact with isNone(), or give it a fallback with orElse(), before a calculation depends on it. The example below shows each fact, or "not stated" where the host gave none.

version 1
study("Instrument facts", overlay = true)

fn shown(s: string) => isNone(s) ? "not stated" : s
fn shownNumber(x: number) => isNone(x) ? "not stated" : text(x)
fn yesNo(b: bool) => isNone(b) ? "not stated" : b ? "yes" : "no"

panel = table("Instrument", 7, 2, position = "topRight")

if bar.isLast
    cell(panel, 0, 0, "Symbol")
    cell(panel, 0, 1, shown(chart.symbol))
    cell(panel, 1, 0, "Exchange")
    cell(panel, 1, 1, shown(chart.exchange))
    cell(panel, 2, 0, "Type")
    cell(panel, 2, 1, shown(chart.instrumentType))
    cell(panel, 3, 0, "Interval")
    cell(panel, 3, 1, shown(chart.interval))
    cell(panel, 4, 0, "Tick size")
    cell(panel, 4, 1, shownNumber(chart.tickSize))
    cell(panel, 5, 0, "Lot size")
    cell(panel, 5, 1, shownNumber(chart.lotSize))
    cell(panel, 6, 0, "Volume supplied")
    cell(panel, 6, 1, yesNo(chart.hasVolume))
A table dashboard fixed to a corner of the chart
A table fixed to a corner of the chart, updated on every bar.

The instrument

chart.symbol

The instrument's symbol as the host names it, such as "RELIANCE" or "SBIN" for NSE equities. It is none when the host names none. Use it in labels and tables, or to adapt a script to one instrument.

chart.symbol: string
Type
string
First value
n/a
version 1
study("Symbol label", overlay = true)

if bar.isLast
    draw.label(time, high, chart.symbol + " " + text(close, 2), textColor = white)

See also. chart.exchange, req.symbol()

chart.exchange

The exchange the instrument trades on, as the host codes it: for example "NSE" or "BSE" for cash equities, "NFO" for NSE futures and options, "MCX" for commodities, or none where the host does not say, as on the /trading chart. It is also the default exchange of req.symbol(), so a read of another instrument looks on the same exchange unless you name one.

chart.exchange: string
Type
string
First value
n/a
version 1
study("Exchange tag", overlay = true)

isDerivative = chart.exchange == "NFO" or chart.exchange == "MCX"
background(isDerivative ? fade(purple, 95) : none)

Remarks. The /trading chart does not state the exchange in this release, so there the value is none and both comparisons above are false: == never returns none, which keeps the example safe to run anywhere.

See also. chart.symbol, chart.instrumentType

chart.instrumentType

What kind of instrument the chart shows. It is one of seven strings: "equity", "future", "option", "index", "currency", "commodity" or "other", or none when the host does not say. Branch on it when a study should behave differently on, say, an index and its future.

chart.instrumentType: string
Type
string
First value
n/a
version 1
study("Index or not", overlay = true)

isIndex = chart.instrumentType == "index"
// Weight the average by volume except on an index, which has none to weight by.
weighted = vwma(close, 20)
plain = sma(close, 20)
plot(isIndex ? plain : weighted, "Mean of 20 bars", isIndex ? orange : aqua)

Remarks. Pair this check with chart.hasVolume and chart.hasOpenInterest rather than assuming what each type supplies. The /trading page does not state the instrument type in this release.

See also. chart.hasVolume, chart.optionType

chart.currency

The currency label the host uses for money on this instrument, such as "INR". It is a label for reports and tables; it changes no number.

chart.currency: string
Type
string
First value
n/a
version 1
study("Value of one lot", overlay = true)

panel = table("Contract", 1, 2, position = "bottomRight")
lotValue = close * chart.lotSize
if bar.isLast
    cell(panel, 0, 0, "One lot, in " + orElse(chart.currency, "the instrument's money"))
    cell(panel, 0, 1, isNone(lotValue) ? "lot size not stated" : text(lotValue, 0))

See also. chart.pointValue, chart.lotSize

Contract arithmetic

chart.tickSize

The instrument's smallest price step, such as 0.05 for many NSE contracts. It is none when the host has not said, rather than a guessed small number, so a script sizing a stop in ticks can tell "one tick is 0.05" from "nobody said".

chart.tickSize: number
Type
number
First value
n/a
version 1
study("Stop twenty ticks below the low", overlay = true)

ticks = input(20, "Stop distance in ticks", min = 1)
stopLevel = low - orElse(chart.tickSize, 0.05) * ticks
plot(roundToTick(stopLevel), "Stop", red, style = "step")

Remarks. Anything derived from an absent tick size is absent too, which is why roundToTick() returns none in that state instead of an unrounded price that looks rounded. The example supplies a fallback with orElse() for the distance, and the plot is still absent if the host gives no tick size at all. Both the /trading chart and its Backtest panel state the tick size.

See also. roundToTick(), roundToStep()

chart.lotSize

How many units make up one lot. On NFO futures and options and on MCX, orders are placed in whole lots, and lot sizes are set by the exchange and revised from time to time, so read this value rather than typing a number into a script. It is none when the host has not said, which on /trading means on the chart: only the Backtest panel states it.

chart.lotSize: number
Type
number
First value
n/a
version 1
study("Exposure of one lot", precision = 0)

lotValue = close * chart.lotSize
plot(lotValue, "Value of one lot", aqua)

Remarks. The /trading Backtest panel states the lot size, from the platform's instrument record. The /trading chart does not in this release, so there the example draws nothing, which is the honest answer: without a lot size there is no lot value. A strategy that sizes in lots can declare qtyType = "lots", and then every order quantity is a count of lots of this size. See Declarations.

See also. order.roundToLot(), chart.pointValue

chart.pointValue

The money one point of price is worth for one unit of the instrument. For a cash equity it is normally 1: a one-rupee move is one rupee per share. It is none when the host does not know it. Multiply by the lot size to get the value of a point for a whole lot.

chart.pointValue: number
Type
number
First value
n/a
version 1
study("Rupees per point, per lot", precision = 0)

perLot = chart.pointValue * orElse(chart.lotSize, 1)
atrMoney = atr(14) * perLot
plot(atrMoney, "Average true range in money, per lot", orange)

Remarks. The /trading Backtest panel states a point value of 1. The /trading chart does not state one in this release, so there the example draws nothing.

See also. chart.lotSize, chart.currency, atr()

What the host supplies

chart.hasVolume

True when the host states that it supplies volume for this instrument, false when it states that it does not, and none when it says neither. Where it is false, volume is absent on every bar. Test this once instead of testing the value on every bar.

chart.hasVolume: bool
Type
bool
First value
n/a
version 1
study("Volume if available", format = "volume")

// Hide the column only when the host says there is no volume.
showVolume = chart.hasVolume != false
plot(showVolume ? volume : none, "Volume", fade(aqua, 40), style = "column")

Remarks. The /trading page does not state this fact in this release, so it is none there. chart.hasVolume ? volume : none would then hide the volume that is actually present; chart.hasVolume != false does not, because none != false is true.

See also. volume, chart.hasOpenInterest

chart.hasOpenInterest

True when the host states that it supplies open interest for this instrument, as it can for futures and options, false when it states that it does not, and none when it says neither. Where it is false, oi is absent.

chart.hasOpenInterest: bool
Type
bool
First value
n/a
version 1
study("Open interest if available", format = "volume")

showOi = chart.hasOpenInterest != false
plot(showOi ? oi : none, "Open interest", purple)

See also. oi, chart.hasVolume

The chart's interval and clock

chart.interval

The chart's interval as the host names it: a count and a unit such as "1m", "5m" or "1h", a bare number of minutes such as "60", or a letter such as "D", "W" or "M", which is how the /trading chart names its daily, weekly and monthly intervals. The unit letter is case sensitive: "1M" is a month and "1m" is a minute.

chart.interval: string
Type
string
First value
n/a
version 1
study("Interval stamp", overlay = true)

if bar.isLast
    draw.label(time, high, chart.symbol + ", " + chart.interval, textColor = silver)

See also. chart.intervalMinutes, req.timeframe()

chart.intervalMinutes

The chart's interval in minutes: 5 on "5m", 60 on "1h" or "60", 1440 on "1D". It is none for an interval with no fixed length in minutes, such as a month, and none for an interval named with a bare letter, such as "D". Use it to turn a duration into a count of bars.

chart.intervalMinutes: number
Type
number
First value
n/a
version 1
study("One hour of bars", overlay = true)

barsPerHour = chart.isIntraday ? max(1, round(60 / chart.intervalMinutes)) : 1
plot(sma(close, barsPerHour), "Mean of the last hour", aqua)

Remarks. A count derived this way assumes no bars are missing. Inside a session that holds; across a session break or a holiday it does not, so measure elapsed time with time where that matters.

See also. chart.isIntraday, chart.interval

chart.isIntraday

True when the chart's interval is shorter than one day, and false for a day or longer. Like chart.intervalMinutes, it is none when the interval is not stated or is named with a bare letter such as "D", and a condition that is none takes the false branch. Session tools such as an opening range only make sense on an intraday chart, so this is the natural guard for them.

chart.isIntraday: bool
Type
bool
First value
n/a
version 1
study("Daily VWAP on intraday charts", overlay = true)

newDay = bar.isFirst or not date.isSameDay(time, time[1])
dayVwap = vwapAnchor(hlc3, newDay)
plot(chart.isIntraday ? dayVwap : none, "VWAP from the day's first bar", orange)

See also. chart.intervalMinutes, vwapAnchor()

chart.timezone

The timezone the chart's time axis is labelled in, as an IANA name (the standard Area/City form) such as "Asia/Kolkata" for Indian exchanges. Every date function and session.isIn() reads time in this zone unless you pass another, so calendar fields agree with the axis you are looking at.

chart.timezone: string
Type
string
First value
n/a
version 1
study("Zone check", overlay = true)

panel = table("Clock", 2, 2, position = "bottomRight")
if bar.isLast
    cell(panel, 0, 0, "Chart zone")
    cell(panel, 0, 1, orElse(chart.timezone, "not stated"))
    cell(panel, 1, 0, "Newest bar opened")
    cell(panel, 1, 1, date.format(time, "dd MMM HH:mm"))

Remarks. The /trading Backtest panel states no timezone in this release. There a date or session call that relies on the default returns none; pass the zone explicitly, as in date.hour(time, "Asia/Kolkata"), in a strategy you backtest.

See also. date.hour(), session.isIn()

chart.now()

The chart's wall clock, as UTC milliseconds. It is the only clock a script can read while a bar runs; everything else is a function of the bars. Use it to ask how old the newest bar is, not to compute anything on history.

chart.now() -> number
Returns
number
First value
n/a
version 1
study("Bar age", precision = 0)

ageMinutes = (chart.now() - time) / 60000
plot(bar.isLast ? ageMinutes : none, "Minutes since the newest bar opened", silver)

Remarks. It is a call rather than a value because it is the one thing in the language that is not fixed for the run. The host decides what it returns, so a test can fix it and a script that uses it can still be reproduced. A backtest has no wall clock: in the /trading Backtest panel it is none.

See also. time, bar.isRealtime

Planned

These facts are named in the language and not available in this release; using one is OS2020. Each will read from the instrument the host supplies.

chart.expiry

Planned

The expiry instant of a futures or options contract, in UTC milliseconds, so a script can count the days or bars left before expiry.

Named in the language and not available in version 0.5.0. Calling it is error OS2020.

chart.expiry: number
Type
number
First value
n/a

chart.strike

Planned

The strike price of an options contract, for studies that compare an option's premium with how far the underlying is from its strike.

Named in the language and not available in version 0.5.0. Calling it is error OS2020.

chart.strike: number
Type
number
First value
n/a

chart.optionType

Planned

Whether an options contract is a call or a put: "call", "put", or "" for an instrument that is not an option or where the host has not said.

Named in the language and not available in version 0.5.0. Calling it is error OS2020.

chart.optionType: string
Type
string
First value
n/a

chart.isReplay

Planned

True when the chart's bars are being replayed one at a time rather than loaded whole, so a script can tell a replay from a history load.

Named in the language and not available in version 0.5.0. Calling it is error OS2020.

chart.isReplay: bool
Type
bool
First value
n/a

Price and volume, session.*, date.*, Other instruments, Timeframes, Position and sizing.