feat: cockpit
This commit is contained in:
@@ -48,13 +48,23 @@ def _saxo_quote(saxo_symbol: str) -> Optional[dict]:
|
|||||||
def watchlist_quotes():
|
def watchlist_quotes():
|
||||||
from services.database import get_instruments_watchlist
|
from services.database import get_instruments_watchlist
|
||||||
from services.data_fetcher import get_quote_with_volatility
|
from services.data_fetcher import get_quote_with_volatility
|
||||||
|
from services.wavelet_signals import _FRIENDLY_TO_YFINANCE
|
||||||
items = []
|
items = []
|
||||||
for row in get_instruments_watchlist():
|
for row in get_instruments_watchlist():
|
||||||
q = None
|
q = None
|
||||||
if row.get("saxo_quote_symbol"):
|
if row.get("saxo_quote_symbol"):
|
||||||
q = _saxo_quote(row["saxo_quote_symbol"])
|
q = _saxo_quote(row["saxo_quote_symbol"])
|
||||||
if q is None:
|
if q is None:
|
||||||
q = get_quote_with_volatility(row["ticker"]) or {}
|
# row["ticker"] is the Cockpit's own label for some rows (BRENT, GOLD...), not a
|
||||||
|
# real yfinance symbol — without this translation yfinance returns nothing and
|
||||||
|
# the card shows "—" instead of falling back to the last available close, same
|
||||||
|
# gap already fixed once for the wavelet cache (see _FRIENDLY_TO_YFINANCE there).
|
||||||
|
yf_ticker = row["ticker"]
|
||||||
|
if yf_ticker in _FRIENDLY_TO_YFINANCE:
|
||||||
|
yf_ticker = _FRIENDLY_TO_YFINANCE[yf_ticker]
|
||||||
|
elif len(yf_ticker) == 6 and yf_ticker.isalpha():
|
||||||
|
yf_ticker += "=X"
|
||||||
|
q = get_quote_with_volatility(yf_ticker) or {}
|
||||||
items.append({
|
items.append({
|
||||||
**row,
|
**row,
|
||||||
"price": q.get("price"),
|
"price": q.get("price"),
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ export default function Dashboard() {
|
|||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
|
|
||||||
const { todayEvents, restOfWeekGrouped, nextWeekGrouped } = useMemo(() => {
|
const { todayEvents, restOfWeekGrouped, nextWeekGrouped, lastPastDayGrouped, isCurrentWeekEmpty } = useMemo(() => {
|
||||||
const all: any[] = (ecoCalendarData as any)?.events ?? []
|
const all: any[] = (ecoCalendarData as any)?.events ?? []
|
||||||
const events = all.filter((ev: any) => ecoImpactFilter.has(ev.impact) && ecoCurrencyFilter.has(ev.currency))
|
const events = all.filter((ev: any) => ecoImpactFilter.has(ev.impact) && ecoCurrencyFilter.has(ev.currency))
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
@@ -295,7 +295,23 @@ export default function Dashboard() {
|
|||||||
const restOfWeek = events.filter((ev: any) => ev.event_date > todayISO && ev.event_date <= weekEndISO)
|
const restOfWeek = events.filter((ev: any) => ev.event_date > todayISO && ev.event_date <= weekEndISO)
|
||||||
const nextWeek = events.filter((ev: any) => ev.event_date > weekEndISO && ev.event_date <= nextWeekEndISO)
|
const nextWeek = events.filter((ev: any) => ev.event_date > weekEndISO && ev.event_date <= nextWeekEndISO)
|
||||||
|
|
||||||
return { todayEvents: today, restOfWeekGrouped: groupByDate(restOfWeek), nextWeekGrouped: groupByDate(nextWeek) }
|
// Weekend / dead-week fallback: nothing today and nothing left this week (e.g. Sunday,
|
||||||
|
// when weekEndISO collapses to todayISO so restOfWeek is structurally always empty) —
|
||||||
|
// surface the most recent past day that actually had results instead of a blank card.
|
||||||
|
const currentWeekEmpty = today.length === 0 && restOfWeek.length === 0
|
||||||
|
let lastPastDay: any[] = []
|
||||||
|
if (currentWeekEmpty) {
|
||||||
|
const past = events
|
||||||
|
.filter((ev: any) => ev.event_date < todayISO)
|
||||||
|
.sort((a: any, b: any) => b.event_date.localeCompare(a.event_date))
|
||||||
|
const mostRecentDate = past[0]?.event_date
|
||||||
|
if (mostRecentDate) lastPastDay = past.filter((ev: any) => ev.event_date === mostRecentDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
todayEvents: today, restOfWeekGrouped: groupByDate(restOfWeek), nextWeekGrouped: groupByDate(nextWeek),
|
||||||
|
lastPastDayGrouped: groupByDate(lastPastDay), isCurrentWeekEmpty: currentWeekEmpty,
|
||||||
|
}
|
||||||
}, [ecoCalendarData, ecoImpactFilter, ecoCurrencyFilter])
|
}, [ecoCalendarData, ecoImpactFilter, ecoCurrencyFilter])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -660,7 +676,9 @@ export default function Dashboard() {
|
|||||||
+Next week
|
+Next week
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{(todayEvents.length > 0 || restOfWeekGrouped.length > 0 || (ecoShowNextWeek && nextWeekGrouped.length > 0)) ? (
|
{(todayEvents.length > 0 || restOfWeekGrouped.length > 0
|
||||||
|
|| (ecoShowNextWeek && nextWeekGrouped.length > 0)
|
||||||
|
|| (isCurrentWeekEmpty && (lastPastDayGrouped.length > 0 || nextWeekGrouped.length > 0))) ? (
|
||||||
<div className="mt-2 space-y-2 overflow-y-auto flex-1 min-h-0">
|
<div className="mt-2 space-y-2 overflow-y-auto flex-1 min-h-0">
|
||||||
{todayEvents.length > 0 && (
|
{todayEvents.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
@@ -682,7 +700,21 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{ecoShowNextWeek && nextWeekGrouped.length > 0 && (
|
{/* Weekend fallback: nothing today/this week left — show the last day that
|
||||||
|
actually had results (e.g. Friday, on a Sunday) instead of a blank card. */}
|
||||||
|
{isCurrentWeekEmpty && lastPastDayGrouped.length > 0 && (
|
||||||
|
<>
|
||||||
|
<div className="text-[8px] text-slate-600 uppercase tracking-wide pt-1 border-t border-slate-700/30">
|
||||||
|
Derniers résultats ({formatDateShort(lastPastDayGrouped[0].date)})
|
||||||
|
</div>
|
||||||
|
{lastPastDayGrouped.map(group => (
|
||||||
|
<div key={group.date} className="space-y-1 pl-0.5">
|
||||||
|
{group.events.map((ev: any, i: number) => <EcoEventRow key={i} ev={ev} showActual />)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{(ecoShowNextWeek || isCurrentWeekEmpty) && nextWeekGrouped.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="text-[8px] text-slate-600 uppercase tracking-wide pt-1 border-t border-slate-700/30">Next week</div>
|
<div className="text-[8px] text-slate-600 uppercase tracking-wide pt-1 border-t border-slate-700/30">Next week</div>
|
||||||
{nextWeekGrouped.map(group => (
|
{nextWeekGrouped.map(group => (
|
||||||
|
|||||||
Reference in New Issue
Block a user