PineForge v0.13.1-379-g9b50973
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
session_time.hpp
Go to the documentation of this file.
1#pragma once
2#include <cstdint>
3#include <ctime>
4#include <string>
5
6namespace pineforge {
7
8// TradingView accepts fixed-offset names like "GMT+1" / "UTC-5" with the
9// human-readable sign convention. POSIX TZ strings use the opposite sign, so
10// engine code that calls setenv("TZ", ...) must normalize through this helper.
11std::string normalize_timezone_for_posix(const std::string& tz);
12
13// ---------------------------------------------------------------------------
14// Timeframe bucket open / close for a bar timestamp (the `time(timeframe,
15// session?, timezone?)` and `time_close(...)` family a source frontend lowers
16// to).
17// Returns Unix milliseconds, or na<int64_t>() when the bar is outside the
18// requested session (TradingView semantics for filtered sessions).
19//
20// `syminfo_tz` is the symbol's exchange timezone (Pine `syminfo.timezone`;
21// codegen passes `syminfo_.timezone`). It is the DEFAULT zone the `session`
22// window is interpreted in when `tz` is empty — Pine reads a tz-less session
23// in the exchange timezone, not UTC. An explicit `tz` always wins. It does
24// NOT influence the timeframe open/close computation (explicit `tz`, else
25// UTC, exactly as before); an empty `syminfo_tz` reproduces the historical
26// UTC session default byte-for-byte.
27//
28// Feature macro: generated code tests this to decide whether the trailing
29// `syminfo_tz` argument exists, so one transpiler output compiles against
30// both this header and older ones (`PF_PINE_TIME_SYMINFO_TZ_ARG(x)` in the
31// emitted prelude expands to `, (x)` only when it is defined).
32// ---------------------------------------------------------------------------
33
34#define PF_PINE_TIME_HAS_SYMINFO_TZ 1
35
36int64_t timeframe_time(int64_t bar_ms,
37 const std::string& tf,
38 const std::string& session,
39 const std::string& tz,
40 const std::string& chart_tf,
41 const std::string& syminfo_tz = std::string());
42
43int64_t timeframe_time_close(int64_t bar_ms,
44 const std::string& tf,
45 const std::string& session,
46 const std::string& tz,
47 const std::string& chart_tf,
48 const std::string& syminfo_tz = std::string());
49
50// Symbol-clock forms. `sym_tz` / `sym_session` are Pine's syminfo.timezone
51// and syminfo.session (the engine's runtime overrides). For a D/W/M `tf`
52// called WITHOUT a valid session argument the returned open / close is the
53// SYMBOL's daily/weekly/monthly bar (17:00 ET on OANDA forex, 09:30 ET RTH
54// on NASDAQ equities, 00:00 UTC on a 24x7 UTC symbol — see
55// session_period_open_ms in timeframe.hpp). A valid `session` argument
56// defines the day in its own `tz` exactly as the forms above do
57// (TradingView rolls `time("D", "0000-2359", "America/New_York")` at New
58// York midnight on a UTC symbol — measured), and its window is read in the
59// explicit `tz`, else `sym_tz` (the syminfo default above), else UTC.
60// An intraday `tf` (with or without a session argument, which only
61// filters) is the symbol's day-stamp-anchored HTF grid bucket
62// (session_intraday_bucket_open_ms in timeframe.hpp — the grid
63// request.security aggregates on): time("60") on NYSE:F is 09:30 / 10:30 /
64// .. / 15:30 ET, time("240") on NSE:NIFTY 09:15 / 13:15 IST and on
65// OANDA:XAUUSD the 17:00-ET-anchored 4h grid (pin-time-hours tapes,
66// 2025-04-01..07-01); the five-argument forms stay on the epoch grid.
67// With sym_tz="UTC" and an empty / "24x7" sym_session these are
68// bit-identical to the five-argument forms above.
69//
70// Feature macro: generated code tests this to decide whether the trailing
71// (sym_tz, sym_session) pair exists (`PF_PINE_TIME_SESSION_DAY_ARGS(tz, s)`
72// in the emitted prelude expands to `, tz, s` only when it is defined, so
73// the same generated.cpp collapses to the 5-arg call on older engines).
74#define PF_PINE_TIME_HAS_SESSION_DAY 1
75
76int64_t timeframe_time(int64_t bar_ms,
77 const std::string& tf,
78 const std::string& session,
79 const std::string& tz,
80 const std::string& chart_tf,
81 const std::string& sym_tz,
82 const std::string& sym_session);
83
84int64_t timeframe_time_close(int64_t bar_ms,
85 const std::string& tf,
86 const std::string& session,
87 const std::string& tz,
88 const std::string& chart_tf,
89 const std::string& sym_tz,
90 const std::string& sym_session);
91
92// ---------------------------------------------------------------------------
93// Low-level session helpers (exposed for engine_run.cpp, unit tests,
94// session_trading_day_open_ms, and session predicates).
95// ---------------------------------------------------------------------------
96
97// Convert "HHMM" string to minutes-since-midnight. Returns -1 on parse error.
98int hhmm_to_minutes(const std::string& hhmm);
99
100// Pine time/date extraction from a Unix-ms bar timestamp in timezone `tz`.
101// Value-identical to Pine hour()/minute()/dayofweek()/... (dayofweek 1=Sun..7=Sat,
102// month 1..12, year full). Codegen routes hour()/minute()/... through these
103// instead of an inline setenv+tzset lambda, which stops the per-call macOS
104// tzset()->notifyd IPC storm (KI-35): UTC uses tzset-free gmtime_r, other zones
105// use the cached ScopedTimezone. DST-correct (localtime_r via the tz database).
106int local_hour(int64_t bar_ms, const std::string& tz);
107int local_minute(int64_t bar_ms, const std::string& tz);
108int local_second(int64_t bar_ms, const std::string& tz);
109int local_dayofmonth(int64_t bar_ms, const std::string& tz);
110int local_dayofweek(int64_t bar_ms, const std::string& tz);
111int local_month(int64_t bar_ms, const std::string& tz);
112int local_year(int64_t bar_ms, const std::string& tz);
113int local_weekofyear(int64_t bar_ms, const std::string& tz);
114
115// True when local_tm falls within any of the comma-separated HHMM-HHMM
116// windows in windows_body. "24x7" or empty always returns true.
117bool local_time_in_session_windows(const std::string& windows_body,
118 const struct tm& local_tm);
119
120// True when bar_ms (Unix ms) falls inside the session string for the given tz.
121bool passes_session_filter(const std::string& session,
122 const std::string& tz,
123 int64_t bar_ms);
124
125// Floor bar_ms to local-calendar midnight in the given IANA timezone.
126// DST edge-case: if mktime() returns -1 (non-existent midnight — e.g.,
127// America/Havana, Pacific/Lord_Howe spring-forward), the implementation
128// retries with midnight + 1 h and midnight + 2 h to find a valid epoch
129// (i.e. uses the first available second after the gap). A warning is
130// logged once per occurrence via fprintf(stderr). The fallback is
131// conservative: the returned timestamp is never more than 2 hours later
132// than the true calendar-day start; downstream consumers
133// (session_trading_day_open_ms) inherit the same semantics.
134int64_t calendar_day_open_local_ms(int64_t bar_ms, const std::string& tz);
135
136// -------------------------------------------------------------------------
137// time_tradingday built-in
138// -------------------------------------------------------------------------
139// Returns the Unix-ms timestamp of the session-open of the trading day
140// that contains bar_ms. See implementation for full algorithm + DST notes.
141int64_t session_trading_day_open_ms(int64_t bar_ms,
142 const std::string& session,
143 const std::string& tz);
144
145// ---------------------------------------------------------------------------
146// Session predicates backing session.is* Pine v6 variables.
147//
148// LIMITATION: The engine has a single syminfo.session string and cannot
149// distinguish RTH from ETH. Therefore:
150// session.isfirstbar_regular == session.isfirstbar
151// session.islastbar_regular == session.islastbar
152// in the current architecture. Future work: add SymInfo.regular_session
153// field for strict RTH separation (deferred — separate sprint).
154//
155// Pre/post-market predicates assume standard US ETH windows:
156// premarket : 04:00 – RTH_open (RTH_open parsed from session string)
157// postmarket: RTH_close – 20:00
158// For exchanges with non-standard ETH (e.g. LSE auctions), accuracy may
159// degrade. This is documented behaviour, not a bug.
160// ---------------------------------------------------------------------------
161
162bool session_in_market(const std::string& session,
163 const std::string& tz,
164 int64_t bar_ms);
165
166bool session_in_premarket(const std::string& session,
167 const std::string& tz,
168 int64_t bar_ms);
169
170bool session_in_postmarket(const std::string& session,
171 const std::string& tz,
172 int64_t bar_ms);
173
174// Chart-timeframe forms: the rule the session.is* variables of a CHART bar
175// follow. TradingView evaluates them on the chart bar, and a daily-or-higher
176// bar covers whole session days rather than a time of day, so on "1D" and
177// above it documents session.ismarket as always true and session.ispremarket
178// / session.ispostmarket as always false (Pine docs, Sessions). OANDA:XAUUSD
179// @1D made the gap physical: an 1800-1700 America/New_York session and a tape
180// stamped at the 17:00 ET break put every daily open OUTSIDE the time-of-day
181// windows above, session.ismarket never held, and a script that ANDs every
182// signal with it took 0 trades against TradingView's 57. With an intraday or
183// empty chart_tf these forms defer to the three-argument time-of-day forms
184// above, byte for byte.
185bool session_in_market(const std::string& session,
186 const std::string& tz,
187 int64_t bar_ms,
188 const std::string& chart_tf);
189
190bool session_in_premarket(const std::string& session,
191 const std::string& tz,
192 int64_t bar_ms,
193 const std::string& chart_tf);
194
195bool session_in_postmarket(const std::string& session,
196 const std::string& tz,
197 int64_t bar_ms,
198 const std::string& chart_tf);
199
200// ---------------------------------------------------------------------------
201// Deprecated `pine_*` spellings. They are exact inline forwards, kept so
202// generated code and the source adapter compile unchanged; new code calls the
203// neutral names above.
204// ---------------------------------------------------------------------------
205
206inline int64_t pine_time(int64_t bar_ms, const std::string& tf,
207 const std::string& session, const std::string& tz,
208 const std::string& chart_tf,
209 const std::string& syminfo_tz = std::string()) {
210 return timeframe_time(bar_ms, tf, session, tz, chart_tf, syminfo_tz);
211}
212
213inline int64_t pine_time(int64_t bar_ms, const std::string& tf,
214 const std::string& session, const std::string& tz,
215 const std::string& chart_tf, const std::string& sym_tz,
216 const std::string& sym_session) {
217 return timeframe_time(bar_ms, tf, session, tz, chart_tf, sym_tz, sym_session);
218}
219
220inline int64_t pine_time_close(int64_t bar_ms, const std::string& tf,
221 const std::string& session, const std::string& tz,
222 const std::string& chart_tf,
223 const std::string& syminfo_tz = std::string()) {
224 return timeframe_time_close(bar_ms, tf, session, tz, chart_tf, syminfo_tz);
225}
226
227inline int64_t pine_time_close(int64_t bar_ms, const std::string& tf,
228 const std::string& session, const std::string& tz,
229 const std::string& chart_tf, const std::string& sym_tz,
230 const std::string& sym_session) {
231 return timeframe_time_close(bar_ms, tf, session, tz, chart_tf, sym_tz, sym_session);
232}
233
234inline int pine_hour(int64_t bar_ms, const std::string& tz) { return local_hour(bar_ms, tz); }
235inline int pine_minute(int64_t bar_ms, const std::string& tz) { return local_minute(bar_ms, tz); }
236inline int pine_second(int64_t bar_ms, const std::string& tz) { return local_second(bar_ms, tz); }
237inline int pine_dayofmonth(int64_t bar_ms, const std::string& tz) { return local_dayofmonth(bar_ms, tz); }
238inline int pine_dayofweek(int64_t bar_ms, const std::string& tz) { return local_dayofweek(bar_ms, tz); }
239inline int pine_month(int64_t bar_ms, const std::string& tz) { return local_month(bar_ms, tz); }
240inline int pine_year(int64_t bar_ms, const std::string& tz) { return local_year(bar_ms, tz); }
241inline int pine_weekofyear(int64_t bar_ms, const std::string& tz) { return local_weekofyear(bar_ms, tz); }
242
243inline int64_t pine_time_tradingday(int64_t bar_ms, const std::string& session,
244 const std::string& tz) {
245 return session_trading_day_open_ms(bar_ms, session, tz);
246}
247
248inline bool pine_session_ismarket(const std::string& session, const std::string& tz,
249 int64_t bar_ms) {
250 return session_in_market(session, tz, bar_ms);
251}
252
253inline bool pine_session_ispremarket(const std::string& session, const std::string& tz,
254 int64_t bar_ms) {
255 return session_in_premarket(session, tz, bar_ms);
256}
257
258inline bool pine_session_ispostmarket(const std::string& session, const std::string& tz,
259 int64_t bar_ms) {
260 return session_in_postmarket(session, tz, bar_ms);
261}
262
263inline bool pine_session_ismarket(const std::string& session, const std::string& tz,
264 int64_t bar_ms, const std::string& chart_tf) {
265 return session_in_market(session, tz, bar_ms, chart_tf);
266}
267
268inline bool pine_session_ispremarket(const std::string& session, const std::string& tz,
269 int64_t bar_ms, const std::string& chart_tf) {
270 return session_in_premarket(session, tz, bar_ms, chart_tf);
271}
272
273inline bool pine_session_ispostmarket(const std::string& session, const std::string& tz,
274 int64_t bar_ms, const std::string& chart_tf) {
275 return session_in_postmarket(session, tz, bar_ms, chart_tf);
276}
277
278} // namespace pineforge
bool pine_session_ispostmarket(const std::string &session, const std::string &tz, int64_t bar_ms)
int local_year(int64_t bar_ms, const std::string &tz)
int64_t pine_time(int64_t bar_ms, const std::string &tf, const std::string &session, const std::string &tz, const std::string &chart_tf, const std::string &syminfo_tz=std::string())
int local_dayofweek(int64_t bar_ms, const std::string &tz)
int pine_year(int64_t bar_ms, const std::string &tz)
int local_dayofmonth(int64_t bar_ms, const std::string &tz)
std::string normalize_timezone_for_posix(const std::string &tz)
int local_minute(int64_t bar_ms, const std::string &tz)
int64_t session_trading_day_open_ms(int64_t bar_ms, const std::string &session, const std::string &tz)
int pine_weekofyear(int64_t bar_ms, const std::string &tz)
bool pine_session_ispremarket(const std::string &session, const std::string &tz, int64_t bar_ms)
int local_weekofyear(int64_t bar_ms, const std::string &tz)
int64_t timeframe_time(int64_t bar_ms, const std::string &tf, const std::string &session, const std::string &tz, const std::string &chart_tf, const std::string &syminfo_tz=std::string())
int local_month(int64_t bar_ms, const std::string &tz)
bool passes_session_filter(const std::string &session, const std::string &tz, int64_t bar_ms)
bool local_time_in_session_windows(const std::string &windows_body, const struct tm &local_tm)
bool session_in_market(const std::string &session, const std::string &tz, int64_t bar_ms)
int pine_dayofmonth(int64_t bar_ms, const std::string &tz)
int pine_month(int64_t bar_ms, const std::string &tz)
int64_t calendar_day_open_local_ms(int64_t bar_ms, const std::string &tz)
bool pine_session_ismarket(const std::string &session, const std::string &tz, int64_t bar_ms)
int pine_minute(int64_t bar_ms, const std::string &tz)
bool session_in_premarket(const std::string &session, const std::string &tz, int64_t bar_ms)
int64_t timeframe_time_close(int64_t bar_ms, const std::string &tf, const std::string &session, const std::string &tz, const std::string &chart_tf, const std::string &syminfo_tz=std::string())
int pine_dayofweek(int64_t bar_ms, const std::string &tz)
int pine_second(int64_t bar_ms, const std::string &tz)
int hhmm_to_minutes(const std::string &hhmm)
int local_second(int64_t bar_ms, const std::string &tz)
int pine_hour(int64_t bar_ms, const std::string &tz)
int local_hour(int64_t bar_ms, const std::string &tz)
bool session_in_postmarket(const std::string &session, const std::string &tz, int64_t bar_ms)
int64_t pine_time_tradingday(int64_t bar_ms, const std::string &session, const std::string &tz)
int64_t pine_time_close(int64_t bar_ms, const std::string &tf, const std::string &session, const std::string &tz, const std::string &chart_tf, const std::string &syminfo_tz=std::string())