使用Java类库ta4j计算基金的布林轨
ta4j简介
对于做金融分析的从业者而言,python的ta-lib是不可或缺的技术分析库,具有简单易用、功能强大的特点。 各种SMA、MACD、BOLL等指标计算,应有尽有。
那么对于擅长Java语言的开发者而言,有没有对应的功能强大的Java类库呢?答案是Java版ta4j。
ta4j是使用纯Java实现的,不是ta-lib的简单封装,因此具有简单易用、无其他依赖的特点。
GitHub: https://github.com/ta4j/ta4j
WiKi: https://ta4j.github.io/ta4j-wiki/Getting-started.html
WebSite: https://ta4j.github.io/ta4j-wiki/
布林带简介
布林带(Bollinger Bands)是股票技术分析中广泛使用的技术指标,由约翰・布林格(John Bollinger)在 20 世纪 80 年代初期发明。它通过价格的标准差来显示股价的波动范围,帮助投资者分析股价走势、判断市场趋势以及识别潜在的交易信号。
布林带的构成
布林带由三条线组成:
. 中轨(Middle Band):通常是特定周期内的简单移动平均线(SMA)。例如,常见的计算周期为 20 日,即通过计算过去 20 个交易日的收盘价总和,再除以 20 得到中轨数值。它反映了股价的平均成本和中期趋势方向。
. 上轨(Upper Band):基于中轨和价格的标准差计算得出。
. 下轨(Lower Band):计算方式与上轨类似。
布林带的应用
判断股价趋势:
上升趋势:
当股价在布林带中轨上方运行,且布林带开口逐渐扩大,表明股价处于上升趋势,市场多头力量较强。随着股价上涨,上轨和中轨同步上升,下轨也可能跟随上升但幅度相对较小。例如,在一段上升行情中,股价不断创新高,同时布林带开口放大,显示市场情绪积极,股价有进一步上升动力。 下降趋势:若股价在布林带中轨下方运行,且布林带开口逐渐扩大,意味着股价处于下降趋势,空头力量占据优势。股价下跌过程中,下轨和中轨同步下降,上轨虽也可能下降但幅度相对较小。比如,某股票股价持续走低,布林带开口扩张,反映市场看空情绪浓厚。
识别股价波动:
布林带收口: 当股价波动逐渐减小,布林带的上轨和下轨逐渐靠近,即开口收窄。这通常预示着市场即将选择方向,可能是股价即将突破或反转的信号。例如,在横盘整理行情后期,布林带收口,表明市场多空双方力量趋于平衡,等待新的驱动因素打破平衡,引发股价的大幅波动。 布林带开口: 与收口相反,当股价波动加剧,布林带的上轨和下轨距离逐渐拉大,即开口扩张。这表示市场活跃度增加,股价波动幅度加大。如在市场出现重大消息时,股价大幅波动,布林带开口迅速扩大。
寻找交易信号:
突破交易:当股价向上突破上轨,可能是短期买入信号,表明股价强势上涨,市场多头力量强劲。但需注意,这种突破可能是短期的超买现象,股价可能很快回调。反之,当股价向下突破下轨,可能是短期卖出信号,暗示股价弱势下跌,空头力量占优,但也可能是短期超卖,随后股价可能反弹。例如,股价突然放量向上突破上轨,一些投资者可能会根据这一信号买入股票,期待股价继续上涨。
回踩交易:股价在上升过程中,回调至中轨附近获得支撑并再次上涨,是一种潜在的买入机会;股价在下降过程中,反弹至中轨附近遇阻回落,则是潜在的卖出机会。这是因为中轨代表了股价的平均成本线,具有一定的支撑和阻力作用。例如,某股票在上升趋势中,股价回调到中轨附近企稳,随后再次上扬,投资者可在股价企稳时考虑买入。
布林带虽然是一种有效的技术分析工具,但在实际应用中,不能仅仅依赖布林带指标做出交易决策,还需要结合其他技术指标(如 MACD、KDJ 等)、基本面分析以及市场环境等因素,综合判断股票的投资价值和交易时机。
使用ta4j计算股票日线级别的布林带
首先你需要有某股票足够多的日线级别的数据(包含开盘价、收盘价、盘中最高价、盘中最低价),至少30个交易日的数据。 因为通常情况下计算20个交易日的布林带。
我们先定义股票日线价格表的实体类,例如:
package top.yjp.app.stock.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
@TableName("price_info")
public class Price {
/**
* ID
*/
@TableId
private Long id;
/**
* 股票ID
*/
@TableField
private Long stockId;
/**
* 交易日
*/
@TableField
private LocalDate exchangeDate;
/**
* 开盘价
*/
@TableField
private BigDecimal openPrice;
/**
* 收盘价
*/
@TableField
private BigDecimal closePrice;
/**
* 最高价
*/
@TableField
private BigDecimal highPrice;
/**
* 最低价
*/
@TableField
private BigDecimal lowPrice;
/**
* 布林带中轨
*/
@TableField
private BigDecimal boll;
/**
* 布林带上轨
*/
@TableField
private BigDecimal bollUpper;
/**
* 布林带下轨
*/
@TableField
private BigDecimal bollLower;
/**
* 数据同步日期
*/
@TableField
private LocalDate syncDate;
/**
* 上一个交易日
*/
@TableField
private LocalDate preExchangeDate;
/**
* 上一个交易日收盘价
*/
@TableField
private BigDecimal preClosePrice;
}
计算布林带的方法定义如下:
/**
* 计算并更新布林线信息
*
* @param prices 待计算的日线价格数据
*/
public void computeBollInfo(List<Price> prices) {
// 创建BarSeries
// 20日
// k 个标准差
int period = 20;
double stdDevMultiplier = 2;
BarSeries series = new BaseBarSeriesBuilder().withName("BOLL").build();
for (Price price : prices) {
ZonedDateTime exchangeDate = ZonedDateTime.of(price.getExchangeDate(), LocalTime.of(0, 0), ZoneId.systemDefault());
Double openPrice = price.getOpenPrice().doubleValue();
Double closePrice = price.getClosePrice().doubleValue();
Double highPrice = price.getHighPrice().doubleValue();
Double lowPrice = price.getLowPrice().doubleValue();
series.addBar(Duration.ofDays(period), exchangeDate, openPrice, highPrice, lowPrice, closePrice, 0);
}
// 计算布林线指标
BollingerBandFacade bollingerBandFacade = new BollingerBandFacade(series, period, stdDevMultiplier);
for (int i = 0; i < series.getBarCount(); i++) {
BigDecimal middlePrice = fix(bollingerBandFacade.middle().getValue(i).doubleValue());
BigDecimal upperPrice = fix(bollingerBandFacade.upper().getValue(i).doubleValue());
BigDecimal lowerPrice = fix(bollingerBandFacade.lower().getValue(i).doubleValue());
Price price = prices.get(i);
price.setBoll(middlePrice);
price.setBollUpper(upperPrice);
price.setBollLower(lowerPrice);
price.setSyncDate(LocalDate.now());
// 将计算后的布林带价格信息使用priceMapper工具类更新回到数据库
priceMapper.updateById(price);
}
}