diff --git a/apps/cli/internal/operator/runner.go b/apps/cli/internal/operator/runner.go index 8b9ceab..97d7487 100644 --- a/apps/cli/internal/operator/runner.go +++ b/apps/cli/internal/operator/runner.go @@ -55,6 +55,7 @@ var marketByName = map[string]altv1.Market{ // timeframeByName maps scenario timeframe strings onto the contract enum. var timeframeByName = map[string]altv1.Timeframe{ + "monthly": altv1.Timeframe_TIMEFRAME_MONTHLY, "daily": altv1.Timeframe_TIMEFRAME_DAILY, "minute_1": altv1.Timeframe_TIMEFRAME_MINUTE_1, "minute_5": altv1.Timeframe_TIMEFRAME_MINUTE_5, diff --git a/apps/cli/internal/operator/scenario.go b/apps/cli/internal/operator/scenario.go index d587a68..fc14c4c 100644 --- a/apps/cli/internal/operator/scenario.go +++ b/apps/cli/internal/operator/scenario.go @@ -161,6 +161,7 @@ var validVenues = map[string]bool{ // validTimeframes is the set of bar timeframe strings a list_bars request may // use, mirroring the API's accepted Timeframe values. var validTimeframes = map[string]bool{ + "monthly": true, "daily": true, "minute_1": true, "minute_5": true, diff --git a/apps/cli/internal/operator/scenario_test.go b/apps/cli/internal/operator/scenario_test.go index 7138a33..8d59f28 100644 --- a/apps/cli/internal/operator/scenario_test.go +++ b/apps/cli/internal/operator/scenario_test.go @@ -5,6 +5,8 @@ import ( "path/filepath" "strings" "testing" + + altv1 "git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1" ) func fixture(t *testing.T, name string) string { @@ -656,6 +658,45 @@ steps: } } +func TestTimeframeVocabularyIncludesMonthlyAndMinuteBaseline(t *testing.T) { + cases := []struct { + timeframe string + proto altv1.Timeframe + }{ + {"monthly", altv1.Timeframe_TIMEFRAME_MONTHLY}, + {"daily", altv1.Timeframe_TIMEFRAME_DAILY}, + {"minute_1", altv1.Timeframe_TIMEFRAME_MINUTE_1}, + {"minute_5", altv1.Timeframe_TIMEFRAME_MINUTE_5}, + } + for _, c := range cases { + t.Run(c.timeframe, func(t *testing.T) { + if !validTimeframes[c.timeframe] { + t.Fatalf("validTimeframes[%q] = false", c.timeframe) + } + if got := timeframeByName[c.timeframe]; got != c.proto { + t.Fatalf("timeframeByName[%q] = %v, want %v", c.timeframe, got, c.proto) + } + + yamlText := fmt.Sprintf(` +name: valid_%s_timeframe +steps: + - id: step1 + action: start_backtest + request: + strategy_id: strat1 + market: kr + timeframe: %s + from_unix_ms: 100 + to_unix_ms: 200 + symbols: ["005930"] +`, c.timeframe, c.timeframe) + if _, err := ParseScenario([]byte(yamlText)); err != nil { + t.Fatalf("ParseScenario(%q) returned error: %v", c.timeframe, err) + } + }) + } +} + func TestValidatePaperTradingScenario(t *testing.T) { yamlText := ` name: valid_paper