Browse Source

sig admin api

main
strange 8 months ago
parent
commit
b85cedd6cd
  1. 25
      api/pub.proto
  2. 73
      api/trading.proto
  3. 5
      internal/sig/args/backtest_args.go
  4. 51
      internal/sig/repoitory/backtest_repository.go
  5. 17
      internal/sig/repoitory/market_repository.go
  6. 42
      internal/sig/service/backtest_service.go
  7. 21
      internal/sig/service/market_service.go
  8. 8
      internal/sig/sig_server.go
  9. 63
      internal/sig/trade_instance/trade_instance_api.go
  10. 4
      internal/trading/backtest/trading_plan_backtester.go
  11. 32
      internal/trading/backtest/types.go
  12. 5
      internal/trading/trading_data_persist.go
  13. 10
      internal/trading/trading_grpc_server.go
  14. 5
      internal/trading/trading_service.go
  15. 37
      pkg/data/common.go
  16. 54
      pkg/trade/types.go

25
api/pub.proto

@ -181,3 +181,28 @@ message InputRange {
int32 type = 2; // 0.fixed, 1.range, 2.enum, 3.simple group
string value = 3; // range => [10,20,1](min,max,step); enum => [1,2,3,4,5]; simple group => [["2025-10-01","2025-12-31"],["2025-01-01","2025-12-31"]]
}
//
message BacktestTradeOrder {
int64 backtest_id = 1;
int64 trade_id = 2;
int32 trade_type = 3;
string inst_id = 4;
Side side = 5;
double qty = 6;
double price = 7;
double fee = 8;
int32 leverage = 9;
int64 ctime = 10;
int32 status = 11;
double peak_px = 12;
string close_cause = 13;
double equity = 14;
string hold_time = 15;
double profit = 16;
double last_px = 17;
double entry_px = 18;
double entry_fee = 19;
int64 entry_time = 20;
repeated int64 trades = 21;
}

73
api/trading.proto

@ -22,14 +22,8 @@ service TradingService {
//
rpc BacktestRace(ReqBacktestRace) returns (RspBacktestRace);
//
// ()
rpc BacktestLog(ReqBacktestLog) returns (RspBacktestLog);
//
rpc BacktestLogTrades(ReqBacktestLogTrades) returns (RspBacktestLogTrades);
//
rpc BacktestLogStats(ReqBacktestLogStats) returns (RspBacktestLogStats);
}
message ReqIndicatorPlots {
@ -87,38 +81,7 @@ message ReqBacktestLog {
message RspBacktestLog {
repeated BacktestLog logs = 1;
}
message BacktestTradeOrder {
int64 backtest_id = 1;
int64 trade_id = 2;
int32 trade_type = 3;
string inst_id = 4;
Side side = 5;
double qty = 6;
double price = 7;
double fee = 8;
int32 leverage = 9;
int64 ctime = 10;
int32 status = 11;
double peak_px = 12;
string close_cause = 13;
double equity = 14;
string hold_time = 15;
double profit = 16;
double last_px = 17;
double entry_px = 18;
double entry_fee = 19;
int64 entry_time = 20;
repeated int64 trades = 21;
}
message ReqBacktestLogTrades {
Paging paging = 1;
int64 backtest_id = 2;
}
message RspBacktestLogTrades {
repeated BacktestTradeOrder trades = 1;
}
message ReqBacktestRace {
int64 plan_id = 1; //
repeated InputRange series_input_range = 5; //
@ -131,19 +94,19 @@ message RspBacktestRace {
}
// (line charts)
message ReqBacktestLogStats {
int64 plan_id = 1;
int64 backtest_id = 2;
}
message RspBacktestLogStats {
repeated int64 times = 1; // k线时间
repeated double equitys = 2; //
repeated BacktestLogBuySellPoint buy_sell = 9; // charts data
}
// {time: 1763890200000, value: 86400, text: 'BUY:86400', direction: 'up'}
message BacktestLogBuySellPoint {
double value = 2; // k线收盘价
Side side = 3; //
string text = 4; //
}
// // (line charts)
// message ReqBacktestLogStats {
// int64 plan_id = 1;
// int64 backtest_id = 2;
// }
// message RspBacktestLogStats {
// repeated int64 times = 1; // k线时间
// repeated double equitys = 2; //
// repeated BacktestLogBuySellPoint buy_sell = 9; // charts data
// }
// // {time: 1763890200000, value: 86400, text: 'BUY:86400', direction: 'up'}
// message BacktestLogBuySellPoint {
// double value = 2; // k线收盘价
// Side side = 3; //
// string text = 4; //
// }

5
internal/sig/args/backtest_args.go

@ -0,0 +1,5 @@
package args
type ListBacktestLogTradesReq struct {
BacktestId int64 `json:"backtestId"`
}

51
internal/sig/repoitory/backtest_repository.go

@ -1,17 +1,58 @@
package repository
import "sig-pub/pkg/storage/persist"
import (
"fmt"
"sig-pub/pkg/data"
"sig-pub/pkg/storage/persist"
"sig-pub/pkg/trade"
)
type BacktestRepository struct {
rdb *persist.DB
db *persist.DB
}
func NewBacktestRepository(rdb *persist.DB) *BacktestRepository {
func NewBacktestRepository(db *persist.DB) *BacktestRepository {
return &BacktestRepository{
rdb: rdb,
db: db,
}
}
func (r *BacktestRepository) ListLog() {
// ListBacktestLogs 用户交易计划回测记录查询
func (p *BacktestRepository) ListBacktestLogs(userId int64, page data.Page) (backtestLogs []*trade.BacktestTradingPlan, err error) {
err = p.db.Select(&backtestLogs, `
select * from t_backtest_trading_plan where user_id = ?
order by id desc
offset ? limit ?
`, userId, page.Offset, page.Limit)
return
}
// BacktestLogTrades 交易计划回测交易单详情
func (p *BacktestRepository) BacktestLogTrades(userId, backtestId int64, page data.Page) (total int, tradeOrders []*trade.TradeOrder, err error) {
sqlFrom := `
from t_backtest_trading_order
where backtest_id = (select id from t_backtest_trading_plan where id = ? and user_id = ?)
`
err = p.db.Select(&total, fmt.Sprintf(`
select count(*) %s
`, sqlFrom), backtestId, userId)
if err != nil || total == 0 {
return
}
err = p.db.Select(&tradeOrders, fmt.Sprintf(`
select * %s
order by trade_id asc
offset ? limit ?
`, sqlFrom), backtestId, userId, page.Offset, page.Limit)
return
}
// BacktestLogStats 交易计划回测结果统计信息
func (p *BacktestRepository) BacktestLogStats(userId, backtestId int64) (stats *trade.BacktestTradingPlan, err error) {
stats = &trade.BacktestTradingPlan{}
err = p.db.Select(stats, `
select * from t_backtest_trading_plan_stats
where backtest_id = (select id from t_backtest_trading_plan where id = ? and user_id = ?)
`, backtestId, userId)
return
}

17
internal/sig/repoitory/market_repository.go

@ -0,0 +1,17 @@
package repository
import "sig-pub/pkg/storage/persist"
type MarketRepository struct {
db *persist.DB
}
func NewMarketRepository(db *persist.DB) *MarketRepository {
return &MarketRepository{
db: db,
}
}
func (r *MarketRepository) List() {
}

42
internal/sig/service/backtest_service.go

@ -2,7 +2,9 @@ package service
import (
"net/http"
"sig-pub/internal/sig/args"
repository "sig-pub/internal/sig/repoitory"
"sig-pub/pkg/data"
"sig-pub/pkg/resp"
"github.com/gin-gonic/gin"
@ -19,12 +21,40 @@ func NewBacktestService(repo *repository.BacktestRepository) *BacktestService {
}
func (svc *BacktestService) Route(group *gin.RouterGroup) {
group.GET("hello", svc.listBacktestLog)
group.GET("listBacktestLog", svc.ListBacktestLog) // 回测记录
group.POST("listBacktestLogTrades", svc.ListBacktestLogTrades) // 回测记录订单详情
}
func (svc *BacktestService) listBacktestLog(ctx *gin.Context) {
name := ctx.Query("name")
ctx.JSON(http.StatusOK, resp.H{
"text": "ojbk: " + name,
})
func (svc *BacktestService) ListBacktestLog(ctx *gin.Context) {
page := data.PageArgs(ctx)
logs, err := svc.repo.ListBacktestLogs(10001, page)
if err != nil {
ctx.JSON(http.StatusInternalServerError, resp.Error(err.Error()))
return
}
ctx.JSON(http.StatusOK, resp.Success(resp.H{
"total": len(logs),
"logs": logs,
}))
}
func (svc *BacktestService) ListBacktestLogTrades(ctx *gin.Context) {
page := data.PageArgs(ctx)
arg := new(args.ListBacktestLogTradesReq)
if err := ctx.ShouldBindJSON(arg); err != nil {
ctx.JSON(http.StatusBadRequest, resp.Fail(err.Error()))
return
}
total, trades, err := svc.repo.BacktestLogTrades(10001, arg.BacktestId, page)
if err != nil {
ctx.JSON(http.StatusInternalServerError, resp.Error(err.Error()))
return
}
ctx.JSON(http.StatusOK, resp.Success(resp.H{
"total": total,
"trades": trades,
}))
}

21
internal/sig/service/market_service.go

@ -0,0 +1,21 @@
package service
import (
repository "sig-pub/internal/sig/repoitory"
"github.com/gin-gonic/gin"
)
type MarketService struct {
repo *repository.MarketRepository
}
func NewMarketService(repo *repository.MarketRepository) *MarketService {
return &MarketService{
repo: repo,
}
}
func (svc *MarketService) Route(group *gin.RouterGroup) {
}

8
internal/sig/sig_server.go

@ -12,13 +12,13 @@ import (
)
type SigServer struct {
rdb *persist.DB
db *persist.DB
engine *gin.Engine
}
func NewSigServer(rdb *persist.DB) *SigServer {
func NewSigServer(db *persist.DB) *SigServer {
return &SigServer{
rdb: rdb,
db: db,
}
}
@ -43,7 +43,7 @@ func (s *SigServer) initGinServer() {
})
routerGroup := s.engine.Group("/api/sig")
service.Init(routerGroup, s.rdb)
service.Init(routerGroup, s.db)
}
func (s *SigServer) Run(addr string) (err error) {

63
internal/sig/trade_instance/trade_instance_api.go

@ -1,63 +0,0 @@
package inst
import (
"net/http"
"sig-pub/internal/market"
"sig-pub/pkg/data/args"
"sig-pub/pkg/resp"
"github.com/gin-gonic/gin"
)
type TradeInstanceApi struct {
service *market.TradeInstanceService // todo grpc call
}
func NewTradeInstanceApi() *TradeInstanceApi {
return &TradeInstanceApi{}
}
func (a *TradeInstanceApi) InitRoute(r *gin.RouterGroup) {
instGroup := r.Group("/inst")
{
instGroup.GET("/one/:inst_id", a.getInst)
instGroup.GET("/listAll", a.listAllInst)
instGroup.POST("/list", a.listInst)
}
}
// getInst 获取指定交易产品
func (a *TradeInstanceApi) getInst(c *gin.Context) {
instId := c.Param("inst_id")
inst, err := a.service.GetInstance(instId)
if err != nil {
c.JSON(http.StatusOK, resp.Error(err.Error()))
return
}
c.JSON(http.StatusOK, resp.Success(inst))
}
// listAllInst 获取所有交易产品
func (a *TradeInstanceApi) listAllInst(c *gin.Context) {
// list, err := a.service.ListAllInstance()
// if err != nil {
// c.JSON(http.StatusOK, resp.Error(err.Error()))
// return
// }
// c.JSON(http.StatusOK, resp.Success(list))
}
// listInst 交易产品分页查询
func (a *TradeInstanceApi) listInst(c *gin.Context) {
pageArg := args.ParsePageArgs(c)
total, list, err := a.service.PageInstance(pageArg.Page, pageArg.Size, "")
if err != nil {
c.JSON(http.StatusOK, resp.Error(err.Error()))
return
}
c.JSON(http.StatusOK, resp.Success(resp.H{
"total": total,
"data": list,
}))
}

4
internal/trading/backtest/trading_plan_backtester.go

@ -94,8 +94,8 @@ func (b *TradingPlanBacktester) Init(cash float64, plan entity.TradePlan, sr *pb
}
// 核心引擎,模拟交易、持仓跟踪、费用计算
func (b *TradingPlanBacktester) Backtest(ctx context.Context) (test *BacktestTradingPlan, err error) {
test = &BacktestTradingPlan{
func (b *TradingPlanBacktester) Backtest(ctx context.Context) (test *trade.BacktestTradingPlan, err error) {
test = &trade.BacktestTradingPlan{
Id: time.Now().Unix(),
UserId: 10001,
PlanId: b.plan.Id,

32
internal/trading/backtest/types.go

@ -1,7 +1,6 @@
package backtest
import (
"sig-pub/api/pb"
"sig-pub/pkg/trade"
)
@ -80,34 +79,3 @@ type EquitySnapshot struct {
Ts int64
Equity float64
}
// BacktestTradingPlan 交易计划回测结果
type BacktestTradingPlan struct {
Id int64 `json:"id" gorm:"column:id;primaryKey"` // 测试id
UserId int64 `json:"userId" gorm:"column:user_id"` // 用户id
PlanId int64 `json:"planId" gorm:"column:plan_id"` // 交易计划id
InstId string `json:"instId" gorm:"column:inst_id"` // 交易产品id
Exchange pb.ExchangeType `json:"exchange" gorm:"column:exchange"` // 交易所
Interval string `json:"interval" gorm:"column:interval"` // 交易周期
SeriesBefore int64 `json:"seriesBefore" gorm:"column:series_before"` // 回测周期开始时间
SeriesAfter int64 `json:"seriesAfter" gorm:"column:series_after"` // 回测周期结束时间
Ctime int64 `json:"ctime" gorm:"column:ctime"` // 创建时间(测试时间)
Etime int64 `json:"etime" gorm:"column:etime"` // 测试结束时间
Cash float64 `json:"cash" gorm:"column:cash"` // 起始金额
EndCash float64 `json:"endCash" gorm:"column:end_cash"` // 结束金额
Profit float64 `json:"profit" gorm:"column:profit"` // 利润
Singals int `json:"singals" gorm:"column:singals"` // 交易信号数
TotalTrades int `json:"totalTrades" gorm:"column:total_trades"` // 总单数
WinningTrades int `json:"winningTrades" gorm:"column:winning_trades"` // 盈利单数
LosingTrades int `json:"losingTrades" gorm:"column:losing_trades"` // 亏损单数
Fee float64 `json:"fee" gorm:"column:fee"` // 总手续费
MaxDrawdown float64 `json:"maxDrawdown" gorm:"column:max_drawdown"` // 最大回撤
SharpeRatio float64 `json:"sharpeRatio" gorm:"column:sharpe_ratio"` // 夏普比率
Trades []*trade.TradeOrder `json:"-" gorm:"-"` // 回测交易单
}
func (BacktestTradingPlan) TableName() string {
return "t_backtest_trading_plan"
}
// 参数迭代

5
internal/trading/trading_data_persist.go

@ -2,7 +2,6 @@ package trading
import (
"context"
"sig-pub/internal/trading/backtest"
"sig-pub/pkg/data"
"sig-pub/pkg/data/entity"
"sig-pub/pkg/storage/ck"
@ -49,7 +48,7 @@ func (p *TradingDataPersist) GetTradePlanById(planId int64) (plan *entity.TradeP
}
// SaveBacktestTradingPlan 保存交易计划回测结果
func (p *TradingDataPersist) SaveBacktestTradingPlan(ctx context.Context, backtestTradingPlan *backtest.BacktestTradingPlan) (err error) {
func (p *TradingDataPersist) SaveBacktestTradingPlan(ctx context.Context, backtestTradingPlan *trade.BacktestTradingPlan) (err error) {
datas := make([]any, 0, len(backtestTradingPlan.Trades)+1)
datas = append(datas, backtestTradingPlan)
for _, trade := range backtestTradingPlan.Trades {
@ -61,7 +60,7 @@ func (p *TradingDataPersist) SaveBacktestTradingPlan(ctx context.Context, backte
}
// ListBacktestLogs 用户交易计划回测记录查询
func (p *TradingDataPersist) ListBacktestLogs(userId int64) (backtestLogs []*backtest.BacktestTradingPlan, err error) {
func (p *TradingDataPersist) ListBacktestLogs(userId int64) (backtestLogs []*trade.BacktestTradingPlan, err error) {
err = p.db.Select(&backtestLogs, `
select * from t_backtest_trading_plan where user_id = ? order by id desc
`, userId)

10
internal/trading/trading_grpc_server.go

@ -132,13 +132,3 @@ func (svr *TradingGrpcServer) BacktestRace(ctx context.Context, req *pb.ReqBackt
})
return
}
func (svr *TradingGrpcServer) BacktestLogTrades(ctx context.Context, req *pb.ReqBacktestLogTrades) (rsp *pb.RspBacktestLogTrades, err error) {
return
}
func (svr *TradingGrpcServer) BacktestLogStats(ctx context.Context, req *pb.ReqBacktestLogStats) (rsp *pb.RspBacktestLogStats, err error) {
return
}

5
internal/trading/trading_service.go

@ -12,6 +12,7 @@ import (
"sig-pub/pkg/indicator"
"sig-pub/pkg/publish"
"sig-pub/pkg/strategy"
"sig-pub/pkg/trade"
"sig-pub/pkg/types"
"sig-pub/pkg/utils/collect"
"sig-pub/pkg/utils/lang"
@ -362,7 +363,7 @@ func (svc *TradingService) Backtest(ctx context.Context, planId, stime, etime in
}
// BacktestLog 回测记录查询
func (svc *TradingService) BacktestLog(ctx context.Context, userId int64) (backtestLogs []*backtest.BacktestTradingPlan, err error) {
func (svc *TradingService) BacktestLog(ctx context.Context, userId int64) (backtestLogs []*trade.BacktestTradingPlan, err error) {
// todo userid from ctx
backtestLogs, err = svc.tradingDataPersist.ListBacktestLogs(userId)
return
@ -449,7 +450,7 @@ func (svc *TradingService) BacktestRace(ctx context.Context, req *pb.ReqBacktest
}
}
}
var results []*backtest.BacktestTradingPlan
var results []*trade.BacktestTradingPlan
for _, backtester := range backtesters {
r, e := backtester.Backtest(ctx)
if e != nil {

37
pkg/data/common.go

@ -1,6 +1,11 @@
package data
import "errors"
import (
"errors"
"strconv"
"github.com/gin-gonic/gin"
)
// 状态枚举
type Status int32
@ -30,3 +35,33 @@ func PageCalc(page, size int) (offset, limit int) {
offset = (page - 1) * size
return offset, size
}
type Page struct {
Page, PageSize int
SortBy string
Asc bool
Offset, Limit int
}
func PageArgs(ctx *gin.Context) Page {
page, _ := strconv.Atoi(ctx.Query("page"))
pageSize, _ := strconv.Atoi(ctx.Query("pageSize"))
sortBy := ctx.Query("sortBy")
asc, _ := strconv.Atoi(ctx.Query("asc"))
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 1000 {
pageSize = 20
}
offset, limit := PageCalc(page, pageSize)
return Page{
Page: page,
PageSize: pageSize,
SortBy: sortBy,
Asc: asc == 1,
Offset: offset,
Limit: limit,
}
}

54
pkg/trade/types.go

@ -1,6 +1,7 @@
package trade
import (
"sig-pub/api/pb"
"sig-pub/pkg/data"
"sig-pub/pkg/types"
@ -54,20 +55,49 @@ type Position struct {
LastPx float64 // 最后更新价格
}
// BacktestTradingPlan 交易计划回测结果
type BacktestTradingPlan struct {
Id int64 `json:"id" gorm:"column:id;primaryKey"` // 测试id
UserId int64 `json:"userId" gorm:"column:user_id"` // 用户id
PlanId int64 `json:"planId" gorm:"column:plan_id"` // 交易计划id
InstId string `json:"instId" gorm:"column:inst_id"` // 交易产品id
Exchange pb.ExchangeType `json:"exchange" gorm:"column:exchange"` // 交易所
Interval string `json:"interval" gorm:"column:interval"` // 交易周期
SeriesBefore int64 `json:"seriesBefore" gorm:"column:series_before"` // 回测周期开始时间
SeriesAfter int64 `json:"seriesAfter" gorm:"column:series_after"` // 回测周期结束时间
Ctime int64 `json:"ctime" gorm:"column:ctime"` // 创建时间(测试时间)
Etime int64 `json:"etime" gorm:"column:etime"` // 测试结束时间
Cash float64 `json:"cash" gorm:"column:cash"` // 起始金额
EndCash float64 `json:"endCash" gorm:"column:end_cash"` // 结束金额
Profit float64 `json:"profit" gorm:"column:profit"` // 利润
Singals int `json:"singals" gorm:"column:singals"` // 交易信号数
TotalTrades int `json:"totalTrades" gorm:"column:total_trades"` // 总单数
WinningTrades int `json:"winningTrades" gorm:"column:winning_trades"` // 盈利单数
LosingTrades int `json:"losingTrades" gorm:"column:losing_trades"` // 亏损单数
Fee float64 `json:"fee" gorm:"column:fee"` // 总手续费
MaxDrawdown float64 `json:"maxDrawdown" gorm:"column:max_drawdown"` // 最大回撤
SharpeRatio float64 `json:"sharpeRatio" gorm:"column:sharpe_ratio"` // 夏普比率
Trades []*TradeOrder `json:"-" gorm:"-"` // 回测交易单
}
func (BacktestTradingPlan) TableName() string {
return "t_backtest_trading_plan"
}
// TradeOrder 交易订单
type TradeOrder struct {
BacktestId int64 `jsno:"backtestId" gorm:"column:backtest_id"` // 回测单id
TradeId int64 `jsno:"tradeId" gorm:"column:trade_id"` // 交易单id
TradeType TradeType `jsno:"tradeType" gorm:"column:trade_type"` // 交易类型: 1.开仓 2.平仓
InstId string `jsno:"instId" gorm:"column:inst_id"` // 交易产品id
Side types.Side `jsno:"side" gorm:"column:side"` // 交易方向
Qty decimal.Decimal `jsno:"qty" gorm:"column:qty"` // 交易量
Price float64 `jsno:"price" gorm:"column:price"` // 交易价格
Fee float64 `jsno:"fee" gorm:"column:fee"` // 手续费
Leverage int32 `jsno:"leverage" gorm:"column:leverage"` // 杠杆倍数
Ctime int64 `jsno:"ctime" gorm:"column:ctime"` // 交易时间
Status data.Status `jsno:"status" gorm:"column:status"` // 1.交易成功 2.交易中 4.交易失败
PeakPx float64 `jsno:"peakPx" gorm:"column:peak_px"` // 持仓最高价格(空单最低价格)
BacktestId int64 `json:"backtestId" gorm:"column:backtest_id"` // 回测单id
TradeId int64 `json:"tradeId" gorm:"column:trade_id"` // 交易单id
TradeType TradeType `json:"tradeType" gorm:"column:trade_type"` // 交易类型: 1.开仓 2.平仓
InstId string `json:"instId" gorm:"column:inst_id"` // 交易产品id
Side types.Side `json:"side" gorm:"column:side"` // 交易方向
Qty decimal.Decimal `json:"qty" gorm:"column:qty"` // 交易量
Price float64 `json:"price" gorm:"column:price"` // 交易价格
Fee float64 `json:"fee" gorm:"column:fee"` // 手续费
Leverage int32 `json:"leverage" gorm:"column:leverage"` // 杠杆倍数
Ctime int64 `json:"ctime" gorm:"column:ctime"` // 交易时间
Status data.Status `json:"status" gorm:"column:status"` // 1.交易成功 2.交易中 4.交易失败
PeakPx float64 `json:"peakPx" gorm:"column:peak_px"` // 持仓最高价格(空单最低价格)
// ----------------- 平仓单信息
CloseCause Cause `json:"closeCause" gorm:"column:close_cause"` // 平仓原因 ["stoploss", "takeprofit", "trailing", "retrace", "signal"](“止损”、“止盈”、“动态跟踪”、“回撤”、“信号”)
Equity float64 `json:"equity" gorm:"column:equity"` // 平仓后账户净值

Loading…
Cancel
Save