Administrator
2025-12-24 81e84c3bb307e2a786d383fad7e14c0c2b4d897e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package com.xcong.excoin.modules.okxNewPrice.indicator;
 
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
 
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
 
/**
 * BOLL (Bollinger Bands) 指标实现
 * 计算逻辑:
 * 1. 中轨(MB)= N日移动平均线
 * 2. 上轨(UP)= 中轨 + K倍标准差
 * 3. 下轨(DN)= 中轨 - K倍标准差
 * 
 * 作用:
 * 1. 测量价格波动范围和市场宽度
 * 2. 价格突破上轨,提示超买或趋势加速
 * 3. 价格跌破下轨,提示超卖或趋势加速
 * 4. 轨道收窄,提示即将发生剧烈波动
 * 5. 价格回归轨道内,提示趋势可能反转
 * 
 * 价格参数类型:
 * - 参数名称:prices
 * - 参数类型:List<BigDecimal>
 * - 参数说明:需要至少20个(默认周期)价格数据点用于计算
 * 
 * 推荐时间粒度及优缺点:
 * 1. 1分钟(1m):
 *    - 优点:反应迅速,适合超短线突破策略
 *    - 缺点:布林带宽度窄,假突破多
 * 2. 5分钟(5m):
 *    - 优点:布林带宽度适中,突破信号相对可靠
 *    - 缺点:仍有一定假突破
 * 3. 15分钟(15m):
 *    - 优点:适合日内交易,突破信号较为可靠
 *    - 缺点:反应速度较慢
 * 4. 1小时(1h)及以上:
 *    - 优点:布林带宽度稳定,突破信号可靠
 *    - 缺点:反应滞后,不适合短线交易
 */
@Slf4j
@Getter
@Setter
public class BOLL extends IndicatorBase {
 
    private static final int DEFAULT_PERIOD = 20;
    private static final double DEFAULT_K = 2.0;
 
    private int period = DEFAULT_PERIOD;
    private BigDecimal k = new BigDecimal(DEFAULT_K);
    private BigDecimal mid = BigDecimal.ZERO;
    private BigDecimal upper = BigDecimal.ZERO;
    private BigDecimal lower = BigDecimal.ZERO;
 
    public BOLL() {}
 
    public BOLL(int period, double k) {
        this.period = period;
        this.k = new BigDecimal(k);
    }
 
    /**
     * 计算BOLL指标
     * @param prices 价格列表
     */
    public void calculate(List<BigDecimal> prices) {
        if (prices == null || prices.size() < period) {
            return;
        }
 
        // 计算中轨(MB)= N日移动平均线
        mid = calculateMA(prices, period);
        
        // 计算标准差
        BigDecimal stdDev = calculateStdDev(prices, period);
        
        // 计算上轨(UP)和下轨(DN)
        BigDecimal bandWidth = k.multiply(stdDev);
        upper = mid.add(bandWidth).setScale(8, RoundingMode.HALF_UP);
        lower = mid.subtract(bandWidth).setScale(8, RoundingMode.HALF_UP);
        
        log.info("BOLL计算结果 - 中轨: {}, 上轨: {}, 下轨: {}", mid, upper, lower);
    }
 
    /**
     * 判断价格是否突破上轨
     * @param price 当前价格
     * @return 是否突破上轨
     */
    public boolean isBreakUpper(BigDecimal price) {
        return price.compareTo(upper) > 0;
    }
 
    /**
     * 判断价格是否跌破下轨
     * @param price 当前价格
     * @return 是否跌破下轨
     */
    public boolean isBreakLower(BigDecimal price) {
        return price.compareTo(lower) < 0;
    }
 
    /**
     * 判断价格是否回归上轨下方
     * @param price 当前价格
     * @param prevPrice 前一期价格
     * @return 是否回归上轨下方
     */
    public boolean isReturnFromUpper(BigDecimal price, BigDecimal prevPrice) {
        return prevPrice.compareTo(upper) > 0 && price.compareTo(upper) <= 0;
    }
 
    /**
     * 判断价格是否回归下轨上方
     * @param price 当前价格
     * @param prevPrice 前一期价格
     * @return 是否回归下轨上方
     */
    public boolean isReturnFromLower(BigDecimal price, BigDecimal prevPrice) {
        return prevPrice.compareTo(lower) < 0 && price.compareTo(lower) >= 0;
    }
 
    /**
     * 判断价格是否在中轨上方
     * @param price 当前价格
     * @return 是否在中轨上方
     */
    public boolean isAboveMid(BigDecimal price) {
        return price.compareTo(mid) > 0;
    }
 
    /**
     * 判断价格是否在中轨下方
     * @param price 当前价格
     * @return 是否在中轨下方
     */
    public boolean isBelowMid(BigDecimal price) {
        return price.compareTo(mid) < 0;
    }
 
    /**
     * 计算布林带宽度
     * @return 布林带宽度
     */
    public BigDecimal calculateBandWidth() {
        if (mid.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return upper.subtract(lower).divide(mid, 8, RoundingMode.HALF_UP);
    }
 
    /**
     * 计算价格相对于布林带的位置
     * @param price 当前价格
     * @return 价格位置指标 (-1: 下轨外, 0: 轨道内, 1: 上轨外)
     */
    public int getPricePosition(BigDecimal price) {
        if (price.compareTo(upper) > 0) {
            return 1;
        } else if (price.compareTo(lower) < 0) {
            return -1;
        } else {
            return 0;
        }
    }
}