/**
* 波动率指标计算类
*
* 波动率是衡量金融市场价格波动程度的指标,通常用价格的标准差与平均值的比率表示。
* 本类实现了基于滚动窗口的波动率计算,通过标准差与平均值的比值计算出百分比形式的波动率。
*/
package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.LinkedList;
import java.util.List;
/**
* 波动率指标实现类
*
波动率计算原理:使用标准差与平均值的比值,以百分比形式表示价格的波动程度。
* 计算公式:波动率 = (标准差 / 平均值) * 100%
*
* 使用示例:
*
* // 初始化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("低波动市场,暂停交易");
* }
*
*/
public class Volatility {
/** 波动率计算的周期(如20日波动率) */
private final int period;
/** 当前计算出的波动率值(百分比形式) */
private BigDecimal volatility = BigDecimal.ZERO;
/** 使用LinkedList存储滚动窗口内的价格数据,便于添加和删除操作 */
private LinkedList priceWindow = new LinkedList<>();
/** 窗口内价格的总和,用于快速计算平均值 */
private BigDecimal sum = BigDecimal.ZERO;
/** 窗口内价格平方的总和,用于快速计算方差 */
private BigDecimal sumSquares = BigDecimal.ZERO;
/**
* 构造函数,创建指定周期的波动率计算器
*
* @param period 波动率计算周期,如20表示计算20日波动率
*/
public Volatility(int period) {
this.period = period;
}
/**
* 添加新价格到计算窗口,并维护窗口内的价格数据
* 采用滑动窗口方式,当价格数量超过周期时,自动移除最早的价格
*
* @param price 新的价格数据,使用BigDecimal确保计算精度
* @throws IllegalArgumentException 当价格为null时抛出异常
*/
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;
}
// 计算平均值:sum / period
BigDecimal avg = sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
// 防止除以零的情况
if (avg.compareTo(BigDecimal.ZERO) == 0) {
volatility = BigDecimal.ZERO;
return;
}
// 计算方差:(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);
}
/**
* 计算BigDecimal的平方根(使用牛顿迭代法)
*
* @param value 要计算平方根的数值
* @param scale 结果的精度(小数位数)
* @return 平方根结果
*/
private BigDecimal sqrt(BigDecimal value, int scale) {
// 负数没有实数平方根,返回0
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 波动率值,以百分比形式表示(例如:2.5表示2.5%)
*/
public BigDecimal getValue() {
return volatility;
}
}