alt/services/worker/internal/papertrading/service_test.go
toki aaf1af6387 feat(paper-trading): paper 상태 모니터링을 연결한다
Paper trading readiness에서 API/worker/CLI가 같은 protobuf 계약으로 paper state를 시작하고 조회할 수 있어야 한다. Headless 운영 경로를 먼저 닫기 위해 contract, worker runtime, API forwarding, CLI scenario, client parser map과 검증 artifact를 함께 반영한다.
2026-06-05 15:14:41 +09:00

89 lines
2.7 KiB
Go

package papertrading
import (
"context"
"errors"
"testing"
"time"
"git.toki-labs.com/toki/alt/packages/domain/backtest"
"git.toki-labs.com/toki/alt/packages/domain/market"
)
func serviceTestRequest() StartRequest {
return StartRequest{
AccountID: "paper-acct-1",
Spec: backtest.RunSpec{
StrategyID: "test-simple-buy",
Market: market.MarketKR,
Timeframe: market.TimeframeDaily,
From: time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC),
To: time.Date(2026, 5, 3, 0, 0, 0, 0, time.UTC),
},
StartingCash: market.Price{Currency: market.CurrencyKRW, Amount: market.Decimal{Value: "10000000"}},
}
}
func newTestService() *Service {
bars := makeBars(nil)
engine := NewEngine(&testBarSource{bars: bars}, &testStrategyPort{strategy: simpleBuyStrategy{id: "test-simple-buy"}})
fixed := time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)
return NewService(engine, func() time.Time { return fixed })
}
func TestServiceStartRecordsState(t *testing.T) {
svc := newTestService()
state, err := svc.StartPaperTrading(context.Background(), serviceTestRequest())
if err != nil {
t.Fatalf("start failed: %v", err)
}
if state.Run.Status != backtest.RunStatusSucceeded {
t.Errorf("expected succeeded run status, got %q", state.Run.Status)
}
if state.Run.ID == "" {
t.Error("expected a non-empty run id")
}
// simpleBuyStrategy buys 1 unit on day 1, fills on day 2 at open 1050.
if len(state.Fills) != 1 {
t.Fatalf("expected 1 fill, got %d", len(state.Fills))
}
if state.Cash.Amount.Value != "9998950" {
t.Errorf("expected cash 9998950, got %s", state.Cash.Amount.Value)
}
if len(state.Positions) != 1 {
t.Errorf("expected 1 position, got %d", len(state.Positions))
}
}
func TestServiceGetStateReturnsRecorded(t *testing.T) {
svc := newTestService()
if _, err := svc.StartPaperTrading(context.Background(), serviceTestRequest()); err != nil {
t.Fatalf("start failed: %v", err)
}
state, err := svc.GetPaperTradingState(context.Background(), "paper-acct-1")
if err != nil {
t.Fatalf("get state failed: %v", err)
}
if state.Run.Status != backtest.RunStatusSucceeded {
t.Errorf("expected succeeded run status, got %q", state.Run.Status)
}
}
func TestServiceGetStateNotFound(t *testing.T) {
svc := newTestService()
_, err := svc.GetPaperTradingState(context.Background(), "never-started")
if !errors.Is(err, ErrAccountNotFound) {
t.Fatalf("expected ErrAccountNotFound, got %v", err)
}
}
func TestServiceStartRequiresAccountID(t *testing.T) {
svc := newTestService()
req := serviceTestRequest()
req.AccountID = ""
if _, err := svc.StartPaperTrading(context.Background(), req); err == nil {
t.Fatal("expected error for empty account id")
}
}