fix: filtres Journal de Bord — direction + asset_class tous onglets
Direction (Ouvert) : t.direction n'existe pas dans trade_entry_prices → utilisait undefined, excluait tout. Remplacé par _isBearishStr(t.strategy) comme Fermés. Asset class (Ouvert, Fermés, Non loggés) : l'IA retournait parfois "commodities", "currencies", "fx", "equity" au lieu des clés canoniques. Double correction : - Frontend : _normalizeAssetClass() mappe les variantes → energy|metals|agriculture| indices|equities|forex dans les 3 sections filtrées - Backend database.py : _normalize_asset_class() appliqué à l'INSERT dans trade_entry_prices et skipped_trades (nouveaux trades normalisés au stockage) - Prompt ai_analyzer.py : suggested_trades[].asset_class contraint à l'enum explicite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1173,14 +1173,14 @@ Retourne UNIQUEMENT ce JSON:
|
|||||||
"strategy": "<Long Call|Long Put|Bull Call Spread|Bear Put Spread|Long Straddle|Iron Condor|Short Strangle|Cash-Secured Put|Covered Call — respecter règles IVR>",
|
"strategy": "<Long Call|Long Put|Bull Call Spread|Bear Put Spread|Long Straddle|Iron Condor|Short Strangle|Cash-Secured Put|Covered Call — respecter règles IVR>",
|
||||||
"underlying": "<ticker Yahoo Finance>",
|
"underlying": "<ticker Yahoo Finance>",
|
||||||
"rationale": "<pourquoi ce trade dans ce contexte macro+géo+vol>",
|
"rationale": "<pourquoi ce trade dans ce contexte macro+géo+vol>",
|
||||||
"asset_class": "<classe>",
|
"asset_class": "<energy|metals|agriculture|indices|equities|forex>",
|
||||||
"expected_move_pct": <float, RENDEMENT OPTION en % pour CE trade si thèse confirmée. Long Call: 80-250%, Spread: 40-120%, Straddle: 60-180%.>
|
"expected_move_pct": <float, RENDEMENT OPTION en % pour CE trade si thèse confirmée. Long Call: 80-250%, Spread: 40-120%, Straddle: 60-180%.>
|
||||||
}},
|
}},
|
||||||
{{
|
{{
|
||||||
"strategy": "<autre stratégie respectant les règles IVR>",
|
"strategy": "<autre stratégie respectant les règles IVR>",
|
||||||
"underlying": "<ticker Yahoo Finance>",
|
"underlying": "<ticker Yahoo Finance>",
|
||||||
"rationale": "<rationale>",
|
"rationale": "<rationale>",
|
||||||
"asset_class": "<classe>",
|
"asset_class": "<energy|metals|agriculture|indices|equities|forex>",
|
||||||
"expected_move_pct": <float, rendement option attendu en % pour ce trade spécifique>
|
"expected_move_pct": <float, rendement option attendu en % pour ce trade spécifique>
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1027,6 +1027,26 @@ def _normalize_yf_ticker(ticker: str) -> str:
|
|||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_asset_class(cls: str) -> str:
|
||||||
|
"""Map AI-returned asset_class variants to canonical keys used in the UI."""
|
||||||
|
if not cls:
|
||||||
|
return ""
|
||||||
|
c = cls.lower().strip()
|
||||||
|
if any(k in c for k in ("energy", "oil", "gas", "petrol", "brent", "wti")):
|
||||||
|
return "energy"
|
||||||
|
if any(k in c for k in ("metal", "gold", "silver", "copper", "mining", "precious")):
|
||||||
|
return "metals"
|
||||||
|
if any(k in c for k in ("agri", "grain", "corn", "wheat", "soy", "crop", "coton", "coffee", "cocoa")):
|
||||||
|
return "agriculture"
|
||||||
|
if any(k in c for k in ("index", "indic", "indices", "spx", "nasdaq", "dow", "s&p", "russell", "cac", "dax")):
|
||||||
|
return "indices"
|
||||||
|
if any(k in c for k in ("equit", "stock", "action", "share", "sector", "xle", "xlf", "xlk")):
|
||||||
|
return "equities"
|
||||||
|
if any(k in c for k in ("forex", "currency", "fx", "devise", "change", "eur", "usd", "jpy", "dxy")):
|
||||||
|
return "forex"
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
def log_trade_entries(run_id: str, scored_patterns: List[Dict[str, Any]], quotes: Dict[str, Any]):
|
def log_trade_entries(run_id: str, scored_patterns: List[Dict[str, Any]], quotes: Dict[str, Any]):
|
||||||
"""
|
"""
|
||||||
For each scored pattern's trade_rankings, record entry price if the trade
|
For each scored pattern's trade_rankings, record entry price if the trade
|
||||||
@@ -1191,7 +1211,7 @@ def log_trade_entries(run_id: str, scored_patterns: List[Dict[str, Any]], quotes
|
|||||||
)
|
)
|
||||||
updated_count += 1
|
updated_count += 1
|
||||||
else:
|
else:
|
||||||
_trade_asset_class = (
|
_trade_asset_class = _normalize_asset_class(
|
||||||
trade.get("asset_class") or
|
trade.get("asset_class") or
|
||||||
sp.get("asset_class") or
|
sp.get("asset_class") or
|
||||||
_orig.get("asset_class") or
|
_orig.get("asset_class") or
|
||||||
@@ -1387,7 +1407,7 @@ def log_skipped_trade(run_id: str, pattern_id: str, pattern_name: str,
|
|||||||
expected_move_pct, skip_reason, skip_detail, asset_class)
|
expected_move_pct, skip_reason, skip_detail, asset_class)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
(run_id, pattern_id, pattern_name, underlying, strategy, score,
|
(run_id, pattern_id, pattern_name, underlying, strategy, score,
|
||||||
expected_move_pct, skip_reason, skip_detail, asset_class)
|
expected_move_pct, skip_reason, skip_detail, _normalize_asset_class(asset_class))
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -239,6 +239,19 @@ function _isBearishStr(strategy: string) {
|
|||||||
return /put|bear|short|sell|vente|baissier/i.test(strategy ?? '')
|
return /put|bear|short|sell|vente|baissier/i.test(strategy ?? '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize AI-returned asset_class variants to canonical CATEGORIES keys
|
||||||
|
function _normalizeAssetClass(cls: string | null | undefined): string {
|
||||||
|
if (!cls) return ''
|
||||||
|
const c = cls.toLowerCase().trim()
|
||||||
|
if (/energy|oil|gas|petrol|brent|wti/.test(c)) return 'energy'
|
||||||
|
if (/metal|gold|silver|copper|mining|precious/.test(c)) return 'metals'
|
||||||
|
if (/agri|grain|corn|wheat|soy|crop|coton|coffee|cocoa/.test(c)) return 'agriculture'
|
||||||
|
if (/index|indic|indices|spx|nasdaq|dow|s&p|russell|cac|dax/.test(c)) return 'indices'
|
||||||
|
if (/equit|stock|action|share|sector|xle|xlf|xlk/.test(c)) return 'equities'
|
||||||
|
if (/forex|currency|fx|devise|change|eur|usd|jpy|dxy/.test(c)) return 'forex'
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
interface FilterBarProps {
|
interface FilterBarProps {
|
||||||
search: string; onSearch: (v: string) => void
|
search: string; onSearch: (v: string) => void
|
||||||
assetClass: string; onAssetClass: (v: string) => void
|
assetClass: string; onAssetClass: (v: string) => void
|
||||||
@@ -330,7 +343,7 @@ function ClosedTradesSection({ days }: { days: number }) {
|
|||||||
const trades = allTrades.filter((t: any) => {
|
const trades = allTrades.filter((t: any) => {
|
||||||
const q = search.toLowerCase()
|
const q = search.toLowerCase()
|
||||||
if (q && !`${t.underlying} ${t.strategy ?? ''} ${t.pattern_name ?? ''}`.toLowerCase().includes(q)) return false
|
if (q && !`${t.underlying} ${t.strategy ?? ''} ${t.pattern_name ?? ''}`.toLowerCase().includes(q)) return false
|
||||||
if (assetClass !== 'all' && t.asset_class !== assetClass) return false
|
if (assetClass !== 'all' && _normalizeAssetClass(t.asset_class) !== assetClass) return false
|
||||||
const bearish = _isBearishStr(t.strategy ?? '')
|
const bearish = _isBearishStr(t.strategy ?? '')
|
||||||
if (direction === 'bullish' && bearish) return false
|
if (direction === 'bullish' && bearish) return false
|
||||||
if (direction === 'bearish' && !bearish) return false
|
if (direction === 'bearish' && !bearish) return false
|
||||||
@@ -900,9 +913,10 @@ function TradeMtmSection({ days }: { days: number }) {
|
|||||||
if ((t.latest_score ?? t.score_at_entry ?? 0) < minScoreFilter) return false
|
if ((t.latest_score ?? t.score_at_entry ?? 0) < minScoreFilter) return false
|
||||||
const q = search.toLowerCase()
|
const q = search.toLowerCase()
|
||||||
if (q && !`${t.underlying} ${t.strategy ?? ''} ${t.pattern_name ?? ''}`.toLowerCase().includes(q)) return false
|
if (q && !`${t.underlying} ${t.strategy ?? ''} ${t.pattern_name ?? ''}`.toLowerCase().includes(q)) return false
|
||||||
if (assetClass !== 'all' && t.asset_class !== assetClass) return false
|
if (assetClass !== 'all' && _normalizeAssetClass(t.asset_class) !== assetClass) return false
|
||||||
if (direction === 'bullish' && t.direction !== 'bullish') return false
|
const bearish = _isBearishStr(t.strategy ?? '')
|
||||||
if (direction === 'bearish' && t.direction !== 'bearish') return false
|
if (direction === 'bullish' && bearish) return false
|
||||||
|
if (direction === 'bearish' && !bearish) return false
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1631,7 +1645,7 @@ function SkippedTradesSection({ days }: { days: number }) {
|
|||||||
const trades = allTrades.filter((t: any) => {
|
const trades = allTrades.filter((t: any) => {
|
||||||
const q = search.toLowerCase()
|
const q = search.toLowerCase()
|
||||||
if (q && !`${t.underlying} ${t.strategy ?? ''} ${t.pattern_name ?? ''}`.toLowerCase().includes(q)) return false
|
if (q && !`${t.underlying} ${t.strategy ?? ''} ${t.pattern_name ?? ''}`.toLowerCase().includes(q)) return false
|
||||||
if (assetClass !== 'all' && t.asset_class !== assetClass) return false
|
if (assetClass !== 'all' && _normalizeAssetClass(t.asset_class) !== assetClass) return false
|
||||||
if (reasonFilter !== 'all' && t.skip_reason !== reasonFilter) return false
|
if (reasonFilter !== 'all' && t.skip_reason !== reasonFilter) return false
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user