You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java WebSocket百分比价格告警:如何存储首条消息计算阈值

实现百分比条件告警的方案

1. 扩展告警类型与实体字段

首先需要在告警类型枚举(AlertType)中新增百分比相关的类型:

  • l_percent:价格下跌指定百分比触发告警
  • u_percent:价格上涨指定百分比触发告警

同时在Alert实体中添加percentage字段,用来存储用户设置的百分比数值(例如用户设置下跌5%,该字段值为5)。

2. 修改告警逻辑,存储初始价格与计算阈值

在AlertUserByPrice方法中,我们需要在WebSocket消息处理器里记录初始价格并计算告警阈值。由于WebSocket消息处理是异步的,用AtomicReference安全存储初始价格(保证线程可见性),用volatile变量存储计算后的阈值。

修改后的AlertUserByPrice代码如下:

public void AlertUserByPrice(Long id) {
    Alert alert = alertRepository.findById(id).orElseThrow(() -> new NoSuchElementException());

    String type = alert.getAlertType().getKey();
    double setPrice = alert.getPrice();
    double percentage = alert.getPercentage(); // 获取用户设置的百分比
    String ticker = alert.getTicker();

    JSONParser jsonParser = new JSONParser();

    NotificationRequest build = null;

    // 修复字符串比较:用equals代替==
    if (type.equals("l_break")) {
        build = NotificationRequest.builder()
                .title(ticker + " alert")
                .message(setPrice + " broke down")
                .token(notificationService.getToken(userDetailService.returnUser().getEmail()))
                .build();
    } else if (type.equals("upper_break")) {
        build = NotificationRequest.builder()
                .title(ticker + " alert")
                .message(setPrice + " pierced upward")
                .token(notificationService.getToken(userDetailService.returnUser().getEmail()))
                .build();
    } else if (type.equals("l_percent")) {
        build = NotificationRequest.builder()
                .title(ticker + " percentage alert")
                .message("Price dropped by " + percentage + "%")
                .token(notificationService.getToken(userDetailService.returnUser().getEmail()))
                .build();
    } else if (type.equals("u_percent")) {
        build = NotificationRequest.builder()
                .title(ticker + " percentage alert")
                .message("Price rose by " + percentage + "%")
                .token(notificationService.getToken(userDetailService.returnUser().getEmail()))
                .build();
    }

    try {
        final WebsocketClientEndpoint clientEndPoint = new WebsocketClientEndpoint();
        Session session = clientEndPoint.connect(new URI("wss://ws.coincap.io/prices?assets=" + ticker));

        // 用AtomicReference存储初始价格,保证线程安全
        final AtomicReference<Double> initialPrice = new AtomicReference<>();
        // 用volatile存储阈值,保证后续消息处理时能读到最新值
        volatile double thresholdPrice = 0.0;

        WebsocketClientEndpoint.MessageHandler handler = new WebsocketClientEndpoint.MessageHandler() {
            public void handleMessage(String message) throws ParseException, IOException {
                Object obj = jsonParser.parse(message);
                JSONObject jsonObject = (JSONObject) obj;
                double currentPrice = Double.parseDouble(jsonObject.get(ticker).toString());

                System.out.println("当前价格 : " + currentPrice);

                // 百分比类型告警:第一次收到消息时计算阈值
                if ((type.equals("l_percent") || type.equals("u_percent")) && initialPrice.get() == null) {
                    initialPrice.set(currentPrice);
                    if (type.equals("l_percent")) {
                        // 下跌阈值:初始价格 * (1 - 百分比/100)
                        thresholdPrice = currentPrice * (1 - percentage / 100);
                        System.out.println("下跌告警阈值已计算:" + thresholdPrice);
                    } else {
                        // 上涨阈值:初始价格 * (1 + 百分比/100)
                        thresholdPrice = currentPrice * (1 + percentage / 100);
                        System.out.println("上涨告警阈值已计算:" + thresholdPrice);
                    }
                    // 第一次仅计算阈值,不触发告警
                    return;
                }

                // 判断告警触发条件
                boolean shouldAlert = false;
                if (type.equals("l_break")) {
                    shouldAlert = currentPrice < setPrice;
                } else if (type.equals("upper_break")) {
                    shouldAlert = currentPrice > setPrice;
                } else if (type.equals("l_percent")) {
                    shouldAlert = currentPrice <= thresholdPrice;
                } else if (type.equals("u_percent")) {
                    shouldAlert = currentPrice >= thresholdPrice;
                }

                if (shouldAlert) {
                    System.out.println("触发告警");
                    notificationService.sendNotification(build);
                    session.close();
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    System.err.println("InterruptedException exception: " + ex.getMessage());
                }
            }
        };
        clientEndPoint.addMessageHandler(handler);

    } catch (URISyntaxException ex) {
        System.err.println("URISyntaxException exception: " + ex.getMessage());
    }
}

3. 关键说明

  • 初始价格存储:用AtomicReference存储初始价格,适配WebSocket可能的多线程消息处理场景,保证读写的线程安全。
  • 阈值计算时机:第一次收到WebSocket消息时,以当前价格为基准计算阈值,后续消息直接用阈值判断,避免重复计算。
  • 字符串比较修复:原代码中用==比较字符串是错误的,Java中必须用equals()方法,否则会因对象引用不同导致判断失效。
  • 告警触发逻辑:百分比类型告警,下跌时判断当前价格是否小于等于阈值,上涨时判断是否大于等于阈值,符合“触及即告警”的需求。

内容的提问来源于stack exchange,提问作者YSEO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 18:20:42