File size: 12,139 Bytes
d9bf6c2 af1b1ee 3182722 cc6796a d9bf6c2 cc6796a d9bf6c2 cc6796a 4542f7d cc6796a 689ae36 d9bf6c2 cc6796a 3182722 cc6796a 3182722 cc6796a 3182722 689ae36 cc6796a 3182722 cc6796a 3182722 cc6796a 3182722 cc6796a ce0b5c5 44bb60b cc6796a 44bb60b e773abb 3182722 d7895f3 3182722 cc6796a 3182722 cc6796a 3182722 cc6796a 3182722 cc6796a 3182722 e773abb 4542f7d cc6796a 44bb60b 4542f7d cc6796a d7895f3 e773abb db34fd4 3182722 4542f7d e773abb cc6796a e773abb cc6796a d7895f3 25f9993 e773abb d7895f3 e773abb cc6796a ce0b5c5 d7895f3 cc6796a db34fd4 dde08ee 689ae36 cc6796a dde08ee 689ae36 ce0b5c5 689ae36 dde08ee d7895f3 dde08ee 689ae36 d7895f3 dde08ee d7895f3 ce0b5c5 689ae36 dde08ee 689ae36 ce0b5c5 689ae36 db34fd4 ce0b5c5 dde08ee 689ae36 dde08ee 689ae36 dde08ee 689ae36 db34fd4 dde08ee 689ae36 9fbdefb cc6796a db34fd4 dde08ee 689ae36 db34fd4 dde08ee cc6796a dde08ee cc6796a db34fd4 dde08ee cc6796a dde08ee cc6796a 689ae36 dde08ee db34fd4 dde08ee cc6796a dde08ee ce0b5c5 a6e8e74 cc6796a a6e8e74 907721e 2a217f4 8bfdc69 a6e8e74 db34fd4 dde08ee a6e8e74 db34fd4 dde08ee db34fd4 8bfdc69 db34fd4 e773abb db34fd4 ce0b5c5 cc6796a bfaa4d5 d7895f3 cc6796a 689ae36 d7895f3 db34fd4 cc6796a 689ae36 db34fd4 689ae36 e773abb d9bf6c2 dde08ee |
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 |
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"
os.makedirs(PDF_DIR, exist_ok=True)
# 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_optimized_path = ""
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) -> tuple:
try:
if not self.validate_image(image_path):
return "Error: Invalid or low-quality image", None
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_optimized_path = optimized_path
return analysis_text, optimized_path
except Exception as e:
return f"Error: {str(e)}", None
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(optimized_image_path: str, analysis_text: str, persona: str) -> str:
try:
# Validasyonlar
if not optimized_image_path:
raise ValueError("Optimized image path is missing")
if not os.path.exists(optimized_image_path):
raise FileNotFoundError(f"Optimized image not found at {optimized_image_path}")
if not analysis_text or not analysis_text.strip():
raise ValueError("Analysis text cannot be empty")
# PDF dosya yolu oluştur
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
filename = f"{PDF_DIR}/report_{timestamp}_{uuid.uuid4().hex[:6]}.pdf"
# PDF içeriğini oluştur
doc = SimpleDocTemplate(filename, pagesize=letter)
story = []
# Resim ekleme
try:
img = Image.open(optimized_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(optimized_image_path, width=target_width, height=target_height))
story.append(Spacer(1, 20))
except Exception as e:
print(f"PDF image error: {str(e)}")
raise
# Persona bilgisi
if persona in PERSONAS:
persona_color = PERSONAS[persona]["color"]
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
styles = create_pdf_styles()
analysis_style = styles['Justify']
cleaned_text = analysis_text.replace('•', '•')
for line in cleaned_text.split('\n'):
if line.strip():
p = Paragraph(line, analysis_style)
story.append(p)
story.append(Spacer(1, 12))
# PDF'i oluştur
doc.build(story)
if not os.path.exists(filename):
raise RuntimeError("PDF file creation failed")
return filename
except Exception as e:
print(f"[PDF Generation Error] {str(e)}")
return None
def upload_to_github(file_path: str) -> str:
try:
if not file_path or file_path == "None":
return "⛔ Error: PDF generation failed in previous step"
if not os.path.exists(file_path):
return f"⛔ File not found: {file_path}"
# GitHub yükleme işlemleri
file_name = os.path.basename(file_path)
url = f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/contents/{PDF_DIR}/{file_name}"
with open(file_path, "rb") as f:
content = base64.b64encode(f.read()).decode("utf-8")
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
# Dosya varlık kontrolü
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": content,
"branch": "main"
}
if sha:
data["sha"] = sha
response = requests.put(url, headers=headers, json=data)
if response.status_code in [200, 201]:
return f"✅ Report successfully uploaded to GitHub!\nURL: {response.json()['content']['html_url']}"
return f"❌ GitHub upload failed ({response.status_code}): {response.text}"
except Exception as e:
return f"⚠️ Upload error: {str(e)}"
with gr.Blocks(theme=gr.themes.Soft()) as demo:
analyzer = ChartAnalyzer()
with gr.Column():
gr.Markdown("# 🚀 CryptoVision Pro")
with gr.Row():
with gr.Column():
chart_input = gr.Image(type="filepath", label="Upload Chart", sources=["upload"])
persona_dropdown = gr.Dropdown(
list(PERSONAS.keys()),
label="Select Trading Persona",
value="Neutral Trader",
info="Choose your trading style"
)
analyze_btn = gr.Button("Analyze Chart", variant="primary")
with gr.Column():
analysis_output = gr.Markdown("## Analysis Results\n*Your analysis will appear here*")
pdf_status = gr.HTML()
# Gizli bileşenler
optimized_image_path = gr.Text(visible=False)
pdf_file = gr.Text(visible=False)
# İşlem Zinciri
analyze_btn.click(
lambda: [
gr.Markdown(visible=False),
gr.HTML(value="<div class='loading-spinner'></div>"),
None
],
outputs=[analysis_output, pdf_status, pdf_file],
queue=False
).then(
analyzer.analyze_chart,
inputs=[chart_input, persona_dropdown],
outputs=[analysis_output, optimized_image_path]
).then(
generate_pdf,
inputs=[optimized_image_path, analysis_output, persona_dropdown],
outputs=pdf_file
).then(
upload_to_github,
inputs=pdf_file,
outputs=pdf_status
)
if __name__ == "__main__":
demo.launch() |