pixcelo / meta-trader

0 stars 0 forks source link

極大値・極小値の計算 #2

Open pixcelo opened 9 months ago

pixcelo commented 9 months ago
// Extrema.mqh
struct Extrema {
    double value;      // 極値
    datetime timestamp;// タイムスタンプ
    bool isPeak;       // ピークかどうか。falseの場合、谷。
};

class LocalExtremaSeeker {
private:
    int depth;         // 新しい高値または安値を描画するために必要な最小のバー数を指定
    int backstep;      // 2つの連続する頂点の間の最小のバー数
    int deviation;     // 極小値と極大値の間の最小の値幅

public:
    // 初期化
    void Initialize(int d=7, int bs=3, int dev=10) {
        depth = d;
        backstep = bs;
        deviation = dev;
    }

    // 極値を見つける
    void FindExtrema(double &prices[], datetime &times[], Extrema &extremaArray[]) {
        int lastExtremaIndex = -backstep;
        bool lastWasPeak = false;

        for (int i = depth; i < ArraySize(prices) - depth; i++) {
            bool isPeak = true;
            bool isValley = true;

            for (int j = 1; j <= depth; j++) {
                if (prices[i] <= prices[i-j] || prices[i] <= prices[i+j]) {
                    isPeak = false;
                }
                if (prices[i] >= prices[i-j] || prices[i] >= prices[i+j]) {
                    isValley = false;
                }
            }

            bool isFarEnoughFromLastExtrema = (i - lastExtremaIndex) >= backstep;
            bool isDifferentFromLastExtrema = (isPeak != lastWasPeak);

            // deviationを考慮して極値を判定
            if (ArraySize(extremaArray) > 0 && isFarEnoughFromLastExtrema && (isPeak || isValley)) {
                double diff = MathAbs(prices[i] - extremaArray[ArraySize(extremaArray)-1].value);
                if (diff < deviation) {
                    isPeak = false;
                    isValley = false;
                }
            }

            if ((isPeak || isValley) && isFarEnoughFromLastExtrema && isDifferentFromLastExtrema) {
                ArrayResize(extremaArray, ArraySize(extremaArray) + 1);
                extremaArray[ArraySize(extremaArray) - 1].value = prices[i];
                extremaArray[ArraySize(extremaArray) - 1].timestamp = times[i];
                extremaArray[ArraySize(extremaArray) - 1].isPeak = isPeak;

                lastExtremaIndex = i;
                lastWasPeak = isPeak;
            }
        }
    }

    // ピークと谷を更新
    void UpdateExtrema(int barTerm=960, Extrema &extremaArray[]) {
        double highPrices[];
        double lowPrices[];
        datetime timeStamps[];

        ArraySetAsSeries(highPrices, true);
        ArraySetAsSeries(lowPrices, true);
        ArraySetAsSeries(timeStamps, true);

        ArrayResize(highPrices, barTerm);
        ArrayResize(lowPrices, barTerm);
        ArrayResize(timeStamps, barTerm);

        for (int shift = 0; shift < barTerm; shift++) {
            highPrices[shift] = iHigh(Symbol(), Period(), shift);
            lowPrices[shift] = iLow(Symbol(), Period(), shift);
            timeStamps[shift] = iTime(Symbol(), Period(), shift);
        }

        ArrayResize(extremaArray, 0);
        FindExtrema(highPrices, timeStamps, extremaArray);
        FindExtrema(lowPrices, timeStamps, extremaArray);
    }

    // Debug
    void printValuesAndIndices(Extrema &extremaArray[]) {
        Print("Extrema:");
        Print("ArraySize ", ArraySize(extremaArray));
        for (int i = 0; i < ArraySize(extremaArray); i++) {
            string type = extremaArray[i].isPeak ? "Peak" : "Valley";
            Print(type, " Value: ", extremaArray[i].value, ", Timestamp: ", extremaArray[i].timestamp);
        }
    }
};
pixcelo commented 9 months ago

構造体・極小値と極大値を交互に格納、deviationの実装

class ExtremaSeeker
{
public:
    Extremum ExtremaArray[]; // ピークと谷を格納する配列

private:
    int depth;         // 新しい高値または安値を描画するために必要な最小のバー数を指定
    int deviation;     // 偏差:高値、安値を描写するレートの転換率(%)
    int backstep;      // 2つの連続する頂点の間の最小のバー数

public:
    void Initialize(int d=7, int dv=5, int bs=3) {
         depth = d;
         deviation = dv;
         backstep = bs;
    }

    void resetArrays() {
        ArrayResize(ExtremaArray, 0);
    }

    // Extremaを見つける
    void FindExtrema(double &highPrices[], double &lowPrices[], datetime &times[]) {
        int lastExtremaIndex = -backstep;
        bool lastWasPeak = false; // 最後に見つけた極値がピークかどうか
        double lastExtremaValue = 0.0; // 最後に見つけた極値の価格

        for (int i = depth; i < ArraySize(highPrices) - depth; i++) {
            bool isPeak = true, isValley = true;

            for (int j = 1; j <= depth; j++) {
                if (highPrices[i] <= highPrices[i-j] || highPrices[i] <= highPrices[i+j]) {
                    isPeak = false;
                }
                if (lowPrices[i] >= lowPrices[i-j] || lowPrices[i] >= lowPrices[i+j]) {
                    isValley = false;
                }
            }

            bool isFarEnoughFromLastExtrema = (i - lastExtremaIndex) >= backstep;

            if (!isFarEnoughFromLastExtrema) {
                continue; // 条件が満たされない場合、次のイテレーションに進む
            }

            // check deviation
            if (lastExtremaIndex >= 0) { // 以前の極値が存在する場合
                double currentPrice = isPeak ? highPrices[i] : lowPrices[i];
                double priceDifference = MathAbs(currentPrice - lastExtremaValue);
                double percentageDifference = (priceDifference / lastExtremaValue) * 100.0;

                if (percentageDifference < deviation) {
                    continue; // 価格の変動が十分でない場合、次のイテレーションに進む
                }
            }

            if (isPeak && !lastWasPeak) {
                AddExtrema(highPrices[i], times[i], true);
                lastExtremaIndex = i;
                lastWasPeak = true;
                lastExtremaValue = highPrices[i];
            } else if (isValley && lastWasPeak) {
                AddExtrema(lowPrices[i], times[i], false);
                lastExtremaIndex = i;
                lastWasPeak = false;
                lastExtremaValue = lowPrices[i];
            }
        }
    }

    void AddExtrema(double value, datetime timestamp, bool isPeak) {
        Extremum ex;
        ex.value = value;
        ex.timestamp = timestamp;
        ex.isPeak = isPeak;
        ArrayResize(ExtremaArray, ArraySize(ExtremaArray) + 1);
        ExtremaArray[ArraySize(ExtremaArray) - 1] = ex;
    }

    // ピークと谷を更新
    void UpdateExtrema(int barTerm=960) {
        double highPrices[];
        double lowPrices[];
        datetime timeStamps[];

        ArraySetAsSeries(highPrices, true);
        ArraySetAsSeries(lowPrices, true);
        ArraySetAsSeries(timeStamps, true);

        ArrayResize(highPrices, barTerm);
        ArrayResize(lowPrices, barTerm);
        ArrayResize(timeStamps, barTerm);

        for (int shift = 0; shift < barTerm; shift++) {
            highPrices[shift] = iHigh(Symbol(), Period(), shift);
            lowPrices[shift] = iLow(Symbol(), Period(), shift);
            timeStamps[shift] = iTime(Symbol(), Period(), shift);
        }

        resetArrays();
        FindExtrema(highPrices, lowPrices, timeStamps);
    }

    void GetExtremaArray(Extremum &outputArray[]) {
        ArrayResize(outputArray, ArraySize(ExtremaArray));
        ArrayCopy(outputArray, ExtremaArray);
    }

    // Debug
    void printValuesAndIndices() {
        Print("ExtremaArray:");
        Print("ArraySize ", ArraySize(ExtremaArray));
        for (int k = 0; k < ArraySize(ExtremaArray); k++) {
            Print("ExtremaArray Value: ", ExtremaArray[k].value, ", Timestamp: ", ExtremaArray[k].timestamp, ", IsPeak: ", ExtremaArray[k].isPeak ? "True" : "False");
        }
    }
};
pixcelo commented 8 months ago

ZigzagSeeker.mqh

// ZigzagSeeker.mqh
struct Extremum {
    double value;       // 極値
    double prevValue;   // 一つ前の極値
    datetime timestamp; // タイムスタンプ
    bool isPeak;        // true の場合はピーク、false の場合は谷
};

// グローバル変数に定義:includeで他クラスからアクセス可能な状態
Extremum ExtremaArray[];
Extremum ExShortArray[];

class ZigzagSeeker {
private:
    int depth, deviation, backstep, timeframe;

public:
    void Initialize(int d=12, int dv=5, int bs=3, int tf=0) {
         depth = d;
         deviation = dv;
         backstep = bs;
         timeframe = tf;
    }

    void UpdateExtremaArray(int term, int limitLength) {
        ArrayResize(ExtremaArray, 0);

        int startBar = 0;
        int endBar = MathMin(Bars, term);

        for (int i = startBar; i < endBar; i++) {
            if (ArraySize(ExtremaArray) == limitLength) {
                break;
            }

            double zigzagValue = iCustom(NULL, timeframe, "ZigZag", depth, deviation, backstep, 0, i);
            if (zigzagValue == 0) {
                continue;
            }

            Extremum ex;
            ex.value = zigzagValue;
            ex.timestamp = iTime(NULL, timeframe, i);
            ex.isPeak = iHigh(NULL, timeframe, i) == zigzagValue;

            ArrayResize(ExtremaArray, ArraySize(ExtremaArray) + 1);
            ExtremaArray[ArraySize(ExtremaArray) - 1] = ex;
        }

        // 起点となった時系列的に一つ前の極値をプロパティに保持
        double lastPeakValue = 0;
        double lastValleyValue = 0;
        for (int j = ArraySize(ExtremaArray) - 1; j >= 0; j--) {
            if (ExtremaArray[j].isPeak) {
                ExtremaArray[j].prevValue = lastValleyValue;
                lastPeakValue = ExtremaArray[j].value;
            } else {
                ExtremaArray[j].prevValue = lastPeakValue;
                lastValleyValue = ExtremaArray[j].value;
            }
        }
    }

    // 最新の極値を取得する
    double GetLatestValue(bool isPeak) {
        int len = ArraySize(ExtremaArray);

        for (int i = 0; i < len; i++) {
            Extremum ex = ExtremaArray[i];
            if (isPeak && ex.isPeak) {
                return ex.value;
            }

            if (!isPeak && !ex.isPeak) {
                return ex.value;
            }
        }

        return 0;
    }

    // 指定した時間足のトレンド転換ラインを取得
    double GetTrendReversalLine(int tf, int term, int direction) {
        Extremum tmpArray[];
        ArrayResize(tmpArray, 0);

        int startBar = 0;
        int endBar = MathMin(Bars, term);
        int i;

        for (i = startBar; i < endBar; i++) {
            double zigzagValue = iCustom(NULL, tf, "ZigZag", depth, deviation, backstep, 0, i);
            if (zigzagValue == 0) {
                continue;
            }

            Extremum ex;
            ex.value = zigzagValue;
            ex.timestamp = iTime(NULL, tf, i);
            ex.isPeak = iHigh(NULL, tf, i) == zigzagValue;

            ArrayResize(tmpArray, ArraySize(tmpArray) + 1);
            tmpArray[ArraySize(tmpArray) - 1] = ex;
        }

        // 起点となった時系列的に一つ前の極値をプロパティに保持
        double lastPeakValue = 0;
        double lastValleyValue = 0;
        for (int j = ArraySize(tmpArray) - 1; j >= 0; j--) {
            if (tmpArray[j].isPeak) {
                tmpArray[j].prevValue = lastValleyValue;
                lastPeakValue = tmpArray[j].value;
            } else {
                tmpArray[j].prevValue = lastPeakValue;
                lastValleyValue = tmpArray[j].value;
            }
        }

        double trendReversalLine = 0;

        if (direction == 1) {
            // 期間内で最安の極小値の起点となったピークをトレンド転換ラインとする
            double lowestValue = DBL_MAX;

            for (i = 0; i < ArraySize(tmpArray); i++) {
                Extremum ex = tmpArray[i];
                if (ex.isPeak) {
                    continue;
                }
                if (lowestValue >= ex.value) {
                    lowestValue = ex.value;
                    trendReversalLine = ex.prevValue;
                }
            }

            return trendReversalLine; // ここを超えたらロング
        }

        if (direction == 2) {
            // 期間内で最高の極大値の起点となった谷をトレンド転換ラインとする
            double highestValue = -DBL_MAX;

            for (i = 0; i < ArraySize(tmpArray); i++) {
                Extremum ex = tmpArray[i];
                if (!ex.isPeak) {
                    continue;
                }
                if (highestValue <= ex.value) {
                    highestValue = ex.value;
                    trendReversalLine = ex.prevValue;
                }
            }

            return trendReversalLine; // ここを抜けたらショート
        }

        return -1;
    }

    // ダウ理論に基づいたトレンド方向を取得
    int GetTrendDirection(Extremum &extremaArray[]) {
        datetime lastPeakTime = 0, secondLastPeakTime = 0;
        datetime lastValleyTime = 0, secondLastValleyTime = 0;
        double lastPeakValue = 0, secondLastPeakValue = 0;
        double lastValleyValue = 0, secondLastValleyValue = 0;

        // 最新と2番目のピークと谷を見つける
        for (int i = 0; i < ArraySize(extremaArray); i++) {
            if (extremaArray[i].isPeak) {
                if (lastPeakTime == 0) {
                    lastPeakTime = extremaArray[i].timestamp;
                    lastPeakValue = extremaArray[i].value;
                } else if (secondLastPeakTime == 0) {
                    secondLastPeakTime = extremaArray[i].timestamp;
                    secondLastPeakValue = extremaArray[i].value;
                }
            } else {
                if (lastValleyTime == 0) {
                    lastValleyTime = extremaArray[i].timestamp;
                    lastValleyValue = extremaArray[i].value;
                } else if (secondLastValleyTime == 0) {
                    secondLastValleyTime = extremaArray[i].timestamp;
                    secondLastValleyValue = extremaArray[i].value;
                }
            }

            if (secondLastPeakTime != 0 && secondLastValleyTime !=0) {
                break;
            }
        }

        // トレンド方向を判断
        if (secondLastPeakTime != 0 && lastPeakTime != 0 && secondLastValleyTime != 0 && lastValleyTime != 0) {
            if (secondLastPeakValue < lastPeakValue && secondLastValleyValue < lastValleyValue) {
                return 1; // 上昇トレンド
            } else if (secondLastPeakValue > lastPeakValue && secondLastValleyValue > lastValleyValue) {
                return 2; // 下降トレンド
            }
        }

        return 0;
    }

    void UpdateExShortArray(int term, int limitLength, int tf = PERIOD_M1) {
        ArrayResize(ExShortArray, 0);

        int startBar = 0;
        int endBar = MathMin(Bars, term);

        for (int i = startBar; i < endBar; i++) {
            if (ArraySize(ExShortArray) == limitLength) {
                break;
            }

            double zigzagValue = iCustom(NULL, tf, "ZigZag", 12, 5, 3, 0, i);
            if (zigzagValue == 0) {
                continue;
            }

            Extremum ex;
            ex.value = zigzagValue;
            ex.timestamp = iTime(NULL, tf, i);
            ex.isPeak = iHigh(NULL, tf, i) == zigzagValue;

            ArrayResize(ExShortArray, ArraySize(ExShortArray) + 1);
            ExShortArray[ArraySize(ExShortArray) - 1] = ex;
        }

        // 起点となった時系列的に一つ前の極値をプロパティに保持
        double lastPeakValue = 0;
        double lastValleyValue = 0;
        for (int j = ArraySize(ExShortArray) - 1; j >= 0; j--) {
            if (ExShortArray[j].isPeak) {
                ExShortArray[j].prevValue = lastValleyValue;
                lastPeakValue = ExShortArray[j].value;
            } else {
                ExShortArray[j].prevValue = lastPeakValue;
                lastValleyValue = ExShortArray[j].value;
            }
        }
    }

};