市场噪声
市场噪声概念
它是指在金融市场上由不确定性、偶然因素或非有用信息引起的价格和交易量的随机波动。这些无意义的波动可能会让投资者对市场走势的真实情况产生误解,从而影响投资决策。
举个通俗的例子:市场噪声很像一个喝酒的人走路回家。设想一个醉汉从酒吧出来,家在酒吧不远处的正北方。由于喝醉了,他走路左摇右晃,甚至可能朝南走,每次步伐方向随机(左、右、前、后),但最终会到家,因为他知道需要向北走。
- 醉汉的步伐:代表市场噪声(短期随机波动)。
- 最终回家的方向:代表市场潜在趋势(长期方向)。
尽管市场短期可能上下波动(如醉汉左摇右晃),但长期会呈现明显趋势(如醉汉最终回家)。投资者需从噪声中筛选有用信息,看清市场真正趋势。
当噪声微小,市场趋势容易识别;噪声增加时,有意义的价格行为模糊,甚至变成随机运动,此时很多交易系统会失效。
噪声的成因与影响
为什么存在噪声?量化交易大师佩里·考夫曼(Perry Kaufman)
指出,噪声是市场无处不在的现象,无法消除,交易者必须学会应对。
- 价格行为分类:
- 有意义的价格行为:长期、有方向、有目的的运动(如从A点到B点)。
- 随机价格行为:短暂、无方向、无目的的波动(由大量交易者的短期操作引发)。
市场参与者在多个时间周期执行不同策略,短期操作的随机性导致噪声,而市场主导情绪推动长期趋势。
以趋势跟踪策略为例,噪声会导致策略失效:
- 噪声低时,策略能捕捉趋势并盈利;
- 噪声高时,价格频繁来回波动,吞噬利润,甚至导致亏损(如频繁假突破、来回止损)。
噪声的量化与应用
衡量噪声的方法之一是效率比(由考夫曼开发),用于量化市场噪声高低:
- 效率比高位:噪声低,适合趋势策略;
- 效率比低位:噪声高,适合震荡或区间策略。
通过计算价格密度(价格在区间内的填充程度),可判断噪声水平:
- 价格平稳地从区间一角到另一角:低噪声;
- 价格频繁填充整个区间:高噪声。
策略细节(以多单为例,空单条件反向)
进场条件
- 无多单持仓;
- 收盘价跌破布林带下沿;
- 噪声过滤器触发:效率比<0.3(高噪声环境)。
出场条件
- 有多单持仓;
- 收盘价涨破布林带上沿,于下一根K线开盘价平仓。
止损设置
基于布林带上下轨距离的百分比:
- 例:上下轨距离30点,止损比例50%,则止损15点(从开仓价向下计算)。
资金管理
固定手数或按风险比例计算,代码中包含持仓判断、价格规范等函数。
开仓图示
代码实现要点
主程序代码
cpp
#property strict
#include <Util/Util.mqh>
Util util;
// 不可以做参数优化的参数
input group "固定和输入通用部分"
sinput int MagicNum = 12345; // EA唯一编号
sinput int SlType = 1; // 止损方式(1:固定金额{100}美金 2:账户{1}% 3:固定{0.1}手 4:子账户总账户{1}%)
sinput int SlParam = 100; // 止损依据(根据上方【止损方式】的不同,所代表的含义也不同)
input group "参数优化"
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // 时间周期
input int BbPeriod = 20; // BB 周期
input int SlBBandsRange = 70; //SLBBandsRange(%)
input int efficiencyRatio = 10; // 效率比率(%)
int barsTotal;
int handle;
double upperBuffer[];
double baseBuffer[];
double lowerBuffer[];
int initBalance;
double point;
MqlTick currentTick;
int efficiencyRatioHandle;
double efficiencyRatioBuffer[];
int OnInit()
{
if (MagicNum < 0)
{
Alert("MagicNum<0");
return INIT_PARAMETERS_INCORRECT;
}
if (BbPeriod < 1)
{
Alert("RSIPeriod<1");
return INIT_PARAMETERS_INCORRECT;
}
handle = iBands(_Symbol,TimeFrame,BbPeriod,0,2,PRICE_CLOSE);
efficiencyRatioHandle = iCustom(_Symbol, TimeFrame,"Efficiency Ratio",efficiencyRatio,PRICE_CLOSE, 0.2);
if (handle == INVALID_HANDLE)
{
Alert((string)MagicNum + " ", _Symbol, " handle 创建失败");
return INIT_FAILED;
}
ArraySetAsSeries(upperBuffer, true);
ArraySetAsSeries(baseBuffer, true);
ArraySetAsSeries(lowerBuffer, true);
ArraySetAsSeries(efficiencyRatioBuffer, true);
Print(MagicNum, " ", _Symbol, "实例化成功");
// 当用户没有打开参数对应的图表周期,自动帮他打开对应周期
if (MQLInfoInteger(MQL_TESTER))
{
ChartSetInteger(0,CHART_SHOW_GRID,false);
}else{
ChartSetSymbolPeriod(0,_Symbol, TimeFrame);
}
initBalance = SlParam;
Comment("MagicNum:", MagicNum, " SlType: ", SlType, " SlParam: ", SlParam);
return INIT_SUCCEEDED;
}
// 节省内存后 买VPS就可以买小一点
void OnDeinit(const int reason)
{
if (handle != INVALID_HANDLE)
{
IndicatorRelease(handle);
}
}
void OnTick()
{
if (!util.isNewBar(_Symbol, TimeFrame, barsTotal))
return;
if(!util.isEnableTimezone()) return; // 点差过大0 不交易
datetime a = StringToTime("2012.01.6 5:00:00");
if(iTime(_Symbol,PERIOD_CURRENT,0) == StringToTime("2012.01.6 5:00:00")){
Print("debug");
}
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int) SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
if (!SymbolInfoTick(_Symbol, currentTick))
{
Print("tick数据获取异常");
}
CopyBuffer(handle, 1, 0, 2, upperBuffer);
CopyBuffer(handle, 2, 0, 2, lowerBuffer);
CopyBuffer(efficiencyRatioHandle, 0, 0, 2, efficiencyRatioBuffer);
int cntBuy, cntSell;
if (!util.countOpenPositions(MagicNum, cntBuy, cntSell))
return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double bullSignal = cntBuy==0 && iClose(_Symbol,TimeFrame,1) <= lowerBuffer[1] && MathAbs(efficiencyRatioBuffer[1])<0.2;
if (bullSignal)
{
double bbRange = upperBuffer[1] - lowerBuffer[1];
double slPoint = NormalizeDouble(bbRange*0.01 * SlBBandsRange,digits);
double sl = currentTick.ask -slPoint;
double tp = 0;
if (!util.NormalizePrice(_Symbol, sl))
return;
if (!util.NormalizePrice(_Symbol, tp))
return;
double slp = SlParam;
if (SlType == 4)
{
slp = util.calculateEachEaBalance(MagicNum, initBalance); // 子账户百分之一
}
double lotSize = util.calcLots(_Symbol, ask, sl, SlType, slp);
trade.SetExpertMagicNumber(MagicNum);
trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, lotSize, currentTick.ask, sl, tp, "BBR BUY");
}
bool bearSignal = cntSell ==0 && iClose(_Symbol,TimeFrame,1) >= upperBuffer[1] && MathAbs(efficiencyRatioBuffer[1])<0.2;
if (bearSignal)
{
double bbRange = upperBuffer[1] - lowerBuffer[1];
double slPoint = NormalizeDouble(bbRange*0.01 * SlBBandsRange,digits);
double sl = currentTick.bid + slPoint;
double tp = 0;
if (!util.NormalizePrice(_Symbol, sl))
return;
if (!util.NormalizePrice(_Symbol, tp))
return;
double slp = SlParam;
if (SlType == 4)
{
slp = util.calculateEachEaBalance(MagicNum, initBalance); // 子账户百分之一
}
double lotSize = util.calcLots(_Symbol, bid, sl, SlType, slp);
trade.SetExpertMagicNumber(MagicNum);
trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, lotSize, currentTick.bid, sl, tp, "BBR SELL");
}
// 出场
if(!util.countOpenPositions(MagicNum, cntBuy, cntSell)) return;
if(cntBuy >0 && iClose(_Symbol,TimeFrame,1)>= upperBuffer[1] ){
util.closePositions(MagicNum, POSITION_TYPE_BUY);
}
if(cntSell >0 && iClose(_Symbol,TimeFrame,1)<= lowerBuffer[1] ){
util.closePositions(MagicNum, POSITION_TYPE_SELL);
}
}
副图效率指标源码
cpp
//+------------------------------------------------------------------
#property copyright "mladen"
#property link "mladenfx@gmail.com"
#property link "https://www.mql5.com"
#property description "Directional efficiency ratio"
//+------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots 1
#property indicator_label1 "Efficiency ratio"
#property indicator_type1 DRAW_COLOR_LINE
#property indicator_color1 clrDarkGray,clrDeepSkyBlue,clrOrangeRed
#property indicator_width1 2
//--- input parameters
enum enMaTypes
{
ma_sma, // Simple moving average
ma_ema, // Exponential moving average
ma_smma, // Smoothed MA
ma_lwma // Linear weighted MA
};
input int inpPeriod = 14; // ER period
input ENUM_APPLIED_PRICE inpPrice = PRICE_CLOSE; // Price
input double inpLevels = 0.25; // Levels for trending/ranging
input int inpSmoothingPeriod = 5; // Smoothing period
input enMaTypes inpMaMethod = ma_ema; // Smoothing method
//--- buffers and global variables declarations
double val[],valc[],diff[],prices[];
string _avgNames[]={"SMA","EMA","SMMA","LWMA"};
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
SetIndexBuffer(0,val,INDICATOR_DATA);
SetIndexBuffer(1,valc,INDICATOR_COLOR_INDEX);
SetIndexBuffer(2,diff,INDICATOR_CALCULATIONS);
SetIndexBuffer(3,prices,INDICATOR_CALCULATIONS);
IndicatorSetInteger(INDICATOR_LEVELS,2);
IndicatorSetDouble(INDICATOR_LEVELVALUE,0,inpLevels);
IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-inpLevels);
//---
IndicatorSetString(INDICATOR_SHORTNAME,"Efficiency ratio ("+_avgNames[inpMaMethod]+") ("+(string)inpPeriod+")");
return (INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator de-initialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(Bars(_Symbol,_Period)<rates_total) return(prev_calculated);
int i=(int)MathMax(prev_calculated-1,1); for(; i<rates_total && !_StopFlag; i++)
{
prices[i] = getPrice(inpPrice,open,close,high,low,i,rates_total);
diff[i] = (i>0) ? prices[i]-prices[i-1] : 0;
//-------------
double _noise = MathAbs(diff[i]); int k=1; for(; k<inpPeriod && (i-k)>=0; k++) _noise+=MathAbs(diff[i-k]);
double _efr = (_noise!=0 && i>inpPeriod) ? (prices[i]-prices[i-inpPeriod+1])/_noise : 0;
val[i] = iCustomMa(inpMaMethod,_efr,inpSmoothingPeriod,i,rates_total,0);
valc[i]= (val[i]>inpLevels) ? 1 :(val[i]<-inpLevels) ? 2 : 0;
}
return (i);
}
//+------------------------------------------------------------------+
//| Custom functions |
//+------------------------------------------------------------------+
#define _maInstances 1
#define _maWorkBufferx1 1*_maInstances
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double iCustomMa(int mode,double price,double length,int r,int bars,int instanceNo=0)
{
switch(mode)
{
case ma_sma : return(iSma(price,(int)length,r,bars,instanceNo));
case ma_ema : return(iEma(price,length,r,bars,instanceNo));
case ma_smma : return(iSmma(price,(int)length,r,bars,instanceNo));
case ma_lwma : return(iLwma(price,(int)length,r,bars,instanceNo));
default : return(price);
}
}
//
//
//
//
//
double workSma[][_maWorkBufferx1];
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double iSma(double price,int period,int r,int _bars,int instanceNo=0)
{
if(ArrayRange(workSma,0)!=_bars) ArrayResize(workSma,_bars);
workSma[r][instanceNo]=price;
double avg=price; int k=1; for(; k<period && (r-k)>=0; k++) avg+=workSma[r-k][instanceNo];
return(avg/(double)k);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double workEma[][_maWorkBufferx1];
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double iEma(double price,double period,int r,int _bars,int instanceNo=0)
{
if(ArrayRange(workEma,0)!=_bars) ArrayResize(workEma,_bars);
workEma[r][instanceNo]=price;
if(r>0 && period>1)
workEma[r][instanceNo]=workEma[r-1][instanceNo]+(2.0/(1.0+period))*(price-workEma[r-1][instanceNo]);
return(workEma[r][instanceNo]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double workSmma[][_maWorkBufferx1];
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double iSmma(double price,double period,int r,int _bars,int instanceNo=0)
{
if(ArrayRange(workSmma,0)!=_bars) ArrayResize(workSmma,_bars);
workSmma[r][instanceNo]=price;
if(r>1 && period>1)
workSmma[r][instanceNo]=workSmma[r-1][instanceNo]+(price-workSmma[r-1][instanceNo])/period;
return(workSmma[r][instanceNo]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double workLwma[][_maWorkBufferx1];
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double iLwma(double price,double period,int r,int _bars,int instanceNo=0)
{
if(ArrayRange(workLwma,0)!=_bars) ArrayResize(workLwma,_bars);
workLwma[r][instanceNo] = price; if(period<1) return(price);
double sumw = period;
double sum = period*price;
for(int k=1; k<period && (r-k)>=0; k++)
{
double weight=period-k;
sumw += weight;
sum += weight*workLwma[r-k][instanceNo];
}
return(sum/sumw);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double getPrice(ENUM_APPLIED_PRICE tprice,const double &open[],const double &close[],const double &high[],const double &low[],int i,int _bars)
{
switch(tprice)
{
case PRICE_CLOSE: return(close[i]);
case PRICE_OPEN: return(open[i]);
case PRICE_HIGH: return(high[i]);
case PRICE_LOW: return(low[i]);
case PRICE_MEDIAN: return((high[i]+low[i])/2.0);
case PRICE_TYPICAL: return((high[i]+low[i]+close[i])/3.0);
case PRICE_WEIGHTED: return((high[i]+low[i]+close[i]+close[i])/4.0);
}
return(0);
}
//+------------------------------------------------------------------+