1、策略更新

2、新增qmt
This commit is contained in:
2025-11-29 00:23:12 +08:00
parent 0a942f92d1
commit c9b61db5b7
47 changed files with 97116 additions and 8867 deletions

View File

@@ -0,0 +1,348 @@
"""
资金流因子模块
包含基于股票截面的资金流因子实现
"""
import numpy as np
import polars as pl
from main.factor.operator_framework import StockWiseFactor
class LGFlowFactor(StockWiseFactor):
"""大单净买量因子"""
def __init__(self):
super().__init__(
name="lg_flow",
parameters={},
required_factor_ids=["buy_lg_vol", "buy_elg_vol", "sell_lg_vol", "sell_elg_vol"]
)
def calc_factor(self, group_df: pl.DataFrame) -> pl.Series:
# 计算大单净买量
buy_lg = group_df["buy_lg_vol"]
buy_elg = group_df["buy_elg_vol"]
sell_lg = group_df["sell_lg_vol"]
sell_elg = group_df["sell_elg_vol"]
lg_net_flow = (buy_lg + buy_elg) - (sell_lg + sell_elg)
return lg_net_flow.alias(self.factor_id)
class FlowIntensityFactor(StockWiseFactor):
"""资金流强度因子"""
def __init__(self):
super().__init__(
name="flow_intensity",
parameters={},
required_factor_ids=["buy_lg_vol", "buy_elg_vol", "sell_lg_vol", "sell_elg_vol", "vol"]
)
def calc_factor(self, group_df: pl.DataFrame) -> pl.Series:
# 计算资金流强度
buy_lg = group_df["buy_lg_vol"]
buy_elg = group_df["buy_elg_vol"]
sell_lg = group_df["sell_lg_vol"]
sell_elg = group_df["sell_elg_vol"]
vol = group_df["vol"]
lg_net_flow = (buy_lg + buy_elg) - (sell_lg + sell_elg)
flow_intensity = lg_net_flow / (vol + 1e-8) # 避免除零
return flow_intensity.alias(self.factor_id)
class FlowDivergenceFactor(StockWiseFactor):
"""资金流背离因子"""
def __init__(self):
super().__init__(
name="flow_divergence",
parameters={},
required_factor_ids=["buy_sm_vol", "sell_sm_vol", "buy_lg_vol", "buy_elg_vol", "sell_lg_vol", "sell_elg_vol"]
)
def calc_factor(self, group_df: pl.DataFrame) -> pl.Series:
# 计算资金流背离度
buy_sm = group_df["buy_sm_vol"]
sell_sm = group_df["sell_sm_vol"]
buy_lg = group_df["buy_lg_vol"]
buy_elg = group_df["buy_elg_vol"]
sell_lg = group_df["sell_lg_vol"]
sell_elg = group_df["sell_elg_vol"]
sm_net_flow = buy_sm - sell_sm
lg_net_flow = (buy_lg + buy_elg) - (sell_lg + sell_elg)
flow_divergence = sm_net_flow - lg_net_flow
return flow_divergence.alias(self.factor_id)
class FlowStructureFactor(StockWiseFactor):
"""资金流结构因子"""
def __init__(self):
super().__init__(
name="flow_structure",
parameters={},
required_factor_ids=["buy_sm_vol", "buy_lg_vol", "buy_elg_vol", "vol"]
)
def calc_factor(self, group_df: pl.DataFrame) -> pl.Series:
# 计算资金流结构
buy_sm = group_df["buy_sm_vol"]
buy_lg = group_df["buy_lg_vol"]
buy_elg = group_df["buy_elg_vol"]
vol = group_df["vol"]
total_buy = buy_sm + buy_lg + buy_elg
lg_elg_buy_prop = (buy_lg + buy_elg) / (total_buy + 1e-8) # 避免除零
flow_structure = lg_elg_buy_prop.diff().alias(self.factor_id)
return flow_structure
class FlowAccelerationFactor(StockWiseFactor):
"""资金流加速度因子"""
def __init__(self):
super().__init__(
name="flow_acceleration",
parameters={},
required_factor_ids=["buy_lg_vol", "buy_elg_vol", "sell_lg_vol", "sell_elg_vol"]
)
def calc_factor(self, group_df: pl.DataFrame) -> pl.Series:
# 计算资金流加速度
buy_lg = group_df["buy_lg_vol"]
buy_elg = group_df["buy_elg_vol"]
sell_lg = group_df["sell_lg_vol"]
sell_elg = group_df["sell_elg_vol"]
lg_net_flow = (buy_lg + buy_elg) - (sell_lg + sell_elg)
lg_net_flow_change = lg_net_flow.diff()
flow_acceleration = lg_net_flow_change.diff()
return flow_acceleration.alias(self.factor_id)
class CostSqueeze(StockWiseFactor):
factor_id = "factor_cost_squeeze"
required_factor_ids = ["close", "cost_15pct", "cost_50pct", "cost_85pct", "vol"]
def __init__(self):
super().__init__(
name=self.factor_id,
parameters={},
required_factor_ids=self.required_factor_ids
)
def calc_factor(self, g: pl.DataFrame) -> pl.Series:
close = g["close"]
cost15 = g["cost_15pct"]
cost50 = g["cost_50pct"]
cost85 = g["cost_85pct"]
vol = g["vol"]
cost_range = cost85 - cost15
median_cost = cost50
price_pos = (close - median_cost) / (cost_range + 1e-6)
vol_5d = vol.rolling_mean(window_size=5, min_periods=1)
vol_ratio = vol / (vol_5d + 1e-6)
# 核心逻辑:成本区间窄 + 价格居中 + 量能萎缩 → 高挤压度
squeeze_score = (
(1.0 / (cost_range / (close + 1e-6) + 1e-6))
* (1.0 - price_pos.abs())
* (1.0 / (vol_ratio + 1e-6))
)
# 稳态化:对数变换
factor = (squeeze_score + 1.0).log()
return factor.alias(self.factor_id)
class HighCostSelling(StockWiseFactor):
factor_id = "factor_high_cost_selling"
required_factor_ids = ["close", "cost_85pct", "buy_sm_vol", "sell_lg_vol", "vol"]
def __init__(self):
super().__init__(
name=self.factor_id,
parameters={},
required_factor_ids=self.required_factor_ids
)
def calc_factor(self, g: pl.DataFrame) -> pl.Series:
close = g["close"]
cost85 = g["cost_85pct"]
buy_sm_vol = g["buy_sm_vol"]
sell_lg_vol = g["sell_lg_vol"]
vol = g["vol"]
is_above_85 = (close > cost85).cast(pl.Float64)
small_buy_ratio = buy_sm_vol / (vol + 1e-6)
large_sell_ratio = sell_lg_vol / (vol + 1e-6)
hcsp = is_above_85 * small_buy_ratio * large_sell_ratio
# 稳态化:取对数(避免极端值)
factor = (hcsp + 1e-6).log()
return factor.alias(self.factor_id)
class LowCostAccumulation(StockWiseFactor):
factor_id = "factor_low_cost_accumulation"
required_factor_ids = ["close", "his_low", "cost_15pct", "buy_lg_vol", "buy_elg_vol", "vol"]
def __init__(self):
super().__init__(
name=self.factor_id,
parameters={}, # 无参数,可扩展
required_factor_ids=self.required_factor_ids
)
def calc_factor(self, g: pl.DataFrame) -> pl.Series:
close = g["close"]
his_low = g["his_low"]
cost15 = g["cost_15pct"]
buy_lg_vol = g["buy_lg_vol"]
buy_elg_vol = g["buy_elg_vol"]
vol = g["vol"]
is_below_15 = (close < cost15).cast(pl.Float64)
# 近5日最低价含当日
rolling_min_5 = his_low.rolling_min(window_size=5, min_periods=1)
# 注意his_low 通常是历史最低,但这里我们用 close 的滚动最小更合理
# 修正:应使用 close 的滚动最小判断是否新低
close_rolling_min_5 = close.rolling_min(window_size=5, min_periods=1)
not_new_low = (close >= close_rolling_min_5).cast(pl.Float64)
big_buy_vol = buy_lg_vol + buy_elg_vol
big_buy_ratio = big_buy_vol / (vol + 1e-6)
lc_am = is_below_15 * not_new_low * big_buy_ratio
# 稳态化
factor = (lc_am + 1e-6).log()
return factor.alias(self.factor_id)
class InstNetAccum(StockWiseFactor):
factor_id = "inst_net_accum"
required_factor_ids = ["close", "buy_lg_vol", "buy_elg_vol", "sell_lg_vol", "sell_elg_vol", "circ_mv"]
def __init__(self):
super(InstNetAccum, self).__init__(
name=self.factor_id,
parameters={},
required_factor_ids=self.required_factor_ids
)
def calc_factor(self, g: pl.DataFrame) -> pl.Series:
close = g["close"]
buy_lg = g["buy_lg_vol"]
buy_elg = g["buy_elg_vol"]
sell_lg = g["sell_lg_vol"]
sell_elg = g["sell_elg_vol"]
circ_mv = g["circ_mv"]
big_net_vol = (buy_lg + buy_elg) - (sell_lg + sell_elg)
circ_shares = circ_mv / (close + 1e-6)
ina = big_net_vol / (circ_shares + 1e-6)
# 3日收益率抑制大涨
ret3 = close / close.shift(3) - 1
ret3 = ret3.fill_null(strategy="forward").fill_null(0.0)
ina = pl.when(ret3.abs() < 0.05).then(ina).otherwise(0.0)
return ina.log1p().alias(self.factor_id)
class ChipLockin(StockWiseFactor):
factor_id = "chip_lockin"
required_factor_ids = ["cost_5pct", "cost_95pct", "winner_rate"]
def __init__(self):
super(ChipLockin, self).__init__(
name=self.factor_id,
parameters={},
required_factor_ids=self.required_factor_ids
)
def calc_factor(self, g: pl.DataFrame) -> pl.Series:
cost5 = g["cost_5pct"]
cost95 = g["cost_95pct"]
winner_rate = g["winner_rate"]
cost_width = cost95 - cost5
width_5d = cost_width.rolling_mean(window_size=5, min_periods=1)
width_10d = cost_width.rolling_mean(window_size=10, min_periods=1)
# 避免除零
width_contraction = (width_10d - width_5d) / (width_10d + 1e-6)
winner_std_5 = winner_rate.rolling_std(window_size=5, min_periods=1).fill_null(1e-6)
clm = width_contraction * (1.0 / (winner_std_5 + 1e-6))
return clm.log1p().alias(self.factor_id)
class RetailOutInstIn(StockWiseFactor):
factor_id = "retail_out_inst_in"
required_factor_ids = ["close", "buy_sm_vol", "sell_sm_vol", "buy_lg_vol", "buy_elg_vol", "sell_lg_vol", "sell_elg_vol"]
def __init__(self):
super(RetailOutInstIn, self).__init__(
name=self.factor_id,
parameters={},
required_factor_ids=self.required_factor_ids
)
def calc_factor(self, g: pl.DataFrame) -> pl.Series:
close = g["close"]
buy_sm = g["buy_sm_vol"]
sell_sm = g["sell_sm_vol"]
buy_lg = g["buy_lg_vol"]
buy_elg = g["buy_elg_vol"]
sell_lg = g["sell_lg_vol"]
sell_elg = g["sell_elg_vol"]
small_net_out = sell_sm - buy_sm
big_net_in = (buy_lg + buy_elg) - (sell_lg + sell_elg)
# 价格抗跌近5日未破位用 close 自身滚动最小)
close_min_5 = close.rolling_min(window_size=5, min_periods=1)
roii = small_net_out * big_net_in
return roii.log1p().alias(self.factor_id)
class AccumAccel(StockWiseFactor):
factor_id = "accum_accel"
required_factor_ids = ["buy_lg_vol", "buy_elg_vol", "sell_lg_vol", "sell_elg_vol", "vol"]
def __init__(self):
super(AccumAccel, self).__init__(
name=self.factor_id,
parameters={},
required_factor_ids=self.required_factor_ids
)
def calc_factor(self, g: pl.DataFrame) -> pl.Series:
buy_lg = g["buy_lg_vol"]
buy_elg = g["buy_elg_vol"]
sell_lg = g["sell_lg_vol"]
sell_elg = g["sell_elg_vol"]
vol = g["vol"]
big_net_vol = (buy_lg + buy_elg) - (sell_lg + sell_elg)
big_net_ratio = big_net_vol / (vol + 1e-6)
net_5d = big_net_ratio.rolling_mean(window_size=5, min_periods=1)
net_5d_lag = net_5d.shift(5).fill_null(strategy="forward").fill_null(0.0)
acceleration = net_5d - net_5d_lag
return acceleration.log1p().alias(self.factor_id)