首页 > 提升投资报酬 > 量化分析

运用水平线与垂直线对象在图表上绘制网格的方式

量化分析 2022-04-24 10:12:21

在本篇章节中,将介绍如何运用水平线与垂直线对象在图表上绘制网格(纵横线)。


首先,在制作新档案中选择「客制化指标」,并将文件名命名为「Grid_test」。由于本次将使用「OnChartEvent」,因此在「客制化指针的事件处理程序」画面中,仅勾选「OnChartEvent」的字段并进入下一步;如此点击「完成」即是雏形。

客制化指标

因需使用到对象,故为了能够一次进行删除,应先登录接头辞;在档案上方的属性「 #property indicator_chart_window」下方,如以下所示加以定义。
 
#define PREFIX “Grid_”
其后,在「Custom indicator initialization function」下方设定「Custom indicator deinit function」,并写入使用OnDeinit函数的以下编码。
 
void OnDeinit(const int reason)

{

ObjectsDeleteAll(0, PREFIX);

}

使用OBJ_VLINE设定垂直线


关于网格的绘图处理,应能够在图表变更时予以执行,并在每次重设之后绘制网格。

首先来设定垂直线;在绘制纵向线条时,将范围指定为左方K线至右方K线。

「CHART_FIRST_VISIBLE_BAR」是图表左边最先出现的K线,「CHART_WIDTH_IN_BARS」则是图表中的K线总数量;至于右边的K线,由于当图表位置变换时,最新的线条可能并不会出现在右边、并成为负数值,因此需将此状况的数值设定为「0」。
 
int barF = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);

int barR = barF – (int)ChartGetInteger(0, CHART_WIDTH_IN_BARS);

if (barR < 0) barR = 0;
接下来将使用for文体,从barF至barR进行计算。如果只希望在需要的位置绘制垂直线,便须再度新增条件;将绘制垂直线时机的「drawV」改为「true」,便会形成在此时绘制垂直线的模式。
 
for (int i = barF; i >= barR; i–) {

bool drawV = false;

if (drawV) {

}

}
此处会从MQL4帮助档中,复制垂直线的范例编码并加以运用。点击MQL4相关参考目录的「Constants, Enumerations and Structures」→「Objects Constants」→「Object Types」,便会出现对象一览;从中选择「OBJ_ VLINE」后,复制预先准备的编码并贴至档案下方。

删除不需要的「//— if the line time is not set, draw it via the last bar」等5行,以及「Print(__FUNCTION__,」「”: failed to create a vertical line! Error code = “,GetLastError());」2行。

另外,颜色从「clr = clrRed」改为「clr = clrGray」,风格则从实线「style = STYLE_SOLID」改为虚线「style = STYLE_DOT」,选择从「selection = true,」改为「selection = false」。

顺道一提,虽然此处为垂直线专用,但亦可将「VLineCreate」改为「LineCreate」以便水平线也能使用;而时间初始设定「datetime time = 0, // line time」的下方,将价格初始设定「double price = 0, // line price」新增double型,然后将「if(!ObjectCreate(chart_ID, name, OBJ_VLINE, sub_window, time, 0)) {」的「0」改为「price」即完成了准备工作。
 
//+——————————————————————+

//| Create the vertical line |

//+——————————————————————+

bool LineCreate(const long chart_ID = 0, // chart’s ID

const string name = “VLine”, // line name

const int sub_window = 0, // subwindow index

datetime time = 0, // line time

double price = 0, // line price

const color clr = clrGray, // line color

const ENUM_LINE_STYLE style = STYLE_DOT, // line style

const int width = 1, // line width

const bool back = false, // in the background

const bool selection = false, // highlight to move

const bool hidden = true, // hidden in the object list

const long z_order = 0) // priority for mouse click

{

//— create a vertical line

if(!ObjectCreate(chart_ID, name, OBJ_VLINE, sub_window, time, price)) {

return(false);

}

//— set line color

ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);

//— set line display style

ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, style);

//— set line width

ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width);

//— display in the foreground (false) or background (true)

ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);

//— enable (true) or disable (false) the mode of moving the line by mouse

//— when creating a graphical object using ObjectCreate function, the object cannot be

//— highlighted and moved by default. Inside this method, selection parameter

//— is true by default making it possible to highlight and move the object

ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);

ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);

//— hide (true) or display (false) graphical object name in the object list

ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);

//— set the priority for receiving the event of a mouse click in the chart

ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);

//— successful execution

return(true);

}

使用switch处理绘制垂直线


接下来,将新增绘制垂直线的编码。

首先在「if (drawV) {」下方「LineCreate」,如以下所示设定参数。图表ID为「0」、名称为「PREFIX+VL+序号」、窗口编号为主要窗口的「0」、时间为核准时间,价格则因是「0」故予以省略。
 
LineCreate(0, PREFIX + “VL” + IntegerToString(i), 0, Time[i]);
如欲在此处将设定改为依照时间周期,便须使用「switch文体」;其与if文体相同,常用于制作执行不同条件的程序。在Switch后方的括号中置入「条件公式」,便会跳到与该公式一致的case位置加以处理。

首先,如果显示图表时间周期的「_Period」数值为1分钟「PERIOD_M1」的处理时,需在「if (drawV) {」上方进行以下编写。在每分钟单位的公式中,如欲以15分钟作为间隔来绘制垂直线,便应将时间除以15分钟,并在余数为「0」时成为「true」。
 
switch (_Period) {

case PERIOD_M1 :

drawV = Time[i] % PeriodSeconds(PERIOD_M15) == 0;

break;

如此便完成了每分钟的设定。
 

根据不同时间周期指定绘制垂直线的间隔


除了1分钟之外,时间周期亦有「5分钟」「15分钟」「30分钟」「1小时」「4小时」「每日」「每周」「每月」等8种,并可设定其各自的条件。

此处将5分钟时间周期设定为每小时间隔、15分钟及30分钟设定为每4小时间隔、1小时则设定为每日间隔。由于15分钟与30分钟同为每4小时进行处理,故可如下所示,删除15分钟的「break;」并一次进行编写。
 
case PERIOD_M5 :

drawV = Time[i] % PeriodSeconds(PERIOD_H1) == 0;

break;

case PERIOD_M15 :

case PERIOD_M30 :

drawV = Time[i] % PeriodSeconds(PERIOD_H4) == 0;

break;

case PERIOD_H1 :

drawV = Time[i] % PeriodSeconds(PERIOD_D1) == 0;

break;
4小时则设定为每周间隔;若使用取得指定日期周间的「TimeDayOfWeek」,便只会在星期一执行处理,并拉出每周的垂直线。
 
case PERIOD_H4 :

drawV = Time[i] % PeriodSeconds(PERIOD_D1) == 0 && TimeDayOfWeek(Time[i]) == MONDAY;

break;
另外,每日则以每月间隔,故可使用「MN1」;但若不满一日,便会无法顺利绘制垂直线,因此此处将公式予以改变,运用「TimeMonth」在月份切换时进行绘制。
 
case PERIOD_D1 :

drawV = TimeMonth(Time[i]) != TimeMonth(Time[i + 1]);

break;
但是,当使用「i+1」、且「barF」存在于图表最左边的数据时,需注意是否超出序列的范围;为了消除此错误,可在「int barF = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);」下方添加以下if文体。
 
if (barF > Bars – 2) barF = Bars – 2;
至于每周的间隔,则以3个月作为区隔;以 TimeMonth(Time[i])除以3,便只会在余数为1、也就是1、4、7、10月时绘制垂直线。
 
case PERIOD_W1 :

drawV = TimeMonth(Time[i]) != TimeMonth(Time[i + 1]) && TimeMonth(Time[i]) % 3 == 1;

break;
最后,每月则是以1年为区隔;使用「TimeYear」,藉以在年份切换时进行绘制。
 
 

在不同时间周期绘制不同间隔的水平线


接下来,就要进行水平线的设定。

水平线与垂直向相同,都会使用switch文体,根据不同时间周期变更间隔。

本次将1分钟、5分钟、15分钟以50pips做出区隔。「_Point」意指最小的价格单位,意即0.1pips;故若乘以500便成为50pips。另外, 30分钟、1小时、4小时单位是以100pips做出间隔;每日单位为200pips、每周及每月则是1000pips。
double pitch = 0;

switch (_Period) {

case PERIOD_M1 :

case PERIOD_M5 :

case PERIOD_M15 :

pitch = 500 * _Point;

break;

case PERIOD_M30 :

case PERIOD_H1 :

case PERIOD_H4 :

pitch = 1000 * _Point;

break;

case PERIOD_D1 :

pitch = 2000 * _Point;

break;

case PERIOD_W1 :

case PERIOD_MN1 :

pitch = 10000 * _Point;

break;

}
针对水平线,目标范围仅从图表上方至下方,因此可透过「ChartGetDouble」取得第1个价格以及其下方1个价格;至于最下方的价格,则使用「MathMod」以间隔除算,将余数从最小值中减去、再予以四舍五入。举例来说,若最下方的价格为「104.2」,则间隔的50pips则为四舍五入的「104」。
 
double max = ChartGetDouble(0, CHART_PRICE_MAX);

double min = ChartGetDouble(0, CHART_PRICE_MIN);

min -= MathMod(min, pitch);
为了将数字化为整数,可使用「NormalizeDouble」,从最小值处拉出n条水平线(「_Digits」会显示该货币对价格的小数点以下位数)。
 
min = NormalizeDouble(min, _Digits);

int n = int((max – min) / pitch) + 1;

for (int i = 0; i < n; i++) {
虽已使用for文体来处理n条列,但「Create the vertical line」的设定为VLINE专用型,故此处将予以修改,以便后续设定样式。在「Create the vertical line」的「const string name = “VLine”, // line name」下方写入以下公式,即可指定两者的样式。
 
const int type = OBJ_VLINE, // type
其后将「if(!ObjectCreate(chart_ID, name, OBJ_VLINE, sub_window, time, price)) {」的「OBJ_VLINE」改为「type」。
 
if(!ObjectCreate(chart_ID, name, type, sub_window, time, price)) {
与此同时,在绘制垂直线的「LineCreate(0, PREFIX + “VL” + IntegerToString(i), 0, Time[i]);」中,在「0」前方添加「OBJ_VLINE」。
 
LineCreate(0, PREFIX + “VL” + IntegerToString(i), OBJ_VLINE, 0, Time[i]);
关于绘制水平线的编码,可延用前文的垂直线内容,并与前述for文体相互搭配。将「VL」改为「HL」、「OBJ_VLINE」改为「OBJ_HLINE」;另外, 因属于HLINE、故时间为「0」、价格为「min+i*pitch」。
 
 

原始码


本次制作的原始码如以下所示。
//+——————————————————————+

//| Grid_test.mq4 |

//| Copyright 2021, MetaQuotes Software Corp. |

//| https://www.mql5.com |

//+——————————————————————+

#property copyright “Copyright 2021, MetaQuotes Software Corp.”

#property link “https://www.mql5.com”

#property version “1.00”

#property strict

#property indicator_chart_window

#define PREFIX “Grid_”

//+——————————————————————+

//| Custom indicator initialization function |

//+——————————————————————+

int OnInit()

{

//— indicator buffers mapping

//—

return(INIT_SUCCEEDED);

}

//+——————————————————————+

//| Custom indicator deinit function |

//+——————————————————————+

void OnDeinit(const int reason)

{

ObjectsDeleteAll(0, PREFIX);

}

//+——————————————————————+

//| Custom indicator iteration function |

//+——————————————————————+

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[])

{

//—

//— return value of prev_calculated for next call

return(rates_total);

}

//+——————————————————————+

//| ChartEvent function |

//+——————————————————————+

void OnChartEvent(const int id,

const long &lparam,

const double &dparam,

const string &sparam)

{

if (id == CHARTEVENT_CHART_CHANGE) {

ObjectsDeleteAll(0, PREFIX);

int barF = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);

if (barF > Bars – 2) barF = Bars – 2;

int barR = barF – (int)ChartGetInteger(0, CHART_WIDTH_IN_BARS);

if (barR < 0) barR = 0;

for (int i = barF; i >= barR; i–) {

bool drawV = false;

switch (_Period) {

case PERIOD_M1 :

drawV = Time[i] % PeriodSeconds(PERIOD_M15) == 0;

break;

case PERIOD_M5 :

drawV = Time[i] % PeriodSeconds(PERIOD_H1) == 0;

break;

case PERIOD_M15 :

case PERIOD_M30 :

drawV = Time[i] % PeriodSeconds(PERIOD_H4) == 0;

break;

case PERIOD_H1 :

drawV = Time[i] % PeriodSeconds(PERIOD_D1) == 0;

break;

case PERIOD_H4 :

drawV = Time[i] % PeriodSeconds(PERIOD_D1) == 0 && TimeDayOfWeek(Time[i]) == MONDAY;

break;

case PERIOD_D1 :

drawV = TimeMonth(Time[i]) != TimeMonth(Time[i + 1]);

break;

case PERIOD_W1 :

drawV = TimeMonth(Time[i]) != TimeMonth(Time[i + 1]) && TimeMonth(Time[i]) % 3 == 1;

break;

case PERIOD_MN1 :

drawV = TimeYear(Time[i]) != TimeYear(Time[i + 1]);

break;

}

if (drawV) {

LineCreate(0, PREFIX + “VL” + IntegerToString(i), OBJ_VLINE, 0, Time[i]);

}

}

double pitch = 0;

switch (_Period) {

case PERIOD_M1 :

case PERIOD_M5 :

case PERIOD_M15 :

pitch = 500 * _Point;

break;

case PERIOD_M30 :

case PERIOD_H1 :

case PERIOD_H4 :

pitch = 1000 * _Point;

break;

case PERIOD_D1 :

pitch = 2000 * _Point;

break;

case PERIOD_W1 :

case PERIOD_MN1 :

pitch = 10000 * _Point;

break;

}

double max = ChartGetDouble(0, CHART_PRICE_MAX);

double min = ChartGetDouble(0, CHART_PRICE_MIN);

min -= MathMod(min, pitch);

min = NormalizeDouble(min, _Digits);

int n = int((max – min) / pitch) + 1;

for (int i = 0; i < n; i++) {

LineCreate(0, PREFIX + “HL” + IntegerToString(i), OBJ_HLINE, 0, 0, min + i * pitch);

}

}

}

//+——————————————————————+

//| Create the vertical line |

//+——————————————————————+

bool LineCreate(const long chart_ID = 0, // chart’s ID

const string name = “VLine”, // line name

const int type = OBJ_VLINE, // type

const int sub_window = 0, // subwindow index

datetime time = 0, // line time

double price = 0, // line price

const color clr = clrGray, // line color

const ENUM_LINE_STYLE style = STYLE_DOT, // line style

const int width = 1, // line width

const bool back = false, // in the background

const bool selection = false, // highlight to move

const bool hidden = true, // hidden in the object list

const long z_order = 0) // priority for mouse click

{

//— create a vertical line

if(!ObjectCreate(chart_ID, name, type, sub_window, time, price)) {

return(false);

}

//— set line color

ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);

//— set line display style

ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, style);

//— set line width

ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width);

//— display in the foreground (false) or background (true)

ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);

//— enable (true) or disable (false) the mode of moving the line by mouse

//— when creating a graphical object using ObjectCreate function, the object cannot be

//— highlighted and moved by default. Inside this method, selection parameter

//— is true by default making it possible to highlight and move the object

ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);

ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);

//— hide (true) or display (false) graphical object name in the object list

ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);

//— set the priority for receiving the event of a mouse click in the chart

ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);

//— successful execution

return(true);

}

//+——————————————————————+
如何使用switch处理绘制垂直线?
哪些贵金属平台好?不错的贵金属交易汇平台推荐:哪些贵金属平台好?2023年平台最新排名
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。
本文相关:无相关信息

留言与评论(共有 0 条评论)
验证码:

免责声明

特别提示:根据《关于严厉查处非法外汇期货和外汇按金交易活动的通知》(证监发字[1978]105号)规定凡未经批准的机构在大陆境内擅自开展外汇期货交易均属非法,提高意识,谨防损失!

本网站所有刊登内容,以及所提供的信息资料,目的是为了更好地服务我们的访问者,本网站不保证所有信息、文本、图形、链接及其它项目的绝对准确性和完整性,网站没有任何盈利目的,故仅供访问者参照使用。本网站已尽力确保所有资料是准确、完整及最新的。就该资料的针对性、精确性以及特定用途的适合性而言,本网站不能作出最对应的方案。所以因依赖该资料所致的任何损失,本网均不负责。 除特别注明之服务条款外,其他一切因使用本站而引致的任何意外、疏忽、合约毁坏、隐秘汇漏、诽谤、版权或知识产权侵犯及其所造成的损失,本站概不负责,亦不承担任何法律责任。如您(单位或个人)认为本网站某部分内容有侵权嫌疑,敬请立即通知我们,我们将在第一时间予以更改或删除。以上声明之解释权归牛犇财经网站所有。法律上有相关解释的,以中国法律之解释为基准。如有争议限在我方所在地司法部门解决。