-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVolumeColor.mq4
More file actions
165 lines (142 loc) · 6.18 KB
/
VolumeColor.mq4
File metadata and controls
165 lines (142 loc) · 6.18 KB
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//+------------------------------------------------------------------+
//| Copyright © 2026, EarnForex.com |
//| https://www.earnforex.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2026 www.EarnForex.com"
#property link "https://www.earnforex.com/indicators/Volume-Color/"
#property version "1.00"
#property strict
#property description "Displays tick volume colored by bar direction, anchored to the bottom of the main chart."
#property indicator_chart_window
#property indicator_buffers 4
// In MQL4 main-chart histograms, buffers are drawn between consecutive
// pairs (0-1, 2-3) and the color of the HIGHER buffer is used. We always
// put the bar's top in the even-indexed buffer so that color shows.
#property indicator_label1 "Volume up top"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrGreen
#property indicator_width1 2
#property indicator_label2 "Volume up bottom"
#property indicator_type2 DRAW_HISTOGRAM
#property indicator_color2 clrGreen
#property indicator_width2 2
#property indicator_label3 "Volume down top"
#property indicator_type3 DRAW_HISTOGRAM
#property indicator_color3 clrRed
#property indicator_width3 2
#property indicator_label4 "Volume down bottom"
#property indicator_type4 DRAW_HISTOGRAM
#property indicator_color4 clrRed
#property indicator_width4 2
input int InpHeightPct = 10; // Bar height (% of visible price range)
double GreenTop[];
double GreenBottom[];
double RedTop[];
double RedBottom[];
int OnInit()
{
SetIndexBuffer(0, GreenTop);
SetIndexBuffer(1, GreenBottom);
SetIndexBuffer(2, RedTop);
SetIndexBuffer(3, RedBottom);
// EMPTY_VALUE in either buffer of a pair suppresses that bar.
SetIndexEmptyValue(0, EMPTY_VALUE);
SetIndexEmptyValue(1, EMPTY_VALUE);
SetIndexEmptyValue(2, EMPTY_VALUE);
SetIndexEmptyValue(3, EMPTY_VALUE);
IndicatorSetString(INDICATOR_SHORTNAME, "Volume Color");
return INIT_SUCCEEDED;
}
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[])
{
if (Bars(_Symbol, _Period) < rates_total) return -1; // Data readiness check.
RefreshVisibleBars();
return rates_total;
}
void OnChartEvent(const int id,
const long& lparam,
const double& dparam,
const string& sparam)
{
if (id == CHARTEVENT_CHART_CHANGE)
{
RefreshVisibleBars();
ChartRedraw(); // Required.
}
}
//+------------------------------------------------------------------+
//| Compute scaled bar geometry for the currently visible bars. |
//+------------------------------------------------------------------+
void RefreshVisibleBars()
{
int total = Bars(_Symbol, _Period);
if (total <= 0) return;
long first_bar = ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
long vis_count = ChartGetInteger(0, CHART_VISIBLE_BARS);
if (first_bar < 0 || vis_count <= 0) return;
// Use the chart's ACTUAL visible price range so the bars re-anchor
// when the user changes the vertical scale.
double price_min = 0.0, price_max = 0.0;
if (!ChartGetDouble(0, CHART_PRICE_MIN, 0, price_min)) return;
if (!ChartGetDouble(0, CHART_PRICE_MAX, 0, price_max)) return;
if (price_max <= price_min) return;
// MQL4 indicator buffers and the OnCalculate parameter arrays are
// SERIES-indexed (0 = newest). CHART_FIRST_VISIBLE_BAR is also
// series-indexed and points at the LEFTMOST (oldest) visible bar.
int oldest_series = (int)first_bar;
int newest_series = (int)first_bar - (int)vis_count + 1;
if (newest_series < 0) newest_series = 0;
if (oldest_series > total - 1) oldest_series = total - 1;
if (newest_series > oldest_series) return;
int count = oldest_series - newest_series + 1;
// Copy* fills its result arrays NON-series by default, so arr[0] = oldest
// of the requested range and arr[count-1] = newest. start_pos = newest
// series index.
double open_arr[], close_arr[];
long vol_arr[];
if (CopyOpen (_Symbol, _Period, newest_series, count, open_arr) <= 0) return;
if (CopyClose (_Symbol, _Period, newest_series, count, close_arr) <= 0) return;
if (CopyTickVolume(_Symbol, _Period, newest_series, count, vol_arr) <= 0) return;
// Normalize against the largest volume currently in view.
long vol_max = vol_arr[0];
for (int k = 1; k < count; k++)
if (vol_arr[k] > vol_max) vol_max = vol_arr[k];
if (vol_max <= 0) return;
double pct = InpHeightPct;
if (pct < 1) pct = 1;
if (pct > 100) pct = 100;
double height = (price_max - price_min) * (pct / 100.0);
// Walk the copied (non-series) arrays and write back to the
// (series-indexed) buffers. arr[k] -> series index (oldest_series - k).
for (int k = 0; k < count; k++)
{
int series_idx = oldest_series - k;
double scaled_top = price_min + height * ((double)vol_arr[k] / (double)vol_max);
bool bullish = (close_arr[k] > open_arr[k]);
if (series_idx < 0 || series_idx >= total) return; // Sanity check.
if (bullish)
{
GreenTop[series_idx] = scaled_top;
GreenBottom[series_idx] = price_min;
RedTop[series_idx] = EMPTY_VALUE;
RedBottom[series_idx] = EMPTY_VALUE;
}
else
{
GreenTop[series_idx] = EMPTY_VALUE;
GreenBottom[series_idx] = EMPTY_VALUE;
RedTop[series_idx] = scaled_top;
RedBottom[series_idx] = price_min;
}
}
}
//+------------------------------------------------------------------+