The Pine Script v6 code
An indicator that counts up bars with var, tracks the day's high so far with a var reset each day, and counts real-time ticks with varip.
Checked by Edgoro Pine Check with no errors and no warnings. Pine Check is a static checker, not TradingView's compiler, and this script was not run in TradingView.
//@version=6
indicator("var and varip", overlay = false)
// var: initialized once, on the first bar, then keeps its value from bar to bar
var int upBars = 0
if close > open
upBars += 1
// var with a reset: the day's high so far
var float dayHigh = na
if timeframe.change("D") or na(dayHigh)
dayHigh := high
else
dayHigh := math.max(dayHigh, high)
// varip: also keeps what each real-time tick changed (no rollback between ticks)
varip int ticks = 0
if barstate.isrealtime
ticks += 1
plot(upBars, "Up bars so far", color.teal, 2)
plot(dayHigh, "Day high so far", color.orange, display = display.data_window)
plot(ticks, "Real-time ticks seen", color.gray, display = display.data_window)
The MQL5 program
A custom indicator with the same three values: the up-bar count drawn, the day's high and the tick count in the Data Window. It only draws.
Save it as MQL5/Indicators/var-persistent-variables.mq5 and compile it in MetaEditor (F7): 0 errors, 0 warnings.
//+------------------------------------------------------------------+
//| var and varip in MQL5 |
//| var -> a value carried from the previous bar's buffer element |
//| varip -> a global variable changed on every tick |
//| Custom indicator: save in MQL5/Indicators and compile (F7). |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_label1 "Up bars so far"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrTeal
#property indicator_width1 2
#property indicator_label2 "Day high so far"
#property indicator_type2 DRAW_NONE
#property indicator_label3 "Real-time ticks seen"
#property indicator_type3 DRAW_NONE
double UpBarsBuf[], DayHighBuf[], TicksBuf[];
// varip int ticks = 0: a global survives from one tick to the next, and nothing rolls it back
int ticks = 0;
int OnInit()
{
SetIndexBuffer(0, UpBarsBuf, INDICATOR_DATA);
SetIndexBuffer(1, DayHighBuf, INDICATOR_DATA);
SetIndexBuffer(2, TicksBuf, INDICATOR_DATA);
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
ticks = 0; // reset here too: an Expert Advisor keeps its globals when only the symbol or timeframe changes
return INIT_SUCCEEDED;
}
// True if the two times fall on different days (server time)
bool NewDay(const datetime prev, const datetime cur)
{
MqlDateTime a, b;
TimeToStruct(prev, a);
TimeToStruct(cur, b);
return a.day != b.day || a.mon != b.mon || a.year != b.year;
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
int start = prev_calculated > 0 ? prev_calculated - 1 : 0;
for(int i = start; i < rates_total; i++)
{
// var int upBars = 0 / if close > open: upBars += 1
// Built from the PREVIOUS bar's value, so recalculating the forming bar on every tick
// never counts it twice: that is Pine's rollback of var on real-time ticks
double before = i > 0 ? UpBarsBuf[i - 1] : 0.0;
UpBarsBuf[i] = before + (close[i] > open[i] ? 1.0 : 0.0);
// var float dayHigh = na, reset on a new day
if(i == 0 || NewDay(time[i - 1], time[i]))
DayHighBuf[i] = high[i];
else
DayHighBuf[i] = MathMax(DayHighBuf[i - 1], high[i]);
TicksBuf[i] = EMPTY_VALUE;
}
// varip: counts every real-time call (prev_calculated > 0 once the history has been calculated)
if(prev_calculated > 0)
ticks++;
TicksBuf[rates_total - 1] = ticks;
return rates_total;
}
//+------------------------------------------------------------------+
Line by line
var int upBars = 0
if close > open
upBars += 1double before = i > 0 ? UpBarsBuf[i - 1] : 0.0;
UpBarsBuf[i] = before + (close[i] > open[i] ? 1.0 : 0.0);The previous bar's element holds the value as of that bar's close. The forming bar is recomputed from it on every tick, so it never counts twice: that is Pine's rollback.
var float dayHigh = na
if timeframe.change("D") or na(dayHigh)
dayHigh := high
else
dayHigh := math.max(dayHigh, high)if(i == 0 || NewDay(time[i - 1], time[i]))
DayHighBuf[i] = high[i];
else
DayHighBuf[i] = MathMax(DayHighBuf[i - 1], high[i]);A reset is the same pattern with a condition. NewDay() compares the calendar dates of two bars in server time; Pine's timeframe.change("D") follows the exchange's sessions.
varip int ticks = 0int ticks = 0; // globalA global keeps every change, and nothing undoes it between ticks.
if barstate.isrealtime
ticks += 1if(prev_calculated > 0)
ticks++;After the first full calculation, each call comes from a new tick (or a history update): the closest MQL5 has to a real-time flag.
// Pine re-runs the script from the first bar on reloadticks = 0; // in OnInit()Reset globals in OnInit(). An Expert Advisor keeps its globals when only its symbol or timeframe changes; an explicit reset avoids carrying a count over.
plot(dayHigh, "Day high so far", color.orange,
display = display.data_window)#property indicator_label2 "Day high so far"
#property indicator_type2 DRAW_NONEShown in the Data Window only, to compare numbers.
Pitfalls
- A global counter in
OnCalculate()counts ticks, not bars.upBars++on every call whereclose > openadds one per tick on the forming bar: after a busy minute it is 40 too high. That isvarip;varneeds the buffer. staticis per program, not per bar. Astaticlocal inOnTick()keeps its value for the program's whole life, like a global. It doesn't reset each bar like a plain Pine variable, and it doesn't roll back likevar.- Reloads. Changing inputs, recompiling or restarting the terminal re-runs an indicator from scratch: buffers are rebuilt from history, so
var-style values come back, but tick counters restart from zero, asvaripvalues do when a Pine script reloads. - State that must survive a restart (trades taken today, a partial close already done) can't live in a variable in either language. Rebuild it from the deal history (
HistorySelect()) or keep it in a terminal global variable (GlobalVariableSet()). - Reading the previous element on bar 0.
UpBarsBuf[i - 1]withi = 0stops the indicator with array out of range: guard the first bar as the code does.
Checking that both give the same values
- Compare "Up bars so far" over a stretch of bars rather than as an absolute number: each platform counts from its own first loaded bar. The increase over the last 50 bars should be the same.
- "Day high so far" should match inside each day; around the day boundary it differs if the broker's server day and the exchange session don't start together.
- Watch the forming bar: on both, the up-bar count changes by at most one while the bar is open.
- The tick counters won't match, since each platform gets its own ticks, but both should rise with every price update.
var in an Expert Advisor
An Expert Advisor has no buffers and doesn't recalculate history, so a Pine var that changes once per bar becomes a global (or static) variable updated in the new-bar block of OnTick(): if(IsNewBar()) { if(iClose(_Symbol, _Period, 1) > iOpen(_Symbol, _Period, 1)) upBars++; }. Updated only there, it changes once per bar, as var does. Updated outside the new-bar block, it changes on every tick, as varip does.
These programs show one technique each; they are not trading systems, and none of this is a recommendation to trade. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.