File size: 11,909 Bytes
a1bf219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
"""
Report viewer component for displaying analysis results.
"""

from typing import Any, Dict, List, Optional, Union

import gradio as gr

from data.providers.base import DataProvider
from graph.state.trading_state import TechnicalWorkflowState
from graph.workflows.comprehensive_workflow import ComprehensiveState
from utils.cost_tracker import format_cost


def format_config_info(config: Dict[str, Any]) -> str:
    """
    Format configuration information for display.

    Args:
        config: Configuration dictionary

    Returns:
        Markdown formatted configuration info
    """
    if not config:
        return ""

    lines = []
    lines.append("### βš™οΈ Analysis Configuration")
    lines.append("")

    # Indicator parameters
    if "indicator_parameters" in config:
        params = config["indicator_parameters"]
        lines.append("**Indicator Parameters:**")
        lines.append(f"- RSI Period: {params.get('rsi_period', 14)}")
        lines.append(
            f"- MACD: Fast={params.get('macd_fast', 12)}, Slow={params.get('macd_slow', 26)}, Signal={params.get('macd_signal', 9)}"
        )
        lines.append(
            f"- Stochastic: K={params.get('stoch_k_period', 14)}, D={params.get('stoch_d_period', 3)}"
        )
        lines.append("")

    # Data providers
    if "data_providers" in config:
        providers = config["data_providers"]
        lines.append("**Data Sources:**")
        lines.append(f"- OHLC: {providers.get('ohlc_primary', 'yfinance')}")
        lines.append(
            f"- Fundamentals: {providers.get('fundamentals_primary', 'alpha_vantage')}"
        )
        lines.append("")

    return "\n".join(lines)


def format_cost_summary(cost_summary: Optional[Dict[str, Any]]) -> str:
    """
    Format cost tracking summary for display.

    Args:
        cost_summary: Cost summary dictionary from CostTracker

    Returns:
        Markdown formatted cost summary
    """
    if not cost_summary:
        return ""

    lines = []
    lines.append("### πŸ’° Analysis Cost")
    lines.append("")

    total_cost = cost_summary.get("total_cost", 0.0)
    total_tokens = cost_summary.get("total_tokens", 0)
    call_count = cost_summary.get("call_count", 0)

    lines.append(f"**Total Cost:** {format_cost(total_cost)}")
    lines.append(f"**Total Tokens:** {total_tokens:,}")
    lines.append(f"**API Calls:** {call_count}")

    if call_count > 0:
        avg_cost = cost_summary.get("average_cost_per_call", 0.0)
        lines.append(f"**Average per Call:** {format_cost(avg_cost)}")

    # Show per-agent breakdown if available
    agent_costs = cost_summary.get("agent_costs", {})
    if agent_costs:
        lines.append("")
        lines.append("<details>")
        lines.append("<summary><b>Cost by Agent</b> (click to expand)</summary>")
        lines.append("")
        for agent_name, cost in sorted(
            agent_costs.items(), key=lambda x: x[1], reverse=True
        ):
            lines.append(f"- **{agent_name}:** {format_cost(cost)}")
        lines.append("")
        lines.append("</details>")

    lines.append("")
    lines.append("---")
    lines.append("")

    return "\n".join(lines)


def get_asset_guidance(ticker: str) -> str:
    """
    Get asset-specific guidance and warnings based on asset type.

    Args:
        ticker: Asset ticker symbol

    Returns:
        Markdown formatted guidance/warning string
    """
    asset_type = DataProvider.detect_asset_type(ticker)
    asset_characteristics = DataProvider.get_asset_characteristics(asset_type)

    guidance_parts = []

    if asset_type == "crypto":
        guidance_parts.append("## πŸͺ™ Cryptocurrency Asset")
        guidance_parts.append("")
        guidance_parts.append("> **⚠️ HIGH VOLATILITY WARNING**")
        guidance_parts.append("> ")
        guidance_parts.append(
            "> - Cryptocurrencies are **extremely volatile** and can experience 10-20%+ swings in minutes"
        )
        guidance_parts.append(
            "> - Market operates **24/7 (365 days)** - no market close for risk management"
        )
        guidance_parts.append(
            "> - **Highly sentiment-driven** - news and social media have immediate impact"
        )
        guidance_parts.append(
            "> - **Regulatory risk** - government actions can cause instant severe moves"
        )
        guidance_parts.append(
            "> - **Liquidity varies** - wide spreads possible during volatility"
        )
        guidance_parts.append("> - Traditional fundamental analysis **does not apply**")
        guidance_parts.append("> ")
        guidance_parts.append(
            "> **Focus on:** Technical patterns, market sentiment, news, trading volume"
        )
        guidance_parts.append("")

    elif asset_type == "commodity":
        guidance_parts.append("## 🌾 Commodity Futures")
        guidance_parts.append("")
        guidance_parts.append("> **⚠️ FUTURES CONTRACT NOTICE**")
        guidance_parts.append("> ")
        guidance_parts.append(
            "> - Most commodity tickers represent **futures contracts** with expiration dates"
        )
        guidance_parts.append(
            "> - **Contract rollover** required - positions may need to be closed before expiry"
        )
        guidance_parts.append(
            "> - **Contango/backwardation** affects long-term holding costs"
        )
        guidance_parts.append(
            "> - **High leverage** inherent in futures - margin requirements apply"
        )
        guidance_parts.append(
            "> - **Gap risk** from overnight geopolitical/weather events"
        )
        guidance_parts.append("> - Traditional financial metrics **do not apply**")
        guidance_parts.append("> ")
        guidance_parts.append(
            "> **Focus on:** Supply/demand dynamics, inventory levels, weather, geopolitical events"
        )
        guidance_parts.append("")

    elif asset_type == "index":
        guidance_parts.append("## πŸ“ˆ Market Index")
        guidance_parts.append("")
        guidance_parts.append("> **ℹ️ INDEX TRACKING NOTICE**")
        guidance_parts.append("> ")
        guidance_parts.append(
            "> - Indices **cannot be traded directly** - use ETFs, futures, or options"
        )
        guidance_parts.append(
            "> - Represents **weighted basket** of underlying securities"
        )
        guidance_parts.append(
            "> - **Sector composition** affects behavior - tech-heavy indices more volatile"
        )
        guidance_parts.append(
            "> - **Diversification** reduces single-stock risk but limits upside"
        )
        guidance_parts.append("> - Traditional company fundamentals **do not apply**")
        guidance_parts.append("> ")
        guidance_parts.append(
            "> **Focus on:** Macro trends, sector rotation, interest rates, earnings season"
        )
        guidance_parts.append("")

    elif asset_type == "forex":
        guidance_parts.append("## πŸ’± Foreign Exchange")
        guidance_parts.append("")
        guidance_parts.append("> **⚠️ CURRENCY PAIR NOTICE**")
        guidance_parts.append("> ")
        guidance_parts.append(
            "> - Forex markets are **highly leveraged** (50:1 to 500:1 typical)"
        )
        guidance_parts.append(
            "> - Market operates **24/5** (Sunday evening to Friday evening)"
        )
        guidance_parts.append(
            "> - **Macro-driven** - central bank policy, interest rates, geopolitical events"
        )
        guidance_parts.append(
            "> - **Bid-ask spreads** vary by broker and market conditions"
        )
        guidance_parts.append("> - Traditional equity fundamentals **do not apply**")
        guidance_parts.append("> ")
        guidance_parts.append(
            "> **Focus on:** Interest rate differentials, economic data, central bank policy"
        )
        guidance_parts.append("")

    elif asset_type == "stock":
        # For stocks, just add a brief note about traditional analysis
        guidance_parts.append("## πŸ“Š Equity Stock")
        guidance_parts.append("")
        guidance_parts.append("> **ℹ️ Traditional equity analysis applies**")
        guidance_parts.append("> ")
        guidance_parts.append("> This is a publicly traded stock. Analysis includes:")
        guidance_parts.append("> - Financial fundamentals (earnings, revenue, margins)")
        guidance_parts.append("> - Technical indicators and chart patterns")
        guidance_parts.append("> - News and market sentiment")
        guidance_parts.append("> - Company-specific events and announcements")
        guidance_parts.append("")

    return "\n".join(guidance_parts)


def create_timeframe_alignment_indicator(
    alignment_score: float, trend_direction: str = "unknown"
) -> str:
    """
    Create a visual indicator for timeframe alignment.

    Args:
        alignment_score: Alignment score (0-1)
        trend_direction: Overall trend direction (bullish, bearish, mixed)

    Returns:
        Visual indicator string with emoji and description
    """
    # Determine alignment strength
    if alignment_score >= 0.75:
        strength = "Strong"
        emoji = "🟒🟒🟒"
    elif alignment_score >= 0.5:
        strength = "Moderate"
        emoji = "🟒🟒"
    elif alignment_score >= 0.25:
        strength = "Weak"
        emoji = "🟑"
    else:
        strength = "Conflicting"
        emoji = "πŸ”΄"

    # Add trend direction
    if trend_direction.lower() == "bullish":
        direction_emoji = "πŸ“ˆ"
    elif trend_direction.lower() == "bearish":
        direction_emoji = "πŸ“‰"
    else:
        direction_emoji = "↔️"

    return f"{emoji} {direction_emoji} {strength} Alignment ({alignment_score:.0%})"


def format_timeframe_context(timeframe_context: Dict[str, Any]) -> str:
    """
    Format timeframe context information for display.

    Args:
        timeframe_context: Timeframe context dict

    Returns:
        Formatted string
    """
    if not timeframe_context:
        return ""

    label = timeframe_context.get("label", "unknown")
    scope = timeframe_context.get("scope", "trading")
    hold_duration = timeframe_context.get("hold_duration", "varies")
    weight = timeframe_context.get("weight", 0.5)

    return f"**Timeframe:** {label} | **Scope:** {scope.title()} | **Expected Hold:** {hold_duration} | **Weight:** {weight:.1f}"


def create_report_viewer() -> gr.Markdown:
    """
    Create report viewer component.

    Returns:
        Gradio Markdown component for displaying reports
    """
    return gr.Markdown(
        label="Analysis Report",
        value="Analysis report will appear here...",
    )


def format_error_report(error_message: str) -> str:
    """
    Format error message as report.

    Args:
        error_message: Error message

    Returns:
        Markdown formatted error report
    """
    return f"""# ⚠️ Analysis Error

An error occurred during analysis:

```
{error_message}
```

Please check:
- Ticker symbol is valid
- Internet connection is working
- API keys are configured (if using Alpha Vantage)
- Try a different timeframe
"""


def format_progress_message(current_agent: str) -> str:
    """
    Format progress message during analysis.

    Args:
        current_agent: Currently executing agent

    Returns:
        Progress message
    """
    agent_display = current_agent.replace("_", " ").title()

    return f"""# πŸ”„ Analysis in Progress

Currently running: **{agent_display}**

Please wait while the multi-agent system analyzes the market data...

**Workflow Steps:**
1. βœ… Fetch Market Data
2. πŸ”„ Indicator Agent - Calculate technical indicators
3. ⏳ Pattern Agent - Identify chart patterns
4. ⏳ Trend Agent - Analyze trends
5. ⏳ Decision Agent - Generate recommendation
6. ⏳ Generate Charts
"""