Merge pull request 'Fix overnight runner stability and token cooldown handling' (#139) from agentson/fix/137-run-overnight-python-tmux into main
Some checks failed
CI / test (push) Has been cancelled

Reviewed-on: #139
This commit was merged in pull request #139.
This commit is contained in:
2026-02-18 00:05:44 +09:00
3 changed files with 58 additions and 27 deletions

View File

@@ -315,6 +315,11 @@ class SmartVolatilityScanner:
logger.info("Overseas scanner: no symbol universe for %s", market.name)
return []
logger.info(
"Overseas scanner: scanning %d fallback symbols for %s",
len(symbols),
market.name,
)
candidates: list[ScanCandidate] = []
for stock_code in symbols:
try:
@@ -350,6 +355,11 @@ class SmartVolatilityScanner:
logger.warning("Failed to analyze overseas %s: %s", stock_code, exc)
except Exception as exc:
logger.error("Unexpected error analyzing overseas %s: %s", stock_code, exc)
logger.info(
"Overseas symbol fallback scan found %d candidates for %s",
len(candidates),
market.name,
)
return candidates
def get_stock_codes(self, candidates: list[ScanCandidate]) -> list[str]:

View File

@@ -104,12 +104,14 @@ class KISBroker:
time_since_last_attempt = now - self._last_refresh_attempt
if time_since_last_attempt < self._refresh_cooldown:
remaining = self._refresh_cooldown - time_since_last_attempt
error_msg = (
f"Token refresh on cooldown. "
f"Retry in {remaining:.1f}s (KIS allows 1/minute)"
# Do not fail fast here. If token is unavailable, upstream calls
# will all fail for up to a minute and scanning returns no trades.
logger.warning(
"Token refresh on cooldown. Waiting %.1fs before retry (KIS allows 1/minute)",
remaining,
)
logger.warning(error_msg)
raise ConnectionError(error_msg)
await asyncio.sleep(remaining)
now = asyncio.get_event_loop().time()
logger.info("Refreshing KIS access token")
self._last_refresh_attempt = now

View File

@@ -82,14 +82,19 @@ class OverseasBroker:
session = self._broker._get_session()
if ranking_type == "volume":
tr_id = self._broker._settings.OVERSEAS_RANKING_VOLUME_TR_ID
path = self._broker._settings.OVERSEAS_RANKING_VOLUME_PATH
configured_tr_id = self._broker._settings.OVERSEAS_RANKING_VOLUME_TR_ID
configured_path = self._broker._settings.OVERSEAS_RANKING_VOLUME_PATH
default_tr_id = "HHDFS76200200"
default_path = "/uapi/overseas-price/v1/quotations/inquire-volume-rank"
else:
tr_id = self._broker._settings.OVERSEAS_RANKING_FLUCT_TR_ID
path = self._broker._settings.OVERSEAS_RANKING_FLUCT_PATH
configured_tr_id = self._broker._settings.OVERSEAS_RANKING_FLUCT_TR_ID
configured_path = self._broker._settings.OVERSEAS_RANKING_FLUCT_PATH
default_tr_id = "HHDFS76200100"
default_path = "/uapi/overseas-price/v1/quotations/inquire-updown-rank"
headers = await self._broker._auth_headers(tr_id)
url = f"{self._broker._base_url}{path}"
endpoint_specs: list[tuple[str, str]] = [(configured_tr_id, configured_path)]
if (configured_tr_id, configured_path) != (default_tr_id, default_path):
endpoint_specs.append((default_tr_id, default_path))
# Try common param variants used by KIS overseas quotation APIs.
param_variants = [
@@ -100,12 +105,18 @@ class OverseasBroker:
]
last_error: str | None = None
saw_http_404 = False
for tr_id, path in endpoint_specs:
headers = await self._broker._auth_headers(tr_id)
url = f"{self._broker._base_url}{path}"
for params in param_variants:
try:
async with session.get(url, headers=headers, params=params) as resp:
text = await resp.text()
if resp.status != 200:
last_error = f"HTTP {resp.status}: {text}"
if resp.status == 404:
saw_http_404 = True
continue
data = await resp.json()
@@ -119,6 +130,14 @@ class OverseasBroker:
last_error = str(exc)
continue
if saw_http_404:
logger.warning(
"Overseas ranking endpoint unavailable (404) for %s/%s; using symbol fallback scan",
exchange_code,
ranking_type,
)
return []
raise ConnectionError(
f"fetch_overseas_rankings failed for {exchange_code}/{ranking_type}: {last_error}"
)