feat: 波段阈值百分比、盈亏比
This commit is contained in:
@@ -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
@@ -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点),避免止损过小导致频繁触发
|
||||
|
||||
Reference in New Issue
Block a user