chore(config): 添加环境变量示例及.gitignore更新
- 新增 .env.example,包含 Tushare API、钉钉机器人和PostgreSQL数据库配置模板 - 更新.gitignore,忽略本地配置文件如 .env.local 和 config_local.py - 添加对报表文件命名规则的支持,保留示例文件不忽略 - 删除废弃的 chart.py 及相关图表模块代码 - 新增 config/settings.py,实现从环境变量读取配置的统一接口 - 设置数据目录及缓存目录,确保目录存在,提高配置管理规范性
This commit is contained in:
100
test.py
Normal file
100
test.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import vectorbt as vbt
|
||||
from numba import njit
|
||||
import vectorbt as vbt
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
import pandas as pd
|
||||
from loguru import logger
|
||||
from chart import resample_data, QuantChart
|
||||
from db_config import DatabaseManager, DatabaseConfig
|
||||
from vectorbt.base.reshape_fns import to_2d_array
|
||||
|
||||
|
||||
def get_kline(code: str) -> list:
|
||||
"""
|
||||
获取所有指数代码
|
||||
:return:
|
||||
"""
|
||||
db_config = DatabaseConfig()
|
||||
logger.info(f"数据库连接: {db_config.connection_string}")
|
||||
|
||||
db_manager = DatabaseManager(db_config)
|
||||
sql = f"SELECT date as time, open, high, low, close, volume FROM public.index_kline where code='{code}' order by date;"
|
||||
res = db_manager.execute_query(sql)
|
||||
data_list = [dict(item) for item in res]
|
||||
df = pd.DataFrame(data_list)
|
||||
df["time"] = pd.to_datetime(df["time"])
|
||||
num_cols = ["open", "high", "low", "close", "volume"]
|
||||
for col in num_cols:
|
||||
if col in df.columns:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce").astype(float)
|
||||
return df
|
||||
|
||||
symbol = "399998"
|
||||
timeframe = "1D"
|
||||
|
||||
df = get_kline(code=symbol)
|
||||
df = resample_data(df, timeframe)
|
||||
df.rename(columns={'time': 'date'}, inplace=True)
|
||||
print(df.head())
|
||||
if 'date' in df.columns:
|
||||
df = df.set_index('date')
|
||||
price = df['close']
|
||||
|
||||
# 2. 计算90天滚动波动率(年化)
|
||||
returns = price.pct_change()
|
||||
volatility_90d = returns.rolling(window=90, min_periods=90).std() * np.sqrt(365)
|
||||
|
||||
# 3. 计算波动率倒数作为权重
|
||||
inv_vol = 1 / volatility_90d
|
||||
# 标准化权重(可选,使其更易解释)
|
||||
inv_vol_normalized = inv_vol / inv_vol.rolling(window=252).mean()
|
||||
|
||||
# 4. 创建每周重新平衡的信号
|
||||
# 获取每周最后一个交易日
|
||||
weekly_rebalance = pd.Series(False, index=price.index)
|
||||
weekly_last_days = price.resample('W').last().index
|
||||
for date in weekly_last_days:
|
||||
# 找到最接近的交易日
|
||||
idx = price.index.get_indexer([date], method='ffill')[0]
|
||||
if idx >= 0:
|
||||
weekly_rebalance.iloc[idx] = True
|
||||
|
||||
# 5. 定义订单函数
|
||||
@njit
|
||||
def order_func_nb(c, inv_vol_arr, rebalance_arr):
|
||||
# 获取当前的波动率倒数权重
|
||||
inv_vol_now = vbt.nb.flex_select_auto_nb(inv_vol_arr, c.i, c.col, False)
|
||||
rebalance_now = vbt.nb.flex_select_auto_nb(rebalance_arr, c.i, c.col, False)
|
||||
|
||||
# 只在重新平衡日调整仓位
|
||||
if not rebalance_now or np.isnan(inv_vol_now):
|
||||
return vbt.nb.order_nothing_nb()
|
||||
|
||||
# 目标仓位 = 总价值 * 波动率倒数权重
|
||||
# 这里使用 TargetPercent 类型,权重越高仓位越大
|
||||
target_percent = min(inv_vol_now, 1.0) # 限制最大100%仓位
|
||||
|
||||
return vbt.nb.order_nb(
|
||||
size=target_percent,
|
||||
size_type=vbt.SizeType.TargetPercent,
|
||||
direction=vbt.Direction.LongOnly
|
||||
)
|
||||
|
||||
# 6. 运行回测
|
||||
pf = vbt.Portfolio.from_order_func(
|
||||
price,
|
||||
order_func_nb,
|
||||
to_2d_array(inv_vol_normalized),
|
||||
to_2d_array(weekly_rebalance),
|
||||
init_cash=100,
|
||||
freq='1D'
|
||||
)
|
||||
|
||||
# 7. 查看结果
|
||||
print(pf.stats())
|
||||
print(f"\n波动率倒数策略 vs 买入持有:")
|
||||
print(f"总收益率: {pf.total_return():.2%}")
|
||||
Reference in New Issue
Block a user