
快捷手动交易工具箱:持仓和挂单操控
内容
概述
我们之前曾创建了基本功能,旨在帮助偏爱手动交易的交易员。 我们重点关注与下单相关的便捷操作,因此大部分函数都与市价入场有关。 然而,任何交易策略,无论是手动还是自动,在以市价操作时,都应该有三个主要阶段。 其中包括市场准入规则、持仓管理和平仓条件。 至于目前,该工具箱仅覆盖第一阶段。 因此,随着深入发展,我们可以增加更多的机会来操控持仓或挂单,并扩展平仓条件。 所有的计算都应该由工具箱来完成,而决策则应由交易者制定。
问题表现
我们从判断实现新功能所需的任务范围开始。 我们依据要继续开发的当前应用程序,来判断主要阶段:
- 修订应用程序结构。该应用程序最初是作为一个主窗口创建的,含有一组任务按钮,其中两个打开内有相应市价单和挂单开单工具的模式窗口。 其他三个按钮提供了一些简单的功能,可按市价平仓和删除所有挂单。 现在,我们需要添加一个管理当前持仓和编辑挂单的界面。
- 扩展订单操作模式。以前,我们可将所有盈利或亏损的订单平仓。 现在,我们需要一个更灵活的功能,按持仓类型划分,并且可设置各种条件来选择所有/选定的订单平仓。 对于挂单,我们只能将早前由工具箱创建的订单批量删除。 这显然还不够。 我们应能够删除单独的挂单或修改它。 另外,市价单和挂单最好分开列出。
我们仔细研究每一个单独的阶段。
修订应用程序结构
我们先来看看已经实现了什么。 下面的图例 1 展示了主要的模块,它们分成两个类别执行任务:开仓/创建,和平仓/删除。 第三个类别是手工管理和修改。
图例 1 工具箱的主要模块
如此,我们来创建三个选项卡。 第一个将用于图例 1 所示的函数。 在其他两个选项卡中,我们将实现处理持仓和挂单的功能。
图例 2 新的应用程序结构
以前位于主窗口中的所有功能都将在 “Trading” 选项卡中实现。 开仓控制将包括一个由此工具开仓的表格。 在该处也将执行与这些持仓相关的命令。 挂单控制功能包括由工具箱创建的订单,以及平仓/修改持仓的控件。 我们更加详细地研究一下这些选卡。
图例 3 在场持仓控制选项卡
图例 3 示意在场持仓控制选项卡。 该选项卡包含以下元素:
- 在场订单选卡。显示应用程序开仓的所有当前在场持仓的信息。 它类似于 MetaTrader 5 中的交易选项卡里的表格。
- 三个输入字段。它们对应并链接到表列:成交量、止损、止盈。
- 两个按钮。单击表格行后,可编辑的持仓参数将显示在输入字段里。 单击编辑,我们可以修改列表中所选持仓的止损和止盈,甚至删除它们。 平仓按钮则以当前价格平仓。 平仓时,系统会额外检查交易量输入字段。 这意味着您可以选择小于当前持仓手数的手数值,并部分平仓。
现在,查看图例 4 中的 Pending Order Control 选项卡。
图例 4 挂单控制。
它与前一个非常相似:
- 一张挂单表格。 它包含由此工具箱创建的挂单列表。
- 三个输入字段。 当表里挂单选取后,可以修改其当前执行价格,也可以编辑或删除止损、止盈。
- 两个按钮。 Edit 按钮,与在场持仓表格不同,可访问所有三个输入字段,编辑所选的挂单。 Close 按钮平单。
我们回到 "Trading" 选卡,并修改它从而获得新功能。 首先,我们修改现有的平仓工具。 不光可以多头平仓,还可以空头平仓。 这是通过平仓模式开关来实现的。
图例 5 扩展在场持仓平仓功能。
正如您在图例 5 所见,我们有 4 个新按钮:Close BUY profit, Close SELL profit, Close BUY loss, Close SELL loss。 在按钮的右边还有另外的开关;我们来更详细地研究一下。 开关相似,因此以下描述适用于所有这些开关。
- All。默认值。 按钮不会设置任何限制,并关闭所有选定项。
- >Point。 若所选类型的所有持仓盈亏高于指定点数则平仓。
- >Currency。所选类型所有持仓盈亏高于指定存款货币金额则平仓。
- Sum>Points。 所选类型的所有持仓总盈亏高于指定点数的平仓。
- Sum>Currency。 所选类型的所有持仓总盈亏高于指定存款币种金额则平仓。
我们来研究一个如图例 5 所示的持仓示例:Close All Loss 且选项 sum>currency。 在这种情况下,工具箱将查找之前由它开立的所有持仓,累计它们的利润,如果按存款货币单位计算它的超过 10 个单位,则所有持仓平仓。
工具箱附加的实现
作为基础,我们将使用早前在文章快捷手动交易工具箱:基本功能里创建的项目。 首先,我们需要重新构造主窗口,如图例 2 所示。 为此,在CProgram基类中创建CreateTabs() 方法,添加选卡界面元素,并在MainWindow.mqh 中实现它。
//+------------------------------------------------------------------+ //| Create a group with tabs | //+------------------------------------------------------------------+ bool CFastTrading::CreateTabs(const int x_gap,const int y_gap) { //--- Store the pointer to the main control m_tab.MainPointer(m_main_window); //--- Properties m_tab.Font(m_base_font); m_tab.FontSize(m_base_font_size); m_tab.LabelColor(clrWhite); m_tab.LabelColorHover(clrWhite); m_tab.IsCenterText(true); m_tab.AutoXResizeMode(true); m_tab.AutoYResizeMode(true); m_tab.AutoXResizeRightOffset(5); m_tab.AutoYResizeBottomOffset(5); m_tab.TabsYSize(27); m_tab.GetButtonsGroupPointer().Font(m_base_font); m_tab.GetButtonsGroupPointer().FontSize(m_base_font_size); //--- Add tabs with the specified properties string tabs_names[3]; tabs_names[0]=TRADING; tabs_names[1]=CAPTION_M_CONTROL_NAME; tabs_names[2]=CAPTION_P_CONTROL_NAME; for(int i=0; i<3; i++) m_tab.AddTab(tabs_names[i],180); //--- Create a control element if(!m_tab.CreateTabs(x_gap,y_gap)) return(false); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(0,m_tab); return(true); }
所创建方法实体有三个新的宏替换 ,它们服务于选卡标题 - 它们需要将两种语言添加到 Defines.mqh 文件之中:
#define TRADING (m_language==RUSSIAN ? "Трейдинг" : "Trading") #define CAPTION_M_CONTROL_NAME (m_language==RUSSIAN ? "Контроль рыночных позиций" : "Market Control") #define CAPTION_P_CONTROL_NAME (m_language==RUSSIAN ? "Контроль отложенных ордеров" : "Pending Control")
在应用新创建的方法之前,我们需要重新构建创建按钮的方法,并将这些方法链接到第一个选项卡。 我们来看看常用方法 CreateButton(),并按如下方式编辑:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateButton(CButton &button,string text,color baseclr,int x_gap,int y_gap) { //--- Store the window pointer button.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(0,button); //--- Set properties before creation button.XSize(180); button.YSize(40); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(baseclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(baseclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(0,button); return(true); }
现在,令创建主窗口方法中的修改生效。
//+------------------------------------------------------------------+ //| Creates a form for orders | //+------------------------------------------------------------------+ bool CFastTrading::CreateMainWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_main_window); //--- Properties m_main_window.XSize(600); m_main_window.YSize(375); //--- Coordinates int x=5; int y=20; m_main_window.CaptionHeight(22); m_main_window.IsMovable(true); m_main_window.CaptionColor(m_caption_color); m_main_window.CaptionColorLocked(m_caption_color); m_main_window.CaptionColorHover(m_caption_color); m_main_window.BackColor(m_background_color); m_main_window.FontSize(m_base_font_size); m_main_window.Font(m_base_font); //--- Create the form if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) return(false); //--- Tabs if(!CreateTabs(5,22+27)) return(false); //--- if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10)) return(false); if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10)) return(false); if(!CreateButton(m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60)) return(false); if(!CreateButton(m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110)) return(false); if(!CreateButton(m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60)) return(false); //--- return(true); }
正如此处所见,首先添加了选卡,然后更新了创建按钮的方法调用,调整了主窗口的大小。 项目编译完成后,所有按钮将移到第一个选项卡:
图例 6 创建选项卡,并将按钮移到第一个选项卡
根据图例 5,我们创建附加的按钮和输入字段,来实现所需的功能。 对于大按钮,我们将利用更新的 CreateButton() 方法。 然而,为了创建输入字段和开关,我们需要引入其他方法:CreateModeButton() — 模式切换开关,CreateModeEdit() — 输入字段。 它们的完整实现如下:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateModeButton(CButton &button,string text,int x_gap,int y_gap) { //--- Store the window pointer button.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(0,button); color baseclr=clrDarkViolet; //--- Set properties before creation button.XSize(80); button.YSize(20); button.Font(m_base_font); button.FontSize(9); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(baseclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(baseclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(0,button); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateModeEdit(CTextEdit &text_edit,int x_gap,int y_gap) { //--- Store the window pointer text_edit.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(0,text_edit); //--- Properties text_edit.XSize(80); text_edit.YSize(20); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.SetDigits(2); text_edit.MaxValue(99999); text_edit.StepValue(0.01); text_edit.MinValue(0.01); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,y_gap)) return(false); text_edit.SetValue(string(0.01)); text_edit.IsLocked(true); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(0,text_edit); return(true); }
下面是更新后的方法,它利用上述方法创建主窗口,并添加图例 5 中的按钮:
//+------------------------------------------------------------------+ //| Creates a form for orders | //+------------------------------------------------------------------+ bool CFastTrading::CreateMainWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_main_window); //--- Properties m_main_window.XSize(600); m_main_window.YSize(375); //--- Coordinates int x=5; int y=20; m_main_window.CaptionHeight(22); m_main_window.IsMovable(true); m_main_window.CaptionColor(m_caption_color); m_main_window.CaptionColorLocked(m_caption_color); m_main_window.CaptionColorHover(m_caption_color); m_main_window.BackColor(m_background_color); m_main_window.FontSize(m_base_font_size); m_main_window.Font(m_base_font); //--- Create the form if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) return(false); //--- Tabs if(!CreateTabs(5,22+27)) return(false); //--- if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10)) return(false); if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10)) return(false); if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1)) return(false); if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1)) return(false); if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT,C'87,128,255',10,10+45*2)) return(false); if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT,C'87,128,255',10,10+45*3)) return(false); if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4)) return(false); if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS,C'87,128,255',10,10+45*5)) return(false); if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS,C'87,128,255',10,10+45*6)) return(false); //--- for(int i=0; i<6; i++) { if(!CreateModeButton(m_mode_button[i],"all",205-10,10+45*(i+1))) return(false); if(!CreateModeEdit(m_mode_edit[i],204-10,30+45*(i+1))) return(false); } //--- return(true); }
此处使用新的宏替换,因此,应将相应的值添加到 Defines.mqh:
#define CLOSE_BUY_PROFIT (m_language==RUSSIAN ? "Закрыть BUY прибыльные" : "Close BUY Profit") #define CLOSE_SELL_PROFIT (m_language==RUSSIAN ? "Закрыть SELL прибыльные" : "Close SELL Profit") #define CLOSE_ALL_PROFIT (m_language==RUSSIAN ? "Закрыть ВСЕ прибыльные" : "Close ALL Profit") #define CLOSE_BUY_LOSS (m_language==RUSSIAN ? "Закрыть BUY убыточные" : "Close BUY Losing") #define CLOSE_SELL_LOSS (m_language==RUSSIAN ? "Закрыть SELL убыточные" : "Close SELL Losing") #define CLOSE_ALL_LOSS (m_language==RUSSIAN ? "Закрыть ВСЕ убыточные" : "Close ALL Losing")
编制项目,并检查中间结果:
图例 7 添加按钮和模式开关
但这只是一个可视化的实现。 下一步是为每个添加的元素分配一个逻辑任务。 我们来设置切换机制,因为所有后续元素都将引用它们的数值和状态。 为按钮对象创建一个新方法ModeButtonSwitch()。 当按下按钮时,它将切换模式。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::ModeButtonSwitch(CButton &button,long lparam,string &states[]) { if(lparam==button.Id()) { int size=ArraySize(states); for(int i=0; i<size; i++) { if(button.LabelText()==states[i]) { if(i==size-1) { SetButtonParam(button,states[0]); break; } else { SetButtonParam(button,states[i+1]); break; } } } } }
另一个新方法 ModeEditSwitch() 为所选模式提供了设置对应关系的输入字段。 例如,点数是整数,当我们用存款货币时,则数值应有两个小数位。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::ModeEditSwitch(long lparam,string &states[]) { for(int i=0; i<6; i++) { if(lparam==m_mode_button[i].Id()) { if(m_mode_button[i].LabelText()==states[1]) { m_mode_edit[i].IsLocked(false); m_mode_edit[i].SetDigits(0); m_mode_edit[i].StepValue(1); m_mode_edit[i].MaxValue(9999); m_mode_edit[i].MinValue(1); m_mode_edit[i].SetValue(string(20)); m_mode_edit[i].GetTextBoxPointer().Update(true); m_current_mode[i]=1; } else if(m_mode_button[i].LabelText()==states[2]) { m_mode_edit[i].IsLocked(false); m_mode_edit[i].SetDigits(2); m_mode_edit[i].StepValue(0.01); m_mode_edit[i].MaxValue(99999); m_mode_edit[i].MinValue(0.01); m_mode_edit[i].SetValue(string(0.1)); m_mode_edit[i].GetTextBoxPointer().Update(true); m_current_mode[i]=2; } else if(m_mode_button[i].LabelText()==states[3]) { m_mode_edit[i].IsLocked(false); m_mode_edit[i].SetDigits(0); m_mode_edit[i].StepValue(1); m_mode_edit[i].MaxValue(9999); m_mode_edit[i].MinValue(1); m_mode_edit[i].SetValue(string(20)); m_mode_edit[i].GetTextBoxPointer().Update(true); m_current_mode[i]=3; } else if(m_mode_button[i].LabelText()==states[4]) { m_mode_edit[i].IsLocked(false); m_mode_edit[i].SetDigits(2); m_mode_edit[i].StepValue(0.01); m_mode_edit[i].MaxValue(99999); m_mode_edit[i].MinValue(0.01); m_mode_edit[i].SetValue(string(0.1)); m_mode_edit[i].GetTextBoxPointer().Update(true); m_current_mode[i]=4; } else { m_mode_edit[i].IsLocked(true); m_current_mode[i]=0; } } } }
当前的实现有一个静态数组 m_current_mode — 其大小对应于模式切换开关的数量,即 6。 用户选择的每个平仓按钮模式将写入此数组。 若要激活新添加的方法,打开 OnEvent() 事件应答程序,并在按钮点击事件中添加以下代码:
//--- string states[5]= {"all",">points",">currency","sum>points","sum>currency"}; for(int i=0; i<6; i++) ModeButtonSwitch(m_mode_button[i],lparam,states); //--- ModeEditSwitch(lparam,states);
编译项目。 现在您可看到模式切换改变了输入字段的属性。 如图例 8 所示。
图例 8 转换在场持仓平仓模式
下一步是根据按钮描述实现动作逻辑,其应与早前添加的模式相链接。 我们已经有两个动作:“所有盈利平仓”和“所有亏损平仓”。 现在,它们应根据新的平仓模式进行扩展。 这些动作由方法 CloseAllMarketProfit() 和 CloseAllMarketLoss() 执行。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseAllMarketProfit(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[2].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_C)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // the number of open positions int sum_pp=0; double sum_cur=0.0; //--- Calculate profit for modes 4 and 5 if(m_current_mode[0]>2) { for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- int profit_pp; if(type==POSITION_TYPE_BUY) profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point); else profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) { switch(m_current_mode[0]) { case 3: sum_pp+=profit_pp; break; case 4: sum_cur+=profit_cur+swap; break; } } } } //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); int profit_pp; //--- if(type==POSITION_TYPE_BUY) profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point); else profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point); //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) { if( (m_current_mode[0]==0 && profit_cur+swap>0) || // Close all profitable positions (m_current_mode[0]==1 && profit_pp>int(m_mode_edit[0].GetValue())) || // Close all positions having profit more than N points (m_current_mode[0]==2 && profit_cur+swap>double(m_mode_edit[0].GetValue())) || // Close all positions having profit more than N deposit currency units (m_current_mode[0]==3 && sum_pp>int(m_mode_edit[0].GetValue())) || // Close all positions with the total profit more than N points (m_current_mode[0]==4 && sum_cur>double(m_mode_edit[0].GetValue())) // Close all positions with the total profit more than N deposit currency units ) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) break; else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } } }
这是针对平仓方法的修改。 早前,我们引入了 m_current_mode 静态数组,它跟踪按钮上每个动作的模式选择。 因此,针对模式 4 和模式 5 进行计算,在模式 4 和模式 5 中,所有持仓的总利润都以点数或存款货币结算。 之后,我们选择属于我们工具箱的持仓,且根据所选平仓模式,选择条件,所有由工具箱创建的在场持仓都应被平仓。
与此类似,修改第二种方法,所有亏损持仓平仓:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseAllMarketLoss(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[6].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_D)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); int total=PositionsTotal(); // the number of open positions int sum_pp=0; double sum_cur=0.0; //--- Calculate losses if(m_current_mode[3]>2) { for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double loss_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); int loss_pp; //--- if(type==POSITION_TYPE_BUY) loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point); else loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) { switch(m_current_mode[3]) { case 3: sum_pp+=loss_pp; break; case 4: sum_cur+=loss_cur+swap; break; } } } } //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double loss_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); int loss_pp; if(type==POSITION_TYPE_BUY) loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point); else loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point); //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) { if( (m_current_mode[3]==0 && loss_cur+swap<0) || (m_current_mode[3]==1 && loss_pp<-int(m_mode_edit[3].GetValue())) || (m_current_mode[3]==2 && loss_cur+swap<-double(m_mode_edit[3].GetValue())) || (m_current_mode[3]==3 && sum_pp<-int(m_mode_edit[3].GetValue())) || (m_current_mode[3]==4 && sum_cur<-double(m_mode_edit[3].GetValue())) ) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action=TRADE_ACTION_DEAL; // trading operation type request.position=position_ticket; // position ticket request.symbol=position_symbol; // symbol request.volume=volume; // position volume request.deviation=5; // allowable price deviation request.magic=m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) break; else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } } }
此外,针对模式执行计算,按点数或存款货币检查所有持仓的总亏损,如果达到相应的亏损条件,那么由工具箱创建的所有在场持仓全都被平仓。
现在,我们继续采取新的行动:买单平仓/卖单平仓,即可盈利、亦或亏损。 事实上,这是上述两种方法的特例。 因此,我们将添加按持仓类型过滤。 首先,创建执行指定操作的方法:
- CloseBuyMarketProfit() — 所有盈利买单平仓。
- CloseSellMarketProfit() — 所有盈利卖单平仓
- CloseBuyMarketLoss() — 所有亏损买单平仓
- CloseSellMarketLoss() — 所有亏损卖单平仓
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseBuyMarketProfit(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_U)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // the number of open positions int sum_pp=0; double sum_cur=0.0; //--- Calculate profit for modes 4 and 5 if(m_current_mode[1]>2) { for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) if(type==POSITION_TYPE_BUY) { int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point); switch(m_current_mode[1]) { case 3: sum_pp+=profit_pp; break; case 4: sum_cur+=profit_cur+swap; break; } } } } //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) if(type==POSITION_TYPE_BUY) { int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point); if( (m_current_mode[1]==0 && profit_cur+swap>0) || // Close all profitable positions (m_current_mode[1]==1 && profit_pp>int(m_mode_edit[1].GetValue())) || // Close all positions having profit more than N points (m_current_mode[1]==2 && profit_cur+swap>double(m_mode_edit[1].GetValue())) || // Close all positions having profit more than N deposit currency units (m_current_mode[1]==3 && sum_pp>int(m_mode_edit[1].GetValue())) || // Close all positions with the total profit more than N points (m_current_mode[1]==4 && sum_cur>double(m_mode_edit[1].GetValue())) // Close all positions with the total profit more than N deposit currency units ) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) break; else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseBuyMarketLoss(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[7].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_H)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // the number of open positions int sum_pp=0; double sum_cur=0.0; //--- Calculate profit for modes 4 and 5 if(m_current_mode[4]>2) { for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) if(type==POSITION_TYPE_BUY) { int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point); switch(m_current_mode[1]) { case 3: sum_pp+=profit_pp; break; case 4: sum_cur+=profit_cur+swap; break; } } } } //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) if(type==POSITION_TYPE_BUY) { int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point); if( (m_current_mode[4]==0 && profit_cur+swap<0) || // Close all profitable positions (m_current_mode[4]==1 && profit_pp<-int(m_mode_edit[4].GetValue())) || // Close all positions having profit more than N points (m_current_mode[4]==2 && profit_cur+swap<-double(m_mode_edit[4].GetValue())) || // Close all positions having profit more than N deposit currency units (m_current_mode[4]==3 && sum_pp<-int(m_mode_edit[4].GetValue())) || // Close all positions with the total profit more than N points (m_current_mode[4]==4 && sum_cur<-double(m_mode_edit[4].GetValue())) // Close all positions with the total profit more than N deposit currency units ) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) break; else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseSellMarketProfit(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[5].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_J)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // the number of open positions int sum_pp=0; double sum_cur=0.0; //--- Calculate profit for modes 4 and 5 if(m_current_mode[2]>2) { for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) if(type==POSITION_TYPE_SELL) { int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point); switch(m_current_mode[2]) { case 3: sum_pp+=profit_pp; break; case 4: sum_cur+=profit_cur+swap; break; } } } } //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) if(type==POSITION_TYPE_SELL) { int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point); if( (m_current_mode[2]==0 && profit_cur+swap>0) || // Close all profitable positions (m_current_mode[2]==1 && profit_pp>int(m_mode_edit[2].GetValue())) || // Close all positions having profit more than N points (m_current_mode[2]==2 && profit_cur+swap>double(m_mode_edit[2].GetValue())) || // Close all positions having profit more than N deposit currency units (m_current_mode[2]==3 && sum_pp>int(m_mode_edit[2].GetValue())) || // Close all positions with the total profit more than N points (m_current_mode[2]==4 && sum_cur>double(m_mode_edit[2].GetValue())) // Close all positions with the total profit more than N deposit currency units ) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) break; else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseSellMarketLoss(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[8].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_L)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // the number of open positions int sum_pp=0; double sum_cur=0.0; //--- Calculate profit for modes 4 and 5 if(m_current_mode[5]>2) { for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) if(type==POSITION_TYPE_SELL) { int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point); switch(m_current_mode[1]) { case 3: sum_pp+=profit_pp; break; case 4: sum_cur+=profit_cur+swap; break; } } } } //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit_cur=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if(magic==m_magic_number) if(position_symbol==Symbol()) if(type==POSITION_TYPE_SELL) { int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point); if( (m_current_mode[5]==0 && profit_cur+swap<0) || // Close all profitable positions (m_current_mode[5]==1 && profit_pp<-int(m_mode_edit[5].GetValue())) || // Close all positions having profit more than N points (m_current_mode[5]==2 && profit_cur+swap<-double(m_mode_edit[5].GetValue())) || // Close all positions having profit more than N deposit currency units (m_current_mode[5]==3 && sum_pp<-int(m_mode_edit[5].GetValue())) || // Close all positions with the total profit more than N points (m_current_mode[5]==4 && sum_cur<-double(m_mode_edit[5].GetValue())) // Close all positions with the total profit more than N deposit currency units ) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) break; else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } } }
在创建并实现之后,调用 OnEvent() 事件应答程序里的方法,无需其它。
//---
CloseBuyMarketProfit(id,lparam);
CloseSellMarketProfit(id,lparam);
CloseBuyMarketLoss(id,lparam);
CloseSellMarketLoss(id,lparam);
每个动作不仅可以单击按钮来执行,还可由按键事件来执行。 您可以在代码中重新分配热键。 出于便捷起见,我们在按钮的操作名称旁边显示它们的值。
//--- if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10)) return(false); if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10)) return(false); if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1)) return(false); if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1)) return(false); if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT+"(U)",C'87,128,255',10,10+45*2)) return(false); if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT+"(J)",C'87,128,255',10,10+45*3)) return(false); if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4)) return(false); if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS+"(H)",C'87,128,255',10,10+45*5)) return(false); if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS+"(L)",C'87,128,255',10,10+45*6)) return(false);
结果就是,我们有以下数值:
图例 9 为添加的动作指定热键
我们已经实现了交易选项卡的所有功能。 现在,我们移入下一个阶段:创建由工具箱开仓的列表,并在 “Market Positions Control” 选项卡上添加管理持仓的可能性。 本文开头的图例 3 包含一个创建界面元素的可视化方案,它由三个输入字段、两个按钮和表格组成。 我们开始创建选项卡。 首先,我们创建三个输入字段,用于编辑持仓的止损和止盈。 这些是在方法 CreateLotControl()、CreateStopLossControl() 和 CreateTakeProfitControl() 里完成的。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateLotControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab) { //--- Store the pointer to the main control text_edit.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(tab,text_edit); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(_Point); text_edit.SetDigits(_Digits); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,y_gap)) return(false); text_edit.SetValue(string(0)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(0,text_edit); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateTakeProfitControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab) { //--- Store the pointer to the main control text_edit.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(tab,text_edit); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(_Point); text_edit.SetDigits(_Digits); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,y_gap)) return(false); text_edit.SetValue(string(0)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(0,text_edit); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateStopLossControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab) { //--- Store the pointer to the main control text_edit.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(tab,text_edit); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(_Point); text_edit.SetDigits(_Digits); text_edit.MinValue(0); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,y_gap)) return(false); text_edit.SetValue(string(0)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(0,text_edit); return(true); }
在 CreateMainWindow() 主体中添加新方法。
//--- Input field for editing open positions if(!CreateLotControl(m_lot_edit[6],375,3,1)) return(false); if(!CreateStopLossControl(m_sl_edit[6],375+80,3,1)) return(false); if(!CreateTakeProfitControl(m_tp_edit[6],375+83*2,3,1)) return(false);
对于 'Edit' 和 'Close' 按钮,我们需要创建两个新方法来实现它们:CreateModifyButton() 和 CreateCloseButton()。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateModifyButton(CButton &button,string text,int x_gap,int y_gap,int tab) { //--- Store the window pointer button.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(tab,button); color baseclr=clrDarkOrange; color pressclr=clrOrange; //--- Set properties before creation button.XSize(80); button.YSize(24); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(0,button); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateCloseButton(CButton &button,string text,int x_gap,int y_gap,int tab) { //--- Store the window pointer button.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(tab,button); color baseclr=clrCrimson; color pressclr=clrFireBrick; //--- Set properties before creation button.XSize(80); button.YSize(24); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(0,button); return(true); }
将它们添加到主窗口创建方法当中:
//--- Position editing/closing buttons if(!CreateModifyButton(m_small_button[0],MODIFY,622,3,1)) return(false); if(!CreateCloseButton(m_small_button[1],CLOSE,622+80,3,1)) return(false);
在此我们用两个新的宏替换来实现 UI 本地化,所以打开 Defines.mqh,并添加相应的值:
#define MODIFY (m_language==RUSSIAN ? "Изменить" : "Modify") #define CLOSE (m_language==RUSSIAN ? "Закрыть" : "Close")
现在,我们继续讨论这个问题。 首先,创建 CreatePositionsTable() 方法,并将其实现添加到主窗口方法之中。
//+------------------------------------------------------------------+ //| Create a table of positions | //+------------------------------------------------------------------+ bool CFastTrading::CreatePositionsTable(CTable &table,const int x_gap,const int y_gap) { #define COLUMNS2_TOTAL 9 //--- Store the pointer to the main control table.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(1,table); //--- Array of column widths int width[COLUMNS2_TOTAL]; ::ArrayInitialize(width,80); width[0]=100; width[1]=110; width[2]=100; width[3]=60; width[6]=90; //--- Array of text alignment in columns ENUM_ALIGN_MODE align[COLUMNS2_TOTAL]; ::ArrayInitialize(align,ALIGN_CENTER); //--- Array of text offset along the X axis in the columns int text_x_offset[COLUMNS2_TOTAL]; ::ArrayInitialize(text_x_offset,7); //--- Array of column image offsets along the X axis int image_x_offset[COLUMNS2_TOTAL]; ::ArrayInitialize(image_x_offset,3); //--- Array of column image offsets along the Y axis int image_y_offset[COLUMNS2_TOTAL]; ::ArrayInitialize(image_y_offset,2); //--- Properties table.Font(m_base_font); table.FontSize(m_base_font_size); table.XSize(782); table.CellYSize(24); table.TableSize(COLUMNS2_TOTAL,1); table.TextAlign(align); table.ColumnsWidth(width); table.TextXOffset(text_x_offset); table.ImageXOffset(image_x_offset); table.ImageYOffset(image_y_offset); table.ShowHeaders(true); table.HeadersColor(C'87,128,255'); table.HeadersColorHover(clrCornflowerBlue); table.HeadersTextColor(clrWhite); table.IsSortMode(false); table.LightsHover(true); table.SelectableRow(true); table.IsZebraFormatRows(clrWhiteSmoke); table.DataType(0,TYPE_LONG); table.AutoYResizeMode(true); table.AutoYResizeBottomOffset(5); //--- Create a control element if(!table.CreateTable(x_gap,y_gap)) return(false); //--- Set the header titles table.SetHeaderText(0,TICKET); table.SetHeaderText(1,SYMBOL); table.SetHeaderText(2,TYPE_POS); table.SetHeaderText(3,PRICE); table.SetHeaderText(4,VOLUME); table.SetHeaderText(5,SL); table.SetHeaderText(6,TP); table.SetHeaderText(7,SWAP); table.SetHeaderText(8,PROFIT); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(0,table); return(true); }
该表将有 9 列。 我们调整列宽,因为列名的长度有所不同。 在 Defines.mqh 文件中用宏替换设定两种可用语言的列名。
#define SYMBOL (m_language==RUSSIAN ? "Символ" : "Symbol") #define VOLUME (m_language==RUSSIAN ? "Объем" : "Volume") #define TYPE_POS (m_language==RUSSIAN ? "Тип позиции" : "Position Type") #define SWAP (m_language==RUSSIAN ? "Своп" : "Swap") #define PROFIT (m_language==RUSSIAN ? "Прибыль" : "Profit")
不过,若您现在尝试编译项目,您将看到表格、按钮和字段超出了窗口的右边缘。 因此,我们需要添加一个机制,根据其内容调整主窗口宽度。 这可以通过 WindowRezise() 方法来完成。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::WindowResize(int x_size,int y_size) { m_main_window.GetCloseButtonPointer().Hide(); m_main_window.ChangeWindowWidth(x_size); m_main_window.ChangeWindowHeight(y_size); m_main_window.GetCloseButtonPointer().Show(); }
在事件应答程序中,创建一个新的选卡点击事件。 在该事件中为每个选项卡添加主窗口宽度。
//--- Tab switching event if(id==CHARTEVENT_CUSTOM+ON_CLICK_TAB) { if(m_tab.SelectedTab()==0) WindowResize(600,m_main_window.YSize()); if(m_tab.SelectedTab()==1) WindowResize(782+10,m_main_window.YSize()); if(m_tab.SelectedTab()==2) WindowResize(682+10,m_main_window.YSize()); }
现在会正确显示信息,如图例 3 所示。 下一步是接收由应用程序开仓的数据,并在所创建表格里显示这些信息。 我们有三个步骤来准备和显示表格中的数据:
- 初始化。 在此,先判定属于我们工具箱的持仓数量,并依据该数据重建表格。
- 添加数据。 根据列标题中的说明,将每笔持仓的参数添加到表格当中。
- 取用填充的数据更新表格。
对于初始化的第一步,创建 InitializePositionsTable() 函数,根据当前品种和魔幻数字选择所有持仓。 如此,我们就得到了满足条件的持仓数量。 向表格中添加相同数量的行。
//+------------------------------------------------------------------+ //| Initializing the table of positions | //+------------------------------------------------------------------+ void CFastTrading::InitializePositionsTable(void) { //--- Get symbols of open positions int total=PositionsTotal(); // the number of open positions int cnt=0; //--- Delete all rows m_table_positions.DeleteAllRows(); //--- Set the number of rows equal to the number of positions for(int i=0; i<total; i++) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) { m_table_positions.AddRow(cnt); cnt++; } } //--- If there are positions if(cnt>0) { //--- Set the values in the table SetValuesToPositionsTable(); //--- Update the table UpdatePositionsTable(); } }
然后检查工具箱是否至少有一笔持仓。 如果存在这样的持仓,则调用 SetValuePositionTable() 方法,在新创建的表格行中设置有关持仓的信息。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::SetValuesToPositionsTable(void) { //--- int cnt=0; for(int i=PositionsTotal()-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double stoploss=PositionGetDouble(POSITION_SL); double takeprofit=PositionGetDouble(POSITION_TP); double swap=PositionGetDouble(POSITION_SWAP); double profit=PositionGetDouble(POSITION_PROFIT); double openprice=PositionGetDouble(POSITION_PRICE_OPEN); string pos=(type==POSITION_TYPE_BUY)?"BUY":"SELL"; profit+=swap; //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) { m_table_positions.SetValue(0,i,string(position_ticket)); m_table_positions.SetValue(1,i,string(position_symbol)); m_table_positions.SetValue(2,i,pos); m_table_positions.SetValue(3,i,string(openprice)); m_table_positions.SetValue(4,i,string(volume)); m_table_positions.SetValue(5,i,string(stoploss)); m_table_positions.SetValue(6,i,string(takeprofit)); m_table_positions.SetValue(7,i,string(swap)); m_table_positions.SetValue(8,i,DoubleToString(profit,2)); //--- m_table_positions.TextColor(2,i,(pos=="BUY")? clrForestGreen : clrCrimson); m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver); cnt++; } } }
填充数据之后,利用 UpdatePositionsTable() 更新表格:
//+------------------------------------------------------------------+ //| Update the table of positions | //+------------------------------------------------------------------+ void CFastTrading::UpdatePositionsTable(void) { //--- Update the table m_table_positions.Update(true); m_table_positions.GetScrollVPointer().Update(true); }
为了在产品令修改生效,我们应该正确配置它们。 打开 SimpleTrading.mq5 文件,找到 OnInit() 函数,并添加调用初始化应用程序类的方法:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- Initialize class variables program.FontName("Trebuchet MS"); program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); //--- Set up the trading panel if(!program.CreateGUI()) { Print(__FUNCTION__," > Failed to create graphical interface!"); return(INIT_FAILED); } program.OnInitEvent(); //--- return(INIT_SUCCEEDED); }
在创建应用程序 GUI CreateGUI() 之后,必须严格执行。 现在,转到 OnInitEvent() 实体,并调用其中的表格初始化。
//+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ void CFastTrading::OnInitEvent(void) { InitializePositionsTable(); }
现在,该表正确地显示了有关持仓的信息。 不过,这些数据只反映应用程序启动时的状况,因此应当不断更新。 这个应该在 OnTrade() 交易事件应答程序和 OnTick() 函数中完成。 在交易事件中,我们跟踪当前持仓的数量及其参数。 每笔订单的当前利润信息将在 OnTick 中更新。
在基类的公开部分创建 OnTradeEvent() 方法,并在其主体中调用表格初始化。
//+------------------------------------------------------------------+ //| Trade event | //+------------------------------------------------------------------+ void CFastTrading::OnTradeEvent(void) { //--- If a new trade InitializePositionsTable(); }
在交易事件应答程序中调用新方法:
//+------------------------------------------------------------------+ //| Trade function | //+------------------------------------------------------------------+ void OnTrade(void) { program.OnTradeEvent(); }
以上动作设置所显示持仓的相关性。 为了更新利润,则在 CFastTrading 类的公开部分创建 UpdatePositionProfit() 方法:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::UpdatePositionProfit(void) { //--- int cnt=0; for(int i=PositionsTotal()-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double swap=PositionGetDouble(POSITION_SWAP); double profit=PositionGetDouble(POSITION_PROFIT); profit+=swap; //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) { m_table_positions.SetValue(8,i,DoubleToString(profit,2)); //--- m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver); cnt++; } } //--- if(cnt>0) UpdatePositionsTable(); }
在 OnTick() 里调用它:
void OnTick() { program.UpdatePositionProfit(); }
表格的实现至此完毕。 现在,我们创建编辑和当前列表中存在持仓平仓的可能性。 我们应当做到点击表格行以后,输入字段将显示所选持仓的手数、止盈和止损。
//--- Event of clicking on a table row if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM) { //--- Check the element ID if(lparam==m_table_positions.Id()) { //--- int row=m_table_positions.SelectedItem(); m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row))); m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row))); m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row))); m_lot_edit[6].GetTextBoxPointer().Update(true); m_sl_edit[6].GetTextBoxPointer().Update(true); m_tp_edit[6].GetTextBoxPointer().Update(true); } }
编译项目,并得到如图例 10 所示的结果:在输入字段里显示的数据来自所选在场持仓。
图例10 选择一笔持仓进一步编辑。
然后创建两个方法 ModifyPosition() 和 ClosePosition()。 但凡单击 “Modify” 和 “Close” 按钮,该方法将对选定的持仓执行相应的操作。
//+------------------------------------------------------------------+ //| Modifying a selected open position | //+------------------------------------------------------------------+ bool CFastTrading::ModifyPosition(long lparam) { //--- Check the element ID if(lparam==m_small_button[0].Id()) { //--- Get index and symbol if(m_table_positions.SelectedItem()==WRONG_VALUE) return(false); int row=m_table_positions.SelectedItem(); ulong ticket=(ulong)m_table_positions.GetValue(0,row); //--- if(PositionSelectByTicket(ticket)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; //--- calculation and rounding of the Stop Loss and Take Profit values double sl=NormalizeDouble((double)m_sl_edit[6].GetValue(),_Digits); double tp=NormalizeDouble((double)m_tp_edit[6].GetValue(),_Digits); //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_SLTP; // trading operation type request.position=ticket; // position ticket request.symbol=Symbol(); // symbol request.sl =sl; // position Stop Loss request.tp =tp; // position Take Profit request.magic=m_magic_number; // position MagicNumber //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) return(true); else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } //--- return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::ClosePosition(long lparam) { //--- Check the element ID if(lparam==m_small_button[1].Id()) { //--- Get index and symbol if(m_table_positions.SelectedItem()==WRONG_VALUE) return(false); int row=m_table_positions.SelectedItem(); ulong ticket=(ulong)m_table_positions.GetValue(0,row); //--- if(PositionSelectByTicket(ticket)) { ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- Set order price and type depending on the position type if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else if(type==POSITION_TYPE_SELL) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- check volume double position_volume=PositionGetDouble(POSITION_VOLUME); double closing_volume=(double)m_lot_edit[6].GetValue(); if(closing_volume>position_volume) closing_volume=position_volume; //--- setting request request.action =TRADE_ACTION_DEAL; request.position =ticket; request.symbol =Symbol(); request.volume =NormalizeLot(Symbol(),closing_volume); request.magic =m_magic_number; request.deviation=5; //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) return(true); else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } //--- return(false); }
“在场持仓控制”选项卡中执行的任务至此完成。 我们继续开发挂单控制选项卡。 在 CreateMainWindow() 主窗口方法体的末尾,添加我们在上一个选项卡中实现的相同功能代码:挂订单、按钮、和编辑订单的输入字段的表格。
//--- Create a table of pending orders if(!CreateOrdersTable(m_table_orders,0,22+5)) return(false); //--- Input fields for editing pending orders if(!CreateLotControl(m_pr_edit[4],360,3,2)) return(false); if(!CreateStopLossControl(m_sl_edit[7],360+80,3,2)) return(false); if(!CreateTakeProfitControl(m_tp_edit[7],360+80*2,3,2)) return(false); //--- Pending order modifying/deleting orders if(!CreateModifyButton(m_small_button[2],MODIFY,361+80*3,3,2)) return(false); if(!CreateCloseButton(m_small_button[3],REMOVE,361+80*3,3+24,2)) return(false);
这有一个附加新方法,它为挂单添加一个表格,且为订单删除按钮添加了新的宏替换了。 我们仔细看看表格是如何创建的:
//+------------------------------------------------------------------+ //| Creates a table of pending orders | //+------------------------------------------------------------------+ //--- bool CFastTrading::CreateOrdersTable(CTable &table,const int x_gap,const int y_gap) { #define COLUMNS1_TOTAL 7 //--- Store the pointer to the main control table.MainPointer(m_tab); //--- Attach to tab m_tab.AddToElementsArray(2,table); //--- Array of column widths int width[COLUMNS1_TOTAL]; ::ArrayInitialize(width,80); width[0]=100; width[2]=100; //--- Array of text offset along the X axis in the columns int text_x_offset[COLUMNS1_TOTAL]; ::ArrayInitialize(text_x_offset,7); //--- Array of column image offsets along the X axis int image_x_offset[COLUMNS1_TOTAL]; ::ArrayInitialize(image_x_offset,3); //--- Array of column image offsets along the Y axis int image_y_offset[COLUMNS1_TOTAL]; ::ArrayInitialize(image_y_offset,2); //--- Array of text alignment in columns ENUM_ALIGN_MODE align[COLUMNS1_TOTAL]; ::ArrayInitialize(align,ALIGN_CENTER); align[6]=ALIGN_LEFT; //--- Properties table.Font(m_base_font); table.FontSize(m_base_font_size); table.XSize(602); table.CellYSize(24); table.TableSize(COLUMNS1_TOTAL,1); table.TextAlign(align); table.ColumnsWidth(width); table.TextXOffset(text_x_offset); table.ImageXOffset(image_x_offset); table.ImageYOffset(image_x_offset); table.ShowHeaders(true); table.HeadersColor(C'87,128,255'); table.HeadersColorHover(clrCornflowerBlue); table.HeadersTextColor(clrWhite); table.IsSortMode(false); table.LightsHover(true); table.SelectableRow(true); table.IsZebraFormatRows(clrWhiteSmoke); table.AutoYResizeMode(true); table.AutoYResizeBottomOffset(5); table.DataType(0,TYPE_LONG); table.DataType(1,TYPE_STRING); table.DataType(2,TYPE_STRING); table.DataType(3,TYPE_DOUBLE); table.DataType(4,TYPE_DOUBLE); table.DataType(5,TYPE_DOUBLE); //--- Create a control element if(!table.CreateTable(x_gap,y_gap)) return(false); //--- Set the header titles table.SetHeaderText(0,TICKET); table.SetHeaderText(1,SYMBOL); table.SetHeaderText(2,TYPE_POS); table.SetHeaderText(3,VOLUME); table.SetHeaderText(4,PRICE); table.SetHeaderText(5,SL); table.SetHeaderText(6,TP); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(0,table); return(true); }
该表含有不同数量的列,这些列按不同的顺序排列。 当编辑持仓时,我们可以修改手数、以及止盈和止损值。 至于挂单,我们可修改开盘价,来替代手数。 编译项目,并检查“项目控制”和“挂单控制”选项卡中的结果:
图例11 用于挂单的界面。
进一步的开发类似于我们在前一个选项卡上所做的工作。 首先,利用 InitializeOrdersTable() 查找属于工具箱的所有订单:
//+------------------------------------------------------------------+ //| Initializing the table of positions | //+------------------------------------------------------------------+ void CFastTrading::InitializeOrdersTable(void) { //--- int total=OrdersTotal(); int cnt=0; //--- Delete all rows m_table_orders.DeleteAllRows(); //--- Set the number of rows equal to the number of positions for(int i=0; i<total; i++) { //--- order parameters ulong order_ticket=OrderGetTicket(i); // order ticket string order_symbol=OrderGetString(ORDER_SYMBOL); // symbol ulong magic=OrderGetInteger(ORDER_MAGIC); // position MagicNumber //--- if MagicNumber matches if(magic==m_magic_number) if(order_symbol==Symbol()) { m_table_orders.AddRow(cnt); cnt++; } } //--- If there are positions if(cnt>0) { //--- Set the values in the table SetValuesToOrderTable(); //--- Update the table UpdateOrdersTable(); } }
如果找到挂单,利用 SetValuesToOrderTable() 方法将相关信息添加到表格中:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::SetValuesToOrderTable(void) { //--- int cnt=0; ulong ticket; for(int i=0; i<OrdersTotal(); i++) { //--- order parameters if((ticket=OrderGetTicket(i))>0) { string position_symbol=OrderGetString(ORDER_SYMBOL); // symbol ulong magic=OrderGetInteger(ORDER_MAGIC); // order MagicNumber double volume=OrderGetDouble(ORDER_VOLUME_INITIAL); // order volume ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); // order type double price=OrderGetDouble(ORDER_PRICE_OPEN); double stoploss=OrderGetDouble(ORDER_SL); double takeprofit=OrderGetDouble(ORDER_TP); string pos=""; if(type==ORDER_TYPE_BUY_LIMIT) pos="Buy Limit"; else if(type==ORDER_TYPE_SELL_LIMIT) pos="Sell Limit"; else if(type==ORDER_TYPE_BUY_STOP) pos="Buy Stop"; else if(type==ORDER_TYPE_SELL_STOP) pos="Sell Stop"; //--- if(magic==m_magic_number) if(position_symbol==Symbol()) { m_table_orders.SetValue(0,i,string(ticket)); m_table_orders.SetValue(1,i,string(position_symbol)); m_table_orders.SetValue(2,i,pos); m_table_orders.SetValue(3,i,string(volume)); m_table_orders.SetValue(4,i,string(price)); m_table_orders.SetValue(5,i,string(stoploss)); m_table_orders.SetValue(6,i,string(takeprofit)); cnt++; } } } }
利用 UpdateOrdersTable() 方法更新添加的数据:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::UpdateOrdersTable(void) { //--- Update the table m_table_orders.Update(true); m_table_orders.GetScrollVPointer().Update(true); }
为了将此功能连接到应用程序,执行与上一个选项卡中相同的操作。 即,初始化挂单表格:
//+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ void CFastTrading::OnInitEvent(void) { InitializeOrdersTable(); InitializePositionsTable(); }
在交易事件应答程序中重复动作:
//+------------------------------------------------------------------+ //| Trade event | //+------------------------------------------------------------------+ void CFastTrading::OnTradeEvent(void) { //--- InitializePositionsTable(); InitializeOrdersTable(); }
为了能够在单击表行时在输入字段中显示相关数据,在事件应答程序的相应部分添加以下代码:
//--- Event of clicking on a table row if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM) { //--- Check the element ID if(lparam==m_table_positions.Id()) { //--- int row=m_table_positions.SelectedItem(); m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row))); m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row))); m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row))); m_lot_edit[6].GetTextBoxPointer().Update(true); m_sl_edit[6].GetTextBoxPointer().Update(true); m_tp_edit[6].GetTextBoxPointer().Update(true); } //--- Check the element ID if(lparam==m_table_orders.Id()) { //--- int row=m_table_orders.SelectedItem(); m_pr_edit[4].SetValue(string(m_table_orders.GetValue(4,row))); m_sl_edit[7].SetValue(string(m_table_orders.GetValue(5,row))); m_tp_edit[7].SetValue(string(m_table_orders.GetValue(6,row))); m_pr_edit[4].GetTextBoxPointer().Update(true); m_sl_edit[7].GetTextBoxPointer().Update(true); m_tp_edit[7].GetTextBoxPointer().Update(true); } }
现在,我们将相关动作分配给按钮 Modify 和 Delete。 创建方法 ModifyOrder() 和 RemoveOrder()。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::ModifyOrder(long lparam) { //--- Check the element ID if(lparam==m_small_button[2].Id()) { //--- Get index and symbol if(m_table_orders.SelectedItem()==WRONG_VALUE) return(false); int row=m_table_orders.SelectedItem(); ulong ticket=(ulong)m_table_orders.GetValue(0,row); //--- if(OrderSelect(ticket)) { string position_symbol=OrderGetString(ORDER_SYMBOL); // symbol ulong magic=OrderGetInteger(ORDER_MAGIC); // order MagicNumber ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); // order type //--- calculation and rounding of the Stop Loss and Take Profit values double sl=NormalizeDouble((double)m_sl_edit[7].GetValue(),_Digits); double tp=NormalizeDouble((double)m_tp_edit[7].GetValue(),_Digits); double price=NormalizeDouble((double)m_pr_edit[4].GetValue(),_Digits); //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action=TRADE_ACTION_MODIFY; // trading operation type request.order = ticket; // order ticket request.symbol=Symbol(); // symbol request.sl =sl; // position Stop Loss request.tp =tp; // position Take Profit request.price=price; // new price request.magic=m_magic_number; // position MagicNumber //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) return(true); else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } //--- return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::RemoveOrder(long lparam) { //--- Check the element ID if(lparam==m_small_button[3].Id()) { //--- Get index and symbol if(m_table_orders.SelectedItem()==WRONG_VALUE) return(false); int row=m_table_orders.SelectedItem(); ulong ticket=(ulong)m_table_orders.GetValue(0,row); //--- if(OrderSelect(ticket)) { string position_symbol=OrderGetString(ORDER_SYMBOL); // symbol ulong magic=OrderGetInteger(ORDER_MAGIC); // order MagicNumber ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); // order type //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action=TRADE_ACTION_REMOVE; // trading operation type request.order = ticket; // order ticket //--- sending a request bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) return(true); else PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } //--- return(false); }
在事件应答程序中调用它们,在任何部分之外:
//--- if(ModifyOrder(lparam)) return; if(RemoveOrder(lparam)) return;
我想添加另一个便捷的功能来操控挂单。 它提供的的功能,可单击图表来设置先前所选挂单的开单价。 如图例 12 所示:
图例12 设置挂单开单价。
其操作如下:
- 单击输入字段可激活数值编辑。
- 然后将鼠标指针移动到图表上的任何位置 — 相应的数值将沿轴显示。 出于方便起见,您可以单击鼠标滚轮,并用十字准线选择所需的价格。
- 将鼠标指针停在所需的价格上,然后单击图表。 此价格值将在输入字段中设置。
使用此方法,您可以快速、方便地设置挂单价格,尤其在直观分析时更容易执行。 如果您需要更精确地设置价格,用键盘在输入字段中输入。 实现非常简单。 在基类应答程序中,创建一个包含单击图表事件的部分,并添加以下代码:
//--- The event of clicking on the chart if(id==CHARTEVENT_CLICK) { for(int i=0; i<4; i++) { if(m_pr_edit[i].GetTextBoxPointer().TextEditState()) { m_last_index=i; break; } else { if(m_last_index>=0) { //--- datetime dt =0; int window=0; //--- convert X and Y coordinates to date/time if(ChartXYToTimePrice(0,(int)lparam,(int)dparam,window,dt,m_xy_price)) { m_pr_edit[m_last_index].SetValue(DoubleToString(m_xy_price)); m_pr_edit[m_last_index].GetTextBoxPointer().Update(true); m_last_index=-1; } } } } }
在此判断哪个字段正在编辑,记住它,通过单击图表接收该数值,并将此数值插入输入字段。 下面的视频显示了主要功能和创新之处。
结束语
附件包含所有列出的文件,这些文件应置于相应的文件夹之中。 为了令其正确运行,您仅需要将 MQL5 文件夹保存到终端文件夹当中。 若要打开 MQL5 文件夹所在的终端根目录,请在 MetaTrader 5 终端中按 Ctrl+Shift+D 组合键,或使用关联菜单,如下图例 13 所示。
图例13 在 MetaTrader 5 终端根目录中打开 MQL5 文件夹
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/7981
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.




这篇文章很有意思,可以提供信息,还可以进一步发展
我也想为 MT4 编写一篇文章
当然可以定制。在市场上进行手动交易时,可以有多种不同的操作。
而对于 MT4,任何人都可以为我订购一个,例如在自由职业者中。
文章很好,感谢发布。
需要提醒的是,编译时会出现以下信息:
"已过时的行为,隐藏方法调用将在未来的 MQL 编译器版本中禁用 ListView.mqh"
有什么建议吗?
谢谢。
文章很好,感谢发表。
作为信息,编译时会出现以下信息:
"已过时的行为,在未来的 MQL 编译器版本中将禁用隐藏方法调用" ListView.mqh
有什么建议吗?
谢谢。