diff --git a/backend/app/engine/__pycache__/recommender.cpython-313.pyc b/backend/app/engine/__pycache__/recommender.cpython-313.pyc index c493ec5c..f0bd9d9c 100644 Binary files a/backend/app/engine/__pycache__/recommender.cpython-313.pyc and b/backend/app/engine/__pycache__/recommender.cpython-313.pyc differ diff --git a/backend/app/engine/__pycache__/screener.cpython-313.pyc b/backend/app/engine/__pycache__/screener.cpython-313.pyc index b2f800e7..a1a743b4 100644 Binary files a/backend/app/engine/__pycache__/screener.cpython-313.pyc and b/backend/app/engine/__pycache__/screener.cpython-313.pyc differ diff --git a/backend/app/engine/recommender.py b/backend/app/engine/recommender.py index e221cbe7..4d4dfebf 100644 --- a/backend/app/engine/recommender.py +++ b/backend/app/engine/recommender.py @@ -245,9 +245,9 @@ async def _load_today_from_db() -> dict: from sqlalchemy import text import json - # 加载市场温度 + # 加载市场温度(按 trade_date 取最新交易日) result = await db.execute( - text("SELECT * FROM market_temperature ORDER BY created_at DESC LIMIT 1") + text("SELECT * FROM market_temperature ORDER BY trade_date DESC LIMIT 1") ) mt_row = result.fetchone() market_temp = None @@ -264,12 +264,14 @@ async def _load_today_from_db() -> dict: temperature=m["temperature"], ) - # 加载推荐(按 ts_code 去重,取最新一条) + # 加载推荐(取最近一个有数据的日期,按 ts_code 去重) result = await db.execute( - text("SELECT * FROM recommendations WHERE date(created_at) = :today " - "AND id IN (SELECT MAX(id) FROM recommendations WHERE date(created_at) = :today GROUP BY ts_code) " - "ORDER BY score DESC"), - {"today": today} + text("SELECT * FROM recommendations " + "WHERE date(created_at) = (SELECT date(created_at) FROM recommendations ORDER BY created_at DESC LIMIT 1) " + "AND id IN (SELECT MAX(id) FROM recommendations " + " WHERE date(created_at) = (SELECT date(created_at) FROM recommendations ORDER BY created_at DESC LIMIT 1) " + " GROUP BY ts_code) " + "ORDER BY score DESC") ) rows = result.fetchall() recommendations = [] diff --git a/backend/app/engine/screener.py b/backend/app/engine/screener.py index 7243106a..d7f80ecd 100644 --- a/backend/app/engine/screener.py +++ b/backend/app/engine/screener.py @@ -1,11 +1,16 @@ -"""趋势突破统一筛选器 +"""趋势突破统一筛选器(自上而下方案) -三阶段管道:全市场批量预筛 → 资金流过滤 → 逐股深度分析 -评分公式:趋势&时机30% + 资金流25% + 供需20% + 板块共振15% + 市场温度10% +三阶段管道: + Step 1: 板块定位 — 找到有资金流入的热门板块 (3-5个) + Step 2: 板块内选股 — 在热门板块成分股中筛出有资金流入的候选 (30-50只) + Step 3: 深度分析 — 供需 + 价格行为 + 趋势 (10-15只推荐) -自动检测是否在交易时段: - - 盘中模式:用前一日 Tushare 数据 + 腾讯实时行情混合筛选 - - 盘后模式:用当日 Tushare 完整数据筛选 +评分公式:供需关系 40% + 价格行为 35% + 趋势 25% +板块和资金流作为前置过滤条件,不参与评分。 + +数据源: + - 盘中模式:Tushare 日线 + 腾讯实时行情混合 + - 盘后模式:Tushare 当日完整数据 """ import logging @@ -47,23 +52,40 @@ async def run_screening(trade_date: str = None) -> dict: market_temp_score = market_temp.temperature - # ── 板块热度(用于板块共振评分) ── - logger.info("=== 板块热度扫描 ===") + # ── Step 1: 板块定位 ── + logger.info("=== Step 1: 板块定位 ===") all_sectors = scan_hot_sectors(trade_date) - hot_sectors = all_sectors[:settings.top_sector_count] + + # 前置过滤:只保留有资金流入 + 非末期的板块 + hot_sectors = [ + s for s in all_sectors + if s.capital_inflow > 0 and s.stage not in ("end",) + ][:settings.top_sector_count] + + if not hot_sectors: + logger.info("无合格热门板块(需要资金流入+非末期),回退到全部板块") + hot_sectors = all_sectors[:settings.top_sector_count] + + for s in hot_sectors: + logger.info(f" 目标板块: {s.sector_name} 涨幅{s.pct_change}% 资金{s.capital_inflow:.0f}万 " + f"涨停{s.limit_up_count} 阶段={s.stage}") # 盘中用实时行情更新板块涨幅和涨停数 if intraday: hot_sectors = await intraday_sector_scan(hot_sectors) - # ── 趋势突破三阶段管道 ── - logger.info("=== 趋势突破扫描 ===") - candidates = await scan_trend_breakout( - trade_date=trade_date, - market_temp=market_temp, - hot_sectors=hot_sectors, - intraday=intraday, - ) + # ── Step 2: 板块内选股 ── + logger.info("=== Step 2: 板块内选股 ===") + candidates = await _select_from_hot_sectors(hot_sectors, trade_date, intraday) + + if not candidates: + logger.info("=== Step 2 无候选,回退到全市场扫描 ===") + candidates = await scan_trend_breakout( + trade_date=trade_date, + market_temp=market_temp, + hot_sectors=hot_sectors, + intraday=intraday, + ) if not candidates: logger.info("=== 筛选完成: 0 只股票 ===") @@ -74,8 +96,9 @@ async def run_screening(trade_date: str = None) -> dict: "scan_mode": scan_mode, } - # ── 构建推荐列表 ── - recommendations = _build_trend_recommendations( + # ── Step 3: 供需 + 价格行为 + 趋势评分 ── + logger.info("=== Step 3: 深度分析 ===") + recommendations = _build_recommendations( candidates, market_temp, hot_sectors, market_temp_score, intraday, ) @@ -96,147 +119,498 @@ async def run_screening(trade_date: str = None) -> dict: } -def _build_trend_recommendations( +async def _select_from_hot_sectors( + hot_sectors: list[SectorInfo], + trade_date: str, + intraday: bool, +) -> list[dict]: + """Step 2: 从热门板块成分股中选出有资金流入的候选 + + 流程: + 1. 收集所有热门板块的成分股代码 + 2. 用 get_daily_all + get_daily_basic 过滤市值/换手率 + 3. 用 get_moneyflow_batch 过滤主力净流入 > 0 + 4. 对候选做入场信号初筛(只需满足任一信号类型) + """ + from app.data.tushare_client import tushare_client + from datetime import datetime, timedelta + import pandas as pd + + if not trade_date: + trade_date = tushare_client.get_latest_trade_date() + + # 收集热门板块成分股代码 + sector_member_codes: set[str] = set() + sector_code_map: dict[str, str] = {} # ts_code -> sector_name + for s in hot_sectors: + try: + members_df = tushare_client.get_ths_members(s.sector_code) + if not members_df.empty and "con_code" in members_df.columns: + codes = members_df["con_code"].tolist() + sector_member_codes.update(codes) + for c in codes: + sector_code_map[c] = s.sector_name + except Exception as e: + logger.warning(f"获取板块 {s.sector_name} 成分股失败: {e}") + + if not sector_member_codes: + logger.info("Step 2: 无板块成分股数据") + return [] + + logger.info(f"Step 2: 热门板块共 {len(sector_member_codes)} 只成分股") + + # 过滤市值/换手率/ST/次新 + stock_basic = tushare_client.get_stock_basic() + exclude_codes = set() + if not stock_basic.empty: + st_codes = set(stock_basic[stock_basic["name"].str.contains("ST", na=False)]["ts_code"]) + exclude_codes.update(st_codes) + cutoff = (datetime.now() - timedelta(days=settings.min_list_days)).strftime("%Y%m%d") + new_codes = set(stock_basic[stock_basic["list_date"] > cutoff]["ts_code"]) + exclude_codes.update(new_codes) + + # 行业映射 + industry_map = {} + if not stock_basic.empty: + for _, row in stock_basic.iterrows(): + industry_map[row["ts_code"]] = row.get("industry", "") + + # 用 daily_basic 过滤 + basic = tushare_client.get_daily_basic(trade_date) + if basic.empty: + logger.info("Step 2: daily_basic 无数据") + return [] + + basic["circ_mv"] = basic["circ_mv"] / 10000 # 万元 → 亿元 + + filtered_basic = basic[ + (basic["ts_code"].isin(sector_member_codes)) & + (~basic["ts_code"].isin(exclude_codes)) & + (basic["circ_mv"] >= settings.min_circ_mv) & + (basic["circ_mv"] <= settings.max_circ_mv) & + (basic["turnover_rate"] >= settings.min_turnover_rate) & + (basic["turnover_rate"] <= settings.max_turnover_rate) + ].copy() + + logger.info(f"Step 2 基本面过滤: {len(sector_member_codes)} 只 → {len(filtered_basic)} 只") + + if filtered_basic.empty: + return [] + + # 资金流过滤:主力净流入 > 0 + mf = tushare_client.get_moneyflow_batch(trade_date) + if mf.empty: + logger.info("Step 2: 资金流数据为空,跳过资金过滤") + candidate_codes = set(filtered_basic["ts_code"].tolist()) + else: + mf["main_net_inflow"] = ( + (mf["buy_elg_amount"] - mf["sell_elg_amount"]) + + (mf["buy_lg_amount"] - mf["sell_lg_amount"]) + ) + total = ( + mf["buy_elg_amount"] + mf["sell_elg_amount"] + + mf["buy_lg_amount"] + mf["sell_lg_amount"] + + mf["buy_md_amount"] + mf["sell_md_amount"] + + mf["buy_sm_amount"] + mf["sell_sm_amount"] + ) + mf["inflow_ratio"] = (mf["main_net_inflow"] / total.replace(0, float("nan")) * 100).fillna(0) + + mf_positive = mf[ + (mf["ts_code"].isin(set(filtered_basic["ts_code"]))) & + (mf["main_net_inflow"] > 0) + ].sort_values("main_net_inflow", ascending=False) + + candidate_codes = set(mf_positive["ts_code"].tolist()) + + # 构建资金流查找表 + mf_lookup = {} + for _, row in mf_positive.iterrows(): + mf_lookup[row["ts_code"]] = { + "main_net_inflow": float(row["main_net_inflow"]), + "inflow_ratio": float(row.get("inflow_ratio", 0)), + } + + logger.info(f"Step 2 资金流过滤: → {len(candidate_codes)} 只主力净流入 > 0") + + if not candidate_codes: + return [] + + # 构建候选列表 + import numpy as np + candidates = [] + for ts_code in candidate_codes: + name = "" + if not stock_basic.empty: + row = stock_basic[stock_basic["ts_code"] == ts_code] + if not row.empty: + name = row.iloc[0]["name"] + + sector_name = sector_code_map.get(ts_code, industry_map.get(ts_code, "")) + b_row = filtered_basic[filtered_basic["ts_code"] == ts_code] + turnover_rate = float(b_row.iloc[0]["turnover_rate"]) if not b_row.empty else 0 + circ_mv = float(b_row.iloc[0]["circ_mv"]) if not b_row.empty else 0 + pe = float(b_row.iloc[0]["pe"]) if not b_row.empty and pd.notna(b_row.iloc[0].get("pe")) else None + pb = float(b_row.iloc[0]["pb"]) if not b_row.empty and pd.notna(b_row.iloc[0].get("pb")) else None + volume_ratio = float(b_row.iloc[0]["volume_ratio"]) if not b_row.empty and pd.notna(b_row.iloc[0].get("volume_ratio")) else None + + try: + mf_info = mf_lookup.get(ts_code, {}) + except NameError: + mf_info = {} + + candidates.append({ + "ts_code": ts_code, + "name": name, + "sector": sector_name, + "turnover_rate": turnover_rate, + "circ_mv": circ_mv, + "pe": pe, + "pb": pb, + "volume_ratio": volume_ratio, + "main_net_inflow": mf_info.get("main_net_inflow", 0), + "inflow_ratio": mf_info.get("inflow_ratio", 0), + }) + + logger.info(f"Step 2 候选: {len(candidates)} 只") + return candidates + + +def _build_recommendations( candidates: list[dict], market_temp: MarketTemperature, hot_sectors: list[SectorInfo], market_temp_score: float = 0, intraday: bool = False, ) -> list[Recommendation]: - """从趋势突破扫描结果构建推荐列表 + """Step 3: 对候选做供需 + 价格行为 + 趋势深度分析 - 评分公式:趋势&时机30% + 资金流25% + 供需20% + 板块共振15% + 市场温度10% + 评分公式:供需关系 40% + 价格行为 35% + 趋势 25% + 板块和资金流已在前置过滤中处理。 """ + from app.data.tushare_client import tushare_client + from app.analysis.technical import add_all_indicators + from app.analysis.breakout_signals import ( + classify_entry_signal, + score_supply_demand, + analyze_volume_pattern, + EntrySignal, + ) + from app.analysis.signals import generate_signals + from app.analysis.capital_flow import _score_valuation + + # 名称和行业映射 + stock_basic = tushare_client.get_stock_basic() + name_map = {} + industry_map = {} + if not stock_basic.empty: + for _, row in stock_basic.iterrows(): + name_map[row["ts_code"]] = row["name"] + industry_map[row["ts_code"]] = row.get("industry", "") + recommendations = [] + total = len(candidates) + signal_counts = {"breakout": 0, "pullback": 0, "launch": 0, "none": 0} - for stock in candidates: - ts_code = stock["ts_code"] - name = stock["name"] - sector = stock["sector"] - entry_signal_type = stock.get("entry_signal_type", "none") - entry_signal_score = stock.get("entry_signal_score", 0) - tech_signal = stock.get("tech_signal") + for idx, stock in enumerate(candidates): + ts_code = stock.get("ts_code", "") + if not ts_code: + continue - # 各维度得分 - trend_timing_score = stock.get("trend_timing_score", 50) - supply_demand_score = stock.get("supply_demand_score", 50) - capital_score = stock.get("capital_score", 50) - position_score = stock.get("position_score", 50) - valuation_score = stock.get("valuation_score", 50) + name = stock.get("name") or name_map.get(ts_code, ts_code) + sector = stock.get("sector") or industry_map.get(ts_code, "") - # 板块共振评分 - sector_score = _score_sector_resonance(sector, hot_sectors) - sector_stage = _get_sector_stage(sector, hot_sectors) + try: + # 获取 120 日 K 线 + df = tushare_client.get_stock_daily(ts_code, 120) + if df.empty or len(df) < 30: + continue - # 综合评分(新权重) - final_score = ( - trend_timing_score * 0.30 + - capital_score * 0.25 + - supply_demand_score * 0.20 + - sector_score * 0.15 + - market_temp_score * 0.10 - ) + # 添加技术指标 + df = add_all_indicators(df) - # 风险乘数 - if tech_signal: - if tech_signal.rally_pct_5d > 20: - final_score *= 0.65 - elif tech_signal.rally_pct_5d > 15: - final_score *= 0.80 + # ── 入场信号分类 ── + entry_signal = classify_entry_signal(df) + signal_type = entry_signal["signal_type"] + if signal_type == EntrySignal.NONE: + signal_counts["none"] += 1 + continue + signal_counts[signal_type.value] += 1 - if sector_stage == "end": - final_score *= 0.70 - elif sector_stage == "late": - final_score *= 0.88 + # ── 三维度评分 ── - if market_temp_score < 30: - final_score *= 0.75 + # 1. 供需关系评分 (40%) + supply_demand_score = score_supply_demand(df) - # 入场信号高置信度奖励 - if entry_signal_score >= 80: - final_score *= 1.10 + # 2. 价格行为评分 (35%) + price_action_score = _score_price_action(df, entry_signal) - # 确定信号和等级 - level = _score_to_level(final_score) - signal = "HOLD" - if entry_signal_type != "none" and entry_signal_score >= 50 and position_score >= 30 and final_score >= 60: - signal = "BUY" + # 3. 趋势评分 (25%) + trend_score = _score_trend(df) - # 价格参考 - entry_price = None - target_price = None - stop_loss = None - if tech_signal: - entry_price = tech_signal.support_price - target_price = tech_signal.resist_price - stop_loss = tech_signal.stop_loss_price + # 综合评分 + final_score = ( + supply_demand_score * 0.40 + + price_action_score * 0.35 + + trend_score * 0.25 + ) - # 根据入场信号类型调整参考价 - details = stock.get("entry_signal_details", {}) - if entry_signal_type == "breakout" and details.get("resist_level"): - entry_price = details["resist_level"] - target_price = round(entry_price * 1.05, 2) - elif entry_signal_type == "pullback" and details.get("support_price"): - entry_price = details["support_price"] - target_price = round(entry_price * 1.05, 2) - elif entry_signal_type == "launch" and details.get("resist_level"): - entry_price = round(details["resist_level"] * 1.01, 2) - target_price = round(details["resist_level"] * 1.08, 2) + # 风险乘数 + tech_signal = generate_signals(ts_code, name) - # 生成推荐理由 - reasons = _generate_reasons(stock, tech_signal, market_temp, intraday) + if tech_signal: + if tech_signal.rally_pct_5d > 20: + final_score *= 0.65 + elif tech_signal.rally_pct_5d > 15: + final_score *= 0.80 - # 风险提示 - risk_note = _generate_risk_note(market_temp, tech_signal, stock) + # 板块末期惩罚(板块信息来自 hot_sectors) + sector_stage = _get_sector_stage(sector, hot_sectors) + if sector_stage == "end": + final_score *= 0.70 + elif sector_stage == "late": + final_score *= 0.88 - rec = Recommendation( - ts_code=ts_code, - name=name, - sector=sector, - score=round(final_score, 1), - market_temp_score=round(market_temp_score, 1), - sector_score=round(sector_score, 1), - capital_score=round(capital_score, 1), - technical_score=round(stock.get("technical_score", 50), 1), - position_score=round(position_score, 1), - valuation_score=round(valuation_score, 1), - signal=signal, - entry_price=entry_price, - target_price=target_price, - stop_loss=stop_loss, - reasons=reasons, - risk_note=risk_note, - level=level, - strategy="trend_breakout", - entry_signal_type=entry_signal_type, - ) - recommendations.append(rec) + # 市场温度风控 + if market_temp_score < 30: + final_score *= 0.75 + elif market_temp_score < 50: + final_score *= 0.88 + + # 高置信度入场信号奖励 + if entry_signal.get("signal_score", 0) >= 80: + final_score *= 1.10 + + # 估值评分(辅助参考,不参与主评分) + pe = stock.get("pe") + pb = stock.get("pb") + valuation_score = _score_valuation(pe, pb) + + # 确定信号和等级 + level = _score_to_level(final_score) + signal = "HOLD" + position_score = tech_signal.position_score if tech_signal else 50 + if (signal_type != EntrySignal.NONE + and entry_signal.get("signal_score", 0) >= 50 + and position_score >= 30 + and final_score >= 60): + signal = "BUY" + + # 价格参考 + entry_price = None + target_price = None + stop_loss = None + if tech_signal: + entry_price = tech_signal.support_price + target_price = tech_signal.resist_price + stop_loss = tech_signal.stop_loss_price + + details = entry_signal.get("details", {}) + st = signal_type.value + if st == "breakout" and details.get("resist_level"): + entry_price = details["resist_level"] + target_price = round(entry_price * 1.05, 2) + elif st == "pullback" and details.get("support_price"): + entry_price = details["support_price"] + target_price = round(entry_price * 1.05, 2) + elif st == "launch" and details.get("resist_level"): + entry_price = round(details["resist_level"] * 1.01, 2) + target_price = round(details["resist_level"] * 1.08, 2) + + # 生成推荐理由 + reasons = _generate_reasons(stock, entry_signal, tech_signal, df, intraday) + risk_note = _generate_risk_note(market_temp, tech_signal, stock) + + # 量价模式 + vol_pattern = analyze_volume_pattern(df) + + rec = Recommendation( + ts_code=ts_code, + name=name, + sector=sector, + score=round(final_score, 1), + market_temp_score=round(market_temp_score, 1), + sector_score=round(_get_sector_heat(sector, hot_sectors), 1), + capital_score=round(_score_capital_simple(stock), 1), + technical_score=round(trend_score, 1), + position_score=round(position_score, 1), + valuation_score=round(valuation_score, 1), + signal=signal, + entry_price=entry_price, + target_price=target_price, + stop_loss=stop_loss, + reasons=reasons, + risk_note=risk_note, + level=level, + strategy="trend_breakout", + entry_signal_type=signal_type.value, + ) + recommendations.append(rec) + + if len(recommendations) >= settings.top_stock_count: + break + + except Exception as e: + logger.debug(f"深度分析 {ts_code} 失败: {e}") + continue + + # 让出控制权(同步函数中无法 await,跳过) + # idx % 10 == 0 的让步在 _select_from_hot_sectors 的上层 async 函数中处理 + + logger.info( + f"Step 3 入场信号分布: " + f"突破={signal_counts['breakout']} 回踩={signal_counts['pullback']} " + f"启动={signal_counts['launch']} 无信号={signal_counts['none']} " + f"(共分析{total}只)" + ) return recommendations -def _score_sector_resonance(sector_name: str, hot_sectors: list[SectorInfo]) -> float: - """板块共振评分 (0-100)""" - for s in hot_sectors: - if s.sector_name == sector_name: - score = 40 # 在热门板块列表中 - score += s.heat_score * 0.3 # 板块热度贡献 - if s.stage == "early": - score += 30 - elif s.stage == "mid": - score += 20 - elif s.stage == "late": - score += 5 - return min(score, 100) - return 10.0 # 不在热门板块 +# ── 价格行为评分 ── -def _get_sector_score(sector_name: str, hot_sectors: list[SectorInfo]) -> float: - """获取板块在热门板块中的得分""" - for s in hot_sectors: - if s.sector_name == sector_name: - return s.heat_score - return 30.0 +def _score_price_action(df, entry_signal: dict) -> float: + """价格行为学评分 (0-100) + + 维度: + - 入场信号类型质量 (40): 突破型/回踩型/启动型各自的得分 + - K线形态强度 (30): 突破日/回踩日的K线实体占比、下影线、收盘位置 + - 支撑阻力位质量 (30): 关键价格位置的测试情况 + """ + score = 0 + last = df.iloc[-1] + details = entry_signal.get("details", {}) + signal_type = entry_signal.get("signal_type") + + # 入场信号类型质量 (40) + signal_score = entry_signal.get("signal_score", 0) + score += signal_score * 0.40 + + # K线形态强度 (30) + day_range = last["high"] - last["low"] + if day_range > 0: + # 实体占比(实体/全振幅) + body = abs(last["close"] - last["open"]) + body_ratio = body / day_range + if body_ratio > 0.7: + score += 20 # 大实体,方向明确 + elif body_ratio > 0.4: + score += 12 + elif body_ratio > 0.2: + score += 6 + + # 收盘位置(越接近高点越好) + close_position = (last["close"] - last["low"]) / day_range + if close_position > 0.8: + score += 10 # 收在上部 20% + elif close_position > 0.6: + score += 6 + elif close_position > 0.4: + score += 3 + + # 支撑阻力位质量 (30) + if signal_type and signal_type.value == "breakout": + # 突破型:阻力位被突破的力度 + breakout_pct = details.get("breakout_pct", 0) + vol_ratio = details.get("volume_ratio", 1) + if breakout_pct > 2 and vol_ratio > 2: + score += 30 # 强力突破 + elif breakout_pct > 1 and vol_ratio > 1.5: + score += 20 + elif breakout_pct > 0: + score += 10 + + elif signal_type and signal_type.value == "pullback": + # 回踩型:支撑位的精确度 + support_ma = details.get("support_ma", "") + shrink = details.get("volume_shrink_ratio", 1) + if support_ma == "MA20" and shrink < 0.6: + score += 30 # 精确回踩 MA20 且大幅缩量 + elif support_ma == "MA20": + score += 22 + elif support_ma == "MA10" and shrink < 0.6: + score += 18 + else: + score += 10 + + elif signal_type and signal_type.value == "launch": + # 启动型:整理的充分度 + range_pct = details.get("price_range_pct", 10) + shrink = details.get("volume_shrink_ratio", 1) + if range_pct < 3 and shrink < 0.4: + score += 30 # 极度缩量窄幅整理 + elif range_pct < 5 and shrink < 0.6: + score += 20 + else: + score += 10 + else: + score += 10 + + return min(score, 100) + + +# ── 趋势评分 ── + + +def _score_trend(df) -> float: + """趋势评分 (0-100) + + 维度: + - 均线排列 (40): MA5>MA10>MA20>MA60 + - 更高高点/更高低点结构 (35): 近 20 日价格结构 + - MA20 方向 (25): MA20 是否持续上行 + """ + import pandas as pd + score = 0 + last = df.iloc[-1] + + # 均线排列 (40) + ma_cols = [c for c in ["ma5", "ma10", "ma20", "ma60"] if c in df.columns] + if len(ma_cols) >= 4 and not any(pd.isna(last[c]) for c in ma_cols): + if last["ma5"] > last["ma10"] > last["ma20"] > last["ma60"]: + score += 40 # 完美多头 + elif last["ma5"] > last["ma10"] > last["ma20"]: + score += 28 + elif last["ma5"] > last["ma20"]: + score += 15 + elif "ma5" in df.columns and "ma20" in df.columns: + if not pd.isna(last["ma5"]) and not pd.isna(last["ma20"]) and last["ma5"] > last["ma20"]: + score += 15 + + # 更高高点/更高低点结构 (35) + if len(df) >= 20: + recent = df.tail(20) + # 检查高点抬升 + first_10_high = recent["high"].iloc[:10].max() + second_10_high = recent["high"].iloc[10:].max() + # 检查低点抬升 + first_10_low = recent["low"].iloc[:10].min() + second_10_low = recent["low"].iloc[10:].min() + + if second_10_high > first_10_high and second_10_low > first_10_low: + score += 35 # 既抬高点又抬低点,最健康 + elif second_10_high > first_10_high: + score += 20 # 至少高点抬升 + elif second_10_low > first_10_low: + score += 12 # 至少低点抬升 + + # MA20 方向 (25) + if "ma20" in df.columns and len(df) >= 5: + ma20_now = last["ma20"] + ma20_5d = df.iloc[-5]["ma20"] + if not pd.isna(ma20_now) and not pd.isna(ma20_5d) and ma20_5d > 0: + ma20_pct = (ma20_now - ma20_5d) / ma20_5d * 100 + if ma20_pct > 2: + score += 25 + elif ma20_pct > 1: + score += 18 + elif ma20_pct > 0: + score += 10 + + return min(score, 100) + + +# ── 辅助函数 ── def _get_sector_stage(sector_name: str, hot_sectors: list[SectorInfo]) -> str: @@ -247,6 +621,41 @@ def _get_sector_stage(sector_name: str, hot_sectors: list[SectorInfo]) -> str: return "mid" +def _get_sector_heat(sector_name: str, hot_sectors: list[SectorInfo]) -> float: + """获取板块热度得分""" + for s in hot_sectors: + if s.sector_name == sector_name: + return s.heat_score + return 30.0 + + +def _score_capital_simple(stock: dict) -> float: + """资金流简单评分(仅基于已有数据,不额外调 API)""" + main_net = stock.get("main_net_inflow", 0) or 0 + inflow_ratio = stock.get("inflow_ratio", 0) or 0 + + score = 0 + if main_net > 10000: + score += 60 + elif main_net > 5000: + score += 45 + elif main_net > 2000: + score += 30 + elif main_net > 0: + score += 15 + + if inflow_ratio > 15: + score += 40 + elif inflow_ratio > 10: + score += 30 + elif inflow_ratio > 5: + score += 20 + elif inflow_ratio > 0: + score += 10 + + return min(score, 100) + + def _score_to_level(score: float) -> str: if score >= 80: return "强烈推荐" @@ -259,38 +668,45 @@ def _score_to_level(score: float) -> str: def _generate_reasons( - stock: dict, tech: TechnicalSignal | None, - market: MarketTemperature, intraday: bool = False, + stock: dict, entry_signal: dict, tech: TechnicalSignal | None, + df, intraday: bool = False, ) -> list[str]: """生成推荐理由""" + import pandas as pd reasons = [] - entry_type = stock.get("entry_signal_type", "none") - signal_map = {"breakout": "突破型", "pullback": "回踩型", "launch": "启动型"} - entry_label = signal_map.get(entry_type, "") + signal_type = entry_signal.get("signal_type") + details = entry_signal.get("details", {}) + signal_map = {EntrySignal.BREAKOUT: "突破型", EntrySignal.PULLBACK: "回踩型", EntrySignal.LAUNCH: "启动型"} + entry_label = signal_map.get(signal_type, "") # 入场信号 - if entry_label: - details = stock.get("entry_signal_details", {}) - if entry_type == "breakout": + if entry_label and signal_type: + st = signal_type.value + if st == "breakout": breakout_pct = details.get("breakout_pct", 0) vol_ratio = details.get("volume_ratio", 0) reasons.append(f"放量突破20日阻力位(涨幅{breakout_pct:.1f}%,量比{vol_ratio:.1f}倍)") - elif entry_type == "pullback": + elif st == "pullback": support = details.get("support_ma", "") shrink = details.get("volume_shrink_ratio", 0) reasons.append(f"缩量回踩{support}支撑(量能收缩至{shrink:.0%})") - elif entry_type == "launch": + elif st == "launch": range_pct = details.get("price_range_pct", 0) shrink = details.get("volume_shrink_ratio", 0) reasons.append(f"高位缩量整理{range_pct:.1f}%后即将变盘(量缩至{shrink:.0%})") # 供需分析 - vol_trend = stock.get("volume_trend", "") - ds_ratio = stock.get("demand_supply_ratio", 1) - if ds_ratio > 1.5: - reasons.append(f"需求主导(上涨均量/下跌均量={ds_ratio:.1f})") - elif vol_trend == "expanding": - reasons.append("量能逐步放大,资金持续介入") + if len(df) >= 10: + recent = df.tail(10) + up_days = recent[recent["pct_chg"] > 0] + down_days = recent[recent["pct_chg"] <= 0] + if len(up_days) > 0 and len(down_days) > 0: + avg_up_vol = up_days["vol"].mean() + avg_down_vol = down_days["vol"].mean() + if avg_down_vol > 0: + ds_ratio = avg_up_vol / avg_down_vol + if ds_ratio > 1.5: + reasons.append(f"需求主导(上涨均量/下跌均量={ds_ratio:.1f})") # 资金流 main_net = stock.get("main_net_inflow", 0) @@ -302,19 +718,7 @@ def _generate_reasons( # 板块 sector = stock.get("sector", "") if sector: - reasons.append(f"所属板块【{sector}】") - - # 技术面 - if tech: - tech_reasons = [] - if tech.ma_bullish: - tech_reasons.append("均线多头排列") - if tech.macd_golden: - tech_reasons.append("MACD金叉") - if tech.pullback_support: - tech_reasons.append("缩量回踩支撑") - if tech_reasons: - reasons.append("技术面: " + "、".join(tech_reasons)) + reasons.append(f"所属热门板块【{sector}】") return reasons[:3] diff --git a/backend/app/llm/__pycache__/prompts.cpython-313.pyc b/backend/app/llm/__pycache__/prompts.cpython-313.pyc index bd78c2ea..986ccbc2 100644 Binary files a/backend/app/llm/__pycache__/prompts.cpython-313.pyc and b/backend/app/llm/__pycache__/prompts.cpython-313.pyc differ diff --git a/backend/app/llm/__pycache__/tool_executor.cpython-313.pyc b/backend/app/llm/__pycache__/tool_executor.cpython-313.pyc index 2234162e..38a1e683 100644 Binary files a/backend/app/llm/__pycache__/tool_executor.cpython-313.pyc and b/backend/app/llm/__pycache__/tool_executor.cpython-313.pyc differ diff --git a/backend/app/llm/__pycache__/tools.cpython-313.pyc b/backend/app/llm/__pycache__/tools.cpython-313.pyc index d64b94e2..642eefe9 100644 Binary files a/backend/app/llm/__pycache__/tools.cpython-313.pyc and b/backend/app/llm/__pycache__/tools.cpython-313.pyc differ diff --git a/backend/app/llm/prompts.py b/backend/app/llm/prompts.py index b1053d18..7df91ac6 100644 --- a/backend/app/llm/prompts.py +++ b/backend/app/llm/prompts.py @@ -42,6 +42,11 @@ CHAT_SYSTEM_PROMPT = """\ 3. 搜索股票代码 4. 基于数据给出专业的市场分析和投资建议 +重要提醒: +- 回答用户关于"今天市场怎么样"之类的问题时,必须调用 get_realtime_indices 获取实时指数数据 +- 盘中时段(9:30-15:00)必须使用实时数据,盘后时段使用当日收盘数据 +- 不要使用过时的数据,必须先调用工具获取最新数据再回答 + 回答要求: 1. 使用工具获取最新数据后再回答,不要凭空编造数据 2. 分析要结合 A 股市场特点(资金驱动、板块轮动、情绪周期) diff --git a/backend/app/llm/tool_executor.py b/backend/app/llm/tool_executor.py index 3d6b13fa..4444493c 100644 --- a/backend/app/llm/tool_executor.py +++ b/backend/app/llm/tool_executor.py @@ -33,6 +33,8 @@ async def execute_tool(name: str, arguments: dict) -> str: return await _get_stock_technical_signal(arguments["ts_code"]) elif name == "get_sector_performance": return await _get_sector_performance(arguments["sector_name"]) + elif name == "get_realtime_indices": + return await _get_realtime_indices() else: return json.dumps({"error": f"未知工具: {name}"}, ensure_ascii=False) except Exception as e: @@ -146,3 +148,41 @@ async def _get_sector_performance(sector_name: str) -> str: return json.dumps({"matched": False, "available_sectors": data}, ensure_ascii=False, default=str) data = _clean_for_json([s.model_dump() for s in matched]) return json.dumps({"matched": True, "sectors": data}, ensure_ascii=False, default=str) + + +async def _get_realtime_indices() -> str: + """获取指数实时行情数据(盘中用腾讯实时数据)""" + from app.data.tencent_client import get_index_realtime + from app.config import is_market_session + + is_trading = is_market_session() + + try: + index_data = await get_index_realtime() + if not index_data: + return json.dumps({"error": "获取指数数据失败"}, ensure_ascii=False) + + # 格式化数据 + results = [] + for ts_code, data in index_data.items(): + results.append({ + "ts_code": ts_code, + "name": data.get("name", ts_code), + "price": round(data.get("price", 0), 2), + "pct_chg": round(data.get("pct_chg", 0), 2), + "volume": data.get("volume", 0), + "amount": data.get("amount", 0), + "high": round(data.get("high", 0), 2), + "low": round(data.get("low", 0), 2), + "open": round(data.get("open", 0), 2), + "pre_close": round(data.get("pre_close", 0), 2), + }) + + return json.dumps({ + "is_realtime": is_trading, + "mode": "盘中实时" if is_trading else "盘后收盘", + "indices": results + }, ensure_ascii=False, default=str) + except Exception as e: + logger.error(f"获取实时指数失败: {e}") + return json.dumps({"error": f"获取指数数据失败: {e}"}, ensure_ascii=False) diff --git a/backend/app/llm/tools.py b/backend/app/llm/tools.py index bb240030..85247ca7 100644 --- a/backend/app/llm/tools.py +++ b/backend/app/llm/tools.py @@ -138,4 +138,16 @@ CHAT_TOOLS = [ }, }, }, + { + "type": "function", + "function": { + "name": "get_realtime_indices", + "description": "获取指数实时行情数据,包括上证指数、深证成指、创业板指的实时涨跌幅和成交数据。盘中时段返回实时数据,盘后返回当日收盘数据", + "parameters": { + "type": "object", + "properties": {}, + "required": [], + }, + }, + }, ] diff --git a/frontend/.next/app-build-manifest.json b/frontend/.next/app-build-manifest.json index 687ee4ea..1d48e9dc 100644 --- a/frontend/.next/app-build-manifest.json +++ b/frontend/.next/app-build-manifest.json @@ -20,6 +20,11 @@ "static/chunks/webpack.js", "static/chunks/main-app.js", "static/chunks/app/sectors/page.js" + ], + "/_not-found/page": [ + "static/chunks/webpack.js", + "static/chunks/main-app.js", + "static/chunks/app/_not-found/page.js" ] } } \ No newline at end of file diff --git a/frontend/.next/server/app-paths-manifest.json b/frontend/.next/server/app-paths-manifest.json index 092d16ac..1c039125 100644 --- a/frontend/.next/server/app-paths-manifest.json +++ b/frontend/.next/server/app-paths-manifest.json @@ -1,5 +1,6 @@ { + "/_not-found/page": "app/_not-found/page.js", "/page": "app/page.js", - "/sectors/page": "app/sectors/page.js", - "/recommendations/page": "app/recommendations/page.js" + "/recommendations/page": "app/recommendations/page.js", + "/sectors/page": "app/sectors/page.js" } \ No newline at end of file diff --git a/frontend/.next/server/webpack-runtime.js b/frontend/.next/server/webpack-runtime.js index 0a157f59..2854777e 100644 --- a/frontend/.next/server/webpack-runtime.js +++ b/frontend/.next/server/webpack-runtime.js @@ -125,7 +125,7 @@ /******/ /******/ /* webpack/runtime/getFullHash */ /******/ (() => { -/******/ __webpack_require__.h = () => ("b931683b67a20916") +/******/ __webpack_require__.h = () => ("37ba5b2074c7bffb") /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ diff --git a/frontend/.next/trace b/frontend/.next/trace index 0b809950..3ba30e11 100644 --- a/frontend/.next/trace +++ b/frontend/.next/trace @@ -19,3 +19,4 @@ [{"name":"client-success","duration":31,"timestamp":6463491542014,"id":1659,"parentId":3,"tags":{},"startTime":1776070438877,"traceId":"89894813de057625"},{"name":"add-entry","duration":18590,"timestamp":6463567258119,"id":1664,"parentId":1663,"tags":{"request":"next-app-loader?name=app%2Fpage&page=%2Fpage&appPaths=%2Fpage&pagePath=private-next-app-dir%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776070514593,"traceId":"89894813de057625"},{"name":"next-swc-transform","duration":18185,"timestamp":6463567314790,"id":1670,"parentId":1669,"tags":{},"startTime":1776070514650,"traceId":"89894813de057625"},{"name":"next-swc-loader","duration":18392,"timestamp":6463567314592,"id":1669,"parentId":1668,"tags":{},"startTime":1776070514650,"traceId":"89894813de057625"},{"name":"build-module-tsx","duration":64563,"timestamp":6463567283085,"id":1668,"parentId":1662,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/components/market-temp.tsx","layer":"ssr"},"startTime":1776070514618,"traceId":"89894813de057625"},{"name":"make","duration":120202,"timestamp":6463567232800,"id":1663,"parentId":1662,"tags":{},"startTime":1776070514568,"traceId":"89894813de057625"},{"name":"chunk-graph","duration":1177,"timestamp":6463567355604,"id":1672,"parentId":1671,"tags":{},"startTime":1776070514691,"traceId":"89894813de057625"},{"name":"optimize-modules","duration":4,"timestamp":6463567356795,"id":1674,"parentId":1671,"tags":{},"startTime":1776070514692,"traceId":"89894813de057625"},{"name":"optimize-chunks","duration":669,"timestamp":6463567356809,"id":1675,"parentId":1671,"tags":{},"startTime":1776070514692,"traceId":"89894813de057625"},{"name":"optimize-tree","duration":15,"timestamp":6463567357499,"id":1676,"parentId":1671,"tags":{},"startTime":1776070514693,"traceId":"89894813de057625"},{"name":"optimize-chunk-modules","duration":3,"timestamp":6463567357524,"id":1677,"parentId":1671,"tags":{},"startTime":1776070514693,"traceId":"89894813de057625"},{"name":"optimize","duration":1811,"timestamp":6463567356791,"id":1673,"parentId":1671,"tags":{},"startTime":1776070514692,"traceId":"89894813de057625"},{"name":"module-hash","duration":339,"timestamp":6463567359262,"id":1678,"parentId":1671,"tags":{},"startTime":1776070514695,"traceId":"89894813de057625"},{"name":"code-generation","duration":4700,"timestamp":6463567359614,"id":1679,"parentId":1671,"tags":{},"startTime":1776070514695,"traceId":"89894813de057625"},{"name":"hash","duration":1327,"timestamp":6463567365183,"id":1680,"parentId":1671,"tags":{},"startTime":1776070514700,"traceId":"89894813de057625"},{"name":"code-generation-jobs","duration":108,"timestamp":6463567366509,"id":1681,"parentId":1671,"tags":{},"startTime":1776070514702,"traceId":"89894813de057625"},{"name":"module-assets","duration":55,"timestamp":6463567366608,"id":1682,"parentId":1671,"tags":{},"startTime":1776070514702,"traceId":"89894813de057625"},{"name":"create-chunk-assets","duration":2031,"timestamp":6463567366669,"id":1683,"parentId":1671,"tags":{},"startTime":1776070514702,"traceId":"89894813de057625"},{"name":"seal","duration":14701,"timestamp":6463567355012,"id":1671,"parentId":1662,"tags":{},"startTime":1776070514690,"traceId":"89894813de057625"},{"name":"webpack-compilation","duration":147729,"timestamp":6463567222614,"id":1662,"parentId":1660,"tags":{"name":"server"},"startTime":1776070514558,"traceId":"89894813de057625"},{"name":"emit","duration":6240,"timestamp":6463567370371,"id":1684,"parentId":1660,"tags":{},"startTime":1776070514706,"traceId":"89894813de057625"},{"name":"webpack-invalidated-server","duration":184278,"timestamp":6463567192803,"id":1660,"parentId":3,"tags":{"trigger":"src/components/market-temp.tsx"},"startTime":1776070514528,"traceId":"89894813de057625"},{"name":"add-entry","duration":6066,"timestamp":6463567381604,"id":1687,"parentId":1686,"tags":{"request":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/compiled/@next/react-refresh-utils/dist/runtime.js"},"startTime":1776070514717,"traceId":"89894813de057625"},{"name":"next-swc-transform","duration":3749,"timestamp":6463567386691,"id":1695,"parentId":1694,"tags":{},"startTime":1776070514722,"traceId":"89894813de057625"},{"name":"next-swc-loader","duration":3820,"timestamp":6463567386627,"id":1694,"parentId":1693,"tags":{},"startTime":1776070514722,"traceId":"89894813de057625"},{"name":"build-module-tsx","duration":10305,"timestamp":6463567386384,"id":1693,"parentId":1685,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/components/market-temp.tsx","layer":"app-pages-browser"},"startTime":1776070514722,"traceId":"89894813de057625"},{"name":"add-entry","duration":16831,"timestamp":6463567381642,"id":1691,"parentId":1686,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fapp-router.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fclient-page.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Ferror-boundary.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Flayout-router.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fnot-found-boundary.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Frender-from-template-context.js%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070514717,"traceId":"89894813de057625"},{"name":"add-entry","duration":16840,"timestamp":6463567381637,"id":1689,"parentId":1686,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070514717,"traceId":"89894813de057625"},{"name":"add-entry","duration":16876,"timestamp":6463567381633,"id":1688,"parentId":1686,"tags":{"request":"./node_modules/next/dist/client/app-next-dev.js"},"startTime":1776070514717,"traceId":"89894813de057625"},{"name":"read-resource","duration":9136,"timestamp":6463567389382,"id":1697,"parentId":1696,"tags":{},"startTime":1776070514725,"traceId":"89894813de057625"},{"name":"postcss-process","duration":64249,"timestamp":6463567398561,"id":1699,"parentId":1698,"tags":{},"startTime":1776070514734,"traceId":"89894813de057625"},{"name":"postcss-loader","duration":65413,"timestamp":6463567398535,"id":1698,"parentId":1696,"tags":{},"startTime":1776070514734,"traceId":"89894813de057625"},{"name":"css-loader","duration":17453,"timestamp":6463567463984,"id":1700,"parentId":1696,"tags":{"astUsed":"true"},"startTime":1776070514799,"traceId":"89894813de057625"},{"name":"build-module-css","duration":93258,"timestamp":6463567389288,"id":1696,"parentId":1692,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/app/globals.css.webpack[javascript/auto]!=!/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[12].use[2]!/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[14].oneOf[12].use[3]!/Users/aaron/source_code/astock-agent/frontend/src/app/globals.css","layer":null},"startTime":1776070514725,"traceId":"89894813de057625"},{"name":"build-module-css","duration":103118,"timestamp":6463567385282,"id":1692,"parentId":1685,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/app/globals.css","layer":"app-pages-browser"},"startTime":1776070514721,"traceId":"89894813de057625"},{"name":"build-module","duration":24,"timestamp":6463567488510,"id":1701,"parentId":1692,"tags":{},"startTime":1776070514824,"traceId":"89894813de057625"},{"name":"add-entry","duration":106912,"timestamp":6463567381640,"id":1690,"parentId":1686,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext-themes%2Fdist%2Findex.mjs%22%2C%22ids%22%3A%5B%22ThemeProvider%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fglobals.css%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fauth-guard.tsx%22%2C%22ids%22%3A%5B%22AuthGuard%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fnav.tsx%22%2C%22ids%22%3A%5B%22SidebarNav%22%2C%22MobileBottomNav%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fuser-menu.tsx%22%2C%22ids%22%3A%5B%22UserMenu%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fhooks%2Fuse-auth.tsx%22%2C%22ids%22%3A%5B%22AuthProvider%22%5D%7D&server=false!"},"startTime":1776070514717,"traceId":"89894813de057625"},{"name":"make","duration":108785,"timestamp":6463567379780,"id":1686,"parentId":1685,"tags":{},"startTime":1776070514715,"traceId":"89894813de057625"},{"name":"chunk-graph","duration":818,"timestamp":6463567536641,"id":1703,"parentId":1702,"tags":{},"startTime":1776070514872,"traceId":"89894813de057625"},{"name":"optimize-modules","duration":4,"timestamp":6463567537483,"id":1705,"parentId":1702,"tags":{},"startTime":1776070514873,"traceId":"89894813de057625"},{"name":"optimize-chunks","duration":45,"timestamp":6463567537497,"id":1706,"parentId":1702,"tags":{},"startTime":1776070514873,"traceId":"89894813de057625"},{"name":"optimize-tree","duration":5,"timestamp":6463567537556,"id":1707,"parentId":1702,"tags":{},"startTime":1776070514873,"traceId":"89894813de057625"},{"name":"optimize-chunk-modules","duration":2,"timestamp":6463567537568,"id":1708,"parentId":1702,"tags":{},"startTime":1776070514873,"traceId":"89894813de057625"},{"name":"optimize","duration":753,"timestamp":6463567537477,"id":1704,"parentId":1702,"tags":{},"startTime":1776070514873,"traceId":"89894813de057625"},{"name":"module-hash","duration":303,"timestamp":6463567538776,"id":1709,"parentId":1702,"tags":{},"startTime":1776070514874,"traceId":"89894813de057625"},{"name":"code-generation","duration":1111,"timestamp":6463567539085,"id":1710,"parentId":1702,"tags":{},"startTime":1776070514874,"traceId":"89894813de057625"},{"name":"hash","duration":1923,"timestamp":6463567541763,"id":1711,"parentId":1702,"tags":{},"startTime":1776070514877,"traceId":"89894813de057625"},{"name":"code-generation-jobs","duration":75,"timestamp":6463567543685,"id":1712,"parentId":1702,"tags":{},"startTime":1776070514879,"traceId":"89894813de057625"},{"name":"module-assets","duration":36,"timestamp":6463567543753,"id":1713,"parentId":1702,"tags":{},"startTime":1776070514879,"traceId":"89894813de057625"},{"name":"create-chunk-assets","duration":2845,"timestamp":6463567543792,"id":1714,"parentId":1702,"tags":{},"startTime":1776070514879,"traceId":"89894813de057625"},{"name":"NextJsBuildManifest-generateClientManifest","duration":72,"timestamp":6463567547602,"id":1716,"parentId":1685,"tags":{},"startTime":1776070514883,"traceId":"89894813de057625"},{"name":"NextJsBuildManifest-createassets","duration":133,"timestamp":6463567547545,"id":1715,"parentId":1685,"tags":{},"startTime":1776070514883,"traceId":"89894813de057625"},{"name":"seal","duration":12694,"timestamp":6463567535975,"id":1702,"parentId":1685,"tags":{},"startTime":1776070514871,"traceId":"89894813de057625"},{"name":"webpack-compilation","duration":169320,"timestamp":6463567379369,"id":1685,"parentId":1661,"tags":{"name":"client"},"startTime":1776070514715,"traceId":"89894813de057625"},{"name":"emit","duration":3724,"timestamp":6463567548714,"id":1717,"parentId":1661,"tags":{},"startTime":1776070514884,"traceId":"89894813de057625"},{"name":"webpack-invalidated-client","duration":359667,"timestamp":6463567194032,"id":1661,"parentId":3,"tags":{"trigger":"src/components/market-temp.tsx"},"startTime":1776070514529,"traceId":"89894813de057625"}] [{"name":"client-success","duration":24,"timestamp":6463567565533,"id":1718,"parentId":3,"tags":{},"startTime":1776070514901,"traceId":"89894813de057625"},{"name":"handle-request","duration":46645,"timestamp":6463567584576,"id":1719,"tags":{"url":"/?_rsc=1p60s","isTurbopack":false},"startTime":1776070514920,"traceId":"89894813de057625"},{"name":"memory-usage","duration":2,"timestamp":6463567631342,"id":1720,"parentId":1719,"tags":{"url":"/?_rsc=1p60s","memory.rss":"270925824","memory.heapUsed":"182629352","memory.heapTotal":"230932480"},"startTime":1776070514967,"traceId":"89894813de057625"},{"name":"client-hmr-latency","duration":446000,"timestamp":6463567196675,"id":1721,"parentId":3,"tags":{"updatedModules":["[project]/src/app/globals.css"],"page":"/","isPageHidden":false},"startTime":1776070514983,"traceId":"89894813de057625"},{"name":"add-entry","duration":11796,"timestamp":6463645731098,"id":1728,"parentId":1726,"tags":{"request":"next-app-loader?name=app%2Fpage&page=%2Fpage&appPaths=%2Fpage&pagePath=private-next-app-dir%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776070593066,"traceId":"89894813de057625"},{"name":"build-module","duration":13379,"timestamp":6463645738972,"id":1729,"parentId":1727,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Frecommendations%2Fpage&page=%2Frecommendations%2Fpage&appPaths=%2Frecommendations%2Fpage&pagePath=private-next-app-dir%2Frecommendations%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!","layer":"rsc"},"startTime":1776070593074,"traceId":"89894813de057625"},{"name":"next-swc-transform","duration":8077,"timestamp":6463645757509,"id":1732,"parentId":1731,"tags":{},"startTime":1776070593093,"traceId":"89894813de057625"},{"name":"next-swc-loader","duration":8221,"timestamp":6463645757373,"id":1731,"parentId":1730,"tags":{},"startTime":1776070593093,"traceId":"89894813de057625"},{"name":"build-module-tsx","duration":9495,"timestamp":6463645757142,"id":1730,"parentId":1729,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/app/recommendations/page.tsx","layer":"rsc"},"startTime":1776070593092,"traceId":"89894813de057625"},{"name":"add-entry","duration":39170,"timestamp":6463645730681,"id":1727,"parentId":1726,"tags":{"request":"next-app-loader?name=app%2Frecommendations%2Fpage&page=%2Frecommendations%2Fpage&appPaths=%2Frecommendations%2Fpage&pagePath=private-next-app-dir%2Frecommendations%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776070593066,"traceId":"89894813de057625"},{"name":"build-module","duration":677,"timestamp":6463645780303,"id":1740,"parentId":1725,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Frecommendations%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=true!","layer":"ssr"},"startTime":1776070593116,"traceId":"89894813de057625"},{"name":"next-swc-transform","duration":5507,"timestamp":6463645785286,"id":1743,"parentId":1742,"tags":{},"startTime":1776070593121,"traceId":"89894813de057625"},{"name":"next-swc-loader","duration":5592,"timestamp":6463645785207,"id":1742,"parentId":1741,"tags":{},"startTime":1776070593121,"traceId":"89894813de057625"},{"name":"build-module-tsx","duration":11423,"timestamp":6463645784914,"id":1741,"parentId":1740,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/app/recommendations/page.tsx","layer":"ssr"},"startTime":1776070593120,"traceId":"89894813de057625"},{"name":"make","duration":75507,"timestamp":6463645729038,"id":1726,"parentId":1725,"tags":{},"startTime":1776070593064,"traceId":"89894813de057625"},{"name":"chunk-graph","duration":1113,"timestamp":6463645806378,"id":1745,"parentId":1744,"tags":{},"startTime":1776070593142,"traceId":"89894813de057625"},{"name":"optimize-modules","duration":10,"timestamp":6463645807526,"id":1747,"parentId":1744,"tags":{},"startTime":1776070593143,"traceId":"89894813de057625"},{"name":"optimize-chunks","duration":1748,"timestamp":6463645807556,"id":1748,"parentId":1744,"tags":{},"startTime":1776070593143,"traceId":"89894813de057625"},{"name":"optimize-tree","duration":6,"timestamp":6463645809320,"id":1749,"parentId":1744,"tags":{},"startTime":1776070593145,"traceId":"89894813de057625"},{"name":"optimize-chunk-modules","duration":3,"timestamp":6463645809336,"id":1750,"parentId":1744,"tags":{},"startTime":1776070593145,"traceId":"89894813de057625"},{"name":"optimize","duration":2731,"timestamp":6463645807518,"id":1746,"parentId":1744,"tags":{},"startTime":1776070593143,"traceId":"89894813de057625"},{"name":"module-hash","duration":314,"timestamp":6463645811019,"id":1751,"parentId":1744,"tags":{},"startTime":1776070593146,"traceId":"89894813de057625"},{"name":"code-generation","duration":1369,"timestamp":6463645811341,"id":1752,"parentId":1744,"tags":{},"startTime":1776070593147,"traceId":"89894813de057625"},{"name":"hash","duration":680,"timestamp":6463645813424,"id":1753,"parentId":1744,"tags":{},"startTime":1776070593149,"traceId":"89894813de057625"},{"name":"code-generation-jobs","duration":42,"timestamp":6463645814104,"id":1754,"parentId":1744,"tags":{},"startTime":1776070593149,"traceId":"89894813de057625"},{"name":"module-assets","duration":28,"timestamp":6463645814141,"id":1755,"parentId":1744,"tags":{},"startTime":1776070593149,"traceId":"89894813de057625"},{"name":"create-chunk-assets","duration":2555,"timestamp":6463645814170,"id":1756,"parentId":1744,"tags":{},"startTime":1776070593149,"traceId":"89894813de057625"},{"name":"seal","duration":12182,"timestamp":6463645805812,"id":1744,"parentId":1725,"tags":{},"startTime":1776070593141,"traceId":"89894813de057625"},{"name":"webpack-compilation","duration":90687,"timestamp":6463645728145,"id":1725,"parentId":1723,"tags":{"name":"server"},"startTime":1776070593063,"traceId":"89894813de057625"},{"name":"emit","duration":3124,"timestamp":6463645818851,"id":1757,"parentId":1723,"tags":{},"startTime":1776070593154,"traceId":"89894813de057625"},{"name":"webpack-invalidated-server","duration":98654,"timestamp":6463645723667,"id":1723,"parentId":3,"tags":{"trigger":"manual"},"startTime":1776070593059,"traceId":"89894813de057625"},{"name":"add-entry","duration":16359,"timestamp":6463645826441,"id":1760,"parentId":1759,"tags":{"request":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/compiled/@next/react-refresh-utils/dist/runtime.js"},"startTime":1776070593162,"traceId":"89894813de057625"},{"name":"add-entry","duration":17572,"timestamp":6463645826471,"id":1762,"parentId":1759,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070593162,"traceId":"89894813de057625"},{"name":"add-entry","duration":21201,"timestamp":6463645826481,"id":1764,"parentId":1759,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fapp-router.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fclient-page.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Ferror-boundary.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Flayout-router.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fnot-found-boundary.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Frender-from-template-context.js%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070593162,"traceId":"89894813de057625"},{"name":"add-entry","duration":27507,"timestamp":6463645826467,"id":1761,"parentId":1759,"tags":{"request":"./node_modules/next/dist/client/app-next-dev.js"},"startTime":1776070593162,"traceId":"89894813de057625"},{"name":"add-entry","duration":27506,"timestamp":6463645826473,"id":1763,"parentId":1759,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext-themes%2Fdist%2Findex.mjs%22%2C%22ids%22%3A%5B%22ThemeProvider%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fglobals.css%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fauth-guard.tsx%22%2C%22ids%22%3A%5B%22AuthGuard%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fnav.tsx%22%2C%22ids%22%3A%5B%22SidebarNav%22%2C%22MobileBottomNav%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fuser-menu.tsx%22%2C%22ids%22%3A%5B%22UserMenu%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fhooks%2Fuse-auth.tsx%22%2C%22ids%22%3A%5B%22AuthProvider%22%5D%7D&server=false!"},"startTime":1776070593162,"traceId":"89894813de057625"},{"name":"build-module","duration":2700,"timestamp":6463645877968,"id":1766,"parentId":1765,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Frecommendations%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!","layer":"app-pages-browser"},"startTime":1776070593213,"traceId":"89894813de057625"},{"name":"next-swc-transform","duration":13098,"timestamp":6463645881748,"id":1769,"parentId":1768,"tags":{},"startTime":1776070593217,"traceId":"89894813de057625"},{"name":"next-swc-loader","duration":13189,"timestamp":6463645881667,"id":1768,"parentId":1767,"tags":{},"startTime":1776070593217,"traceId":"89894813de057625"},{"name":"build-module-tsx","duration":20774,"timestamp":6463645881442,"id":1767,"parentId":1766,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/app/recommendations/page.tsx","layer":"app-pages-browser"},"startTime":1776070593217,"traceId":"89894813de057625"},{"name":"add-entry","duration":92570,"timestamp":6463645826484,"id":1765,"parentId":1759,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Frecommendations%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070593162,"traceId":"89894813de057625"},{"name":"make","duration":95391,"timestamp":6463645823703,"id":1759,"parentId":1758,"tags":{},"startTime":1776070593159,"traceId":"89894813de057625"},{"name":"chunk-graph","duration":2710,"timestamp":6463645922609,"id":1771,"parentId":1770,"tags":{},"startTime":1776070593258,"traceId":"89894813de057625"},{"name":"optimize-modules","duration":4,"timestamp":6463645925343,"id":1773,"parentId":1770,"tags":{},"startTime":1776070593261,"traceId":"89894813de057625"},{"name":"optimize-chunks","duration":49,"timestamp":6463645925360,"id":1774,"parentId":1770,"tags":{},"startTime":1776070593261,"traceId":"89894813de057625"},{"name":"optimize-tree","duration":5,"timestamp":6463645925423,"id":1775,"parentId":1770,"tags":{},"startTime":1776070593261,"traceId":"89894813de057625"},{"name":"optimize-chunk-modules","duration":3,"timestamp":6463645925440,"id":1776,"parentId":1770,"tags":{},"startTime":1776070593261,"traceId":"89894813de057625"},{"name":"optimize","duration":1577,"timestamp":6463645925335,"id":1772,"parentId":1770,"tags":{},"startTime":1776070593261,"traceId":"89894813de057625"},{"name":"module-hash","duration":721,"timestamp":6463645928124,"id":1777,"parentId":1770,"tags":{},"startTime":1776070593263,"traceId":"89894813de057625"},{"name":"code-generation","duration":1768,"timestamp":6463645928878,"id":1778,"parentId":1770,"tags":{},"startTime":1776070593264,"traceId":"89894813de057625"},{"name":"hash","duration":4005,"timestamp":6463645933130,"id":1779,"parentId":1770,"tags":{},"startTime":1776070593268,"traceId":"89894813de057625"},{"name":"code-generation-jobs","duration":164,"timestamp":6463645937134,"id":1780,"parentId":1770,"tags":{},"startTime":1776070593272,"traceId":"89894813de057625"},{"name":"module-assets","duration":44,"timestamp":6463645937287,"id":1781,"parentId":1770,"tags":{},"startTime":1776070593273,"traceId":"89894813de057625"},{"name":"create-chunk-assets","duration":4248,"timestamp":6463645937334,"id":1782,"parentId":1770,"tags":{},"startTime":1776070593273,"traceId":"89894813de057625"},{"name":"NextJsBuildManifest-generateClientManifest","duration":98,"timestamp":6463645942352,"id":1784,"parentId":1758,"tags":{},"startTime":1776070593278,"traceId":"89894813de057625"},{"name":"NextJsBuildManifest-createassets","duration":270,"timestamp":6463645942293,"id":1783,"parentId":1758,"tags":{},"startTime":1776070593278,"traceId":"89894813de057625"},{"name":"seal","duration":23697,"timestamp":6463645919922,"id":1770,"parentId":1758,"tags":{},"startTime":1776070593255,"traceId":"89894813de057625"},{"name":"webpack-compilation","duration":120130,"timestamp":6463645823510,"id":1758,"parentId":1739,"tags":{"name":"client"},"startTime":1776070593159,"traceId":"89894813de057625"},{"name":"emit","duration":17838,"timestamp":6463645943655,"id":1785,"parentId":1739,"tags":{},"startTime":1776070593279,"traceId":"89894813de057625"},{"name":"compile-path","duration":238323,"timestamp":6463645723748,"id":1724,"tags":{"trigger":"/recommendations","isTurbopack":false},"startTime":1776070593059,"traceId":"89894813de057625"},{"name":"webpack-invalidated-client","duration":191072,"timestamp":6463645771281,"id":1739,"parentId":3,"tags":{"trigger":"manual"},"startTime":1776070593107,"traceId":"89894813de057625"}] [{"name":"client-success","duration":9,"timestamp":6463645965129,"id":1786,"parentId":3,"tags":{},"startTime":1776070593300,"traceId":"89894813de057625"},{"name":"handle-request","duration":302489,"timestamp":6463645711244,"id":1722,"tags":{"url":"/recommendations?_rsc=19zvn","isTurbopack":false},"startTime":1776070593047,"traceId":"89894813de057625"},{"name":"memory-usage","duration":1,"timestamp":6463646013781,"id":1787,"parentId":1722,"tags":{"url":"/recommendations?_rsc=19zvn","memory.rss":"276774912","memory.heapUsed":"189029680","memory.heapTotal":"232685568"},"startTime":1776070593349,"traceId":"89894813de057625"},{"name":"client-hmr-latency","duration":246000,"timestamp":6463645771331,"id":1788,"parentId":3,"tags":{"updatedModules":[],"page":"/","isPageHidden":false},"startTime":1776070593355,"traceId":"89894813de057625"},{"name":"handle-request","duration":10611,"timestamp":6463646020065,"id":1789,"tags":{"url":"/recommendations?_rsc=1808t","isTurbopack":false},"startTime":1776070593355,"traceId":"89894813de057625"},{"name":"memory-usage","duration":6,"timestamp":6463646030862,"id":1790,"parentId":1789,"tags":{"url":"/recommendations?_rsc=1808t","memory.rss":"276856832","memory.heapUsed":"190297496","memory.heapTotal":"232685568"},"startTime":1776070593366,"traceId":"89894813de057625"},{"name":"add-entry","duration":14485,"timestamp":6463650195214,"id":1798,"parentId":1795,"tags":{"request":"next-app-loader?name=app%2Frecommendations%2Fpage&page=%2Frecommendations%2Fpage&appPaths=%2Frecommendations%2Fpage&pagePath=private-next-app-dir%2Frecommendations%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776070597531,"traceId":"89894813de057625"},{"name":"add-entry","duration":27113,"timestamp":6463650194980,"id":1796,"parentId":1795,"tags":{"request":"next-app-loader?name=app%2Fpage&page=%2Fpage&appPaths=%2Fpage&pagePath=private-next-app-dir%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776070597530,"traceId":"89894813de057625"},{"name":"build-module","duration":24892,"timestamp":6463650207972,"id":1799,"parentId":1797,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Fsectors%2Fpage&page=%2Fsectors%2Fpage&appPaths=%2Fsectors%2Fpage&pagePath=private-next-app-dir%2Fsectors%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!","layer":"rsc"},"startTime":1776070597543,"traceId":"89894813de057625"},{"name":"next-swc-transform","duration":2054,"timestamp":6463650236064,"id":1802,"parentId":1801,"tags":{},"startTime":1776070597571,"traceId":"89894813de057625"},{"name":"next-swc-loader","duration":2172,"timestamp":6463650235952,"id":1801,"parentId":1800,"tags":{},"startTime":1776070597571,"traceId":"89894813de057625"},{"name":"build-module-tsx","duration":2861,"timestamp":6463650235687,"id":1800,"parentId":1799,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/app/sectors/page.tsx","layer":"rsc"},"startTime":1776070597571,"traceId":"89894813de057625"},{"name":"add-entry","duration":44633,"timestamp":6463650195209,"id":1797,"parentId":1795,"tags":{"request":"next-app-loader?name=app%2Fsectors%2Fpage&page=%2Fsectors%2Fpage&appPaths=%2Fsectors%2Fpage&pagePath=private-next-app-dir%2Fsectors%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776070597531,"traceId":"89894813de057625"},{"name":"build-module","duration":648,"timestamp":6463650247474,"id":1813,"parentId":1794,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fsectors%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=true!","layer":"ssr"},"startTime":1776070597583,"traceId":"89894813de057625"},{"name":"next-swc-transform","duration":5759,"timestamp":6463650251260,"id":1816,"parentId":1815,"tags":{},"startTime":1776070597587,"traceId":"89894813de057625"},{"name":"next-swc-loader","duration":5843,"timestamp":6463650251185,"id":1815,"parentId":1814,"tags":{},"startTime":1776070597587,"traceId":"89894813de057625"},{"name":"build-module-tsx","duration":13601,"timestamp":6463650250925,"id":1814,"parentId":1813,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/app/sectors/page.tsx","layer":"ssr"},"startTime":1776070597586,"traceId":"89894813de057625"},{"name":"make","duration":80165,"timestamp":6463650192398,"id":1795,"parentId":1794,"tags":{},"startTime":1776070597528,"traceId":"89894813de057625"},{"name":"chunk-graph","duration":1456,"timestamp":6463650274536,"id":1818,"parentId":1817,"tags":{},"startTime":1776070597610,"traceId":"89894813de057625"},{"name":"optimize-modules","duration":5,"timestamp":6463650276010,"id":1820,"parentId":1817,"tags":{},"startTime":1776070597611,"traceId":"89894813de057625"},{"name":"optimize-chunks","duration":1314,"timestamp":6463650276026,"id":1821,"parentId":1817,"tags":{},"startTime":1776070597611,"traceId":"89894813de057625"},{"name":"optimize-tree","duration":12,"timestamp":6463650277356,"id":1822,"parentId":1817,"tags":{},"startTime":1776070597613,"traceId":"89894813de057625"},{"name":"optimize-chunk-modules","duration":3,"timestamp":6463650277380,"id":1823,"parentId":1817,"tags":{},"startTime":1776070597613,"traceId":"89894813de057625"},{"name":"optimize","duration":2739,"timestamp":6463650276004,"id":1819,"parentId":1817,"tags":{},"startTime":1776070597611,"traceId":"89894813de057625"},{"name":"module-hash","duration":425,"timestamp":6463650279569,"id":1824,"parentId":1817,"tags":{},"startTime":1776070597615,"traceId":"89894813de057625"},{"name":"code-generation","duration":1821,"timestamp":6463650280006,"id":1825,"parentId":1817,"tags":{},"startTime":1776070597615,"traceId":"89894813de057625"},{"name":"hash","duration":3235,"timestamp":6463650282756,"id":1826,"parentId":1817,"tags":{},"startTime":1776070597618,"traceId":"89894813de057625"},{"name":"code-generation-jobs","duration":70,"timestamp":6463650285991,"id":1827,"parentId":1817,"tags":{},"startTime":1776070597621,"traceId":"89894813de057625"},{"name":"module-assets","duration":41,"timestamp":6463650286051,"id":1828,"parentId":1817,"tags":{},"startTime":1776070597621,"traceId":"89894813de057625"},{"name":"create-chunk-assets","duration":1889,"timestamp":6463650286099,"id":1829,"parentId":1817,"tags":{},"startTime":1776070597621,"traceId":"89894813de057625"},{"name":"seal","duration":15512,"timestamp":6463650273847,"id":1817,"parentId":1794,"tags":{},"startTime":1776070597609,"traceId":"89894813de057625"},{"name":"webpack-compilation","duration":101964,"timestamp":6463650191136,"id":1794,"parentId":1792,"tags":{"name":"server"},"startTime":1776070597526,"traceId":"89894813de057625"},{"name":"emit","duration":2462,"timestamp":6463650293134,"id":1830,"parentId":1792,"tags":{},"startTime":1776070597628,"traceId":"89894813de057625"},{"name":"webpack-invalidated-server","duration":110209,"timestamp":6463650185671,"id":1792,"parentId":3,"tags":{"trigger":"manual"},"startTime":1776070597521,"traceId":"89894813de057625"},{"name":"build-module","duration":674,"timestamp":6463650303286,"id":1840,"parentId":1839,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fsectors%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!","layer":"app-pages-browser"},"startTime":1776070597639,"traceId":"89894813de057625"},{"name":"add-entry","duration":3947,"timestamp":6463650300761,"id":1838,"parentId":1832,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Frecommendations%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070597636,"traceId":"89894813de057625"},{"name":"add-entry","duration":4608,"timestamp":6463650300717,"id":1833,"parentId":1832,"tags":{"request":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/compiled/@next/react-refresh-utils/dist/runtime.js"},"startTime":1776070597636,"traceId":"89894813de057625"},{"name":"add-entry","duration":5718,"timestamp":6463650300755,"id":1835,"parentId":1832,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070597636,"traceId":"89894813de057625"},{"name":"add-entry","duration":7426,"timestamp":6463650300759,"id":1837,"parentId":1832,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fapp-router.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fclient-page.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Ferror-boundary.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Flayout-router.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fnot-found-boundary.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Frender-from-template-context.js%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070597636,"traceId":"89894813de057625"},{"name":"add-entry","duration":7490,"timestamp":6463650300751,"id":1834,"parentId":1832,"tags":{"request":"./node_modules/next/dist/client/app-next-dev.js"},"startTime":1776070597636,"traceId":"89894813de057625"},{"name":"add-entry","duration":7489,"timestamp":6463650300757,"id":1836,"parentId":1832,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext-themes%2Fdist%2Findex.mjs%22%2C%22ids%22%3A%5B%22ThemeProvider%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fglobals.css%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fauth-guard.tsx%22%2C%22ids%22%3A%5B%22AuthGuard%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fnav.tsx%22%2C%22ids%22%3A%5B%22SidebarNav%22%2C%22MobileBottomNav%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fuser-menu.tsx%22%2C%22ids%22%3A%5B%22UserMenu%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fhooks%2Fuse-auth.tsx%22%2C%22ids%22%3A%5B%22AuthProvider%22%5D%7D&server=false!"},"startTime":1776070597636,"traceId":"89894813de057625"},{"name":"next-swc-transform","duration":3836,"timestamp":6463650307630,"id":1843,"parentId":1842,"tags":{},"startTime":1776070597643,"traceId":"89894813de057625"},{"name":"next-swc-loader","duration":3904,"timestamp":6463650307564,"id":1842,"parentId":1841,"tags":{},"startTime":1776070597643,"traceId":"89894813de057625"},{"name":"build-module-tsx","duration":7188,"timestamp":6463650307275,"id":1841,"parentId":1840,"tags":{"name":"/Users/aaron/source_code/astock-agent/frontend/src/app/sectors/page.tsx","layer":"app-pages-browser"},"startTime":1776070597643,"traceId":"89894813de057625"},{"name":"add-entry","duration":18586,"timestamp":6463650300763,"id":1839,"parentId":1832,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fsectors%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776070597636,"traceId":"89894813de057625"},{"name":"make","duration":21869,"timestamp":6463650297506,"id":1832,"parentId":1831,"tags":{},"startTime":1776070597633,"traceId":"89894813de057625"},{"name":"chunk-graph","duration":695,"timestamp":6463650320768,"id":1845,"parentId":1844,"tags":{},"startTime":1776070597656,"traceId":"89894813de057625"},{"name":"optimize-modules","duration":2,"timestamp":6463650321473,"id":1847,"parentId":1844,"tags":{},"startTime":1776070597657,"traceId":"89894813de057625"},{"name":"optimize-chunks","duration":21,"timestamp":6463650321484,"id":1848,"parentId":1844,"tags":{},"startTime":1776070597657,"traceId":"89894813de057625"},{"name":"optimize-tree","duration":2,"timestamp":6463650321513,"id":1849,"parentId":1844,"tags":{},"startTime":1776070597657,"traceId":"89894813de057625"},{"name":"optimize-chunk-modules","duration":2,"timestamp":6463650321524,"id":1850,"parentId":1844,"tags":{},"startTime":1776070597657,"traceId":"89894813de057625"},{"name":"optimize","duration":528,"timestamp":6463650321469,"id":1846,"parentId":1844,"tags":{},"startTime":1776070597657,"traceId":"89894813de057625"},{"name":"module-hash","duration":276,"timestamp":6463650322535,"id":1851,"parentId":1844,"tags":{},"startTime":1776070597658,"traceId":"89894813de057625"},{"name":"code-generation","duration":1136,"timestamp":6463650322816,"id":1852,"parentId":1844,"tags":{},"startTime":1776070597658,"traceId":"89894813de057625"},{"name":"hash","duration":1850,"timestamp":6463650324834,"id":1853,"parentId":1844,"tags":{},"startTime":1776070597660,"traceId":"89894813de057625"},{"name":"code-generation-jobs","duration":56,"timestamp":6463650326683,"id":1854,"parentId":1844,"tags":{},"startTime":1776070597662,"traceId":"89894813de057625"},{"name":"module-assets","duration":25,"timestamp":6463650326736,"id":1855,"parentId":1844,"tags":{},"startTime":1776070597662,"traceId":"89894813de057625"},{"name":"create-chunk-assets","duration":1545,"timestamp":6463650326763,"id":1856,"parentId":1844,"tags":{},"startTime":1776070597662,"traceId":"89894813de057625"},{"name":"NextJsBuildManifest-generateClientManifest","duration":84,"timestamp":6463650328875,"id":1858,"parentId":1831,"tags":{},"startTime":1776070597664,"traceId":"89894813de057625"},{"name":"NextJsBuildManifest-createassets","duration":134,"timestamp":6463650328828,"id":1857,"parentId":1831,"tags":{},"startTime":1776070597664,"traceId":"89894813de057625"},{"name":"seal","duration":9563,"timestamp":6463650320125,"id":1844,"parentId":1831,"tags":{},"startTime":1776070597655,"traceId":"89894813de057625"},{"name":"webpack-compilation","duration":32462,"timestamp":6463650297245,"id":1831,"parentId":1812,"tags":{"name":"client"},"startTime":1776070597633,"traceId":"89894813de057625"},{"name":"emit","duration":4593,"timestamp":6463650329717,"id":1859,"parentId":1812,"tags":{},"startTime":1776070597665,"traceId":"89894813de057625"},{"name":"compile-path","duration":148984,"timestamp":6463650185807,"id":1793,"tags":{"trigger":"/sectors","isTurbopack":false},"startTime":1776070597521,"traceId":"89894813de057625"},{"name":"webpack-invalidated-client","duration":93831,"timestamp":6463650241307,"id":1812,"parentId":3,"tags":{"trigger":"manual"},"startTime":1776070597577,"traceId":"89894813de057625"}] +[{"name":"client-success","duration":1,"timestamp":6463650336291,"id":1860,"parentId":3,"tags":{},"startTime":1776070597672,"traceId":"89894813de057625"},{"name":"handle-request","duration":174462,"timestamp":6463650176982,"id":1791,"tags":{"url":"/sectors?_rsc=17yb1","isTurbopack":false},"startTime":1776070597512,"traceId":"89894813de057625"},{"name":"memory-usage","duration":1,"timestamp":6463650351484,"id":1861,"parentId":1791,"tags":{"url":"/sectors?_rsc=17yb1","memory.rss":"246808576","memory.heapUsed":"202610472","memory.heapTotal":"237928448"},"startTime":1776070597687,"traceId":"89894813de057625"},{"name":"client-hmr-latency","duration":121000,"timestamp":6463650241848,"id":1863,"parentId":3,"tags":{"updatedModules":[],"page":"/recommendations","isPageHidden":false},"startTime":1776070597698,"traceId":"89894813de057625"},{"name":"handle-request","duration":48050,"timestamp":6463650360495,"id":1862,"tags":{"url":"/sectors?_rsc=165d9","isTurbopack":false},"startTime":1776070597696,"traceId":"89894813de057625"},{"name":"memory-usage","duration":1,"timestamp":6463650408591,"id":1864,"parentId":1862,"tags":{"url":"/sectors?_rsc=165d9","memory.rss":"247119872","memory.heapUsed":"203961360","memory.heapTotal":"237928448"},"startTime":1776070597744,"traceId":"89894813de057625"},{"name":"handle-request","duration":357028,"timestamp":6465423399730,"id":1865,"tags":{"url":"/sectors","isTurbopack":false},"startTime":1776072370736,"traceId":"89894813de057625"},{"name":"memory-usage","duration":1,"timestamp":6465423757890,"id":1866,"parentId":1865,"tags":{"url":"/sectors","memory.rss":"105775104","memory.heapUsed":"178173064","memory.heapTotal":"184565760"},"startTime":1776072371094,"traceId":"89894813de057625"},{"name":"add-entry","duration":60279,"timestamp":6465423984939,"id":1878,"parentId":1872,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Frecommendations%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776072371321,"traceId":"89894813de057625"},{"name":"add-entry","duration":60392,"timestamp":6465423984943,"id":1880,"parentId":1872,"tags":{"request":"next-client-pages-loader?absolutePagePath=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fnot-found-error.js&page=%2F_not-found%2Fpage!"},"startTime":1776072371321,"traceId":"89894813de057625"},{"name":"add-entry","duration":65756,"timestamp":6465423983744,"id":1873,"parentId":1872,"tags":{"request":"/Users/aaron/source_code/astock-agent/frontend/node_modules/next/dist/compiled/@next/react-refresh-utils/dist/runtime.js"},"startTime":1776072371320,"traceId":"89894813de057625"},{"name":"add-entry","duration":65930,"timestamp":6465423984941,"id":1879,"parentId":1872,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fsectors%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776072371321,"traceId":"89894813de057625"},{"name":"add-entry","duration":69061,"timestamp":6465423984930,"id":1875,"parentId":1872,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fpage.tsx%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776072371321,"traceId":"89894813de057625"},{"name":"add-entry","duration":73453,"timestamp":6465423984936,"id":1877,"parentId":1872,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fapp-router.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fclient-page.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Ferror-boundary.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Flayout-router.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fnot-found-boundary.js%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Frender-from-template-context.js%22%2C%22ids%22%3A%5B%5D%7D&server=false!"},"startTime":1776072371321,"traceId":"89894813de057625"},{"name":"add-entry","duration":73524,"timestamp":6465423984933,"id":1876,"parentId":1872,"tags":{"request":"next-flight-client-entry-loader?modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fnode_modules%2Fnext-themes%2Fdist%2Findex.mjs%22%2C%22ids%22%3A%5B%22ThemeProvider%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp%2Fglobals.css%22%2C%22ids%22%3A%5B%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fauth-guard.tsx%22%2C%22ids%22%3A%5B%22AuthGuard%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fnav.tsx%22%2C%22ids%22%3A%5B%22SidebarNav%22%2C%22MobileBottomNav%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fcomponents%2Fuser-menu.tsx%22%2C%22ids%22%3A%5B%22UserMenu%22%5D%7D&modules=%7B%22request%22%3A%22%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fhooks%2Fuse-auth.tsx%22%2C%22ids%22%3A%5B%22AuthProvider%22%5D%7D&server=false!"},"startTime":1776072371321,"traceId":"89894813de057625"},{"name":"add-entry","duration":74845,"timestamp":6465423984916,"id":1874,"parentId":1872,"tags":{"request":"./node_modules/next/dist/client/app-next-dev.js"},"startTime":1776072371321,"traceId":"89894813de057625"},{"name":"make","duration":81249,"timestamp":6465423978546,"id":1872,"parentId":1871,"tags":{},"startTime":1776072371315,"traceId":"89894813de057625"},{"name":"chunk-graph","duration":4142,"timestamp":6465424102803,"id":1882,"parentId":1881,"tags":{},"startTime":1776072371439,"traceId":"89894813de057625"},{"name":"optimize-modules","duration":5,"timestamp":6465424106973,"id":1884,"parentId":1881,"tags":{},"startTime":1776072371443,"traceId":"89894813de057625"},{"name":"optimize-chunks","duration":78,"timestamp":6465424106992,"id":1885,"parentId":1881,"tags":{},"startTime":1776072371443,"traceId":"89894813de057625"},{"name":"optimize-tree","duration":11,"timestamp":6465424107089,"id":1886,"parentId":1881,"tags":{},"startTime":1776072371443,"traceId":"89894813de057625"},{"name":"optimize-chunk-modules","duration":4,"timestamp":6465424107111,"id":1887,"parentId":1881,"tags":{},"startTime":1776072371443,"traceId":"89894813de057625"},{"name":"optimize","duration":953,"timestamp":6465424106966,"id":1883,"parentId":1881,"tags":{},"startTime":1776072371443,"traceId":"89894813de057625"},{"name":"module-hash","duration":1073,"timestamp":6465424108721,"id":1888,"parentId":1881,"tags":{},"startTime":1776072371445,"traceId":"89894813de057625"},{"name":"code-generation","duration":5180,"timestamp":6465424109807,"id":1889,"parentId":1881,"tags":{},"startTime":1776072371446,"traceId":"89894813de057625"},{"name":"hash","duration":4746,"timestamp":6465424117176,"id":1890,"parentId":1881,"tags":{},"startTime":1776072371453,"traceId":"89894813de057625"},{"name":"code-generation-jobs","duration":113,"timestamp":6465424121922,"id":1891,"parentId":1881,"tags":{},"startTime":1776072371458,"traceId":"89894813de057625"},{"name":"module-assets","duration":48,"timestamp":6465424122028,"id":1892,"parentId":1881,"tags":{},"startTime":1776072371458,"traceId":"89894813de057625"},{"name":"create-chunk-assets","duration":3023,"timestamp":6465424122079,"id":1893,"parentId":1881,"tags":{},"startTime":1776072371458,"traceId":"89894813de057625"},{"name":"NextJsBuildManifest-generateClientManifest","duration":78,"timestamp":6465424125840,"id":1895,"parentId":1871,"tags":{},"startTime":1776072371462,"traceId":"89894813de057625"},{"name":"NextJsBuildManifest-createassets","duration":122,"timestamp":6465424125799,"id":1894,"parentId":1871,"tags":{},"startTime":1776072371462,"traceId":"89894813de057625"},{"name":"seal","duration":25246,"timestamp":6465424101986,"id":1881,"parentId":1871,"tags":{},"startTime":1776072371438,"traceId":"89894813de057625"},{"name":"webpack-compilation","duration":153095,"timestamp":6465423974163,"id":1871,"parentId":1868,"tags":{"name":"client"},"startTime":1776072371310,"traceId":"89894813de057625"},{"name":"emit","duration":7662,"timestamp":6465424127275,"id":1896,"parentId":1868,"tags":{},"startTime":1776072371464,"traceId":"89894813de057625"},{"name":"webpack-invalidated-client","duration":177038,"timestamp":6465423958410,"id":1868,"parentId":3,"tags":{"trigger":"manual"},"startTime":1776072371295,"traceId":"89894813de057625"},{"name":"add-entry","duration":17455,"timestamp":6465424149371,"id":1901,"parentId":1898,"tags":{"request":"next-app-loader?name=app%2Frecommendations%2Fpage&page=%2Frecommendations%2Fpage&appPaths=%2Frecommendations%2Fpage&pagePath=private-next-app-dir%2Frecommendations%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776072371486,"traceId":"89894813de057625"},{"name":"add-entry","duration":17463,"timestamp":6465424149374,"id":1902,"parentId":1898,"tags":{"request":"next-app-loader?name=app%2Fsectors%2Fpage&page=%2Fsectors%2Fpage&appPaths=%2Fsectors%2Fpage&pagePath=private-next-app-dir%2Fsectors%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776072371486,"traceId":"89894813de057625"},{"name":"add-entry","duration":17716,"timestamp":6465424149332,"id":1899,"parentId":1898,"tags":{"request":"next-app-loader?name=app%2F_not-found%2Fpage&page=%2F_not-found%2Fpage&appPaths=&pagePath=..%2Fnode_modules%2Fnext%2Fdist%2Fclient%2Fcomponents%2Fnot-found-error.js&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776072371486,"traceId":"89894813de057625"},{"name":"add-entry","duration":17914,"timestamp":6465424149367,"id":1900,"parentId":1898,"tags":{"request":"next-app-loader?name=app%2Fpage&page=%2Fpage&appPaths=%2Fpage&pagePath=private-next-app-dir%2Fpage.tsx&appDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Faaron%2Fsource_code%2Fastock-agent%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!"},"startTime":1776072371486,"traceId":"89894813de057625"},{"name":"make","duration":40653,"timestamp":6465424140149,"id":1898,"parentId":1897,"tags":{},"startTime":1776072371476,"traceId":"89894813de057625"},{"name":"chunk-graph","duration":1045,"timestamp":6465424182091,"id":1915,"parentId":1914,"tags":{},"startTime":1776072371518,"traceId":"89894813de057625"},{"name":"optimize-modules","duration":2,"timestamp":6465424183146,"id":1917,"parentId":1914,"tags":{},"startTime":1776072371519,"traceId":"89894813de057625"},{"name":"optimize-chunks","duration":1306,"timestamp":6465424183157,"id":1918,"parentId":1914,"tags":{},"startTime":1776072371519,"traceId":"89894813de057625"},{"name":"optimize-tree","duration":3,"timestamp":6465424184473,"id":1919,"parentId":1914,"tags":{},"startTime":1776072371521,"traceId":"89894813de057625"},{"name":"optimize-chunk-modules","duration":3,"timestamp":6465424184486,"id":1920,"parentId":1914,"tags":{},"startTime":1776072371521,"traceId":"89894813de057625"},{"name":"optimize","duration":1888,"timestamp":6465424183141,"id":1916,"parentId":1914,"tags":{},"startTime":1776072371519,"traceId":"89894813de057625"},{"name":"module-hash","duration":213,"timestamp":6465424185568,"id":1921,"parentId":1914,"tags":{},"startTime":1776072371522,"traceId":"89894813de057625"},{"name":"code-generation","duration":1196,"timestamp":6465424185787,"id":1922,"parentId":1914,"tags":{},"startTime":1776072371522,"traceId":"89894813de057625"},{"name":"hash","duration":1121,"timestamp":6465424187908,"id":1923,"parentId":1914,"tags":{},"startTime":1776072371524,"traceId":"89894813de057625"},{"name":"code-generation-jobs","duration":55,"timestamp":6465424189028,"id":1924,"parentId":1914,"tags":{},"startTime":1776072371525,"traceId":"89894813de057625"},{"name":"module-assets","duration":68,"timestamp":6465424189079,"id":1925,"parentId":1914,"tags":{},"startTime":1776072371525,"traceId":"89894813de057625"},{"name":"create-chunk-assets","duration":159,"timestamp":6465424189150,"id":1926,"parentId":1914,"tags":{},"startTime":1776072371525,"traceId":"89894813de057625"},{"name":"seal","duration":9520,"timestamp":6465424181620,"id":1914,"parentId":1897,"tags":{},"startTime":1776072371518,"traceId":"89894813de057625"},{"name":"webpack-compilation","duration":53151,"timestamp":6465424139452,"id":1897,"parentId":1870,"tags":{"name":"server"},"startTime":1776072371476,"traceId":"89894813de057625"},{"name":"emit","duration":2461,"timestamp":6465424192618,"id":1927,"parentId":1870,"tags":{},"startTime":1776072371529,"traceId":"89894813de057625"},{"name":"compile-path","duration":236858,"timestamp":6465423958555,"id":1869,"tags":{"trigger":"/_not-found","isTurbopack":false},"startTime":1776072371295,"traceId":"89894813de057625"},{"name":"webpack-invalidated-server","duration":236514,"timestamp":6465423959548,"id":1870,"parentId":3,"tags":{"trigger":"manual"},"startTime":1776072371296,"traceId":"89894813de057625"}]