Skip to content

feat: harden KIS US OHLCV cache behavior and source mapping#164

Open
robin-watcha wants to merge 1 commit intomainfrom
codex/kis-us-ohlcv-cache-hardening
Open

feat: harden KIS US OHLCV cache behavior and source mapping#164
robin-watcha wants to merge 1 commit intomainfrom
codex/kis-us-ohlcv-cache-hardening

Conversation

@robin-watcha
Copy link
Collaborator

@robin-watcha robin-watcha commented Feb 17, 2026

Summary

  • switch US daily OHLCV cache usage to apply only when end_date is not provided
  • align ET as-of boundary to 16:00 and harden probe suppression for non-trading or partial-latest days
  • normalize KIS OHLCV cache key/meta schema with one-release legacy-key fallback
  • keep US quote path on Yahoo while unifying OHLCV/indicator-derived source metadata to KIS
  • update MCP docs and correlation description for KIS-based US correlation path

Test

  • uv run pytest tests/test_kis_ohlcv_cache.py -q --no-cov
  • uv run pytest tests/test_services.py -k "inquire_overseas_daily_price" -q --no-cov
  • uv run pytest tests/test_mcp_server_tools.py -k "get_ohlcv or get_indicators or correlation or support_resistance or get_quote_us_equity or get_holdings_preserves_kis_values_on_yahoo_failure" -q --no-cov
  • make test

Summary by CodeRabbit

  • New Features

    • Added Redis-backed caching for US daily market data with configurable TTL and retention limits.
    • Switched US market data source to KIS with 200-entry maximum per request.
    • Cache automatically handles market close times (ET 16:00) with weekend rollback adjustments.
  • Documentation

    • Updated documentation to reflect new US data source and cache behavior details.

@coderabbitai
Copy link

coderabbitai bot commented Feb 17, 2026

📝 Walkthrough

Walkthrough

The pull request introduces a Redis-backed OHLCV cache layer for US equity data and migrates US market data sources from Yahoo Finance to KIS. Configuration options for cache management are added, along with a new caching module featuring distributed locking and probe-based refresh logic. Multiple tool handlers are updated to use the new KIS data source.

Changes

Cohort / File(s) Summary
Configuration & Service Exports
app/core/config.py, env.example, app/services/__init__.py
Added new KIS OHLCV cache configuration fields (enabled, ttl, max_days, lock_ttl, probe_retry) to Settings and environment variables. Exposed kis_ohlcv_cache module at package level.
Documentation
app/mcp_server/README.md
Documented US market behavior: get_ohlcv, get_indicators, get_support_resistance, and get_correlation now use KIS overseas daily price data with count capped at 200. Cache applies only for day period without end_date, as-of 16:00 ET with weekend rollback.
Tool Description Updates
app/mcp_server/tooling/analysis_registration.py
Updated get_correlation tool description to reflect KIS as the source for US stocks instead of yfinance.
Source Mapping Updates (Yahoo → KIS)
app/mcp_server/tooling/analysis_screening.py, analysis_tool_handlers.py, fundamentals_handlers.py, portfolio_dca_core.py, portfolio_holdings.py
Changed equity_us source mapping from "yahoo" to "kis" across multiple tool implementations for consistent data sourcing.
Market Data Services
app/mcp_server/tooling/market_data_quotes.py, market_data_indicators.py
Replaced Yahoo-based OHLCV fetching with KIS overseas price lookups leveraging Redis cache. Added exchange code resolution, caching path for day period without end_date, and fallback to direct KIS API calls. Increased US OHLCV count cap from 100 to 200.
Core KIS Services
app/services/kis.py, app/services/kis_ohlcv_cache.py
Added end_date parameter to inquire_overseas_daily_price for date-bounded fetching. Introduced new kis_ohlcv_cache module with Redis-backed caching, distributed locking, probe-based refresh logic, retention trimming, and graceful fallback on unavailability.
Tests
tests/test_kis_ohlcv_cache.py, tests/test_mcp_server_tools.py, tests/test_services.py
Added comprehensive test suite for cache module covering edge cases and concurrency. Updated existing tests to mock KIS cache instead of Yahoo, verify correct argument passing, and expect "kis" as data source. Added test for end_date parameter handling.

Sequence Diagram

sequenceDiagram
    participant MCP as MCP Server
    participant QS as Quote Service
    participant Cache as KIS OHLCV Cache
    participant Lock as Redis Lock
    participant Fetch as Raw Fetcher
    participant Redis as Redis Store
    
    MCP->>QS: get_ohlcv(symbol, count, period, end_date)
    QS->>Cache: get_closed_daily_candles(symbol, exchange_code, count, raw_fetcher)
    
    Cache->>Cache: Normalize inputs & compute target_asof
    Cache->>Redis: Check cache availability
    Redis-->>Cache: Return cached rows (if available)
    
    alt Cache available and fresh
        Cache-->>QS: Return cached data
    else Cache stale or empty
        Cache->>Cache: Check probe status
        
        alt Probe active (recent fetch in progress)
            Cache-->>Cache: Skip fetch, return existing cache
        else Probe inactive
            Cache->>Lock: Acquire distributed lock
            
            alt Lock acquired
                Lock-->>Cache: Lock granted
                Cache->>Fetch: Call raw_fetcher (KIS API)
                Fetch-->>Cache: Return fresh OHLCV data
                Cache->>Cache: Normalize daily frame
                Cache->>Redis: Upsert rows, update probe, set TTL
                Redis-->>Cache: Confirm write
                Cache->>Lock: Release lock
            else Lock timeout
                Lock-->>Cache: Lock failed
                Cache-->>Cache: Return cached data if available
            end
        end
        Cache-->>QS: Return final data (cached or fresh)
    end
    
    QS-->>MCP: Return OHLCV candles
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • PR #141: Modifies app/services/kis.py (inquire_overseas_daily_price signature), directly affected by end_date parameter addition in this PR.
  • PR #114: Added original MCP server market-data tool implementations that are now being extended and refactored by this PR to use KIS cache and overseas price fetching.
  • PR #129: Updates KISClient in app/services/kis.py with ranking/order methods; overlaps at the same service class level as this PR's end_date enhancements.

Poem

🐰 A rabbit with whiskers of Redis so bright,
Built caches that shimmer through day and through night,
From Yahoo we danced to the KIS we now trust,
With locks that embrace us and probes clear the dust,
Each candle now cached, every quote swift and true! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (17 files):

⚔️ app/core/config.py (content)
⚔️ app/jobs/daily_scan.py (content)
⚔️ app/mcp_server/README.md (content)
⚔️ app/mcp_server/tooling/analysis_registration.py (content)
⚔️ app/mcp_server/tooling/analysis_screening.py (content)
⚔️ app/mcp_server/tooling/analysis_tool_handlers.py (content)
⚔️ app/mcp_server/tooling/fundamentals_handlers.py (content)
⚔️ app/mcp_server/tooling/market_data_indicators.py (content)
⚔️ app/mcp_server/tooling/market_data_quotes.py (content)
⚔️ app/mcp_server/tooling/portfolio_dca_core.py (content)
⚔️ app/mcp_server/tooling/portfolio_holdings.py (content)
⚔️ app/services/__init__.py (content)
⚔️ app/services/kis.py (content)
⚔️ env.example (content)
⚔️ tests/test_daily_scan.py (content)
⚔️ tests/test_mcp_server_tools.py (content)
⚔️ tests/test_services.py (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main changes: hardening KIS US OHLCV cache behavior and aligning source mapping from Yahoo to KIS.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/kis-us-ohlcv-cache-hardening
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch codex/kis-us-ohlcv-cache-hardening
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
17.8% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@claude
Copy link

claude bot commented Feb 17, 2026

PR Review: KIS US OHLCV Cache & Source Mapping

Summary

This PR introduces a sophisticated Redis-based caching layer for KIS US daily OHLCV data and unifies the data source for US equities from Yahoo to KIS across multiple tools. The implementation is well-architected with comprehensive test coverage.


✅ Strengths

1. Excellent Architecture

  • Separation of concerns: The cache logic in kis_ohlcv_cache.py is cleanly isolated as a standalone module
  • Timezone handling: Proper ET timezone conversion with 16:00 close boundary is well-implemented (app/services/kis_ohlcv_cache.py:24-38)
  • Lock-based concurrency: Distributed locking prevents duplicate API calls during cache misses (app/services/kis_ohlcv_cache.py:436-460)
  • Graceful degradation: Returns None on Redis errors allowing fallback to direct API calls (app/services/kis_ohlcv_cache.py:520-524)

2. Robust Caching Strategy

  • Smart probe suppression: Prevents repeated API calls on weekends/holidays when data isn't available (app/services/kis_ohlcv_cache.py:427-434)
  • Legacy key compatibility: One-release backward compatibility ensures smooth migration (app/services/kis_ohlcv_cache.py:407-421, 490-491)
  • Data retention: Automatic trimming keeps cache size bounded (app/services/kis_ohlcv_cache.py:475)
  • Cache-only on latest: Only uses cache when end_date is not provided, ensuring historical queries bypass cache (app/mcp_server/tooling/market_data_quotes.py:209)

3. Comprehensive Testing

  • 580 lines of test coverage demonstrating thorough edge case handling
  • Tests cover: cache hits/misses, lock contention, timezone boundaries, legacy compatibility, error conditions
  • Excellent use of _FakeRedis mock for deterministic testing without external dependencies
  • Test naming follows clear conventions (tests/test_kis_ohlcv_cache.py:1124-1510)

4. Documentation

  • Well-documented changes in app/mcp_server/README.md:26-30
  • Comprehensive environment variable documentation in env.example
  • Clear PR description with test commands

🔍 Code Quality Observations

Potential Issues

1. Global State Management (Minor)

Location: app/services/kis_ohlcv_cache.py:21

_REDIS_CLIENT: redis.Redis | None = None

Issue: Global mutable state can cause issues in testing/concurrent contexts
Recommendation: The current implementation is acceptable for this use case, but consider documenting that close_kis_ohlcv_cache_redis() should be called during app shutdown

2. Magic Numbers (Minor)

Location: app/services/kis_ohlcv_cache.py:443

for _ in range(6):
    await asyncio.sleep(0.05)

Issue: Hardcoded retry count and sleep duration
Recommendation: Consider extracting to settings for easier tuning:

KIS_OHLCV_CACHE_LOCK_RETRY_COUNT=6
KIS_OHLCV_CACHE_LOCK_RETRY_SLEEP=0.05

3. Error Handling Breadth (Minor)

Location: app/services/kis_ohlcv_cache.py:520-524

except Exception as exc:
    logger.warning(...)
    return None

Issue: Catches all exceptions which might hide unexpected errors
Recommendation: Consider catching specific exception types and re-raising truly unexpected ones

4. Count Capping Inconsistency (Medium)

Locations:

  • app/mcp_server/tooling/market_data_quotes.py:205 (capped to 200)
  • app/mcp_server/tooling/market_data_indicators.py:106 (capped to 250)

Issue: Different max counts for US equity OHLCV data
Recommendation: Standardize to 200 across all code paths or document why they differ

5. Missing Exchange Code Validation (Medium)

Location: app/services/kis_ohlcv_cache.py:358-360

normalized_symbol = str(symbol or "").strip().upper()
normalized_exchange = str(exchange_code or "").strip().upper()
if not normalized_symbol or not normalized_exchange:
    return _empty_dataframe()

Issue: Returns empty DataFrame for invalid inputs, which might be indistinguishable from "no data available"
Recommendation: Consider logging a warning when inputs are invalid


🔐 Security Review

✅ No Security Concerns Identified

  • No SQL injection risks (using Redis, not SQL)
  • No command injection vectors
  • No sensitive data exposure (symbols/prices are not secrets)
  • Proper input validation with .upper() and .strip()
  • Redis keys are properly namespaced preventing collisions

⚡ Performance Considerations

Strengths

  1. Efficient Redis operations: Uses sorted sets for date-based queries
  2. Pipeline batching: Redis pipeline reduces round trips (app/services/kis_ohlcv_cache.py:274-283)
  3. Lock timeout: Prevents indefinite blocking (10s default)
  4. Count limiting: Caps to prevent excessive memory usage

Potential Optimizations

  1. Cache prewarming: Consider background job to populate cache for popular symbols
  2. Compression: Large OHLCV datasets could benefit from JSON compression before Redis storage
  3. Connection pooling: Already using max_connections setting which is good

🧪 Test Coverage Assessment

Excellent Coverage

  • ✅ Cache miss → fetch → store → hit flow
  • ✅ Legacy key fallback compatibility
  • ✅ Lock contention handling
  • ✅ Timezone boundary conditions (EST/EDT)
  • ✅ Weekend rollback logic
  • ✅ Probe suppression on partial data
  • ✅ Redis connection failures
  • ✅ Error propagation in MCP tools

Missing Test Cases (Recommendations)

  1. Redis connection pooling exhaustion: What happens when max_connections is reached?
  2. Very large datasets: Test with count approaching max_days (400)
  3. Concurrent writes to same symbol: Race conditions beyond simple lock tests
  4. Cache corruption recovery: Malformed JSON in Redis hash

📝 Documentation & Maintenance

Strengths

  • Clear README updates explaining behavior changes
  • Environment variable examples provided
  • Inline comments explain complex logic
  • Test plan included in PR description

Recommendations

  1. Migration guide: Document steps for deployments with existing Yahoo-sourced cache data
  2. Monitoring alerts: Recommend metrics to track (cache hit rate, Redis errors, lock timeout rate)
  3. Runbook: Document how to manually clear cache if needed (using Redis CLI)

🎯 Best Practices Adherence

✅ Follows Project Conventions

  • Uses uv for dependency management
  • Async/await patterns consistent with codebase
  • Type hints properly used throughout
  • Follows existing naming conventions
  • Proper use of @pytest.mark.asyncio

✅ Follows CLAUDE.md Guidelines

  • Maintains separation between services and analysis layers
  • Uses existing Redis infrastructure
  • Adds appropriate environment variables
  • Comprehensive test coverage as required

🔄 Breaking Changes & Migration

Breaking Changes

  1. Source metadata change: US equity data now reports source: "kis" instead of "yahoo"
  2. Count limits: US OHLCV capped to 200 (was 100 in some places, unlimited in Yahoo)
  3. Cache behavior: Only applies to period="day" without end_date

Migration Impact

  • Low risk: Changes are mostly internal implementation details
  • API compatibility: Tool interfaces remain unchanged
  • Cache warmup: First requests after deployment will be slower (cache misses)
  • Rollback: Can revert by setting KIS_OHLCV_CACHE_ENABLED=false

📊 Final Assessment

Category Rating Notes
Code Quality ⭐⭐⭐⭐⭐ Excellent structure and readability
Test Coverage ⭐⭐⭐⭐⭐ Comprehensive with good edge cases
Documentation ⭐⭐⭐⭐☆ Good but could add migration guide
Performance ⭐⭐⭐⭐⭐ Well-optimized caching strategy
Security ⭐⭐⭐⭐⭐ No concerns identified
Maintainability ⭐⭐⭐⭐☆ Clear code, minor magic numbers

✅ Recommendation

APPROVE with minor suggestions

This is high-quality work that significantly improves the system by:

  1. Reducing API calls to KIS through intelligent caching
  2. Unifying data sources for consistency
  3. Handling timezones and weekends correctly
  4. Providing excellent test coverage

The suggestions above are minor improvements that can be addressed in follow-up PRs if desired. The current implementation is production-ready.

Pre-merge Checklist

  • Verify all tests pass (make test)
  • Confirm Redis is running in deployment environment
  • Review environment variables are set in production config
  • Consider adding monitoring for cache hit rates
  • Document cache clearing procedure for operators

Great work on this PR! The caching implementation is robust and well-tested. 🚀

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/services/kis.py (1)

1818-1835: ⚠️ Potential issue | 🟡 Minor

Document the new end_date parameter in the docstring.

The function signature now includes end_date, but the Args list doesn’t reflect it.

✏️ Suggested docstring update
         Args:
             symbol: 종목 심볼 (예: "AAPL")
             exchange_code: 거래소 코드 (NASD/NYSE/AMEX 등)
             n: 조회할 캔들 수 (최소 200개 권장, 이동평균선 계산용)
             period: D(일봉)/W(주봉)/M(월봉)
+            end_date: 종료 날짜 (None이면 최신 기준)
As per coding guidelines: Document complex analysis logic and API integration details in docstrings using Google style format.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/services/kis.py` around lines 1818 - 1835, The docstring for the method
that accepts parameters (symbol, exchange_code, n, period, end_date) is missing
documentation for the new end_date parameter; update the Args section to include
end_date with its type (datetime.date | None), default (None), and behavior (the
last date to include in the returned candles; if None uses the most recent
available date), and briefly note how it affects returned data alongside
existing params like symbol, exchange_code, n, period and the return DataFrame
(date, open, high, low, close, volume).
🧹 Nitpick comments (1)
tests/test_kis_ohlcv_cache.py (1)

263-287: Extract repeated settings monkeypatching into a shared fixture.

The same block of ~6 monkeypatch.setattr(kis_ohlcv_cache.settings, ...) calls is duplicated across five async tests. A shared fixture would reduce boilerplate and make it easier to adjust defaults in one place.

Suggested fixture
`@pytest.fixture`
def _patch_cache_settings(monkeypatch):
    """Enable cache with sensible test defaults."""
    monkeypatch.setattr(kis_ohlcv_cache.settings, "kis_ohlcv_cache_enabled", True, raising=False)
    monkeypatch.setattr(kis_ohlcv_cache.settings, "kis_ohlcv_cache_ttl_seconds", 300, raising=False)
    monkeypatch.setattr(kis_ohlcv_cache.settings, "kis_ohlcv_cache_max_days", 400, raising=False)
    monkeypatch.setattr(kis_ohlcv_cache.settings, "kis_ohlcv_cache_lock_ttl_seconds", 10, raising=False)
    monkeypatch.setattr(kis_ohlcv_cache.settings, "kis_ohlcv_cache_probe_retry_seconds", 1800, raising=False)

Then use it via @pytest.mark.usefixtures("_patch_cache_settings") or as a parameter on each test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_kis_ohlcv_cache.py` around lines 263 - 287, Extract the repeated
monkeypatch.setattr calls into a shared pytest fixture named
_patch_cache_settings that uses monkeypatch to set
kis_ohlcv_cache.settings.kis_ohlcv_cache_enabled,
kis_ohlcv_cache.settings.kis_ohlcv_cache_ttl_seconds,
kis_ohlcv_cache.settings.kis_ohlcv_cache_max_days,
kis_ohlcv_cache.settings.kis_ohlcv_cache_lock_ttl_seconds, and
kis_ohlcv_cache.settings.kis_ohlcv_cache_probe_retry_seconds to the test
defaults shown; then remove the repeated monkeypatch blocks from the tests and
apply the fixture via `@pytest.mark.usefixtures`("_patch_cache_settings") or by
adding _patch_cache_settings as a test parameter so all tests reuse the same
configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/mcp_server/tooling/market_data_quotes.py`:
- Around line 276-321: The nested function _raw_fetcher inside
_fetch_ohlcv_equity_us is missing a return type hint; update its signature to
add an explicit return type (e.g., -> Any or the precise response type returned
by KISClient.inquire_overseas_daily_price) and ensure parameter types match
(symbol: str, exchange_code: str, n: int, end_date: datetime.date | None) so the
nested function has full type annotations; adjust any imports if you choose a
concrete return type and run type checks to confirm compatibility with
kis_ohlcv_cache.get_closed_daily_candles and
KISClient.inquire_overseas_daily_price.

In `@app/services/kis_ohlcv_cache.py`:
- Around line 24-38: The public helper functions expected_asof_et and
get_closed_daily_candles lack Google-style docstrings; add concise Google-style
docstrings to each (function summary, Args with types and meaning, Returns with
type and semantics, Raises if any exceptions can be thrown, and a short Example
or Notes for edge cases) describing their purpose (how expected_asof_et computes
the previous ET trading date given a UTC datetime and how
get_closed_daily_candles retrieves/constructs cached daily OHLCV rows),
reference parameter names (now_utc for expected_asof_et and the public
parameters of get_closed_daily_candles), and document timezone/market-close
logic and weekend handling so callers understand behavior and return values.
- Around line 289-308: In _set_probe_meta ensure the Redis meta-key TTL cannot
be shorter than the probe retry window: compute retry_seconds from settings (as
already done) and set the expire TTL to
max(int(settings.kis_ohlcv_cache_ttl_seconds), retry_seconds, 1) so the stored
probe_until_ts (and related probe fields in _set_probe_meta) will never expire
before the retry window elapses; update the expire call in _set_probe_meta to
use that computed value.

In `@tests/test_kis_ohlcv_cache.py`:
- Around line 194-197: The tests in this file (e.g.,
test_expected_asof_et_handles_est_edt_and_weekend_rollbacks) lack pytest
markers; add a file-level marker by defining pytestmark = pytest.mark.unit near
the top of the file (after imports) or alternatively decorate each test with
`@pytest.mark.unit`; ensure pytest is imported in the file (import pytest) if not
already present so the marker resolves.

In `@tests/test_mcp_server_tools.py`:
- Around line 5741-5756: Add the pytest unit marker and parameter/return type
hints to the async test
"test_get_support_resistance_us_error_payload_source_is_kis": add the decorator
`@pytest.mark.unit` above `@pytest.mark.asyncio`, and update the function signature
to include the monkeypatch parameter type and return type (e.g. monkeypatch:
pytest.MonkeyPatch) -> None. Ensure the test still uses build_tools and
_patch_runtime_attr as before and that pytest is imported so pytest.MonkeyPatch
is available.
- Around line 1268-1286: Add the required pytest classification marker and type
hints to the test: annotate the async test function
test_get_ohlcv_us_equity_caps_count_to_200 with the unit marker (e.g. add
`@pytest.mark.unit` above the existing `@pytest.mark.asyncio`) and add
parameter/return type hints (change signature to async def
test_get_ohlcv_us_equity_caps_count_to_200(monkeypatch: pytest.MonkeyPatch) ->
None). Ensure pytest is imported so pytest.MonkeyPatch is available.
- Around line 5759-5774: Add the appropriate pytest marker and type hints for
the async test function test_get_correlation_us_metadata_source_is_kis: decorate
the function with a marker such as `@pytest.mark.unit` (ensure pytest is imported)
and change the signature to include type hints (e.g., monkeypatch:
pytest.MonkeyPatch) and an explicit return type of -> None (async def
test_get_correlation_us_metadata_source_is_kis(monkeypatch: pytest.MonkeyPatch)
-> None:). Keep the existing AsyncMock/monkeypatch usage and other symbols like
build_tools and _patch_runtime_attr unchanged.
- Around line 1288-1325: The new test function
test_get_ohlcv_us_equity_day_with_end_date_bypasses_cache is missing the
required pytest classification marker and lacks type hints on its signature; add
an appropriate pytest marker (e.g., `@pytest.mark.unit` or
`@pytest.mark.integration`) above the test and annotate the async function
signature and any parameters/returns used in the test (for example annotate the
test function parameters if any and specify -> None return) so that test files
follow the project's typing and marker rules; update the test declaration for
test_get_ohlcv_us_equity_day_with_end_date_bypasses_cache and ensure consistency
with other helpers like build_tools, _single_row_df, and
DummyKISClient/inquire_overseas_daily_price usage.

In `@tests/test_services.py`:
- Around line 1062-1107: Add a pytest marker and type hints to the test function
declaration for test_inquire_overseas_daily_price_uses_end_date_as_bymd:
annotate the test's parameters (self, mock_settings, mock_client_class) with
types (e.g., TestCase/self: Any or the test class type, mocks as
MagicMock/AsyncMock or appropriate typing from unittest.mock) and add a return
type of None, and add an appropriate pytest marker decorator (e.g.,
`@pytest.mark.unit`) above the function; ensure the function name and mocked
symbols (KISClient, inquire_overseas_daily_price, mock_client_class) remain
unchanged.

---

Outside diff comments:
In `@app/services/kis.py`:
- Around line 1818-1835: The docstring for the method that accepts parameters
(symbol, exchange_code, n, period, end_date) is missing documentation for the
new end_date parameter; update the Args section to include end_date with its
type (datetime.date | None), default (None), and behavior (the last date to
include in the returned candles; if None uses the most recent available date),
and briefly note how it affects returned data alongside existing params like
symbol, exchange_code, n, period and the return DataFrame (date, open, high,
low, close, volume).

---

Nitpick comments:
In `@tests/test_kis_ohlcv_cache.py`:
- Around line 263-287: Extract the repeated monkeypatch.setattr calls into a
shared pytest fixture named _patch_cache_settings that uses monkeypatch to set
kis_ohlcv_cache.settings.kis_ohlcv_cache_enabled,
kis_ohlcv_cache.settings.kis_ohlcv_cache_ttl_seconds,
kis_ohlcv_cache.settings.kis_ohlcv_cache_max_days,
kis_ohlcv_cache.settings.kis_ohlcv_cache_lock_ttl_seconds, and
kis_ohlcv_cache.settings.kis_ohlcv_cache_probe_retry_seconds to the test
defaults shown; then remove the repeated monkeypatch blocks from the tests and
apply the fixture via `@pytest.mark.usefixtures`("_patch_cache_settings") or by
adding _patch_cache_settings as a test parameter so all tests reuse the same
configuration.

Comment on lines 276 to +321
async def _fetch_ohlcv_equity_us(
symbol: str, count: int, period: str, end_date: datetime.datetime | None
) -> dict[str, Any]:
"""Fetch US equity OHLCV from Yahoo Finance."""
capped_count = min(count, 100)
df = await yahoo_service.fetch_ohlcv(
ticker=symbol, days=capped_count, period=period, end_date=end_date
)
capped_count = min(count, 200)
kis = KISClient()
exchange_code = get_exchange_by_symbol(symbol.upper()) or "NASD"

if period == "day" and end_date is None:

async def _raw_fetcher(
*, symbol: str, exchange_code: str, n: int, end_date: datetime.date | None
):
return await kis.inquire_overseas_daily_price(
symbol=symbol,
exchange_code=exchange_code,
n=n,
period="D",
end_date=end_date,
)

cached = await kis_ohlcv_cache.get_closed_daily_candles(
symbol=symbol,
exchange_code=exchange_code,
count=capped_count,
instrument_type="equity_us",
raw_fetcher=_raw_fetcher,
)
if cached is not None:
df = cached
else:
df = await kis.inquire_overseas_daily_price(
symbol=symbol,
exchange_code=exchange_code,
n=capped_count,
period="D",
)
else:
kis_period_map = {"day": "D", "week": "W", "month": "M"}
df = await kis.inquire_overseas_daily_price(
symbol=symbol,
exchange_code=exchange_code,
n=capped_count,
period=kis_period_map.get(period, "D"),
end_date=end_date.date() if end_date else None,
)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a return type hint for the nested _raw_fetcher.

Project guidelines require type hints for all function params/returns.

🔧 Suggested tweak
         async def _raw_fetcher(
             *, symbol: str, exchange_code: str, n: int, end_date: datetime.date | None
-        ):
+        ) -> pd.DataFrame:
             return await kis.inquire_overseas_daily_price(
As per coding guidelines: **/*.py: Use type hints for all function parameters and return types in Python files.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/mcp_server/tooling/market_data_quotes.py` around lines 276 - 321, The
nested function _raw_fetcher inside _fetch_ohlcv_equity_us is missing a return
type hint; update its signature to add an explicit return type (e.g., -> Any or
the precise response type returned by KISClient.inquire_overseas_daily_price)
and ensure parameter types match (symbol: str, exchange_code: str, n: int,
end_date: datetime.date | None) so the nested function has full type
annotations; adjust any imports if you choose a concrete return type and run
type checks to confirm compatibility with
kis_ohlcv_cache.get_closed_daily_candles and
KISClient.inquire_overseas_daily_price.

Comment on lines +24 to +38
def expected_asof_et(now_utc: datetime) -> date:
base = now_utc
if base.tzinfo is None:
base = base.replace(tzinfo=UTC)
et_now = base.astimezone(_ET)

anchor = (
et_now.date()
if et_now.time() >= time(16, 0)
else et_now.date() - timedelta(days=1)
)

while anchor.weekday() >= 5:
anchor -= timedelta(days=1)
return anchor
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add Google‑style docstrings to the public cache helpers.

expected_asof_et and get_closed_daily_candles are part of the public API surface and should be documented.

📝 Suggested docstrings
 def expected_asof_et(now_utc: datetime) -> date:
+    """Compute the expected closed trading date in ET.
+
+    Args:
+        now_utc: Current UTC timestamp.
+
+    Returns:
+        The ET trading date that should be treated as closed.
+    """
     base = now_utc
 async def get_closed_daily_candles(
     *,
     symbol: str,
     exchange_code: str,
     count: int,
     instrument_type: str,
     raw_fetcher: Callable[..., Awaitable[pd.DataFrame]],
     now_utc: datetime | None = None,
 ) -> pd.DataFrame | None:
+    """Return cached daily candles up to the last closed session.
+
+    Args:
+        symbol: Ticker symbol.
+        exchange_code: Exchange code (e.g., NASD).
+        count: Requested candle count.
+        instrument_type: Cache namespace (e.g., equity_us).
+        raw_fetcher: Fallback fetcher used to refresh cache.
+        now_utc: Override for current UTC time.
+
+    Returns:
+        Cached OHLCV frame, or None when cache is disabled/unavailable.
+    """
     if not settings.kis_ohlcv_cache_enabled:
As per coding guidelines: Document complex analysis logic and API integration details in docstrings using Google style format.

Also applies to: 340-349

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/services/kis_ohlcv_cache.py` around lines 24 - 38, The public helper
functions expected_asof_et and get_closed_daily_candles lack Google-style
docstrings; add concise Google-style docstrings to each (function summary, Args
with types and meaning, Returns with type and semantics, Raises if any
exceptions can be thrown, and a short Example or Notes for edge cases)
describing their purpose (how expected_asof_et computes the previous ET trading
date given a UTC datetime and how get_closed_daily_candles retrieves/constructs
cached daily OHLCV rows), reference parameter names (now_utc for
expected_asof_et and the public parameters of get_closed_daily_candles), and
document timezone/market-close logic and weekend handling so callers understand
behavior and return values.

Comment on lines +289 to +308
async def _set_probe_meta(
redis_client: redis.Redis,
meta_key: str,
target_asof: date,
now_utc: datetime,
) -> None:
retry_seconds = max(int(settings.kis_ohlcv_cache_probe_retry_seconds), 1)
until_ts = int(now_utc.timestamp()) + retry_seconds
await redis_client.hset(
meta_key,
mapping={
"last_probe_target_asof": target_asof.isoformat(),
# Backward compatibility for one release window.
"last_probe_asof": target_asof.isoformat(),
"probe_until_ts": str(until_ts),
"last_probe_ts": str(int(now_utc.timestamp())),
},
)
await redis_client.expire(meta_key, max(int(settings.kis_ohlcv_cache_ttl_seconds), 1))

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Probe suppression can expire before the retry window.

probe_until_ts can be set for longer than the meta-key TTL (default 1800s vs 300s). If the hash expires early, probe suppression is lost and the system may re-probe too aggressively.

🛠️ Suggested fix
     retry_seconds = max(int(settings.kis_ohlcv_cache_probe_retry_seconds), 1)
     until_ts = int(now_utc.timestamp()) + retry_seconds
     await redis_client.hset(
         meta_key,
         mapping={
             "last_probe_target_asof": target_asof.isoformat(),
             # Backward compatibility for one release window.
             "last_probe_asof": target_asof.isoformat(),
             "probe_until_ts": str(until_ts),
             "last_probe_ts": str(int(now_utc.timestamp())),
         },
     )
-    await redis_client.expire(meta_key, max(int(settings.kis_ohlcv_cache_ttl_seconds), 1))
+    ttl_seconds = max(int(settings.kis_ohlcv_cache_ttl_seconds), 1)
+    expire_seconds = max(ttl_seconds, retry_seconds)
+    await redis_client.expire(meta_key, expire_seconds)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/services/kis_ohlcv_cache.py` around lines 289 - 308, In _set_probe_meta
ensure the Redis meta-key TTL cannot be shorter than the probe retry window:
compute retry_seconds from settings (as already done) and set the expire TTL to
max(int(settings.kis_ohlcv_cache_ttl_seconds), retry_seconds, 1) so the stored
probe_until_ts (and related probe fields in _set_probe_meta) will never expire
before the retry window elapses; update the expire call in _set_probe_meta to
use that computed value.

Comment on lines +194 to +197
def test_expected_asof_et_handles_est_edt_and_weekend_rollbacks():
assert kis_ohlcv_cache.expected_asof_et(
datetime(2026, 1, 15, 20, 30, tzinfo=UTC)
) == date(2026, 1, 14)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Missing pytest markers on all test functions.

None of the tests in this file have @pytest.mark.unit (or other category markers). The coding guidelines require categorizing tests with @pytest.mark.unit, @pytest.mark.integration, or @pytest.mark.slow.

These are all unit-level tests (no real Redis, no network). Add @pytest.mark.unit to each, or apply it file-wide via pytestmark:

Proposed fix

Add near the top of the file (e.g., after the imports):

pytestmark = pytest.mark.unit

As per coding guidelines, tests/test_*.py: "Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow) to categorize tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_kis_ohlcv_cache.py` around lines 194 - 197, The tests in this file
(e.g., test_expected_asof_et_handles_est_edt_and_weekend_rollbacks) lack pytest
markers; add a file-level marker by defining pytestmark = pytest.mark.unit near
the top of the file (after imports) or alternatively decorate each test with
`@pytest.mark.unit`; ensure pytest is imported in the file (import pytest) if not
already present so the marker resolves.

Comment on lines +1268 to +1286
@pytest.mark.asyncio
async def test_get_ohlcv_us_equity_caps_count_to_200(monkeypatch):
tools = build_tools()
df = _single_row_df()
cache_mock = AsyncMock(return_value=df)
monkeypatch.setattr(
market_data_quotes.kis_ohlcv_cache,
"get_closed_daily_candles",
cache_mock,
)

result = await tools["get_ohlcv"]("AAPL", count=999)

cache_mock.assert_awaited_once()
assert cache_mock.call_args is not None
assert cache_mock.call_args.kwargs["count"] == 200
assert result["count"] == 200
assert result["source"] == "kis"

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add required pytest marker + type hints for the new test.

New tests in tests/test_*.py should be categorized and typed per repo standards.

Suggested fix
 `@pytest.mark.asyncio`
-async def test_get_ohlcv_us_equity_caps_count_to_200(monkeypatch):
+@pytest.mark.unit
+async def test_get_ohlcv_us_equity_caps_count_to_200(
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:

As per coding guidelines: tests/test_*.py: Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow) to categorize tests; and **/*.py: Use type hints for all function parameters and return types in Python files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_mcp_server_tools.py` around lines 1268 - 1286, Add the required
pytest classification marker and type hints to the test: annotate the async test
function test_get_ohlcv_us_equity_caps_count_to_200 with the unit marker (e.g.
add `@pytest.mark.unit` above the existing `@pytest.mark.asyncio`) and add
parameter/return type hints (change signature to async def
test_get_ohlcv_us_equity_caps_count_to_200(monkeypatch: pytest.MonkeyPatch) ->
None). Ensure pytest is imported so pytest.MonkeyPatch is available.

Comment on lines +1288 to +1325
@pytest.mark.asyncio
async def test_get_ohlcv_us_equity_day_with_end_date_bypasses_cache(monkeypatch):
tools = build_tools()
df = _single_row_df()
cache_mock = AsyncMock(side_effect=AssertionError("cache should be bypassed"))
monkeypatch.setattr(
market_data_quotes.kis_ohlcv_cache,
"get_closed_daily_candles",
cache_mock,
)
called: dict[str, object] = {}

class DummyKISClient:
async def inquire_overseas_daily_price(
self,
symbol: str,
exchange_code: str = "NASD",
n: int = 200,
period: str = "D",
end_date: date | None = None,
):
called["symbol"] = symbol
called["exchange_code"] = exchange_code
called["n"] = n
called["period"] = period
called["end_date"] = end_date
return df

_patch_runtime_attr(monkeypatch, "KISClient", DummyKISClient)

result = await tools["get_ohlcv"]("AAPL", count=5, period="day", end_date="2026-01-15")

cache_mock.assert_not_awaited()
assert called["symbol"] == "AAPL"
assert called["period"] == "D"
assert called["end_date"] == date(2026, 1, 15)
assert result["source"] == "kis"

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add required pytest marker + type hints for the new test.

Same compliance gap here for the new cache-bypass test.

Suggested fix
 `@pytest.mark.asyncio`
-async def test_get_ohlcv_us_equity_day_with_end_date_bypasses_cache(monkeypatch):
+@pytest.mark.unit
+async def test_get_ohlcv_us_equity_day_with_end_date_bypasses_cache(
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:

As per coding guidelines: tests/test_*.py: Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow) to categorize tests; and **/*.py: Use type hints for all function parameters and return types in Python files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_mcp_server_tools.py` around lines 1288 - 1325, The new test
function test_get_ohlcv_us_equity_day_with_end_date_bypasses_cache is missing
the required pytest classification marker and lacks type hints on its signature;
add an appropriate pytest marker (e.g., `@pytest.mark.unit` or
`@pytest.mark.integration`) above the test and annotate the async function
signature and any parameters/returns used in the test (for example annotate the
test function parameters if any and specify -> None return) so that test files
follow the project's typing and marker rules; update the test declaration for
test_get_ohlcv_us_equity_day_with_end_date_bypasses_cache and ensure consistency
with other helpers like build_tools, _single_row_df, and
DummyKISClient/inquire_overseas_daily_price usage.

Comment on lines +5741 to +5756
@pytest.mark.asyncio
async def test_get_support_resistance_us_error_payload_source_is_kis(monkeypatch):
tools = build_tools()
_patch_runtime_attr(
monkeypatch,
"_fetch_ohlcv_for_indicators",
AsyncMock(side_effect=RuntimeError("fetch failed")),
)

result = await tools["get_support_resistance"]("AAPL", market="us")

assert result["source"] == "kis"
assert result["instrument_type"] == "equity_us"
assert result["symbol"] == "AAPL"
assert "fetch failed" in result["error"]

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add required pytest marker + type hints for the new test.

This new async test needs the unit marker and parameter typing.

Suggested fix
 `@pytest.mark.asyncio`
-async def test_get_support_resistance_us_error_payload_source_is_kis(monkeypatch):
+@pytest.mark.unit
+async def test_get_support_resistance_us_error_payload_source_is_kis(
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:

As per coding guidelines: tests/test_*.py: Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow) to categorize tests; and **/*.py: Use type hints for all function parameters and return types in Python files.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.mark.asyncio
async def test_get_support_resistance_us_error_payload_source_is_kis(monkeypatch):
tools = build_tools()
_patch_runtime_attr(
monkeypatch,
"_fetch_ohlcv_for_indicators",
AsyncMock(side_effect=RuntimeError("fetch failed")),
)
result = await tools["get_support_resistance"]("AAPL", market="us")
assert result["source"] == "kis"
assert result["instrument_type"] == "equity_us"
assert result["symbol"] == "AAPL"
assert "fetch failed" in result["error"]
`@pytest.mark.asyncio`
`@pytest.mark.unit`
async def test_get_support_resistance_us_error_payload_source_is_kis(
monkeypatch: pytest.MonkeyPatch,
) -> None:
tools = build_tools()
_patch_runtime_attr(
monkeypatch,
"_fetch_ohlcv_for_indicators",
AsyncMock(side_effect=RuntimeError("fetch failed")),
)
result = await tools["get_support_resistance"]("AAPL", market="us")
assert result["source"] == "kis"
assert result["instrument_type"] == "equity_us"
assert result["symbol"] == "AAPL"
assert "fetch failed" in result["error"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_mcp_server_tools.py` around lines 5741 - 5756, Add the pytest unit
marker and parameter/return type hints to the async test
"test_get_support_resistance_us_error_payload_source_is_kis": add the decorator
`@pytest.mark.unit` above `@pytest.mark.asyncio`, and update the function signature
to include the monkeypatch parameter type and return type (e.g. monkeypatch:
pytest.MonkeyPatch) -> None. Ensure the test still uses build_tools and
_patch_runtime_attr as before and that pytest is imported so pytest.MonkeyPatch
is available.

Comment on lines +5759 to +5774
async def test_get_correlation_us_metadata_source_is_kis(monkeypatch):
tools = build_tools()
frame = pd.DataFrame({"close": [100.0, 101.0, 102.0, 103.0]})

_patch_runtime_attr(
monkeypatch,
"_fetch_ohlcv_for_indicators",
AsyncMock(return_value=frame),
)

result = await tools["get_correlation"](["AAPL", "MSFT"], period=30)

assert result["success"] is True
assert result["metadata"]["sources"]["AAPL"] == "kis"
assert result["metadata"]["sources"]["MSFT"] == "kis"

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add required pytest marker + type hints for the new test.

Same compliance issue for the correlation metadata test.

Suggested fix
 `@pytest.mark.asyncio`
-async def test_get_correlation_us_metadata_source_is_kis(monkeypatch):
+@pytest.mark.unit
+async def test_get_correlation_us_metadata_source_is_kis(
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:

As per coding guidelines: tests/test_*.py: Use pytest markers (@pytest.mark.unit, @pytest.mark.integration, @pytest.mark.slow) to categorize tests; and **/*.py: Use type hints for all function parameters and return types in Python files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_mcp_server_tools.py` around lines 5759 - 5774, Add the appropriate
pytest marker and type hints for the async test function
test_get_correlation_us_metadata_source_is_kis: decorate the function with a
marker such as `@pytest.mark.unit` (ensure pytest is imported) and change the
signature to include type hints (e.g., monkeypatch: pytest.MonkeyPatch) and an
explicit return type of -> None (async def
test_get_correlation_us_metadata_source_is_kis(monkeypatch: pytest.MonkeyPatch)
-> None:). Keep the existing AsyncMock/monkeypatch usage and other symbols like
build_tools and _patch_runtime_attr unchanged.

Comment on lines +1062 to +1107
@pytest.mark.asyncio
@patch("app.services.kis.httpx.AsyncClient")
@patch("app.services.kis.settings")
async def test_inquire_overseas_daily_price_uses_end_date_as_bymd(
self, mock_settings, mock_client_class
):
from datetime import date

from app.services.kis import KISClient

mock_settings.kis_account_no = "1234567890"
mock_settings.kis_access_token = "test_token"

mock_client = AsyncMock()
mock_client_class.return_value.__aenter__.return_value = mock_client

mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"rt_cd": "0",
"output2": [
{
"xymd": "20260115",
"open": "190.0",
"high": "191.0",
"low": "189.0",
"clos": "190.5",
"tvol": "100",
}
],
}
mock_client.get.return_value = mock_response

client = KISClient()
client._ensure_token = AsyncMock(return_value=None)
client._token_manager = AsyncMock()

end_date = date(2026, 1, 15)
result = await client.inquire_overseas_daily_price(
symbol="AAPL", n=1, end_date=end_date
)

assert len(result) == 1
params = mock_client.get.call_args.kwargs["params"]
assert params["BYMD"] == "20260115"

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a pytest category marker and type hints for the new test.

This keeps the test suite compliant with marker policy and typing requirements.

✅ Suggested update
+    `@pytest.mark.unit`
     `@pytest.mark.asyncio`
     `@patch`("app.services.kis.httpx.AsyncClient")
     `@patch`("app.services.kis.settings")
     async def test_inquire_overseas_daily_price_uses_end_date_as_bymd(
-        self, mock_settings, mock_client_class
-    ):
+        self, mock_settings: MagicMock, mock_client_class: MagicMock
+    ) -> None:
As per coding guidelines: tests/test_*.py: Use pytest markers (`@pytest.mark.unit`, `@pytest.mark.integration`, `@pytest.mark.slow`) to categorize tests; **/*.py: Use type hints for all function parameters and return types in Python files.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_services.py` around lines 1062 - 1107, Add a pytest marker and
type hints to the test function declaration for
test_inquire_overseas_daily_price_uses_end_date_as_bymd: annotate the test's
parameters (self, mock_settings, mock_client_class) with types (e.g.,
TestCase/self: Any or the test class type, mocks as MagicMock/AsyncMock or
appropriate typing from unittest.mock) and add a return type of None, and add an
appropriate pytest marker decorator (e.g., `@pytest.mark.unit`) above the
function; ensure the function name and mocked symbols (KISClient,
inquire_overseas_daily_price, mock_client_class) remain unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant