feat: box
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
//| 突破交易策略 - 完整交易版本 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Breakout Strategy"
|
||||
#property version "1.01"
|
||||
#property version "1.03"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
@@ -14,6 +14,12 @@ enum ENUM_LOT_SIZE_MODE {
|
||||
LOT_SIZE_RISK_PERCENT = 1 // 结余风险比例(RiskPercent)
|
||||
};
|
||||
|
||||
// 破高/破低开仓方向
|
||||
enum ENUM_BREAKOUT_DIRECTION {
|
||||
BREAKOUT_DIR_FOLLOW = 0, // 顺势(破高多/破低空)
|
||||
BREAKOUT_DIR_REVERSE = 1 // 反向(破高空/破低多)
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 输入参数 |
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -25,6 +31,11 @@ input double InpMaxWavePercent = 10.0; // 最大波段阈值百分比(%
|
||||
input double InpPullbackTolerance = 0.0; // 反向突破容忍度(%) 0=不容忍
|
||||
input int InpMinWaveBars = 3; // 有效波段最少K线数(含两端极值所在K)
|
||||
|
||||
input group "=== 突破交易参数 ==="
|
||||
input ENUM_BREAKOUT_DIRECTION InpBreakoutDirection = BREAKOUT_DIR_FOLLOW; // 开仓方向
|
||||
input bool InpEnablePullbackReverse = false; // 启用突破回落反向
|
||||
input int InpPullbackReverseWatchBars = 3; // 回落监测K线数(突破K+后2根=3)
|
||||
|
||||
input group "=== 风险管理参数 ==="
|
||||
input int InpStopLossPoints = 200; // 止损点数
|
||||
input int InpTakeProfitPoints = 300; // 止盈点数
|
||||
@@ -60,12 +71,27 @@ struct ValidWaveInfo {
|
||||
double high_price; // 高点价格
|
||||
double low_price; // 低点价格
|
||||
datetime update_time; // 更新时间
|
||||
bool high_used; // 高点是否已使用
|
||||
bool low_used; // 低点是否已使用
|
||||
bool high_used; // 高点-突破挂单已用(每极值限1次)
|
||||
bool low_used; // 低点-突破挂单已用(每极值限1次)
|
||||
bool high_pullback_reverse_used; // 高点-回落反向已用(每极值限1次)
|
||||
bool low_pullback_reverse_used; // 低点-回落反向已用(每极值限1次)
|
||||
};
|
||||
|
||||
struct PullbackWatchState {
|
||||
bool active;
|
||||
double extreme_price;
|
||||
int bars_checked;
|
||||
};
|
||||
|
||||
ValidWaveInfo latest_wave; // 最新有效波段
|
||||
|
||||
PullbackWatchState g_high_pullback_watch;
|
||||
PullbackWatchState g_low_pullback_watch;
|
||||
datetime g_pullback_bar_time_high = 0;
|
||||
bool g_pullback_opened_on_bar_high = false;
|
||||
datetime g_pullback_bar_time_low = 0;
|
||||
bool g_pullback_opened_on_bar_low = false;
|
||||
|
||||
double g_sync_wave_high = 0.0; // 已挂单的波段高价(用于检测换波段)
|
||||
double g_sync_wave_low = 0.0;
|
||||
|
||||
@@ -92,8 +118,17 @@ int CheckBreakout(int index, const MqlRates &rates[], const double &ma[]);
|
||||
void FilterBreakouts(const int &breakout_bars[], const int &breakout_types[],
|
||||
const MqlRates &rates[], int &filtered_bars[], int &filtered_types[]);
|
||||
void SyncBreakoutPendingOrders();
|
||||
void CancelEaPendingOrders(const bool cancel_buy_stop, const bool cancel_sell_stop);
|
||||
void CancelAllEaPendingOrders();
|
||||
void CancelEaPendingOrderType(const ENUM_ORDER_TYPE order_type);
|
||||
void CancelEaPendingAtHigh();
|
||||
void CancelEaPendingAtLow();
|
||||
ulong FindEaPendingOrder(const ENUM_ORDER_TYPE order_type);
|
||||
ENUM_ORDER_TYPE PendingTypeOnHighBreakout();
|
||||
ENUM_ORDER_TYPE PendingTypeOnLowBreakout();
|
||||
long PosTypeOnHighBreakout();
|
||||
long PosTypeOnLowBreakout();
|
||||
void SyncPendingAtExtreme(const bool at_high, const double trigger_price,
|
||||
const double wave_high, const double wave_low, const double lots);
|
||||
bool CalcPendingSlTp(const ENUM_ORDER_TYPE pending_type, const double trigger_price, double &sl, double &tp);
|
||||
bool PlaceBreakoutPendingOrder(const ENUM_ORDER_TYPE pending_type, const double trigger_price,
|
||||
const double wave_high, const double wave_low);
|
||||
@@ -105,6 +140,20 @@ double NormalizeVolumeLots(double lots);
|
||||
void ManagePositions();
|
||||
void CheckTrailingStop(ulong ticket);
|
||||
void CheckAndCloseManualOrders();
|
||||
void CheckPullbackReverseSignals();
|
||||
void TryArmPullbackWatch();
|
||||
void ProcessPullbackReverseOnNewBar();
|
||||
ENUM_ORDER_TYPE OrderTypeOnHighPullbackReverse();
|
||||
ENUM_ORDER_TYPE OrderTypeOnLowPullbackReverse();
|
||||
bool IsPullbackBarOpenAllowed(const bool for_high_extreme);
|
||||
void MarkPullbackBarOpened(const bool for_high_extreme);
|
||||
void SyncPullbackBarLock(const bool for_high_extreme);
|
||||
bool IsPullbackOpenBlocked();
|
||||
bool OpenPullbackReversePosition(ENUM_ORDER_TYPE order_type, const bool from_high_extreme,
|
||||
const double extreme_price, const double wave_high,
|
||||
const double wave_low);
|
||||
bool CalcMarketSlTp(const ENUM_ORDER_TYPE order_type, const double entry_price, double &sl,
|
||||
double &tp);
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
@@ -130,6 +179,10 @@ int OnInit()
|
||||
latest_wave.update_time = 0;
|
||||
latest_wave.high_used = false;
|
||||
latest_wave.low_used = false;
|
||||
latest_wave.high_pullback_reverse_used = false;
|
||||
latest_wave.low_pullback_reverse_used = false;
|
||||
g_high_pullback_watch.active = false;
|
||||
g_low_pullback_watch.active = false;
|
||||
|
||||
Print("========================================");
|
||||
Print("突破交易策略EA初始化成功");
|
||||
@@ -154,7 +207,15 @@ int OnInit()
|
||||
Print("手数模式: 结余风险比例 ", InpRiskPercent, "% (按止损距离反推手数)");
|
||||
else
|
||||
Print("手数模式: 固定手数 ", InpFixedLots);
|
||||
Print("开仓方式: 突破挂单 (高点BUY STOP / 低点SELL STOP)");
|
||||
if(InpBreakoutDirection == BREAKOUT_DIR_FOLLOW)
|
||||
Print("开仓方向: 顺势(破高多/破低空) 挂单: 高BUY STOP / 低SELL STOP");
|
||||
else
|
||||
Print("开仓方向: 反向(破高空/破低多) 挂单: 高SELL LIMIT / 低BUY LIMIT");
|
||||
if(InpEnablePullbackReverse)
|
||||
Print("回落反向: 开 破极值后监测", InpPullbackReverseWatchBars,
|
||||
"根K收盘回到极值内开反向单(与突破挂单独立)");
|
||||
else
|
||||
Print("回落反向: 关");
|
||||
Print("禁止手工单:", (InpCloseManualOrders ? "启用 (自动平掉手工单)" : "禁用"));
|
||||
Print("========================================");
|
||||
|
||||
@@ -171,7 +232,7 @@ void OnDeinit(const int reason)
|
||||
|
||||
// 删除所有标记
|
||||
ObjectsDeleteAll(0, "ValidWave_");
|
||||
CancelEaPendingOrders(true, true);
|
||||
CancelAllEaPendingOrders();
|
||||
|
||||
Print("突破交易策略EA已卸载");
|
||||
}
|
||||
@@ -188,12 +249,22 @@ void OnTradeTransaction(const MqlTradeTransaction& trans,
|
||||
if(HistoryDealGetString(trans.deal, DEAL_SYMBOL) == _Symbol &&
|
||||
HistoryDealGetInteger(trans.deal, DEAL_MAGIC) == InpMagicNumber &&
|
||||
HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_IN) {
|
||||
const ENUM_DEAL_TYPE deal_type =
|
||||
(ENUM_DEAL_TYPE)HistoryDealGetInteger(trans.deal, DEAL_TYPE);
|
||||
if(deal_type == DEAL_TYPE_BUY)
|
||||
latest_wave.high_used = true;
|
||||
else if(deal_type == DEAL_TYPE_SELL)
|
||||
latest_wave.low_used = true;
|
||||
const string deal_comment = HistoryDealGetString(trans.deal, DEAL_COMMENT);
|
||||
if(StringFind(deal_comment, "-PB") < 0) {
|
||||
const ENUM_DEAL_TYPE deal_type =
|
||||
(ENUM_DEAL_TYPE)HistoryDealGetInteger(trans.deal, DEAL_TYPE);
|
||||
if(deal_type == DEAL_TYPE_BUY) {
|
||||
if(PosTypeOnHighBreakout() == POSITION_TYPE_BUY)
|
||||
latest_wave.high_used = true;
|
||||
else if(PosTypeOnLowBreakout() == POSITION_TYPE_BUY)
|
||||
latest_wave.low_used = true;
|
||||
} else if(deal_type == DEAL_TYPE_SELL) {
|
||||
if(PosTypeOnHighBreakout() == POSITION_TYPE_SELL)
|
||||
latest_wave.high_used = true;
|
||||
else if(PosTypeOnLowBreakout() == POSITION_TYPE_SELL)
|
||||
latest_wave.low_used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,10 +347,13 @@ void OnTick()
|
||||
// 2. 更新最新有效波段
|
||||
UpdateLatestValidWave();
|
||||
|
||||
// 3. 同步突破挂单(BUY STOP@高 / SELL STOP@低)
|
||||
// 3. 突破回落反向(破极值后K线收盘回到极值内,与突破挂单独立)
|
||||
CheckPullbackReverseSignals();
|
||||
|
||||
// 4. 同步突破挂单(顺势/反向由 InpBreakoutDirection 决定)
|
||||
SyncBreakoutPendingOrders();
|
||||
|
||||
// 4. 管理已有持仓
|
||||
// 5. 管理已有持仓
|
||||
ManagePositions();
|
||||
}
|
||||
|
||||
@@ -552,12 +626,24 @@ void UpdateLatestValidWave()
|
||||
high != latest_wave.high_price ||
|
||||
low != latest_wave.low_price) {
|
||||
|
||||
const double prev_high = latest_wave.high_price;
|
||||
const double prev_low = latest_wave.low_price;
|
||||
|
||||
latest_wave.exists = true;
|
||||
latest_wave.high_price = high;
|
||||
latest_wave.low_price = low;
|
||||
latest_wave.update_time = extremes[i].time;
|
||||
latest_wave.high_used = false; // 新波段,重置使用状态
|
||||
latest_wave.low_used = false;
|
||||
|
||||
if(high != prev_high) {
|
||||
latest_wave.high_used = false;
|
||||
latest_wave.high_pullback_reverse_used = false;
|
||||
g_high_pullback_watch.active = false;
|
||||
}
|
||||
if(low != prev_low) {
|
||||
latest_wave.low_used = false;
|
||||
latest_wave.low_pullback_reverse_used = false;
|
||||
g_low_pullback_watch.active = false;
|
||||
}
|
||||
|
||||
// 绘制最新有效波段
|
||||
if(InpShowMarkers) {
|
||||
@@ -697,23 +783,57 @@ void FilterBreakouts(const int &breakout_bars[], const int &breakout_types[],
|
||||
//+------------------------------------------------------------------+
|
||||
//| 突破挂单管理 |
|
||||
//+------------------------------------------------------------------+
|
||||
void CancelEaPendingOrders(const bool cancel_buy_stop, const bool cancel_sell_stop)
|
||||
long PosTypeOnHighBreakout()
|
||||
{
|
||||
for(int i = OrdersTotal() - 1; i >= 0; i--) {
|
||||
const ulong ticket = OrderGetTicket(i);
|
||||
if(ticket == 0 || !OrderSelect(ticket))
|
||||
continue;
|
||||
if(OrderGetString(ORDER_SYMBOL) != _Symbol)
|
||||
continue;
|
||||
if(OrderGetInteger(ORDER_MAGIC) != InpMagicNumber)
|
||||
continue;
|
||||
if(InpBreakoutDirection == BREAKOUT_DIR_REVERSE)
|
||||
return POSITION_TYPE_SELL;
|
||||
return POSITION_TYPE_BUY;
|
||||
}
|
||||
|
||||
const ENUM_ORDER_TYPE ot = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
|
||||
if(cancel_buy_stop && ot == ORDER_TYPE_BUY_STOP)
|
||||
trade.OrderDelete(ticket);
|
||||
else if(cancel_sell_stop && ot == ORDER_TYPE_SELL_STOP)
|
||||
trade.OrderDelete(ticket);
|
||||
}
|
||||
long PosTypeOnLowBreakout()
|
||||
{
|
||||
if(InpBreakoutDirection == BREAKOUT_DIR_REVERSE)
|
||||
return POSITION_TYPE_BUY;
|
||||
return POSITION_TYPE_SELL;
|
||||
}
|
||||
|
||||
ENUM_ORDER_TYPE PendingTypeOnHighBreakout()
|
||||
{
|
||||
if(InpBreakoutDirection == BREAKOUT_DIR_REVERSE)
|
||||
return ORDER_TYPE_SELL_LIMIT;
|
||||
return ORDER_TYPE_BUY_STOP;
|
||||
}
|
||||
|
||||
ENUM_ORDER_TYPE PendingTypeOnLowBreakout()
|
||||
{
|
||||
if(InpBreakoutDirection == BREAKOUT_DIR_REVERSE)
|
||||
return ORDER_TYPE_BUY_LIMIT;
|
||||
return ORDER_TYPE_SELL_STOP;
|
||||
}
|
||||
|
||||
void CancelEaPendingOrderType(const ENUM_ORDER_TYPE order_type)
|
||||
{
|
||||
const ulong ticket = FindEaPendingOrder(order_type);
|
||||
if(ticket > 0)
|
||||
trade.OrderDelete(ticket);
|
||||
}
|
||||
|
||||
void CancelEaPendingAtHigh()
|
||||
{
|
||||
CancelEaPendingOrderType(ORDER_TYPE_BUY_STOP);
|
||||
CancelEaPendingOrderType(ORDER_TYPE_SELL_LIMIT);
|
||||
}
|
||||
|
||||
void CancelEaPendingAtLow()
|
||||
{
|
||||
CancelEaPendingOrderType(ORDER_TYPE_SELL_STOP);
|
||||
CancelEaPendingOrderType(ORDER_TYPE_BUY_LIMIT);
|
||||
}
|
||||
|
||||
void CancelAllEaPendingOrders()
|
||||
{
|
||||
CancelEaPendingAtHigh();
|
||||
CancelEaPendingAtLow();
|
||||
}
|
||||
|
||||
ulong FindEaPendingOrder(const ENUM_ORDER_TYPE order_type)
|
||||
@@ -742,7 +862,7 @@ bool CalcPendingSlTp(const ENUM_ORDER_TYPE pending_type, const double trigger_pr
|
||||
sl = 0.0;
|
||||
tp = 0.0;
|
||||
|
||||
if(pending_type == ORDER_TYPE_BUY_STOP) {
|
||||
if(pending_type == ORDER_TYPE_BUY_STOP || pending_type == ORDER_TYPE_BUY_LIMIT) {
|
||||
sl = NormalizeDouble(trigger_price - stop_loss_amount, _Digits);
|
||||
tp = NormalizeDouble(trigger_price + take_profit_amount, _Digits);
|
||||
if(stops_level > 0) {
|
||||
@@ -751,7 +871,7 @@ bool CalcPendingSlTp(const ENUM_ORDER_TYPE pending_type, const double trigger_pr
|
||||
if(tp - trigger_price < min_stop_distance)
|
||||
tp = NormalizeDouble(trigger_price + min_stop_distance, _Digits);
|
||||
}
|
||||
} else if(pending_type == ORDER_TYPE_SELL_STOP) {
|
||||
} else if(pending_type == ORDER_TYPE_SELL_STOP || pending_type == ORDER_TYPE_SELL_LIMIT) {
|
||||
sl = NormalizeDouble(trigger_price + stop_loss_amount, _Digits);
|
||||
tp = NormalizeDouble(trigger_price - take_profit_amount, _Digits);
|
||||
if(stops_level > 0) {
|
||||
@@ -785,6 +905,10 @@ bool PlaceBreakoutPendingOrder(const ENUM_ORDER_TYPE pending_type, const double
|
||||
result = trade.BuyStop(lots, trigger_price, _Symbol, sl, tp, ORDER_TIME_GTC, 0, comment);
|
||||
else if(pending_type == ORDER_TYPE_SELL_STOP)
|
||||
result = trade.SellStop(lots, trigger_price, _Symbol, sl, tp, ORDER_TIME_GTC, 0, comment);
|
||||
else if(pending_type == ORDER_TYPE_BUY_LIMIT)
|
||||
result = trade.BuyLimit(lots, trigger_price, _Symbol, sl, tp, ORDER_TIME_GTC, 0, comment);
|
||||
else if(pending_type == ORDER_TYPE_SELL_LIMIT)
|
||||
result = trade.SellLimit(lots, trigger_price, _Symbol, sl, tp, ORDER_TIME_GTC, 0, comment);
|
||||
|
||||
if(!result) {
|
||||
Print("挂单失败 ", EnumToString(pending_type), " 触发价:", trigger_price,
|
||||
@@ -799,10 +923,54 @@ bool PlaceBreakoutPendingOrder(const ENUM_ORDER_TYPE pending_type, const double
|
||||
return true;
|
||||
}
|
||||
|
||||
void SyncPendingAtExtreme(const bool at_high, const double trigger_price,
|
||||
const double wave_high, const double wave_low,
|
||||
const double lots)
|
||||
{
|
||||
const ENUM_ORDER_TYPE pending_type = at_high ?
|
||||
PendingTypeOnHighBreakout() : PendingTypeOnLowBreakout();
|
||||
const ENUM_ORDER_TYPE stale_type = at_high ?
|
||||
(pending_type == ORDER_TYPE_BUY_STOP ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_BUY_STOP) :
|
||||
(pending_type == ORDER_TYPE_SELL_STOP ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_SELL_STOP);
|
||||
CancelEaPendingOrderType(stale_type);
|
||||
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
const bool price_crossed = at_high ?
|
||||
(ask >= trigger_price - _Point * 0.5) :
|
||||
(bid <= trigger_price + _Point * 0.5);
|
||||
|
||||
if(price_crossed) {
|
||||
if(at_high)
|
||||
CancelEaPendingAtHigh();
|
||||
else
|
||||
CancelEaPendingAtLow();
|
||||
if(InpShowDebugInfo)
|
||||
Print("价格已越过波段", (at_high ? "高点" : "低点"),
|
||||
",暂不挂", EnumToString(pending_type),
|
||||
" ", (at_high ? "Ask:" : "Bid:"),
|
||||
(at_high ? ask : bid), " 极值:", trigger_price);
|
||||
return;
|
||||
}
|
||||
|
||||
ulong ticket = FindEaPendingOrder(pending_type);
|
||||
if(ticket > 0 && OrderSelect(ticket)) {
|
||||
const double order_price = OrderGetDouble(ORDER_PRICE_OPEN);
|
||||
const double order_lots = OrderGetDouble(ORDER_VOLUME_CURRENT);
|
||||
if(MathAbs(order_price - trigger_price) > _Point * 0.5 ||
|
||||
MathAbs(order_lots - lots) > 1e-8) {
|
||||
trade.OrderDelete(ticket);
|
||||
ticket = 0;
|
||||
}
|
||||
}
|
||||
if(ticket == 0)
|
||||
PlaceBreakoutPendingOrder(pending_type, trigger_price, wave_high, wave_low);
|
||||
}
|
||||
|
||||
void SyncBreakoutPendingOrders()
|
||||
{
|
||||
if(!latest_wave.exists) {
|
||||
CancelEaPendingOrders(true, true);
|
||||
CancelAllEaPendingOrders();
|
||||
g_sync_wave_high = 0.0;
|
||||
g_sync_wave_low = 0.0;
|
||||
return;
|
||||
@@ -811,7 +979,7 @@ void SyncBreakoutPendingOrders()
|
||||
if(InpConsecutiveLosses > 0 && InpFreezeBarCount > 0 && freeze_bar_index > 0) {
|
||||
const int current_bars = Bars(_Symbol, InpTimeframe);
|
||||
if(current_bars < freeze_bar_index) {
|
||||
CancelEaPendingOrders(true, true);
|
||||
CancelAllEaPendingOrders();
|
||||
return;
|
||||
}
|
||||
if(freeze_bar_index > 0) {
|
||||
@@ -824,7 +992,7 @@ void SyncBreakoutPendingOrders()
|
||||
|
||||
if(MathAbs(latest_wave.high_price - g_sync_wave_high) > _Point * 0.5 ||
|
||||
MathAbs(latest_wave.low_price - g_sync_wave_low) > _Point * 0.5) {
|
||||
CancelEaPendingOrders(true, true);
|
||||
CancelAllEaPendingOrders();
|
||||
g_sync_wave_high = latest_wave.high_price;
|
||||
g_sync_wave_low = latest_wave.low_price;
|
||||
}
|
||||
@@ -849,67 +1017,296 @@ void SyncBreakoutPendingOrders()
|
||||
}
|
||||
|
||||
if(total_positions >= InpMaxPositions) {
|
||||
CancelEaPendingOrders(true, true);
|
||||
CancelAllEaPendingOrders();
|
||||
if(InpShowDebugInfo)
|
||||
Print("已达最大持仓笔数,撤销突破挂单: ", total_positions, "/", InpMaxPositions);
|
||||
return;
|
||||
}
|
||||
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
const double wave_high = latest_wave.high_price;
|
||||
const double wave_low = latest_wave.low_price;
|
||||
const double lots = CalculateLotSize(wave_high, wave_low);
|
||||
|
||||
// 高点 BUY STOP
|
||||
if(!latest_wave.high_used && (!InpOnePositionPerDirection || buy_positions < 1)) {
|
||||
if(ask >= wave_high - _Point * 0.5) {
|
||||
CancelEaPendingOrders(true, false);
|
||||
if(InpShowDebugInfo)
|
||||
Print("价格已越过波段高点,暂不挂BUY STOP Ask:", ask, " 高:", wave_high);
|
||||
} else {
|
||||
ulong ticket = FindEaPendingOrder(ORDER_TYPE_BUY_STOP);
|
||||
if(ticket > 0 && OrderSelect(ticket)) {
|
||||
const double order_price = OrderGetDouble(ORDER_PRICE_OPEN);
|
||||
const double order_lots = OrderGetDouble(ORDER_VOLUME_CURRENT);
|
||||
if(MathAbs(order_price - wave_high) > _Point * 0.5 ||
|
||||
MathAbs(order_lots - lots) > 1e-8) {
|
||||
trade.OrderDelete(ticket);
|
||||
ticket = 0;
|
||||
}
|
||||
}
|
||||
if(ticket == 0)
|
||||
PlaceBreakoutPendingOrder(ORDER_TYPE_BUY_STOP, wave_high, wave_high, wave_low);
|
||||
}
|
||||
const long high_pos_type = PosTypeOnHighBreakout();
|
||||
const long low_pos_type = PosTypeOnLowBreakout();
|
||||
const int high_dir_positions = (high_pos_type == POSITION_TYPE_BUY) ?
|
||||
buy_positions : sell_positions;
|
||||
const int low_dir_positions = (low_pos_type == POSITION_TYPE_BUY) ?
|
||||
buy_positions : sell_positions;
|
||||
|
||||
if(!latest_wave.high_used &&
|
||||
(!InpOnePositionPerDirection || high_dir_positions < 1)) {
|
||||
SyncPendingAtExtreme(true, wave_high, wave_high, wave_low, lots);
|
||||
} else {
|
||||
CancelEaPendingOrders(true, false);
|
||||
CancelEaPendingAtHigh();
|
||||
}
|
||||
|
||||
// 低点 SELL STOP
|
||||
if(!latest_wave.low_used && (!InpOnePositionPerDirection || sell_positions < 1)) {
|
||||
if(bid <= wave_low + _Point * 0.5) {
|
||||
CancelEaPendingOrders(false, true);
|
||||
if(InpShowDebugInfo)
|
||||
Print("价格已越过波段低点,暂不挂SELL STOP Bid:", bid, " 低:", wave_low);
|
||||
} else {
|
||||
ulong ticket = FindEaPendingOrder(ORDER_TYPE_SELL_STOP);
|
||||
if(ticket > 0 && OrderSelect(ticket)) {
|
||||
const double order_price = OrderGetDouble(ORDER_PRICE_OPEN);
|
||||
const double order_lots = OrderGetDouble(ORDER_VOLUME_CURRENT);
|
||||
if(MathAbs(order_price - wave_low) > _Point * 0.5 ||
|
||||
MathAbs(order_lots - lots) > 1e-8) {
|
||||
trade.OrderDelete(ticket);
|
||||
ticket = 0;
|
||||
}
|
||||
}
|
||||
if(ticket == 0)
|
||||
PlaceBreakoutPendingOrder(ORDER_TYPE_SELL_STOP, wave_low, wave_high, wave_low);
|
||||
if(!latest_wave.low_used &&
|
||||
(!InpOnePositionPerDirection || low_dir_positions < 1)) {
|
||||
SyncPendingAtExtreme(false, wave_low, wave_high, wave_low, lots);
|
||||
} else {
|
||||
CancelEaPendingAtLow();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 突破回落反向(破极值后收盘回到极值内开反向单,与突破挂单独立) |
|
||||
//+------------------------------------------------------------------+
|
||||
ENUM_ORDER_TYPE OrderTypeOnHighPullbackReverse()
|
||||
{
|
||||
return ORDER_TYPE_SELL;
|
||||
}
|
||||
|
||||
ENUM_ORDER_TYPE OrderTypeOnLowPullbackReverse()
|
||||
{
|
||||
return ORDER_TYPE_BUY;
|
||||
}
|
||||
|
||||
void SyncPullbackBarLock(const bool for_high_extreme)
|
||||
{
|
||||
const datetime bar_time = iTime(_Symbol, InpTimeframe, 0);
|
||||
if(bar_time == 0)
|
||||
return;
|
||||
if(for_high_extreme) {
|
||||
if(bar_time != g_pullback_bar_time_high) {
|
||||
g_pullback_bar_time_high = bar_time;
|
||||
g_pullback_opened_on_bar_high = false;
|
||||
}
|
||||
} else {
|
||||
CancelEaPendingOrders(false, true);
|
||||
if(bar_time != g_pullback_bar_time_low) {
|
||||
g_pullback_bar_time_low = bar_time;
|
||||
g_pullback_opened_on_bar_low = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPullbackBarOpenAllowed(const bool for_high_extreme)
|
||||
{
|
||||
SyncPullbackBarLock(for_high_extreme);
|
||||
if(for_high_extreme)
|
||||
return !g_pullback_opened_on_bar_high;
|
||||
return !g_pullback_opened_on_bar_low;
|
||||
}
|
||||
|
||||
void MarkPullbackBarOpened(const bool for_high_extreme)
|
||||
{
|
||||
SyncPullbackBarLock(for_high_extreme);
|
||||
if(for_high_extreme)
|
||||
g_pullback_opened_on_bar_high = true;
|
||||
else
|
||||
g_pullback_opened_on_bar_low = true;
|
||||
}
|
||||
|
||||
bool IsPullbackOpenBlocked()
|
||||
{
|
||||
if(InpConsecutiveLosses > 0 && InpFreezeBarCount > 0 && freeze_bar_index > 0) {
|
||||
if(Bars(_Symbol, InpTimeframe) < freeze_bar_index)
|
||||
return true;
|
||||
}
|
||||
|
||||
int total_positions = 0;
|
||||
int buy_positions = 0;
|
||||
int sell_positions = 0;
|
||||
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
if(!PositionSelectByTicket(PositionGetTicket(i)))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
|
||||
continue;
|
||||
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
|
||||
continue;
|
||||
total_positions++;
|
||||
const ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if(pos_type == POSITION_TYPE_BUY)
|
||||
buy_positions++;
|
||||
else if(pos_type == POSITION_TYPE_SELL)
|
||||
sell_positions++;
|
||||
}
|
||||
|
||||
if(total_positions >= InpMaxPositions)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPullbackDirectionBlocked(const ENUM_ORDER_TYPE order_type)
|
||||
{
|
||||
if(!InpOnePositionPerDirection)
|
||||
return false;
|
||||
|
||||
const long pos_type = (order_type == ORDER_TYPE_BUY) ?
|
||||
POSITION_TYPE_BUY : POSITION_TYPE_SELL;
|
||||
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
if(!PositionSelectByTicket(PositionGetTicket(i)))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
|
||||
continue;
|
||||
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
|
||||
continue;
|
||||
if(PositionGetInteger(POSITION_TYPE) == pos_type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CalcMarketSlTp(const ENUM_ORDER_TYPE order_type, const double entry_price, double &sl,
|
||||
double &tp)
|
||||
{
|
||||
const ENUM_ORDER_TYPE pending_equiv = (order_type == ORDER_TYPE_BUY) ?
|
||||
ORDER_TYPE_BUY_STOP : ORDER_TYPE_SELL_STOP;
|
||||
return CalcPendingSlTp(pending_equiv, entry_price, sl, tp);
|
||||
}
|
||||
|
||||
bool OpenPullbackReversePosition(ENUM_ORDER_TYPE order_type, const bool from_high_extreme,
|
||||
const double extreme_price, const double wave_high,
|
||||
const double wave_low)
|
||||
{
|
||||
if(!IsPullbackBarOpenAllowed(from_high_extreme))
|
||||
return false;
|
||||
|
||||
if(IsPullbackOpenBlocked()) {
|
||||
if(InpShowDebugInfo)
|
||||
Print("【回落反向】开仓跳过 - 冷冻中或已达最大持仓笔数");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(IsPullbackDirectionBlocked(order_type)) {
|
||||
if(InpShowDebugInfo)
|
||||
Print("【回落反向】开仓跳过 - 同向已有持仓");
|
||||
return false;
|
||||
}
|
||||
|
||||
const double lots = CalculateLotSize(wave_high, wave_low);
|
||||
if(lots <= 0.0)
|
||||
return false;
|
||||
|
||||
const double entry_price = (order_type == ORDER_TYPE_BUY) ?
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
double sl = 0.0, tp = 0.0;
|
||||
if(!CalcMarketSlTp(order_type, entry_price, sl, tp))
|
||||
return false;
|
||||
|
||||
const int wave_range_points = (int)MathRound(MathAbs(wave_high - wave_low) / _Point);
|
||||
const string comment = StringFormat("WR%d-PB", wave_range_points);
|
||||
|
||||
bool result = false;
|
||||
if(order_type == ORDER_TYPE_BUY)
|
||||
result = trade.Buy(lots, _Symbol, entry_price, sl, tp, comment);
|
||||
else
|
||||
result = trade.Sell(lots, _Symbol, entry_price, sl, tp, comment);
|
||||
|
||||
if(!result) {
|
||||
Print("【回落反向】开仓失败: ", trade.ResultRetcode(), " - ",
|
||||
trade.ResultRetcodeDescription());
|
||||
return false;
|
||||
}
|
||||
|
||||
MarkPullbackBarOpened(from_high_extreme);
|
||||
Print("【回落反向】开仓成功 ", (order_type == ORDER_TYPE_BUY ? "多" : "空"),
|
||||
" 手数:", lots, " 极值:", extreme_price, " SL:", sl, " TP:", tp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TryArmPullbackWatch()
|
||||
{
|
||||
if(!InpEnablePullbackReverse || !latest_wave.exists)
|
||||
return;
|
||||
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
if(ask > latest_wave.high_price && !latest_wave.high_pullback_reverse_used) {
|
||||
if(!g_high_pullback_watch.active ||
|
||||
g_high_pullback_watch.extreme_price != latest_wave.high_price) {
|
||||
g_high_pullback_watch.active = true;
|
||||
g_high_pullback_watch.extreme_price = latest_wave.high_price;
|
||||
g_high_pullback_watch.bars_checked = 0;
|
||||
if(InpShowDebugInfo)
|
||||
Print("【回落反向】开始监测破高 极值:", latest_wave.high_price,
|
||||
" 共", InpPullbackReverseWatchBars, "根K");
|
||||
}
|
||||
}
|
||||
|
||||
if(bid < latest_wave.low_price && !latest_wave.low_pullback_reverse_used) {
|
||||
if(!g_low_pullback_watch.active ||
|
||||
g_low_pullback_watch.extreme_price != latest_wave.low_price) {
|
||||
g_low_pullback_watch.active = true;
|
||||
g_low_pullback_watch.extreme_price = latest_wave.low_price;
|
||||
g_low_pullback_watch.bars_checked = 0;
|
||||
if(InpShowDebugInfo)
|
||||
Print("【回落反向】开始监测破低 极值:", latest_wave.low_price,
|
||||
" 共", InpPullbackReverseWatchBars, "根K");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessPullbackReverseOnNewBar()
|
||||
{
|
||||
if(!InpEnablePullbackReverse || !latest_wave.exists)
|
||||
return;
|
||||
|
||||
const int max_bars = MathMax(1, InpPullbackReverseWatchBars);
|
||||
const double wave_high = latest_wave.high_price;
|
||||
const double wave_low = latest_wave.low_price;
|
||||
|
||||
if(g_high_pullback_watch.active && !latest_wave.high_pullback_reverse_used) {
|
||||
const double close1 = iClose(_Symbol, InpTimeframe, 1);
|
||||
if(close1 > 0.0 && close1 <= g_high_pullback_watch.extreme_price) {
|
||||
if(OpenPullbackReversePosition(OrderTypeOnHighPullbackReverse(), true,
|
||||
g_high_pullback_watch.extreme_price,
|
||||
wave_high, wave_low)) {
|
||||
latest_wave.high_pullback_reverse_used = true;
|
||||
}
|
||||
g_high_pullback_watch.active = false;
|
||||
} else {
|
||||
g_high_pullback_watch.bars_checked++;
|
||||
if(g_high_pullback_watch.bars_checked >= max_bars) {
|
||||
if(InpShowDebugInfo)
|
||||
Print("【回落反向】破高监测结束 未回落极值内");
|
||||
g_high_pullback_watch.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(g_low_pullback_watch.active && !latest_wave.low_pullback_reverse_used) {
|
||||
const double close1 = iClose(_Symbol, InpTimeframe, 1);
|
||||
if(close1 > 0.0 && close1 >= g_low_pullback_watch.extreme_price) {
|
||||
if(OpenPullbackReversePosition(OrderTypeOnLowPullbackReverse(), false,
|
||||
g_low_pullback_watch.extreme_price,
|
||||
wave_high, wave_low)) {
|
||||
latest_wave.low_pullback_reverse_used = true;
|
||||
}
|
||||
g_low_pullback_watch.active = false;
|
||||
} else {
|
||||
g_low_pullback_watch.bars_checked++;
|
||||
if(g_low_pullback_watch.bars_checked >= max_bars) {
|
||||
if(InpShowDebugInfo)
|
||||
Print("【回落反向】破低监测结束 未回升极值内");
|
||||
g_low_pullback_watch.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckPullbackReverseSignals()
|
||||
{
|
||||
if(!InpEnablePullbackReverse)
|
||||
return;
|
||||
|
||||
TryArmPullbackWatch();
|
||||
|
||||
static datetime s_last_bar_time = 0;
|
||||
const datetime bar_time = iTime(_Symbol, InpTimeframe, 0);
|
||||
if(bar_time == 0 || bar_time == s_last_bar_time)
|
||||
return;
|
||||
s_last_bar_time = bar_time;
|
||||
|
||||
ProcessPullbackReverseOnNewBar();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 止损/止盈距离(点数) |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//| 突破策略 - 单笔突破开仓 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Breakout Strategy"
|
||||
#property version "3.18"
|
||||
#property version "3.20"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
@@ -30,7 +30,7 @@ input group "=== 波段识别参数 ==="
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M1; // K线周期
|
||||
input int InpMAPeriod = 14; // MA周期
|
||||
input double InpMinWavePercent = 0.1; // 最小波段阈值百分比(%)
|
||||
input double InpMaxWavePercent = 0.25; // 最大波段阈值百分比(%)
|
||||
input double InpMaxWavePercent = 1.0; // 最大波段阈值百分比(%)
|
||||
input double InpPullbackTolerance = 0.05; // 反向突破容忍度(%) 0=不容忍
|
||||
input int InpMinWaveBars = 3; // 有效波段最少K线数(含两端极值所在K)
|
||||
|
||||
@@ -39,17 +39,17 @@ input ENUM_BREAKOUT_DIRECTION InpBreakoutDirection = BREAKOUT_DIR_RANDOM; // 开
|
||||
input double InpDailyMADevThreshold = 1.0; // 日MA偏离率阈值(%),|偏离|超过才触发反向,0=关
|
||||
input int InpBatchTakeProfitPoints = 300; // 按批统盈点数(0=关):均价±点数平该批
|
||||
input double InpStopLossBalancePct = 30.0; // 止损:持仓浮亏达结余比例(%)全部清仓,0=关
|
||||
input double InpLargeLotMinVolume = 1.0; // 大单独立止盈手数阈值(>=,0=关)
|
||||
input int InpLargeLotTakeProfitPoints = 500; // 大单最大止盈点数(0=关,不参与统盈)
|
||||
input bool InpEnablePullbackReverse = true; // 启用突破回落反向
|
||||
input double InpLargeLotMinVolume = 0.0; // 大单独立止盈手数阈值(>=,0=关)
|
||||
input int InpLargeLotTakeProfitPoints = 0; // 大单最大止盈点数(0=关,不参与统盈)
|
||||
input bool InpEnablePullbackReverse = false; // 启用突破回落反向
|
||||
input int InpPullbackReverseWatchBars = 3; // 回落监测K线数(突破K+后2根=3)
|
||||
|
||||
input group "=== 仓位管理参数 ==="
|
||||
input string InpTierLotsList =
|
||||
"0.05,0.05,0.05,0.8,0.8,1.1,1,2,2,1,1,1,2,2,1,1,1,5,5"; // 各档手数(逗号分隔,档数=最多开仓笔数)
|
||||
"0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,5,4,3,2,1,5"; // 各档手数(逗号分隔,档数=最多开仓笔数)
|
||||
input double InpMaxLots = 99.0; // 同向最大合计手数(0=不限制)
|
||||
input double InpMinSameDirAddSpacingPercent = 0.0; // 同向最小开仓间距(%) 0=不限制
|
||||
input ENUM_DUAL_FULL_ACTION InpDualFullAction = DUAL_FULL_CLEAR_ALL; // 双向满档后处理
|
||||
input ENUM_DUAL_FULL_ACTION InpDualFullAction = DUAL_FULL_NEW_ROUND; // 双向满档后处理
|
||||
|
||||
input group "=== 调试选项 ==="
|
||||
input bool InpShowDebugInfo = false; // 显示调试信息
|
||||
@@ -131,6 +131,8 @@ int CountAllBreakoutPositions();
|
||||
double TotalLotsInDirection(const long pos_type);
|
||||
bool IsDirectionFullActive(const long pos_type);
|
||||
void ResetBreakoutWaveEntryFlags();
|
||||
void ResetTradingStateAfterManualClose();
|
||||
bool IsManualDealCloseReason(const long reason);
|
||||
void CheckDualSideFullCapacity();
|
||||
double NormalizeVolume(double lots);
|
||||
bool InitTierLotsFromInput();
|
||||
@@ -1173,6 +1175,24 @@ void ResetBreakoutWaveEntryFlags()
|
||||
g_low_pullback_watch.active = false;
|
||||
}
|
||||
|
||||
bool IsManualDealCloseReason(const long reason)
|
||||
{
|
||||
return (reason == DEAL_REASON_CLIENT ||
|
||||
reason == DEAL_REASON_MOBILE ||
|
||||
reason == DEAL_REASON_WEB);
|
||||
}
|
||||
|
||||
void ResetTradingStateAfterManualClose()
|
||||
{
|
||||
g_trade_round = 1;
|
||||
g_dual_full_handled = false;
|
||||
ResetBreakoutWaveEntryFlags();
|
||||
g_breakout_opened_on_bar_high = false;
|
||||
g_breakout_opened_on_bar_low = false;
|
||||
g_pullback_opened_on_bar_high = false;
|
||||
g_pullback_opened_on_bar_low = false;
|
||||
}
|
||||
|
||||
int CountAllBreakoutPositions()
|
||||
{
|
||||
int count = 0;
|
||||
@@ -1974,7 +1994,7 @@ void CheckAndCloseManualOrders()
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 成交事件(大单被平台TP平仓时重置满档状态) |
|
||||
//| 成交事件(手动平仓重置状态 / 大单TP重置满档) |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTradeTransaction(const MqlTradeTransaction& trans,
|
||||
const MqlTradeRequest& request,
|
||||
@@ -1990,18 +2010,27 @@ void OnTradeTransaction(const MqlTradeTransaction& trans,
|
||||
return;
|
||||
if(HistoryDealGetInteger(trans.deal, DEAL_ENTRY) != DEAL_ENTRY_OUT)
|
||||
return;
|
||||
if(HistoryDealGetInteger(trans.deal, DEAL_REASON) != DEAL_REASON_TP)
|
||||
return;
|
||||
|
||||
const string comment = HistoryDealGetString(trans.deal, DEAL_COMMENT);
|
||||
if(!IsEaBreakoutComment(comment))
|
||||
return;
|
||||
|
||||
const long reason = HistoryDealGetInteger(trans.deal, DEAL_REASON);
|
||||
const double vol = HistoryDealGetDouble(trans.deal, DEAL_VOLUME);
|
||||
const int round_id = ParseRoundFromComment(comment);
|
||||
|
||||
if(IsManualDealCloseReason(reason)) {
|
||||
ResetTradingStateAfterManualClose();
|
||||
Print("【手动平仓】已重置轮次/满档/极值锁 手数:", vol, " 原轮次:", round_id,
|
||||
" 备注:", comment, " (马丁档按剩余第1轮持仓重计)");
|
||||
return;
|
||||
}
|
||||
|
||||
if(reason != DEAL_REASON_TP)
|
||||
return;
|
||||
if(!IsLargeLotVolume(vol))
|
||||
return;
|
||||
|
||||
const int round_id = ParseRoundFromComment(comment);
|
||||
if(round_id == g_trade_round)
|
||||
g_dual_full_handled = false;
|
||||
|
||||
|
||||
+86
-10
@@ -1,57 +1,133 @@
|
||||
# 突破策略(极值点突破开仓)
|
||||
|
||||
MQL5 EA:`20260522_Breakout.mq5`(v3.18)。
|
||||
|
||||
|
||||
MQL5 EA:`20260522_Breakout.mq5`(v3.20)。
|
||||
|
||||
|
||||
|
||||
## 参数默认值(与输入界面一致)
|
||||
|
||||
|
||||
|
||||
| 分组 | 参数 | 默认 |
|
||||
|
||||
|------|------|------|
|
||||
|
||||
| 波段 | K线周期 / MA周期 | M1 / 14 |
|
||||
| 波段 | 最小/最大波段% | 0.1 / **0.25** |
|
||||
|
||||
| 波段 | 最小/最大波段% | 0.1 / **1.0** |
|
||||
|
||||
| 波段 | 有效波段最少K线 | **3** |
|
||||
|
||||
| 波段 | 反向突破容忍度% | **0.05** |
|
||||
|
||||
| 交易 | 开仓方向 | **随机**(可选:顺势 / 反向 / 前日收线相对日MA / **日MA偏离率反向**) |
|
||||
|
||||
| 交易 | 日MA偏离率阈值% | **1.0**(\|偏离\|超过才触发,0=关) |
|
||||
|
||||
| 交易 | 按批统盈点数 / 止损% | **300** / **30** |
|
||||
| 交易 | 大单止盈阈值 / 点数 | **1.0手** / **500点** |
|
||||
| 交易 | 回落反向 / 监测K线数 | **开** / **3** |
|
||||
| 仓位 | 各档手数(19档) | 见下 |
|
||||
| 仓位 | 同向最大合计 / 最小间距% / 满档后 | 99 / **0** / **清仓** |
|
||||
|
||||
| 交易 | 大单止盈阈值 / 点数 | **0(关)** / **0(关)** |
|
||||
|
||||
| 交易 | 回落反向 / 监测K线数 | **关** / **3** |
|
||||
|
||||
| 仓位 | 各档手数(14档) | 见下 |
|
||||
|
||||
| 仓位 | 同向最大合计 / 最小间距% / 满档后 | 99 / **0** / **开新轮(不清仓)** |
|
||||
|
||||
| 调试 | 调试 / 极值标记 / Magic / 禁手工单 | false / true / 20260520 / true |
|
||||
|
||||
**各档手数:** `0.05,0.05,0.05,0.8,0.8,1.1,1,2,2,1,1,1,2,2,1,1,1,5,5`(19 档)
|
||||
|
||||
|
||||
**各档手数:** `0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,5,4,3,2,1,5`(14 档)
|
||||
|
||||
|
||||
|
||||
## 前日收线相对日MA(开仓方向选项)
|
||||
|
||||
|
||||
|
||||
取**前一交易日**日 K **收盘价**与**日 K 上同周期 MA**(默认 MA14)比较:
|
||||
|
||||
|
||||
|
||||
| 前日收线位置 | 允许开仓 |
|
||||
|
||||
|-------------|----------|
|
||||
|
||||
| **在日 MA 下方** | 破**低**极值 → 空;**高**极值出现回落反向 → 空 |
|
||||
|
||||
| **在日 MA 上方** | 破**高**极值 → 多;**低**极值出现回落反向 → 多 |
|
||||
|
||||
|
||||
|
||||
- 偏空时不做「破高」突破单,等高极值**回落反向**或等破低;偏多时不做「破低」突破单,等低极值**回落反向**或等破高。
|
||||
|
||||
- 前日收线恰在 MA 上(中性)时,该模式不开仓。
|
||||
|
||||
|
||||
|
||||
## 日 MA 偏离率反向(开仓方向选项)
|
||||
|
||||
|
||||
|
||||
**偏离率** = (当前价 − 日 MA) ÷ 日 MA × 100%(当前价取买卖中间价,日 MA 周期同 `InpMAPeriod`)。
|
||||
|
||||
|
||||
|
||||
| 偏离率 | 含义 | 允许开仓(反向/均值回归) |
|
||||
|
||||
|--------|------|--------------------------|
|
||||
|
||||
| **> +阈值%** | 价显著高于日 MA | 破**高** → 空;**高**极值回落反向 → 空 |
|
||||
|
||||
| **< −阈值%** | 价显著低于日 MA | 破**低** → 多;**低**极值回落反向 → 多 |
|
||||
|
||||
| \|偏离\| ≤ 阈值 | 未超阈值 | **不开仓** |
|
||||
|
||||
|
||||
|
||||
参数 `日MA偏离率阈值` 默认 **1.0%**,设为 **0** 关闭此逻辑。
|
||||
|
||||
|
||||
|
||||
## 大单独立止盈
|
||||
|
||||
- 开仓手数 **≥ 大单止盈阈值**(默认 1 手)的单子,按 **大单最大止盈点数** 单独止盈,**不参与按批统盈**的加权均价计算。
|
||||
- 大单止盈平仓后(平台 TP 或 EA 监测触发),**下一档马丁手数**与**统盈均价**均仅按**当前仍持仓**的单子重算,与已平大单无关。
|
||||
- 阈值或点数设为 **0** 时关闭此功能,所有单子统一走按批统盈。
|
||||
|
||||
|
||||
- 默认**关闭**(阈值 0 或点数 0)。开启后:开仓手数 **≥ 阈值** 的单子按 **点数** 单独止盈,**不参与按批统盈**。
|
||||
|
||||
- 大单止盈平仓后,马丁档与统盈均价按**剩余持仓**重计。
|
||||
|
||||
|
||||
|
||||
## 手动平仓 EA 单
|
||||
|
||||
|
||||
|
||||
在终端/手机/Web 上**手动平掉**本 EA 持仓(Magic 与备注 `[突破…]` 一致)时,会立即重置:
|
||||
|
||||
|
||||
|
||||
- **交易轮次** → 第 1 轮(仅统计备注为第 1 轮的持仓计档;更高轮次旧仓仍保留,统盈仍按各自轮次批处理)
|
||||
|
||||
- **双向满档**已处理标记
|
||||
|
||||
- **极值锁**:波段高/低突破与回落反向已用标记、回落监测、同 K 线限 1 笔
|
||||
|
||||
|
||||
|
||||
EA 程序平仓(统盈、止损、满档清仓、大单止盈等)**不会**触发上述重置。
|
||||
|
||||
|
||||
|
||||
## 说明
|
||||
|
||||
|
||||
|
||||
- 突破开仓与回落反向共用档位手数;列表长度 = 同向最多开仓笔数。
|
||||
|
||||
- 按批统盈、结余%止损、双向满档逻辑见 EA 输入注释。
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
# 20260526 大波段 + 聚合箱体突破 EA
|
||||
|
||||
## 逻辑概要
|
||||
|
||||
1. **波段识别(结构)**:MA 突破 → 极值 → 有效波段(0.1%~2%,最少 3 根 K)。`InpPullbackTolerance` 默认 0.05%,仅影响结构识别时是否切断波段。
|
||||
|
||||
2. **大波段突破**:最新有效波段振幅 **>0.25% 且 ≤2%** 时,在波段高/低挂突破单。备注 `LW{振幅点数}@{挂单价}`(成交后可对比成交价算滑点)。独立 SL/TP/移动止损/止损再挂。
|
||||
|
||||
3. **聚合箱体**:从最新向旧连续纳入振幅 **0.1%~0.25%** 的小波段,**1~3** 段成箱(超出 3 段则只保留最近 3 段),在箱沿挂突破单。备注 `BX{振幅点数}@{挂单价}`。独立 SL/TP/移动止损/止损再挂。
|
||||
|
||||
两套机制 **同时运行、互不影响**(同一 `InpMagicNumber`,靠订单备注区分)。
|
||||
|
||||
4. **交易时段(北京时间 UTC+8)**:默认 **关闭**;启用后按 `可交易时段`(如 `15:00-02:00`)才挂突破单,时段外撤销全部 EA 挂单,持仓照常管理。多段用逗号分隔。
|
||||
|
||||
## 代码参数对照
|
||||
|
||||
见 `参数设置.md`;`20260526_box_breakout.mq5` 中 input 默认值已与该文档对齐。
|
||||
|
||||
## 已移除(参数文档未定义)
|
||||
|
||||
回落反向、连续亏损冷冻、最大持仓笔数、单方向限仓、单箱仅一单、箱体合并包络上限、双 Magic 偏移、调试开关等。
|
||||
@@ -0,0 +1,52 @@
|
||||
## 波段识别参数
|
||||
|
||||
- K线周期:1min
|
||||
- MA周期:14
|
||||
- 最小波段振幅百分比:0.1
|
||||
- 最大波段阈值百分比:2
|
||||
- 反向突破容忍度:0.05
|
||||
- 有效波段最少K线数:3
|
||||
|
||||
## 大波段突破机制参数
|
||||
|
||||
- 启用大波段突破机制:true
|
||||
- 开仓方向:顺势
|
||||
- 最小波段振幅百分比(>该值):0.25
|
||||
- 最大波段振幅百分比(<=该值):2
|
||||
- 边界止损出局后是否允许再挂一单:false
|
||||
- 止损点数:80
|
||||
- 止盈点数:120
|
||||
- 使用移动止损:false
|
||||
|
||||
## 聚合箱体机制参数
|
||||
|
||||
- 启用聚合箱体突破机制:true
|
||||
- 开仓方向:顺势
|
||||
- 成箱最小波段振幅百分比(>=该值):0.1
|
||||
- 成箱最大波段振幅百分比(<=该值):0.25
|
||||
- 成箱最少有效波段数:1
|
||||
- 成箱最多有效波段数:3
|
||||
- 边界止损出局后是否允许再挂一单:false
|
||||
- 止损点数:80
|
||||
- 止盈点数:120
|
||||
- 使用移动止损:false
|
||||
|
||||
## 仓位管理模式
|
||||
|
||||
- 仓位管理模式:RiskPercent模式
|
||||
- 固定手数(固定手数模式):0.01
|
||||
- 每笔风险占结余%(RiskPercent模式): 5
|
||||
- 单笔最大手数:10
|
||||
|
||||
## 其他参数
|
||||
|
||||
- 禁止手工单:true
|
||||
- EA魔术号:20260526
|
||||
- 显示极值点标记:false
|
||||
- 显示箱体标记:false
|
||||
- 显示最近有效波段标记:false
|
||||
|
||||
## 交易时段(北京时间)
|
||||
|
||||
- 启用时段过滤:false(时段外撤掉本 EA 全部挂单;已有持仓仍管理 SL/TP/移动止损)
|
||||
- 可交易时段:15:00-02:00(跨日:当天 15:00 至次日 02:00 前;02:00 起不可交易至 15:00)
|
||||
Reference in New Issue
Block a user