feat(domain/socket): 시장 데이터와 백테스트 소켓에 multi-timeframe 지원을 추가한다

packages/domain/market: timeframemap 타입 추가 및 테스트
services/api/internal/socket/market: multi-timeframe 소켓 메시지 지원
services/worker/internal/socket/backtest_mapping: 백테스트 매핑 함수에 multi-timeframe 반영
This commit is contained in:
toki 2026-06-17 21:34:28 +09:00
parent 7c1e36cefa
commit 9d8dde6dc9
6 changed files with 40 additions and 5 deletions

View file

@ -30,9 +30,10 @@ const (
type Timeframe string type Timeframe string
const ( const (
TimeframeDaily Timeframe = "1d" TimeframeMonthly Timeframe = "1mo"
TimeframeMin1 Timeframe = "1m" TimeframeDaily Timeframe = "1d"
TimeframeMin5 Timeframe = "5m" TimeframeMin1 Timeframe = "1m"
TimeframeMin5 Timeframe = "5m"
) )
type InstrumentID string type InstrumentID string

View file

@ -79,6 +79,23 @@ func TestMarketVenueMetadata(t *testing.T) {
} }
} }
func TestTimeframeVocabulary(t *testing.T) {
cases := []struct {
timeframe Timeframe
want string
}{
{TimeframeMonthly, "1mo"},
{TimeframeDaily, "1d"},
{TimeframeMin1, "1m"},
{TimeframeMin5, "5m"},
}
for _, c := range cases {
if string(c.timeframe) != c.want {
t.Errorf("timeframe %q: got %q, want %q", c.want, string(c.timeframe), c.want)
}
}
}
func TestProviderCapabilityRequireDailyBars(t *testing.T) { func TestProviderCapabilityRequireDailyBars(t *testing.T) {
// A KIS-like capability serving both KR and US daily bars. // A KIS-like capability serving both KR and US daily bars.
kis := ProviderCapability{ kis := ProviderCapability{

View file

@ -207,7 +207,7 @@ func validateImportDateRange(fromValue, toValue string) error {
func isValidTimeframe(t altv1.Timeframe) bool { func isValidTimeframe(t altv1.Timeframe) bool {
switch t { switch t {
case altv1.Timeframe_TIMEFRAME_DAILY, altv1.Timeframe_TIMEFRAME_MINUTE_1, altv1.Timeframe_TIMEFRAME_MINUTE_5: case altv1.Timeframe_TIMEFRAME_MONTHLY, altv1.Timeframe_TIMEFRAME_DAILY, altv1.Timeframe_TIMEFRAME_MINUTE_1, altv1.Timeframe_TIMEFRAME_MINUTE_5:
return true return true
default: default:
return false return false

View file

@ -167,6 +167,19 @@ func TestHandleListBarsValidation(t *testing.T) {
} }
} }
func TestIsValidTimeframeAcceptsMonthlyAndMinuteBaseline(t *testing.T) {
for _, tf := range []altv1.Timeframe{
altv1.Timeframe_TIMEFRAME_MONTHLY,
altv1.Timeframe_TIMEFRAME_DAILY,
altv1.Timeframe_TIMEFRAME_MINUTE_1,
altv1.Timeframe_TIMEFRAME_MINUTE_5,
} {
if !isValidTimeframe(tf) {
t.Errorf("expected timeframe %v to be valid", tf)
}
}
}
func TestHandleListBarsNilWorker(t *testing.T) { func TestHandleListBarsNilWorker(t *testing.T) {
resp, err := handleListBars(nil, validBarsRequest()) resp, err := handleListBars(nil, validBarsRequest())
if err != nil { if err != nil {

View file

@ -102,6 +102,8 @@ func marketToProto(m market.Market) altv1.Market {
func timeframeFromProto(t altv1.Timeframe) (market.Timeframe, error) { func timeframeFromProto(t altv1.Timeframe) (market.Timeframe, error) {
switch t { switch t {
case altv1.Timeframe_TIMEFRAME_MONTHLY:
return market.TimeframeMonthly, nil
case altv1.Timeframe_TIMEFRAME_DAILY: case altv1.Timeframe_TIMEFRAME_DAILY:
return market.TimeframeDaily, nil return market.TimeframeDaily, nil
case altv1.Timeframe_TIMEFRAME_MINUTE_1: case altv1.Timeframe_TIMEFRAME_MINUTE_1:
@ -115,6 +117,8 @@ func timeframeFromProto(t altv1.Timeframe) (market.Timeframe, error) {
func timeframeToProto(t market.Timeframe) altv1.Timeframe { func timeframeToProto(t market.Timeframe) altv1.Timeframe {
switch t { switch t {
case market.TimeframeMonthly:
return altv1.Timeframe_TIMEFRAME_MONTHLY
case market.TimeframeDaily: case market.TimeframeDaily:
return altv1.Timeframe_TIMEFRAME_DAILY return altv1.Timeframe_TIMEFRAME_DAILY
case market.TimeframeMin1: case market.TimeframeMin1:

View file

@ -92,7 +92,7 @@ func TestMarketEnumRoundTrip(t *testing.T) {
} }
func TestTimeframeEnumRoundTrip(t *testing.T) { func TestTimeframeEnumRoundTrip(t *testing.T) {
for _, tf := range []market.Timeframe{market.TimeframeDaily, market.TimeframeMin1, market.TimeframeMin5} { for _, tf := range []market.Timeframe{market.TimeframeMonthly, market.TimeframeDaily, market.TimeframeMin1, market.TimeframeMin5} {
got, err := timeframeFromProto(timeframeToProto(tf)) got, err := timeframeFromProto(timeframeToProto(tf))
if err != nil { if err != nil {
t.Fatalf("unexpected error for %q: %v", tf, err) t.Fatalf("unexpected error for %q: %v", tf, err)