KOL feeds: fix dead/blocked sources, drop stale feeds (29→25)
Feed-health pass over KOL_FEEDS: - raoulpal: stale Substack (last 2024-05) → Real Vision podcast feed - dampedspring: paywalled (0 entries) → free "Damped Spring 101" Substack - unchained: Cloudflare 403 → canonical Megaphone podcast feed - lynalden: Cloudflare 202 → FeedBurner mirror - glassnode: recovered via httpx http2=True (was 403 on HTTP/1.1) - browser User-Agent + Accept headers on feed fetch - removed dead feeds with no active replacement: placeholder, dragonfly, niccarter, eugene - pin h2==4.3.0 (required by http2=True) All 25 remaining feeds verified fetching real body content; newest post per feed ≤88d. Bundles in-flight KOL-module work already in the working tree (kol_x ingest, migration 027, tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+20
-13
@@ -19,27 +19,34 @@ ACTION_VIEW_USER = "view_user"
|
||||
|
||||
|
||||
def _trade_to_schema(trade: BotTrade) -> BotTradeSchema:
|
||||
# Join against trigger_post to surface the source tag. When a trade was
|
||||
# opened by an ingested signal (VCP scanner, user's module, etc.) the
|
||||
# source reveals WHICH module produced it — critical for "is module X
|
||||
# actually making money?" attribution analysis.
|
||||
trigger_source = None
|
||||
trigger_post_text = None
|
||||
if trade.trigger_post is not None:
|
||||
trigger_source = trade.trigger_post.source
|
||||
# Paper trades are tagged via hl_order_id at open time; that's the only
|
||||
# stable signal we have to distinguish them in aggregate views.
|
||||
trigger_post_text = trade.trigger_post.text
|
||||
elif trade.hl_order_id and str(trade.hl_order_id).startswith("adopted:"):
|
||||
# Adopted (sys2 manage-only) trades have trigger_post_id=NULL by
|
||||
# construction, so trigger_source would otherwise fall through to
|
||||
# "Unknown" in the UI. The hl_order_id prefix is the reliable marker
|
||||
# (see adoption.py / CLAUDE.md) — surface it as a proper attribution.
|
||||
trigger_source = "adopted"
|
||||
is_paper = (trade.hl_order_id == "paper")
|
||||
# Preserve None for nullable fields — do NOT coerce to 0 / "".
|
||||
# The frontend uses null to distinguish "unknown/not yet settled" from
|
||||
# a genuine zero (e.g. a break-even trade, a 0-second hold).
|
||||
# `or 0` was silently masking externally-closed trades and unsettled PnL.
|
||||
return BotTradeSchema(
|
||||
id=trade.id,
|
||||
asset=trade.asset,
|
||||
side=trade.side,
|
||||
entry_price=trade.entry_price,
|
||||
exit_price=trade.exit_price or 0.0,
|
||||
pnl_usd=trade.pnl_usd or 0.0,
|
||||
hold_seconds=trade.hold_seconds or 0,
|
||||
trigger_post_id=trade.trigger_post_id or 0,
|
||||
exit_price=trade.exit_price, # None = position still open or extern-closed
|
||||
pnl_usd=trade.pnl_usd, # None = unsettled / extern-closed
|
||||
hold_seconds=trade.hold_seconds, # None = not yet computed
|
||||
trigger_post_id=trade.trigger_post_id, # None = adopted/manual, no trigger post
|
||||
opened_at=iso_utc(trade.opened_at) or "",
|
||||
closed_at=iso_utc(trade.closed_at) or "",
|
||||
closed_at=iso_utc(trade.closed_at), # None = still open (shouldn't happen here)
|
||||
trigger_post_text=trigger_post_text,
|
||||
trigger_source=trigger_source,
|
||||
is_paper=is_paper,
|
||||
)
|
||||
@@ -50,7 +57,7 @@ async def get_trades(
|
||||
wallet: str = Query(..., description="Wallet address (lower-cased internally)"),
|
||||
ts: int = Query(..., description="Signed timestamp (ms)"),
|
||||
sig: str = Query(..., description="EIP-191 signature"),
|
||||
limit: int = Query(default=20, ge=1, le=100),
|
||||
limit: int = Query(default=20, ge=1, le=500), # raised from 100: Analytics needs 500 for full history
|
||||
page: int = Query(default=1, ge=1),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
@@ -71,7 +78,7 @@ async def get_trades(
|
||||
.options(joinedload(BotTrade.trigger_post))
|
||||
.where(BotTrade.wallet_address == wallet)
|
||||
.where(BotTrade.closed_at.is_not(None))
|
||||
.order_by(BotTrade.opened_at.desc())
|
||||
.order_by(BotTrade.closed_at.desc()) # closed_at = settlement time; opened_at would bury recently-closed old trades
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user