trr / app.py
aidevhund's picture
Update app.py
cc6796a verified
raw
history blame
10.8 kB
import gradio as gr
import openai
import os
import base64
from functools import lru_cache
from PIL import Image
import cv2
import numpy as np
import datetime
import uuid
import requests
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Image as RLImage, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.lib import colors
# OpenAI ve GitHub Konfigürasyonları
openai.api_key = os.getenv("OPENAI_API_KEY")
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
REPO_OWNER = os.getenv("GITHUB_REPO_OWNER")
REPO_NAME = os.getenv("GITHUB_REPO_NAME")
# Sabitler
ANALYSIS_MODEL = "gpt-4o"
MAX_TOKENS = 4096
PDF_DIR = "reports"
# Persona Tanımları
PERSONAS = {
"Aggressive Trader": {
"description": "High-risk, short-term gains, leverages volatile market movements.",
"prompt": "Focus on high-risk strategies, short-term gains, and leverage opportunities. Suggest aggressive entry and exit points.",
"color": colors.red
},
"Conservative Trader": {
"description": "Low-risk, long-term investments, prioritizes capital preservation.",
"prompt": "Focus on low-risk strategies, long-term investments, and capital preservation. Suggest safe entry points and strict stop-loss levels.",
"color": colors.blue
},
"Neutral Trader": {
"description": "Balanced approach, combines short and long-term strategies.",
"prompt": "Focus on balanced strategies, combining short and long-term opportunities. Suggest moderate risk levels and trend-following approaches.",
"color": colors.green
},
"Reactive Trader": {
"description": "Quick decisions based on market news and social media trends.",
"prompt": "Focus on quick decision-making, momentum trading, and reacting to market news. Suggest strategies based on current trends and FOMO opportunities.",
"color": colors.orange
},
"Systematic Trader": {
"description": "Algorithm-based, rule-driven, and emotionless trading.",
"prompt": "Focus on algorithmic strategies, backtested rules, and quantitative analysis. Suggest data-driven entry and exit points.",
"color": colors.purple
}
}
# Sistem Prompt'u
SYSTEM_PROMPT = """Professional Crypto Technical Analyst:
1. Identify all technical patterns in the chart
2. Determine key support/resistance levels
3. Analyze volume and momentum indicators
4. Calculate risk/reward ratios
5. Provide clear trading recommendations
6. Include specific price targets
7. Assess market sentiment
8. Evaluate trend strength
9. Identify potential breakout/breakdown levels
10. Provide time-based projections"""
class ChartAnalyzer:
def __init__(self):
self.last_analysis = ""
os.makedirs(PDF_DIR, exist_ok=True)
def validate_image(self, image_path: str) -> bool:
try:
with Image.open(image_path) as img:
img.verify()
img = cv2.imread(image_path)
if img is None:
return False
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
return np.sum(edges) >= 1000
except Exception:
return False
def optimize_image(self, image_path: str) -> str:
try:
img = Image.open(image_path)
original_width, original_height = img.size
max_size = 1024
if original_width > max_size or original_height > max_size:
ratio = min(max_size/original_width, max_size/original_height)
new_size = (int(original_width * ratio), int(original_height * ratio))
img = img.resize(new_size, Image.LANCZOS)
unique_id = uuid.uuid4().hex
optimized_path = f"{PDF_DIR}/optimized_chart_{unique_id}.png"
img.save(optimized_path, "PNG", optimize=True, quality=85)
return optimized_path
except Exception as e:
print(f"Image optimization error: {str(e)}")
return image_path
def encode_image(self, image_path: str) -> str:
if not os.path.exists(image_path):
raise FileNotFoundError("File not found")
if os.path.getsize(image_path) > 5 * 1024 * 1024:
raise ValueError("Maximum file size is 5MB")
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
@lru_cache(maxsize=100)
def analyze_chart(self, image_path: str, persona: str) -> str:
try:
optimized_path = self.optimize_image(image_path)
base64_image = self.encode_image(optimized_path)
persona_prompt = PERSONAS.get(persona, {}).get("prompt", "")
full_system_prompt = f"{SYSTEM_PROMPT}\n\n{persona_prompt}"
response = openai.ChatCompletion.create(
model=ANALYSIS_MODEL,
messages=[
{"role": "system", "content": full_system_prompt},
{"role": "user", "content": [
{"type": "text", "text": "Perform detailed technical analysis of this chart:"},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
}
]}
],
max_tokens=MAX_TOKENS
)
analysis_text = response.choices[0].message.content
self.last_analysis = analysis_text
return analysis_text
except Exception as e:
return f"Error: {str(e)}"
def create_pdf_styles():
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(
'Justify',
parent=styles['BodyText'],
alignment=TA_JUSTIFY,
spaceAfter=6
))
styles.add(ParagraphStyle(
'PersonaTitle',
fontSize=14,
textColor=colors.white,
backColor=colors.darkblue,
alignment=1,
spaceAfter=12
))
return styles
def generate_pdf(image_path: str, analysis_text: str, persona: str) -> str:
styles = create_pdf_styles()
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
filename = f"{PDF_DIR}/report_{timestamp}_{uuid.uuid4().hex[:6]}.pdf"
doc = SimpleDocTemplate(filename, pagesize=letter)
story = []
# Resim ekleme
try:
img = Image.open(image_path)
img_width, img_height = img.size
aspect = img_height / float(img_width)
target_width = 400
target_height = target_width * aspect
if target_height > 600:
target_height = 600
target_width = target_height / aspect
story.append(RLImage(image_path, width=target_width, height=target_height))
story.append(Spacer(1, 20))
except Exception as e:
print(f"PDF image error: {str(e)}")
# Persona bilgisi
persona_color = PERSONAS.get(persona, {}).get("color", colors.black)
story.append(Paragraph(f"Persona: {persona}", ParagraphStyle(
'PersonaTitle',
fontSize=14,
textColor=colors.white,
backColor=persona_color,
alignment=1
)))
story.append(Spacer(1, 20))
# Analiz metni
analysis_style = styles['Justify']
for line in analysis_text.split('\n'):
if line.strip():
p = Paragraph(line.replace('•', '•'), analysis_style)
story.append(p)
story.append(Spacer(1, 12))
doc.build(story)
return filename
def upload_to_github(file_path: str) -> bool:
try:
with open(file_path, "rb") as f:
content = f.read()
file_name = os.path.basename(file_path)
url = f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/contents/{PDF_DIR}/{file_name}"
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
# Check if file exists
response = requests.get(url, headers=headers)
sha = response.json().get("sha") if response.status_code == 200 else None
data = {
"message": f"Add report {file_name}",
"content": base64.b64encode(content).decode("utf-8"),
"branch": "main"
}
if sha:
data["sha"] = sha
response = requests.put(url, headers=headers, json=data)
return response.status_code in [200, 201]
except Exception as e:
print(f"GitHub upload error: {str(e)}")
return False
# Gradio Arayüzü
custom_css = """
:root { --primary-color: #2563eb; --secondary-color: #1e40af; }
.container { max-width: 1200px; margin: 0 auto; }
.analysis-box { background: white; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
.loading-spinner { border: 4px solid #f3f3f3; border-top: 4px solid var(--primary-color); }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
"""
with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
analyzer = ChartAnalyzer()
with gr.Column(elem_classes=["container"]):
gr.Markdown("""<div style="text-align: center;"><h1>🚀 CryptoVision Pro</h1></div>""")
with gr.Row():
with gr.Column():
with gr.Box(elem_classes=["analysis-box"]):
chart_input = gr.Image(type="filepath", label="Chart", sources=["upload"])
persona_dropdown = gr.Dropdown(list(PERSONAS.keys()), label="Trading Persona", value="Neutral Trader")
analyze_btn = gr.Button("Analyze", variant="primary")
with gr.Column():
with gr.Box(elem_classes=["analysis-box"]):
analysis_output = gr.Markdown("Analysis will appear here...")
pdf_status = gr.HTML()
analyze_btn.click(
lambda: (gr.update(visible=False), gr.update(value="<div class='loading-spinner'></div>")),
outputs=[analysis_output, pdf_status],
queue=False
).then(
analyzer.analyze_chart,
[chart_input, persona_dropdown],
analysis_output
).then(
lambda img, text, persona: generate_pdf(img, text, persona),
[chart_input, analysis_output, persona_dropdown],
None
).then(
lambda path: upload_to_github(path) if all([GITHUB_TOKEN, REPO_OWNER, REPO_NAME]) else None,
None,
pdf_status
)
if __name__ == "__main__":
demo.launch()