ZhuLinsen/daily_stock_analysis · error · Error
大盘复盘正在执行中,请稍后再试
Error message
大盘复盘正在执行中,请稍后再试
What it means
Raised during quantity replay (portfolio_service.py:763) when a trade row's side is neither 'buy' nor 'sell' after strip+lowercase. The replay cannot apply the event to the running position, so it fails with validation_error instead of guessing.
Source
Thrown at apps/dsa-web/src/api/analysis.ts:121
triggerMarketReview: async (data: MarketReviewRequest = {}): Promise<MarketReviewAccepted> => {
const response = await apiClient.post<Record<string, unknown>>(
'/api/v1/analysis/market-review',
{
send_notification: data.sendNotification ?? true,
report_language: data.reportLanguage,
...(data.regions !== undefined && { region: serializeMarketReviewRegions(data.regions) }),
},
{
validateStatus: (status) => status === 202 || status === 409,
}
);
if (response.status === 409) {
const detail = response.data?.detail;
const message = detail && typeof detail === 'object' && 'message' in detail
? String((detail as { message?: unknown }).message || '')
: String(response.data?.message || '');
throw new Error(message || '大盘复盘正在执行中,请稍后再试');
}
return toCamelCase<MarketReviewAccepted>(response.data);
},
/**
* Get async task status.
* @param taskId Task ID
*/
getStatus: async (taskId: string): Promise<TaskStatus> => {
const response = await apiClient.get<Record<string, unknown>>(
`/api/v1/analysis/status/${taskId}`
);
const data = toCamelCase<TaskStatus>(response.data);
// Ensure nested result payloads are converted recursively.
if (data.result) {View on GitHub (pinned to 5159bd72e8)
Solutions
- Query trades for the symbol/account where side NOT IN ('buy','sell') or side IS NULL and repair the rows
- Map the source vocabulary (B->buy, S->sell) in the importer before writing
- Re-write rows through add_trade which enforces VALID_SIDES at write time
- Add a NOT NULL + CHECK constraint or audit query to catch drift early
Example fix
# before repo_inserted_row.side = "B" # after svc.add_trade(account_id=1, symbol="AAPL", side="buy", quantity=10, price=100.0, ...)
Defensive patterns
Strategy: validation
Validate before calling
VALID_SIDES = {"buy", "sell"}
def stored_side_ok(side):
return bool(side) and side.strip().lower() in VALID_SIDES Type guard
from typing import Literal, TypeGuard
StoredSide = Literal["buy", "sell"]
def is_stored_side(value: str | None) -> TypeGuard[StoredSide]:
return bool(value) and value.strip().lower() in {"buy", "sell"} Try / catch
try:
svc.get_snapshot(account_id=a)
except ValueError as exc:
if "Unsupported trade side" in str(exc):
normalize_sides_in_db(a); svc.get_snapshot(account_id=a)
else:
raise Prevention
- Write trades only through add_trade
- Map B/S vocabulary in importers
- Add a DB CHECK constraint on side
When it happens
Trigger: A trades row with side='B', side='Bought', side=NULL/empty (None or '' fails both the buy and sell branches), or any non-enum value reaching _calculate_available_quantity; typically rows written outside add_trade's validated path.
Common situations: Broker exports using B/S codes loaded by a script that writes straight to the repository; NULL side from a partially failed import; a schema change where side became optional and older rows have empty strings.
Related errors
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/c5df192ecc9ab89b.
Report an issue: GitHub.