From 47dbc5aab7f41ed4ef407fae1209fedff2be1aaf Mon Sep 17 00:00:00 2001 From: smallfish06 Date: Sat, 6 Jun 2026 21:13:34 +0900 Subject: [PATCH] Add LS overseas TR rate limits --- internal/ls/client.go | 28 ++++++++++++++++++++++++++++ internal/ls/client_test.go | 18 ++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/internal/ls/client.go b/internal/ls/client.go index 3d81134..2fe785b 100644 --- a/internal/ls/client.go +++ b/internal/ls/client.go @@ -207,6 +207,11 @@ func (c *Client) callEndpointAttempt( return nil, err } } + if limiter := c.trLimiter(trCD); limiter != nil { + if err := limiter.Wait(ctx); err != nil { + return nil, err + } + } method = strings.ToUpper(strings.TrimSpace(method)) if method == "" { @@ -288,6 +293,29 @@ func (c *Client) callEndpointAttempt( return out, nil } +func (c *Client) trLimiter(trCD string) *ratelimit.Limiter { + rps, burst, ok := lsTRRateLimit(trCD) + if !ok { + return nil + } + appKey, _ := c.getCredentials() + if appKey == "" { + appKey = "default" + } + return ratelimit.Shared("ls-"+strings.ToLower(strings.TrimSpace(trCD)), rps, burst, appKey) +} + +func lsTRRateLimit(trCD string) (float64, int, bool) { + switch strings.ToLower(strings.TrimSpace(trCD)) { + case TROverseasStockChart: + return 1, 1, true + case TROverseasStockQuote, "g3102", TROverseasStockInstrument, "g3106", TROverseasStockMaster: + return 10, 1, true + default: + return 0, 0, false + } +} + func (c *Client) refreshToken(ctx context.Context) error { appKey, appSecret := c.getCredentials() if appKey == "" || appSecret == "" { diff --git a/internal/ls/client_test.go b/internal/ls/client_test.go index aa1a014..0424383 100644 --- a/internal/ls/client_test.go +++ b/internal/ls/client_test.go @@ -239,6 +239,24 @@ func TestClientCallEndpoint_RetriesAfterInvalidCachedToken(t *testing.T) { } } +func TestLSTRRateLimit_UsesDocumentedOverseasQuotas(t *testing.T) { + t.Parallel() + + rps, burst, ok := lsTRRateLimit(TROverseasStockChart) + if !ok || rps != 1 || burst != 1 { + t.Fatalf("g3204 limit = rps %v burst %d ok %v, want 1/1/true", rps, burst, ok) + } + + rps, burst, ok = lsTRRateLimit(TROverseasStockQuote) + if !ok || rps != 10 || burst != 1 { + t.Fatalf("g3101 limit = rps %v burst %d ok %v, want 10/1/true", rps, burst, ok) + } + + if _, _, ok := lsTRRateLimit(TRStockQuote); ok { + t.Fatalf("domestic stock quote should use the shared client limiter only") + } +} + func TestOverseasRealtimeKey_PadsToDocumentedWidth(t *testing.T) { key, err := OverseasRealtimeKey("82", "AAPL") if err != nil {