English Русский Español Deutsch 日本語 Português
preview
利用深度强化学习优化Ilan智能交易系统

利用深度强化学习优化Ilan智能交易系统

MetaTrader 5测试者 |
39 3
Yevgeniy Koshtenko
Yevgeniy Koshtenko

概述

在算法交易领域,有些策略宛如金融星空中不灭的恒星,在交易史上留下了深刻的印记。其中,Ilan便是2010年代曾风靡一时的传奇网格智能交易系统(EA)。它以看似简单的逻辑和低波动期潜在的有效性,吸引了无数交易者的注意。

然而,时间从未停滞。在量子计算、神经网络与机器学习主导的新时代,传统策略亟需根本性革新。如果将Ilan网格加仓的经典机制与前沿人工智能(AI)算法相结合,会擦出怎样的火花?假如摒弃硬编码规则,让系统自主适应并持续优化,能否突破瓶颈?

本文旨在颠覆对传统交易模型的认知,我们大胆尝试将深度强化学习与动态Q-table技术融入经典llan,使其具备“自我进化”能力。我们并非简单修改既有代码,而是构建一个能从自身经验中学习、实时适应市场变化并优化交易决策的全新智能模型。

这段探索之旅将带您深入算法交易的迷宫。在这里,数学的严谨与计算的流畅完美融合,创新的机器学习方法让经典的马丁格尔策略焕发新生。无论您是资深算法交易者、交易架构开发者,还是金融科技爱好者,本文都将提供关于自动化交易未来的独特视角。

准备好了吗?我们即将开启llan3.0AI的构建之旅,见证传统与创新的碰撞,看过去的经验如何演化为未来的趋势。


解构经典Ilan的内在逻辑

在踏入AI领域之前,需先理解Ilan在2010年代风靡的核心原因。其核心机制是仓位平均化。当价格反向波动时,EA不会止损离场,而是反向加仓,通过拉低平均入场价实现潜在盈利。

以下简化的代码段说明了这一逻辑:

// Simplified averaging logic in the original Ilan
if(positionCount == 0) {
   // Opening first position on signal
   if(OpenSignal()) {
      OpenPosition(ORDER_TYPE_BUY, StartLot);
   }
} else {
   // Calculating level for averaging
   double averagePrice = CalculateAveragePrice();
   double gridLevel = averagePrice - GridSize * Point();
   
   // If price has reached grid level, add position
   if(Bid <= gridLevel) {
      double newLot = StartLot * MathPow(LotMultiplier, positionCount);
      OpenPosition(ORDER_TYPE_BUY, newLot);
   }
   
   // Checking for closing of all TP positions
   if(Bid >= averagePrice + TakeProfit * Point()) {
      CloseAllPositions();
   }
}

马丁格尔的魔力:交易者为何痴迷于Ilan

Ilan的流行可归因于多重因素。首先,其运作逻辑对新手交易者而言直观易懂。在震荡行情中,该系统展现出近乎神奇的效果——每一次价格波动都转化为盈利来源。通过仓位平均化机制,初始亏损的交易得以"拯救",让交易者产生策略架构无懈可击的错觉。

另一个极具吸引力的特质在于其极简的参数定制功能:

// Key parameters of Ilan Expert Advisor
input double   StartLot = 0.01;          // Initial lot size
input double   LotMultiplier = 1.5;      // Lot multiplier for each new position
input int      GridSize = 30;            // Grid size in points
input int      TakeProfit = 40;          // Profit for closing all positions
input int      MaxPositions = 10;        // Maximum number of positions to open

这种简易的参数配置营造了掌控全局的错觉。交易者可以通过调整不同参数组合,在历史数据回测中收获令人惊叹的成果。

进化之路:Ilan 2.0的革新尝试

在Ilan 2.0版本中,开发者试图解决初代版本的部分缺陷。已添加基于市场波动性的网格步长动态计算、多货币对交易及其相关性分析、新增开仓的条件过滤以及防止过度亏损的保护机制。

// Dynamic calculation of grid step in Ilan 2.0
double CalculateGridStep(string symbol) {
   double atr = iATR(symbol, PERIOD_CURRENT, ATR_Period, 0);
   return atr * ATR_Multiplier;
}

// Protective mechanism to limit losses
bool EquityProtection() {
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   double maxAllowedDrawdown = AccountInfoDouble(ACCOUNT_BALANCE) * MaxDrawdownPercent / 100.0;
   
   if(AccountInfoDouble(ACCOUNT_BALANCE) - currentEquity > maxAllowedDrawdown) {
      CloseAllPositions();
      return true;
   }
   return false;
}

这些改进确实提升了系统的抗风险能力,但未能解决缺乏自适应性的根本问题。Ilan 2.0仍基于静态规则运行,无法从历史经验中学习,更无法适应瞬息万变的市场环境。

为何交易者仍对Ilan趋之若鹜?

尽管缺陷明显,众多交易者仍持续使用Ilan及其变种。这背后存在多重心理诱因。亏损者往往沉默不语,而成功案例却被大肆宣扬。交易者只关注盈利阶段,而忽视风险信号。坚信通过微调参数即可解决所有的问题。马丁格尔策略触发了与赌博相同的心理快感机制。

客观来说,在特定市场条件下(尤其是低波动震荡行情),该EA确实能长期展现惊人的表现。问题在于,市场状态终将改变,而EA却无法随之进化。

Ilan为何不可避免地失控并导致爆仓?

长期运行后,Ilan的致命缺陷暴露无遗。在震荡市中表现完美的策略,面对长期趋势行情时却极具灾难性。以典型的失控场景为例。

首先开立第一个买入仓位。然后市场开始稳步下跌。EA按照指数级增加手数来加仓。经数轮平均后,仓位规模变得如此之大,以至于即使是小幅度的进一步波动也会导致追加保证金通知。

从数学上讲,该问题的表述如下:采用1.5倍倍增系数的马丁格尔策略,初始仓位0.01手时,第10次连续加仓的仓位规模将达0.57手(初始的57倍),所有持仓总和约1.1手。在1000美元账户、1:100杠杆条件下,这相当于使用近全部可用保证金。

// Calculation of total size of martingale positions
double totalVolume = 0;
double currentLot = StartLot;
for(int i = 0; i < MaxPositions; i++) {
   totalVolume += currentLot;
   currentLot *= LotMultiplier;
}
// At StartLot = 0.01 and LotMultiplier = 1.5 after 10 positions
// totalVolume will be about 1.1 lots!

核心问题在于缺乏终止平均化策略的判断机制。Ilan如同不知止损的赌徒,在亏损时不断加倍下注,妄图通过一次胜利挽回所有损失。

为何Ilan需要AI?

剖析llan的优劣可知,其致命缺陷在于缺乏自主学习和动态适应能力。无论过往盈亏如何,EA始终机械地执行既定规则。

这正是AI的用武之地。如果我们保留Ilan的基本运行机制,但为其添加分析操作结果并调整策略的功能会怎样?如果我们不用硬编码规则,而是让模型根据积累的经验独立找到最优参数和入场点又会怎样?

这一想法正是我们 Ilan 3.0 AI项目的核心,我们将久经考验的平均策略与先进的机器学习方法相结合。在下一章节中,我们将探讨Q-learning技术如何使这一概念成为现实。


Q-learning将如何改进Ilan EA

从经典Ilan模型向能够学习的智能系统转变,需要从根本上改变EA的架构。Q-learning算法成为Ilan 3.0 AI的核心。它是强化学习领域中最重要的技术之一。

基于您行为的Q-learning基础

Q-learning因"质量函数"而得名,该函数用于确定特定条件下某个操作的价值。在实现中,我们通过维护Q-table这种数据结构,存储每个"状态—操作"对的评分:

// Structure for Q-table
struct QEntry {
    string state;     // Discretized state of market
    int action;       // Action (0-nothing, 1-buy, 2-sell)
    double value;     // Q-value
};

// Global array for Q-table
QEntry QTable[];
int QTableSize = 0;
Q-learning的核心特性在于系统具备从自身经验中学习的能力。每次执行操作后,算法会基于获得的奖励(盈利)或惩罚(亏损),动态调整对该操作在特定市场状况下价值的评估:
// Updating Q-value using Bellman formula
void UpdateQValue(string stateStr, int action, double reward, string nextStateStr, bool done)
{
    double currentQ = GetQValue(stateStr, action);
    double nextMaxQ = 0;
    
    if(!done)
    {
        // Find maximum Q for following state
        nextMaxQ = GetMaxQValue(nextStateStr);
    }
    
    // Updating Q-value
    double newQ = currentQ + LearningRate * (reward + DiscountFactor * nextMaxQ - currentQ);
    
    // Saving updated value
    SetQValue(stateStr, action, newQ);
}

这一公式被称为贝尔曼方程,是我们方法的核心基石。其中,学习率参数控制每次经验更新的权重,折扣因子则决定未来收益相对于当前收益的重要程度。

探索与利用的平衡之道

强化学习中的关键挑战之一,在于如何平衡探索新策略与利用已知最优解。为解决这一问题,我们采用ε-贪婪策略。

// Choosing action with balance between exploration and exploiting
int SelectAction(string stateStr)
{
    // With epsilon probability choose random action (research)
    if(MathRand() / (double)32767 < currentEpsilon)
    {
        return MathRand() % ActionCount;
    }
    
    // Otherwise, choose action with maximum Q-value (exploiting)
    int bestAction = 0;
    double maxQ = GetQValue(stateStr, 0);
    
    for(int a = 1; a < ActionCount; a++)
    {
        double q = GetQValue(stateStr, a);
        if(q > maxQ)
        {
            maxQ = q;
            bestAction = a;
        }
    }
    
    return bestAction;
}
当前ε值(currentEpsilon)参数决定了系统随机选择操作的概率。随着时间的推移,该参数值会逐渐减小,使系统从主动探索新策略转向优先利用已积累的知识:
// Reducing epsilon for a gradual transition from exploration to exploiting
if(currentEpsilon > MinExplorationRate)
    currentEpsilon *= ExplorationDecay;

数字感知:算法如何“读懂”市场

为了有效训练算法,必须赋予其市场“感知”能力,即对当前市场状态建立结构化认知。在Ilan 3.0 AI中,我们采用多维状态向量,其中包括技术指标、持仓数据和其他市场信息:

// Getting current market state for Q-learning
void GetCurrentState(string symbol, double &state[])
{
    ArrayResize(state, StateDimension);
    
    // Technical indicators
    double rsi = iRSI(symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE, 0);
    double cci = iCCI(symbol, PERIOD_CURRENT, CCI_Period, PRICE_TYPICAL, 0);
    double macd = iMACD(symbol, PERIOD_CURRENT, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 0);
    
    // Normalization of indicators
    double normalized_rsi = rsi / 100.0;
    double normalized_cci = (cci + 500) / 1000.0;
    
    // Metrics of positions
    int positions = (symbol == "EURUSD") ? euroUsdPositions : audUsdPositions;
    double normalized_positions = (double)positions / MaxTrades;
    
    // Calculating price difference
    double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
    double currentBid = SymbolInfoDouble(symbol, SYMBOL_BID);
    double avgPrice = CalculateAveragePrice(symbol);
    double price_diff = (currentBid - avgPrice) / (100 * point);
    
    // Filling in state vector
    state[0] = normalized_rsi;
    state[1] = normalized_cci;
    state[2] = normalized_positions;
    state[3] = price_diff;
    state[4] = macd / (100 * point);
    state[5] = AccountInfoDouble(ACCOUNT_EQUITY) / AccountInfoDouble(ACCOUNT_BALANCE);
}
为适配Q-table,连续状态值必须进行离散化处理——即将其转换为具有有限选项的字符串表示形式:
// Converting state to string for Q-table
string StateToString(double &state[])
{
    string stateStr = "";
    
    for(int i = 0; i < ArraySize(state); i++)
    {
        // Rounding to 2 decimal places for discretization
        double discretized = MathRound(state[i] * 100) / 100.0;
        stateStr += DoubleToString(discretized, 2);
        
        if(i < ArraySize(state) - 1)
            stateStr += ",";
    }
    
    return stateStr;
}

离散化处理使系统能够通过迁移经验实现泛化——将过往经验应用于相似但非完全相同的市场状况。

激励机制:AI世界的"胡萝卜加大棒"

在强化学习系统中,奖励机制的设计至关重要,直接决定了算法的学习效率。在Ilan 3.0AI 中,我们开发了一个多层激励系统,引导算法向正确的方向发展。

奖励经济学:如何激励算法

当以盈利平仓时,模型会获得与盈利金额成正比的正向奖励:

// Reward at closing profitable positions
if(shouldClose)
{
    if(ClosePositions(symbol, magic))
    {
        if(isTraining)
        {
            double reward = profit; // Reward is equal to profit
            UpdateQValue(stateStr, action, reward, stateStr, true);
            
            // Learning statistics
            episodeCount++;
            totalReward += reward;
            
            Print(“Episode ", episodeCount, " completed with reward: ", reward, 
                 ". Average reward: ", totalReward / episodeCount);
        }
    }
}
当对亏损仓位进行加仓平摊成本时,系统会受到轻微惩罚,以此引导算法寻找最优入场点并规避需要加仓的被动局面:
// Penalty at averaging positions
if(OpenPosition(symbol, ORDER_TYPE_BUY, CalculateLot(positionsCount, symbol), StopLoss, TakeProfit, magic))
{
    if(isTraining)
    {
        double reward = -1; // Small penalty for averaging
        UpdateQValue(stateStr, action, reward, stateStr, false);
    }
}

在首次建仓时未设置即时奖励,迫使系统聚焦长期收益而非短期操作。

模型参数的精细调优

Q-learning系统的效能高度依赖其核心参数的精准配置。在Ilan 3.0 AI中,我们开发了全维度学习参数调优模块:

// Reinforcement learning parameters
input double   LearningRate = 0.01;      // Learning rate
input double   DiscountFactor = 0.95;    // Discount factor
input double   ExplorationRate = 0.3;    // Initial probability of exploration
input double   ExplorationDecay = 0.995; // Exploration reduction factor
input double   MinExplorationRate = 0.01;// Minimal probability of exploration

学习率决定Q值的更新速率。Q值较高可加速学习进程,但容易导致策略不稳定。Q值较低虽确保学习稳定性,却会降低收敛速度。

折扣因子权衡未来收益的重要性。值越接近1,模型越倾向于追求长期利润最大化,甚至可能牺牲短期收益。

探索参数调控新策略探索与已知最优解利用之间的平衡。随着时间的推移,探索概率逐步衰减,使得系统逐渐依赖积累的经验进行决策。


从理论到实践:系统实现

Ilan 3.0 AI的实现需将Q-learning机制与传统EA逻辑深度融合。其核心模块为基于Q-learning的交易管理函数,该函数通过以下机制实现决策:
// Trade management function using Q-learning
void ManagePairWithDQN(string symbol, int &positionsCount, CArrayDouble &trades, 
                     int magic, datetime &firstTradeTime)
{
    // Getting current market data
    double currentBid = SymbolInfoDouble(symbol, SYMBOL_BID);
    double currentAsk = SymbolInfoDouble(symbol, SYMBOL_ASK);
    double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
    
    // Forming current state
    double state[];
    GetCurrentState(symbol, state);
    string stateStr = StateToString(state);
    
    // Selecting action using Q-table
    int action = SelectAction(stateStr);
    
    // Converting action into trading operation
    bool shouldTrade = (action > 0);
    ENUM_ORDER_TYPE orderType = (action == 1) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
    
    // Logic of opening and managing positions
    if(shouldTrade)
    {
        // Logic of opening first position or averaging
        // ...
    }
    
    // Checking need to close positions
    double profit = CalculatePositionsPnL(symbol, magic);
    if(profit > 0 && positionsCount > 0)
    {
        // Logic of closing profitable positions
        // ...
    }
}

系统经验存储

为了保存积累的经验,我们实现了在会话之间保存和加载Q-table的机制:

// Saving Q-table to file
bool SaveQTable(string filename)
{
    int handle = FileOpen(filename, FILE_WRITE|FILE_BIN);
    if(handle == INVALID_HANDLE)
    {
        Print("Error when opening file for writing Q-table: ", GetLastError());
        return false;
    }
    
    // Writing table size
    FileWriteInteger(handle, QTableSize);
    
    // Writing values
    for(int i = 0; i < QTableSize; i++)
    {
        FileWriteString(handle, QTable[i].state);
        FileWriteInteger(handle, QTable[i].action);
        FileWriteDouble(handle, QTable[i].value);
    }
    
    FileClose(handle);
    return true;
}

// Downloading Q-table from file
bool LoadQTable(string filename)
{
    if(!FileIsExist(filename))
    {
        Print("Q-table file does not exist: ", filename);
        return false;
    }
    
    int handle = FileOpen(filename, FILE_READ|FILE_BIN);
    if(handle == INVALID_HANDLE)
    {
        Print("Error when opening file to read Q-table: ", GetLastError());
        return false;
    }
    
    // Reading table size
    int size = FileReadInteger(handle);
    
    // Allocating memory for table
    if(size > ArraySize(QTable))
    {
        ArrayResize(QTable, size);
    }
    
    // Reading values
    for(int i = 0; i < size; i++)
    {
        QTable[i].state = FileReadString(handle);
        QTable[i].action = FileReadInteger(handle);
        QTable[i].value = FileReadDouble(handle);
        QTableSize++;
    }
    
    FileClose(handle);
    return true;
}

让我们考察该EA的测试表现。测试基于15分钟K线图的OHLC建模方法,对2020-2025年期间的AUDUSD和EURUSD货币对进行回测(主测试标的为EURUSD,但EA同时加载了AUDUSD数据作为辅助分析)。

但"全Ticks数据"回测无情地揭示了马丁格尔策略导致的巨额回撤。尽管系统设置了惩罚机制,模型仍会偶尔陷入连续加仓平摊成本的被动局面。未来的版本中,我们计划通过DQN风险管理体系彻底消除此类回撤风险:

需要注意的是,该交易EA采用超大仓位操作,此设计有助于根据经纪商的返佣机制获取佣金。

算法优化方向

尽管Ilan 3.0 AI已实现显著改进,但其仅代表智能交易系统演进的第一步。未来值得探索的优化路径包括:

  1. 将Q-table替换为完备的神经网络架构,提升策略对市场模式的泛化能力
  2. 实现深度强化学习(DQN、DDPG、PPO)算法
  3. 采用元学习技术,构建快速适应市场状况的机制
  4. 整合新闻事件与基本面分析架构


结论

llan3.0Al的问世,标志着算法交易方式的根本变革。我们正走出基于固定规则的静态系统,迈向具备自主学习与进化能力的自适应算法新时代。

通过经典Ilan策略与现代机器学习的融合,为交易系统的开发开辟了新天地。不再局限于无休止的参数调优,我们构建的系统能够自主探索最优策略,并适应变化的市场状况。

算法交易的未来,在于将久经考验的交易策略与创新的Al技术深度融合。Ilan 3.0 AI不仅是经典EA的升级版,更是开创了全新的智能交易系统类别 —— 此类系统能够与市场同步学习、自适应进化并持续发展。

我们正站在算法交易新时代的门槛上 —— 一个模型不再机械地执行预设的规则,而是通过持续进化,在瞬息万变的金融市场中自主探寻最优策略。

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/17455

附加的文件 |
Ilan_3.0_DQN.mq5 (83.75 KB)
最近评论 | 前往讨论 (3)
Roman Shiredchenko
Roman Shiredchenko | 9 4月 2025 在 10:41

目前只是粗略浏览了一下——乍一看这篇文章——太棒了!

得仔细观察并比较不同符号——哪些更适合伊拉诺的“训练”,哪些则不太适合!!!

Evgen Khenkin
Evgen Khenkin | 12 4月 2025 在 17:04

马丁欠了很多人的人情……

能获得一款具有发展前景的先进产品,这将是一件很有意思的事。

Sabino Martiradonna
Sabino Martiradonna | 7 2月 2026 在 15:47

你好,叶夫根尼

CalculateAveragePrice(symbol) 的代码在哪里?

    // 计算价格差
    double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
    double currentBid = SymbolInfoDouble(symbol, SYMBOL_BID);
    double avgPrice = CalculateAveragePrice(symbol);
    double price_diff = (currentBid - avgPrice) / (100 * point);

另外,如何预先计算两个或多个仓位平均价格的止损(SL)和止盈(TP)?

谢谢,萨比诺。

开发多币种 EA(第 28 部分):添加平仓管理器 开发多币种 EA(第 28 部分):添加平仓管理器
在并行运行多个策略时,您可能希望定期关闭所有持仓,并重新启动策略。现有代码仅允许通过手动操作来实现这一行为。让我们尝试将这一部分自动化。
MQL5 交易策略自动化(第二十七部分):创建带可视化反馈的价格行为螃蟹谐波形态 MQL5 交易策略自动化(第二十七部分):创建带可视化反馈的价格行为螃蟹谐波形态
本文将基于 MQL5 开发一套螃蟹谐波形态交易系统。程序借助枢轴点与斐波那契比例识别看涨、看跌螃蟹谐波形态,并按照精准的入场、止损、止盈点位触发交易。同时使用三角形、趋势线等图表对象做可视化展示,完整展示 XABCD 形态结构与各个交易价位。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
价格行为分析工具包开发(第三十六部分):实现Python直接读取MetaTrader 5行情数据流 价格行为分析工具包开发(第三十六部分):实现Python直接读取MetaTrader 5行情数据流
借助Python的数据科学生态以及MetaTrader 5官方客户端库,充分挖掘MetaTrader 5终端的全部能力。本文演示如何完成身份校验,将实时Tick数据与分钟K线数据流直接写入Parquet存储;利用Ta技术指标库与Prophet时间序列预测工具完成高阶特征工程,并训练具备时间感知能力的梯度提升模型。接下来,我们部署轻量Flask服务,对外实时输出交易信号。无论您正在搭建混合量化框架,还是希望借助机器学习增强EA策略,都将掌握一套稳健的端到端流水线,用于数据驱动的算法交易。