1.首先,我们需要知道波动率是什么?在数学定义上波动率就是标准,标准差就是方差开根号,我们先看一下方差计算公式:
其中x为这组数据的平均数,下面我们需要对方差进行推导:
图 2.
图3.标准差 :
图4.归一化处理:
推导过程:我们先不管前面的1/n,对里面展开:x为平均数,第4步括号1里面为X1到Xn平方的和,我们对后面的式子乘以n再除以n,X1+X2+...+Xn除以n就是平均数所以根据第5步我们得到第6步,因为x是平均数,x等于X1+X2+...+Xn除以n,所以x的平方拆开得到第7步。
不要忘了我们下面还有一个n分只1,才是方差
所以第8步为方差公式,图2的方差公式并不正确,图2的公式是第9步,是对方差乘以n的平方后的公式。但图三的标准差的公式是正确的, 对方差又除以n的平方。所以我们以图2,图3,图4 进行计算。计算完以后进行归一化处理:
测试类:SpringbootApplicationTests.java
package com.zhangdi.springboot;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import com.zhangdi.springboot.service.UserService;
import com.zhangdi.springboot.statistics.MarketDataManager;
import com.zhangdi.springboot.statistics.PriceDate;
import com.zhangdi.springboot.statistics.PriceProperty;
import com.zhangdi.springboot.statistics.PriceStatistics;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootApplicationTests {
@Autowired
UserService userService;
@Test
@Rollback(false)
public void contextLoads() {
}
@Test
public void test() {
//准备测试数据
List<PriceDate> listPriceDate = new ArrayList<PriceDate>();
PriceDate priceDate = new PriceDate(new BigDecimal(2),"bbbb",0.0);
PriceDate priceDate1 = new PriceDate(new BigDecimal(4),"bbbb",0.0);
PriceDate priceDate2 = new PriceDate(new BigDecimal(6),"bbbb",0.0);
PriceDate priceDate3 = new PriceDate(new BigDecimal(4),"bbbb",0.0);
PriceDate priceDate4 = new PriceDate(new BigDecimal(6),"bbbb",0.0);
PriceDate priceDate5 = new PriceDate(new BigDecimal(2),"bbbb",0.0);
listPriceDate.add(priceDate);
listPriceDate.add(priceDate1);
listPriceDate.add(priceDate2);
listPriceDate.add(priceDate3);
listPriceDate.add(priceDate4);
listPriceDate.add(priceDate5);
PriceProperty priceProp = new PriceProperty();
priceProp.setMaxSampleSize(10); //设置样本容量
ConcurrentHashMap<String, PriceProperty> priceProperty = MarketDataManager.getInstance().getPriceProperty();
priceProperty.put("aaa", priceProp);
for(PriceDate pd:listPriceDate) {
String key = pd.getType();
if (!MarketDataManager.getInstance().getPriceStatistics().containsKey(key)) {
MarketDataManager.getInstance().getPriceStatistics().put(key, new PriceStatistics());//根据type求不同类型数据的波动率
}
PriceStatistics priceStatistics = MarketDataManager.getInstance().getPriceStatistics().get(key);
boolean passAsk = priceStatistics.CalculateStatistics(pd, priceStatistics.askStats); //计算波动率
if(priceStatistics.askStats.get(key)!=null) {
priceStatistics.askStats.get(key).getPriceVolatility(); //获取归一化的波动率(测试打印未归一化)
}
}
}
}
计算类 PriceStatistics.java
package com.zhangdi.springboot.statistics;
import java.util.concurrent.ConcurrentHashMap;
public class PriceStatistics {
public int max_sample_size; //样本容量,最大多少数据进行波动率计算
// public double error_margin;
// public int error_count;
public ConcurrentHashMap<String, SingleFeedStatistics> askStats;
public PriceStatistics() {
askStats = new ConcurrentHashMap<String, SingleFeedStatistics>();
}
/**
* 根据过滤规则过滤报价信息
*
* @author tingting.song
* @param fxData
* @param stats
* @param priceData
* @param sideFlag
* 0:ask, 1:bid
* @return
*/
public boolean CalculateStatistics(PriceDate priceDate,
ConcurrentHashMap<String, SingleFeedStatistics> stats
) {
String key = priceDate.getType();
PriceProperty priceProperty = MarketDataManager.getInstance().getPriceProperty().get("aaa");
if (!ObjectUtil.isEmpty(priceProperty)) {
max_sample_size = priceProperty.getMaxSampleSize(); //获取样本容量大小
}
SingleFeedStatistics singleFeedStatistics;
if (stats.containsKey(key)) {
singleFeedStatistics = stats.get(key);
singleFeedStatistics.getStatsTracker().addValue(priceDate.getPrice().doubleValue()); //计算波动率 先处理数据
} else {
stats.put(key, new SingleFeedStatistics( max_sample_size)); //new 一个计算对象
singleFeedStatistics = stats.get(key);
singleFeedStatistics.getStatsTracker().addValue(priceDate.getPrice().doubleValue());//计算波动率 先处理数据
}
double price = 0;
price = priceDate.getPrice().doubleValue();
boolean pass = singleFeedStatistics.calculateAll(price);
return pass;
}
}
计算类 SingleFeedStatistics.java
package com.zhangdi.springboot.statistics;
public class SingleFeedStatistics {
StatsTracker priceTracker; //每个Tracker都是一个波动率计算,价格的波动率
//StatsTracker tickTracker;
//StatsTracker freqTracker;
// parameters;
public int max_sample_size; //样本容量
// volatility
double priceVolatility;
public SingleFeedStatistics(int max_sample_size) {
this.max_sample_size = max_sample_size;
priceTracker = new StatsTracker(max_sample_size);
priceVolatility = 0;
}
public StatsTracker getStatsTracker() {
return priceTracker;
}
/**
* 获取价格波动率
*
* @return
*/
public double getPriceVolatility() {
return priceVolatility;
}
/**
* 计算波动率
* @param price
* @return
*/
public boolean calculateAll(double price) {
calculatePriceStatistics(price);
// MOVED adding to statTrackers after both results come in rather than just one.
//priceTracker.addValue(price); //计算完对数据处理
return true;
}
public void calculatePriceStatistics(double price) {
priceVolatility = getVolatilityIndex(priceTracker, price);
}
/**stats.getMovingAverage() 为平均数
* stats.getMovingStandardDev() 为标准差 即波动率
* @param stats
* @param value
* @return
*/
protected double getVolatilityIndex(StatsTracker stats, double value) {
//对波动率进行归一化处理,这里归一化减去Xn的平均数,公式是减去Xn-1的平均数
double actualDiff = Math.abs(value - stats.getMovingAverage());
double numStdDevDiff = Math.abs(actualDiff / stats.getMovingStandardDev());
System.out.println(stats.getMovingAverage()+"..."+stats.getMovingStandardDev());
// double vol = numStdDevDiff * 11 / error_margin;
// double vol = numStdDevDiff * 11 / 4;
//vol = vol + 1;
return numStdDevDiff;
}
}
数据处理类 StatsTracker.java
package com.zhangdi.springboot.statistics;
import java.math.BigDecimal;
public class StatsTracker {
public long totalSampleSize;
long count;
double s1; //tracking moving sum (s1 = s1+val)
double s2; //traving moving square sum(s2 + (val*val))
double movingAverage;
double movingStandardDev;
public StatsTracker() {
super();
}
public StatsTracker(int totalSampleSize) {
this.totalSampleSize = totalSampleSize;
count = 0;
s1 = 0;
s2 = 0;
movingAverage = 0;
movingStandardDev = 0;
}
public long getCount() {
return count;
}
public double getMovingAverage() {
return movingAverage;
}
public double getMovingStandardDev() {
return movingStandardDev;
}
public boolean addValue(double value) {
if(value <= 0) {
return false;
}
if(count>=totalSampleSize) { //当到达样本容量时 这里减去的是平均数 不是第一个数
count --;
s1 = s1 - movingAverage;
s2 = s2 - (movingAverage*movingAverage);
}
count++;
s1 = s1+value; //s1 是所有数的和
s2 = s2 + (value*value); //s2 是所有数的平方和
movingAverage = (s1)/count;
double mVar = Math.abs(((count*s2)-(s1*s1)));
/**
* right now, this is calculating population standard deviation.
* we need to get it to calculate standard deviation.
* The difference is, for standard deviation, we need to divide by n-1
* whereas in population standard deviation, we need to divide by n
* so in order to not screw up my count, i need to create a custom divisor.
*/
movingStandardDev = ((Math.sqrt(mVar))/count);
movingAverage = CalculateUtil.truncate(new BigDecimal(movingAverage), 6).doubleValue();
if(!(movingStandardDev==0)){
movingStandardDev = CalculateUtil.truncate(new BigDecimal(movingStandardDev), 6).doubleValue();
}
return true;
}
}
价格实体类 PriceDate.java
package com.zhangdi.springboot.statistics;
import java.math.BigDecimal;
public class PriceDate {
private BigDecimal price;
private String type;
private double volatility;
public PriceDate(BigDecimal price,String type, Double volatility){
this.price=price;
this.type=type;
this.volatility=volatility;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public double getVolatility() {
return volatility;
}
public void setVolatility(double volatility) {
this.volatility = volatility;
}
}
属性实体类 PriceProperty.java
package com.zhangdi.springboot.statistics;
public class PriceProperty {
private int maxSampleSize;
public int getMaxSampleSize() {
return maxSampleSize;
}
public void setMaxSampleSize(int maxSampleSize) {
this.maxSampleSize = maxSampleSize;
}
}
计算工具类 CalculateUtil.java
package com.zhangdi.springboot.statistics;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class CalculateUtil {
/**
* BigDecimal加法运算
* @author tingting.song
* @param v1
* @param v2
* @return
*/
public static BigDecimal add(String v1, String v2) {
if (ObjectUtil.isEmpty(v1) || ObjectUtil.isEmpty(v2)) {
return null;
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.add(b2);
}
/**
* BigDecimal减法运算
* @author tingting.song
* @param v1
* @param v2
* @return
*/
public static BigDecimal sub(String v1, String v2) {
if (ObjectUtil.isEmpty(v1) || ObjectUtil.isEmpty(v2)) {
return null;
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.subtract(b2);
}
/**
* BigDecimal乘法运算
* @author tingting.song
* @param v1
* @param v2
* @return
*/
public static BigDecimal multi(String v1, String v2) {
if (ObjectUtil.isEmpty(v1) || ObjectUtil.isEmpty(v2)) {
return null;
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.multiply(b2);
}
/**
* BigDecimal除法运算
* @author tingting.song
* @param v1
* @param v2
* @return
*/
public static BigDecimal div(String v1, String v2) {
if (ObjectUtil.isEmpty(v1) || ObjectUtil.isEmpty(v2)) {
return null;
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.divide(b2);
}
/**
* BigDecimal除法运算
* @author tingting.song
* @param v1
* @param v2
* @param scale 保留几位小数
* @param round 是否要四舍五入(BigDecimal.ROUND_HALF_UP)
* @return
*/
public static BigDecimal div(String v1, String v2, int scale, int round) {
if (ObjectUtil.isEmpty(v1) || ObjectUtil.isEmpty(v2)) {
return null;
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.divide(b2, scale, round);
}
/**
* 按精度,四舍五入
* @author tingting.song
* @param value
* @param places
* @return
*/
public static BigDecimal round(BigDecimal value, int places) {
if(value.equals(BigDecimal.ZERO)) {
return value;
}
if (places < 0) throw new IllegalArgumentException();
value = value.setScale(places, RoundingMode.HALF_UP);
return value;
}
/**
* 根据截取的精度,得到一定精度的数值(不四舍五入)
* @author tingting.song
* @param value
* @param places
* @return
*/
public static BigDecimal truncate(BigDecimal value, int places) {
if (value.equals(BigDecimal.ZERO) || value == null) {
return value;
}
if (places < 0) {
throw new IllegalArgumentException();
}
String decimalPlace = "";
for (int i = 0; i < places; i++) {
decimalPlace += 0;
}
java.text.DecimalFormat df = new java.text.DecimalFormat("#." + decimalPlace);
df.setRoundingMode(RoundingMode.FLOOR);
return new BigDecimal(df.format(value));
}
}
辅助工具类 ObjectUtil.java
package com.zhangdi.springboot.statistics;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
public class ObjectUtil {
/**
* 判断该对象是否为null
* @author tingting.song
* @param o
* @return
*/
public static boolean isNull(Object o) {
return (o == null);
}
/**
* 判断该对象是否不为null
* @author tingting.song
* @param o
* @return
*/
public static boolean notNull(Object o) {
return (!(isNull(o)));
}
/**
* 判断该对象是否为空
* @author tingting.song
* @param o
* @return
*/
@SuppressWarnings("rawtypes")
public static boolean isEmpty(Object o) {
if (isNull(o)) {
return true;
}
if (o instanceof String) {
return "".equals(o.toString());
}
if (o instanceof Collection) {
return ((Collection)o).isEmpty();
}
if (o instanceof Map) {
return ((Map)o).isEmpty();
}
if (o.getClass().isArray()) {
return (Array.getLength(o) == 0);
}
return false;
}
/**
* 判断该对象是否不为空
* @author tingting.song
* @param o
* @return
*/
public static boolean notEmpty(Object o) {
return (!(isEmpty(o)));
}
/**
* 防止空指针异常
* @author tingting.song
* @param actual
* @param safe
* @return
*/
public static <T> T nullSafe(T actual, T safe) {
return ((actual == null) ? safe : actual);
}
}
有些童鞋说少了一个类,我就从新复制了一下代码,写出来了,万幸,跑出来结果还是一样的。
package com.hxjf.equipment.zhangdi;
import java.util.concurrent.ConcurrentHashMap;
public class MarketDataManager {
private static final MarketDataManager instance = new MarketDataManager();
public MarketDataManager() {
super();
}
public static MarketDataManager getInstance() {
return instance;
}
private ConcurrentHashMap<String,PriceStatistics> priceStatistics = new ConcurrentHashMap<String,PriceStatistics>();
public ConcurrentHashMap<String,PriceStatistics> getPriceStatistics() {
return priceStatistics;
}
private ConcurrentHashMap<String,PriceProperty> priceProperty = new ConcurrentHashMap<String,PriceProperty>();
public ConcurrentHashMap<String,PriceProperty> getPriceProperty() {
return priceProperty;
}
}
计算结果:...左边是平均数,...右边是标准差(波动率),具体计算过程可以根据实际情况自行修改。