From 32331e187236646996590cecac7f23cf19272d7c Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Fri, 26 Dec 2025 15:24:02 +0800
Subject: [PATCH] feat(indicator): 添加MACD和MA组合交易策略核心组件

---
 src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/Volatility.java |  167 +++++++++++++++++++++++++++++++++----------------------
 1 files changed, 101 insertions(+), 66 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/Volatility.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/Volatility.java
index 105427d..74041a1 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/Volatility.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/Volatility.java
@@ -1,91 +1,126 @@
+/**
+ * 波动率指标计算类
+ * <p>
+ * 波动率是衡量金融市场价格波动程度的指标,通常用价格的标准差与平均值的比率表示。
+ * 本类实现了基于滚动窗口的波动率计算,通过标准差与平均值的比值计算出百分比形式的波动率。
+ */
 package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
  * 波动率指标实现类
- * 
- * 波动率是衡量价格波动程度的技术指标,反映市场的活跃度和风险水平。
- * 本类通过计算价格的标准差来衡量波动率,使用高精度BigDecimal计算避免精度损失。
- * 
- * 【核心功能】
- * 1. 计算指定周期内价格的波动率(标准差)
- * 2. 使用牛顿迭代法实现BigDecimal的平方根计算,确保金融计算的高精度要求
- * 3. 提供获取最新计算结果的接口
- * 
- * 【使用场景】
- * - 在MACD+MA策略中,用于过滤低波动率的市场环境(波动率<1%时跳过交易)
- * - 适用于各种时间周期的价格数据,通常使用20日或30日周期
- * 【注意事项】
- * - 波动率指标对市场流动性和交易活跃度敏感
- * - 低波动率可能预示着市场趋势即将发生变化
+ * <p>波动率计算原理:使用标准差与平均值的比值,以百分比形式表示价格的波动程度。</p>
+ * <p>计算公式:波动率 = (标准差 / 平均值) * 100%</p>
+ *
+ * <p>使用示例:</p>
+ * <pre>
+ * // 初始化20日波动率计算器
+ * Volatility vol = new Volatility(20);
+ *
+ * // 动态添加每日价格
+ * priceFeed.subscribe(price -> {
+ * vol.addPrice(price);
+ * vol.calculate();
+ * });
+ *
+ * // 判断是否满足低波动条件(<1%)
+ * if (vol.getValue().compareTo(new BigDecimal("1.00")) < 0) {
+ * System.out.println("低波动市场,暂停交易");
+ * }
+ * </pre>
  */
 public class Volatility {
-    /**
-     * 波动率计算的周期
-     */
+    /** 波动率计算的周期(如20日波动率) */
     private final int period;
-    
-    /**
-     * 最新计算的波动率值
-     */
+
+    /** 当前计算出的波动率值(百分比形式) */
     private BigDecimal volatility = BigDecimal.ZERO;
 
+    /** 使用LinkedList存储滚动窗口内的价格数据,便于添加和删除操作 */
+    private LinkedList<BigDecimal> priceWindow = new LinkedList<>();
+
+    /** 窗口内价格的总和,用于快速计算平均值 */
+    private BigDecimal sum = BigDecimal.ZERO;
+
+    /** 窗口内价格平方的总和,用于快速计算方差 */
+    private BigDecimal sumSquares = BigDecimal.ZERO;
+
     /**
-     * 构造方法
-     * 
-     * @param period 波动率计算的周期
+     * 构造函数,创建指定周期的波动率计算器
+     *
+     * @param period 波动率计算周期,如20表示计算20日波动率
      */
     public Volatility(int period) {
         this.period = period;
     }
 
     /**
-     * 计算波动率
-     * 
-     * @param prices 历史价格数据列表
+     * 添加新价格到计算窗口,并维护窗口内的价格数据
+     * 采用滑动窗口方式,当价格数量超过周期时,自动移除最早的价格
+     *
+     * @param price 新的价格数据,使用BigDecimal确保计算精度
+     * @throws IllegalArgumentException 当价格为null时抛出异常
      */
-    public void calculate(List<BigDecimal> prices) {
-        // 如果价格数据不足计算周期,不进行计算
-        if (prices.size() < period) {
+    public void addPrice(BigDecimal price) {
+        if (price == null) {
+            throw new IllegalArgumentException("Price cannot be null");
+        }
+
+        // 当窗口大小达到周期时,移除最早的价格,并从总和中减去
+        if (priceWindow.size() == period) {
+            BigDecimal removed = priceWindow.removeFirst();
+            sum = sum.subtract(removed);
+            sumSquares = sumSquares.subtract(removed.pow(2));
+        }
+
+        // 添加新价格到窗口,并更新总和
+        priceWindow.add(price);
+        sum = sum.add(price);
+        sumSquares = sumSquares.add(price.pow(2));
+    }
+
+    /**
+     * 计算当前窗口内价格的波动率
+     * 使用标准差与平均值的比值计算波动率百分比
+     */
+    public void calculate() {
+        // 数据点不足,无法计算波动率
+        if (priceWindow.size() < period) {
             return;
         }
 
-        BigDecimal sum = BigDecimal.ZERO;
-        BigDecimal avg = calculateAverage(prices); // 计算平均价格
-        
-        // 计算每个价格与平均价格的偏差平方和
-        for (int i = prices.size()-period; i < prices.size(); i++) {
-            BigDecimal dev = prices.get(i).subtract(avg);
-            sum = sum.add(dev.pow(2));
+        // 计算平均值:sum / period
+        BigDecimal avg = sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
+
+        // 防止除以零的情况
+        if (avg.compareTo(BigDecimal.ZERO) == 0) {
+            volatility = BigDecimal.ZERO;
+            return;
         }
-        
-        // 计算方差,保留8位小数
-        BigDecimal variance = sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
-        
-        // 计算标准差(波动率),使用牛顿迭代法确保高精度
-        volatility = sqrt(variance, 8);
+
+        // 计算方差:(sumSquares / period) - avg^2
+        BigDecimal variance = sumSquares.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP)
+                .subtract(avg.pow(2));
+
+        // 确保方差非负(防止浮点数计算误差导致负数方差)
+        variance = variance.max(BigDecimal.ZERO);
+
+        // 计算标准差:sqrt(variance)
+        BigDecimal stdDev = sqrt(variance, 8);
+
+        // 计算波动率:(标准差 / 平均值) * 100%
+        volatility = stdDev.divide(avg, 8, RoundingMode.HALF_UP)
+                .multiply(new BigDecimal(100))
+                .setScale(2, RoundingMode.HALF_UP);
     }
 
     /**
-     * 计算价格平均值
-     * 
-     * @param prices 历史价格数据列表
-     * @return 平均价格
-     */
-    private BigDecimal calculateAverage(List<BigDecimal> prices) {
-        BigDecimal sum = BigDecimal.ZERO;
-        for (int i = prices.size()-period; i < prices.size(); i++) {
-            sum = sum.add(prices.get(i));
-        }
-        return sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
-    }
-
-    /**
-     * 计算BigDecimal的平方根(牛顿迭代法)
-     * 
+     * 计算BigDecimal的平方根(使用牛顿迭代法)
+     *
      * @param value 要计算平方根的数值
      * @param scale 结果的精度(小数位数)
      * @return 平方根结果
@@ -95,26 +130,26 @@
         if (value.compareTo(BigDecimal.ZERO) < 0) {
             return BigDecimal.ZERO;
         }
-        
+
         // 使用牛顿迭代法计算平方根
         BigDecimal x = value.divide(new BigDecimal(2), scale, RoundingMode.HALF_UP); // 初始猜测值
         BigDecimal prev;
-        
+
         do {
             prev = x;
             // 牛顿迭代公式:x(n+1) = (x(n) + value/x(n))/2
             x = x.add(value.divide(x, scale, RoundingMode.HALF_UP)).divide(new BigDecimal(2), scale, RoundingMode.HALF_UP);
         } while (x.subtract(prev).abs().compareTo(BigDecimal.ONE.movePointLeft(scale)) > 0); // 直到满足精度要求
-        
+
         return x;
     }
 
     /**
      * 获取最新的波动率计算结果
-     * 
-     * @return 波动率值
+     *
+     * @return 波动率值,以百分比形式表示(例如:2.5表示2.5%)
      */
     public BigDecimal getValue() {
         return volatility;
     }
-}
+}
\ No newline at end of file

--
Gitblit v1.9.1