RSI&MA策略,黄金策略,顺势而为,根据MA和RSI开发出的黄金策略,适合中长线投资者
/*策略思路:当价格在MA之上,RSI穿越下轨时做多
当价格在MA之下,RSI穿越上轨时做空
*/
//- - - - - - - - - - 加载库&声明库对象 - - - - - - - - - -
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>
CTrade trade;
CPositionInfo positionInfo;
COrderInfo orderInfo;
//- - - - - - - - - - 加载库&声明库对象 - - - - - - - - - -
//- - - - - - - - - - EA相关设置 - - - - - - - - - -
input group "设置参数"
sinput long MagicNum = 8888; // EA 编号
sinput int SlType = 1; // 止损方式(1:固定金额美金 2:账户% 3:固定手, 4:真仓总账户%)
sinput double SlParam = 30; // 止损依据
input group "参数优化"
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // 时间级别
input int RSIPeriod = 14; // RSI周期
input int RSILevel = 70; // RSI上沿值
input int StopLoss = 50; // 止损点数 (0=没有止损)
input int TakeProfit = 50; // 止盈点数 (0=没有止盈)
input bool CloseSignal = true; // 出现相反信号时是否平仓
input group "均线过滤"
input bool UseMAFilter = true; // 是否使用均线过滤?
input int MAPeriod = 20; // 均线周期
input ENUM_TIMEFRAMES MATimeFrame = PERIOD_H1; // 均线时间级别
//- - - - - - - - - - EA相关设置 - - - - - - - - - -
//- - - - - - - - - - 全局变量 - - - - - - - - - -
int barsTotal;
double point;
double initBalance;
int RsiHandle;
double RsiBuffer[];
int MAHandle;
double MABuffer[];
MqlTick currentTick;
//- - - - - - - - - - 全局变量 - - - - - - - - - -
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int OnInit() {
//检查输入参数
if(MagicNum<0) {
Alert("MagicNum<0");
return INIT_PARAMETERS_INCORRECT;
}
if(RSIPeriod<=1) {
Alert("RSIPeriod <=1");
return INIT_PARAMETERS_INCORRECT;
}
if(RSILevel>=100 || RSILevel<=50) {
Alert("RSILevel>=100 || RSILevel<=50");
return INIT_PARAMETERS_INCORRECT;
}
if(UseMAFilter && MAPeriod<=1) {
Alert("UseMAFilter is True & MAPeriod <=1");
return INIT_PARAMETERS_INCORRECT;
}
if(StopLoss<0) {
Alert("Stop loss<0");
return INIT_PARAMETERS_INCORRECT;
}
if(TakeProfit < 0) {
Alert("Take profit <0");
return INIT_PARAMETERS_INCORRECT;
}
if(UseMAFilter && MATimeFrame<TimeFrame) {
Alert("UseMAFiilter is True & MATimeFrame <TimeFrame");
return INIT_PARAMETERS_INCORRECT;
}
//句柄买例化
RsiHandle = iRSI(_Symbol,TimeFrame,RSIPeriod,PRICE_CLOSE);
if(RsiHandle == INVALID_HANDLE) {
Alert((string)MagicNum,"",_Symbol,"RsiHandle创建失败!");
return INIT_FAILED;
}
ArraySetAsSeries (RsiBuffer, true);
if(UseMAFilter) {
MAHandle = iMA(_Symbol,MATimeFrame,MAPeriod,0,MODE_SMA,PRICE_CLOSE);
if(MAHandle == INVALID_HANDLE) {
Alert((string)MagicNum," ",_Symbol,"MAHandle创建失败!");
return INIT_FAILED;
}
ArraySetAsSeries (MABuffer, true);
}
Print(MagicNum," ",_Symbol,"实例化成功");
if(!MQLInfoInteger(MQL_TESTER)) ChartSetSymbolPeriod(0,_Symbol,TimeFrame);
initBalance = SlParam;
Comment("MagicNum:",MagicNum," SlType:",SlType,"SlParam:", SlParam);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnTick() {
if(!isNewBar(_Symbol, TimeFrame, barsTotal)) return;
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(!SymbolInfoTick (_Symbol,currentTick)) {
Print("tick数据获取异常!");
return;
}
CopyBuffer(RsiHandle,0,0,3,RsiBuffer);
if(UseMAFilter) CopyBuffer(MAHandle,0,0,2,MABuffer);
// 统计开仓数量
int cntBuy,cntSell;
if(!countOpenPositions(MagicNum, cntBuy,cntSell)) return ;
// 多单开仓条件
bool bullSignal = cntBuy==0 && RsiBuffer>=(100-RSILevel) && RsiBuffer<(100-RSILevel);
if(UseMAFilter) bullSignal = bullSignal && currentTick.ask>MABuffer;
if(bullSignal) {
if(CloseSignal&&cntSell>0) closePositions(MagicNum,POSITION_TYPE_SELL);
double sl = StopLoss==0 ? 0 : currentTick.ask - StopLoss * point;
double tp = TakeProfit==0 ? 0 : currentTick.ask + TakeProfit * point;
if(!NormalizePrice(_Symbol, sl)) return;
if(!NormalizePrice(_Symbol, tp)) return;
double slp = SlParam;
if(SlType==4) slp = calculateEachEaBalace(MagicNum, initBalance);//子账户百分之1
double lotSize = calculateLots(_Symbol,currentTick.ask,sl,SlType,slp);
trade.SetExpertMagicNumber(MagicNum);
trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,lotSize,currentTick.ask,sl,tp,__FILE__);
}
// 空单开仓条件
bool bearSignal = cntSell==0 && RsiBuffer<=RSILevel && RsiBuffer>RSILevel;
if(UseMAFilter) bearSignal = bearSignal && currentTick.bid<MABuffer;
if(bearSignal) {
if(CloseSignal&&cntBuy>0) closePositions(MagicNum,POSITION_TYPE_BUY);
double sl = StopLoss==0 ? 0 : currentTick.bid + StopLoss * point;
double tp = TakeProfit==0 ? 0 : currentTick.bid - TakeProfit * point;
if(!NormalizePrice(_Symbol, sl)) return;
if(!NormalizePrice(_Symbol, tp)) return;
double slp = SlParam;
if(SlType==4) slp = calculateEachEaBalace(MagicNum, initBalance);//子账户百分之1
double lotSize = calculateLots(_Symbol,currentTick.bid,sl,SlType,slp);
trade.SetExpertMagicNumber(MagicNum);
trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,lotSize,currentTick.bid,sl,tp,__FILE__);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
if(RsiHandle!=INVALID_HANDLE) IndicatorRelease(RsiHandle);
if(MAHandle!=INVALID_HANDLE) IndicatorRelease(MAHandle);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool isNewBar(string symbol, ENUM_TIMEFRAMES timeframe, int& bTotal) {
int bars = iBars(symbol,timeframe);
if(bTotal == bars) return false;
bTotal = bars;
return true;
}
//-----------------------------------规范化价格-----------------------------------
// 交易品种symbol和一个引用类型的价格price
bool NormalizePrice(string symbol, double &price) {
// 获取交易品种symbol的最小价格波动大小tickSize
double tickSize=0;
if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE,tickSize)) {
Print("SYMBOL_TRADE_TICK_SIZE 获取异常!");
return false;
}
// 获取交易品种symbol的小数点位数digits
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
// 将价格price除以tickSize取整,然后再乘以tickSize得到规范化后的价格,并使用NormalizeDouble函数将其舍入到指定的小数点位数digits。
price = NormalizeDouble(MathRound(price/tickSize)*tickSize,digits);
return true;
}
//-----------------------------------平仓-----------------------------------
bool closePositions(long magicNum, ENUM_POSITION_TYPE positionType) {
// 获取当前已开仓的仓位数量
int total = PositionsTotal();
// 循环遍历所有开仓仓位。从最后一个仓位开始循环,直到第一个仓位(i=0)。
for(int i=total-1; i>=0; i--) {
// 获取第i个仓位的订单号,并将其赋值给positionTicket。每个订单都有唯一的订单号,该函数的参数i表示第i个订单。
ulong positionTicket = PositionGetTicket(i);
if(positionTicket<=0) {
Print("获取positionTicket失败");
return false;
}
// 根据订单编号选择指定持仓
if(!PositionSelectByTicket(positionTicket)) {
Print("选中持仓失败");
return false;
}
// 获取持仓的魔术号
long magic;
if(!PositionGetInteger(POSITION_MAGIC,magic)) {
Print ("获取魔术号失败");
return false;
}
// 判断持仓的魔术号是否与传入的魔术号相同
if(magic==magicNum) {
// 获取持仓的类型(买入/卖出)
long type;
if(!PositionGetInteger (POSITION_TYPE,type) ) {
Print("持仓的类型获取失败");
return false;
}
// 如果要关闭的仓位类型是多单,但是当前选择的仓位类型是空单,则跳过本次循环,继续下一个仓位的处理。
if(positionType==POSITION_TYPE_BUY && type==POSITION_TYPE_SELL) continue;
// 如果要关闭的仓位类型是空单,但是当前选择的仓位类型是多单,则跳过本次循环,继续下一个仓位的处理。
if(positionType==POSITION_TYPE_SELL && type==POSITION_TYPE_BUY) continue;
// 平仓 并且检查结果是否成功。如果不成功,则提示用户
trade.PositionClose(positionTicket);
if(trade.ResultRetcode()!=TRADE_RETCODE_DONE) {
Print("平仓失败 positionTicket:",(string)positionTicket," 异常:", (string)trade.ResultRetcode(), " :", trade.ResultRetcodeDescription());
return false;
}
}
}
return true;
}
//-----------------------------------计算仓位-----------------------------------
// symbol(品种名称), entryPrice(入场价格), slPrice(止损价格), slType(止损类型), slParam(止损参数)
double calculateLots(string symbol, double entryPrice, double slPrice, int slType, double slParam) {
double slMoney = 0;
if(slType == 1) slMoney = slParam; //1 固定止损金额, eg:100U
else if(slType == 2) slMoney = AccountInfoDouble(ACCOUNT_BALANCE) * slParam / 100; //2 账户余额百分比, eg:1%
else if(slType == 3) return slParam; //3 固定手数, eg:0.1
else if(slType == 4) slMoney = slParam; //4 子账户百分比, eg:1%
//计算止损距离
int digits = (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);//小数位数
double slDistance = NormalizeDouble(MathAbs(entryPrice - slPrice), digits) / point;
if(slDistance <= 0) return 0; //zero divide
//获取点值 每次尝试间隔1秒。如果尝试三次后仍然无法获取到交易点值,则打印异常信息,并返回0。
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickValue == 0) {
Sleep(1000);
tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
Print("SYMBOL_TRADE_TICK_VALUE异常第一次尝试:"+(string)tickValue);
if(tickValue == 0) {
Sleep(1000);
tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
Print("SYMBOL_TRADE_TICK_VALUE异常第二次尝试:"+(string)tickValue);
}
if(tickValue == 0) {
Sleep(1000);
tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
Print("SYMBOL_TRADE_TICK_VALUE异常第三次尝试:"+(string)tickValue);
}
if(tickValue == 0) {
Print("获取SYMBOL_TRADE_TICK_VALUE异常:",symbol,"-",entryPrice,"-",slPrice,"-",slType,"-",slParam);
Print("获取SYMBOL_TRADE_TICK_VALUE异常2:",point,"-",slDistance,"-",tickValue);
return 0;
}
}
// 计算手数
double lot = NormalizeDouble(slMoney / slDistance / tickValue, 2);//风控 / 止损 / 点值
// 规范手数 确保手数符合品种的步长要求
double lotstep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
lot = MathRound(lot / lotstep) * lotstep;
if(lot < SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN)) lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
else if(lot >= SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX)) {
Print("手数异常:",symbol,"-",entryPrice,"-",slPrice,"-",slType,"-",slParam);
Print("手数异常2:",point,"-",slDistance,"-",tickValue,"-",lot,"-",lotstep);
return 0;
}
return lot;
}
//-----------------------------------计算子账户余额-----------------------------------
// magicNum(用于筛选交易单的魔术数字)和initBa(初始余额)
double calculateEachEaBalace(long magicNum, double initBa) {
double countProfit = 0, countCommission = 0,countSwap = 0, countMoney = 0;
// 选择历史交易记录
HistorySelect(0,TimeCurrent());
// 获取历史交易总数
uint deals=HistoryDealsTotal();
// 遍历历史交易记录
for(uint i=0;i<deals;i++) {
// 获取特定索引处交易的订单号
ulong ticket = HistoryDealGetTicket(i);
if(ticket<=0) continue;
long magic = HistoryDealGetInteger(ticket,DEAL_MAGIC);
string symbol = HistoryDealGetString(ticket,DEAL_SYMBOL);
double profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);
double commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);
double swap = HistoryDealGetDouble(ticket,DEAL_SWAP);
// 累加全部的利润 手续费 库存费 得到该ea 的 绩效
if(magic==magicNum) {
countProfit+=profit;
countCommission+=commission;
countSwap+=swap;
countMoney = countProfit + countCommission + countSwap;
}
}
// 目前账户的余额是多少
double currentBalance = countMoney + initBa;
return currentBalance / 100;
}
//-----------------------------------统计该ea的多单和空单开仓数量-----------------------------------
// magicNum是要统计的特定魔术号
// countBuy和countSell是输出参数,分别用于存储多单和空单的开仓数量。在函数执行完毕后,它们会被更新。
// &countBuy和&countSell是引用参数。它们的作用是将函数内部的计算结果传递回函数外部。
// 值传递
bool countOpenPositions(long magicNum, int &countBuy,int &countSell) {
// 初始化count为0,用于统计多/空单开仓数量。
countBuy =0;
countSell=0;
// 获取当前已开仓的仓位数量
int total =PositionsTotal();
// 循环遍历所有开仓仓位。从最后一个仓位开始循环,直到第一个仓位(i=0)。
for(int i=total-1; i>=0; i--) {
// 获取第i个仓位的订单号,并将其赋值给positionTicket。每个订单都有唯一的订单号,该函数的参数i表示第i个订单。
ulong positionTicket = PositionGetTicket(i);
if(positionTicket<=0) {
Print("获取positionTicket失败");
return false;
}
// 根据订单编号选择指定持仓
if(!PositionSelectByTicket(positionTicket)) {
Print("选中持仓失败");
return false;
}
// 获取持仓的魔术号
long magic;
if(!PositionGetInteger(POSITION_MAGIC,magic)) {
Print ("获取魔术号失败");
return false;
}
// 判断持仓的魔术号是否与传入的魔术号相同
if(magic==magicNum) {
// 获取持仓的类型(买入/卖出)
long type;
if(!PositionGetInteger (POSITION_TYPE,type)) {
Print("持仓的类型获取失败");
return false;
}
// 买入持仓
if(type==POSITION_TYPE_BUY) countBuy++;
// 卖出持仓
if(type==POSITION_TYPE_SELL) countSell++;
}
}
return true;
}
//+------------------------------------------------------------------+
有用的话,给博主打赏一下[调皮] bucuo 支持一下 很好源码谢谢 感谢分享,不错 学习一下,希望有用!! 学习学习感谢 黄金策略,顺势而为, [偷笑] 怎么使用?加载到 H1的周期上吗? 感谢分享,有很多收获 移动止盈有吗,楼主 4390876545678 谢谢楼主的分享精神 ,收藏了 感谢分享,有很多收获! 多好的策略,遇到单边就over 谢谢分享精神 ,收藏了![微笑]
页:
[1]