波段突破策略,计算近期高点和地点做突破 (MT5)
黄金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[1];
[*]}
[*]
[*]//+------------------------------------------------------------------+
[*]//| 找最近的波段低点(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[1];
[*]}
[*]
[*]//+------------------------------------------------------------------+
[*]//| 更新压力/支撑位(持仓时也要定期更新) |
[*]//+------------------------------------------------------------------+
[*]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[0].time != g_lastBar);
[*]
[*] if(!isNewBar)
[*] {
[*] int posType = GetPositionType();
[*] if(posType != 0) CheckTPStep(rates);
[*] return;
[*] }
[*] g_lastBar = rates[0].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[1].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[1].time)
[*] {
[*] if(g_breakoutConfirm <= 0) g_breakoutConfirm = 1;
[*] else g_breakoutConfirm++;
[*] }
[*] else if(below && g_lastSignalBar != rates[1].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[1].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[1].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);
[*]}
[*]//+------------------------------------------------------------------+
页:
[1]