TradingView 是一款广受交易者青睐的行情分析软件,而 lightweight-charts 是其推出的精简版 Js 开源库。凭借 TradingView 在交易领域的深厚积累,lightweight-charts 拥有高性能与出色的用户体验。
lightweight-charts-python 是基于 TradingView 的 lightweight-charts 开发的 Python 版本。它通过 webview 将 lightweight-charts 集成到 Python 环境中,让 Python 开发者无需掌握 Javascript,即可轻松使用 lightweight-charts。
此外,Python 的灵活性使得 lightweight-charts-python 能与各类框架无缝对接。例如,国内知名的实盘框架 vnpy,其 GUI 界面基于 PySide6 构建。lightweight-charts-python 可轻松集成到 PySide6 中,进而融入我们的CryptoTrader项目中,为大家提供更强大的工具。
1. 环境准备
1.1 安装lightweight-charts-python
pip install lightweight-charts
1.2 导入对应的库
from datetime import datetime
import pandas as pd
from gui.ui import QtCore
from gui.ui.qt import QVBoxLayout, QMainWindow, QWidget, QApplication
from core.trader.constant import Exchange, Interval
from core.trader.database import get_database, BaseDatabase
from lightweight_charts.widgets import QtChart
- • gui.ui封装了pyside6的类;
- • core.trader.constant里面封装了常数项;
- • core.trader.database封装了数据库的实现。
1.3 获取数据
database: BaseDatabase = get_database()
bars = database.load_bar_data(
symbol="BTC-USDT-SWAP",
exchange=Exchange.OKX,
interval=Interval.MINUTE,
start=datetime(2024, 3, 7),
end=datetime(2024, 9, 1)
)
n = 1000
history = bars[:n]
new_data = bars[n:]
data: list = []
for bar in history:
data.append([bar.datetime.strftime("%Y-%m-%d %H:%M:%S"), bar.open_price, bar.high_price, bar.low_price, bar.close_price, bar.volume])
df = pd.DataFrame(data, columns=["time", "open", "high", "low", "close", "volume"])
- • 取从2024年3月7日到2024年9月1日OKX的"BTC-USDT-SWAP"一分钟数据;
- • 取前1000根K线数据进行初始化,后面的数据动态展示的时候使用;
- • 讲从数据库获取的时候转化为DataFrame格式数据。
2. 功能展示
2.1 展示历史数据
widget = QtChart(main_widget)
widget.watermark(text='知识星球:创新技术阁')
widget.set(df)
- • 创建一个QtChart,用于在pyside6中主窗口使用;
- • 添加水印;
- • 设置数据。
2.2 增加指标
定义主图SAM计算方法:
def calculate_sma(df, period:int=20):
return pd.DataFrame({
'time': df['time'],
f'SMA{period}': df['close'].rolling(window=period).mean()
}).dropna()
定义副图RSI的计算方法:
def calculate_rsi(df, period:int=14):
return pd.DataFrame({
'time': df['time'],
f'RSI': talib.RSI(df['close'], timeperiod=14)
})
分别添加指标SAM20和RSI到主图和副图:
line = widget.create_line('SMA20')
sma_data = calculate_sma(df, period=20)
line.set(sma_data)
rsi_chart = widget.create_subchart(height=0.3, width=1, sync=True)
rsi_line = rsi_chart.create_line('RSI', color='#FF6B6B')
rsi_line.set(calculate_rsi(df, period=14))
运行结果:
2.3 增加图例
widget.legend(True)
rsi_chart.legend(True)
2.4 多K线图支持
widget = QtChart(main_widget, inner_width=1, inner_height=0.5)
widget.watermark(text='知识星球:创新技术阁')
weekly_chart = widget.create_subchart(width=0.5, height=0.5,sync=True)
weekly_chart.watermark(text='知识星球:创新技术阁')
monthly_chart = widget.create_subchart(width=0.5, height=0.5,sync=True)
monthly_chart.watermark(text='知识星球:创新技术阁')
widget.legend(True)
weekly_chart.legend(True)
monthly_chart.legend(True)
widget.set(df)
weekly_chart.set(df)
monthly_chart.set(df)
- • 创建两个子图:weekly_chart和monthly_chart;
- • 主图使用inner_width控制宽度,1表示整个窗口宽度,inner_height控制高度,0.5表示一半窗口高度;
- • 子图使用width控制宽度,0.5表示一半窗口宽度,height控制高度,0.5表示一半窗口高度
2.5 动态更新K线
def update_bar():
bar = new_data.pop(0)
new_bar = pd.Series([bar.datetime.strftime("%Y-%m-%d %H:%M:%S"), bar.open_price, bar.high_price, bar.low_price, bar.close_price, bar.volume],
index=["time", "open", "high", "low", "close", "volume"])
widget.update(new_bar)
weekly_chart.update(new_bar)
monthly_chart.update(new_bar)
timer = QtCore.QTimer()
timer.timeout.connect(update_bar)
timer.start(1000)
- • 使用定时器模拟数据更新;
- • 使用update更新新的bar数据。
2.6 回调函数
lightweight-charts-python 还支持图表事件的回调机制。你可以方便地实现用户交互功能,比如时间框架选择、搜索符号、横线位置调整等。例如,当用户选择不同的时间框架时,可以自动更新图表数据:
def on_timeframe_selection(chart): # 当用户改变timeframe的时候调用
if len(new_data) == 0:
return
chart.set(new_data, True)
3. 完整代码
#!/usr/bin/env python
# encoding: utf-8
"""
@version: v1.0
@author: Kandy.Ye
@contact: Kandy.Ye@outlook.com
@file: kline_test.py
@time: 2025/4/9 9:02
"""
from datetime import datetime
import pandas as pd
import talib
from gui.ui import QtCore
from gui.ui.qt import QVBoxLayout, QMainWindow, QWidget, QApplication
from core.trader.constant import Exchange, Interval
from core.trader.database import get_database, BaseDatabase
from lightweight_charts.widgets import QtChart
def calculate_sma(df, period:int=20):
return pd.DataFrame({
'time': df['time'],
f'SMA{period}': df['close'].rolling(window=period).mean()
}).dropna()
def calculate_rsi(df, period:int=14):
return pd.DataFrame({
'time': df['time'],
f'RSI': talib.RSI(df['close'], timeperiod=14)
})
if __name__ == "__main__":
app = QApplication([])
window = QMainWindow()
layout = QVBoxLayout()
main_widget = QWidget()
main_widget.setLayout(layout)
window.resize(800, 500)
layout.setContentsMargins(0, 0, 0, 0)
database: BaseDatabase = get_database()
bars = database.load_bar_data(
symbol="BTC-USDT-SWAP",
exchange=Exchange.OKX,
interval=Interval.MINUTE,
start=datetime(2024, 3, 7),
end=datetime(2024, 9, 1)
)
n = 1000
history = bars[:n]
new_data = bars[n:]
data: list = []
for bar in history:
data.append([bar.datetime.strftime("%Y-%m-%d %H:%M:%S"), bar.open_price, bar.high_price, bar.low_price, bar.close_price, bar.volume])
df = pd.DataFrame(data, columns=["time", "open", "high", "low", "close", "volume"])
widget = QtChart(main_widget, inner_width=1, inner_height=0.5)
widget.watermark(text='知识星球:创新技术阁')
weekly_chart = widget.create_subchart(width=0.5, height=0.5,sync=True)
weekly_chart.watermark(text='知识星球:创新技术阁')
monthly_chart = widget.create_subchart(width=0.5, height=0.5,sync=True)
monthly_chart.watermark(text='知识星球:创新技术阁')
widget.legend(True)
weekly_chart.legend(True)
monthly_chart.legend(True)
widget.set(df)
weekly_chart.set(df)
monthly_chart.set(df)
# line = widget.create_line('SMA20')
# sma_data = calculate_sma(df, period=20)
# line.set(sma_data)
# rsi_chart = widget.create_subchart(height=0.3, width=1, sync=True)
# rsi_line = rsi_chart.create_line('RSI', color='#FF6B6B')
# rsi_line.set(calculate_rsi(df, period=14))
# rsi_chart.legend(True)
def update_bar():
bar = new_data.pop(0)
new_bar = pd.Series([bar.datetime.strftime("%Y-%m-%d %H:%M:%S"), bar.open_price, bar.high_price, bar.low_price, bar.close_price, bar.volume],
index=["time", "open", "high", "low", "close", "volume"])
widget.update(new_bar)
weekly_chart.update(new_bar)
monthly_chart.update(new_bar)
def on_timeframe_selection(chart): # Called when the user changes the timeframe.
if len(new_data) == 0:
return
chart.set(new_data, True)
timer = QtCore.QTimer()
timer.timeout.connect(update_bar)
timer.start(1000)
layout.addWidget(widget.get_webview())
window.setCentralWidget(main_widget)
window.show()
app.exec()
4. 联系方式
- • Github: github.com/KandyYe