Spaces:
Paused
Paused
| import json | |
| import time | |
| import requests | |
| from flask import Flask, jsonify | |
| from apscheduler.schedulers.background import BackgroundScheduler | |
| from fetch_news import get_recent_news_items, build_news_summary | |
| from get_price import get_live_rates_for_pair | |
| from ai_api import call_o1_ai_api | |
| from db_news import ( | |
| fetch_json_from_github as fetch_news_json_from_github, | |
| fetch_authenticity_token_and_commit_oid as fetch_news_auth_token_and_oid, | |
| update_user_json_file as update_news_json_file | |
| ) | |
| # NEW: import twitter db helpers | |
| from db_twiter import ( | |
| fetch_json_from_github as fetch_twitter_json_from_github, | |
| fetch_authenticity_token_and_commit_oid as fetch_twiter_auth_token_and_oid, | |
| update_user_json_file as update_twiter_json_file | |
| ) | |
| # Create Flask app | |
| app = Flask(__name__) | |
| # Global variable to store the last update result | |
| last_update_result = { | |
| "success": False, | |
| "timestamp": None, | |
| "message": "No updates yet" | |
| } | |
| # Simple logger helper | |
| def log(step, msg): | |
| print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] [{step}] {msg}", flush=True) | |
| # ========================= | |
| # Screenshot helper | |
| # ========================= | |
| SCREENSHOT_API = "https://corvo-ai-xx-sc.hf.space/capture" | |
| def capture_screenshots(urls, width=1080, height=1920, full_page=True, timeout=400): | |
| """ | |
| Calls the screenshot API with one or more URLs and returns a dict: | |
| { | |
| 'success': True/False, | |
| 'images': [ { 'srcUrl': <original>, 'imageUrl': <png> }, ... ], | |
| 'errors': [ ...raw errors... ] | |
| } | |
| """ | |
| step = "SCREENSHOT" | |
| payload = { | |
| "urls": urls, | |
| "width": width, | |
| "height": height, | |
| "fullPage": full_page | |
| } | |
| try: | |
| log(step, f"Requesting screenshots for {len(urls)} URL(s): {urls} w={width} h={height} fullPage={full_page}") | |
| resp = requests.post(SCREENSHOT_API, json=payload, timeout=timeout) | |
| log(step, f"API status code: {resp.status_code}") | |
| resp.raise_for_status() | |
| data = resp.json() | |
| results = data.get("results", []) | |
| errors = data.get("errors", []) | |
| log(step, f"API results count: {len(results)}, errors count: {len(errors)}") | |
| images = [] | |
| for item in results: | |
| out = item.get("output", {}) | |
| img_url = out.get("imageUrl") | |
| src = item.get("url") | |
| log(step, f"Parsed result for {src}: imageUrl={'OK' if img_url else 'MISSING'}") | |
| if img_url: | |
| images.append({ | |
| "srcUrl": src, | |
| "imageUrl": img_url | |
| }) | |
| success = len(images) > 0 | |
| log(step, f"Collected {len(images)} image(s). Success={success}") | |
| if errors: | |
| log(step, f"Errors: {errors}") | |
| return {"success": success, "images": images, "errors": errors} | |
| except Exception as e: | |
| log(step, f"Exception: {e}") | |
| return {"success": False, "images": [], "errors": [str(e)]} | |
| # ========================= | |
| # Chat history builder | |
| # ========================= | |
| def build_formatted_chat_history( | |
| old_ai_response=None, | |
| news_summary="", | |
| twitter_summary="", | |
| gold_price_data=None, | |
| chart_images=None | |
| ): | |
| step = "CHAT_BUILD" | |
| log(step, "Start building chat history") | |
| chat_history = [] | |
| # 1) role=system | |
| log(step, "Append system role prompt") | |
| chat_history.append({"role": "system", "content": """(أنت نظام ذكاء اصطناعي احترافي متخصص في بناء استراتيجية تداول ذكية ومباشرة على الذهب XAUUSD. | |
| هدفك: | |
| - إنتاج استراتيجية تداول احترافية قابلة للتنفيذ، معتمدة على مزيج من التحليل الإخباري والقراءات الفنية المتقدمة وإدارة المخاطر الديناميكية، ومهيكلة بتنسيق XML المحدد. | |
| - اختيار زوج واحد فقط لعلاقة الارتباط لاكتشاف الفخاخ، دون شرح داخل <Relation>. | |
| مسؤولياتك الأساسية: | |
| 1) الأخبار والسياق والسعر: | |
| - جمع آخر النقاط الجوهرية من الأخبار المؤثرة على الذهب (الفيدرالي، التضخم، PMI، التوترات الجيوسياسية، النمو، الدولار، العوائد، سيولة السوق). | |
| - الاستدلال على دفة المخاطر Risk-on/Risk-off. | |
| - تحديد الاتجاه المرجّح قصير/متوسط المدى إن أمكن. | |
| - ملاحظة السعر الحالي أو نطاق سعري حديث إن توفّر. إذا لم يتوفر سعر لحظي، استخدم نطاقاً تقديرياً مع توضيح عدم اليقين. | |
| - إضافة ملخص تويتر مختصر مع ذكر الحسابات ذات التأثير على الذهب/الفوركس. | |
| 2) بناء الاستراتيجية: | |
| - استراتيجية تداول احترافية متعددة السيناريوهات، تتضمن: | |
| • شروط دخول/خروج محددة قابلة للاختبار (مستويات، مؤشرات، سرعات شموع، أحجام، وقت). | |
| • شروط متقدمة مثل: Velocity Candle Detection، Signal Score (درجة إشارة رقمية)، فلترة الأخبار القوية (توقف/خفض حجم قبل وبعد الأخبار). | |
| • إدارة صفقات ديناميكية Dynamic Trade Management: وقف خسارة متحرك، جني أرباح جزئي، إعادة توازن الحجم، إلغاء أو تعديل الأوامر المعلّقة. | |
| • قواعد التعامل مع التقلبات: توسيع/تقليص الوقف، تجميد الدخول أثناء سبريد مرتفع أو عزوف سيولة. | |
| • جداول احترافية للسيناريوهات والفخاخ المحتملة مرتبطة بالزوج المختار في <Relation>. | |
| 3) علاقة الارتباط والفخاخ: | |
| - اختر زوجاً واحداً فقط له ارتباط مهم مع الذهب للمقارنة والكشف عن الفخاخ (مثل قوة/ضعف الدولار أو العوائد). | |
| - استخدم هذا الزوج في الجداول لتمييز السيناريوهات المتضادة (Divergence/Confluence). | |
| - في وسم <Relation> اكتب اسم الزوج فقط دون شرح. | |
| 4) جودة المخرجات وتنسيق XML: | |
| - استخدم العربية الفصيحة الموجزة والعملية. | |
| - لا تضف نصاً خارج عناصر XML المطلوبة. | |
| - التزم بالبنية التالية حصراً: | |
| <news>…</news> | |
| <twiter>…</twiter> | |
| <Strategy>…</Strategy> | |
| <ُExpired>..<ُ/Expired> | |
| <BestOpportunities> | |
| <Buy>…</Buy> | |
| <Sell>…</Sell> | |
| </BestOpportunities> | |
| <Relation>زوج-واحد-فقط</Relation> | |
| تفاصيل العناصر: | |
| • <news> | |
| - أساسية: توجه الفيدرالي، بيانات تضخم/وظائف حديثة، تحركات عوائد وسندات، شهية المخاطرة، أحداث جيوسياسية، وضع الدولار. | |
| - اختتم باستنتاج اتجاهي مرجّح وتأثيره المتوقع على XAUUSD. | |
| - إذا كانت البيانات غير محدثة، اذكر ذلك بوضوح وقدّم إطار احتمالات بدل الجزم. | |
| • <twiter> | |
| - قدم ملخصاً مختصراً م لأبرز ما يتداوله على تويتر. | |
| - اذكر حسابات قامت بنشر اشياء تهمك مع ذكر المنشور @username | |
| - ركّز على المزاج العام، التحذيرات من التقلب، أي إشارات توافق/اختلاف مع السرد الإخباري. | |
| • <Strategy> | |
| - ضع هنا استراتيجيه قويه تقلل خسائر و تصنع ربح حوالي 100 نقطه بناء عل الاخبار و التوجه العام لارسالها الي بوت صانع صفقات | |
| • <ُExpired> | |
| - ضع هنا تاريخ ووقت و سعر تعتقد ان استرااتيجيتك تنتهي اذا وصلت | |
| • <BestOpportunities> | |
| - قدّم وصفاً عملياً قصيراً لأفضل حالات الشراء وأفضل حالات البيع وفق شروط مثالية متكاملة (مستوى، زخم، Score، فلترة أخبار، إدارة). | |
| • <Relation> | |
| - اسم زوج واحد فقط، مثل: DXY أو USDJPY أو US10Y أو EURUSD. اكتب الرمز/الاسم فقط بلا أي شرح. | |
| معايير الدقة والشفافية: | |
| - لا تخترع أسعاراً مؤكدة إن لم تكن متاحة؛ استخدم نطاقات تقريبية مع توضيح عدم اليقين. | |
| - اذكر الافتراضات بوضوح. | |
| - احرص أن تكون القواعد قابلة للتنفيذ وليست عامة أو إنشائية. | |
| - لا تكرر المحتوى بلا داع. | |
| - أعد المخرجات دائماً بصيغة XML بالترتيب المحدد أعلاه دون إضافات.)"""}) | |
| # 2) role=user -> chart images as multipart entries | |
| if chart_images: | |
| log(step, f"Appending {len(chart_images)} chart image(s) to chat") | |
| for img in chart_images: | |
| timeframe = img.get("timeframe", "").strip() | |
| image_url = img.get("imageUrl") | |
| if image_url: | |
| chat_history.append({ | |
| "role": "user", | |
| "type": "multipart", | |
| "content": [ | |
| {"type": "image", "url": image_url}, | |
| {"type": "text", "text": f"هذه لقطة شاشة لزوج XAUUSD إطار زمني {timeframe}"} | |
| ] | |
| }) | |
| log(step, f"Added image message for timeframe={timeframe}, url={image_url}") | |
| else: | |
| log(step, f"Skipped image missing URL for timeframe={timeframe}") | |
| else: | |
| log(step, "No chart images available to append") | |
| # 3) role=user -> News + Twitter + Price | |
| log(step, "Appending news + twitter + price text block") | |
| user_content = "News from Tridingview News :" | |
| if news_summary: | |
| user_content += f"{news_summary}\n\n" | |
| else: | |
| user_content += "No fresh news summary available.\n\n" | |
| if twitter_summary: | |
| user_content += "Twitter Feed Summary:\n" | |
| user_content += f"{twitter_summary}\n\n" | |
| else: | |
| user_content += "Twitter Feed Summary: No recent Twitter data available.\n\n" | |
| if gold_price_data is not None: | |
| user_content += ( | |
| f"Gold Price [XAUUSD]:\n" | |
| f" Bid: {gold_price_data.get('bid')}\n" | |
| f" Ask: {gold_price_data.get('ask')}\n" | |
| f" Spread: {gold_price_data.get('difference')}\n" | |
| ) | |
| chat_history.append({"role": "user", "content": user_content}) | |
| log(step, "Appended news/price block") | |
| # 4) role=user -> OLD ANALYSIS if exists | |
| if old_ai_response: | |
| chat_history.append({"role": "user", "content": "OLD ANALYSIS FROM YOU:" + old_ai_response}) | |
| log(step, "Appended old AI analysis") | |
| else: | |
| log(step, "No old AI analysis available") | |
| # 5) role=user -> update question | |
| update_prompt = "هل تريد تحديث تحليلك واستراتيجيتك بناءً على هذه المعطيات؟" | |
| chat_history.append({"role": "user", "content": update_prompt}) | |
| log(step, "Appended update question") | |
| log(step, "Chat history build complete") | |
| return chat_history | |
| def update_strategy_job(): | |
| """Function to be scheduled, runs the main logic""" | |
| global last_update_result | |
| step = "JOB" | |
| log(step, "Scheduled update started") | |
| try: | |
| # 1) Fetch old data from news.json in GitHub | |
| log(step, "Fetching old data from GitHub (news.json)") | |
| old_data_response = fetch_news_json_from_github() | |
| old_ai_response = None | |
| if old_data_response.get("success") and old_data_response.get("data"): | |
| log(step, "Old data fetch success") | |
| old_data = old_data_response["data"] | |
| old_ai_response = json.dumps(old_data, ensure_ascii=False, separators=(',', ':')) | |
| else: | |
| log(step, f"Old data fetch failed or empty: {old_data_response}") | |
| old_ai_response = None | |
| # 2) Fetch news + gold price | |
| log(step, "Fetching recent news items") | |
| news_items = get_recent_news_items(hours=24) | |
| log(step, f"Fetched {len(news_items) if news_items else 0} news item(s)") | |
| news_summary = build_news_summary(news_items) | |
| log(step, "Built news summary") | |
| log(step, "Fetching live gold price XAUUSD") | |
| gold_price_data = get_live_rates_for_pair("XAUUSD") | |
| log(step, f"Price data: {gold_price_data}") | |
| # 2b) Fetch Twitter summary from twiter.json | |
| log(step, "Fetching twitter summary from GitHub (twiter.json)") | |
| twitter_summary = "" | |
| try: | |
| tw_resp = fetch_twitter_json_from_github() | |
| if tw_resp.get("success") and tw_resp.get("data"): | |
| data = tw_resp["data"] | |
| if isinstance(data, dict) and "twiter" in data: | |
| twitter_summary = data.get("twiter", "") | |
| elif isinstance(data, str): | |
| twitter_summary = data | |
| else: | |
| twitter_summary = json.dumps(data, ensure_ascii=False) | |
| log(step, "Twitter summary fetched successfully") | |
| else: | |
| log(step, f"Twitter fetch returned no data: {tw_resp}") | |
| twitter_summary = "" | |
| except Exception as te: | |
| log(step, f"Twitter fetch error: {te}") | |
| twitter_summary = "" | |
| # 2c) Capture chart screenshots for 15m and 1h | |
| charts = [ | |
| { | |
| "url": "https://corvo-ai-charts.static.hf.space/index.html?symbol=XAUUSD&interval=&exchange=OANDA", | |
| "timeframe": "" | |
| }, | |
| { | |
| "url": "https://corvo-ai-charts.static.hf.space/index.html?symbol=XAUUSD&interval=15&exchange=OANDA", | |
| "timeframe": "15 دقيقة" | |
| }, | |
| { | |
| "url": "https://corvo-ai-charts.static.hf.space/index.html?symbol=XAUUSD&interval=1&exchange=OANDA", | |
| "timeframe": "1 ساعة" | |
| }, | |
| { | |
| "url": "https://corvo-ai-charts.static.hf.space/index.html?symbol=XAUUSD&interval=60&exchange=OANDA", | |
| "timeframe": "1 ساعة" | |
| } | |
| ] | |
| chart_images = [] | |
| try: | |
| log(step, "Capturing chart screenshots (15m, 1h)") | |
| capture_result = capture_screenshots([c["url"] for c in charts], width=0, height=0, full_page=True) | |
| if capture_result.get("success"): | |
| log(step, "Screenshot capture success") | |
| src_to_tf = {c["url"]: c["timeframe"] for c in charts} | |
| for img in capture_result.get("images", []): | |
| chart_images.append({ | |
| "timeframe": src_to_tf.get(img.get("srcUrl"), ""), | |
| "imageUrl": img.get("imageUrl") | |
| }) | |
| log(step, f"Prepared {len(chart_images)} chart image entries for chat") | |
| else: | |
| errs = capture_result.get("errors") | |
| log(step, f"Screenshot capture failed. Errors: {errs}") | |
| except Exception as e: | |
| log(step, f"Screenshot exception: {e}") | |
| # 3) Build chat_history including images | |
| log(step, "Building chat history") | |
| chat_history = build_formatted_chat_history( | |
| old_ai_response=old_ai_response, | |
| news_summary=news_summary, | |
| twitter_summary=twitter_summary, | |
| gold_price_data=gold_price_data, | |
| chart_images=chart_images | |
| ) | |
| log(step, f"Chat history ready. Messages count: {len(chat_history)}") | |
| # 4) Call AI | |
| log(step, "Calling AI API with chat history") | |
| ai_response, updated_chat_history = call_o1_ai_api(chat_history) | |
| log(step, "AI API call completed") | |
| # 5) Save AI response to GitHub in news.json | |
| log(step, "Serializing AI response for GitHub") | |
| result_json_object = { | |
| "response": ai_response, | |
| "timestamp": time.time() | |
| } | |
| new_content_one_line = json.dumps([result_json_object], ensure_ascii=False, separators=(',', ':')) | |
| log(step, f"Serialized content length: {len(new_content_one_line)}") | |
| # 6) Get authenticity_token and commit_oid | |
| log(step, "Fetching authenticity token and commit oid for news.json") | |
| token, commit_oid = fetch_news_auth_token_and_oid() | |
| if not token or not commit_oid: | |
| last_update_result = { | |
| "success": False, | |
| "timestamp": time.time(), | |
| "message": "Could not fetch authenticity token or commit oid for news.json." | |
| } | |
| log(step, last_update_result['message']) | |
| return | |
| log(step, "Fetched token and commit oid successfully") | |
| # 7) Update GitHub file | |
| log(step, "Updating news.json on GitHub") | |
| update_result = update_news_json_file(token, commit_oid, new_content_one_line) | |
| if update_result.get("success"): | |
| last_update_result = { | |
| "success": True, | |
| "timestamp": time.time(), | |
| "message": "Updated news.json successfully!", | |
| "response": ai_response | |
| } | |
| log(step, last_update_result['message']) | |
| else: | |
| last_update_result = { | |
| "success": False, | |
| "timestamp": time.time(), | |
| "message": f"Failed to update news.json: {update_result.get('message')}" | |
| } | |
| log(step, last_update_result['message']) | |
| except Exception as e: | |
| last_update_result = { | |
| "success": False, | |
| "timestamp": time.time(), | |
| "message": f"Error during update: {str(e)}" | |
| } | |
| log(step, last_update_result['message']) | |
| # API routes | |
| def get_status(): | |
| """Returns the status of the last update""" | |
| log("API", "GET /status called") | |
| return jsonify({ | |
| "success": last_update_result.get("success", False), | |
| "timestamp": last_update_result.get("timestamp"), | |
| "message": last_update_result.get("message"), | |
| "last_updated": time.strftime('%Y-%m-%d %H:%M:%S', | |
| time.localtime(last_update_result.get("timestamp"))) | |
| if last_update_result.get("timestamp") else None | |
| }) | |
| def force_update(): | |
| """Force an immediate update""" | |
| log("API", "GET /force-update called") | |
| update_strategy_job() | |
| return jsonify({ | |
| "success": last_update_result.get("success", False), | |
| "message": "Update job triggered", | |
| "result": last_update_result | |
| }) | |
| def get_latest(): | |
| """Get the latest strategy response""" | |
| log("API", "GET /latest called") | |
| if "response" in last_update_result: | |
| return jsonify({ | |
| "success": True, | |
| "timestamp": last_update_result.get("timestamp"), | |
| "response": last_update_result.get("response"), | |
| "last_updated": time.strftime('%Y-%m-%d %H:%M:%S', | |
| time.localtime(last_update_result.get("timestamp"))) | |
| }) | |
| else: | |
| return jsonify({ | |
| "success": False, | |
| "message": "No strategy data available yet" | |
| }) | |
| # Setup the scheduler | |
| def init_scheduler(): | |
| """Initialize and start the scheduler""" | |
| scheduler = BackgroundScheduler() | |
| scheduler.add_job(func=update_strategy_job, trigger="interval", hours=1) | |
| scheduler.start() | |
| log("SCHED", "Scheduler started: will run every hour") | |
| # Run once immediately on startup | |
| log("SCHED", "Running initial update job on startup") | |
| update_strategy_job() | |
| if __name__ == "__main__": | |
| # Initialize the scheduler before starting the Flask app | |
| init_scheduler() | |
| # Start the Flask app | |
| log("APP", "Starting Flask server on port 7860") | |
| app.run(host='0.0.0.0', port=7860) |