Open pixcelo opened 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 ×[]) {
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");
}
}
};
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;
}
}
}
};