布林带(Bollinger Bands)也称为布林通道、保力加通道,是由约翰·布林格(John Bollinger)发明的技术分析指标。布林通道通常被用来确认资产价格波动范围。

布林通道是由三条平滑的曲线组成的趋势线图表,中线为均线,上/下线为中线±2个标准差。

在上一个章节没有自己造轮子,直接使用 Pandas 函数计算 MACD,非常方便的实现了计算和绘图。

本次使用更快捷的方式,直接上飞机:使用 AI 来写代码。

我在上篇文章《利用AI快速跨过新手区:用DevChat编写Python程序》 介绍了如何使用 DevChat 编写程序。

这次直接借助 DevChat 来编写布林带并绘图。

AI 编程

[量化投资-学习笔记007]Python+TDengine从零开始搭建量化分析平台-布林带-LMLPHP

因为数据源来源不一致,对程序进行修改,直接套用之前的代码。

import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import requests
import json
from requests.auth import HTTPBasicAuth
import datetime
import numpy as np
import fun

## def global variables
global dbname 
global stbname 
global username
global password
global tdurl
global logfile

dbname = 'trade_data_a'
stbname = 'tdata'
username = 'root'
password = 'taosdata'
## 如果是本地数据库将地址修改为localhost
tdurl = 'http://10.7.7.14:6041/rest/sql/trade_data_a?tz=Asia/Shanghai'
logfile = 'error.log'

##SQL
st = '2022-06-01'
et = '2022-10-01'
sql = 'select tdate,close from trade_data_c.tdata where fcode="000001" and tdate>="'+st+'" and tdate<="'+et+'"'

## Get data to DataFrame
def request_get_d(resInfo):
    load_data = json.loads(resInfo)
    data = load_data.get("data")
    df = pd.DataFrame(data)
    df.rename(columns={0:'tdate',1:'close'},inplace=True)
    return df

if __name__ == '__main__':
    # 指定默认字体
    matplotlib.rcParams['font.sans-serif'] = ['SimHei']
    matplotlib.rcParams['font.family']='sans-serif'
    # 解决保存图像是负号'-'显示为方块的问题
    matplotlib.rcParams['axes.unicode_minus'] = False    
    rt = fun.request_post(tdurl,sql,username,password)
    scode = fun.check_return(rt)
    if scode != 'error':
        df = request_get_d(rt)
# 计算20日移动平均线、标准差、布林上线和布林下线
        df['20日MA'] = df['close'].rolling(window=20).mean()
        df['20日STD'] = df['close'].rolling(window=20).std()
        df['布林上线'] = df['20日MA'] + (df['20日STD'] * 2)
        df['布林下线'] = df['20日MA'] - (df['20日STD'] * 2)

# 绘制布林带
        df[['close','20日MA','布林上线','布林下线']].plot(figsize=(12,6))
        plt.title('平安银行布林带')
        plt.ylabel('价格 (人民币)')
        plt.show()

运行脚本,图形可以正常绘制,但是中文显示有问题。不过不急,直接问AI。
[量化投资-学习笔记007]Python+TDengine从零开始搭建量化分析平台-布林带-LMLPHP

[量化投资-学习笔记007]Python+TDengine从零开始搭建量化分析平台-布林带-LMLPHP
修改后图形就显示正常了。

[量化投资-学习笔记007]Python+TDengine从零开始搭建量化分析平台-布林带-LMLPHP

程序优化

利用 DevChat 对程序进行进一步优化:

import pandas as pd
import matplotlib.pyplot as plt
import requests
import json
from matplotlib import font_manager

# Global variables
DB_NAME = 'trade_data_a'
STB_NAME = 'tdata'
USERNAME = 'root'
PASSWORD = 'taosdata'
TD_URL = 'http://10.7.7.14:6041/rest/sql/trade_data_a?tz=Asia/Shanghai'
LOG_FILE = 'error.log'

# Constants
START_DATE = '2022-06-01'
END_DATE = '2022-10-01'
SQL = f'select tdate,close from trade_data_c.tdata where fcode="000001" and tdate>="{START_DATE}" and tdate<="{END_DATE}"'

# Specify default font
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.family'] = 'sans-serif'

# Resolve issue with negative numbers being displayed as a square
plt.rcParams['axes.unicode_minus'] = False


def request_get_d(resInfo):
    load_data = json.loads(resInfo)
    data = load_data.get("data")
    df = pd.DataFrame(data)
    df.rename(columns={0: 'tdate', 1: 'close'}, inplace=True)
    return df


if __name__ == '__main__':
    rt = fun.request_post(TD_URL, SQL, USERNAME, PASSWORD)
    scode = fun.check_return(rt)
    
    if scode != 'error':
        df = request_get_d(rt)
        # Calculate 20-Day Moving Average, Standard Deviation, Upper Band and Lower Band
        df['20 Day MA'] = df['close'].rolling(window=20).mean()
        df['20 Day STD'] = df['close'].rolling(window=20).std()
        df['Upper Band'] = df['20 Day MA'] + (df['20 Day STD'] * 2)
        df['Lower Band'] = df['20 Day MA'] - (df['20 Day STD'] * 2)
        # Draw Bollinger Bands
        df[['close', '20 Day MA', 'Upper Band', 'Lower Band']].plot(figsize=(12, 6))
        plt.title('Ping An Bank Bollinger Bands')
        plt.ylabel('Price')
        plt.show()

别的不说,起码看上去挺漂亮的。不过有两个小问题:

  1. import fun 被删除了,这是我的一个存储函数的文件,不引用这个,很多自定义函数会报错。
  2. 中文名称变英文了。
    [量化投资-学习笔记007]Python+TDengine从零开始搭建量化分析平台-布林带-LMLPHP

进阶

直接让 DevChat 编写完整的程序。
[量化投资-学习笔记007]Python+TDengine从零开始搭建量化分析平台-布林带-LMLPHP
[量化投资-学习笔记007]Python+TDengine从零开始搭建量化分析平台-布林带-LMLPHP

import taos
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt

# 连接到 TDengine 数据库
conn = taos.connect(host='10.7.7.14', user='root', password='taosdata', database='trade_data_c')

# 查询一段时间内的收盘价
query = "select tdate,close from trade_data_c.tdata where fcode='000001' and tdate>= '2022-03-01' AND tdate <= '2023-02-28'"
result = conn.query(query)
df = pd.DataFrame(result.fetch_all(), columns=['Timestamp', 'Close'])
df['Timestamp'] = pd.to_datetime(df['Timestamp'])
df.set_index('Timestamp', inplace=True)

# 计算20日均线和标准差
df['20 Day MA'] = df['Close'].rolling(window=20).mean()
df['20 Day STD'] = df['Close'].rolling(window=20).std()

# 计算布林带上线和下线
df['Upper Band'] = df['20 Day MA'] + (df['20 Day STD'] * 2)
df['Lower Band'] = df['20 Day MA'] - (df['20 Day STD'] * 2)

# 绘制布林带图形
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['Close'], label='Close')
plt.plot(df.index, df['Upper Band'], label='Upper Band')
plt.plot(df.index, df['Lower Band'], label='Lower Band')
plt.title('Bollinger Bands')
plt.xlabel('Timestamp')
plt.ylabel('Price')
plt.legend()
plt.show()

# 提示买卖信号
last_close = df['Close'].iloc[-1]
last_upper_band = df['Upper Band'].iloc[-1]
last_lower_band = df['Lower Band'].iloc[-1]

if last_close > last_upper_band:
    print('做空')
elif last_close < last_lower_band:
    print('做多')
else:
    print('继续观察')

[量化投资-学习笔记007]Python+TDengine从零开始搭建量化分析平台-布林带-LMLPHP
有一种要失业的预感!!

题外话

布林带或者叫布林通道,顾名思义,绘制了一条以均线为中心,4倍标准差宽度的通道。

和 MACD 一样,布林带依然是对价格趋势的描述,只是相较于 MACD 描述趋势的变化程度,布林带更关注于价格的异常波动。

要搞懂布林带,就需要从定义入手,除了之前提到的均线,另一个重要的概念就是标准差。

标准差是反映一组数据离散程度最常用的一种量化形式,是表示精确度的重要指标。标准差的计算步骤如下:

  1. 求平均:计算数据集中数值的平均值。
  2. 计算每个数值与平均值的差:从每个数值中减去平均值,然后求差的平方。
  3. 求结果的平均:将所有差的平方相加,然后除以数据集中的数值个数,再取平方根。

由此看出,布林带绘制了一条价格变化的通道,在绝大多数情况下,价格会处于通道内,并围绕中线上下波动。

对于单边趋势(如上涨),价格如果突破布林带,说明开始了加速行情。
对于震荡趋势,价格如果突破了布林带,说明要开始回调或突破。

世界上并不存在圣杯和银弹,像盲人摸象一样,任何指标都只能反应市场的一部分,而且计算的过程越复杂,离真是可能越远。

11-08 17:56