ZhuLinsen/daily_stock_analysis · error · Error

选股功能不可用。请检查策略配置、数据依赖和服务日志。

Error message

选股功能不可用。请检查策略配置、数据依赖和服务日志。

What it means

Raised inside quantity replay (portfolio_service.py:765) when a historical sell event exceeds the running quantity_held at that point in the ordered event stream. This is the same oversell contract as the write-time check, but detected during replay of already-stored events, e.g. when computing a snapshot; PortfolioOversellError carries the event's symbol, date, and quantities.

Source

Thrown at apps/dsa-web/src/api/screening.ts:460

      `/api/v1/screening/hotspots/${encodeURIComponent(payload.topic)}`,
      {
        params: {
          provider: payload.provider || 'akshare',
          refresh: payload.refresh ?? false,
          include_search: payload.includeSearch ?? false,
        },
        timeout: SCREENING_REQUEST_TIMEOUT_MS,
      },
    );
    return toCamelCase<ScreeningHotspotDetail>(response.data);
  },

  async enable(): Promise<void> {
    await setScreeningEnabled('true');
    try {
      const status = await screeningApi.getStatus();
      if (!status.available) {
        throw new Error('选股功能不可用。请检查策略配置、数据依赖和服务日志。');
      }
    } catch (error) {
      try {
        await setScreeningEnabled('false');
      } catch {
        // Preserve the original availability/status failure for the caller.
      }
      throw error;
    }
  },
};

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Reconstruct a consistent timeline: ensure for each symbol every sell date has cumulative buys+splits covering it
  2. Restore or re-add the deleted/edited buy events, or delete the orphaned sells if they were wrong
  3. Insert the missing split_adjustment events so historical quantities scale correctly
  4. Import in chronological order and never mutate stored event dates directly in the DB

Example fix

# before
# sell imported with date before its buy
svc.add_trade(account_id=1, symbol="AAPL", side="sell", quantity=10, trade_date=date(2025,1,1), ...)
svc.add_trade(account_id=1, symbol="AAPL", side="buy", quantity=10, trade_date=date(2024,12,31), ...)
# after
svc.add_trade(account_id=1, symbol="AAPL", side="buy", quantity=10, trade_date=date(2024,12,31), ...)
svc.add_trade(account_id=1, symbol="AAPL", side="sell", quantity=10, trade_date=date(2025,1,1), ...)
Defensive patterns

Strategy: try-catch

Validate before calling

def timeline_consistent(svc, account_id, symbol):
    held = 0.0
    for ev in svc.query_trades(account_id=account_id, symbol=symbol, page=1, page_size=10000)["items"]:
        held += ev["quantity"] if ev["side"] == "buy" else -ev["quantity"]
        if held < -1e-8:
            return False
    return True

Type guard

from src.services.portfolio_service import PortfolioOversellError

def is_oversell(exc: Exception) -> bool:
    return isinstance(exc, PortfolioOversellError)

Try / catch

from src.services.portfolio_service import PortfolioOversellError

try:
    svc.get_snapshot(account_id=a)
except PortfolioOversellError as exc:
    # exc.symbol / exc.trade_date pinpoint the first inconsistent sell
    quarantine_event(exc.symbol, exc.trade_date); svc.get_snapshot(account_id=a)

Prevention

When it happens

Trigger: Deleting or re-dating a buy after sells were recorded; inserting a sell with an earlier trade_date than its covering buy; a missing split_adjustment so historical holdings are understated; importing an out-of-order history where an early sell is replayed before its buy. Because writes are validated at insert time, hitting this at replay usually means the event timeline changed after the fact.

Common situations: Manual edits to trade dates; deletion of a buy via direct DB access; import jobs inserting events in reverse-chronological batches with later backfills; timezone handling that shifts a buy's date to after a same-day sell.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/244c090d1715bbe4. Report an issue: GitHub.