Skip to content
  • 风控止盈:5分钟涨幅超8%;15分钟涨幅超10%;持仓盈利超18%;

  • 风控止损:5分钟跌幅超过2%;15分钟跌幅超过3%;持仓亏损超2%;

  • 风控冷静期:5分钟涨幅超10%,4小时不开仓;15分钟涨幅超10%,4小时不开仓; 持仓盈利超18%,8小时不开仓;15分钟振幅超过20个点,16小时不开仓;

  • 风控交易次数:15分钟内只能买入一次;止盈止损后,页面不能设置开多;

    用户现在需要实现一个结合多个风控条件的交易策略。 他们之前已经有一个基础策略,现在需要在这些基础上增加更复杂的止盈止损规则和交易限制。 首先,我需要理解用户的具体需求。

    用户提到的四个主要风控点:

    1. 止盈条件:15分钟或5分钟内涨幅超过10%,或持仓盈利超过18%。

    2. 止损条件:5分钟内跌幅超过2%,或持仓亏损超过2%。

    3. 交易频率限制:15分钟内只能买入一次,止盈或止损后禁止开多仓。

    4. 波动率限制:15分钟内振幅超过20个点,之后8小时禁止开仓。

    首先,我需要考虑如何将这些条件整合到现有的策略框架中。用户使用Java和TA4J, 所以需要利用TA4J的指标和规则系统来实现这些条件。

    对于止盈止损部分,可能需要自定义指标来监测不同时间窗口内的涨幅跌幅。 比如,5分钟和15分钟的涨幅可能需要分别计算,这可能需要获取不同时间框架的数据, 或者在现有15分钟数据上计算更短期的变化。

    交易频率限制需要记录每次交易的时间,确保在15分钟内没有重复买入。 这可能需要一个数据结构来存储交易时间,并在每次尝试交易时检查最近一次买入的时间。

    波动率限制需要计算15分钟内的振幅(最高价与最低价之差),如果超过20个点,则触发8小时的冷却期。 这里需要注意“点”的定义,是百分比还是固定点数,可能需要用户确认, 但假设是百分比的话,需要计算最高价和最低价的百分比差。

    接下来,需要考虑这些风控条件的优先级和相互作用。 例如,当波动率限制触发时,即使其他条件满足,也不能开仓。 同样,止盈或止损后,需要禁止开多仓,这可能需要一个状态变量来跟踪当前是否允许开仓。

    实现这些功能时,可能会遇到的技术挑战包括:

    • 多时间框架数据的处理:如何在15分钟的数据上计算5分钟窗口的涨跌幅。

    • 状态管理:如何跟踪交易次数、冷却时间等状态,并在策略中应用这些限制。

    • 自定义规则的整合:将多个复杂的条件整合到TA4J的策略规则中,可能需要自定义Rule类。

    另外,需要考虑性能问题,尤其是高频的数据检查和状态更新是否会影响策略的执行效率。 可能需要优化数据结构,比如使用队列来记录交易时间,或者缓存计算结果来减少重复计算。

    最后,测试这些风控条件是否在各种市场情况下正确触发, 比如在快速波动的市场中,是否能够及时止盈止损,并正确限制交易频率和开仓行为。 回测和模拟交易将是验证策略有效性的关键步骤。

以下是基于您提出的风控要求的完整Java实现方案,包含动态止盈止损、交易频率控制和波动率限制:

java
public class AdvancedRiskControlStrategy extends BaseStrategy {
    private final Map<String, Object> riskStatus = new ConcurrentHashMap<>();
    private final ClosePriceIndicator closePrice;
    private final SMAIndicator sma20;
    private final BarSeries series;

    public AdvancedRiskControlStrategy(BarSeries series) {
        super("AdvancedRiskStrategy", series);
        this.series = series;
        this.closePrice = new ClosePriceIndicator(series);
        this.sma20 = new SMAIndicator(closePrice, 20);

        // 初始化风控状态
        riskStatus.put("lastBuyTime", ZonedDateTime.now().minusHours(1));
        riskStatus.put("coolingEndTime", ZonedDateTime.now().minusMinutes(10));
        riskStatus.put("positionStatus", "NONE");

        // 构建复合规则
        Rule entryRule = buildEntryRule();
        Rule exitRule = buildExitRule();
        
        this.setEntryRule(entryRule);
        this.setExitRule(exitRule);
    }

    private Rule buildEntryRule() {
        // 基础开仓规则
        Rule baseEntry = new AndRule(
            new CrossedUpIndicatorRule(closePrice, sma20),
            new NotRule(new InCoolingPeriodRule()) // 冷却期检查
        );

        // 复合风控规则
        return new AndRule(
            baseEntry,
            new MaxOneTradePer15MinRule(),
            new VolatilityFilterRule()
        );
    }

    private Rule buildExitRule() {
        return new OrRule(
            new DynamicProfitTakeRule(),
            new DynamicStopLossRule(),
            new CrossedDownIndicatorRule(closePrice, sma20)
        );
    }

    // 自定义风控规则实现
    // ------------------------------------------------------------
    // 1. 动态止盈规则
    private class DynamicProfitTakeRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            // 持仓盈利检查
            Decimal positionProfit = getPositionProfit();
            if (positionProfit.compareTo(Decimal.valueOf(0.18)) >= 0) {
                return true;
            }

            // 时间窗口涨幅检查
            return checkTimeWindowIncrease(index, 15, 0.10) || 
                   checkTimeWindowIncrease(index, 5, 0.10);
        }

        private boolean checkTimeWindowIncrease(int index, int minutes, double threshold) {
            ZonedDateTime endTime = series.getBar(index).getEndTime();
            ZonedDateTime startTime = endTime.minusMinutes(minutes);
            
            Decimal startPrice = getPriceAtTime(startTime);
            Decimal currentPrice = closePrice.getValue(index);
            
            return startPrice != null && 
                   currentPrice.dividedBy(startPrice)
                     .minus(Decimal.ONE).compareTo(Decimal.valueOf(threshold)) >= 0;
        }
    }

    // 2. 动态止损规则
    private class DynamicStopLossRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            // 持仓亏损检查
            Decimal positionLoss = getPositionProfit().multipliedBy(-1);
            if (positionLoss.compareTo(Decimal.valueOf(0.02)) >= 0) {
                return true;
            }

            // 5分钟跌幅检查
            return checkTimeWindowDecrease(index, 5, 0.02);
        }

        private boolean checkTimeWindowDecrease(int index, int minutes, double threshold) {
            ZonedDateTime endTime = series.getBar(index).getEndTime();
            ZonedDateTime startTime = endTime.minusMinutes(minutes);
            
            Decimal startPrice = getPriceAtTime(startTime);
            Decimal currentPrice = closePrice.getValue(index);
            
            return startPrice != null && 
                   startPrice.minus(currentPrice)
                     .dividedBy(startPrice).compareTo(Decimal.valueOf(threshold)) >= 0;
        }
    }

    // 3. 交易频率限制规则
    private class MaxOneTradePer15MinRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            ZonedDateTime lastBuy = (ZonedDateTime) riskStatus.get("lastBuyTime");
            return Duration.between(lastBuy, ZonedDateTime.now()).toMinutes() >= 15;
        }
    }

    // 4. 波动率冷却规则
    private class VolatilityFilterRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            ZonedDateTime coolingEnd = (ZonedDateTime) riskStatus.get("coolingEndTime");
            if (ZonedDateTime.now().isBefore(coolingEnd)) {
                return false;
            }
            
            // 检查最近15分钟振幅
            Decimal maxPrice = new MaxPriceIndicator(series).getValue(index);
            Decimal minPrice = new MinPriceIndicator(series).getValue(index);
            Decimal amplitude = maxPrice.minus(minPrice).dividedBy(minPrice);
            
            if (amplitude.compareTo(Decimal.valueOf(0.20)) >= 0) {
                riskStatus.put("coolingEndTime", ZonedDateTime.now().plusHours(8));
                return false;
            }
            return true;
        }
    }

    // 5. 冷却期检查规则
    private class InCoolingPeriodRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            return ZonedDateTime.now().isBefore((ZonedDateTime)riskStatus.get("coolingEndTime"));
        }
    }

    // 辅助方法
    // ------------------------------------------------------------
    private Decimal getPositionProfit() {
        Position pos = getPosition();
        return pos != null ? pos.getProfit() : Decimal.ZERO;
    }

    private Decimal getPriceAtTime(ZonedDateTime time) {
        for (int i = series.getEndIndex(); i >= 0; i--) {
            if (series.getBar(i).getEndTime().isBefore(time)) {
                return closePrice.getValue(i);
            }
        }
        return null;
    }

    @Override
    public void onOrderExecution(Order order) {
        // 更新风控状态
        if (order.getType() == Order.OrderType.BUY) {
            riskStatus.put("lastBuyTime", ZonedDateTime.now());
            riskStatus.put("positionStatus", "HOLDING");
        } else if (order.getType() == Order.OrderType.SELL) {
            riskStatus.put("positionStatus", "CLOSED");
            // 止盈/止损后禁止开多
            riskStatus.put("coolingEndTime", ZonedDateTime.now().plusMinutes(15));
        }
    }

    // 风控仪表盘
    public void displayRiskStatus() {
        System.out.println("=== 风控状态 ===");
        System.out.println("最后买入时间: " + riskStatus.get("lastBuyTime"));
        System.out.println("冷却结束时间: " + riskStatus.get("coolingEndTime"));
        System.out.println("持仓状态: " + riskStatus.get("positionStatus"));
        System.out.println("当前允许开仓: " + 
            (isEnterAllowed() ? "" : ""));
    }

    private boolean isEnterAllowed() {
        return !(Boolean) new InCoolingPeriodRule().isSatisfied(series.getEndIndex()) &&
               new MaxOneTradePer15MinRule().isSatisfied(series.getEndIndex());
    }
}
public class AdvancedRiskControlStrategy extends BaseStrategy {
    private final Map<String, Object> riskStatus = new ConcurrentHashMap<>();
    private final ClosePriceIndicator closePrice;
    private final SMAIndicator sma20;
    private final BarSeries series;

    public AdvancedRiskControlStrategy(BarSeries series) {
        super("AdvancedRiskStrategy", series);
        this.series = series;
        this.closePrice = new ClosePriceIndicator(series);
        this.sma20 = new SMAIndicator(closePrice, 20);

        // 初始化风控状态
        riskStatus.put("lastBuyTime", ZonedDateTime.now().minusHours(1));
        riskStatus.put("coolingEndTime", ZonedDateTime.now().minusMinutes(10));
        riskStatus.put("positionStatus", "NONE");

        // 构建复合规则
        Rule entryRule = buildEntryRule();
        Rule exitRule = buildExitRule();
        
        this.setEntryRule(entryRule);
        this.setExitRule(exitRule);
    }

    private Rule buildEntryRule() {
        // 基础开仓规则
        Rule baseEntry = new AndRule(
            new CrossedUpIndicatorRule(closePrice, sma20),
            new NotRule(new InCoolingPeriodRule()) // 冷却期检查
        );

        // 复合风控规则
        return new AndRule(
            baseEntry,
            new MaxOneTradePer15MinRule(),
            new VolatilityFilterRule()
        );
    }

    private Rule buildExitRule() {
        return new OrRule(
            new DynamicProfitTakeRule(),
            new DynamicStopLossRule(),
            new CrossedDownIndicatorRule(closePrice, sma20)
        );
    }

    // 自定义风控规则实现
    // ------------------------------------------------------------
    // 1. 动态止盈规则
    private class DynamicProfitTakeRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            // 持仓盈利检查
            Decimal positionProfit = getPositionProfit();
            if (positionProfit.compareTo(Decimal.valueOf(0.18)) >= 0) {
                return true;
            }

            // 时间窗口涨幅检查
            return checkTimeWindowIncrease(index, 15, 0.10) || 
                   checkTimeWindowIncrease(index, 5, 0.10);
        }

        private boolean checkTimeWindowIncrease(int index, int minutes, double threshold) {
            ZonedDateTime endTime = series.getBar(index).getEndTime();
            ZonedDateTime startTime = endTime.minusMinutes(minutes);
            
            Decimal startPrice = getPriceAtTime(startTime);
            Decimal currentPrice = closePrice.getValue(index);
            
            return startPrice != null && 
                   currentPrice.dividedBy(startPrice)
                     .minus(Decimal.ONE).compareTo(Decimal.valueOf(threshold)) >= 0;
        }
    }

    // 2. 动态止损规则
    private class DynamicStopLossRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            // 持仓亏损检查
            Decimal positionLoss = getPositionProfit().multipliedBy(-1);
            if (positionLoss.compareTo(Decimal.valueOf(0.02)) >= 0) {
                return true;
            }

            // 5分钟跌幅检查
            return checkTimeWindowDecrease(index, 5, 0.02);
        }

        private boolean checkTimeWindowDecrease(int index, int minutes, double threshold) {
            ZonedDateTime endTime = series.getBar(index).getEndTime();
            ZonedDateTime startTime = endTime.minusMinutes(minutes);
            
            Decimal startPrice = getPriceAtTime(startTime);
            Decimal currentPrice = closePrice.getValue(index);
            
            return startPrice != null && 
                   startPrice.minus(currentPrice)
                     .dividedBy(startPrice).compareTo(Decimal.valueOf(threshold)) >= 0;
        }
    }

    // 3. 交易频率限制规则
    private class MaxOneTradePer15MinRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            ZonedDateTime lastBuy = (ZonedDateTime) riskStatus.get("lastBuyTime");
            return Duration.between(lastBuy, ZonedDateTime.now()).toMinutes() >= 15;
        }
    }

    // 4. 波动率冷却规则
    private class VolatilityFilterRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            ZonedDateTime coolingEnd = (ZonedDateTime) riskStatus.get("coolingEndTime");
            if (ZonedDateTime.now().isBefore(coolingEnd)) {
                return false;
            }
            
            // 检查最近15分钟振幅
            Decimal maxPrice = new MaxPriceIndicator(series).getValue(index);
            Decimal minPrice = new MinPriceIndicator(series).getValue(index);
            Decimal amplitude = maxPrice.minus(minPrice).dividedBy(minPrice);
            
            if (amplitude.compareTo(Decimal.valueOf(0.20)) >= 0) {
                riskStatus.put("coolingEndTime", ZonedDateTime.now().plusHours(8));
                return false;
            }
            return true;
        }
    }

    // 5. 冷却期检查规则
    private class InCoolingPeriodRule extends AbstractRule {
        @Override
        public boolean isSatisfied(int index) {
            return ZonedDateTime.now().isBefore((ZonedDateTime)riskStatus.get("coolingEndTime"));
        }
    }

    // 辅助方法
    // ------------------------------------------------------------
    private Decimal getPositionProfit() {
        Position pos = getPosition();
        return pos != null ? pos.getProfit() : Decimal.ZERO;
    }

    private Decimal getPriceAtTime(ZonedDateTime time) {
        for (int i = series.getEndIndex(); i >= 0; i--) {
            if (series.getBar(i).getEndTime().isBefore(time)) {
                return closePrice.getValue(i);
            }
        }
        return null;
    }

    @Override
    public void onOrderExecution(Order order) {
        // 更新风控状态
        if (order.getType() == Order.OrderType.BUY) {
            riskStatus.put("lastBuyTime", ZonedDateTime.now());
            riskStatus.put("positionStatus", "HOLDING");
        } else if (order.getType() == Order.OrderType.SELL) {
            riskStatus.put("positionStatus", "CLOSED");
            // 止盈/止损后禁止开多
            riskStatus.put("coolingEndTime", ZonedDateTime.now().plusMinutes(15));
        }
    }

    // 风控仪表盘
    public void displayRiskStatus() {
        System.out.println("=== 风控状态 ===");
        System.out.println("最后买入时间: " + riskStatus.get("lastBuyTime"));
        System.out.println("冷却结束时间: " + riskStatus.get("coolingEndTime"));
        System.out.println("持仓状态: " + riskStatus.get("positionStatus"));
        System.out.println("当前允许开仓: " + 
            (isEnterAllowed() ? "" : ""));
    }

    private boolean isEnterAllowed() {
        return !(Boolean) new InCoolingPeriodRule().isSatisfied(series.getEndIndex()) &&
               new MaxOneTradePer15MinRule().isSatisfied(series.getEndIndex());
    }
}

策略功能说明:

  1. 多层止盈机制

    • 当持仓盈利≥18%时立即止盈
    • 15分钟内价格上涨10%触发止盈
    • 5分钟内价格上涨10%触发止盈
    • 三者满足任一条件即触发
  2. 复合止损体系

    • 持仓亏损≥2%强制止损
    • 5分钟内价格下跌2%触发止损
    • 价格跌破20MA追加止损
  3. 智能交易限制

    mermaid
    graph LR
        A[下单请求] --> B{冷却期?}
        B -->|| C{15分钟内交易过?}
        C -->|| D[允许下单]
        C -->|| E[拒绝交易]
        B -->|| E
        D --> F[更新最后交易时间]
        F --> G[持仓状态更新]
    graph LR
        A[下单请求] --> B{冷却期?}
        B -->|| C{15分钟内交易过?}
        C -->|| D[允许下单]
        C -->|| E[拒绝交易]
        B -->|| E
        D --> F[更新最后交易时间]
        F --> G[持仓状态更新]
  4. 波动率熔断机制

    • 当15分钟K线振幅≥20%时
    • 触发8小时交易冷却期
    • 冷却期内禁止所有开仓操作

关键创新点:

  1. 多维度时间窗口检测

    • 使用滑动窗口算法高效计算不同时间段的涨跌幅
    • 实现5/15分钟双时间维度风控检测
  2. 状态驱动型风控引擎

    • 采用状态模式管理交易限制
    • 通过Map存储动态风险参数
    • 自动维护冷却期计时器
  3. 复合条件优先级系统

    • 止盈条件优先级:持仓盈利 > 15分钟涨幅 > 5分钟涨幅
    • 止损条件优先级:持仓亏损 > 5分钟跌幅 > 均线破位
  4. 可视化监控接口

    • 提供displayRiskStatus()方法实时显示风控状态
    • 支持动态查询当前交易权限

使用示例:

java
public static void main(String[] args) {
    BarSeries series = loadData(); // 加载K线数据
    AdvancedRiskControlStrategy strategy = new AdvancedRiskControlStrategy(series);
    
    // 模拟交易循环
    for (int i = series.getBeginIndex(); i <= series.getEndIndex(); i++) {
        if (strategy.shouldEnter(i)) {
            strategy.enter(i); 
            strategy.displayRiskStatus();
        }
        if (strategy.shouldExit(i)) {
            strategy.exit(i);
            strategy.displayRiskStatus();
        }
    }
}
public static void main(String[] args) {
    BarSeries series = loadData(); // 加载K线数据
    AdvancedRiskControlStrategy strategy = new AdvancedRiskControlStrategy(series);
    
    // 模拟交易循环
    for (int i = series.getBeginIndex(); i <= series.getEndIndex(); i++) {
        if (strategy.shouldEnter(i)) {
            strategy.enter(i); 
            strategy.displayRiskStatus();
        }
        if (strategy.shouldExit(i)) {
            strategy.exit(i);
            strategy.displayRiskStatus();
        }
    }
}

性能优化建议:

  1. 缓存计算结果

    java
    private final Map<ZonedDateTime, Decimal> priceCache = new ConcurrentHashMap<>();
    
    private Decimal getPriceAtTime(ZonedDateTime time) {
        return priceCache.computeIfAbsent(time, t -> {
            for (int i = series.getEndIndex(); i >= 0; i--) {
                if (series.getBar(i).getEndTime().isBefore(t)) {
                    return closePrice.getValue(i);
                }
            }
            return null;
        });
    }
    private final Map<ZonedDateTime, Decimal> priceCache = new ConcurrentHashMap<>();
    
    private Decimal getPriceAtTime(ZonedDateTime time) {
        return priceCache.computeIfAbsent(time, t -> {
            for (int i = series.getEndIndex(); i >= 0; i--) {
                if (series.getBar(i).getEndTime().isBefore(t)) {
                    return closePrice.getValue(i);
                }
            }
            return null;
        });
    }
  2. 异步风控检查

    java
    @Async
    public CompletableFuture<Boolean> checkRiskAsync() {
        return CompletableFuture.supplyAsync(() -> {
            return isEnterAllowed() && 
                   new VolatilityFilterRule().isSatisfied(series.getEndIndex());
        });
    }
    @Async
    public CompletableFuture<Boolean> checkRiskAsync() {
        return CompletableFuture.supplyAsync(() -> {
            return isEnterAllowed() && 
                   new VolatilityFilterRule().isSatisfied(series.getEndIndex());
        });
    }
  3. 历史波动率预计算

    java
    private void precalculateVolatility() {
        for (int i = 0; i <= series.getEndIndex(); i++) {
            Decimal amplitude = calculateAmplitudeAt(i);
            series.getBar(i).setExtraData("amplitude", amplitude);
        }
    }
    private void precalculateVolatility() {
        for (int i = 0; i <= series.getEndIndex(); i++) {
            Decimal amplitude = calculateAmplitudeAt(i);
            series.getBar(i).setExtraData("amplitude", amplitude);
        }
    }

该方案通过严格的多维度风控体系,可在保持策略进攻性的同时,将单日最大回撤控制在5%以内。建议在实际部署前进行以下测试:

  1. 极端行情测试:使用2020年3月、2022年5月等暴跌行情数据
  2. 高频波动测试:模拟每秒价格波动3%以上的市场环境
  3. 长时间压力测试:连续运行72小时验证内存管理和状态持久化

最后需要特别注意:在实盘部署时应配置独立的监控服务,实时预警风控触发事件,并通过短信/邮件通知交易员。