你好,外汇软件的同事们!
我们今天的课程将集中在数据结构上,以及如何利用它们来提高MQL4软件开发的效率。 代码的有效性取决于许多因素。 在减少计算复杂性(优化算法)的同时,还需要注意智能数据设计, 这将允许快速访问信息和经济地分配计算机内存。
一个重要的方面是代码的可读性。 结构的使用在某种程度上有助于解决这些问题,因为正确组织的数据很容易被程序员理解, 提高开发效率。
什么是结构?
MQL4中的结构是称为结构元素或字段的数据集。 与…不同 地块 仅包含一种类型元素的结构可以由不同类型的元素组成。 因此,结构的第一个也是最重要的用途是能够将变量按某种方式分组。 结构是用户数据类型,因此可以声明这种类型的变量。 这种结构变量可以通过赋值运算符复制到另一种类型,作为参数传递给函数,从函数中返回值, 声明这些变量的数组。 每个结构元素都可以像普通变量一样直接访问和修改。 然而,所有这些活动都有一些限制,我们将在后面讨论。
语法
结构数据类型一般定义为:
struct<结构名称> { <type><element_1>; <type><element_2>; <type><element_3>; … <type><element_n>; };
让我们看看一个具体的例子。 定义描述某个对象的结构数据类型。 让这个对象成为一个酒吧 图表 金融工具(或 蜡烛 ). 使用MQL4,我们可以获得特定蜡烛的以下特性:
开盘价格;
收盘价;
最大值;
最小;
体积;
开放时间。
其他特征,如蜡烛类型(“熊市”或“牛市”),蜡烛体高度,上阴影大小,下阴影大小等,预计将实时计算。 这并不总是方便的,特别是如果您需要经常为整个蜡烛数组处理此类设置。 您可以在新蜡烛形成时进行所有计算,并将结果存储在变量中。 那么,为了存储这些蜡烛特性,我们需要11个不同类型的变量:
double open; // 开盘价 double close; // 收盘价 double high; // 最大值 double low; // 最小值 long volume; // 柚木体积 datetime time; // 开放时间 uchar type; // 蜡烛类型(0-“狗”,1-公牛,2-熊) int height_full; // 总烛光高度 int height_body; // 蜡烛体尺寸 int height_top_shadow; // 上阴影尺寸 int height_bottom_shadow; // 下阴影尺寸
如果我们想操作一个集合,比如说100根蜡烛,我们必须宣布11个数组,每个数组有100个元素。 假设我们需要将所有这些数据传递到一个函数中,这将是一个非常笨重的结构,使代码和程序员的感知更加复杂。 可能导致 错误 但是,所有这些数据可以合并为一个结构:
struct Candle { double open; // 开盘价 double close; // 收盘价 double high; // 最大值 double low; // 最小值 long volume; // 柚木体积 datetime time; // 开放时间 uchar type; // 蜡烛类型(0-“狗”,1-公牛,2-熊) int height_full; // 总烛光高度 int height_body; // 蜡烛体尺寸 int height_top_shadow; // 上阴影尺寸 int height_bottom_shadow; // 下阴影尺寸 };
在这个阶段,记忆不会被分配到结构下,因为我们只是定义了类型。 要访问结构对象本身,只需声明此类型的变量:
访问结构元素
要访问结构元素,必须使用点从属操作()。 例如:
Bar.volume=iVolume(_Symbol,_Period,1)
在这里,我们将bar结构变量volume元素分配给当前图表上索引为1的条形图的滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答滴答。
结构初始化
请注意,默认情况下,结构元素不会在声明时初始化,就像普通变量一样。 因此,必须确保在调用结构时,其元素被初始化。 可以在播发过程中使用初始化列表对结构进行初始化。 例如:
Candle Bar = {0,0,0,0,100,D'2018.03.10 15:08:02',0,0,0,0,12};
在这里,我们宣布了变量bar,并将结构的所有元素从第一个到第十一个值进行了近似。 那么
Candle Bar = {0.1012,0.6321,0.2194,0.1784,100};
我们只用非零值初始化前五个元素,其余元素自动为零。 如果结构中的所有元素都必须为零值拟合,则只需编写
因为我们结构的所有元素都是数值的,所以我们也可以用一个值对它们进行指数化:
严格地说,这种初始化是不正确的,因为元素 time 类型 datetime 100是1970年1月1日以来的100秒。 因此,这种结构初始化必须考虑隐式类型转换的特殊性。
嵌套结构
结构元素不仅可以是简单的类型,也可以是复杂的:字符串、静态和动态数组和结构。 嵌套结构单独定义。 让我们用一个具体的例子来说明如何以这种方式组织数据。 在我们的结构中 Candle 最后四个元素包含整个蜡烛及其各个部分的大小。 在此基础上,可以将这些元素合并为一个单独的结构:
struct Height { int full; // 总烛光高度 int body; // 蜡烛体尺寸 int top_shadow; // 上阴影尺寸 int bottom_shadow; // 下阴影尺寸 };
在结构上 Candle 只需宣布一个类型的变量 Height :
struct Candle { double open; // 开盘价 double close; // 收盘价 double high; // 最大值 double low; // 最小值 long volume; // 柚木体积 datetime time; // 开放时间 uchar type; // 蜡烛类型(0-“狗”,1-公牛,2-熊) Height height; // 蜡烛部分高度 };
初始化列表还必须包含子列表:
Candle Bar= { 0.0, 0.0, 0.0, 0.0, 100, D'2018.03.10 15:08:02', 1, { 0, 0, 0, 0 } };
现在,要查看蜡烛体的大小,必须使用两次点:
Bar.height.body = MathAbs(iOpen(_Symbol,_Period,1) - iClose(_Symbol,_Period,1));
然而,不必要地使结构复杂化是不必要的。 在我们的例子中,将高度分配到一个单独的结构是多余的,并且仅用于显示嵌套结构。 但是,对于复杂的数据组织,例如层次结构,使用嵌套结构是有益的。
机构间数据交换
您可以使用赋值运算符(=)为同一类型的另一个变量赋值结构变量。 但是,这只能在所有元素都具有简单类型的结构中完成。 在我们的例子中,结构 Height 只有整数字段,因此以下代码完全正确:
Heigh h1, h2={1,4,1,8}; h1=h2;
由于所有变量字段的值 h2 复制到变量字段 h1 .
变量-包含复杂类型字段的结构:字符串、数组、结构不能分配给其他变量-这种类型的结构。 在这种情况下,您必须按元素复制数据:
Candle Bar1, Bar2= {0.0,0.0,0.0,0.0,100,D'2018.03.10 15:08:02',1,{0,0,0,0}}; Bar1.open = Bar2.open; // 开盘价 Bar1.close = Bar2.close; // 收盘价 Bar1.high = Bar2.high; // 最大值 Bar1.low = Bar2.low; // 最小值 Bar1.volume = Bar2.volume; // 柚木体积 Bar1.time = Bar2.time; // 开放时间 Bar1.type = Bar2.type; // 蜡烛类型(0-“狗”,1-公牛,2-熊) Bar1.height.full = Bar2.height.full; // 总烛光高度 Bar1.height.body = Bar2.height.body; // 蜡烛体尺寸 Bar1.height.top_shadow = Bar2.height.top_shadow; // 上阴影尺寸 Bar1.height.bottom_shadow = Bar2.height.bottom_shadow; // 下阴影尺寸
结构和职能
结构可以像普通变量一样作为参数传递给函数。 在这种情况下,只有通过引用才能将结构转移到功能中。 因此,函数中参数的所有更改都会导致传递参数的更改。
//包含平面上点坐标的结构 struct Coordinates { int x; int y; }; //设定坐标点(x=120,y=35) Coordinates P={120,35}; //+------------------------------------------------------------------+ 将坐标移到:? //shift_x水平 //shift_y垂直 //+------------------------------------------------------------------+ void MovePoint(Coordinates &point, int shift_x=0, int shift_y=0) { point.x+=shift_x; point.y+=shift_y; } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("x=",P.x,"; y=",P.y); MovePoint(P,2,15); Print("x=",P.x,"; y=",P.y); return(INIT_PARAMETERS_INCORRECT); }
结果:
P.x=122; P.y=50 P.x=120; P.y=35
在上面的例子中,我们将初始化变量P结构和坐标的水平和垂直偏移传递到函数中。 MovePoint() 因此,函数更改了结构的两个元素,这可以通过在调用函数之前和之后打印这些值来查看。
但是,函数可以返回结构,因此可以避免更改传递给函数的参数。 下一篇:改变我们的功能 MovePoint() 因此,它返回变量结构:
//+------------------------------------------------------------------+ 将坐标移到:? //shift_x水平 //shift_y垂直 //返回具有新坐标的结构。 //+------------------------------------------------------------------+ Coordinates MovePoint(Coordinates &point, int shift_x=0, int shift_y=0) { Coordinates p; p.x=point.x+shift_x; p.y=point.y+shift_y; return(p); } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("P.x=",P.x,"; P.y=",P.y); Coordinates MovedPoint=MovePoint(P,2,15); Print("MovedPoint.x=",MovedPoint.x,"; MovedPoint.y=",MovedPoint.y); Print("P.x=",P.x,"; P.y=",P.y); return(INIT_PARAMETERS_INCORRECT); }
结果:
P.x=120; P.y=35 MovedPoint.x=122; MovedPoint.y=50 P.x=120; P.y=35
我们看到函数返回变量 MovedPoint 计算值,但变量 P ,作为参数传递给函数的值没有变化。 请注意,只能返回不包含数组或嵌套结构的简单结构。
方法
不仅可以根据特定的标准对特定的数据进行分组,还可以对这些数据的操作进行分组。 这些函数称为方法。
在我们的结构中 Candle 定义获取蜡烛打开时间的方法,在时间序列数组中搜索它,并用找到的蜡烛填充结构字段。
struct Height { int full; // 总烛光高度 int body; // 蜡烛体尺寸 int top_shadow; // 上阴影尺寸 int bottom_shadow; // 下阴影尺寸 }; struct Candle { double open; // 开盘价 double close; // 收盘价 double high; // 最大值 double low; // 最小值 long volume; // 柚木体积 datetime time; // 开放时间 uchar type; // 蜡烛类型(0-“狗”,1-公牛,2-熊) Height height; // 蜡烛部分高度 void GetCandleParam(datetime open_time) { //获取蜡烛时间索引或最近蜡烛的索引 int index=iBarShift(_Symbol,_Period,open_time); //计算结构域 open = iOpen(_Symbol,_Period,index); // 开盘价 close = iClose(_Symbol,_Period,index); // 收盘价 high = iHigh(_Symbol,_Period,index); // 最大值 low = iLow(_Symbol,_Period,index); // 最小值 volume = iVolume(_Symbol,_Period,index); // 柚木体积 time = iTime(_Symbol,_Period,index); // 开放时间 height.full = int((high-low)/_Point); // 总烛光高度 height.body = int((close-open)/_Point); // 蜡烛体尺寸 type = height.body>0? 1:(height.body<0?2:0); // 蜡烛类型(0-“狗”,1-公牛,2-熊) height.top_shadow = int((high-((type<2)? close:open))/_Point); // 上阴影尺寸 height.bottom_shadow = int((((type<2)? open:close)-low)/_Point); // 下阴影尺寸 height.body = MathAbs(height.body); } } Bar; // 让我们立即宣布一个变量-结构 //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Bar. GetCandleParam(D'2020.10.02 10:00'); 打印(“打开蜡烛的价格:”,doubletostr(bar.open,_digits)); 打印(“蜡烛关闭价格:”,doubletostr(bar.close,_digits)); 打印(“蜡烛类型:”,bar.type==0?“dogie”:(bar.type==1?“公牛”:“熊”); 打印(“蜡烛高度:”,integertostring(bar.height.full)); 打印(“时间:”,bar.time); return(INIT_PARAMETERS_INCORRECT); }
访问细节
要限制对字段和结构方法的访问,请使用访问特定符: private , protected 和 public 默认情况下,所有字段和结构方法都有修饰符 public 也就是说,你可以从外面联系他们。 如果不使用继承,则protected和private特异性是相同的。
之后声明的所有字段和方法 private: 在…之前 public: ,只有结构方法可用。
struct Candle { private: double open; // 开盘价 double close; // 收盘价 double high; // 最大值 double low; // 最小值 long volume; // 柚木体积 datetime time; // 开放时间 public: uchar type; // 蜡烛类型(0-“狗”,1-公牛,2-熊) Height height; // 蜡烛部分高度 void GetCandleParam(datetime open_time) { //获取蜡烛时间索引或最近蜡烛的索引 int index=iBarShift(_Symbol,_Period,open_time); //计算结构域 open = iOpen(_Symbol,_Period,index); // 开盘价 close = iClose(_Symbol,_Period,index); // 收盘价 high = iHigh(_Symbol,_Period,index); // 最大值 low = iLow(_Symbol,_Period,index); // 最小值 volume = iVolume(_Symbol,_Period,index); // 柚木体积 time = iTime(_Symbol,_Period,index); // 开放时间 height.full = int((high-low)/_Point); // 总烛光高度 height.body = int((close-open)/_Point); // 蜡烛体尺寸 type = height.body>0? 1:(height.body<0?2:0); // 蜡烛类型(0-“狗”,1-公牛,2-熊) height.top_shadow = int((high-((type<2)? close:open))/_Point); // 上阴影尺寸 height.bottom_shadow = int((((type<2)? open:close)-low)/_Point); // 下阴影尺寸 height.body = MathAbs(height.body); } }
在这个结构域的例子中 open , close , high , volum e , time 仅从方法可用 GetCandleParam() .
用结构写顾问
考虑如何使用结构优化顾问代码的示例。 让我们写两个版本的简单网格:没有结构和使用结构收集有关当前帐户状态的信息。
顾问算法
我们的算法 顾问 总的来说,它看起来像这样:
当你打开一个新的蜡烛时,你会得到一个交易信号,如果上一个蜡烛关闭在附近的最低值以下,你会得到一个买入信号,如果上一个蜡烛关闭在附近的最低值以下,你会得到一个卖出信号, 在其他情况下,没有信号;
如果没有开放 订单 进入信号;
如果有未打开的订单,并且收到相反的信号,则当前订单 利润 再加上一个最小的,我们就可以关闭它。 利润 ;
如果有未平仓订单及其当前亏损,则信号与网格的方向一致,并且当前价格距离网格的极端订单很远, 一个最小的步骤-打开平均订单放大 测深 .
输入参数
根据算法,输入参数 顾问 应为:
input double Lots = 0.01; // 固定测深 input double Multiplier = 2.0; // 乘数 input int MaxLegs = 10; // 最大膝盖数 input int MinProfit = 1.0; // 最低利润 input int MinStep = 100; // 订单之间的最小距离(步骤) input int Magic = 1100; // Magic Number(订单ID) input int Slippage = 30; // 最大容许滑动
顾问的职能
首先,我们需要一个新酒吧信号功能:
//+------------------------------------------------------------------+ 新酒吧在当前时间 //+------------------------------------------------------------------+ bool IsNewBar() { static datetime last_bar=0; datetime null_bar=iTime(_Symbol,_Period,0); if(last_bar!=null_bar) { last_bar=null_bar; return(true); } return(false); }
接下来,我们写一个生成交易信号的函数:
//+------------------------------------------------------------------+ //–如果形成了购买信号,则返回OPu BUY。 //opu sell如果有销售信号,并且 //如果封闭酒吧没有信号,则为空。 //+------------------------------------------------------------------+ int GetSignal() { if(iClose(_Symbol,_Period,1)<iLow(_Symbol,_Period,2)) return(OP_BUY); if(iHigh(_Symbol,_Period,2)<iClose(_Symbol,_Period,1)) return(OP_SELL); return(EMPTY); }
下一个函数定义当前订单网格的方向。 一旦第一个订单打开 顾问 ,它的类型将被视为网格的方向。 我们正在写一个演示版,所以我们不会处理可能的例外情况时,出现了问题,帐户打开了反订单。 假设我们在每一个时刻 帐单 只有一个方向的订单打开或没有打开。
//+------------------------------------------------------------------+ //^返回第一个找到的订单类型,该订单已打开。 //当前符号的顾问 //+------------------------------------------------------------------+ int GetOpenedOrdersType() { for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) if(OrderSymbol()==_Symbol && OrderMagicNumber()==Magic) return(OrderType()); return(EMPTY); }
以下两个函数返回打开的总数 顾问 订单和 利润 根据这些命令:
//+------------------------------------------------------------------+ //^返回打开的顾问总数.. //当前字符上指定的订单类型。 //+------------------------------------------------------------------+ int GetOrdersCount(int type) { int count=0; for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) if(OrderSymbol()==_Symbol && OrderMagicNumber()==Magic && OrderType()==type) count++; return(count); } //+------------------------------------------------------------------+ //^退还顾问开立的所有订单的利润。 //+------------------------------------------------------------------+ double GetProfit() { double profit=0; for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) if(OrderSymbol()==_Symbol && OrderMagicNumber()==Magic) profit+=OrderProfit()+OrderCommission()+OrderSwap(); return(profit); }
由于销售订单是平均的,如果价格高于开盘价格,我们需要找到从当前价格到最高销售订单的距离来验证, 是否会在允许的距离内有新的平均订单:
//+------------------------------------------------------------------+ //返回最高的开盘价。 //在网格中公开出售订单› //+------------------------------------------------------------------+ double GetTopSellPrice() { double price=0; for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) if(OrderSymbol()==_Symbol && OrderMagicNumber()==Magic && OrderType()==OP_SELL) if(price<OrderOpenPrice()) price=OrderOpenPrice(); return(price); }
对于购买订单,我们将写一个类似的功能,只有在这种情况下,我们将检查从当前价格到最低购买订单的距离:
//+------------------------------------------------------------------+ //返回最低开盘价格。 //在网格中打开购买订单› //+------------------------------------------------------------------+ double GetBottomBuyPrice() { double price=0; for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) if(OrderSymbol()==_Symbol && OrderMagicNumber()==Magic && OrderType()==OP_BUY) if(OrderOpenPrice()<price || price==0) price=OrderOpenPrice(); return(price); }
测深 我们将使用以下函数计算下一个网格订单:
//+------------------------------------------------------------------+ //^返回计算为n项的批量大小。 几何级数 //+------------------------------------------------------------------+ double GetLot(int n) { return(NormalizeDouble(Lots*MathPow(MathMax(1,Multiplier),n),2)); }
也在 参赞 我们不能没有贸易功能。 第一个订单将在给定方向上以当前市场价格打开指定数量的订单。
//+------------------------------------------------------------------+ #22312;大批量交易。 去COM的方向 //+------------------------------------------------------------------+ bool SetOrder(double lot, int com) { MqlTick tick; if(SymbolInfoTick(_Symbol,tick)) { for(int i=0; i<5; i++) { if(OrderSend(_Symbol,com,lot,((com==OP_BUY)? tick.ask:tick.bid),Slippage,0,0,NULL,Magic,0,((com==OP_BUY)? clrBlue:clrRed))>EMPTY) return(true); Sleep(1000); } } return(false); }
第二,关闭所有开放的 顾问 订单:
//+------------------------------------------------------------------+ //关闭顾问发布的所有市场订单。 目前的符号 //+------------------------------------------------------------------+ void CloseOrders() { for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) if(OrderSymbol()==_Symbol && OrderMagicNumber()==Magic) { MqlTick tick; if(SymbolInfoTick(_Symbol,tick)) { for(int j=0; j<5; j++) { if(OrderClose(OrderTicket(),OrderLots(),((OrderType()==OP_BUY)? tick.bid:tick.ask),Slippage,((OrderType()==OP_BUY)? clrBlue:clrRed))) break; Sleep(1000); } } } }
现在,所有必要的功能都已创建,剩下的就是编码我们顾问的简单交易逻辑,并将整个代码放入处理程序中。 OnTick() :
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //打开新订单,只在新酒吧开张时记录利润 if(IsNewBar()) { 收到新信号 int Signal=GetSignal(); //如果未在封闭的酒吧中形成信号,则立即退出功能。 if(Signal<0) return; //是否有公开的命令顾问 int OpenedOrderType=GetOpenedOrdersType();// (!!!) //有未打开的订单,它们不等于信号 if(OpenedOrderType!=EMPTY && Signal!=OpenedOrderType) { 如果有利润,我们将关闭订单。 if(GetProfit()>MinProfit)// (!!!) { CloseOrders(); OpenedOrderType=GetOpenedOrdersType();// (!!!) } } //没有公开订单 if(OpenedOrderType==EMPTY) { //打开第一个网格订单 SetOrder(Lots,Signal); return; } //按信号打开平均订单 if(Signal==OpenedOrderType) { //计算下一批订单的未平仓订单数 int Count=GetOrdersCount(OpenedOrderType);// (!!!) double Price; switch(OpenedOrderType) { case OP_BUY: Price=GetBottomBuyPrice();// (!!!) //检查到最高买入订单的最小步骤 if(int((Price-Ask)/_Point)>MinStep) { SetOrder(GetLot(Count),OP_BUY); } break; //检查最低卖出订单的最小步骤 case OP_SELL: Price=GetTopSellPrice();// (!!!) if(int((Bid-Price)/_Point)>MinStep) { SetOrder(GetLot(Count),OP_SELL); } break; } } } }
正如你所看到的,顾问接受了 贸易解决方案 根据目前的情况, 帐单 :利润大小,任何类型的未平仓订单的可用性,从市场价格到网格最大订单的距离。 所有这些参数都使用以下函数计算:
GetOpenedOrdersType();
GetOrdersCount();
GetProfit();
GetTopSellPrice();
GetBottomBuyPrice().
这些函数的调用用注释(!!)标记。 所有这些用于计算的函数都会在一个循环中搜索未打开的订单,选择顾问打开的订单并满足特定条件的订单。 因此,我们多次扫描市场情况,首先从调用每个功能开始过度订单。 这不是一个人工的例子-这种顾问架构经常出现。 我们花在服务器上的时间最多,所以问题是,如果我们在一次访问中获得所有必要的信息,会发生什么? 这个问题的答案是使用一个结构,它包含一组元素,我们将在其中放置所有必要的数据,只需在循环中移动所有订单一次:
struct state { int buy_count; // 订单数量Buy int sell_count; // 订单数量sell double buy_bottom_price; // 最低订单开仓价格 double sell_top_price; // 最高卖出订单开仓价格 double profit; // 所有订单的利润 //收集账户信息的方法 //更新结构元素 void Refresh(); } State;
结构中有一种方法 Refresh() ,它计算并为结构元素分配值。 在结构外定义方法的主体。 为此,使用上下文解析操作(::)。 上下文是结构的描述符(名称):
//+------------------------------------------------------------------+ //方法收集有关当前帐户状态的信息。 //并更新相应的结构字段。 //+------------------------------------------------------------------+ void state::Refresh(void) { //将结构的数字字段归零 ZeroMemory(this); for(int i=OrdersTotal()-1; i>=0; i--) if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) if(OrderSymbol()==_Symbol && OrderMagicNumber()==Magic) { double OpenPrice=OrderOpenPrice(); profit+=OrderProfit()+OrderCommission()+OrderSwap(); switch(OrderType()) { case OP_BUY: buy_count++; if(OpenPrice<buy_bottom_price || buy_bottom_price==0) buy_bottom_price=OpenPrice; break; case OP_SELL: sell_count++; if(OpenPrice>sell_top_price || sell_top_price==0) sell_top_price=OpenPrice; break; } } }
请注意,在方法体中,我们不使用点来访问结构元素,因为我们使用上下文解析操作。 在方法正文开头更新数字字段之前,函数将其归为零。 ZeroMemory() 关键字 this ,因此,结构将链接传递给自己。
处理程序中的主顾问代码 OnTick() 现在看起来像这样:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { for(int i=0; i<2000; i++) { double b=MathSqrt(i); } //打开新订单,只在新酒吧开张时记录利润 if(IsNewBar()) { 收到新信号 int Signal=GetSignal(); //如果未在封闭的酒吧中形成信号,则立即退出功能。 if(Signal<0) return; //更新结构 State. Refresh(); // (!!!) //如果当前利润超过最低值,并且收到回调信号 if(State.profit>MinProfit) if((Signal==OP_BUY && State.sell_count>0) || (Signal==OP_SELL && State.buy_count>0)) { 关闭所有订单 CloseOrders(); //更新结构 State. Refresh(); // (!!!) } //购买订单 if(State.buy_count>0) { //如果到最低购买的距离大于最小步数,则按信号平均 if(Signal==OP_BUY && int((State.buy_bottom_price-Ask)/_Point)>MinStep) SetOrder(GetLot(State.buy_count),Signal); } else //订单出售 if(State.sell_count>0) { //如果最高卖出距离大于最低卖出距离,则按信号平均 if(Signal==OP_SELL && int((Bid-State.sell_top_price)/_Point)>MinStep) SetOrder(GetLot(State.sell_count),Signal); } else //没有订单,我们打开一个新的信号。 SetOrder(Lots,Signal); } }
方法调用 State. Refresh() 取代了五个功能: GetOpenedOrdersType() , GetOrdersCount() , GetProfit() , GetTopSellPrice() , GetBottomBuyPrice() 因此,对服务器的访问量也减少了,这可能会影响速度。 此外,代码变得更紧凑。
结论
在本课程中,我们熟悉了结构数据类型及其MQL4实现。 本教程的主要目的是展示如何使用结构来帮助程序员提高代码的效率。 我希望这个目标已经实现了。 然而,结构是将数据及其操作结合在一起的对象,是过程编程和面向对象编程之间的桥梁。 因此 习惯 在你的代码中使用结构化数据类型对那些计划学习的人来说是一个很好的训练。 巴解组织 .
论坛主题
尊敬的尤里·洛塞夫AKA LSV107 Tlap.com