从版本0.12开始,Ta4j支持使用不同类型进行Indicator
或中进行的基本计算BarSeries
。这意味着您可以编写自己的Num
接口实现,也可以在现有实现之间进行选择。目前,有两个可用的现有实现:(PrecisionNum
默认)和DoubleNum
。顾名思义,可用的实现使用不同的类型(代理)进行算术计算。DoubleNum
采用了double
原始的和PrecisionNum
使用BigDecimal
的计算类。以下代码段说明了它们的区别:
加上操作DoubleNum
:
@Override
public Num plus(Num augend) {
return augend.isNaN() ? NaN : new DoubleNum(delegate + ((DoubleNum)augend).delegate);
}
加上操作PrecisionNum
:
public Num plus(Num augend) {
return augend.isNaN() ? NaN : new BigDecimalNum(delegate.add(((BigDecimalNum)augend).delegate, MATH_CONTEXT));
}
查看用法示例中的相应部分,以了解如何使用Num
和BaseBarSeries
。
Num
实施为算术运算支持不同数据类型的主要目的是技术分析中的相反目标。一方面,性能是高频交易或大数据分析中的关键因素,另一方面,加密货币和货币价值的一般处理要求的算法不基于诸如或的二进制浮点类型。例如,以下使用简单代码片段将打印出意外的结果:double
float
double
System.out.println(1.0 - 9*0.1)
输出将0.0999999999999999998
不等于0.1。这不是错误,而是尝试在二进制数字系统中表示十进制值的结果。不可能精确地以double或float表示0.1(或其他任何负的10的幂)。使用这些数据类型,您只能近似使用这种十进制值。在许多情况下,这种表示可能就足够了,您会说“四舍五入到最后一分将为我提供正确的值”,但请注意,.9999万亿美元大约等于1万亿美元。您能把差额存入我的银行帐户吗?
在正确的方式来解决这个问题的方法是使用BigDecimal
,int
或long
货币计算。这并不意味着双打永远不能用于此目的。基于指标仅使用货币值作为输入但进一步的计算和结果不具有货币维度这一事实,Double的53个有效位(〜16个十进制数字)通常足以满足那些仅要求准确性的事情。 您必须了解您的应用程序,并且应该研究目标并告知自己哪种数据类型实现最适合您。
使用BigDecimal作为delgate的PrecisionNum
实现,可以表示精确到32位小数的任何十进制值。它可用于进行高度精确的计算并与需要大量小数位表示的加密货币一起使用。如果创建,则是默认实现。对于某些需要快速进行运算或大量计算的目的,您可能注意到由于实现了性能瓶颈。Num
Num
BaseBarSeries
Num
BarSeries series_1 = new BaseBarSeriesBuilder().build() // implicit initialize BarSeries with PrecisionNum
BarSeries series_2 = new BaseBarSeriesBuilder().withNumType(PrecisionNum::valueOf).build() // explicit initialize BarSeries with PrecisionNum
在发现有关的缺点之后DoubleNum
,请注意,它可以使您的Ta4j应用程序大幅提升性能。您可以按如下方式BaseBarSeries
使用DoubleNum
:
BarSeries series_3 = new BaseBarSeriesBuilder().withNumType(DoubleNum::valueOf).build() // explicit initialize BarSeries with DoubleNum
如果要编写自己的实现,则Num
只需让您的类实现Num
接口即可:
public class MyNum implements Num {
// Override interface functions...
}
例如,您可以使用integer
或long
作为代理来解决性能与准确性的问题。现有的替代方法BigDecimal
可能是Decimal4j。
特别注意需要以下Num
接口原型:
public Function<Number, Num> function(); // required from every class that implements Num..
此函数必须返回一个java.util.Function对象,该对象允许用户和库的其他类将任何Number扩展类(例如Double,Integer,BigDecimal等)转换为Num实现。
现有的实现DoubleNum
并BigDecimalNum
提供静态函数以将转换Number
为相应的Num
实现类:
/**
* Returns a {@code Num} version of the given {@code Number}.
* Warning: This method turns the number into a string first
* @param val the number
* @return the {@code Num}
*/
public static BigDecimalNum valueOf(Number val) {
return new BigDecimalNum(val.toString());
}
会function()
返回此静态valueOf()
函数的lamba表达式。以下代码片段显示了BigDecimalNum
类如何在方法参考的function()
帮助下覆盖函数:
@Override
public Function<Number, Num> function() {
return BigDecimalNum::valueOf;
}
BarSeries
并且Bar
需要对此Function
对象的引用,该引用能够将任何对象转换Number
为所需的Num
实现。因此,您必须在Bar
手动创建时传递此函数:
// The bar object has to transform the intput into Num with help of the given function
Bar bar = new BaseBar(ZonedDateTime.now(),1,3,1,1,1,BigDecimalNum::valueOf);
BarSeries也需要此Function
。解决此问题的最简单方法是使用SeriesBuilder并将条形数据直接添加到BarSeries:
BarSeries series = new BaseBarSeries.SeriesBuilder().withName("mySeries").build(); // the builder uses BigDecimalNum as default
ZonedDateTime endTime = ZonedDateTime.now();
// add bar data directly. It will be transformed automatically to Num implementation of BarSeries
series.addBar(endTime, 105.42, 112.99, 104.01, 111.42, 1337);
series.addBar(endTime.plusDays(1), 111.43, 112.83, 107.77, 107.99, 1234);
series.addBar(endTime.plusDays(2), 107.90, 117.50, 107.90, 115.42, 4242);
您可以使用以下功能通过构建器确定Num
转换:Function
withNumTypeOf(function)
BarSeries series = new BaseBarSeries.SeriesBuilder().withName("mySeries").withNumTypeOf(DoubleNum::valueOf).build();
请注意,实例化BarSeries
具有特定Num
实现的实例后,就无法将另一个Num
实现中的数据添加到中BarSeries
。
BarSeries series = BaseBarSeries.SeriesBuilder().build() // implicit initialize with PrecisionNum
series.addTrade(DoubleNum.valueOf(volume), DoubleNum.valueOf(bid)); // try to add DoubleNum values
// throws ClassCastException: org.ta4j.core.num.DoubleNum
// cannot be cast to org.ta4j.core.num.PrecisionNum
免责声明:好库网所展示的信息由买卖双方自行提供,其真实性、准确性和合法性由信息发布人负责。好库网不提供任何保证,并不承担任何法律责任。