Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换) - 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制) - 增量同步:支持 -incr/-since 参数按存档日期增量入库 - 策略回测:净值曲线/10指标/基准对比,含基本面过滤 - 用户认证:用户名密码 + 邮箱验证码双登录方式 - 前端:三Tab SPA(实时行情/策略回测/数据管理)
This commit is contained in:
76
internal/service/market.go
Normal file
76
internal/service/market.go
Normal 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")
|
||||
}
|
||||
Reference in New Issue
Block a user