我们之前曾创建了基本功能,旨在帮助偏爱手动交易的交易员。 我们重点关注与下单相关的便捷操作,因此大部分函数都与市价入场有关。 然而,任何交易策略,无论是手动还是自动,在以市价操作时,都应该有三个主要阶段。 其中包括市场准入规则、持仓管理和平仓条件。 至于目前,该工具箱仅覆盖第一阶段。 因此,随着深入发展,我们可以增加更多的机会来操控持仓或挂单,并扩展平仓条件。 所有的计算都应该由工具箱来完成,而决策则应由交易者制定。
我们从判断实现新功能所需的任务范围开始。 我们依据要继续开发的当前应用程序,来判断主要阶段:
我们仔细研究每一个单独的阶段。
我们先来看看已经实现了什么。 下面的图例 1 展示了主要的模块,它们分成两个类别执行任务:开仓/创建,和平仓/删除。 第三个类别是手工管理和修改。
图例 1 工具箱的主要模块
如此,我们来创建三个选项卡。 第一个将用于图例 1 所示的函数。 在其他两个选项卡中,我们将实现处理持仓和挂单的功能。
图例 2 新的应用程序结构
以前位于主窗口中的所有功能都将在 “Trading” 选项卡中实现。 开仓控制将包括一个由此工具开仓的表格。 在该处也将执行与这些持仓相关的命令。 挂单控制功能包括由工具箱创建的订单,以及平仓/修改持仓的控件。 我们更加详细地研究一下这些选卡。
图例 3 在场持仓控制选项卡
图例 3 示意在场持仓控制选项卡。 该选项卡包含以下元素:
现在,查看图例 4 中的 Pending Order Control 选项卡。
图例 4 挂单控制。
它与前一个非常相似:
我们回到 "Trading" 选卡,并修改它从而获得新功能。 首先,我们修改现有的平仓工具。 不光可以多头平仓,还可以空头平仓。 这是通过平仓模式开关来实现的。
图例 5 扩展在场持仓平仓功能。
正如您在图例 5 所见,我们有 4 个新按钮:Close BUY profit, Close SELL profit, Close BUY loss, Close SELL loss。 在按钮的右边还有另外的开关;我们来更详细地研究一下。 开关相似,因此以下描述适用于所有这些开关。
我们来研究一个如图例 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 } } } } } }
此外,针对模式执行计算,按点数或存款货币检查所有持仓的总亏损,如果达到相应的亏损条件,那么由工具箱创建的所有在场持仓全都被平仓。
现在,我们继续采取新的行动:买单平仓/卖单平仓,即可盈利、亦或亏损。 事实上,这是上述两种方法的特例。 因此,我们将添加按持仓类型过滤。 首先,创建执行指定操作的方法:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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 文件夹
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程