Tradingview has a few handy time-based variables and functions which allow you to manually keep track of time, events, sessions and detect time changes. These come in handy if you want to limit back tests to trade only during in-session hours or have indicators reset at certain intervals.
Tradingview Wiki
First things first, the Tradingview wiki actually has some good examples and tutorials showing how to use some of the variables and functions. Instead of reinventing the wheel, this post will try to add some value by showing the output of variables that do not have examples in the wiki and by providing a real-world example strategy.
You can take a look at Tradingview’s wiki tutorial here:
https://www.tradingview.com/wiki/Sessions_and_Time_Functions
Primer
For those that are just following along in the series or have not read over the Tradingview wiki, a quick primer will help with digesting the rest of the post.
Regarding time, Pine Script has both special variables and a set of functions that use the same name. This means to avoid unintended syntax errors or behavior, you need to pay special attention to whether the code has brackets()
at the end of the name.
An example straight from the Wiki shows:
- time: A variable – Which returns a timestamp of the current date/time in a UNIX format.
- time(): A function – Which also returns a timestamp but for a specified resolution or session. This is useful for mixing time frames.
The Wikipedia description for UNIX time is an integer that represents the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970. However, it is important to note that Pine Script works with timestamps in millisecond format rather than seconds since 1970. If you are interested in why the 1st of January 1970 is used, take a look at this post on stack exchange which does a good job of explaining it:
https://stackoverflow.com/questions/1090869/why-is-1-1-1970-the-epoch-time
Next up if plot(na(t) ? 0 : 1)
does not make any sense to you, it might be an idea to first take a look at another tutorial in the series: https://backtest-rookies.com/2017/09/04/tradingview-ternary-conditional-operators/ then come back here.
Exploring variables
As mentioned above, Pine script has a number of built-in variables for tracking the time. In order to know how to use them, it is worth to plot each of them so you can see their expected output. Not all variables plot like time
where the output is always increasing. Some variables such as dayofweek
and hour
cycle in a range. Let’s take a look.
year
month
weekofyear
dayofmonth
Notice how this one does not step up as nicely as the month
of weekofyear
variables. This is because we are often missing trading days due to the weekend and national holidays.
dayofweek
The range of dayofweek
already assumes weekends are not included. On the chart below we can clearly see the national holidays.
hour
The hour
variable holds the current bar hour in the exchange timezone.
minute
Like hour
, the minute
variable holds the current bar minute in the exchange time zone.
second
Like hour
and minute
, the second
variable holds the current bar second in the exchange timezone.
time vs timenow
Time provides the current time of the bar on the chart. Therefore it steps up in the same resolution as the timeframe you are working on. Using timenow
on the other hand, gives you the current time right now. This results in historical bars all showing the time now. When live data kicks in, you will see the two lines converge to an extent.
If you want to know about the differences between historical bars of data and real-time bars, see my post here: Tradingview: Understanding lookahead, historical and realtime data
Exploring time functions:
The time()
function itself is well covered in Tradingview’s wiki. However, there are a couple of points that can be expanded upon for the beginner. First, let’s take a look at the code:
1 2 3 |
study("Session bars") t = time(period, "0930-1600") plot(na(t) ? 0 : 1) |
The explanation as noted in the wiki is:
The function
time
returns the time of the bar in milliseconds UNIX time or NaN value if the bar is located outside the given trade session (09:30-16:00 in our example).
Some extra notes for the beginner are:
- The
period
variable in the Tradingview wiki example contains the resolution to be displayed. - The session string is given in the exchange timezone and not the timezone on your chart! See below for more details.
NaN
means “not a number”, it is this which allows us the easily differentiate between open and closed sessions bars. When the session is open, the variable which contains the series data will hold a UNIX timestamp for that bar. Conversely, if it is closed, the series will contain aNaN
value for the bar.- The
na()
function checks a number/value in a series. In this case, we are passing it thet
series variable. IfNaN
is found,na()
will returnTrue
. If a valid number is found, it will returnFalse
. - Finally, the
plot
line is using a ternary condition to check the returned value fromna()
. If it returns aTrue
value (because we are NOT in session) then 0 will be plotted on the indicators. If it returnsFalse
, then 1 is plotted on the indicator.
Session vs chart time zones
As noted above, the session value that you enter is for the exchange timezone. This may seem confusing at first but using the session time zone instead of the chart time zone simplifies deployment of the code. It ensures that the code can be copied and pasted for any user around the world. Different users will have different chart time preferences and as such, they would potentially be out of sync with the session open / close if they are not located in the same timezone as the code developer. Using the exchange timezone ensures that the code will open and close and the same time regardless of a users preference.
year(), month(), second() etc….
The rest of the time-based functions all do pretty much the same thing. They all accept a UTC timestamp in milliseconds and they return either a year, month, second etc for the timestamp given.
Practical Example
All the theory is good and well but it is useless unless you put it into practice. A real-world example is provided below to get you moving on your way.
In-session Trading
This example will create a simple stochastic strategy to buy and sell only when we are in the Asian trading session and ADR (average directional index) is showing that we are not heavily trending. The strategy will also close open positions at the end of the session.
The code shown below was setup to trade GBPUSD. The chart was set to Tokyo time zone so I could verify trades were being opened correctly at the right local times. You will see the exchange times used in the codes are not the same as the local times.
The Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
//@version=3 strategy("Asian Stoch", overlay=true) //########### Inputs #################\\ adxlen = input(14, title="ADX Smoothing") dilen = input(14, title="DI Length") maxADX = input(26, title="Maximum ADX value allowed to trade") stPeriod = input(14, title="The lookback period for the stochastic") stBuy = input(30, title="Buy below this stochastic value") stSell = input(70, title="Sell above this stochastic value") //########### ADR CODE #################\\ dirmov(len) => up = change(high) down = -change(low) truerange = rma(tr, len) plus = fixnan(100 * rma(up > down and up > 0 ? up : 0, len) / truerange) minus = fixnan(100 * rma(down > up and down > 0 ? down : 0, len) / truerange) [plus, minus] adx(dilen, adxlen) => [plus, minus] = dirmov(dilen) sum = plus + minus adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adxlen) sig = adx(dilen, adxlen) not_trending = sig < maxADX //########### Asian Session CODE #################\\ t = time(period, "1900-0315") session_open = na(t) ? false : true //########### Stochastic CODE #################\\ stoch = stoch(close, high, low, stPeriod) long = stoch < stBuy short = stoch > stSell //########### STRATEGY CODE #################\\ if (long and session_open and not_trending) // strategy.close("Short 1") strategy.entry("Long 1", strategy.long) if (short and session_open and not_trending) // strategy.close("Long 1") strategy.entry("Short 1", strategy.short) //Close out position before the end of the session. if not (session_open) strategy.close("Long 1") strategy.close("Short 1") |
The Result
On the charts, it looks something like this.
You can see the end of day closes in purple. Otherwise, it alternates between long and short positions.
What about the profitability? Don’t get too excited! It is nothing to write home about:
The results above used an order size of 5% of equity on a 100,000 USD account and did not take commissions into account. However, that was not the point of the exercise!
Hello Thank you for a very useful article. Can you please also advise if it is possible to find the price at a particular time e.g. if I need to monitor the price at 9:45 AM every day (I can do it on a 15 minute bar in NYSE but that will give me only a few months of data as 15 minute data is limited. But if I can find that on a daily bar, then I will have more than 10 years of 9:45 am data. So a way to achieve this will be very useful.
And in the same context, is it possible to find the price at the open (e.g. 9:30 am on NYSE) while we are on a 15 minute chart? I know security function can provide the larger duration data but in version 3 it will provide the open of the previous day.
Thanks for any inputs. And thank you for your wonderful series of articles
Hello,
Thank you very much for all the tutorials, I’ve just found this website and is amazing !! I’m learning a lot !!
I’ve being playing with pinescript for 48 hours now and as you can imagine I have a lot of questions 😀
Is it possible to use strategy.close after certain amount of time? For example, close the strategy after 5 minutes, 15 minutes, 1 hour, etc.
So far I’m trying to use strategy.close usen “when” trying to add 5 minutes (in milliseconds) to timestamp, but it’s not working :/
Thank you for your help and for such an amazing content !!
Keep the good work !!
Adrian, did you ever get an answer to your question? I am trying to do the same thing.
Hey Allan,
You would need to use the
barssince()
function to count how many bars it has been since you entered the position.You could then close on a multiple of the chart timeframe. For example, if you are on a 15 minute chart, you could close a position after 15, 30, 45, 60 minutes and so on…
I have been searching for a way to st the time and date from which an indicator will begin.
Historically I have been using the following:
historydays = input(21300, title=”Swing high/low look-back time-frame in hours (Default=60)”)
milliseconds_in_5days = 1000 * 60 * 60 * historydays
leftborder = timenow – time < milliseconds_in_5days
This is not practical though, since the start time is always moving forwards in time.
The following doesn't work for me. It is always starting from the first bar available, no matter the date entered:
fromDay = input(defval = 1, title = "From Day", type = input.integer, minval = 1, maxval = 31)
fromMonth = input(defval = 4, title = "From Month", type = input.integer, minval = 1, maxval = 12)
fromYear = input(defval = 2020, title = "From Year", type = input.integer, minval = 1970)
start = timestamp(fromYear, fromMonth, fromDay, 00, 00)
leftborder = timenow – time < start
rightborder = barstate.islast
Can anyone point me in the right direction?
I want close all trade at a dedicate time each trading day
E.G. Close all trade at 14:59:00
——code———–
exit_long = time(’14:59:00′)
strategy.close(id=”Long”, when=exit_long, comment = “Exit”)
—————–
But this not work, can help me or give me some hints ?