- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换) - 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制) - 增量同步:支持 -incr/-since 参数按存档日期增量入库 - 策略回测:净值曲线/10指标/基准对比,含基本面过滤 - 用户认证:用户名密码 + 邮箱验证码双登录方式 - 前端:三Tab SPA(实时行情/策略回测/数据管理)
77 lines
3.0 KiB
Go
77 lines
3.0 KiB
Go
// 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")
|
||
}
|