Summary: 专业的抛硬币随机入场MT4 EA,使用伪随机数决定多空方向。包含固定止损止盈、点差控制、风险百分比仓位管理和每日交易限制,完整源码可直接编译。




# 抛硬币随机入场EA - 完整MQL4源码

本文提供一个基于纯随机入场逻辑的完整自动交易EA,通常被称为“抛硬币”策略。与传统的技术分析EA不同,该系统使用伪随机数生成来做交易决策。虽然概念简单,但它包含了专业的风险管理功能,适合策略测试和教育目的。

策略逻辑



EA监控持仓状态。当没有持仓时,生成一个随机数。如果数字为偶数,开多单;为奇数,开空单。每笔交易包含可配置的止损、止盈和可选的保本管理。尽管是随机入场,但稳健的风险控制可以防止灾难性亏损。

完整MQL4代码



```mql4
//+------------------------------------------------------------------+
//| RandomCoinFlipEA.mq4 |
//| 自主编译 |
//| |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "1.00"
#property strict

//--- 输入参数
input double FixedLotSize = 0.1; // 固定手数(RiskPercent=0时使用)
input double RiskPercent = 1.0; // 风险百分比(基于可用保证金)
input int StopLoss = 60; // 止损点数
input int TakeProfit = 120; // 止盈点数
input bool UseBreakeven = true; // 启用保本止损
input int BreakevenTrigger = 30; // 保本触发点数
input int MaxSpread = 35; // 最大允许点差
input int DailyTradeLimit = 0; // 每日最大交易次数(0=无限制)
input int Slippage = 10; // 最大滑点
input int MagicNumber = 202413; // EA魔术号
input bool CloseOpposite = true; // 反向信号时平仓反向单

//--- 全局变量
int pointMultiplier = 10; // 5位报价平台乘数
int tradesToday = 0;
string currentDate = "";

//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 检测平台位数格式
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else if(Digits == 2 || Digits == 4)
pointMultiplier = 1;

// 使用服务器时间初始化随机种子
MathSrand(TimeCurrent());

Print("抛硬币随机EA已初始化. 魔术号: ", MagicNumber);
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("抛硬币随机EA已移除. 原因: ", reason);
}

//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 检查新交易日以重置计数器
ResetDailyCounter();

// 检查每日交易限制
if(!CheckDailyLimit())
return;

// 检查点差条件
if(!IsSpreadOK())
return;

// 管理现有持仓(保本)
if(UseBreakeven)
ManageBreakeven();

// 统计当前持仓
int totalPositions = CountPositions();

// 仅当无持仓时开新仓
if(totalPositions == 0)
{
OpenRandomTrade();
}
}

//+------------------------------------------------------------------+
//| 开随机方向交易 |
//+------------------------------------------------------------------+
void OpenRandomTrade()
{
// 生成随机数(0到32767)
int randomValue = MathRand();

// 决定方向:偶数=多单,奇数=空单
int direction = (randomValue % 2 == 0) ? OP_BUY : OP_SELL;

// 基于风险管理计算手数
double lotSize = CalculateLotSize();
if(lotSize <= 0)
{
Print("计算的手数无效");
return;
}

// 计算订单价格
double price = (direction == OP_BUY) ? Ask : Bid;

// 计算止损和止盈
double sl = 0, tp = 0;

if(StopLoss > 0)
{
if(direction == OP_BUY)
sl = price - StopLoss * Point * pointMultiplier;
else
sl = price + StopLoss * Point * pointMultiplier;
}

if(TakeProfit > 0)
{
if(direction == OP_BUY)
tp = price + TakeProfit * Point * pointMultiplier;
else
tp = price - TakeProfit * Point * pointMultiplier;
}

// 发送订单
string comment = (direction == OP_BUY) ? "正面" : "反面";
int ticket = OrderSend(Symbol(), direction, lotSize, price, Slippage, sl, tp,
comment, MagicNumber, 0, (direction == OP_BUY) ? clrBlue : clrRed);

if(ticket < 0)
{
Print("开仓失败. 错误码: ", GetLastError());
}
else
{
tradesToday++;
Print("开仓成功. 订单号: ", ticket, " 方向: ", comment, " 手数: ", lotSize);
}
}

//+------------------------------------------------------------------+
//| 基于风险百分比计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
if(RiskPercent <= 0)
{
// 使用固定手数
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);

double lot = FixedLotSize;
if(lot < minLot) lot = minLot;
if(lot > maxLot) lot = maxLot;
lot = MathFloor(lot / lotStep) * lotStep;

return NormalizeDouble(lot, 2);
}

// 基于风险百分比的动态手数
double stopLossPoints = StopLoss * pointMultiplier;
if(stopLossPoints <= 0) return FixedLotSize;

double riskAmount = AccountFreeMargin() * RiskPercent / 100.0;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);

if(tickValue == 0 || lotStep == 0) return FixedLotSize;

double calculatedLot = riskAmount / (stopLossPoints * tickValue);

// 按允许步长取整
calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;

// 最小/最大手数检查
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

if(calculatedLot < minLot) calculatedLot = minLot;
if(calculatedLot > maxLot) calculatedLot = maxLot;

return NormalizeDouble(calculatedLot, 2);
}

//+------------------------------------------------------------------+
//| 管理现有持仓的保本止损 |
//+------------------------------------------------------------------+
void ManageBreakeven()
{
if(BreakevenTrigger <= 0) return;

for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double breakevenPoints = BreakevenTrigger * Point * pointMultiplier;

if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / Point / pointMultiplier;
if(profitPips >= BreakevenTrigger && OrderStopLoss() < OrderOpenPrice())
{
bool modified = OrderModify(OrderTicket(), OrderOpenPrice(),
OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
if(modified)
Print("保本止损已触发 BUY #", OrderTicket());
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / Point / pointMultiplier;
if(profitPips >= BreakevenTrigger && (OrderStopLoss() > OrderOpenPrice() || OrderStopLoss() == 0))
{
bool modified = OrderModify(OrderTicket(), OrderOpenPrice(),
OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
if(modified)
Print("保本止损已触发 SELL #", OrderTicket());
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| 检查点差是否在限制范围内 |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;

int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
bool spreadOK = (currentSpread <= MaxSpread);

if(!spreadOK)
Comment("点差过高: ", currentSpread, "/", MaxSpread);

return spreadOK;
}

//+------------------------------------------------------------------+
//| 统计本EA的持仓数量 |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}

//+------------------------------------------------------------------+
//| 检查每日交易限制 |
//+------------------------------------------------------------------+
bool CheckDailyLimit()
{
if(DailyTradeLimit <= 0) return true;
return (tradesToday < DailyTradeLimit);
}

//+------------------------------------------------------------------+
//| 新交易日重置交易计数器 |
//+------------------------------------------------------------------+
void ResetDailyCounter()
{
string today = TimeToString(TimeCurrent(), TIME_DATE);
if(currentDate != today)
{
currentDate = today;
tradesToday = 0;
Print("新交易日的开始. 交易计数器已重置.");
}
}

//+------------------------------------------------------------------+
//| 平仓所有持仓(工具函数) |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
bool closed = false;
if(OrderType() == OP_BUY)
closed = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrNONE);
else if(OrderType() == OP_SELL)
closed = OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrNONE);

if(!closed)
Print("平仓失败 #", OrderTicket(), " 错误码: ", GetLastError());
}
}
}
}

//+------------------------------------------------------------------+
//| 手动平仓所有订单(可通过外部输入调用) |
//+------------------------------------------------------------------+
void CloseAll()
{
CloseAllPositions();
Print("所有持仓已手动平仓.");
}
//+------------------------------------------------------------------+
```

参数详解



| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 固定手数 | 固定交易手数(RiskPercent=0时使用) | 0.01-0.1 |
| 风险百分比 | 每笔风险占可用保证金的百分比 | 1.0-2.0 |
| 止损点数 | 止损距离 | 40-80 |
| 止盈点数 | 止盈距离 | 80-160 |
| 启用保本 | 启用自动保本止损 | true |
| 保本触发 | 移动止损到开仓价的触发点数 | 25-40 |
| 最大点差 | 允许的最大点差 | 20-40 |
| 每日限制 | 每日最大交易次数(0=无限制) | 3-10 |
| 滑点 | 最大滑点容忍度 | 10 |
| 魔术号 | EA唯一标识 | 任意不重复数字 |
| 反向平仓 | 新信号时平掉反向持仓 | true |

安装步骤



1. 复制代码到MT4的MetaEditor(按F4)
2. 创建新的EA(文件 > 新建 > 智能交易系统)
3. 将所有默认代码替换为上方完整代码
4. 按编译按钮(F7)- 验证无错误
5. 从导航器将EA拖拽到图表上
6. 在输入参数选项卡中调整参数
7. 启用自动交易(Alt+T)

编译与修改技巧



关于随机入场EA的重要说明:
  • 随机种子使用服务器时间初始化,确保真正的随机性

  • 本策略仅用于教育和策略测试目的,不具备持续盈利能力

  • 在考虑实盘部署前,务必在模拟账户中测试


  • 修改不同行为的示例:
  • 将`randomValue % 2 == 0`改为`randomValue % 3 == 0`可获得33/67的分布比例

  • 通过在开仓前检查`Hour()`函数添加时间过滤

  • 通过跟踪累计盈亏实现最大日亏损限制


  • 重要警告:
    纯随机入场策略在扣除点差和手续费后具有负的数学期望值。本EA仅用于教育和蒙特卡洛模拟测试目的。

    参考来源



    本文EA源码为自主编译,基于MQL5社区文档中记录的"Heads or Tails"抛硬币随机入场原理。风险管理模块遵循MQL4标准实践。

    *如需更专业的优化版EA(含高级技术分析过滤、多时间框架确认、完整回测报告),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*