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

85
cmd/fetch/main.go Normal file
View File

@@ -0,0 +1,85 @@
// fetch 是数据拉取验证命令(CLI):
//
// go run ./cmd/fetch # 拉取全量可转债实时行情
// go run ./cmd/fetch -code 113667 # 拉取指定可转债历史K线
package main
import (
"context"
"flag"
"fmt"
"os"
"sort"
"stock-analysis/internal/datasource/eastmoney"
"stock-analysis/internal/datasource/fallback"
"stock-analysis/internal/datasource/tencent"
"stock-analysis/internal/service"
)
func main() {
code := flag.String("code", "", "可转债代码(如 113667),指定时拉取历史K线")
days := flag.Int("days", 30, "打印最近N根K线")
flag.Parse()
ds := fallback.New(eastmoney.NewClient(), tencent.NewClient())
svc := service.NewMarketService(ds)
ctx := context.Background()
if *code != "" {
if err := fetchKline(ctx, svc, *code, *days); err != nil {
fmt.Fprintln(os.Stderr, "kline error:", err)
os.Exit(1)
}
return
}
if err := fetchConvertibles(ctx, svc); err != nil {
fmt.Fprintln(os.Stderr, "list error:", err)
os.Exit(1)
}
}
// fetchConvertibles 拉取并打印全量可转债。
func fetchConvertibles(ctx context.Context, svc *service.MarketService) error {
bonds, err := svc.ListConvertibles(ctx)
if err != nil {
return err
}
fmt.Printf("共拉取可转债 %d 只\n\n", len(bonds))
// 按溢价率升序展示前 15 只(低溢价候选池雏形)。
sort.Slice(bonds, func(i, j int) bool { return bonds[i].PremiumRate < bonds[j].PremiumRate })
fmt.Printf("%-8s %-10s %8s %7s %8s %8s %8s %8s %8s\n",
"代码", "名称", "价格", "涨跌%", "溢价率%", "转股价值", "转股价", "纯债价值", "正股")
for i, b := range bonds {
if i >= 15 {
break
}
fmt.Printf("%-8s %-10s %8.3f %7.2f %8.2f %8.2f %8.2f %8.2f %s\n",
b.Code, b.Name, b.Price, b.ChangePct, b.PremiumRate,
b.ConvertValue, b.ConvertPrice, b.PureBondVal, b.StockName)
}
fmt.Println("\n... (仅展示前15只,按溢价率升序)")
return nil
}
// fetchKline 拉取并打印指定可转债的历史K线。
func fetchKline(ctx context.Context, svc *service.MarketService, code string, days int) error {
klines, err := svc.GetDailyKline(ctx, code, "20200101")
if err != nil {
return err
}
fmt.Printf("代码 %s 共 %d 根日K(%s ~ %s)\n\n",
code, len(klines), klines[0].Date, klines[len(klines)-1].Date)
start := len(klines) - days
if start < 0 {
start = 0
}
fmt.Printf("%-12s %8s %8s %8s %8s %12s\n", "日期", "开盘", "收盘", "最高", "最低", "成交量")
for _, k := range klines[start:] {
fmt.Printf("%-12s %8.3f %8.3f %8.3f %8.3f %12d\n",
k.Date, k.Open, k.Close, k.High, k.Low, k.Volume)
}
return nil
}