English Русский Español Deutsch 日本語 Português
preview
威廉·甘恩(William Gann)方法(第一部分):创建甘恩角度指标

威廉·甘恩(William Gann)方法(第一部分):创建甘恩角度指标

MetaTrader 5交易 | 12 三月 2025, 12:26
450 0
Yevgeniy Koshtenko
Yevgeniy Koshtenko

概述

威廉·德尔伯特·甘恩(William Delbert Gann)是一位传奇交易员和技术分析大师,他创新的市场分析方法至今仍吸引着现代交易者的关注。甘恩最著名的工具之一是他的角度分析法,用于预测价格走势并识别潜在的支撑位和阻力位。

在本文中,我们将深入探索威廉·甘恩的交易方法,从在MQL5中创建甘恩角度指标开始。我们将深入探讨这一工具背后的理论,并逐步将其作为自定义指标实现到MetaTrader 5平台中。

无论你是正在寻找新的分析工具的经验丰富的交易者,还是希望扩展技术指标库的初学者,本文都将帮助你更好地理解和应用威廉·甘恩最引人入胜的方法之一,以提升你的交易能力。


甘恩角度的创建历史

威廉·德尔伯特·甘恩(1878-1955)在20世纪初基于多年对市场走势的研究以及他对时间和价格关系的独特理解,开发了他的角度系统。

甘恩认为市场以可预测的几何模式运动,而这些运动可以通过数学、几何学和星象学的结合来预测。他提出了角度的概念,这些角度是图表上的对角线,反映了时间和价格运动之间的完美平衡。

甘恩理论的核心观点是,45度角(即1x1角)代表时间和价格之间的完美平衡。他认为,当价格以这个角度上升或下降时,表明趋势是平衡且稳定的。

甘恩还开发了其他角度,如2x1、3x1、4x1及其倒数,这些角度代表时间和价格之间的不同关系。这些角度成为他交易系统和市场分析的基础。

尽管甘恩的部分理论仍存在争议,但他的方法,包括甘恩角度,仍然吸引着全球交易者和分析师的关注,并在现代交易中保持相关性。


甘恩角度理论及其在技术分析中的重要性

甘恩角度理论基于一个概念,即市场运动遵循可以通过图表上的特殊角度绘制来识别的可预测几何模式。该理论基于时间和价格之间的平衡,其中1x1角(45度)代表完美平衡,假设价格在每个时间段内变化一个单位。

甘恩开发了一个不同的角度系统,如2x1、3x1、4x1及其倒数,每个角度都反映了时间和价格运动之间的特定关系。 这些角度作为动态支撑位和阻力位,帮助交易者判断趋势的强度和方向。较陡的角度表示趋势较强,而较平的角度表示运动较弱。


构建甘恩角度的基本原则


甘恩角度的构建基于几个关键原则,这些原则使交易者能够有效地在技术分析中使用这一工具。选择起点(通常是图表上的一个重要最低点或最高点)是最重要的。基于这一点开始构建角度。

基础角度被认为是1x1角,它在图表上形成一条45度线。这个角度反映了时间和价格之间的完美平衡,即价格在一个时间段内变化一个单位。其他角度,如2x1、3x1、4x1及其倒数,是相对于这个基础角度构建的。

在绘制时,需要注意图表的比例。交易者通常使用特殊的模板或工具来确保角度的准确性。角度线会延伸到未来,从而可以预测潜在的支撑位和阻力位。


甘恩角度的类型及其解释

威廉·甘恩开发了一个角度系统,每个角度在技术分析中都有其独特的含义和解释。1x1角(45度)被认为是主要角度,反映了时间和价格之间的平衡。这个角度是评估趋势强度的基本指导线。

2x1角(63.75度)表示价格运动更强,价格的上升速度是时间的两倍。这通常被视为强劲看涨趋势的迹象。相比之下,1x2角(26.25度)表示价格相对于时间的上升速度较慢,可能表明趋势正在减弱。

3x1(71.25度)和4x1(75度)角度代表更激进的价格走势,通常与非常强劲的趋势或潜在的市场过热有关。它们的倒数—1x3(18.75度)和1x4(15度)—可能表明强劲的阻力或支撑。

甘恩角度的解释不仅限于其斜率本身。价格与这些线的互动也很重要。价格穿过角度线可能表明趋势可能发生潜在变化。如果价格沿着角度线运动,则通常被解释为当前趋势强度的确认。


甘恩角度在交易中的实际应用

交易者将这些工具用于多种目的,从识别趋势到选择仓位的入场和出场点。

为了确定趋势,通常从一个重要最低点或最高点开始绘制1x1角。如果价格高于这条线,则被解释为上升趋势;如果低于这条线,则为下降趋势。2x1或3x1等较陡的角度用于确认趋势的强度。

在选择入场点时,许多交易者会寻找价格从甘恩角度线反弹或突破的时刻。例如,价格在趋势方向上从1x1线反弹,可以被视为潜在的入场机会。

在风险管理中,甘恩角度常用于设置止损。交易者可以在多头仓位的最近角度线下方或空头仓位的上方放置止损。

在长期交易中,甘恩角度有助于确定市场的总体方向。我们可以使用较平的角度,如1x2或1x4,来评估长期趋势并做出战略决策。


使用甘恩角度的交易策略示例

甘恩角度为交易者提供了广泛的选项,可用于创建多种交易策略。以下是一些在实际交易中有效使用甘恩角度的示例。

角度反弹策略(Angle Bounce Strategy)基于甘恩角度线通常作为支撑位或阻力位的假设。交易者寻找价格接近甘恩角度线(尤其是1x1或2x1)并在此反弹的情况。在确认反弹后(例如,形成反转K线图模式),交易者会进入仓位。

角度突破策略(Angle Breakout Strategy)是另一种流行的策略。交易者等待价格突破重要的甘恩角度线,尤其是当这种突破伴随着交易量增加时。向上突破可能表明潜在的多头机会,而向下突破可能表明空头机会。

甘恩扇形策略(Gann Fan Strategy)使用从单一点辐射出的多条角度线,形成扇形结构。交易者分析价格与扇形线的互动,从而确定支撑位、阻力位和潜在反转点。

结合角度和时间周期策略(Combining Angles and Time Cycles)是一种更复杂的策略,交易者将甘恩角度与甘恩开发的时间周期概念相结合。分析重要时间线与甘恩角度的交点,以确定进入或退出市场的关键时机。

多时间框架策略(Multi-Timeframe Strategy)涉及在不同时间框架上分析甘恩角度。例如,交易者可以使用日线图上的1x1角度来确定总体趋势,然后切换到小时图,使用更陡的角度寻找入场点。


在MQL5中创建甘恩角度指标:基本步骤


在MQL5中创建甘恩角度指标涉及几个关键步骤。这个过程需要理解甘恩角度的构建原理以及MetaTrader 5编程环境的特性。

第一步是定义指标结构。在此,我们设置基本参数,例如指标名称、用于设置角度的输入参数以及所需的库。

构建甘恩角度的核心逻辑位于OnCalculate()函数中。在此,我们定义绘制角度的起点,计算每个角度的坐标,并在图表上绘制线条。

考虑到图表的比例和选定的时间区间,正确计算角度的坐标至关重要。这需要精确的数学方法和对甘恩角度几何学的理解。

最后一步是测试和调试指标。需要在不同的时间框架和交易品种上验证角度的构建是否正确。


指标代码结构

以下是可以在MetaTrader 5中使用的甘恩角度指标的基本代码:

#property copyright "Copyright 2024, Evgeniy Shtenco"
#property link      "https://www.mql5.com/en/users/koshtenko"
#property version   "1.00"
#property indicator_chart_window

// Input parameters
input datetime StartDate = D'2023.01.01 00:00'; // Start date for analysis
input datetime EndDate = D'2023.12.31 23:59';   // End date for analysis
input color GannFanColor = clrBlue;             // Color for Gann Fan lines

// Global variables
double extremumPrice;
datetime extremumTime;
double oppositeExtremumPrice;
datetime oppositeExtremumTime;
bool isTrendUp;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, "GannFan_");
    ObjectsDeleteAll(0, "OppositeGannFan_");
}

//+------------------------------------------------------------------+
//| 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(rates_total < 1) return(0);

    // Clear previous objects
    if (prev_calculated == 0)
    {
        ObjectsDeleteAll(0, "GannFan_");
        ObjectsDeleteAll(0, "OppositeGannFan_");
    }

    // Find extremums within the specified date range
    FindExtremums(rates_total, high, low, time);

    // Draw Gann Fans
    DrawGannFan(extremumPrice, extremumTime);
    DrawOppositeGannFan(oppositeExtremumPrice, oppositeExtremumTime);

    return(rates_total);
}

//+------------------------------------------------------------------+
//| Find both extremums within the specified date range              |
//+------------------------------------------------------------------+
void FindExtremums(const int rates_total, const double &high[], const double &low[], const datetime &time[])
{
    int startIndex = -1;
    int endIndex = -1;

    for (int i = 0; i < rates_total; i++)
    {
        if (time[i] >= StartDate && startIndex == -1)
        {
            startIndex = i;
        }
        if (time[i] <= EndDate)
        {
            endIndex = i;
        }
    }

    if (startIndex == -1 || endIndex == -1 || startIndex > endIndex)
    {
        Print("Error: Invalid date range or no data available in the specified range");
        return;
    }

    int highestIndex = ArrayMaximum(high, startIndex, endIndex - startIndex + 1);
    int lowestIndex = ArrayMinimum(low, startIndex, endIndex - startIndex + 1);

    // Determine the most recent extremum
    if (time[highestIndex] > time[lowestIndex])
    {
        extremumPrice = high[highestIndex];
        extremumTime = time[highestIndex];
        oppositeExtremumPrice = low[lowestIndex];
        oppositeExtremumTime = time[lowestIndex];
        isTrendUp = false;
    }
    else
    {
        extremumPrice = low[lowestIndex];
        extremumTime = time[lowestIndex];
        oppositeExtremumPrice = high[highestIndex];
        oppositeExtremumTime = time[highestIndex];
        isTrendUp = true;
    }
}

//+------------------------------------------------------------------+
//| Draw Gann Fan                                                    |
//+------------------------------------------------------------------+
void DrawGannFan(double extremum, datetime extremumTime)
{
    double angles[] = {82.5, 75, 71.25, 63.75, 45, 26.25, 18.75, 15, 7.5};
    string angleNames[] = {"1x8", "1x4", "1x3", "1x2", "1x1", "2x1", "3x1", "4x1", "8x1"};

    datetime endTime = extremumTime + PeriodSeconds() * 300;

    for(int i = 0; i < ArraySize(angles); i++)
    {
        string label = "GannFan_" + angleNames[i];
        double angle = angles[i];
        
        double priceShift = MathTan(angle * M_PI / 180.0) * 300 * _Point;
        double endPrice;
        
        if(isTrendUp)
        {
            endPrice = extremum + priceShift;
        }
        else
        {
            endPrice = extremum - priceShift;
            angle = -angle; // Invert the angle for a downtrend
        }

        if(ObjectCreate(0, label, OBJ_TREND, 0, extremumTime, extremum, endTime, endPrice))
        {
            ObjectSetInteger(0, label, OBJPROP_COLOR, GannFanColor);
            ObjectSetInteger(0, label, OBJPROP_STYLE, STYLE_SOLID);
            ObjectSetInteger(0, label, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, label, OBJPROP_RAY_RIGHT, true);
            ObjectSetString(0, label, OBJPROP_TOOLTIP, "Gann Fan " + angleNames[i]);
        }
        else
        {
            Print("Failed to create Gann Fan line: ", GetLastError());
        }
    }
}

//+------------------------------------------------------------------+
//| Draw Opposite Gann Fan                                           |
//+------------------------------------------------------------------+
void DrawOppositeGannFan(double extremum, datetime extremumTime)
{
    double angles[] = {82.5, 75, 71.25, 63.75, 45, 26.25, 18.75, 15, 7.5};
    string angleNames[] = {"1x8", "1x4", "1x3", "1x2", "1x1", "2x1", "3x1", "4x1", "8x1"};

    datetime endTime = extremumTime + PeriodSeconds() * 300;

    for(int i = 0; i < ArraySize(angles); i++)
    {
        string label = "OppositeGannFan_" + angleNames[i];
        double angle = angles[i];
        
        double priceShift = MathTan(angle * M_PI / 180.0) * 300 * _Point;
        double endPrice;
        
        if(!isTrendUp) // Opposite trend
        {
            endPrice = extremum + priceShift;
        }
        else
        {
            endPrice = extremum - priceShift;
            angle = -angle; // Invert the angle for a downtrend
        }

        if(ObjectCreate(0, label, OBJ_TREND, 0, extremumTime, extremum, endTime, endPrice))
        {
            ObjectSetInteger(0, label, OBJPROP_COLOR, GannFanColor);
            ObjectSetInteger(0, label, OBJPROP_STYLE, STYLE_SOLID);
            ObjectSetInteger(0, label, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, label, OBJPROP_RAY_RIGHT, true);
            ObjectSetString(0, label, OBJPROP_TOOLTIP, "Opposite Gann Fan " + angleNames[i]);
        }
        else
        {
            Print("Failed to create Opposite Gann Fan line: ", GetLastError());
        }
    }
}

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
    // Redraw objects when chart changes
    if(id == CHARTEVENT_CHART_CHANGE)
    {
        // Find extremums and redraw Gann Fans
        int rates_total = Bars(_Symbol, PERIOD_CURRENT);
        double high[], low[];
        datetime time[];
        ArraySetAsSeries(high, true);
        ArraySetAsSeries(low, true);
        ArraySetAsSeries(time, true);
        CopyHigh(_Symbol, PERIOD_CURRENT, 0, rates_total, high);
        CopyLow(_Symbol, PERIOD_CURRENT, 0, rates_total, low);
        CopyTime(_Symbol, PERIOD_CURRENT, 0, rates_total, time);
        
        FindExtremums(rates_total, high, low, time);
        DrawGannFan(extremumPrice, extremumTime);
        DrawOppositeGannFan(oppositeExtremumPrice, oppositeExtremumTime);
    }
}


主要函数及其作用

该段代码表示一个MetaTrader 5指标,用于在图表上绘制甘恩扇形角度。以下是主要函数及其作用的描述:

  1. OnInit()是指标的初始化函数。在此情况下,它仅返回一个成功的初始化结果。
  2. OnDeinit()是一个去初始化函数,当指标从图表中移除时,它会删除指标创建的所有对象。
  3. OnCalculate() 是指标的主函数。它会在每个tick时被调用。它会清除之前的对象,在给定日期范围内寻找极值,并绘制甘恩角度。
  4. FindExtremums()是一个用于在给定日期范围内寻找价格极值(最高点和最低点)的函数。它确定哪个极值更接近当前时间,并确定趋势的方向。
  5. DrawGannFan()是一个从找到的极值点绘制主要甘恩扇形的函数。它会创建九条不同角度的线。
  6. DrawOppositeGannFan()是一个从另一个极值点绘制相反方向甘恩扇形的函数。它也会创建九条角度线,但方向相反。
  7. OnChartEvent()是一个响应图表事件的函数。在这种情况下,当图表发生变化时,它会重新绘制甘恩扇形。

该指标使用输入参数来设置分析的日期范围(StartDate 和 EndDate)以及线条的颜色(GannFanColor)。它会在该范围内寻找价格极值,确定趋势方向,并绘制两个甘恩扇形——一个从最近的极值点出发,另一个从相反的极值点出发。每个扇形包含九条线,分别对应不同的甘恩角度(82.5°、75°、71.25°、63.75°、45°、26.25°、18.75°、15° 和 7.5°)。

代码还包含错误处理功能,并且在图表发生变化时能够动态更新,这使得它相当稳健且能适应不同的市场条件。通过使用MQL5函数(如ArrayMaximum()和ArrayMinimum()),代码能够高效地处理数据。

甘恩角度计算算法

甘恩角度的计算算法是基于从极值点进行几何构造的线条。以下是该算法的关键要点:

  • 确定极值点:指标会在给定的时间范围内找到最高价和最低价。较晚出现的极值点用于作为主要甘恩扇形的起点,而较早出现的极值点用于相反方向的扇形。
  • 角度集合:使用一组固定的甘恩角度(82.5°、75°、71.25°、63.75°、45°、26.25°、18.75°、15° 和 7.5°)。这些角度对应于传统的甘恩比例:1x8、1x4、1x3、1x2、1x1、2x1、3x1、4x1 和 8x1。
  • 对于每个角度,计算线条的终点。计算公式如下:计算公式如下:
终点价格 = 极值点 + MathTan(角度 × M_PI/180.0) × 300× Point
  • 这里,300是一个常规的向前延伸的K线数量,用于构建线条。
  • 下降趋势的角度反转:如果趋势向下,角度会被反转(改变符号),使得线条从极值点向下延伸。
  • 线条构建:对于每个角度,创建一个线条对象(OBJ_TREND),从极值点延伸到计算出的终点。线条会向右延伸以覆盖未来数据。
  • 双重扇形:算法会分别应用于两个扇形:一次用于从最近的极值点构建主要扇形,第二次用于从较早的极值点构建相反方向的扇形。这使得我们可以在两侧可视化潜在的支撑位和阻力位。
  • 动态更新:当图表发生变化(例如,新数据到达时),算法会重新计算极值点并重建扇形,确保分析始终是最新的。

该算法能够在图表上可视化经典的甘恩角度,为交易者提供分析潜在支撑位、阻力位和趋势方向的工具。 


能否实现盈利交易?

能否使用甘恩角度实现盈利交易?这个问题没有明确的答案,但采用正确的方法,盈利交易是完全可能的。成功的关键在于综合使用这一工具。

然而,即使是最仔细和细致的分析,也不能忘记风险管理。这是盈利交易的基石。没有任何方法能够保证100%的成功,因此合理的资金管理和风险管理在长期盈利中起着重要作用。


基于甘恩角度的EA开发前景


开发基于甘恩角度的EA是算法交易系统开发者的一项有趣且有前景的任务。这样的EA可以自动化地应用甘恩理论中的原则进行分析和交易。

基于甘恩角度的EA基础可以是已经实现的甘恩角度指标。EA算法可以包括对价格与甘恩线互动的分析,根据这些线的交叉点确定入场和出场点,同时考虑交易量和市场波动等额外因素。

基于甘恩角度的自动化系统的一个关键优势是消除交易决策中的情绪因素。EA可以严格遵循既定策略,不会受到恐惧或贪婪的影响,这些情绪往往会影响人类交易者的决策。

然而,开发这样的EA面临一系列的挑战。首先,需要准确解释甘恩角度生成的信号。甘恩理论在很大程度上是主观的,需要对市场有深入的理解,这很难完全以算法的形式实现。

当然,这种EA的成功在很大程度上取决于开发者对甘恩理论的理解深度,以及他们将这一理论与现代市场分析和风险管理方法有效结合的能力。


最大挑战:角度缩放问题

创建和使用基于甘恩角度的指标和EA时,最严重的问题之一是缩放的困难。这个问题显著影响基于甘恩方法的交易系统的准确性和效率。

问题的本质在于,甘恩角度作为一种几何构造,高度依赖于图表的比例尺。当时间轴或价格轴的比例发生变化时,角度的视觉表示可能会严重失真。在一个比例下看起来是完美的1x1(45度)角度,当比例改变时,可能会变成完全不同的角度。

这给软件实现带来了严重的困难。指标或EA需要不断考虑当前图表的比例,并相应调整角度计算。此外,甘恩角度所基于的“时间单位等于价格单位”的概念在处理不同的金融工具和时间框架时变得模糊不清。

解决这一问题的尝试往往导致妥协。一些开发者会固定比例,这限制了工具的适用性。另一些人引入复杂的角度重新计算算法,可能会降低指标或EA的速度。

此外,缩放问题使得在不同交易品种或时间框架之间比较分析结果变得困难。在一个货币对的日线图上有效工作的角度,在小时图上或针对另一种交易品种时可能会产生完全不同的结果。

这一问题质疑了甘恩角度在自动化交易中的普遍适用性。它要求开发者不仅要深入理解甘恩理论,还要了解交易平台上图表操作的具体细节。


结论

甘恩角度是一种独特的技术分析工具,仍然吸引着交易者和交易系统开发者的关注。我们的回顾表明,在MQL5中创建甘恩角度指标是完全可行的,而提供的代码为进一步开发提供了一个很好的起点。

然而,在自动化交易中使用甘恩角度面临着一系列重大挑战。其中最主要的是缩放问题,这使得基于这种方法创建一个通用且可靠的交易EA变得更加困难。

尽管存在这些困难,甘恩角度作为一种市场分析工具的潜力仍然很高。如果使用正确,并与其他技术分析方法和适当的风险管理相结合,那么可以成为交易者工具箱中一个有效的补充。

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/15556

附加的文件 |
从基础到中级:变量 (III): 从基础到中级:变量 (III):
今天,我们将学习如何使用预定义的 MQL5 语言变量和常量。此外,我们将分析另一种特殊类型的变量:函数。知道如何正确使用这些变量可能意味着一个有效的应用程序和一个无效的应用程序之间的区别。为了理解这里介绍的内容,有必要理解前几篇文章中讨论的材料。
您应当知道的 MQL5 向导技术(第 25 部分):多时间帧测试和交易 您应当知道的 MQL5 向导技术(第 25 部分):多时间帧测试和交易
默认情况下,由于组装类中使用了 MQL5 代码架构,故基于多时间帧策略,且由向导组装的智能系统无法进行测试。我们探索一种绕过该限制的方式,看看搭配二次移动平均线的情况下,研究运用多时间帧策略的可能性。
MQL5集成:Python MQL5集成:Python
Python是一种广为人知且流行的语言,具有许多功能,尤其是在金融、数据科学、人工智能和机器学习领域。Python也是一种强大的工具,可以在交易中发挥作用。MQL5允许我们将这种强大的语言作为集成工具,以高效地实现我们的目标。在本文中,我们将在了解一些Python的基本信息后,分享如何在MQL5中使用Python作为集成工具。
让新闻交易轻松上手(第3部分):执行交易 让新闻交易轻松上手(第3部分):执行交易
在本文中,我们的新闻交易EA将根据存储在数据库中的经济日历开始交易。此外,我们将改进EA的图表,以显示更多关于即将到来的经济日历事件的相关信息。