这是系列教学的最后一篇文章,不知大家学会了多少,想进一步交流的加学长的微信:u31u31。
下面是课程中的自定义指标源代码:
#property copyright "tudaojiaoyi"
#property indicator_chart_window // 指定指标在主图表窗口中显示
#property indicator_buffers 3 // 指定指标使用的缓冲区数量 (中轨, 上轨, 下轨)
#property indicator_color1 DeepSkyBlue // 设置第一个缓冲区(EMA)的颜色
#property indicator_color2 DeepSkyBlue // 设置第二个缓冲区(UpperBand)的颜色
#property indicator_color3 DeepSkyBlue // 设置第三个缓冲区(LowerBand)的颜色
// ===== 外部输入参数 =====
// 用户可以在指标属性中修改这些值
extern int BandsPeriod = 20; // 均线和标准差的计算周期
extern int BandsShift = 0; // 指标整体水平位移(K线根数)
extern int BandsMethod = MODE_EMA; // 移动平均线的计算方法 (EMA: 指数移动平均)
extern int BandsPrice = PRICE_CLOSE; // 计算所使用的价格类型 (收盘价)
extern double Deviations = 2.0; // 标准差的倍数,用于计算上下轨的宽度
// ===== 指标缓冲区 =====
// 这些数组用于存储指标线上每个点的值
double EMA[]; // 存储中轨(指数移动平均线)的值
double UpperBand[]; // 存储上轨的值
double LowerBand[]; // 存储下轨的值
// ===== 初始化函数 =====
// 在指标首次加载到图表时执行一次,用于设置指标属性
int OnInit()
{
// --- 设置中轨线 (第0个缓冲区) ---
SetIndexStyle(0, DRAW_LINE); // 设置为画线模式
SetIndexBuffer(0, EMA); // 将第0个缓冲区与EMA数组关联
SetIndexLabel(0, "EMA (" + BandsPeriod + ")"); // 设置在数据窗口中显示的标签
// --- 设置上轨线 (第1个缓冲区) ---
SetIndexStyle(1, DRAW_LINE);
SetIndexBuffer(1, UpperBand);
SetIndexLabel(1, "Upper Band (" + DoubleToStr(Deviations, 1) + " SD)"); // SD: Standard Deviation
// --- 设置下轨线 (第2个缓冲区) ---
SetIndexStyle(2, DRAW_LINE);
SetIndexBuffer(2, LowerBand);
SetIndexLabel(2, "Lower Band (" + DoubleToStr(Deviations, 1) + " SD)");
// 如果设置了水平位移
if(BandsShift != 0)
{
SetIndexShift(0, BandsShift); // 将中轨线水平移动指定的K线数
SetIndexShift(1, BandsShift); // 将上轨线水平移动指定的K线数
SetIndexShift(2, BandsShift); // 将下轨线水平移动指定的K线数
}
// 设置在图表左上角显示的指标简称
IndicatorShortName("EMA Bands (" + BandsPeriod + "," + DoubleToStr(Deviations, 1) + ")");
return(INIT_SUCCEEDED); // 返回初始化成功信号
}
// ===== 反初始化函数 =====
// 在指标从图表移除时执行,可用于释放资源
void OnDeinit(const int reason)
{
// 此处无特殊操作
}
// ===== 主计算函数 =====
// 每当有新的报价(tick)或图表刷新时,此函数会被调用
int OnCalculate(const int rates_total, // 图表上总的K线数
const int prev_calculated, // 上次调用此函数时已计算的K线数
const datetime &time[], // K线时间数组
const double &open[], // 开盘价数组
const double &high[], // 最高价数组
const double &low[], // 最低价数组
const double &close[], // 收盘价数组
const long &tick_volume[], // Tick成交量数组
const long &volume[], // 真实成交量数组
const int &spread[]) // 点差数组
{
// 如果K线总数不足以计算周期,则直接返回
if(rates_total < BandsPeriod) return(0);
int start_pos; // 定义计算的起始位置
// 确定本次计算的起始K线索引,这是为了优化性能,避免重复计算历史数据
if(prev_calculated == 0)
start_pos = rates_total - 1; // 首次计算:从图表最右侧(最新的K线)开始,一直算到最左侧
else
start_pos = rates_total - prev_calculated; // 后续调用:仅计算新生成的K线
// 循环计算每个K线的指标值,从最新的K线向旧的K线方向计算
for(int i = start_pos; i >= 0; i--)
{
// 调用内置iMA函数计算当前K线(i)的移动平均值
EMA[i] = iMA(NULL, 0, BandsPeriod, 0, BandsMethod, BandsPrice, i);
// 调用内置iStdDev函数计算当前K线(i)的标准差
double StdDev = iStdDev(NULL, 0, BandsPeriod, 0, BandsMethod, BandsPrice, i);
// 计算上轨 = 中轨 + (标准差 * 倍数)
UpperBand[i] = EMA[i] + (StdDev * Deviations);
// 计算下轨 = 中轨 - (标准差 * 倍数)
LowerBand[i] = EMA[i] - (StdDev * Deviations);
}
// 返回已计算的K线总数,供下次调用时作为 prev_calculated 的值
return(rates_total);
}