java 实现移动 波动率 的设计与实现

本文深入解析了波动率的数学定义及计算方法,通过方差和标准差的推导,介绍了如何在Spring Boot应用中实现波动率的计算。利用自定义的数据处理类和计算工具,文章展示了波动率计算的具体步骤,并提供了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
	}
}

 

计算结果:...左边是平均数,...右边是标准差(波动率),具体计算过程可以根据实际情况自行修改。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值