Initial commit: 可转债轮动分析系统

- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换)
- 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制)
- 增量同步:支持 -incr/-since 参数按存档日期增量入库
- 策略回测:净值曲线/10指标/基准对比,含基本面过滤
- 用户认证:用户名密码 + 邮箱验证码双登录方式
- 前端:三Tab SPA(实时行情/策略回测/数据管理)
This commit is contained in:
2026-08-17 20:23:22 +08:00
commit e28bfec957
45 changed files with 7889 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
// Package service 定义业务逻辑层,位于 MVC 架构中 Controller 与 Model 之间。
// 本层封装领域业务(行情、策略、回测),不感知 HTTP 与具体数据源实现。
package service
import (
"context"
"fmt"
"time"
"stock-analysis/internal/datasource"
"stock-analysis/internal/model"
)
// MarketService 提供行情查询业务。
type MarketService struct {
ds datasource.MarketDataSource
}
// NewMarketService 创建行情服务。
func NewMarketService(ds datasource.MarketDataSource) *MarketService {
return &MarketService{ds: ds}
}
// ListConvertibles 返回全量可转债实时行情(过滤已退市转债)。
func (s *MarketService) ListConvertibles(ctx context.Context) ([]model.ConvertibleBond, error) {
all, err := s.ds.FetchConvertibles(ctx)
if err != nil {
return nil, err
}
// 过滤掉已退市的转债:退市后东财接口返回的价格为 0。
var active []model.ConvertibleBond
for _, b := range all {
if b.Price > 0 {
active = append(active, b)
}
}
return active, nil
}
// GetDailyKline 返回指定可转债的日K线(自 beg 至今)。
func (s *MarketService) GetDailyKline(ctx context.Context, code, beg string) ([]model.Kline, error) {
// klt=101 为日K,beg/end 格式 20060102;end 取极大值表示拉取至今。
return s.ds.FetchKline(ctx, code, "101", beg, "20500101")
}
// FetchAllDailyKline 按年度窗口分批拉取指定标的的全量日K线。
// fromYear 为起始年份(如烽火转债 2020),到当前年份为止,保证单次请求数据量可控。
func (s *MarketService) FetchAllDailyKline(ctx context.Context, code string, fromYear int) ([]model.Kline, error) {
curYear := time.Now().Year()
var all []model.Kline
for year := fromYear; year <= curYear; year++ {
beg := fmt.Sprintf("%04d0101", year)
end := fmt.Sprintf("%04d1231", year)
ks, err := s.ds.FetchKline(ctx, code, "101", beg, end)
if err != nil {
return nil, fmt.Errorf("fetch kline %d: %w", year, err)
}
all = append(all, ks...)
}
return all, nil
}
// FetchFullKline 单次请求拉取指定标的自 fromYear 起的全量日K线。
// 适合批量入库场景:每只转债仅一次请求,大幅降低全市场抓取时的请求总量。
// 可转债存续期最长约 6 年(约 1500 根),单次请求即可返回完整历史。
func (s *MarketService) FetchFullKline(ctx context.Context, code string, fromYear int) ([]model.Kline, error) {
beg := fmt.Sprintf("%04d0101", fromYear)
return s.ds.FetchKline(ctx, code, "101", beg, "20500101")
}
// FetchIncrementalKline 增量拉取日K线:从 begDate 起到当前日期。
// begDate 格式为 YYYYMMDD(如 "20260801"),对应数据源 API 入参;end 取极大值表示拉取至今。
// 用于增量入库:配合 LastKlineDate 使用,避免重复拉取已存档数据。
func (s *MarketService) FetchIncrementalKline(ctx context.Context, code, begDate string) ([]model.Kline, error) {
return s.ds.FetchKline(ctx, code, "101", begDate, "20500101")
}