Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换) - 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制) - 增量同步:支持 -incr/-since 参数按存档日期增量入库 - 策略回测:净值曲线/10指标/基准对比,含基本面过滤 - 用户认证:用户名密码 + 邮箱验证码双登录方式 - 前端:三Tab SPA(实时行情/策略回测/数据管理)
This commit is contained in:
83
internal/backtest/fundamental.go
Normal file
83
internal/backtest/fundamental.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package backtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"stock-analysis/internal/model"
|
||||
)
|
||||
|
||||
// FundamentalFilter 正股基本面过滤器,用于排除信用风险过高的转债。
|
||||
// 过滤规则:
|
||||
// - ST / *ST 正股对应的转债一律排除
|
||||
// - 资产负债率超过阈值的转债排除
|
||||
// - 净利润为负(连续亏损)的转债排除
|
||||
type FundamentalFilter struct {
|
||||
MaxDebtRatio float64 // 资产负债率上限(%),默认 70
|
||||
stock2bond map[string]string
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// NewFundamentalFilter 创建基本面过滤器。
|
||||
// stock2bond 为正股代码到转债代码的映射(一只正股可能对应多只转债,取任意一只即可)。
|
||||
func NewFundamentalFilter(stock2bond map[string]string) *FundamentalFilter {
|
||||
return &FundamentalFilter{
|
||||
MaxDebtRatio: 70,
|
||||
stock2bond: stock2bond,
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
// SetEnabled 启用/禁用过滤器。
|
||||
func (f *FundamentalFilter) SetEnabled(v bool) { f.enabled = v }
|
||||
|
||||
// Apply 给定当日 universe 行情和基本面数据,返回过滤后的行情列表。
|
||||
// fundamentals 的 key 为正股代码。
|
||||
func (f *FundamentalFilter) Apply(ctx context.Context, universe []Quote, fundamentals map[string]*model.StockFundamental) []Quote {
|
||||
if !f.enabled || len(fundamentals) == 0 {
|
||||
return universe
|
||||
}
|
||||
|
||||
// 构建转债代码到正股代码的映射(反向查找)。
|
||||
bond2stock := make(map[string]string, len(f.stock2bond))
|
||||
for stock, bond := range f.stock2bond {
|
||||
bond2stock[bond] = stock
|
||||
}
|
||||
|
||||
filtered := make([]Quote, 0, len(universe))
|
||||
for _, q := range universe {
|
||||
stockCode, ok := bond2stock[q.Code]
|
||||
if !ok {
|
||||
// 无正股映射,允许通过(不因数据缺失误杀)。
|
||||
filtered = append(filtered, q)
|
||||
continue
|
||||
}
|
||||
fund, ok := fundamentals[stockCode]
|
||||
if !ok {
|
||||
// 无基本面数据,允许通过。
|
||||
filtered = append(filtered, q)
|
||||
continue
|
||||
}
|
||||
if f.isBlocked(fund) {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, q)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// isBlocked 判断正股基本面是否触发过滤条件。
|
||||
func (f *FundamentalFilter) isBlocked(fund *model.StockFundamental) bool {
|
||||
// ST / *ST 一律排除。
|
||||
if fund.Status >= 2 {
|
||||
return true
|
||||
}
|
||||
// 资产负债率超过阈值。
|
||||
if f.MaxDebtRatio > 0 && fund.DebtRatio > f.MaxDebtRatio {
|
||||
return true
|
||||
}
|
||||
// 净利润为负(连续亏损)。
|
||||
if fund.NetProfit < 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user