| | |
| | | /** |
| | | * 波动率指标计算类 |
| | | * <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; |
| | | |
| | | /** |
| | | * 波动率指标实现类 |
| | | * <p>波动率计算原理:使用标准差与平均值的比值,以百分比形式表示价格的波动程度。</p> |
| | | * <p>计算公式:波动率 = (标准差 / 平均值) * 100%</p> |
| | | * |
| | | * 波动率是衡量价格波动程度的技术指标,反映市场的活跃度和风险水平。 |
| | | * 本类通过计算价格的标准差来衡量波动率,使用高精度BigDecimal计算避免精度损失。 |
| | | * <p>使用示例:</p> |
| | | * <pre> |
| | | * // 初始化20日波动率计算器 |
| | | * Volatility vol = new Volatility(20); |
| | | * |
| | | * 【核心功能】 |
| | | * 1. 计算指定周期内价格的波动率(标准差) |
| | | * 2. 使用牛顿迭代法实现BigDecimal的平方根计算,确保金融计算的高精度要求 |
| | | * 3. 提供获取最新计算结果的接口 |
| | | * // 动态添加每日价格 |
| | | * priceFeed.subscribe(price -> { |
| | | * vol.addPrice(price); |
| | | * vol.calculate(); |
| | | * }); |
| | | * |
| | | * 【使用场景】 |
| | | * - 在MACD+MA策略中,用于过滤低波动率的市场环境(波动率<1%时跳过交易) |
| | | * - 适用于各种时间周期的价格数据,通常使用20日或30日周期 |
| | | * 【注意事项】 |
| | | * - 波动率指标对市场流动性和交易活跃度敏感 |
| | | * - 低波动率可能预示着市场趋势即将发生变化 |
| | | * // 判断是否满足低波动条件(<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); // 计算平均价格 |
| | | // 计算平均值:sum / period |
| | | BigDecimal avg = sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP); |
| | | |
| | | // 计算每个价格与平均价格的偏差平方和 |
| | | for (int i = prices.size()-period; i < prices.size(); i++) { |
| | | BigDecimal dev = prices.get(i).subtract(avg); |
| | | sum = sum.add(dev.pow(2)); |
| | | // 防止除以零的情况 |
| | | if (avg.compareTo(BigDecimal.ZERO) == 0) { |
| | | volatility = BigDecimal.ZERO; |
| | | return; |
| | | } |
| | | |
| | | // 计算方差,保留8位小数 |
| | | BigDecimal variance = sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP); |
| | | // 计算方差:(sumSquares / period) - avg^2 |
| | | BigDecimal variance = sumSquares.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP) |
| | | .subtract(avg.pow(2)); |
| | | |
| | | // 计算标准差(波动率),使用牛顿迭代法确保高精度 |
| | | volatility = sqrt(variance, 8); |
| | | // 确保方差非负(防止浮点数计算误差导致负数方差) |
| | | 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 波动率值 |
| | | * @return 波动率值,以百分比形式表示(例如:2.5表示2.5%) |
| | | */ |
| | | public BigDecimal getValue() { |
| | | return volatility; |