欢迎访问

# Price Structure Suite 指标开发说明_2

author emer | 66 人阅读 | 0 人评论 |

//+------------------------------------------------------------------+
//| Price Structure Suite.mq4 |
//| Visualizes structure, price action patterns, volume, MTF bias, |
//| and risk metrics for discretionary traders. |
//+------------------------------------------------------------------+

property strict

property indicator_chart_window

property indicator_plots 4

property indicator_label1 "Swing High"

property indicator_type1 DRAW_ARROW

property indicator_color1 clrTomato

property indicator_style1 STYLE_SOLID

property indicator_width1 1

property indicator_label2 "Swing Low"

property indicator_type2 DRAW_ARROW

property indicator_color2 clrDeepSkyBlue

property indicator_style2 STYLE_SOLID

property indicator_width2 1

property indicator_label3 "Volume Boost"

property indicator_type3 DRAW_HISTOGRAM

property indicator_color3 clrGold

property indicator_style3 STYLE_SOLID

property indicator_width3 2

property indicator_label4 "Structure Bias"

property indicator_type4 DRAW_LINE

property indicator_color4 clrSilver

property indicator_style4 STYLE_DOT

property indicator_width4 1

//--- inputs
input int SwingDepth = 5; // fractal depth
input int MaxLookbackBars = 800; // max bars processed
input int VolumeLookback = 20; // volume average window
input double VolumeFactor = 1.6; // volume spike factor
input ENUM_TIMEFRAMES HigherTF = PERIOD_H4;
input int HigherMAPeriod = 55;
input int AtrPeriod = 14;
input double RiskPercent = 1.0; // account risk per trade (percent)
input double AccountValueOverride = 0.0; // custom equity override
input double PatternTolerancePips = 20.0; // allowed deviation for patterns
input double LabelOffsetPips = 15.0; // text/arrow offset
input bool EnableHeadShoulders = true;
input bool EnableTriangles = true;
input bool EnableDoubleTopBottom = true;

//--- buffers
double SwingHighBuffer[];
double SwingLowBuffer[];
double VolumeBoostBuffer[];
double StructureBiasBuffer[];

//--- internal state

define MAX_SWINGS 20

string PatternPrefix = "PS_PATTERN";
int patternIdCounter = 0;

int swingIndex[MAX_SWINGS];
double swingPrice[MAX_SWINGS];
int swingType[MAX_SWINGS]; // 1 = high, -1 = low
datetime swingTime[MAX_SWINGS];
int swingCount = 0;

//--- pattern anchors
int lastDoubleTopIdx = -1;
int lastDoubleBottomIdx = -1;
int lastHSPatternIdx = -1;
int lastInvHSPatternIdx = -1;
int lastAscTriangleIdx = -1;
int lastDescTriangleIdx = -1;

//--- function declarations
bool IsSwingHigh(const double &high[], int index);
bool IsSwingLow(const double &low[], int index);
void RegisterSwing(int index, double price, int type, datetime time);
int DetermineStructureBias();
int HigherTimeframeBias(datetime t);
void EvaluatePatterns();
bool DetectDoubleTop(double tolerance);
bool DetectDoubleBottom(double tolerance);
bool DetectHeadAndShoulders(double tolerance);
bool DetectInverseHeadAndShoulders(double tolerance);
bool DetectAscendingTriangle(double tolerance);
bool DetectDescendingTriangle(double tolerance);
void RenderPattern(int arrayPos, const string label, color clr, bool placeAbove);
double SimpleAverageVolume(const long &tick_volume[], int index);
void UpdateRiskDashboard();
double PipValue();

//+------------------------------------------------------------------+
int OnInit()
{
IndicatorSetString(INDICATOR_SHORTNAME, "Price Structure Suite");

SetIndexBuffer(0, SwingHighBuffer, INDICATOR_DATA);
SetIndexBuffer(1, SwingLowBuffer, INDICATOR_DATA);
SetIndexBuffer(2, VolumeBoostBuffer, INDICATOR_DATA);
SetIndexBuffer(3, StructureBiasBuffer, INDICATOR_DATA);

ArraySetAsSeries(SwingHighBuffer, true);
ArraySetAsSeries(SwingLowBuffer, true);
ArraySetAsSeries(VolumeBoostBuffer, true);
ArraySetAsSeries(StructureBiasBuffer, true);

PlotIndexSetInteger(0, PLOT_ARROW, 234); // up arrow
PlotIndexSetInteger(1, PLOT_ARROW, 241); // down arrow
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
PlotIndexSetInteger(2, PLOT_SHOW_DATA, true);
PlotIndexSetInteger(3, PLOT_SHOW_DATA, true);

SetIndexDrawBegin(2, VolumeLookback + SwingDepth);
SetIndexDrawBegin(3, SwingDepth);

IndicatorDigits(Digits());

return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PatternPrefix);
}
//+------------------------------------------------------------------+
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 < SwingDepth * 2 + 10)
return prev_calculated;

int barsToProcess = rates_total;
if(MaxLookbackBars > 0 && MaxLookbackBars < rates_total)
barsToProcess = MaxLookbackBars;

// reset buffers
for(int i = 0; i < rates_total; i++)
{
SwingHighBuffer[i] = EMPTY_VALUE;
SwingLowBuffer[i] = EMPTY_VALUE;
VolumeBoostBuffer[i] = 0.0;
StructureBiasBuffer[i] = EMPTY_VALUE;
}

// reset swing and pattern state
swingCount = 0;
for(int j = 0; j < MAX_SWINGS; j++)
{
swingIndex[j] = -1;
swingPrice[j] = 0.0;
swingType[j] = 0;
swingTime[j] = 0;
}

lastDoubleTopIdx = -1;
lastDoubleBottomIdx = -1;
lastHSPatternIdx = -1;
lastInvHSPatternIdx = -1;
lastAscTriangleIdx = -1;
lastDescTriangleIdx = -1;
patternIdCounter = 0;

ObjectsDeleteAll(0, PatternPrefix);

int startIndex = MathMin(rates_total - SwingDepth - 1, barsToProcess);

for(int i = startIndex; i >= SwingDepth; i--)
{
bool swingHigh = IsSwingHigh(high, i);
bool swingLow = IsSwingLow(low, i);

  if(swingHigh)
  {
     SwingHighBuffer[i] = high[i] + LabelOffsetPips * _Point;
     RegisterSwing(i, high[i], 1, time[i]);
     EvaluatePatterns();
  }
  else if(swingLow)
  {
     SwingLowBuffer[i] = low[i] - LabelOffsetPips * _Point;
     RegisterSwing(i, low[i], -1, time[i]);
     EvaluatePatterns();
  }

  int bias = DetermineStructureBias();
  int higherBias = HigherTimeframeBias(time[i]);
  StructureBiasBuffer[i] = bias + higherBias;

  double avgVol = SimpleAverageVolume(tick_volume, i);
  if(avgVol > 0 && tick_volume[i] > avgVol * VolumeFactor && (bias != 0 || higherBias != 0))
     VolumeBoostBuffer[i] = (double)tick_volume[i];
  else
     VolumeBoostBuffer[i] = 0.0;

}

UpdateRiskDashboard();

return(rates_total);
}
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high[], int index)
{
for(int k = 1; k <= SwingDepth; k++)
{
if(high[index] <= high[index - k] || high[index] < high[index + k])
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low[], int index)
{
for(int k = 1; k <= SwingDepth; k++)
{
if(low[index] >= low[index - k] || low[index] > low[index + k])
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
void RegisterSwing(int index, double price, int type, datetime t)
{
for(int i = MAX_SWINGS - 1; i > 0; i--)
{
swingIndex[i] = swingIndex[i - 1];
swingPrice[i] = swingPrice[i - 1];
swingType[i] = swingType[i - 1];
swingTime[i] = swingTime[i - 1];
}

swingIndex[0] = index;
swingPrice[0] = price;
swingType[0] = type;
swingTime[0] = t;

if(swingCount < MAX_SWINGS)
swingCount++;
}
//+------------------------------------------------------------------+
int DetermineStructureBias()
{
if(swingCount < 4)
return(0);

double lastHigh = -1.0, prevHigh = -1.0;
double lastLow = -1.0, prevLow = -1.0;

for(int i = 0; i < swingCount; i++)
{
if(swingType[i] == 1)
{
if(lastHigh < 0.0)
lastHigh = swingPrice[i];
else if(prevHigh < 0.0)
prevHigh = swingPrice[i];
}
else if(swingType[i] == -1)
{
if(lastLow < 0.0)
lastLow = swingPrice[i];
else if(prevLow < 0.0)
prevLow = swingPrice[i];
}

  if(lastHigh > 0 && prevHigh > 0 && lastLow > 0 && prevLow > 0)
     break;

}

if(lastHigh < 0 || prevHigh < 0 || lastLow < 0 || prevLow < 0)
return(0);

if(lastHigh > prevHigh && lastLow > prevLow)
return(1);
if(lastHigh < prevHigh && lastLow < prevLow)
return(-1);
return(0);
}
//+------------------------------------------------------------------+
int HigherTimeframeBias(datetime t)
{
if(HigherTF == PERIOD_CURRENT)
return(0);

int shift = iBarShift(NULL, HigherTF, t, true);
if(shift < 0)
return(0);

double ma = iMA(NULL, HigherTF, HigherMAPeriod, 0, MODE_EMA, PRICE_CLOSE, shift);
double closePrice = iClose(NULL, HigherTF, shift);

if(closePrice > ma)
return(1);
if(closePrice < ma)
return(-1);
return(0);
}
//+------------------------------------------------------------------+
void EvaluatePatterns()
{
if(swingCount < 3)
return;

double tolerance = PatternTolerancePips _Point;
if(tolerance <= 0)
tolerance = 10
_Point;

if(EnableDoubleTopBottom)
{
if(swingType[0] == 1 && DetectDoubleTop(tolerance) && lastDoubleTopIdx != swingIndex[0])
{
RenderPattern(0, "Double Top", clrOrangeRed, true);
lastDoubleTopIdx = swingIndex[0];
}
if(swingType[0] == -1 && DetectDoubleBottom(tolerance) && lastDoubleBottomIdx != swingIndex[0])
{
RenderPattern(0, "Double Bottom", clrDeepSkyBlue, false);
lastDoubleBottomIdx = swingIndex[0];
}
}

if(EnableHeadShoulders)
{
if(swingType[0] == 1 && DetectHeadAndShoulders(tolerance) && lastHSPatternIdx != swingIndex[0])
{
RenderPattern(0, "Head & Shoulders", clrFireBrick, true);
lastHSPatternIdx = swingIndex[0];
}
if(swingType[0] == -1 && DetectInverseHeadAndShoulders(tolerance) && lastInvHSPatternIdx != swingIndex[0])
{
RenderPattern(0, "Inverse H&S", clrDodgerBlue, false);
lastInvHSPatternIdx = swingIndex[0];
}
}

if(EnableTriangles)
{
if(swingType[0] == -1 && DetectAscendingTriangle(tolerance) && lastAscTriangleIdx != swingIndex[0])
{
RenderPattern(0, "Ascending Triangle", clrMediumSeaGreen, false);
lastAscTriangleIdx = swingIndex[0];
}
if(swingType[0] == 1 && DetectDescendingTriangle(tolerance) && lastDescTriangleIdx != swingIndex[0])
{
RenderPattern(0, "Descending Triangle", clrDarkOrange, true);
lastDescTriangleIdx = swingIndex[0];
}
}
}
//+------------------------------------------------------------------+
bool DetectDoubleTop(double tolerance)
{
int firstHigh = -1, secondHigh = -1;

for(int i = 0; i < swingCount; i++)
{
if(swingType[i] != 1)
continue;

  if(firstHigh == -1)
     firstHigh = i;
  else
  {
     secondHigh = i;
     break;
  }

}

if(firstHigh == -1 || secondHigh == -1)
return(false);

if(MathAbs(swingPrice[firstHigh] - swingPrice[secondHigh]) > tolerance)
return(false);

for(int j = firstHigh + 1; j < secondHigh; j++)
{
if(swingType[j] == -1)
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
bool DetectDoubleBottom(double tolerance)
{
int firstLow = -1, secondLow = -1;

for(int i = 0; i < swingCount; i++)
{
if(swingType[i] != -1)
continue;

  if(firstLow == -1)
     firstLow = i;
  else
  {
     secondLow = i;
     break;
  }

}

if(firstLow == -1 || secondLow == -1)
return(false);

if(MathAbs(swingPrice[firstLow] - swingPrice[secondLow]) > tolerance)
return(false);

for(int j = firstLow + 1; j < secondLow; j++)
{
if(swingType[j] == 1)
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
bool DetectHeadAndShoulders(double tolerance)
{
if(swingCount < 5)
return(false);

if(!(swingType[0] == 1 && swingType[1] == -1 && swingType[2] == 1 &&
swingType[3] == -1 && swingType[4] == 1))
return(false);

double right = swingPrice[0];
double necklineRight = swingPrice[1];
double head = swingPrice[2];
double necklineLeft = swingPrice[3];
double left = swingPrice[4];

if(MathAbs(left - right) > tolerance)
return(false);

double neckline = (necklineLeft + necklineRight) / 2.0;
if(head <= left || head <= right)
return(false);

if(MathAbs(necklineRight - necklineLeft) > tolerance)
return(false);

if(head - neckline < tolerance * 0.5)
return(false);

return(true);
}
//+------------------------------------------------------------------+
bool DetectInverseHeadAndShoulders(double tolerance)
{
if(swingCount < 5)
return(false);

if(!(swingType[0] == -1 && swingType[1] == 1 && swingType[2] == -1 &&
swingType[3] == 1 && swingType[4] == -1))
return(false);

double right = swingPrice[0];
double necklineRight = swingPrice[1];
double head = swingPrice[2];
double necklineLeft = swingPrice[3];
double left = swingPrice[4];

if(MathAbs(left - right) > tolerance)
return(false);

double neckline = (necklineLeft + necklineRight) / 2.0;
if(head >= left || head >= right)
return(false);

if(MathAbs(necklineRight - necklineLeft) > tolerance)
return(false);

if(neckline - head < tolerance * 0.5)
return(false);

return(true);
}
//+------------------------------------------------------------------+
bool DetectAscendingTriangle(double tolerance)
{
if(swingCount < 5)
return(false);

double highs[2];
double lows[3];
int hCount = 0, lCount = 0;

for(int i = 0; i < swingCount && (hCount < 2 || lCount < 3); i++)
{
if(swingType[i] == 1 && hCount < 2)
highs[hCount++] = swingPrice[i];
else if(swingType[i] == -1 && lCount < 3)
lows[lCount++] = swingPrice[i];
}

if(hCount < 2 || lCount < 3)
return(false);

if(MathAbs(highs[0] - highs[1]) > tolerance)
return(false);

if(!(lows[0] > lows[1] && lows[1] > lows[2]))
return(false);

return(true);
}
//+------------------------------------------------------------------+
bool DetectDescendingTriangle(double tolerance)
{
if(swingCount < 5)
return(false);

double highs[3];
double lows[2];
int hCount = 0, lCount = 0;

for(int i = 0; i < swingCount && (hCount < 3 || lCount < 2); i++)
{
if(swingType[i] == 1 && hCount < 3)
highs[hCount++] = swingPrice[i];
else if(swingType[i] == -1 && lCount < 2)
lows[lCount++] = swingPrice[i];
}

if(hCount < 3 || lCount < 2)
return(false);

if(!(highs[0] < highs[1] && highs[1] < highs[2]))
return(false);

if(MathAbs(lows[0] - lows[1]) > tolerance)
return(false);

return(true);
}
//+------------------------------------------------------------------+
void RenderPattern(int arrayPos, const string label, color clr, bool placeAbove)
{
if(arrayPos >= swingCount || arrayPos < 0)
return;

string name = PatternPrefix + "_" + IntegerToString(patternIdCounter++);
double offset = LabelOffsetPips * _Point;
double price = swingPrice[arrayPos];
datetime t = swingTime[arrayPos];

if(placeAbove)
price += offset;
else
price -= offset;

if(!ObjectCreate(0, name, OBJ_TEXT, 0, t, price))
return;

ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_ANCHOR, placeAbove ? ANCHOR_LOWER : ANCHOR_UPPER);
ObjectSetInteger(0, name, OBJPROP_BACK, false);
ObjectSetText(name, label, 8, "Arial", clr);
}
//+------------------------------------------------------------------+
double SimpleAverageVolume(const long &tick_volume[], int index)
{
double sum = 0.0;
int count = 0;

for(int i = 0; i < VolumeLookback && (index + i) < Bars; i++)
{
sum += (double)tick_volume[index + i];
count++;
}

if(count == 0)
return(0.0);
return(sum / count);
}
//+------------------------------------------------------------------+
double PipValue()
{
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
if(tickValue <= 0.0 || tickSize <= 0.0)
return(0.0);
return(tickValue / tickSize _Point);
}
//+------------------------------------------------------------------+
void UpdateRiskDashboard()
{
double equity = (AccountValueOverride > 0.0) ? AccountValueOverride : AccountEquity();
double atr = iATR(NULL, 0, AtrPeriod, 0);
double pipVal = PipValue();
double riskMoney = equity
(RiskPercent / 100.0);
double suggestedLots = 0.0;
double atrPips = 0.0;

if(atr > 0.0 && pipVal > 0.0)
{
atrPips = atr / _Point;
double riskPerLot = atr * pipVal;
if(riskPerLot > 0.0)
suggestedLots = riskMoney / riskPerLot;
}

string text = StringFormat("ATR(%d): %.1f pips | Risk %.2f%% approx %.2f | Suggested lots approx %.2f",
AtrPeriod,
atrPips,
RiskPercent,
riskMoney,
suggestedLots);

Comment(text);
}
//+------------------------------------------------------------------+

还没有人打赏,支持一下