|
|
不识庐山真面目,只缘身在此山中!我们之所有在股市沉浮就是很多时候没有看清股市的全貌,今天我们就一步一步创建股市的全景3D模型图。
1.账号登录,调用账号的例子:
from jqdatasdk import *
import config
auth(config.JQ_USERNAME, config.JQ_PASSWORD)
df = get_fundamentals(
query(
valuation.code,
valuation.market_cap
).limit(10),
date='2024-12-31'
)
print(df)
2.聚宽数据接入,获取指定股票数据,获取宁德时代,鲁信创投、金风科技、航天电子、锋龙股份、中国卫通、北斗星通,获取2025-10-11之前过去1年日线的波动数据,(股票名称,股票上市时间,股票总市值,3周的价格,),将这些波动数据用在下面的3D股票状态模型之中。
3.3D 股票状态模型,x轴为上市时间,y轴是分类(军工、人工智能、能源、矿产、电子还有地产、汽车、医疗、教育、食品加工、农业、矿产类的股票,50种比较能代表行业的股票),z轴为当前价格,3D 动画 / 轨迹按3周35天波动,气泡模型,红色表示在膨胀,绿色表示在收缩,气泡的体积是总市值,价格是高度。每个球上面标识股票名称,涨跌幅强度映射颜色深浅,文字的颜色和气泡的颜色同步,小涨 → 浅红、✔ 大涨 → 深红、✔ 小跌 → 浅绿✔ 大跌 → 深绿
✔ 横盘 → 灰,并且颜色要明确使用 RGBA(4 通道),alpha 固定为 1.0,关闭 depthshade。
参考代码:
from jqdatasdk import *
import config
auth(config.JQ_USERNAME, config.JQ_PASSWORD)
stocks = {
"宁德时代": "300750.XSHE",
"鲁信创投": "600783.XSHG",
"金风科技": "002202.XSHE",
"航天电子": "600879.XSHG",
"锋龙股份": "002931.XSHE",
"中国卫通": "601698.XSHG",
"北斗星通": "002151.XSHE"
}
codes = list(stocks.values())
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# ===== 聚宽账号可用的最后日期 =====
end_date = "2025-10-11"
# 向前取 21 天(3 周)
start_date = (
datetime.strptime(end_date, "%Y-%m-%d") - timedelta(days=21)
).strftime("%Y-%m-%d")
price_df = get_price(
codes,
start_date=start_date,
end_date=end_date,
frequency='daily',
fields=['close', 'volume', 'money'],
panel=False
)
# 按股票分组
groups = price_df.groupby('code')
stocks_data = {}
for code, g in groups:
g = g.sort_values('time')
# ===== 核心量化特征 =====
returns = g['close'].pct_change()
volatility = returns.std() # 波动率
activity = g['volume'].mean() / 1e6 # 成交活跃度
price_level = (g['close'].iloc[-1] - g['close'].min()) / \
(g['close'].max() - g['close'].min())
money = g['money'].mean() # 资金规模
stocks_data[code] = {
"activity": activity,
"price_level": price_level,
"volatility": volatility,
"market_cap_proxy": money
}
df_base = pd.DataFrame.from_dict(stocks_data, orient='index')
df_base['name'] = [k for k in stocks.keys()]
df_base['size'] = np.log10(df_base['market_cap_proxy'])
n_days = 5
days_data = []
for i in range(n_days):
df = df_base.copy()
df["activity"] *= (1 + np.random.normal(0, df["volatility"]))
df["price_level"] += np.random.normal(0, df["volatility"] * 2)
df["price_level"] = df["price_level"].clip(0, 1)
df["size"] += np.random.normal(0, 0.02)
days_data.append(df)
colors_per_day = []
for i in range(n_days):
if i == 0:
colors_per_day.append(['gray'] * len(df_base))
else:
delta = days_data[i]["price_level"].values - days_data[i-1]["price_level"].values
colors = np.where(delta >= 0, 'red', 'green')
colors_per_day.append(colors)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib import cm
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
n_stocks = len(days_data[0])
# ===== 颜色映射(活跃度)=====
all_activity = np.concatenate(
[d["activity"].values for d in days_data]
)
act_min, act_max = all_activity.min(), all_activity.max()
norm_activity = lambda x: (x - act_min) / (act_max - act_min + 1e-6)
cmap = cm.RdYlGn_r # 绿 → 黄 → 红
# ===== 创建 3D 图 =====
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# 初始帧
df0 = days_data[0]
colors0 = cmap(norm_activity(df0["activity"].values))
sc = ax.scatter(
df0["activity"],
df0["price_level"],
df0["size"],
s=df0["size"] * 120, # 球体量
c=colors0,
alpha=0.9,
depthshade=True
)
# ===== 股票名称标签 =====
texts = []
for i in range(n_stocks):
t = ax.text(
df0["activity"].iloc[i],
df0["price_level"].iloc[i],
df0["size"].iloc[i] + 0.03,
df0["name"].iloc[i],
fontsize=9,
ha='center'
)
texts.append(t)
# ===== 轨迹 =====
lines = []
for _ in range(n_stocks):
line, = ax.plot([], [], [], color='gray', alpha=0.35, linewidth=1)
lines.append(line)
ax.set_xlabel("Activity(成交活跃度)")
ax.set_ylabel("Price Level(价格位置)")
ax.set_zlabel("Log(资金规模)")
ax.set_xlim(0, df0["activity"].max() * 1.4)
ax.set_ylim(0, 1.05)
ax.set_zlim(df0["size"].min() - 0.1, df0["size"].max() + 0.15)
# ===== 动画更新 =====
def update(frame):
df = days_data[frame]
colors = cmap(norm_activity(df["activity"].values))
# 更新球位置
sc._offsets3d = (
df["activity"].values,
df["price_level"].values,
df["size"].values
)
sc.set_sizes(df["size"].values * 120)
sc.set_facecolors(colors)
# 更新文字
for i, txt in enumerate(texts):
txt.set_position((df["activity"].iloc[i], df["price_level"].iloc[i]))
txt.set_3d_properties(df["size"].iloc[i] + 0.03)
txt.set_text(df["name"].iloc[i])
# 更新轨迹
for i in range(n_stocks):
xs = [days_data[d]["activity"].iloc[i] for d in range(frame + 1)]
ys = [days_data[d]["price_level"].iloc[i] for d in range(frame + 1)]
zs = [days_data[d]["size"].iloc[i] for d in range(frame + 1)]
lines[i].set_data(xs, ys)
lines[i].set_3d_properties(zs)
ax.set_title(f"3D 股票状态演化(第 {frame + 1} 帧)")
return sc, *lines, *texts
ani = FuncAnimation(
fig,
update,
frames=len(days_data),
interval=1200,
blit=False
)
plt.show()
|
|