feat: 波段阈值百分比、盈亏比

This commit is contained in:
zhangyangbin
2026-05-17 07:05:39 +08:00
parent 45de4103cd
commit 9538444713
2 changed files with 86 additions and 114 deletions
+54 -99
View File
@@ -14,14 +14,12 @@
input group "=== 波段识别参数 ==="
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M1; // K线周期
input int InpMAPeriod = 14; // MA周期
input bool InpUsePercentMode = true; // 使用百分比模式
input int InpWaveThreshold = 1000; // 波段阈值(点数/固定模式)
input double InpWavePercent = 0.25; // 波段阈值(百分比/百分比模式)
input double InpWavePercent = 0.1; // 波段阈值百分比(%)
input double InpPullbackTolerance = 0.0; // 反向突破容忍度(%) 0=不容忍
input group "=== 风险管理参数 ==="
input int InpStopLoss = 200; // 止损点数
input int InpTakeProfit = 300; // 止盈点数
input double InpStopLossPercent = 0.05; // 止损百分比(%)
input double InpRiskRewardRatio = 1.5; // 盈亏比
input int InpMaxHoldingMinutes = 5; // 最大持仓时间(分钟)
input group "=== 仓位管理参数 ==="
@@ -72,7 +70,6 @@ void CheckOpenSignals();
bool OpenPosition(ENUM_ORDER_TYPE order_type, double wave_high, double wave_low, double threshold_points);
double CalculateLotSize();
void ManagePositions();
bool CheckTakeProfitReached(ulong ticket);
void CheckTrailingStop(ulong ticket);
//+------------------------------------------------------------------+
@@ -105,15 +102,12 @@ int OnInit()
Print("品种:", _Symbol);
Print("K线周期:", EnumToString(InpTimeframe));
Print("MA周期:", InpMAPeriod);
if(InpUsePercentMode)
Print("波段阈值模式: 百分比 - ", InpWavePercent, "%");
else
Print("波段阈值模式: 固定点数 - ", InpWaveThreshold, "点");
Print("波段阈值: ", InpWavePercent, "%");
if(InpPullbackTolerance > 0)
Print("反向突破容忍度: ", DoubleToString(InpPullbackTolerance, 1), "% (启用)");
else
Print("反向突破容忍度: 0% (禁用 - 保持原有逻辑)");
Print("止损:", InpStopLoss, " | 盈:", InpTakeProfit, "点");
Print("止损:", InpStopLossPercent, "% | 盈亏比:", InpRiskRewardRatio);
Print("最大持仓时间:", InpMaxHoldingMinutes, "分钟");
Print("最大开仓手数:", InpMaxPositions);
Print("手数模式:", (InpUseCompounding ? "复利" : "固定"),
@@ -358,16 +352,9 @@ void UpdateLatestValidWave()
double price_diff = MathAbs(extremes[i].price - extremes[i-1].price);
double price_diff_points = price_diff / _Point;
// 计算阈值
double threshold = 0;
if(InpUsePercentMode) {
// 百分比模式:以前一个极值点价格为基准计算百分比
double base_price = extremes[i-1].price;
threshold = (base_price * InpWavePercent / 100.0) / _Point;
} else {
// 固定点数模式
threshold = InpWaveThreshold;
}
// 计算阈值(百分比模式:以前一个极值点价格为基准计算百分比)
double base_price = extremes[i-1].price;
double threshold = (base_price * InpWavePercent / 100.0) / _Point;
if(price_diff_points >= threshold) {
extremes[i-1].is_valid = true;
@@ -385,14 +372,9 @@ void UpdateLatestValidWave()
double price_diff = MathAbs(extremes[i].price - extremes[i-1].price);
double price_diff_points = price_diff / _Point;
// 计算阈值
double threshold = 0;
if(InpUsePercentMode) {
double base_price = extremes[i-1].price;
threshold = (base_price * InpWavePercent / 100.0) / _Point;
} else {
threshold = InpWaveThreshold;
}
// 计算阈值(百分比模式)
double base_price = extremes[i-1].price;
double threshold = (base_price * InpWavePercent / 100.0) / _Point;
if(price_diff_points >= threshold) {
// 找到最新的有效波段
@@ -420,13 +402,10 @@ void UpdateLatestValidWave()
}
if(InpShowDebugInfo) {
string threshold_info = InpUsePercentMode ?
StringFormat("%.2f%% (%.0f点)", InpWavePercent, threshold) :
StringFormat("%d点", InpWaveThreshold);
Print("更新最新有效波段 - 高:", DoubleToString(high, _Digits),
" 低:", DoubleToString(low, _Digits),
" 价差:", (int)price_diff_points, "点",
" 阈值:", threshold_info);
" 阈值:", StringFormat("%.2f%% (%.0f点)", InpWavePercent, threshold));
}
}
break;
@@ -578,16 +557,9 @@ void CheckOpenSignals()
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// 计算当前波段的实际阈值
double wave_threshold_points = 0;
if(InpUsePercentMode) {
// 百分比模式:取高低点的平均值作为基准
double base_price = (latest_wave.high_price + latest_wave.low_price) / 2.0;
wave_threshold_points = (base_price * InpWavePercent / 100.0) / _Point;
} else {
// 固定点数模式
wave_threshold_points = InpWaveThreshold;
}
// 计算当前波段的实际阈值(百分比模式:取高低点的平均值作为基准)
double base_price = (latest_wave.high_price + latest_wave.low_price) / 2.0;
double wave_threshold_points = (base_price * InpWavePercent / 100.0) / _Point;
// 检查多单信号:突破高点(且该高点未使用过)
if(!latest_wave.high_used && current_price > latest_wave.high_price) {
@@ -627,6 +599,17 @@ bool OpenPosition(ENUM_ORDER_TYPE order_type, double wave_high, double wave_low,
SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
SymbolInfoDouble(_Symbol, SYMBOL_BID);
// 计算止损和止盈(基于开仓价的百分比)
double stop_loss_amount = current_price * InpStopLossPercent / 100.0;
// 确保止损金额不小于1美元(100点)
double min_stop_loss = 1.0; // 1美元 = 100点
if(stop_loss_amount < min_stop_loss) {
stop_loss_amount = min_stop_loss;
}
double take_profit_amount = stop_loss_amount * InpRiskRewardRatio;
double sl = 0, tp = 0;
bool result = false;
@@ -638,24 +621,28 @@ bool OpenPosition(ENUM_ORDER_TYPE order_type, double wave_high, double wave_low,
threshold_points);
if(order_type == ORDER_TYPE_BUY) {
sl = current_price - InpStopLoss * _Point;
tp = current_price + InpTakeProfit * _Point;
sl = current_price - stop_loss_amount;
tp = current_price + take_profit_amount;
result = trade.Buy(lots, _Symbol, 0, sl, tp, comment);
if(result) {
Print("开多单成功 - 手数:", lots,
" 波段:H:", wave_high, " L:", wave_low,
" 止损:", sl, " 止盈:", tp);
" 开仓价:", current_price,
" 止损:", sl, "(", InpStopLossPercent, "%)",
" 止盈:", tp, "(盈亏比", InpRiskRewardRatio, ")");
}
} else {
sl = current_price + InpStopLoss * _Point;
tp = current_price - InpTakeProfit * _Point;
sl = current_price + stop_loss_amount;
tp = current_price - take_profit_amount;
result = trade.Sell(lots, _Symbol, 0, sl, tp, comment);
if(result) {
Print("开空单成功 - 手数:", lots,
" 波段:H:", wave_high, " L:", wave_low,
" 止损:", sl, " 止盈:", tp);
" 开仓价:", current_price,
" 止损:", sl, "(", InpStopLossPercent, "%)",
" 止盈:", tp, "(盈亏比", InpRiskRewardRatio, ")");
}
}
@@ -717,53 +704,12 @@ void ManagePositions()
continue;
}
// 手动检查止盈(防止跳空未触发)
ulong ticket = PositionGetTicket(i);
if(CheckTakeProfitReached(ticket)) {
trade.PositionClose(ticket);
Print("手动止盈平仓 - Ticket:", ticket);
continue;
}
// 检查移动止损
ulong ticket = PositionGetTicket(i);
CheckTrailingStop(ticket);
}
}
//+------------------------------------------------------------------+
//| 检查是否达到止盈(防止跳空未触发) |
//+------------------------------------------------------------------+
bool CheckTakeProfitReached(ulong ticket)
{
if(!PositionSelectByTicket(ticket))
return false;
// 如果止盈设置为0,不检查
if(InpTakeProfit <= 0)
return false;
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double current_price = (pos_type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// 计算浮盈点数
double profit_points = 0;
if(pos_type == POSITION_TYPE_BUY) {
profit_points = (current_price - open_price) / _Point;
} else {
profit_points = (open_price - current_price) / _Point;
}
// 检查是否达到或超过止盈点数
if(profit_points >= InpTakeProfit) {
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查移动止损 |
//+------------------------------------------------------------------+
@@ -779,16 +725,25 @@ void CheckTrailingStop(ulong ticket)
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// 计算浮盈点数
double profit_points = 0;
if(pos_type == POSITION_TYPE_BUY) {
profit_points = (current_price - open_price) / _Point;
} else {
profit_points = (open_price - current_price) / _Point;
// 计算止损金额(开仓价的百分比)
double stop_loss_amount = open_price * InpStopLossPercent / 100.0;
// 确保止损金额不小于1美元(100点)
double min_stop_loss = 1.0; // 1美元 = 100点
if(stop_loss_amount < min_stop_loss) {
stop_loss_amount = min_stop_loss;
}
// 浮盈达到止损点数,移动止损至成本价
if(profit_points >= InpStopLoss) {
// 计算浮盈
double profit_amount = 0;
if(pos_type == POSITION_TYPE_BUY) {
profit_amount = current_price - open_price;
} else {
profit_amount = open_price - current_price;
}
// 浮盈达到止损金额,移动止损至成本价
if(profit_amount >= stop_loss_amount) {
double new_sl = open_price;
// 检查是否需要更新
+32 -15
View File
@@ -18,7 +18,8 @@
## 开仓逻辑
- 价格突破有效波段的高点开多单,价格突破有效波点的低点开空单
- 该开仓逻辑仅适用最新的一个有效波段
- 浮盈达到止损点数后,即可将止损移动至成本价
- 止损和止盈基于开仓价的百分比计算,止盈 = 止损金额 × 盈亏比
- 浮盈达到止损金额后,即可将止损移动至成本价
- 已经开仓后,继续监控新的有效波段突破,每单开仓后就与有效波段无关了
## 参数设置
@@ -26,14 +27,12 @@
### 波段识别参数
- K线周期:支持参数设置,不跟随图表,默认 **1 Minute**
- MA周期:默认 **14**
- 使用百分比模式:默认 **false**(使用固定点数模式)
- 波段阈值点数:默认 **1000点**(固定模式有效)
- 波段阈值百分比:默认 **0.1%**(百分比模式有效)
- 波段阈值百分比:默认 **0.1%**
- **反向突破容忍度:默认0.0%(新功能 - 详见下方说明)**
### 风险管理参数
- 止损点数:默认 **200点**
- 止盈点数:默认 **300点**
- 止损百分比:默认 **0.05%**(基于开仓价计算,最小1美元/100点)
- 盈亏比:默认 **1.5**(止盈 = 止损金额 × 盈亏比)
- 最大持仓时间:默认 **5分钟**
### 仓位管理参数
@@ -90,14 +89,32 @@
3. 建议先回测不同容忍度,找到最适合的数值
4. 容忍度只影响波段识别,不影响止损止盈等其他逻辑
### 波段阈值模式说明
**固定点数模式**
- 相邻极值点价差达到固定点数即为有效波段
- 适合明确知道想要的波段大小的场景
- 例如:设置1000点,则任何1000点以上的波段都有效
**百分比模式:**
### 波段阈值说明
本策略使用**百分比模式**计算波段阈值:
- 以前一个极值点价格为基准,按百分比计算阈值
- 动态适应不同价格水平
- 例如:设置1%,价格4700时阈值为47美元(4700点),价格2350时阈值为23.5美元(2350点)
- 适合价格波动较大或需要根据价格水平动态调整的场景
- 例如:设置0.1%,价格2600时阈值为2.6美元(260点),价格4700时阈值为4.7美元(470点)
- 百分比模式能够根据价格水平自动调整,更好地适应市场变化
### 止损止盈说明
本策略使用**百分比模式**计算止损和止盈:
- **止损计算**:止损金额 = MAX(开仓价 × 止损百分比, 1美元)
- 示例1(高价位):开仓价2600,止损0.05%,计算值 = 2600 × 0.05% = 1.3美元 > 1美元,使用1.3美元
- 多单止损价 = 2600 - 1.3 = 2598.7
- 空单止损价 = 2600 + 1.3 = 2601.3
- 示例2(低价位):开仓价1500,止损0.05%,计算值 = 1500 × 0.05% = 0.75美元 < 1美元,使用最小值1美元
- 多单止损价 = 1500 - 1 = 1499.0
- 空单止损价 = 1500 + 1 = 1501.0
- **止盈计算**:止盈金额 = 止损金额 × 盈亏比
- 示例1:止损金额1.3美元,盈亏比1.5,止盈金额 = 1.3 × 1.5 = 1.95美元
- 多单止盈价 = 2600 + 1.95 = 2601.95
- 空单止盈价 = 2600 - 1.95 = 2598.05
- 示例2:止损金额1美元,盈亏比1.5,止盈金额 = 1 × 1.5 = 1.5美元
- 多单止盈价 = 1500 + 1.5 = 1501.5
- 空单止盈价 = 1500 - 1.5 = 1498.5
- **移动止损**:当浮盈达到止损金额时,自动将止损移至成本价(开仓价)
- **最小止损保护**:无论百分比设置多小,止损金额始终不低于1美元(100点),避免止损过小导致频繁触发