波段突破策略,计算近期高点和地点做突破
黄金1H。100刀起步
//+------------------------------------------------------------------+
//| 波段突破策略.mq5 |
//| v6.1 Pivot局部高低点突破 + 动态刷新压力支撑位 |
//+------------------------------------------------------------------+
#property copyright "Swing Breakout Strategy"
#property version "6.10"
#include <Trade\Trade.mqh>
CTrade trade;
//+------------------------------------------------------------------+
//| 参数 |
//+------------------------------------------------------------------+
input int PivotLeft = 5; // 左侧K线数(找波段高/低点)
input int PivotRight = 5; // 右侧K线数
input double SL_Points = 150; // 止损点数
input double TP_Step = 50; // 止盈步进(点)
input double RiskPercent = 1.0; // 每笔风险%
input int MagicNumber = 8888;
input int Slippage = 3;
input string TradeComment = "Swing";
input bool EnableLong = true;
input bool EnableShort = true;
input int ConfirmBars = 1; // 突破确认K线数
//+------------------------------------------------------------------+
double g_point;
datetime g_lastBar = 0;
double g_entryPrice = 0;
double g_swingHigh = 0;
double g_swingLow = 0;
bool g_levelsValid = false;
datetime g_lastSignalBar = 0;
int g_tpSteps = 0;
int g_cooldownBars = 0;
int g_breakoutConfirm = 0;
//+------------------------------------------------------------------+
//| 初始化 |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFillingBySymbol(_Symbol);
g_point = _Point;
if(_Digits == 3 || _Digits == 5) g_point *= 10;
g_cooldownBars = 0;
g_levelsValid = false;
g_breakoutConfirm = 0;
Print("波段突破 v6.0 | Pivot(", PivotLeft, ",", PivotRight, ") 止损:", SL_Points, "点步进:", TP_Step, "点");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ObjectFind(0, "Swing_High") >= 0) ObjectDelete(0, "Swing_High");
if(ObjectFind(0, "Swing_Low") >= 0) ObjectDelete(0, "Swing_Low");
Comment("");
}
//+------------------------------------------------------------------+
//| 找最近的波段高点(pivot high: 比左右都高) |
//+------------------------------------------------------------------+
double FindRecentSwingHigh()
{
double high[];
ArraySetAsSeries(high, true);
// 扩大搜索范围到100根K线
int searchRange = 100;
int leftBars = PivotLeft;
int rightBars = PivotRight;
int needBars = searchRange + leftBars + rightBars;
if(CopyHigh(_Symbol, PERIOD_CURRENT, 1, needBars, high) < needBars) return 0;
// 从近到远搜索,找到第一个有效的pivot high
for(int start = rightBars; start < needBars - leftBars; start++)
{
bool isPivot = true;
for(int l = 1; l <= leftBars; l++)
{
if(high >= high) { isPivot = false; break; }
}
if(!isPivot) continue;
for(int r = 1; r <= rightBars; r++)
{
if(high >= high) { isPivot = false; break; }
}
if(isPivot)
{
Print("找到波段高点 #", start, " 价格:", high);
return high;
}
}
// 找不到pivot就用最近N根K线的最高价
double maxH = 0;
int lookback = MathMin(50, needBars);
for(int i = 1; i < lookback; i++)
{
if(high > maxH) maxH = high;
}
return maxH > 0 ? maxH : high;
}
//+------------------------------------------------------------------+
//| 找最近的波段低点(pivot low: 比左右都低) |
//+------------------------------------------------------------------+
double FindRecentSwingLow()
{
double low[];
ArraySetAsSeries(low, true);
int searchRange = 100;
int leftBars = PivotLeft;
int rightBars = PivotRight;
int needBars = searchRange + leftBars + rightBars;
if(CopyLow(_Symbol, PERIOD_CURRENT, 1, needBars, low) < needBars) return 0;
for(int start = rightBars; start < needBars - leftBars; start++)
{
bool isPivot = true;
for(int l = 1; l <= leftBars; l++)
{
if(low <= low) { isPivot = false; break; }
}
if(!isPivot) continue;
for(int r = 1; r <= rightBars; r++)
{
if(low <= low) { isPivot = false; break; }
}
if(isPivot)
{
Print("找到波段低点 #", start, " 价格:", low);
return low;
}
}
// 找不到pivot就用最近N根K线的最低价
double minL = DBL_MAX;
int lookback = MathMin(50, needBars);
for(int i = 1; i < lookback; i++)
{
if(low < minL) minL = low;
}
return minL < DBL_MAX ? minL : low;
}
//+------------------------------------------------------------------+
//| 更新压力/支撑位(持仓时也要定期更新) |
//+------------------------------------------------------------------+
bool CalcLevels()
{
double sh = FindRecentSwingHigh();
double sl = FindRecentSwingLow();
if(sh > 0 && sl > 0 && sh > sl)
{
g_swingHigh = sh;
g_swingLow = sl;
g_levelsValid = true;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查是否需要刷新压力/支撑位 |
//+------------------------------------------------------------------+
void CheckRefreshLevels()
{
if(!g_levelsValid)
{
CalcLevels();
return;
}
// 价格已经远离当前区间,需要重新计算
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid <= 0) return;
bool farAbove = (bid > g_swingHigh + g_point * 50);// 远离压力位50点以上
bool farBelow = (bid < g_swingLow - g_point * 50); // 远离支撑位50点以上
if(farAbove || farBelow)
{
Print("价格远离当前区间,刷新压力支撑位");
g_levelsValid = false;
CalcLevels();
}
}
//+------------------------------------------------------------------+
void ResetAfterClose()
{
g_entryPrice = 0;
g_tpSteps = 0;
g_levelsValid = false;
g_cooldownBars = 10;
g_breakoutConfirm = 0;
}
//+------------------------------------------------------------------+
void OnTick()
{
MqlRates rates[];
ArraySetAsSeries(rates, true);
if(CopyRates(_Symbol, PERIOD_CURRENT, 0, 3, rates) < 3) return;
bool isNewBar = (rates.time != g_lastBar);
if(!isNewBar)
{
int posType = GetPositionType();
if(posType != 0) CheckTPStep(rates);
return;
}
g_lastBar = rates.time;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(ask <= 0 || bid <= 0 || ask == bid) return;
int posType = GetPositionType();
// 检测持仓是否刚被止损
if(posType == 0 && g_entryPrice > 0)
{
Print(">>>检测止损平仓<<< 重置状态");
ResetAfterClose();
UpdateDashboard();
return;
}
// 持仓中
if(posType != 0)
{
CheckTPStep(rates);
UpdateDashboard();
return;
}
// 冷却期
if(g_cooldownBars > 0)
{
g_cooldownBars--;
UpdateDashboard();
return;
}
// 动态计算/刷新压力/支撑位
CheckRefreshLevels();
if(!g_levelsValid)
{
UpdateDashboard();
return;
}
// 每根新K线都重绘
DrawLevelLines(g_swingHigh, g_swingLow);
double prevClose = rates.close;
static int dbg = 0;
if(dbg < 15)
{
Print("信号[", dbg, "] 收盘:", DoubleToString(prevClose, _Digits),
" 压力:", DoubleToString(g_swingHigh, _Digits),
" 支撑:", DoubleToString(g_swingLow, _Digits),
" 突破压力:", (prevClose > g_swingHigh),
" 跌破支撑:", (prevClose < g_swingLow));
dbg++;
}
bool above = (prevClose > g_swingHigh);
bool below = (prevClose < g_swingLow);
if(!above && !below)
{
g_breakoutConfirm = 0;
}
else if(above && g_lastSignalBar != rates.time)
{
if(g_breakoutConfirm <= 0) g_breakoutConfirm = 1;
else g_breakoutConfirm++;
}
else if(below && g_lastSignalBar != rates.time)
{
if(g_breakoutConfirm >= 0) g_breakoutConfirm = -1;
else g_breakoutConfirm--;
}
if(EnableLong && g_breakoutConfirm >= ConfirmBars)
{
double lot = CalcLot();
double slPrice = ask - SL_Points * g_point;
if(trade.Buy(lot, _Symbol, ask, slPrice, 0, TradeComment))
{
g_entryPrice = ask;
g_tpSteps = 0;
g_lastSignalBar = rates.time;
g_breakoutConfirm = 0;
Print(">>>突破做多<<< 压力:", DoubleToString(g_swingHigh, _Digits),
" 入场:", ask, " 止损:", slPrice, " 手数:", lot);
}
else Print("做多失败: ", trade.ResultRetcodeDescription());
}
else if(EnableShort && g_breakoutConfirm <= -ConfirmBars)
{
double lot = CalcLot();
double slPrice = bid + SL_Points * g_point;
if(trade.Sell(lot, _Symbol, bid, slPrice, 0, TradeComment))
{
g_entryPrice = bid;
g_tpSteps = 0;
g_lastSignalBar = rates.time;
g_breakoutConfirm = 0;
Print(">>>跌破做空<<< 支撑:", DoubleToString(g_swingLow, _Digits),
" 入场:", bid, " 止损:", slPrice, " 手数:", lot);
}
else Print("做空失败: ", trade.ResultRetcodeDescription());
}
UpdateDashboard();
}
//+------------------------------------------------------------------+
double CalcLot()
{
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double riskAmount = equity * RiskPercent / 100.0;
double slDistance = SL_Points * g_point;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickValue <= 0 || tickSize <= 0) return 0.01;
double dollarPerPoint = tickValue / tickSize;
double lot = riskAmount / (slDistance * dollarPerPoint);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(lot < minLot) lot = minLot;
if(lot > maxLot) lot = maxLot;
lot = MathFloor(lot / step) * step;
return lot;
}
//+------------------------------------------------------------------+
int GetPositionType()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong tk = PositionGetTicket(i);
if(!PositionSelectByTicket(tk)) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
return (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? 1 : -1;
}
return 0;
}
//+------------------------------------------------------------------+
void CloseAll()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong tk = PositionGetTicket(i);
if(!PositionSelectByTicket(tk)) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
trade.PositionClose(tk);
}
}
//+------------------------------------------------------------------+
void DrawLevelLines(double high, double low)
{
static double prevH = 0, prevL = 0;
double tol = g_point * 5;
if(MathAbs(high - prevH) < tol && MathAbs(low - prevL) < tol) return;
prevH = high;
prevL = low;
if(ObjectFind(0, "Swing_High") >= 0) ObjectDelete(0, "Swing_High");
if(ObjectFind(0, "Swing_Low") >= 0) ObjectDelete(0, "Swing_Low");
ObjectCreate(0, "Swing_High", OBJ_HLINE, 0, 0, high);
ObjectSetInteger(0, "Swing_High", OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, "Swing_High", OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, "Swing_High", OBJPROP_WIDTH, 2);
ObjectSetInteger(0, "Swing_High", OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, "Swing_High", OBJPROP_BACK, true);
ObjectCreate(0, "Swing_Low", OBJ_HLINE, 0, 0, low);
ObjectSetInteger(0, "Swing_Low", OBJPROP_COLOR, clrBlue);
ObjectSetInteger(0, "Swing_Low", OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, "Swing_Low", OBJPROP_WIDTH, 2);
ObjectSetInteger(0, "Swing_Low", OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, "Swing_Low", OBJPROP_BACK, true);
}
//+------------------------------------------------------------------+
void CheckTPStep(MqlRates &rates[])
{
int posType = GetPositionType();
if(posType == 0)
{
ResetAfterClose();
return;
}
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double currentProfit = 0;
int posCount = 0;
double openPrice = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong tk = PositionGetTicket(i);
if(!PositionSelectByTicket(tk)) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
currentProfit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
posCount++;
}
if(posCount == 0) return;
double profitPoints = 0;
if(posType == 1)
profitPoints = (bid - openPrice) / g_point;
else
profitPoints = (openPrice - ask) / g_point;
if(profitPoints <= -SL_Points)
{
Print(">>>止损触发<<< 亏损:[ DISCUZ_CODE_0 ]quot;, DoubleToString(currentProfit, 2));
CloseAll();
ResetAfterClose();
return;
}
int targetSteps = (int)(profitPoints / TP_Step);
if(targetSteps > g_tpSteps && targetSteps > 0)
{
g_tpSteps = targetSteps;
double newSL = 0;
if(posType == 1)
newSL = openPrice + (g_tpSteps - 1) * TP_Step * g_point;
else
newSL = openPrice - (g_tpSteps - 1) * TP_Step * g_point;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong tk = PositionGetTicket(i);
if(!PositionSelectByTicket(tk)) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
double curSL = PositionGetDouble(POSITION_SL);
if(posType == 1 && (newSL > curSL || curSL == 0))
trade.PositionModify(tk, newSL, 0);
else if(posType == -1 && (newSL < curSL || curSL == 0))
trade.PositionModify(tk, newSL, 0);
}
Print(">>>止盈步进<<< 第", g_tpSteps, "步 新止损:", DoubleToString(newSL, _Digits), " 盈利:[ DISCUZ_CODE_0 ]quot;, DoubleToString(currentProfit, 2));
}
}
//+------------------------------------------------------------------+
void UpdateDashboard()
{
double eq = AccountInfoDouble(ACCOUNT_EQUITY);
double bal = AccountInfoDouble(ACCOUNT_BALANCE);
int posType = GetPositionType();
string dirStr = (posType == 1) ? "多" : (posType == -1) ? "空" : "无";
string s = "═══ 波段突破 v6.1 ══\n";
s += "Balance [ DISCUZ_CODE_0 ]quot; + DoubleToString(bal, 2) + "Equity [ DISCUZ_CODE_0 ]quot; + DoubleToString(eq, 2) + "\n";
s += "方向: " + dirStr + "止盈步进: " + IntegerToString(g_tpSteps);
if(g_cooldownBars > 0) s += "冷却: " + IntegerToString(g_cooldownBars) + "根";
if(g_breakoutConfirm != 0) s += "突破计数: " + IntegerToString(MathAbs(g_breakoutConfirm));
s += "\n压力: " + DoubleToString(g_swingHigh, _Digits) + "支撑: " + DoubleToString(g_swingLow, _Digits) + "\n";
s += "Pivot(" + IntegerToString(PivotLeft) + "," + IntegerToString(PivotRight) + ")止损: " + DoubleToString(SL_Points, 0) + "点步进: " + DoubleToString(TP_Step, 0) + "点";
if(posType != 0 && g_entryPrice > 0)
s += "\n开仓: " + DoubleToString(g_entryPrice, _Digits);
Comment(s);
}
//+------------------------------------------------------------------+
是自动EA还是半自动的? 函数参考...... 非常好 就是有点看不懂 函数非常好
页:
[1]