Update app.py
Browse files
app.py
CHANGED
|
@@ -54,7 +54,6 @@ def calculate_basic_indicators(data: pd.DataFrame) -> pd.DataFrame:
|
|
| 54 |
|
| 55 |
return df.ffill().bfill()
|
| 56 |
|
| 57 |
-
# Also update the system prompt in generate_analysis_prompt to ensure structured output:
|
| 58 |
@pxt.udf
|
| 59 |
def generate_analysis_prompt(data: str, analysis_type: str) -> list[dict]:
|
| 60 |
"""Generate a structured prompt for AI analysis"""
|
|
@@ -112,7 +111,6 @@ def parse_analysis_response(response: str) -> Dict[str, str]:
|
|
| 112 |
for line in response.split('\n'):
|
| 113 |
line = line.strip()
|
| 114 |
|
| 115 |
-
# Check if this line is a section header (now handling markdown formatting)
|
| 116 |
matched_section = None
|
| 117 |
for section in sections.keys():
|
| 118 |
# Remove asterisks and check for exact match
|
|
@@ -133,11 +131,9 @@ def parse_analysis_response(response: str) -> Dict[str, str]:
|
|
| 133 |
if cleaned_content: # Only add non-empty lines
|
| 134 |
buffer.append(cleaned_content)
|
| 135 |
|
| 136 |
-
# Save the last section
|
| 137 |
if current_section and buffer:
|
| 138 |
sections[current_section] = '\n'.join(buffer).strip()
|
| 139 |
|
| 140 |
-
# Clean up sections and provide meaningful defaults
|
| 141 |
section_messages = {
|
| 142 |
'SUMMARY': 'Market analysis summary not available',
|
| 143 |
'TECHNICAL ANALYSIS': 'Technical analysis not available',
|
|
@@ -147,7 +143,6 @@ def parse_analysis_response(response: str) -> Dict[str, str]:
|
|
| 147 |
'RECOMMENDATION': 'Investment recommendation not available'
|
| 148 |
}
|
| 149 |
|
| 150 |
-
# Only use default messages if section is truly empty
|
| 151 |
for key in sections:
|
| 152 |
if sections[key] is None or not sections[key].strip():
|
| 153 |
sections[key] = section_messages[key]
|
|
@@ -163,7 +158,6 @@ def create_visualization(data: pd.DataFrame, technical_depth: str) -> go.Figure:
|
|
| 163 |
subplot_titles=('Price & Moving Averages', 'Volume', 'RSI' if technical_depth == 'advanced' else None)
|
| 164 |
)
|
| 165 |
|
| 166 |
-
# Price candlesticks with improved styling
|
| 167 |
fig.add_trace(
|
| 168 |
go.Candlestick(
|
| 169 |
x=data.index,
|
|
@@ -178,7 +172,6 @@ def create_visualization(data: pd.DataFrame, technical_depth: str) -> go.Figure:
|
|
| 178 |
row=1, col=1
|
| 179 |
)
|
| 180 |
|
| 181 |
-
# Moving averages with distinct colors
|
| 182 |
colors = {'MA20': '#1E88E5', 'MA50': '#FFC107', 'MA200': '#7B1FA2'}
|
| 183 |
for ma, color in colors.items():
|
| 184 |
fig.add_trace(
|
|
@@ -191,7 +184,6 @@ def create_visualization(data: pd.DataFrame, technical_depth: str) -> go.Figure:
|
|
| 191 |
row=1, col=1
|
| 192 |
)
|
| 193 |
|
| 194 |
-
# Volume with color based on price change
|
| 195 |
colors = ['#26A69A' if close >= open_price else '#EF5350'
|
| 196 |
for close, open_price in zip(data['Close'].values, data['Open'].values)]
|
| 197 |
fig.add_trace(
|
|
@@ -215,7 +207,6 @@ def create_visualization(data: pd.DataFrame, technical_depth: str) -> go.Figure:
|
|
| 215 |
row=3, col=1
|
| 216 |
)
|
| 217 |
|
| 218 |
-
# Add RSI reference lines
|
| 219 |
fig.add_hline(y=70, line_dash="dash", line_color="red", row=3, col=1)
|
| 220 |
fig.add_hline(y=30, line_dash="dash", line_color="green", row=3, col=1)
|
| 221 |
|
|
@@ -232,7 +223,6 @@ def create_visualization(data: pd.DataFrame, technical_depth: str) -> go.Figure:
|
|
| 232 |
)
|
| 233 |
)
|
| 234 |
|
| 235 |
-
# Update y-axes labels
|
| 236 |
fig.update_yaxes(title_text="Price", row=1, col=1)
|
| 237 |
fig.update_yaxes(title_text="Volume", row=2, col=1)
|
| 238 |
if technical_depth == 'advanced':
|
|
@@ -281,7 +271,6 @@ def process_outputs(ticker_symbol, analysis_type, time_horizon, risk_tolerance,
|
|
| 281 |
max_tokens=1000
|
| 282 |
)
|
| 283 |
|
| 284 |
-
# Process the analysis with better error handling
|
| 285 |
try:
|
| 286 |
analysis_text = data_table.select(
|
| 287 |
analysis=data_table.analysis.choices[0].message.content
|
|
@@ -310,7 +299,6 @@ def process_outputs(ticker_symbol, analysis_type, time_horizon, risk_tolerance,
|
|
| 310 |
parsed_analysis = parse_analysis_response("")
|
| 311 |
raw_llm_output = f"Error processing analysis: {str(analysis_error)}"
|
| 312 |
|
| 313 |
-
# Prepare market stats with proper number formatting
|
| 314 |
try:
|
| 315 |
current_price = float(technical_data['Close'].iloc[-1])
|
| 316 |
previous_price = float(technical_data['Close'].iloc[-2])
|
|
@@ -327,7 +315,6 @@ def process_outputs(ticker_symbol, analysis_type, time_horizon, risk_tolerance,
|
|
| 327 |
'RSI': f"{rsi:.2f}"
|
| 328 |
}
|
| 329 |
|
| 330 |
-
# Add timestamp to technical data
|
| 331 |
technical_data_with_time = technical_data.reset_index()
|
| 332 |
technical_data_with_time['Date'] = technical_data_with_time['Date'].dt.strftime('%Y-%m-%d %H:%M:%S')
|
| 333 |
|
|
@@ -379,7 +366,6 @@ def create_interface() -> gr.Blocks:
|
|
| 379 |
"""
|
| 380 |
)
|
| 381 |
|
| 382 |
-
# Information Accordions
|
| 383 |
with gr.Row():
|
| 384 |
with gr.Column():
|
| 385 |
with gr.Accordion("π― What does it do?", open=False):
|
|
@@ -428,7 +414,7 @@ def create_interface() -> gr.Blocks:
|
|
| 428 |
)
|
| 429 |
|
| 430 |
with gr.Row():
|
| 431 |
-
# Left sidebar for inputs
|
| 432 |
with gr.Column(scale=1):
|
| 433 |
with gr.Row():
|
| 434 |
gr.Markdown("### π Analysis Parameters")
|
|
@@ -516,13 +502,12 @@ def create_interface() -> gr.Blocks:
|
|
| 516 |
with gr.Row():
|
| 517 |
plot_output = gr.Plot()
|
| 518 |
|
| 519 |
-
# AI Analysis section
|
| 520 |
with gr.Row():
|
| 521 |
with gr.Column(scale=2):
|
| 522 |
with gr.Row():
|
| 523 |
gr.Markdown("### π€ AI Analysis")
|
| 524 |
|
| 525 |
-
# Summary at the top
|
| 526 |
with gr.Row():
|
| 527 |
summary = gr.Textbox(
|
| 528 |
label="Executive Summary",
|
|
@@ -531,7 +516,6 @@ def create_interface() -> gr.Blocks:
|
|
| 531 |
show_label=True
|
| 532 |
)
|
| 533 |
|
| 534 |
-
# Main analysis sections
|
| 535 |
with gr.Row():
|
| 536 |
with gr.Column(scale=1):
|
| 537 |
tech_analysis = gr.Textbox(
|
|
@@ -561,7 +545,6 @@ def create_interface() -> gr.Blocks:
|
|
| 561 |
show_label=True
|
| 562 |
)
|
| 563 |
|
| 564 |
-
# Recommendation at the bottom
|
| 565 |
with gr.Row():
|
| 566 |
recommendation = gr.Textbox(
|
| 567 |
label="Investment Recommendation",
|
|
@@ -570,7 +553,6 @@ def create_interface() -> gr.Blocks:
|
|
| 570 |
show_label=True
|
| 571 |
)
|
| 572 |
|
| 573 |
-
# Examples section at the bottom
|
| 574 |
gr.Examples(
|
| 575 |
examples=[
|
| 576 |
["AAPL", "comprehensive", "medium", "moderate", "balanced", "advanced"],
|
|
|
|
| 54 |
|
| 55 |
return df.ffill().bfill()
|
| 56 |
|
|
|
|
| 57 |
@pxt.udf
|
| 58 |
def generate_analysis_prompt(data: str, analysis_type: str) -> list[dict]:
|
| 59 |
"""Generate a structured prompt for AI analysis"""
|
|
|
|
| 111 |
for line in response.split('\n'):
|
| 112 |
line = line.strip()
|
| 113 |
|
|
|
|
| 114 |
matched_section = None
|
| 115 |
for section in sections.keys():
|
| 116 |
# Remove asterisks and check for exact match
|
|
|
|
| 131 |
if cleaned_content: # Only add non-empty lines
|
| 132 |
buffer.append(cleaned_content)
|
| 133 |
|
|
|
|
| 134 |
if current_section and buffer:
|
| 135 |
sections[current_section] = '\n'.join(buffer).strip()
|
| 136 |
|
|
|
|
| 137 |
section_messages = {
|
| 138 |
'SUMMARY': 'Market analysis summary not available',
|
| 139 |
'TECHNICAL ANALYSIS': 'Technical analysis not available',
|
|
|
|
| 143 |
'RECOMMENDATION': 'Investment recommendation not available'
|
| 144 |
}
|
| 145 |
|
|
|
|
| 146 |
for key in sections:
|
| 147 |
if sections[key] is None or not sections[key].strip():
|
| 148 |
sections[key] = section_messages[key]
|
|
|
|
| 158 |
subplot_titles=('Price & Moving Averages', 'Volume', 'RSI' if technical_depth == 'advanced' else None)
|
| 159 |
)
|
| 160 |
|
|
|
|
| 161 |
fig.add_trace(
|
| 162 |
go.Candlestick(
|
| 163 |
x=data.index,
|
|
|
|
| 172 |
row=1, col=1
|
| 173 |
)
|
| 174 |
|
|
|
|
| 175 |
colors = {'MA20': '#1E88E5', 'MA50': '#FFC107', 'MA200': '#7B1FA2'}
|
| 176 |
for ma, color in colors.items():
|
| 177 |
fig.add_trace(
|
|
|
|
| 184 |
row=1, col=1
|
| 185 |
)
|
| 186 |
|
|
|
|
| 187 |
colors = ['#26A69A' if close >= open_price else '#EF5350'
|
| 188 |
for close, open_price in zip(data['Close'].values, data['Open'].values)]
|
| 189 |
fig.add_trace(
|
|
|
|
| 207 |
row=3, col=1
|
| 208 |
)
|
| 209 |
|
|
|
|
| 210 |
fig.add_hline(y=70, line_dash="dash", line_color="red", row=3, col=1)
|
| 211 |
fig.add_hline(y=30, line_dash="dash", line_color="green", row=3, col=1)
|
| 212 |
|
|
|
|
| 223 |
)
|
| 224 |
)
|
| 225 |
|
|
|
|
| 226 |
fig.update_yaxes(title_text="Price", row=1, col=1)
|
| 227 |
fig.update_yaxes(title_text="Volume", row=2, col=1)
|
| 228 |
if technical_depth == 'advanced':
|
|
|
|
| 271 |
max_tokens=1000
|
| 272 |
)
|
| 273 |
|
|
|
|
| 274 |
try:
|
| 275 |
analysis_text = data_table.select(
|
| 276 |
analysis=data_table.analysis.choices[0].message.content
|
|
|
|
| 299 |
parsed_analysis = parse_analysis_response("")
|
| 300 |
raw_llm_output = f"Error processing analysis: {str(analysis_error)}"
|
| 301 |
|
|
|
|
| 302 |
try:
|
| 303 |
current_price = float(technical_data['Close'].iloc[-1])
|
| 304 |
previous_price = float(technical_data['Close'].iloc[-2])
|
|
|
|
| 315 |
'RSI': f"{rsi:.2f}"
|
| 316 |
}
|
| 317 |
|
|
|
|
| 318 |
technical_data_with_time = technical_data.reset_index()
|
| 319 |
technical_data_with_time['Date'] = technical_data_with_time['Date'].dt.strftime('%Y-%m-%d %H:%M:%S')
|
| 320 |
|
|
|
|
| 366 |
"""
|
| 367 |
)
|
| 368 |
|
|
|
|
| 369 |
with gr.Row():
|
| 370 |
with gr.Column():
|
| 371 |
with gr.Accordion("π― What does it do?", open=False):
|
|
|
|
| 414 |
)
|
| 415 |
|
| 416 |
with gr.Row():
|
| 417 |
+
# Left sidebar for inputs
|
| 418 |
with gr.Column(scale=1):
|
| 419 |
with gr.Row():
|
| 420 |
gr.Markdown("### π Analysis Parameters")
|
|
|
|
| 502 |
with gr.Row():
|
| 503 |
plot_output = gr.Plot()
|
| 504 |
|
| 505 |
+
# AI Analysis section
|
| 506 |
with gr.Row():
|
| 507 |
with gr.Column(scale=2):
|
| 508 |
with gr.Row():
|
| 509 |
gr.Markdown("### π€ AI Analysis")
|
| 510 |
|
|
|
|
| 511 |
with gr.Row():
|
| 512 |
summary = gr.Textbox(
|
| 513 |
label="Executive Summary",
|
|
|
|
| 516 |
show_label=True
|
| 517 |
)
|
| 518 |
|
|
|
|
| 519 |
with gr.Row():
|
| 520 |
with gr.Column(scale=1):
|
| 521 |
tech_analysis = gr.Textbox(
|
|
|
|
| 545 |
show_label=True
|
| 546 |
)
|
| 547 |
|
|
|
|
| 548 |
with gr.Row():
|
| 549 |
recommendation = gr.Textbox(
|
| 550 |
label="Investment Recommendation",
|
|
|
|
| 553 |
show_label=True
|
| 554 |
)
|
| 555 |
|
|
|
|
| 556 |
gr.Examples(
|
| 557 |
examples=[
|
| 558 |
["AAPL", "comprehensive", "medium", "moderate", "balanced", "advanced"],
|