English Русский Deutsch 日本語
preview
创建动态多货币对EA(第二部分):投资组合多元化与优化

创建动态多货币对EA(第二部分):投资组合多元化与优化

MetaTrader 5测试者 |
539 8
Hlomohang John Borotho
Hlomohang John Borotho

概述

在专业交易领域,维持投资组合的稳定性和健全的风险管理协议是最具持续性的挑战之一。交易者常常过度依赖单一资产或策略,这会在市场格局突然转变时增加面临大幅回撤的风险。加剧这种风险的是,人们普遍倾向于过度使用相关工具的杠杆,这增加了同时出现亏损的可能性,并破坏了回报的稳定性。如果缺乏严格多元化和优化的投资组合,交易者将面临业绩结果不稳定的情况,这往往会引发情绪驱动的决策和利润的波动。因此,对于可持续的长期业绩而言,一个能够在一系列不相关资产中战略性地平衡风险调整后回报的系统性框架是必不可少的。

为应对这些挑战,一种整合了投资组合优化、多资产多元化以及由振荡器确认的突破交易策略的量化方法提供了一个可靠的解决方案。通过在多个货币对上部署突破策略,交易者可以抓住由高概率动量驱动的价格走势带来的机会,同时通过接触不相关市场来分散风险。振荡器的整合有助于验证入场信号,减少参与虚假突破的情况,并减少无成效交易。这种方法不仅提高了盈利潜力,还通过系统地利用不同市场阶段的机会来增强投资组合的稳定性。由此产生的策略对波动性具有更强的韧性,确保业绩与不断变化的宏观经济和技术条件保持一致。



EA逻辑

买入模型:

智能交易系统(EA)首先计算在UTC+2时间10:00至12:00之间的价格区间,确定该时间段内的最高价和最低价。在12:00或之后的任何时间,如果价格突破之前确定的最高价,则触发潜在的买入机会。然而,为了确认走势,随机振荡器(Stochastic Oscillator)必须处于或低于20水平,这表明市场处于超卖状态。这样确保了突破不会过度延伸,并且有向上动量的空间。一旦满足这两个条件,EA就会执行买入交易,旨在利用突破获利,同时通过基于振荡器的过滤来最小化虚假信号。

卖出模型:

对于卖出模型,EA遵循相同的区间计算流程,计算UTC+2时间上午10:00至12:00的价格区间,确定该时段的高点和低点。如果在12:00或之后,价格跌破既定低点,则视为出现卖出机会。然而,只有当随机振荡器处于或高于80水平,表明市场处于超买状态时,才会执行交易。这样确保了向下突破有潜在的抛售压力支撑,而非虚假走势。一旦这两个条件同时满足,EA就会执行卖出交易,借熊市契机获利,同时避免过早入场。

开始使用:

//+------------------------------------------------------------------+
//|                                                      Dyna MP.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
CTrade trade;

enum Signal_Breakout{
   Normal_Signal,
   Reversed_Signal,
};

#include <Trade/Trade.mqh>这行代码引入MQL5交易库。Trade.mqh头文件定义了一些类和函数,这些类和函数可简化交易任务,例如开仓、修改或平仓。在此,从CTrade类实例化一个名为trade的对象。该类包含以结构化方式执行交易操作(例如提交买入/卖出请求、修改未平仓交易或平仓)的方法。

input group "--------------General Inputs--------------"
input string Symbols = "XAUUSD, GBPUSD, USDCAD, USDJPY";
input Signal_Breakout BreakOutMode = Reversed_Signal;
input double  In_Lot = 0.01;
input int TakeProfit = 500;
 double StopLoss = 500;
input bool TrailYourStop = false;
input int TrailingStop = 50;

// Stochastic
input int KPeriod = 21;
input int upprer_level = 80;

Symbols 指定了待交易的资产(例如“XAUUSD(黄金兑美元)、GBPUSD(英镑兑美元)”),In_Lot设定固定交易规模(0.01手),TakeProfit(500点)和StopLoss(1000点)分别定义了止盈和风险限制,而TrailYourStop与Trailing Stop(70点)则用于激活并调整动态追踪止损。对于随机振荡器,KPeriod(20)决定了百分比线的计算周期,upper_level(80)则标记了超买阈值。这些输入参数共同平衡了交易执行规则、风险管理和技术信号生成。

//+------------------------------------------------------------------+
//|                           Global vars                            |
//+------------------------------------------------------------------+
int handles[];
double bufferM[];


int RangeStart = 600; 
int RangeDuration = 120;
int RangeClose = 1200;

int Num_symbs = 0;
string symb_List[];
string Formatted_Symbs[];

这部分对全局变量进行定义。handles数组用于存储随机振荡器的句柄,这使得EA能够高效地管理多种资产。buffer数组用于存储计算得出的值,例如指标读数或历史价格水平。RangeStart变量设置为600分钟(UTC+2时间上午10:00),标志着区间测量的起始时间,而RangeDuration设置为120分钟,定义了一个用于捕捉最高价和最低价的2小时时段。一旦达到1200分钟(UTC+2时间中午12:00)的RangeClose时间,EA将停止计算区间,并开始监测突破条件。

在交易品种管理方面,symb_List数组保存待处理的原始交易品种列表。此外,Formatted_Symbs数组保存解析Symbols输入后要使用的交易品种总数。这些变量共同使EA能够在多种资产上动态执行交易,同时确保突破检测的区间计算精确无误。

//+------------------------------------------------------------------+
//|                    Ranger Global Vars                            |
//+------------------------------------------------------------------+
struct RANGER{
   datetime start_time;
   datetime end_time;
   datetime close_time;
   double high;
   double low;
   bool b_entry;
   bool b_high_breakout;
   bool b_low_breakout;
   
   RANGER() : start_time(0), end_time(0), close_time(0), high(0), low(999999), b_entry(false), b_high_breakout(false), b_low_breakout(false) {};
};

Ranger结构体用于存储和管理区间突破策略的关键数据点。它定义了一种结构化的方式,用于跟踪区间的时间边界、价格水平以及突破条件。变量start_time、end_time和close_time分别表示区间计算的开始时间、结束时间和收盘时间,确保EA能正确地识别突破窗口。变量high和low分别存储区间内记录的最高价和最低价,其中low初始化为一个极高的值(999999),以确保价格更新准确无误。low (999999)是在价格序列中查找最小值的一种标准模式。

此外,我们还设置了三个布尔标识。b_entry用于跟踪是否已执行交易入场操作,防止在同一突破事件中多次下单。b_high_breakout用于指示价格是否突破了区间高点,确认潜在的买入信号,而b_low_breakout则用于指示价格是否跌破了区间低点,确认卖出信号。构造函数Ranger()将所有值初始化为默认状态,确保该结构体在实时交易中动态更新前以干净的数据状态启动。

RANGER rangeArray[];
MqlTick prevTick[], currTick[];

这些变量在跟踪价格走势以及管理EA中的区间突破逻辑方面发挥着至关重要的作用。rangeArray是一个由RANGER结构体组成的数组,它存储了多个已定义的区间突破参数实例。这样使得EA能够同时跟踪多个交易品种,确保每个交易品种都有其专属的区间数据,包括开始和结束时间、最高价和最低价以及突破条件。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   string separator = ",";
   ushort usprtr;
   usprtr = StringGetCharacter(separator, 0);
   StringSplit(Symbols, usprtr, symb_List);
   Num_symbs = ArraySize(symb_List);
   ArrayResize(Formatted_Symbs, Num_symbs);
   
   for(int i = 0; i < Num_symbs; i++){
      Formatted_Symbs[i] = symb_List[i];
   }
   
   ArrayResize(rangeArray, Num_symbs);
   ArrayResize(prevTick, Num_symbs);
   ArrayResize(currTick, Num_symbs);
   ArrayResize(handles, Num_symbs);   
   ArraySetAsSeries(bufferM, true);
   
   // Calculate initial ranges for each symbol
   for (int i = 0; i < ArraySize(Formatted_Symbs); i++) {
      CalculateRange(i, Formatted_Symbs[i]);  // Pass the symbol index
      handles[i] = iStochastic(Formatted_Symbs[i], PERIOD_CURRENT, KPeriod, 1, 3, MODE_SMA, STO_LOWHIGH);
      
      if(handles[i] == INVALID_HANDLE){
         Alert("Failed to create indicator handle");
         return INIT_FAILED;
      }
      
      StopLoss = SymbolInfoDouble(Formatted_Symbs[i], SYMBOL_POINT)*TrailingStop;
   }
   return(INIT_SUCCEEDED);
}

OnInit函数负责初始化并设置关键变量、处理交易品种,以及准备所需的数据结构。我们仍使用分隔符“,”将交易品种列表拆分为单个可交易资产,并存储在symb_List中。我们确定交易品种总数Num_symbs,然后相应调整Formatted_Symbs数组的大小,确保每个交易品种格式正确且可供交易使用。

接下来,EA动态调整多个数组的大小,包括用于存储区间突破数据的rangeArray、用于跟踪价格更新的prevTick和currTick、用于随机振荡器的handles,以及用于存储指标值的bufferM,确保在EA开始执行前,所有所需的数据结构均已正确分配。

数组设置完成后,EA会遍历Formatted_Symbs中的每个交易品种,使用CalculateRange()函数计算初始区间。还会为每个交易品种创建一个随机振荡器句柄(istochastic),后续将用于通过检查超买和超卖条件来确认突破。如果指标句柄初始化失败(返回INVALID_HANDLE),EA将触发警报,并以INIT_FAILED状态退出。此外,我们根据每个交易品种的点值和追踪止损参数计算其止损值。如果所有内容均成功初始化,该函数将返回INIT_SUCCEEDED,允许EA继续监控价格走势并执行交易。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   for(int i = 0; i < ArraySize(Formatted_Symbs); i++){
      if(handles[i] != INVALID_HANDLE){
         IndicatorRelease(handles[i]);
      }   
   }
}

OnDeinit()函数是EA的反初始化函数,负责在EA从图表中移除或停止运行时正确释放资源。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
 
   for(int i = 0; i < ArraySize(Formatted_Symbs); i++){
      string symbol = Formatted_Symbs[i];
      prevTick[i] = currTick[i];
      SymbolInfoTick(symbol, currTick[i]);
      
      // Range Cal
      if(currTick[i].time > rangeArray[i].start_time && currTick[i].time < rangeArray[i].end_time){
         // flag
         rangeArray[i].b_entry = true;
         
         // high
         if(currTick[i].ask > rangeArray[i].high){
            rangeArray[i].high = currTick[i].ask;
         }
         
         // low
         if(currTick[i].bid < rangeArray[i].low){
            rangeArray[i].low = currTick[i].bid;
         }
      }
            
      // now calculate range
      if(((RangeClose >= 0 && currTick[i].time >= rangeArray[i].close_time)
         || (rangeArray[i].b_high_breakout && rangeArray[i].b_low_breakout)
         || (rangeArray[i].end_time == 0)
         || (rangeArray[i].end_time != 0 && currTick[i].time > rangeArray[i].end_time && !rangeArray[i].b_entry))){
         CalculateRange(i, Formatted_Symbs[i]);
      }
      checkBreak(i, Formatted_Symbs[i]);
   }
   
}

众所周知,OnTick函数是EA的核心执行循环,在每个新的报价更新时运行。其负责实时价格分析、调整区间参数,并评估所有已配置交易工具的突破机会。该流程首先按顺序评估Formatted_Symbs列表中的每个交易品种。对于每个交易品种,它将当前报价数据存档到历史缓冲区(prevTick),并通过SymbolInfoTick获取最新的市场状态。

在预定义的活跃交易时段(start_timeend_time)内,系统会动态跟踪价格极值。它使用最高ask(卖出价)值提升区间高点,使用最低bid(买入价)值降低区间低点。如果当前时间处于该时段内,则激活b_entry标识,将该区间标记为有效交易区间。

交易时段结束后,逻辑会评估三个更新触发条件:

  1. 计划时段结束(close_time)
  2. 同时出现高点和低点突破
  3. 无效或过期的end_time时间戳
如果满足任一条件,CalculateRange()将重新生成新的区间参数。循环最后调用checkBreak(),该函数会扫描超出区间边界的价格突破情况,并据此发起交易。这一框架实现了对市场的持续监控,以及与突破动态相匹配的战略性订单布局,确保对实时价格走势做出迅速响应。
//+------------------------------------------------------------------+
//|                  Range Calculation function                      |
//+------------------------------------------------------------------+
void CalculateRange(int index, string symbol) {
   for(index = 0; index < ArraySize(Formatted_Symbs); index++){
      symbol = Formatted_Symbs[index];
      
      // Reset all the range variables
      rangeArray[index].start_time = 0;
      rangeArray[index].end_time = 0;
      rangeArray[index].close_time = 0;
      rangeArray[index].high = 0.0;
      rangeArray[index].low = 999999;
      rangeArray[index].b_entry = false;
      rangeArray[index].b_high_breakout = false;
      rangeArray[index].b_low_breakout = false;
      
      // Calculate range start time
      int time_cycle = 86400;
      rangeArray[index].start_time = (currTick[index].time - (currTick[index].time % time_cycle)) + RangeStart * 60;
      for(int i = 0; i < 8; i++){
         MqlDateTime tmp;
         TimeToStruct(rangeArray[index].start_time, tmp);
         int dotw = tmp.day_of_week;
         if(currTick[index].time >= rangeArray[index].start_time || dotw == 6 || dotw == 0){
            rangeArray[index].start_time += time_cycle;
         }
      }
   
      // Calculate range end time
      rangeArray[index].end_time = rangeArray[index].start_time + RangeDuration * 60;
      for(int i = 0 ; i < 2; i++){
         MqlDateTime tmp;
         TimeToStruct(rangeArray[index].end_time, tmp);
         int dotw = tmp.day_of_week;
         if(dotw == 6 || dotw == 0){
            rangeArray[index].end_time += time_cycle;
         }
      }
      
      // Calculate range close
      rangeArray[index].close_time = (rangeArray[index].end_time - (rangeArray[index].end_time % time_cycle)) + RangeClose * 60;
      for(int i = 0; i < 3; i++){
         MqlDateTime tmp;
         TimeToStruct(rangeArray[index].close_time, tmp);
         int dotw = tmp.day_of_week;
         if(rangeArray[index].close_time <= rangeArray[index].end_time || dotw == 6 || dotw == 0){
            rangeArray[index].close_time += time_cycle;
         }
      } 
      
   }
}

CalculateRange函数负责为多个交易品种初始化并配置基于时间的交易区间。对于Formatted_Symbs数组中的每个交易品种,该函数首先将关键的区间参数——起始/结束/收盘时间、最高/最低价格阈值以及突破标志——重置为默认值。start_time通过将当前报价时间对齐到每日边界(使用24小时制的time_cycle),然后根据用户定义的RangeStart(以分钟为单位)进行偏移计算得出。通过一个循环确保起始时间避开周末(周六/周日),并在出现冲突时通过按整天递增时间戳来保证时间顺序的有效性。这就为交易窗口创建了一个基准,同时尊重市场休市时段。

设置好区间起始时间后,该函数通过将RangeDuration添加到start_time上,计算出区间结束时间。与起始时间计算类似,它通过一个验证循环确保结束时间不会放在周末。最后,该函数确定区间收盘时间,即EA停止监测突破的时间点。该时间由RangeClose推导得出,并经过调整以避开周末。通过维持这些动态计算,该函数确保EA能够为不同交易品种准确设置区间交易条件,避免在非交易时段和周末进行交易,同时确保突破检测的精确性。

bool CLots(double sl, double &lots){
   lots = In_Lot;

   if(!CHLots(lots)){return false;}
   return true;
}

CLots()函数在执行交易前设定并验证交易手数,该函数接收两个参数:止损价位(sl)和手数(一个用于存储最终交易手数的引用变量)。

bool CHLots(double &lots){
   for(int i = 0; i < ArraySize(Formatted_Symbs); i++){
      string symbol = Formatted_Symbs[i];
      double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
      double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
      double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
      if(lots < min){
         lots = min;
         return true;
      }
      if(lots > max){
         return false;
      }
      
      lots = (int)MathFloor(lots / step)  * step;
      
   }
   return true;

}

CHLots()函数用于验证交易手数,确保其符合各交易品种经纪商特定的交易规则。该函数会遍历Formatted_Symbs列表中的所有交易品种,提取经纪商规定的限制条件——最小交易手数、最大交易手数以及允许的递增步长。这些参数为订单数量设定了操作范围。当拟定的交易手数低于最小阈值时,函数会自动将其修正为最小允许交易量,并通过返回true确认其有效性。相反,如果请求的交易手数超过经纪商设定的最大限额,函数将通过返回false中止该请求,从而阻止不符合规定的交易。

为确保精确性,函数通过公式MathFloor(lots/step) * step对手数进行向下取整,强制其与步长对齐。这样消除了可能导致经纪商拒绝的分数或不规则递增。如果交易手数无需调整即符合所有限制条件,函数将返回true,确认其可接受性。通过严格实施这些检查,CHLots()函数充当了关键的安全屏障,防止因交易量违规导致的订单被拒,增强了EA在实盘交易环境中的运行可靠性。

//+------------------------------------------------------------------+
//|                      Check for Breakout                          |
//+------------------------------------------------------------------+
void checkBreak(int i, string symbol) {
   for (i = 0; i < ArraySize(Formatted_Symbs); i++) {
      symbol = Formatted_Symbs[i];
      
      //get indicator vals
      if(CopyBuffer(handles[i], 0, 1, 2, bufferM) != 2){
         Print("Failed to get indicator values");
         return;
      }

      int stopLevel = (int)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL);
      int spread = (int)SymbolInfoInteger(symbol, SYMBOL_SPREAD);
      double Bid = SymbolInfoDouble(symbol, SYMBOL_BID);
      double Ask = SymbolInfoDouble(symbol, SYMBOL_ASK);

      if (currTick[i].time >= rangeArray[i].end_time && rangeArray[i].end_time > 0 && rangeArray[i].b_entry) {
         double rangeSize = rangeArray[i].high - rangeArray[i].low;

         // High Breakout (BUY/SELL)
         bool upperBreak = bufferM[0] >= upprer_level && bufferM[1] < upprer_level;
         bool lowerBreak = bufferM[0] <= (100 - upprer_level) && bufferM[1] > (100 - upprer_level);
         bool HighSigType,LowSigType;
         
         if(BreakOutMode == Normal_Signal){
            HighSigType = upperBreak;
         }else{HighSigType = lowerBreak;}
         if (!rangeArray[i].b_high_breakout && currTick[i].ask >= rangeArray[i].high && HighSigType) {
            rangeArray[i].b_high_breakout = true;

            double entry = NormalizeDouble(Ask + 100 * _Point, _Digits);
            double sl = rangeArray[i].low;
            //sl = NormalizeDouble(sl, true);
            double tp = entry + TakeProfit * _Point;

            double lots;
            if (!CLots(entry - sl, lots)) continue;

            if (!trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, currTick[i].ask, sl, tp, "High Breakout"))
               Print("Buy Order Failed: ", GetLastError());
         }                                                                      
                                                                                
         if(BreakOutMode == Normal_Signal){
            LowSigType = upperBreak;
         }else{LowSigType = lowerBreak; }                                                                       
         // Low Breakout (SELL)
         if (!rangeArray[i].b_low_breakout && currTick[i].bid <= rangeArray[i].low  && LowSigType) {
            rangeArray[i].b_low_breakout = true;

            double entry = NormalizeDouble(Bid - 100 * _Point, _Digits);
            double sl = rangeArray[i].high;
            //sl = NormalizeDouble(sl,true);
            double tp = entry - TakeProfit * _Point;

            double lots;
            if (!CLots(sl - entry, lots)) continue;

            if (!trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, currTick[i].bid, sl, tp, "Low Breakout"))
               Print("Sell Order Failed: ", GetLastError());
         }
      }
   }
}

该函数通过结合价格走势与随机振荡器信号,监控价格突破情况并管理交易执行。它会遍历 Formatted_Symbs列表中的每个交易品种,首先尝试通过CopyBuffer().获取振荡器的数值。如果数据获取失败,函数将记录错误并终止运行,以避免基于错误数据做出决策。对于每个交易品种,函数会收集关键参数,如止损水平、点差以及当前的买入价/卖出价。突破评估仅在区间时段结束后进行,通过检查当前时间是否超过区间的end_timeb_entry标识处于激活状态,确保分析仅限于有效的交易窗口。

对于高位突破(买入信号),函数需确认两个条件:卖出价超过区间时段的高点,且振荡器显示超卖状态(低于100 - upper_level)。验证通过后,函数将触发买入订单——计算入场价格、止损(基于区间波动性)和止盈水平——随后通过CLots()验证交易手数是否符合要求。

相反,对于低位突破(卖出信号),则要求买入价跌破区间时段的低点,同时振荡器显示超买状态(高于upper_level)。如果条件满足,将生成卖出订单并设置类似的风险参数。两种场景均包含对失败订单的错误日志记录,以确保透明度。通过将价格阈值与基于振荡器的确认信号同步,该函数实现了严格、基于标准的交易执行。

//+------------------------------------------------------------------+
//|                      Trailing Stoploss                           |
//+------------------------------------------------------------------+
void Trailler(){
   if(!TrailYourStop) return;
   
   for(int i = PositionsTotal()-1; i >= 0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket <= 0) continue;
      
      if(!PositionSelectByTicket(ticket)) continue;
      
      // Get position details
      string symbol = PositionGetString(POSITION_SYMBOL);
      long magic;
      if(!PositionGetInteger(POSITION_MAGIC, magic)) continue;
      if(magic != MagicNumber) continue;

      // Get current prices
      MqlTick latestTick;
      if(!SymbolInfoTick(symbol, latestTick)) continue;
      
      long type;
      double openPrice, currentSl, currentTp;
      PositionGetInteger(POSITION_TYPE, type);
      PositionGetDouble(POSITION_PRICE_OPEN, openPrice);
      PositionGetDouble(POSITION_SL, currentSl);
      PositionGetDouble(POSITION_TP, currentTp);
      
      // Calculate pip values
      double pipSize = 10 * SymbolInfoDouble(symbol, SYMBOL_POINT);
      double currentPrice = type == POSITION_TYPE_BUY ? latestTick.bid : latestTick.ask;
      double priceMove = MathAbs(currentPrice - openPrice);
      
      // Calculate required moves
      double requiredMove = 70 * pipSize; // 20 pips
      double trailAmount = 10 * pipSize;  // 10 pips
      
      // Calculate new stop loss
      double newSl = currentSl;
      bool inProfit = type == POSITION_TYPE_BUY ? 
                     (currentPrice > openPrice) : 
                     (currentPrice < openPrice);
      
      if(inProfit && priceMove >= requiredMove){
         int steps = int(priceMove / requiredMove);
         if(type == POSITION_TYPE_BUY){
             newSl = openPrice + (steps * trailAmount);
             newSl = MathMax(newSl, currentSl + trailAmount);
         }
         else{
             newSl = openPrice - (steps * trailAmount);
             newSl = MathMin(newSl, currentSl - trailAmount);
         }
      }
      
      // Validate and modify SL
      if(newSl != currentSl){
         // Check stop levels
         double minDist = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
         newSl = NormalizeDouble(newSl, _Digits);
         
         if(type == POSITION_TYPE_BUY && (currentPrice - newSl) >= minDist){
             if(!trade.PositionModify(ticket, newSl, currentTp))
                 Print("Buy Trailing Failed: ", GetLastError());
         }
         else if(type == POSITION_TYPE_SELL && (newSl - currentPrice) >= minDist){
             if(!trade.PositionModify(ticket, newSl, currentTp))
                 Print("Sell Trailing Failed: ", GetLastError());
         }
      }
   }
}

交易中的“追踪止损”(TSL)功能可确保当交易朝有利方向移动时,止损点自动调整,以锁定利润,同时将风险降至最低。Trailer()函数会根据价格走势以及高低价区间的百分比重新计算并更新止损水平,防止过早退出交易,同时确保已获得的收益。



结论

总之,投资组合优化与分散化是交易中至关重要的策略,旨在通过将投资分散至多种资产,在最大化收益的同时最小化风险。传统交易方法往往侧重于单一交易品种策略,使交易者面临更高的波动性和特定市场风险。分散化增强了投资组合的抗风险能力,而优化方法则通过分析历史表现模式、资产波动性指标以及市场间关系,战略性地配置资本,以实现高效的风险收益平衡。

综上所述,将投资组合优化与分散化纳入交易策略,为应对市场波动提供了更具韧性和适应性的方法。通过将突破交易策略与振荡器指标相结合,交易者可以识别高概率交易机会,同时动态管理风险。这种方法不仅提高了持续盈利的可能性,还通过降低回撤幅度增强了长期可持续性。

为获得以下测试结果,本EA以EURUSD(欧元兑美元)为基准交易品种,在零延迟理想执行条件每tick建模的环境下进行测试,以确保最大准确性。测试期间为2022年2月1日至2022年3月22日。在输入设置中,BreakoutMode(突破模式)设置为Reversed_signal(反向信号),并启用了TrailYourStop(追踪止损)(设置为true),以允许动态调整止损点。所有其他输入参数均保持默认值。

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16089

附加的文件 |
Dyna_MP.mq5 (37.35 KB)
最近评论 | 前往讨论 (8)
Alberto Tortella
Alberto Tortella | 19 4月 2025 在 13:48

好的,我在输入/符号中写入了EURUSD,现在可以正常工作了。

谢谢

CapeCoddah
CapeCoddah | 19 4月 2025 在 20:10

好文章!我明天就试试。 我很想知道为什么你的策略测试 使用了如此奇怪的时间段。 我本来以为是 2024 年的整月。 我喜欢你的追踪止损概念,我也在使用同样的技术。我做的一个小改动是,如果交易在几乎达到盈亏平衡后转为负值,我也会尝试将损失降到最低。


干杯,继续写文章,它们很棒

科达角

Brian Pereira
Brian Pereira | 23 4月 2025 在 08:07
Alberto Tortella 随机振荡器 在图形上运行良好。


您能帮我吗?谢谢

每种输入货币之间只能用逗号隔开。货币之间不要有空格

CapeCoddah
CapeCoddah | 30 4月 2025 在 11:33

又见面了、


我试着在活动图表上使用您的系统,发现了一些改进之处


Alberto 的问题可能是他的市场观察窗口(ctlM)的符号列表中没有包含所有货币对。 我在使用 XAUUSD 时也遇到了这个错误

对于 For 语句,不要使用 ArraySize(...),而应使用 Num_symbls,这样会更快一些。 另外,我发现拼出全名有助于他人更好地理解您的代码,还能避免很多语法错误,例如,我认为 Number_Symbols 比 Num_symbls 更好。

代码中没有显示对象,是我添加的。

在显示对象中,我添加了一个条件,只选择图表符号。 通过其他符号进行枚举不是必需的,而且会扰乱屏幕。 但也许我遗漏了什么。

最后是范围计算的问题。 在活动图表上,而不是在策略测试器中,启动 EA 会产生一条超出当前日期的未来射线。 例如,从 4/30 日开始,会产生一条从 4/30 日上午 10 点开始到 5/1 日结束的射线。 这将导致一条不可见的射线,它不会显示在图表上,但会显示在对象列表中。 我会让你解决这个问题。

我附上我的代码供您使用


谢谢,科达角

CapeCoddah
CapeCoddah | 30 4月 2025 在 13:02
我觉得有些东西被弄乱了,因为第 1 部分和第 2 部分的 eas 完全相同。 看起来第 1 部分和第 2 部分完全相同
解密开盘区间突破(ORB)日内交易策略 解密开盘区间突破(ORB)日内交易策略
开盘区间突破(ORB)策略基于这样一种理念:市场开盘后不久确立的初始交易区间,反映了买卖双方就价格价值达成共识的重要水平。通过识别突破某一特定区间上方或下方的走势,交易者可以把握随之而来的市场契机——当市场方向愈发明朗时,这种契机往往会进一步显现。本文将探讨三种源自康克瑞图姆集团(Concretum Group)改良的ORB策略。
从基础到中级:模板和类型名称 (五) 从基础到中级:模板和类型名称 (五)
在本文中,我们将探讨模板的最后一个简单用例,并讨论在代码中使用 typename 的好处和必要性。虽然这篇文章乍一看可能有点复杂,但为了以后使用模板和 typename,正确理解它很重要。
交易中的神经网络:具有层化记忆的智代 交易中的神经网络:具有层化记忆的智代
模仿人类认知过程的层化记忆方式令复杂金融数据的处理、以及适配新信号成为可能,因此在动态市场中提升投资决策的有效性。
使用Python和MQL5进行特征工程(第四部分):基于UMAP回归的K线模式识别 使用Python和MQL5进行特征工程(第四部分):基于UMAP回归的K线模式识别
降维技术被广泛用于提升机器学习模型的性能。让我们来讨论一项被称为“统一流形逼近与投影”的相对较新的技术(UMAP)。这项新技术的开发旨在针对性地克服传统方法在数据中产生伪影和失真的局限性。UMAP是一种强大的降维技术,它能以一种新颖而有效的方式帮助我们将相似的K线进行分组,从而降低在样本外数据上的错误率,并提升我们的交易表现。