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

77 lines
3.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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")
}