22 changed files with 423 additions and 266 deletions
@ -0,0 +1,10 @@
|
||||
FROM ubuntu:24.10 |
||||
ARG APP |
||||
ENV LANG=C.UTF-8 |
||||
WORKDIR /opt |
||||
COPY localtime /etc/localtime |
||||
COPY target/${APP} /opt/app |
||||
COPY config/*.toml /opt/config/ |
||||
COPY config/production/*.toml /opt/config/ |
||||
RUN chmod +x /opt/app |
||||
CMD ["./app"] |
||||
@ -0,0 +1,7 @@
|
||||
#! /bin/bash |
||||
|
||||
./build.sh market 1.0 \ |
||||
&& ./build.sh exchange 1.0 \ |
||||
&& ./build.sh gateway 1.0 \ |
||||
&& ./build.sh admin 1.0 \ |
||||
&& ./build.sh trading 1.0 |
||||
@ -0,0 +1,21 @@
|
||||
#! /bin/bash |
||||
|
||||
# ./build.sh exchange 1.0 |
||||
|
||||
APP=$1 |
||||
DOCKER_IMAGE_VERSION=$2 |
||||
GO111MODULE=on |
||||
CGO_ENABLED=0 |
||||
GOOS=linux |
||||
GOARCH=amd64 |
||||
|
||||
go build -o target/$APP cmd/$APP/main.go |
||||
|
||||
if [ -n "$DOCKER_IMAGE_VERSION" ]; then |
||||
echo "docker build -t sig-$APP:$DOCKER_IMAGE_VERSION --build-arg APP=$APP ." |
||||
cp -f /usr/share/zoneinfo/Asia/Shanghai ./localtime |
||||
docker build -t sig-$APP:$DOCKER_IMAGE_VERSION --build-arg APP=$APP . |
||||
rm -f localtime |
||||
fi |
||||
|
||||
echo "$APP build finished" |
||||
@ -0,0 +1,40 @@
|
||||
networks: |
||||
sig-network: |
||||
driver: bridge |
||||
|
||||
services: |
||||
sig-gateway: |
||||
image: 'sig-gateway:1.0' |
||||
container_name: sig-gateway |
||||
hostname: sig-gateway |
||||
networks: |
||||
- sig-network |
||||
ports: |
||||
- 7001:7001 |
||||
- 7101:7101 |
||||
sig-market: |
||||
image: 'sig-market:1.0' |
||||
container_name: sig-market |
||||
hostname: sig-market |
||||
networks: |
||||
- sig-network |
||||
# restart: always |
||||
sig-exchange: |
||||
image: 'sig-exchange:1.0' |
||||
container_name: sig-exchange |
||||
hostname: sig-exchange |
||||
network_mode: host |
||||
# networks: |
||||
# - sig-network |
||||
sig-trading: |
||||
image: 'sig-trading:1.0' |
||||
container_name: sig-trading |
||||
hostname: sig-trading |
||||
networks: |
||||
- sig-network |
||||
sig-admin: |
||||
image: 'sig-admin:1.0' |
||||
container_name: sig-admin |
||||
hostname: sig-admin |
||||
networks: |
||||
- sig-network |
||||
@ -0,0 +1,108 @@
|
||||
package repository |
||||
|
||||
import ( |
||||
"fmt" |
||||
"math" |
||||
"sig-pub/pkg/data" |
||||
"sig-pub/pkg/storage/persist" |
||||
"sig-pub/pkg/trade" |
||||
"sig-pub/pkg/utils/conver" |
||||
) |
||||
|
||||
type BacktestRepository struct { |
||||
db *persist.DB |
||||
} |
||||
|
||||
func NewBacktestRepository(db *persist.DB) *BacktestRepository { |
||||
return &BacktestRepository{ |
||||
db: db, |
||||
} |
||||
} |
||||
|
||||
// GetBacktest 用户交易计划回测详情
|
||||
func (p *BacktestRepository) GetBacktest(userId int64, backtestId int64) (test *trade.BacktestTradingPlan, err error) { |
||||
test = new(trade.BacktestTradingPlan) |
||||
err = p.db.Select(&test, ` |
||||
select * from t_backtest_trading_plan where id = ? and user_id = ? |
||||
`, backtestId, userId) |
||||
return |
||||
} |
||||
|
||||
// ListBacktest 用户交易计划回测记录查询
|
||||
func (p *BacktestRepository) ListBacktest(userId int64, page data.Page) (total int64, backtestLogs []*trade.BacktestTradingPlan, err error) { |
||||
sqlFrom := ` |
||||
from t_backtest_trading_plan where user_id = ? |
||||
` |
||||
err = p.db.Select(&total, fmt.Sprintf(` |
||||
select count(*) %s |
||||
`, sqlFrom), userId) |
||||
if err != nil || total == 0 { |
||||
return |
||||
} |
||||
err = p.db.Select(&backtestLogs, fmt.Sprintf(` |
||||
select * %s |
||||
order by id desc |
||||
offset ? limit ? |
||||
`, sqlFrom), userId, page.Offset, page.Limit) |
||||
return |
||||
} |
||||
|
||||
// ListBacktestTrades 交易计划回测交易单详情
|
||||
func (p *BacktestRepository) ListBacktestTrades(userId, backtestId int64, page data.Page) (total int64, 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 |
||||
} |
||||
|
||||
// BacktestEquities 回测记录资金曲线
|
||||
func (p *BacktestRepository) BacktestEquities(userId, backtestId int64) (times []int64, equities []float64, tradeIds []string, err error) { |
||||
var datas []map[string]any |
||||
err = p.db.Select(&datas, ` |
||||
select trade_id, ctime, equity from t_backtest_trading_order |
||||
where backtest_id = (select id from t_backtest_trading_plan where id = ? and user_id = ?) and trade_type = 2 |
||||
order by ctime, trade_id asc |
||||
`, backtestId, userId) |
||||
if err != nil { |
||||
return |
||||
} |
||||
pow := math.Pow10(2) |
||||
for _, data := range datas { |
||||
trade_id := conver.ToInt64(data["trade_id"]) |
||||
ctime := conver.ToInt64(data["ctime"]) |
||||
equity := conver.ToFloat64(data["equity"]) |
||||
equity = math.Round(equity*pow) / pow |
||||
// 同一时间两笔单
|
||||
if length := len(times); length > 0 && times[length-1] == ctime { |
||||
equities[length-1] = equity |
||||
tradeIds[length-1] += fmt.Sprintf(";%d", trade_id) |
||||
} else { |
||||
times = append(times, ctime) |
||||
equities = append(equities, equity) |
||||
tradeIds = append(tradeIds, fmt.Sprintf("%d", trade_id)) |
||||
} |
||||
} |
||||
return |
||||
} |
||||
@ -0,0 +1,7 @@
|
||||
package repository |
||||
|
||||
type BacktestEquity struct { |
||||
Time int64 `json:"time"` |
||||
Value float64 `json:"value"` |
||||
TradeId string `json:"tradeId"` |
||||
} |
||||
@ -0,0 +1,55 @@
|
||||
package repository |
||||
|
||||
import ( |
||||
"sig-pub/pkg/data" |
||||
"sig-pub/pkg/data/entity" |
||||
"sig-pub/pkg/storage/persist" |
||||
) |
||||
|
||||
type MarketRepository struct { |
||||
db *persist.DB |
||||
} |
||||
|
||||
func NewMarketRepository(db *persist.DB) *MarketRepository { |
||||
return &MarketRepository{ |
||||
db: db, |
||||
} |
||||
} |
||||
|
||||
func (s *MarketRepository) ListAllInstanceId() (insts []string, err error) { |
||||
err = s.db.Select(&insts, `select inst_id from t_trade_instance where status != ?`, data.StatusDeleted) |
||||
return |
||||
} |
||||
|
||||
func (s *MarketRepository) ListAllInstance() (insts []*entity.TradeInstance, err error) { |
||||
err = s.db.Select(&insts, `select * from t_trade_instance where status != ?`, data.StatusDeleted) |
||||
if err != nil { |
||||
return |
||||
} |
||||
err = s.AttachInstExchanges(insts) |
||||
return |
||||
} |
||||
|
||||
func (s *MarketRepository) AttachInstExchanges(insts []*entity.TradeInstance) (err error) { |
||||
// 交易产品交易所
|
||||
var instIds []string |
||||
var instMap = make(map[string]*entity.TradeInstance, len(insts)) |
||||
for _, inst := range insts { |
||||
instIds = append(instIds, inst.InstId) |
||||
instMap[inst.InstId] = inst |
||||
} |
||||
|
||||
var instExchanges []*entity.TradeInstanceExchange |
||||
err = s.db.Select(&instExchanges, ` |
||||
select * from t_trade_instance_exchange where inst_id in ? and status != ? order by inst_id, exchange |
||||
`, instIds, data.StatusDeleted) |
||||
if err != nil { |
||||
return |
||||
} |
||||
for _, instEx := range instExchanges { |
||||
if inst, ok := instMap[instEx.InstId]; ok { |
||||
inst.Exchanges = append(inst.Exchanges, instEx) |
||||
} |
||||
} |
||||
return |
||||
} |
||||
@ -0,0 +1,100 @@
|
||||
package service |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net/http" |
||||
"sig-pub/internal/admin/args" |
||||
repository "sig-pub/internal/admin/repoitory" |
||||
"sig-pub/pkg/data" |
||||
"sig-pub/pkg/resp" |
||||
|
||||
"github.com/gin-gonic/gin" |
||||
"github.com/spf13/cast" |
||||
) |
||||
|
||||
type BacktestService struct { |
||||
repo *repository.BacktestRepository |
||||
} |
||||
|
||||
func NewBacktestService(repo *repository.BacktestRepository) *BacktestService { |
||||
return &BacktestService{ |
||||
repo: repo, |
||||
} |
||||
} |
||||
|
||||
func (svc *BacktestService) Route(group *gin.RouterGroup) { |
||||
group.POST("listBacktest", svc.ListBacktest) // 回测记录分页
|
||||
group.GET("getBacktest", svc.GetBacktest) // 回测记录详情
|
||||
group.POST("listBacktestTrades", svc.ListBacktestTrades) // 回测记录交易订单分页
|
||||
group.GET("testEquities", svc.TestEquities) // 回测记录资金曲线
|
||||
} |
||||
|
||||
func (svc *BacktestService) ListBacktest(ctx *gin.Context) { |
||||
page := data.PageArgs(ctx) |
||||
total, tests, err := svc.repo.ListBacktest(10001, page) |
||||
if err != nil { |
||||
ctx.JSON(http.StatusInternalServerError, resp.Error(err.Error())) |
||||
return |
||||
} |
||||
ctx.JSON(http.StatusOK, resp.Success(resp.H{ |
||||
"total": total, |
||||
"tests": tests, |
||||
})) |
||||
} |
||||
|
||||
func (svc *BacktestService) GetBacktest(ctx *gin.Context) { |
||||
backtestId, err := cast.ToInt64E(ctx.Query("backtestId")) |
||||
if backtestId == 0 || err != nil { |
||||
ctx.JSON(http.StatusBadRequest, resp.Error("param backtestId format error")) |
||||
return |
||||
} |
||||
test, err := svc.repo.GetBacktest(10001, backtestId) |
||||
if err != nil { |
||||
ctx.JSON(http.StatusInternalServerError, resp.Error(err.Error())) |
||||
return |
||||
} |
||||
if test == nil || test.Id == 0 { |
||||
ctx.JSON(http.StatusInternalServerError, resp.Error(fmt.Sprintf("backtest %d not exists", backtestId))) |
||||
return |
||||
} |
||||
ctx.JSON(http.StatusOK, resp.Success(test)) |
||||
} |
||||
|
||||
func (svc *BacktestService) ListBacktestTrades(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.ListBacktestTrades(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, |
||||
})) |
||||
} |
||||
|
||||
// TestEquityCurve 回测记录资金曲线
|
||||
func (svc *BacktestService) TestEquities(ctx *gin.Context) { |
||||
backtestId, err := cast.ToInt64E(ctx.Query("backtestId")) |
||||
if backtestId == 0 || err != nil { |
||||
ctx.JSON(http.StatusBadRequest, resp.Error("param backtestId format error")) |
||||
return |
||||
} |
||||
times, equities, tradeIds, err := svc.repo.BacktestEquities(10001, backtestId) |
||||
if err != nil { |
||||
ctx.JSON(http.StatusInternalServerError, resp.Error(err.Error())) |
||||
return |
||||
} |
||||
ctx.JSON(http.StatusOK, resp.Success(resp.H{ |
||||
"time": times, |
||||
"equity": equities, |
||||
"tradeId": tradeIds, |
||||
})) |
||||
} |
||||
@ -0,0 +1,46 @@
|
||||
package service |
||||
|
||||
import ( |
||||
"net/http" |
||||
repository "sig-pub/internal/admin/repoitory" |
||||
"sig-pub/pkg/resp" |
||||
|
||||
"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) { |
||||
group.GET("listInstanceId", svc.ListInstanceId) // 所有交易产品ID
|
||||
group.GET("listInstance", svc.ListInstance) // 所有交易产品信息
|
||||
} |
||||
|
||||
func (svc *MarketService) ListInstanceId(ctx *gin.Context) { |
||||
instIds, err := svc.repo.ListAllInstanceId() |
||||
if err != nil { |
||||
ctx.JSON(http.StatusInternalServerError, resp.Error(err.Error())) |
||||
return |
||||
} |
||||
ctx.JSON(http.StatusOK, resp.Success(instIds)) |
||||
} |
||||
|
||||
func (svc *MarketService) ListInstance(ctx *gin.Context) { |
||||
insts, err := svc.repo.ListAllInstance() |
||||
if err != nil { |
||||
ctx.JSON(http.StatusInternalServerError, resp.Error(err.Error())) |
||||
return |
||||
} |
||||
|
||||
ctx.JSON(http.StatusOK, resp.Success(resp.H{ |
||||
"total": len(insts), |
||||
"insts": insts, |
||||
})) |
||||
} |
||||
@ -0,0 +1,17 @@
|
||||
package service |
||||
|
||||
import ( |
||||
repository "sig-pub/internal/admin/repoitory" |
||||
"sig-pub/pkg/storage/persist" |
||||
|
||||
"github.com/gin-gonic/gin" |
||||
) |
||||
|
||||
// Init services list
|
||||
func Init(group *gin.RouterGroup, rdb *persist.DB) { |
||||
backtestRepo := repository.NewBacktestRepository(rdb) |
||||
marketRepo := repository.NewMarketRepository(rdb) |
||||
|
||||
NewBacktestService(backtestRepo).Route(group.Group("/backtest")) |
||||
NewMarketService(marketRepo).Route(group.Group("/market")) |
||||
} |
||||
@ -1,9 +1,9 @@
|
||||
package sig |
||||
package admin |
||||
|
||||
import ( |
||||
"net/http" |
||||
"runtime/debug" |
||||
"sig-pub/internal/sig/service" |
||||
"sig-pub/internal/admin/service" |
||||
"sig-pub/pkg/resp" |
||||
"sig-pub/pkg/storage/persist" |
||||
"sig-pub/pkg/zlog" |
||||
@ -1,58 +0,0 @@
|
||||
package repository |
||||
|
||||
import ( |
||||
"fmt" |
||||
"sig-pub/pkg/data" |
||||
"sig-pub/pkg/storage/persist" |
||||
"sig-pub/pkg/trade" |
||||
) |
||||
|
||||
type BacktestRepository struct { |
||||
db *persist.DB |
||||
} |
||||
|
||||
func NewBacktestRepository(db *persist.DB) *BacktestRepository { |
||||
return &BacktestRepository{ |
||||
db: db, |
||||
} |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
@ -1,17 +0,0 @@
|
||||
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() { |
||||
|
||||
} |
||||
@ -1,60 +0,0 @@
|
||||
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" |
||||
) |
||||
|
||||
type BacktestService struct { |
||||
repo *repository.BacktestRepository |
||||
} |
||||
|
||||
func NewBacktestService(repo *repository.BacktestRepository) *BacktestService { |
||||
return &BacktestService{ |
||||
repo: repo, |
||||
} |
||||
} |
||||
|
||||
func (svc *BacktestService) Route(group *gin.RouterGroup) { |
||||
group.GET("listBacktestLog", svc.ListBacktestLog) // 回测记录
|
||||
group.POST("listBacktestLogTrades", svc.ListBacktestLogTrades) // 回测记录订单详情
|
||||
} |
||||
|
||||
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, |
||||
})) |
||||
} |
||||
@ -1,21 +0,0 @@
|
||||
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) { |
||||
|
||||
} |
||||
@ -1,15 +0,0 @@
|
||||
package service |
||||
|
||||
import ( |
||||
repository "sig-pub/internal/sig/repoitory" |
||||
"sig-pub/pkg/storage/persist" |
||||
|
||||
"github.com/gin-gonic/gin" |
||||
) |
||||
|
||||
// Init services list
|
||||
func Init(group *gin.RouterGroup, rdb *persist.DB) { |
||||
backtestRepository := repository.NewBacktestRepository(rdb) |
||||
|
||||
NewBacktestService(backtestRepository).Route(group.Group("/backtest")) |
||||
} |
||||
Loading…
Reference in new issue