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"],
|