All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xlc.quant.data.indicator.FixedWindowCircularCalculator Maven / Gradle / Ivy

There is a newer version: XLCQ20240604
Show newest version
package xlc.quant.data.indicator;

import java.util.function.Consumer;
import java.util.function.Function;


/**
 * 固定窗口环形计算器
 * @author Rootfive
 * @param  固定窗口载体
 * @param   计算器的输出结果,Object 可以是任意类型
 */
public abstract class FixedWindowCircularCalculator,RESULT> {
	
	/** 计算所需的载体数据,是一个固定长度的环形数组 */
	protected final transient Object [] carrierData;

	/** 环形数组的周期 */
	protected final transient int circularPeriod;

	/** 执行总数 */
	protected int executeTotal = 0;

	/** 满容计算:指环形数组满容时才会执行计算 */
	private final transient boolean isFullCapacityCalculate;

	/** 头数据角标:已经插入环形数组的最新的数据数组角标 */
	private int headIndex = 0;

	/**
	 * @param maxPeriodLength     [环形数组]的最大周期长度
	 * @param isFullCapacityCalculate   是否满容计算
	 */
	public FixedWindowCircularCalculator(int maxPeriodLength, boolean isFullCapacityCalculate) {
		super();
		this.carrierData =  new Object [maxPeriodLength];
		this.circularPeriod = maxPeriodLength;
		this.isFullCapacityCalculate = isFullCapacityCalculate;
	}


	/**
	 * @param newCarrier 新计算载体
	 * @param propertyGetter  委托方法,上一个载体获取上一个计算结果
	 * @param propertySetter  委托方法,设置计算结果到载体的哪个属性
	 * @return
	 */
	public synchronized RESULT input(CARRIER newCarrier,Function propertyGetter,Consumer propertySetter) {
		boolean addResult = addFirst(newCarrier);
		if (addResult) {
			// 新增成功
			if (!isFullCapacityCalculate || (isFullCapacityCalculate && this.isFullCapacity())) {
				// 二者满足其中一种。均可执行计算,条件:1、不是满容计算 [或] 2满容计算且已经满容,
				RESULT calculateResult = executeCalculate(propertyGetter);
				
				//设置计算结果 到 输入新数据[newCarrier]属性值上,做到指标和数据对应
				propertySetter.accept(calculateResult);
				return calculateResult;
			}
		}
		return null;
	}

	
	/**
	 * @param newCarrier 新计算载体
	 * @return 将newCarrier追加环形数组的第一个位置(头插法)。
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private synchronized boolean addFirst(CARRIER newCarrier) {
		if (newCarrier == null) {
			throw new NullPointerException("输入指标计算器 数据不能为:null");
		}

		// 取出环形数组[head]
		CARRIER head = getHead();
		if (head != null) {
			// [head]-交易时间
			Comparable headTradeTime = head.getTradeTime();
			// [newCarrier]-交易时间
			Comparable newCarrierTradeTime = newCarrier.getTradeTime();
		
			if (newCarrierTradeTime.compareTo(headTradeTime) <= 0) {
				// 交易时间【不】在头数据之后,说明是历史数据,不刷新
				return false;
			} else if (newCarrierTradeTime.compareTo(headTradeTime) >  0) {
				// 交易时间在头数据之后,说明是新数据
				
				// [head]-收盘时间
				Comparable headCloseTime = head.getCloseTime();
				// [newCarrier]-收盘时间
				Comparable newCarrierCloseTime = newCarrier.getCloseTime();
				if (newCarrierCloseTime.compareTo(headCloseTime) > 0) {
					//[newCarrierCloseTime]相较于[headCloseTime],是一个新的周期,需要刷新 头数据角标
					++this.executeTotal;
					
					// 指针移动-循环数组核心
					if (this.headIndex == (this.carrierData.length - 1)) {
						// 当前[head] 等于 缓冲区长度-1
						// 说明:此时环形数组的已经满了,环形数组中[head]角标指向数组最后一个空位,要想更新新元素,头元素需要指定数组角标为0的位置
						this.headIndex = 0;
					} else {
						++this.headIndex;
					}
				}
			}
		} else {
			this.executeTotal = 1;
		}
		// 设置头数据-不更新元素和元素的角标
		this.carrierData[this.headIndex] = newCarrier;
		
		//数据刷新成功
		return true;
	}

	/**
	 * 执行计算,由子类具体某个指标的计算器实现
	 * @param propertyGetter  委托方法,从载体获取计算结果
	 * @return
	 */
	protected abstract RESULT executeCalculate(Function propertyGetter);
	
	/**
	 * 尾元素角标
	 * 
	 * @return int
	 */
	private int getTailIndex() {
		if (this.headIndex == this.carrierData.length-1 ) {
			return 0;
		}else {
			return this.headIndex + 1;
		}
	}

	@SuppressWarnings("unchecked")
	private CARRIER getCircularArrayElement(int index) {
        return (CARRIER) this.carrierData[index];
    }
	
	
	/**
	 * 获取头元素
	 * 
	 * @return
	 */
	public CARRIER getHead() {
		return this.getCircularArrayElement(this.headIndex);
	}

	/**
	 * 获取尾元素
	 * 
	 * @return
	 */
	public CARRIER getTail() {
		return this.getCircularArrayElement(getTailIndex());
	}

	/**
	 * @param prevNum  按照倒叙插入顺序,获取指定前面输入的[计算载体数据],有效范围是:[0,circularData.length-1]
	 * @return 指定前面输入的[计算载体数据]
	 * 
	 * 
	 *  1、prevNum 有效值范围是:[0,carrierData.length-1]
	 *  2、当 prevNum <= 0 时,返回是循环数组的[Head头数据],也就是[环形数组内 最新插入 的数据] 
	 *  3、当 prevNum >= this.circularData.length -1 时,返回是循环数组的[Tail尾巴数据],也就是[环形数组内 最早新插入 的数据] 
	 * 
*/ public CARRIER getPrevByNum(int prevNum) { if (prevNum <= 0) { return this.getHead(); }else if (prevNum >= this.carrierData.length -1) { return this.getTail(); } int needIndex = this.headIndex - prevNum; if (needIndex <= -1) { needIndex = needIndex + this.carrierData.length; } return this.getCircularArrayElement(needIndex); } /** * 前(上)一个数据 * * @return */ public CARRIER getPrev() { return getPrevByNum(1); } /** * 是否满容量:指环形数组是否满容量 */ public boolean isFullCapacity() { return executeTotal >= this.carrierData.length ? true : false; } /** * 环形数组 元素数量 * * @return */ public int size() { return isFullCapacity() ? this.carrierData.length : executeTotal; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy