追踪止损的实现
追踪止损是一种动态调整止损位的风险管理技术。其核心思想是:当订单产生浮动盈利时,自动将止损位朝着盈利方向移动,以“锁定”已获得的利润,并在价格反向回调时保护这些利润。
工作原理:
- 设置距离: 首先设定一个追踪距离,通常以pips为单位(例如50pips)。
- 触发条件: 只有当订单的浮动盈利达到或超过这个追踪距离时,追踪止损才开始生效并可能移动。
- 追踪移动: 一旦生效,系统会不断检查当前市场价格与当前止损位之间的距离。
- 如果这个距离大于您设定的追踪距离,系统会自动移动止损位,使其刚好维持在距离当前市场价格(用于触发平仓的价格)一个追踪距离的位置。
- 买单: 止损只会被向上移动(提高)。移动的目标位置 =
当前 Bid - 追踪距离
。 - 卖单: 止损只会被向下移动(降低)。移动的目标位置 =
当前 Ask + 追踪距离
。
- 单向移动: 如果市场价格向不利方向移动(利润回吐),追踪止损位将保持在最后的位置不动,不会反向(向下对买单,向上对卖单)移动。
MQL4实现逻辑: 我们需要在订单遍历循环中加入对每个符合条件的订单进行追踪止损检查和修改的逻辑。
-
计算基准: 追踪止损的距离是相对于能够触发平仓的价格来计算的:
- 买单: 以当前的买价 (Bid) 为基准。
- 卖单: 以当前的卖价 (Ask) 为基准。
-
计算目标追踪止损位 (
targetTrailingStopPrice
):- 买单:
targetTrailingStopPrice = 当前 Bid - (追踪距离 pips * 每 pip 价值)
。这是根据当前 Bid 价所允许的最高的追踪止损位置。 - 卖单:
targetTrailingStopPrice = 当前 Ask + (追踪距离 pips * 每 pip 价值)
。这是根据当前 Ask 价所允许的最低的追踪止损位置。
- 买单:
-
判断是否需要移动:
- 买单: 如果当前止损位 (
OrderStopLoss()
) < 目标止损位 (targetTrailingStopPrice
),意味着当前止损离现价太远(利润已超过追踪距离),需要向上移动到targetTrailingStopPrice
(或者一个经过验证的价格)。还需要确保目标价高于开仓价。 - 卖单: 如果 ( 当前止损位 (
OrderStopLoss()
) > 目标止损位 (targetTrailingStopPrice
) ) 或者 ( 当前根本没有止损 (OrderStopLoss() == 0
) ),并且目标价低于开仓价,意味着当前止损离现价太远或不存在,需要向下移动(或首次设置)到targetTrailingStopPrice
(或者一个经过验证的价格)。
- 买单: 如果当前止损位 (
-
执行修改: 如果判断需要移动,则调用
OrderModify()
函数,将订单的止损位修改为计算并验证过的目标追踪止损价格。
代码实现 (以处理买单为例):
首先,定义追踪止损的输入参数:
// 输入参数
extern double TrailingStopPips = 50; // 追踪止损距离 (pips),如果为 0 则不启用
然后,在 OnTick()
函数的订单遍历循环中加入处理逻辑
/**
* @brief 为所有符合条件的买单执行带【最小盈利触发】的追踪止损。
* @param argSymbol 交易品种。
* @param argTrailingStopPips 追踪止损距离 (pips)。
* @param argMagicNumber 魔术数字。
*/
void BuyTrailingStopWithMinProfit(string argSymbol, int argTrailingStopPips, int argMagicNumber)
{
// (建议从后往前遍历)
int total = OrdersTotal();
//for (int i = 0; i < total; i++)
for (int i = total-1; i >=0; i--)
{
if (OrderSelect(i, SELECT_BY_POS) == true)
{
// 筛选条件:本EA、本品种、买入市价单
if (OrderSymbol() == argSymbol && OrderMagicNumber() == argMagicNumber && OrderType() == OP_BUY)
{
// 检查是否启用追踪 (TrailingStopPips > 0)
if (argTrailingStopPips > 0)
{
// 1. 获取所需信息
double openPrice = OrderOpenPrice();
double currentStopLoss = OrderStopLoss();
if(!RefreshRates())
{
Print("刷新价格失败,跳过订单 #", OrderTicket());
continue;
}
double currentBid = MarketInfo(argSymbol, MODE_BID);
// 4. 计算【理论目标追踪止损位】
double targetTrailingStopPrice = currentBid - (argTrailingStopPips * PipPoint(argSymbol));
// 5. 【核心判断条件】: 同时满足以下所有条件才移动止损
// 目标追踪止损位高于开仓价 (确保不把止损移到亏损区)
// 目标追踪止损位显著高于当前止损位 (或当前无止损)
if (
targetTrailingStopPrice > openPrice &&
(currentStopLoss <= 0 || targetTrailingStopPrice > currentStopLoss + Point*0.1))
{
// 6. 对【目标止损位】进行规范化和【停止级别验证】(非常重要!)
targetTrailingStopPrice = NormalizeDouble(targetTrailingStopPrice, Digits);
// (应加入 AdjustBelowStopLevel 验证逻辑, 确保 target <= Bid - StopLevel*Point)
// targetTrailingStopPrice = AdjustTrailingStopPrice(argSymbol, targetTrailingStopPrice, OP_BUY); // 假设有封装函数
// 7. (假设 WaitForTradeContext 和 HandleTradeError 已封装)
if (!WaitForTradeContext()) continue;
bool modified = OrderModify(OrderTicket(), openPrice, targetTrailingStopPrice,
OrderTakeProfit(), OrderExpiration(), clrNONE); // 保持 TP 和 Expiration 不变
// 8. 错误处理
if (!modified) {
HandleTradeError(StringFormat("BuyTrailingStopWithMinProfit 修改 #%d", OrderTicket()), GetLastError());
} else {
Print("成功移动买单 #", OrderTicket(), " (带最小盈利) 追踪止损至 ", targetTrailingStopPrice);
}
} // end if (满足所有移动条件)
} // end if (启用追踪)
} // end if (匹配订单)
} // end if OrderSelect
} // end for
} // end BuyTrailingStopWithMinProfit
处理卖单的追踪止损:
- 筛选条件为
OrderType() == OP_SELL
。 - 计算基准价为当前卖价 (Ask)。
- 计算目标追踪止损位
targetTrailingStopPrice = Ask + (TrailingStopPips * g_onePipValue)
。 - 判断条件为:
- 目标位已低于开仓价 (
targetTrailingStopPrice < openPrice
)。 - 并且,目标位显著低于当前止损位 (
targetTrailingStopPrice < currentStopLoss - Point*0.1
),或者当前没有止损 (currentStopLoss == 0
)。 这对应了原文中的(CurrentStop > MaxStopLoss || CurrentStop == 0)
条件,其中MaxStopLoss
指的是计算出的目标追踪价targetTrailingStopPrice
。CurrentStop > MaxStopLoss
(对卖单)意味着当前 SL(例如 -50 pips)比目标 SL(例如 -40 pips)更低(数值上更大),所以需要向下移动。而CurrentStop == 0
则处理了首次设置追踪止损的情况。
- 目标位已低于开仓价 (
- 执行修改时,调用
OrderModify
更新止损位。
实现一个健壮可靠的追踪止损功能,需要严谨地处理价格比较、边界条件、停止级别验证以及错误情况。
追踪止损增强:加入最小盈利触发
前面介绍的基础追踪止损有一个潜在问题:它可能会在订单刚开仓、略有浮盈时就立即开始移动止损,这可能会覆盖掉您精心设置的、具有一定缓冲空间的初始止损位。例如,您设置了 100 点的初始止损,但追踪止损距离设为 50 点,那么只要价格朝有利方向移动一点点,满足追踪条件,止损就可能被立刻调整到距离现价 50 点的位置,使得最初的 100 点止损变得毫无意义。
为了解决这个问题,我们可以为追踪止损增加一个最小盈利触发条件。只有当订单的浮动盈利达到或超过这个预设的最小盈利点数时,追踪止损功能才会被激活并开始工作。
- 示例场景:
- 初始止损设为 100 pips。
- 追踪止损距离设为 50 pips。
- 最小盈利触发点设为 50 pips。
- 行为:
- 订单开仓后,止损保持在初始的 100 pips 位置。
- 当且仅当该订单的浮动盈利达到或超过 50 pips 时,追踪止损机制启动。
- 启动后,系统检查当前价格与止损位的距离。如果距离大于 50 pips,则移动止损,使其保持在距离当前平仓价 50 pips 的位置。
实现: 首先,在外部输入参数中增加 MinimumProfitPips
:
// 输入参数
extern int TrailingStopPips = 50; // 追踪止损距离 (pips)
extern int MinimumProfitPips = 50; // 最小盈利触发点 (pips)
然后,修改之前的追踪止损函数(以买单为例),加入最小盈利判断:
/**
* @brief 为所有符合条件的买单执行带【最小盈利触发】的追踪止损。
* @param argSymbol 交易品种。
* @param argTrailingStopPips 追踪止损距离 (pips)。
* @param argMinProfitPips 最小盈利触发点 (pips)。
* @param argMagicNumber 魔术数字。
*/
void BuyTrailingStopWithMinProfit(string argSymbol, int argTrailingStopPips, int argMinProfitPips,
int argMagicNumber)
{
// (建议从后往前遍历)
int total = OrdersTotal();
//for (int i = 0; i < total; i++)
for (int i = total-1; i >=0; i--)
{
if (OrderSelect(i, SELECT_BY_POS) == true)
{
// 筛选条件:本EA、本品种、买入市价单
if (OrderSymbol() == argSymbol && OrderMagicNumber() == argMagicNumber && OrderType() == OP_BUY)
{
// 检查是否启用追踪 (TrailingStopPips > 0)
if (argTrailingStopPips > 0)
{
// 1. 获取所需信息
double openPrice = OrderOpenPrice();
double currentStopLoss = OrderStopLoss();
if(!RefreshRates())
{
Print("刷新价格失败,跳过订单 #", OrderTicket());
continue;
}
double currentBid = MarketInfo(argSymbol, MODE_BID);
// 2. 计算【当前盈利】对应的价格差
double currentProfitPriceDiff = currentBid - openPrice;
// 3. 计算【最小盈利阈值】对应的价格差 (需要 PipPoint 函数)
double minProfitPriceThreshold = argMinProfitPips * PipPoint(argSymbol);
// 4. 计算【理论目标追踪止损位】
double targetTrailingStopPrice = currentBid - (argTrailingStopPips * PipPoint(argSymbol));
// 5. 【核心判断条件】: 同时满足以下所有条件才移动止损
// a) 当前盈利已达到或超过最小盈利阈值
// b) 目标追踪止损位高于开仓价 (确保不把止损移到亏损区)
// c) 目标追踪止损位显著高于当前止损位 (或当前无止损)
if (currentProfitPriceDiff >= minProfitPriceThreshold && // 条件 a
targetTrailingStopPrice > openPrice && // 条件 b
(currentStopLoss <= 0 || targetTrailingStopPrice > currentStopLoss + Point*0.1)) // 条件 c
{
// 6. 对【目标止损位】进行规范化和【停止级别验证】(非常重要!)
targetTrailingStopPrice = NormalizeDouble(targetTrailingStopPrice, Digits);
// (应加入 AdjustBelowStopLevel 验证逻辑, 确保 target <= Bid - StopLevel*Point)
// targetTrailingStopPrice = AdjustTrailingStopPrice(argSymbol, targetTrailingStopPrice, OP_BUY); // 假设有封装函数
// 7. (假设 WaitForTradeContext 和 HandleTradeError 已封装)
if (!WaitForTradeContext()) continue;
bool modified = OrderModify(OrderTicket(), openPrice, targetTrailingStopPrice,
OrderTakeProfit(), OrderExpiration(), clrNONE); // 保持 TP 和 Expiration 不变
// 8. 错误处理
if (!modified) {
HandleTradeError(StringFormat("BuyTrailingStopWithMinProfit 修改 #%d", OrderTicket()), GetLastError());
} else {
Print("成功移动买单 #", OrderTicket(), " (带最小盈利) 追踪止损至 ", targetTrailingStopPrice);
}
} // end if (满足所有移动条件)
} // end if (启用追踪)
} // end if (匹配订单)
} // end if OrderSelect
} // end for
} // end BuyTrailingStopWithMinProfit
- 代码关键修改:
- 计算当前盈利 (价格差):
currentProfitPriceDiff = currentBid - openPrice
。 - 计算最小盈利阈值 (价格差):
minProfitPriceThreshold = argMinProfitPips * PipPoint(argSymbol)
。 - 增强的
if
判断条件: 在原有的追踪止损条件(目标位优于当前位)基础上,额外增加了currentProfitPriceDiff >= minProfitPriceThreshold
的判断,确保只有当浮动盈利达到用户设定的最小点数后,才开始执行追踪止损。
- 计算当前盈利 (价格差):
总结: 加入了最小盈利触发条件的追踪止损 (BuyTrailingStopWithMinProfit
函数) 比基础版本更加灵活实用。它允许策略在初期使用一个可能较宽的初始止损来抵御正常波动,直到确认交易方向并且获得一定利润后,再启动追踪止损来积极锁定利润。因此,在您的 EA 中,可能会更倾向于使用这种带有最小盈利触发的版本。