from flask import Flask, request, jsonify
from get_indicators import get_tradingview_data
import requests
import json
import time
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
import pytz
import logging
import re
# إعداد Flask
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# إعدادات APIs
MESSAGE_API_URL = "https://aoamrnuwara.pythonanywhere.com/api/send-message"
MESSAGE_API_KEY = "Seakp0683asppoit2"
SCREENSHOT_API_URL = "https://corvo-ai-xx-sc.hf.space/capture"
AI_API_URL = "https://corvo-ai-xx-o1.hf.space/chat"
AI_API_URL_O1 = "https://corvo-ai-xx-o1.hf.space/chat"
ALERT_API_URL = "https://dooratre-alert.hf.space/monitor"
# إعدادات المحاولات
MAX_RETRIES = 5
RETRY_DELAY = 30
# استيراد وحدات قاعدة البيانات
import db_system
import db_signals
import db_analysis
import get_price
def get_indicators_for_timeframe(timeframe):
"""Fetch indicators for a specific timeframe"""
try:
indicators = get_tradingview_data(timeframe)
if indicators:
return indicators
return "Indicators unavailable"
except Exception as e:
logger.warning(f"Failed to fetch indicators for timeframe {timeframe}: {e}")
return "Indicators unavailable"
def thinking_animation():
for _ in range(3):
print(".", end="", flush=True)
time.sleep(0.5)
def has_active_signal():
try:
signals_data = db_signals.fetch_json_from_github()
return bool(signals_data and signals_data.get("success") and signals_data.get("data"))
except Exception:
return False
def call_o1_ai_api(formatted_chat_history, api_url=AI_API_URL):
headers = {"Content-Type": "application/json"}
payload = {"chat_history": formatted_chat_history}
timeout = 600
for attempt in range(MAX_RETRIES):
try:
print("AI THINKING", end="", flush=True)
thinking_animation()
response = requests.post(api_url, headers=headers, data=json.dumps(payload), timeout=timeout)
response.raise_for_status()
assistant_response = response.json().get("assistant_response", "No response received.")
formatted_chat_history.append({"role": "assistant", "content": assistant_response})
return assistant_response, formatted_chat_history
except requests.exceptions.Timeout:
print(f"Timeout on attempt {attempt + 1}, retrying...")
time.sleep(RETRY_DELAY)
except Exception as e:
print(f"Error on attempt {attempt + 1}: {e}, retrying...")
time.sleep(RETRY_DELAY)
return "Error processing request. Please try again.", formatted_chat_history
def send_message_to_api(message, max_retries=5, retry_delay=10):
headers = {
"Content-Type": "application/json",
"X-API-Key": MESSAGE_API_KEY
}
payload = {"message": message}
for attempt in range(1, max_retries + 1):
try:
response = requests.post(MESSAGE_API_URL, headers=headers, data=json.dumps(payload))
if response.status_code == 200:
logger.info(f"✅ Message sent successfully on attempt {attempt}")
return {"success": True, "response": response.json()}
else:
logger.warning(f"⚠️ Attempt {attempt}: Received status code {response.status_code}")
except requests.exceptions.RequestException as e:
logger.warning(f"⚠️ Attempt {attempt}: API request failed with error: {e}")
if attempt < max_retries:
logger.info(f"🔁 Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
else:
logger.error("❌ Max retries reached. Failed to send message.")
return {"success": False, "error": "Failed after multiple retries"}
def get_time_zones():
zones = {
"Greenwich": "UTC",
"London": "Europe/London",
"New York": "America/New_York",
"Tokyo": "Asia/Tokyo",
"Sydney": "Australia/Sydney"
}
times = {}
for city, zone in zones.items():
tz = pytz.timezone(zone)
current_time = datetime.now(tz)
times[city] = current_time.strftime("%Y-%m-%d %H:%M:%S %Z")
return times
def build_signal_timestamps():
tz_times = get_time_zones()
iso_utc = datetime.now(timezone.utc).isoformat()
return {"zones": tz_times, "iso_utc": iso_utc}
def capture_charts():
urls = [
"https://dooratre-charts.static.hf.space/index.html?symbol=XAUUSD&interval=15&exchange=OANDA",
"https://dooratre-charts.static.hf.space/index.html?symbol=XAUUSD&interval=3&exchange=OANDA",
"https://dooratre-charts.static.hf.space/index.html?symbol=XAUUSD&interval=1&exchange=OANDA"
]
payload = {
"urls": urls,
"width": 1080,
"height": 1920,
"fullPage": False
}
try:
response = requests.post(SCREENSHOT_API_URL, json=payload, timeout=180)
response.raise_for_status()
return response.json()
except Exception as e:
logger.error(f"Error capturing charts: {e}")
return None
def get_live_prices_for_pairs():
pairs = ["XAUUSD"]
prices = {}
for p in pairs:
try:
data = get_price.get_live_rates_for_pair(p)
if data:
prices[p] = {
"bid": data.get("bid", "N/A"),
"ask": data.get("ask", "N/A"),
"difference": data.get("difference", "N/A")
}
else:
prices[p] = {"bid": "N/A", "ask": "N/A", "difference": "N/A"}
except Exception as e:
logger.warning(f"Price fetch failed for {p}: {e}")
prices[p] = {"bid": "N/A", "ask": "N/A", "difference": "N/A"}
return prices
def build_chat_history(alert_message=None):
chat_history = []
# System Role only once
try:
times = get_time_zones()
time_info = "\n".join([f"{city}: {time}" for city, time in times.items()])
chat_history.append({
"role": "system",
"content": f'''
انت نموذج قوي من شركة rinet ai الليبية
مهمتك هي انشاء و تتبع صفقات على الذهب XAUUSD
هدفك:
صفقات سريعة تحقق 50-60 نقطة ربح مع استوب قريب جدًا. الاستراتيجية تهدف إلى خسارة 0%
التركيز على السعر الحالي فقط: يجب على التحليل التنبؤ بمكان السعر الآن وكيف سيتحرك مباشرة، ولا تقترح استراتيجيات تتعلق بأسعار مستقبلية بعيدة.
بداية مهمتك هي اما انشاء صفقة او انتظار
في حالة المستخدم حاليا في صفقة يرجى متابعتها وذكر نقاط سواء ربح خسارة او اغلاقها الخ...
ممنوع منعا باتا ان تنشئ صفقة أخرى مدام المستخدم في صفقة حاليا
ردك سيكون عبارة عن XML لا غير وهي المفاتيح الأساسية لتحريك النظام هنا شرحها
before everything you have here take your time to analysis the images for XAUUSD with analysis the indicators and Volume for every imageput here the Kind of trend based in frame 15min (UPTREND/DOWNTREND/SIDEWAY)
and analysis the news in here analysis the news and how can effect if we open trade in direction if you can not see news in my message please put it with tell me Empty News
اولا الانتظار :
تستخدم هذه في حالة ان المستخدم ينتظر منك انشاء صفقة ولاكن تحليلك يقول ان ليس الوقت المناسب ضع هنا سبب عدم وجود صفقة
ثانيا انشاء صفقة :
XAUUSD
Buy أو Sell
سعر الدخول الان
وقف الخسارة الفني
الهدف
اعتقد انها واضحه وبسيطة تأكد فقط من انك تضع السعر ولا تستعمل اي كلمات في خانات الأسعار لأنها حساسة
أيضا في ال TYPE اما شراء او بيع
ولا تستعمل LIMIT/STOP ابدا
you need to make the signal about SL about 10 20 30 and TP from 70 80 100 ...
add some emojeis i want it tidy and great
خامسا اهم نقطة ويجب أن تستعملها دائما في كل تحليل المنبه :
3...here....... //put more than 1 price as you want for next analysis , for messages put the saved messages in the prices for Auto send to group (all in arabic)
..... may more than one price
.. //the input here as number without any letters with it for example 20 10 15 etc... that using for if the price still in same place and don't moving alot so the duration if end gonna back to you
I think it’s very clear. Let me explain only the structure:
3...here.......
NOTE : never ever put TP or SL in price here because the trade didnt active yet
price → the price level at which you want the next analysis to be triggered.
message → the auto message that will be sent to the group when that price is hit.
This is your main engine that reactivates you after finishing your current analysis. You use it to create an alert for your next analysis, where you set the upper and lower target prices at which you want to analyze again. This applies regardless of whether you’re reviewing an existing trade or creating a new one — it will notify the group when your requested price is reached.
You can also give it a time limit (in minutes) in case neither target is reached and the price keeps fluctuating in between.
⚠️ Keep in mind: the price fields are extremely sensitive. You must enter the price as if you’re entering it in a numeric-only field.
3...here.......
..... may more than one price
..
🟢 Case 1: No active trade (analysis mode)
Structure contains only , , and .
You must not include TP or SL here, because no trade is open yet.
Purpose: re-activate analysis when price hits certain levels or after a time limit.
🔴 Case 2: Active trade (trade running)
Structure must include and in addition to , , and .
= take profit level.
= stop loss level.
and are optional monitoring alerts while the trade is active.
is used if the market is stuck without movement.
⚡️ The rule is simple:
If and exist → it means the agent is in active trade mode.
If and are missing → it means the agent is in analysis mode only.
والان ابدا
'''
})
except Exception as e:
logger.error(f"Error fetching system data: {e}")
# Prepare one multipart user turn
multipart_content = []
# صور المؤشرات (10 images) + dynamic captions using per-pair prices
try:
charts_data = capture_charts()
# Fetch live prices for required pairs (XAUUSD, USDJPY, AUDUSD)
prices = get_live_prices_for_pairs()
def price_caption(pair):
p = prices.get(pair, {})
bid = p.get('bid', 'N/A')
ask = p.get('ask', 'N/A')
diff = p.get('difference', 'N/A')
return f"Bid: {bid}, Ask: {ask}, Diff: {diff}"
labels = [
f"XAUUSD 15 Minute (this gonna use it in choose one of trend (Uptrend/Downtrend/Sideways)) - {price_caption('XAUUSD')}",
f"XAUUSD 5 Minute (IT IS THE IMPORTANT ONE FOR CHANGE TP SL AND ENTER NEW SIGNAL) - {price_caption('XAUUSD')}",
f"XAUUSD 1 Minute (IT IS THE IMPORTANT FOR MAKE SURE THE SIGNAL OKAY) - {price_caption('XAUUSD')}",
]
if charts_data and charts_data.get("results"):
for i, result in enumerate(charts_data["results"]):
url = result.get("output", {}).get("imageUrl")
if url:
multipart_content.append({"type": "image", "url": url})
caption = labels[i] if i < len(labels) else "Chart"
multipart_content.append({"type": "text", "text": caption})
except Exception as e:
logger.error(f"Error adding charts: {e}")
# Previous analysis (as short text part)
try:
analysis_data = db_analysis.fetch_json_from_github()
if analysis_data["success"] and analysis_data["data"]:
try:
raw_text = analysis_data["data"][0]["response"]
analyses = json.loads(raw_text)
if isinstance(analyses, list):
previous_block = format_analysis_with_time(analyses)
else:
previous_block = str(analyses)
except json.JSONDecodeError:
previous_block = analysis_data["data"][0]["response"]
multipart_content.append({
"type": "text",
"text": f"التحليلات السابقة:\n{previous_block}"
})
except Exception as e:
logger.error(f"Error fetching previous analysis: {e}")
multipart_content.append({
"type": "text",
"text": "لا توجد تحليلات سابقة أو حدث خطأ في استرجاعها."
})
# Alert and current/open signal context (append as text to the same multipart user message)
try:
indicators_15m = get_indicators_for_timeframe("15")
indicators_5m = get_indicators_for_timeframe("5")
indicators_1m = get_indicators_for_timeframe("1")
times = get_time_zones()
time_info = "\n".join([f"{city}: {time}" for city, time in times.items()])
system_data = db_system.fetch_json_from_github()
if system_data["success"] and system_data["data"]:
system_content = system_data["data"][0]["response"]
print(system_content)
else:
system_content = "NO NEWS TODAY"
message_content = ""
if alert_message:
message_content += f"رسالة المنبه: {alert_message}\n\n"
else:
message_content += "لا توجد رسالة من المنبه (أول تشغيل)\n\n"
signals_data = db_signals.fetch_json_from_github()
if signals_data["success"] and signals_data["data"]:
current_signal = signals_data["data"][0]
message_content += '''
FOR NO SIGNAL ACTIVE :
You need to decide what Kind of the Trend based on 15min frame -
After decide we gonna go to next STEP whcih find best Trade
Based on the images and Indicators table:
You Have All indicators you need to decide
Just make sure to Enter From 5min timeFrame
Use Wait if you need to .
Our Analysis we need it work in Scalping :
SL : MAX 5$ = 50 pip (you can choose SL from 1$ to 5$ based on your analysis)
TP : MAX 10$ = 100 pip
Analysis based on scalping plan
IF THE TREND SIDEWAY (Not UP OR DOWN)
ROLE 1 : BUY when Indicators 5min OVERSOLD , SELL when Indicators 5min OVERBOUGHT
ROLE 2 : Don't BUY / SELL Direct if you see Indicators OVER , USE FBB to Breake R or S Also find Confirmation candle in Timeframe 1min then you can trust the Indicators gonna move Also to make sure 100% you will see the Volume, And important thing Wait the Indicators to get Over and start to get out that mean that perfect Enter price
ROLE 3 : ELSE of my roles Don't BUY/SELL use and tell user what do you waiting for , and duration make it about 5min to don't miss any Enter
ROLE 4 : Be smart using Alert if you are Waiting and put price you wait for with SELL/BUY message to USER go Earlier then open the trade using
IF (UP/DOWN TREND) ?
IF UPTREND - you need to find just BUY Trades
IF DOWNTREND - you need to find just SELL Trades
But make sure to get OVER in Indicators with FBB breake
IF SIDEWAY (Natural)?
✅ Don’t chase big trends → focus on buying near support and selling near resistance.
✅ Wait for a breakout to join the next real trend.
FINAL ROLE : when you create signal tell user which trend now and why we are in this trade
You are Ready Now
FOR SIGNAL ACTIVE :
You Have the RSI FBB Bolinger Bands
Just make sure to Exit From 1min
and use 1 hour to understand the Trend
Change the SL TP using the alert if you need to
But make to add to TP 50 pip = 5$ and add more if the price gonna hit it but not in one time every time see the price and think is it desrve ?,
and make sure to save 70 ... and go ahead using SL
use oppisite things from Entery to know the Exit place
HERE HOW :
يمكنك تعديل استراتيجية الصفقة المفتوحة باستخدام الأمر التالي:
```
1234.561234.56
```
لتعديل وقف الخسارة فقط:
```
1234.56
```
لتعديل هدف الربح فقط:
```
1234.56
ثالثا ارسال رسالة للمستخدم :
هذه تستخدمها كل مره تحلل فيها وهي مهمة ل يرى المستخدم ماذا يحصل وملخص تحليلك او مراجعتك للصفقة مفتوحة حاليا فقط تخيل وكانها منبرك لتحدث مع المستخدم، دائما اجعلها قصيرة ومفيد ة ولا تضع الصفقة بحد ذاتها الا اذا احتجت لان المستخدم سيرى ال XML الثاني إذا انشئت صفقة
STATUS :
'''
message_content += f"الصفقة المفتوحة حالياً:\n"
message_content += f"الزوج: {current_signal.get('pair', 'N/A')}\n"
message_content += f"النوع: {current_signal.get('type', 'N/A')}\n"
message_content += f"الدخول: {current_signal.get('entry', 'N/A')}\n"
message_content += f"وقف الخسارة: {current_signal.get('stop_loss', 'N/A')}\n"
message_content += f"الهدف: {current_signal.get('take_profit', 'N/A')}\n"
ts = current_signal.get("timestamps", {})
if ts:
zones = ts.get("zones", {})
iso_utc = ts.get("iso_utc", "N/A")
message_content += "وقت إنشاء الصفقة:\n"
for city, t in zones.items():
message_content += f"- {city}: {t}\n"
message_content += f"- ISO_UTC: {iso_utc}\n"
message_content += f"الوقت الآن:\n{time_info}"
else:
message_content += f'''
First thing before Indicator analysis you need to check the summary of news then deside what best to do ? complete to analysis the Indicators or market , news ununderstood then need to wait
Summary :
{system_content}
THEN :
You need to decide what Kind of the Trend based on 15min frame -
Indicators 15min :
{indicators_15m}
After decide we gonna go to next STEP whcih find best Trade
Based on the images and Indicators table:
You Have All indicators you need to decide
Just make sure to Enter From 5min timeFrame
Indicators 5min :
{indicators_5m}
Use Wait if you need to .
Our Analysis we need it work in Scalping :
SL : MAX 5$ = 50 pip (you can choose SL from 1$ to 5$ based on your analysis)
TP : MAX 10$ = 100 pip
Analysis based on scalping plan
IF THE TREND SIDEWAY (Not UP OR DOWN)
ROLE 1 : BUY when Indicators 5min OVERSOLD , SELL when Indicators 5min OVERBOUGHT
ROLE 2 : Don't BUY / SELL Direct if you see Indicators OVER , USE FBB to Breake R or S Also find Confirmation candle in Timeframe 1min
Indicators 1min :
{indicators_1m}
then you can trust the Indicators gonna move Also to make sure 100% you will see the Volume, And important thing Wait the Indicators to get Over and start to get out that mean that perfect Enter price
ROLE 3 : ELSE of my roles Don't BUY/SELL use and tell user what do you waiting for , and duration make it about 5min to don't miss any Enter
ROLE 4 : Be smart using Alert if you are Waiting and put price you wait for with SELL/BUY message to USER go Earlier then open the trade using
IF (UP/DOWN TREND) ?
IF UPTREND - you need to find just BUY Trades
IF DOWNTREND - you need to find just SELL Trades
But make sure to get OVER in Indicators with FBB breake
IF SIDEWAY (Natural)?
✅ Don’t chase big trends → focus on buying near support and selling near resistance.
✅ Wait for a breakout to join the next real trend.
FINAL ROLE : when you create signal tell user which trend now and why we are in this trade
You are Ready Now
STATUS :
'''
message_content += "المستخدم ليس في اي صفقة حاليا.\n please don't send to him anything using the just don't use this tag"
message_content += f"TIME NOW:\n{time_info}"
multipart_content.append({"type": "text", "text": message_content})
except Exception as e:
logger.error(f"Error adding alert and signals: {e}")
# Ensure exactly one user turn with multipart content
if multipart_content:
chat_history.append({
"role": "user",
"type": "multipart",
"content": multipart_content
})
else:
chat_history.append({
"role": "user",
"content": "No charts or additional context available."
})
return chat_history
def build_analysis_record(text):
"""Create a tidy analysis record with Greenwich (UTC) timestamp."""
ts = datetime.now(timezone.utc).isoformat()
return {
"timestamp_utc": ts,
"response": text
}
def load_existing_analyses_safe():
"""Fetch current analyses as a Python list of dicts. Return [] on any issue."""
try:
analysis_data = db_analysis.fetch_json_from_github()
if analysis_data and analysis_data.get("success") and analysis_data.get("data"):
raw = analysis_data["data"][0].get("response", "")
try:
parsed = json.loads(raw)
if isinstance(parsed, list):
cleaned = []
for item in parsed:
if isinstance(item, dict) and ("response" in item):
if "timestamp_utc" in item:
cleaned.append(item)
else:
cleaned.append({
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
"response": item.get("response", "")
})
elif isinstance(item, str):
cleaned.append({
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
"response": item
})
return cleaned
else:
return [{
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
"response": str(parsed)
}]
except json.JSONDecodeError:
return [{
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
"response": raw
}]
return []
except Exception as e:
logger.warning(f"load_existing_analyses_safe fallback: {e}")
return []
def save_latest_analyses(new_text):
"""Save only the latest 3 analyses (newest first) with UTC timestamps."""
try:
existing = load_existing_analyses_safe()
new_record = build_analysis_record(new_text)
combined = [new_record] + existing
def ts_key(x):
try:
return datetime.fromisoformat(x.get("timestamp_utc", "1970-01-01T00:00:00+00:00"))
except Exception:
return datetime(1970, 1, 1, tzinfo=timezone.utc)
combined_sorted = sorted(combined, key=ts_key, reverse=True)
trimmed = combined_sorted[:3]
auth_token, commit_oid = db_analysis.fetch_authenticity_token_and_commit_oid()
if auth_token and commit_oid:
new_payload_text = json.dumps(trimmed, ensure_ascii=False)
result = db_analysis.update_user_json_file(auth_token, commit_oid, new_payload_text)
return result.get("success", False)
else:
logger.error("Failed to fetch auth or commit OID for analysis saving.")
return False
except Exception as e:
logger.error(f"Error saving latest analyses: {e}")
return False
def format_analysis_with_time(analysis_list):
"""Format analyses with timestamps for display."""
formatted = []
for item in analysis_list:
try:
ts_str = item.get("timestamp_utc", "")
dt = datetime.fromisoformat(ts_str) if ts_str else None
if dt:
time_str = dt.strftime("%Y-%m-%d %H:%M:%S UTC")
formatted.append(f"--- {time_str} ---\n{item.get('response', '')}\n")
else:
formatted.append(f"{item.get('response', '')}\n")
except Exception:
formatted.append(f"{item.get('response', '')}\n")
return "\n".join(formatted)
def to_float_safe(val):
try:
return float(str(val).strip())
except Exception:
return None
def parse_alert_block(alert_xml: str):
"""
Parse the new Alert block format:
... optional
... optional
1234.5text one or more
10 required
Returns dict: { "tp": float|None, "sl": float|None, "duration": int, "price_messages": [ {price, message}, ... ] }
Raises ValueError on invalid/missing fields.
"""
tp_match = re.search(r'(.*?)', alert_xml, re.DOTALL)
sl_match = re.search(r'(.*?)', alert_xml, re.DOTALL)
tp = to_float_safe(tp_match.group(1)) if tp_match else None
sl = to_float_safe(sl_match.group(1)) if sl_match else None
dur_match = re.search(r'(.*?)', alert_xml, re.DOTALL)
if not dur_match:
raise ValueError("duration_min missing in ")
try:
duration_minutes = int(str(dur_match.group(1)).strip())
if duration_minutes <= 0:
raise ValueError
except Exception:
raise ValueError("duration_min must be positive integer")
price_blocks = re.findall(r'(.*?)', alert_xml, re.DOTALL)
if not price_blocks:
raise ValueError("At least one ... block is required")
price_messages = []
for block in price_blocks:
msg_match = re.search(r'(.*?)', block, re.DOTALL)
message = ""
if msg_match:
message = msg_match.group(1).strip()
price_text = re.sub(r'.*?', '', block, flags=re.DOTALL).strip()
price_val = to_float_safe(price_text)
if price_val is None:
raise ValueError(f"Invalid price value in block: '{price_text}'")
price_messages.append({"price": price_val, "message": message})
return {
"tp": tp,
"sl": sl,
"duration": duration_minutes,
"price_messages": price_messages
}
def build_monitor_payload_from_alert(parsed_alert: dict, symbol="XAUUSD"):
payload = {
"symbol": symbol,
"duration_minutes": parsed_alert["duration"],
"price_messages": parsed_alert["price_messages"]
}
if parsed_alert.get("tp") is not None:
payload["tp"] = float(parsed_alert["tp"])
if parsed_alert.get("sl") is not None:
payload["sl"] = float(parsed_alert["sl"])
return payload
def parse_ai_response(ai_response):
actions_performed = []
# 1. Save analysis (latest 3 with UTC time, newest first)
try:
ok = save_latest_analyses(ai_response)
if ok:
actions_performed.append("✅ تم حفظ آخر 3 تحليلات مع توقيت Greenwich (UTC)")
else:
actions_performed.append("❌ فشل في حفظ التحليل")
except Exception as e:
logger.error(f"Error saving analysis: {e}")
actions_performed.append("❌ خطأ في حفظ التحليل")
# 2. Close trades first
close_matches = re.finditer(r'(.*?)', ai_response, re.DOTALL)
for close_match in close_matches:
try:
close_reason = close_match.group(1).strip()
auth_token, commit_oid = db_signals.fetch_authenticity_token_and_commit_oid()
if auth_token and commit_oid:
empty_signals = json.dumps([])
result = db_signals.update_user_json_file(auth_token, commit_oid, empty_signals)
if result["success"]:
close_message = f"🔴⚠️ إغلاق طارئ للصفقة ⚠️🔴\n\n📝 السبب:\n{close_reason}"
send_result = send_message_to_api(close_message)
if send_result["success"]:
actions_performed.append("✅ تم إغلاق الصفقة وإرسال الإشعار")
else:
actions_performed.append("⚠️ تم إغلاق الصفقة لكن فشل الإرسال")
else:
actions_performed.append("❌ فشل في إغلاق الصفقة")
except Exception as e:
logger.error(f"Error closing trade: {e}")
actions_performed.append(f"❌ خطأ في إغلاق الصفقة: {str(e)}")
# 3. Edit existing signals
edit_matches = re.finditer(r'(.*?)', ai_response, re.DOTALL)
for edit_match in edit_matches:
try:
edit_xml = edit_match.group(1)
edit_data = {}
sl_match = re.search(r'(.*?)', edit_xml) or re.search(r'(.*?)', edit_xml)
if sl_match:
edit_data["stop_loss"] = pair_number(sl_match.group(1).strip())
tp_match = re.search(r'(.*?)', edit_xml)
if tp_match:
edit_data["take_profit"] = pair_number(tp_match.group(1).strip())
if edit_data:
signals_data = db_signals.fetch_json_from_github()
if signals_data["success"] and signals_data["data"]:
current_signal = signals_data["data"][0]
updates_made = []
if "stop_loss" in edit_data:
old_sl = current_signal.get("stop_loss", "N/A")
current_signal["stop_loss"] = edit_data["stop_loss"]
updates_made.append(f"stop_loss: {old_sl} → {edit_data['stop_loss']}")
if "take_profit" in edit_data:
old_tp = current_signal.get("take_profit", "N/A")
current_signal["take_profit"] = edit_data["take_profit"]
updates_made.append(f"take_profit: {old_tp} → {edit_data['take_profit']}")
if updates_made:
auth_token, commit_oid = db_signals.fetch_authenticity_token_and_commit_oid()
if auth_token and commit_oid:
updated_signal = json.dumps([current_signal])
result = db_signals.update_user_json_file(auth_token, commit_oid, updated_signal)
if result["success"]:
update_message = "🔄 تم تحديث الصفقة 🔄\n\n"
if "stop_loss" in edit_data:
update_message += f"🛑 تم تغيير وقف الخسارة إلى: {edit_data['stop_loss']} 🛑\n"
if "take_profit" in edit_data:
update_message += f"💰 تم تغيير الهدف إلى: {edit_data['take_profit']} 💰\n"
send_result = send_message_to_api(update_message)
if send_result["success"]:
actions_performed.append(f"✅ تم تحديث الصفقة: {', '.join(updates_made)}")
else:
actions_performed.append(f"⚠️ تم تحديث الصفقة لكن فشل إرسال الإشعار: {', '.join(updates_made)}")
else:
actions_performed.append("❌ فشل في حفظ تحديث الصفقة")
else:
actions_performed.append("❌ فشل في الحصول على رموز المصادقة لتحديث الصفقة")
else:
actions_performed.append("⚠️ لم يتم إجراء أي تغييرات على الصفقة")
else:
actions_performed.append("⚠️ لا توجد صفقة مفتوحة للتحديث")
else:
actions_performed.append("⚠️ تم تجاهل أمر التحديث: لا توجد بيانات للتحديث")
except Exception as e:
logger.error(f"Error processing edit: {e}")
actions_performed.append(f"❌ خطأ في معالجة تحديث الصفقة: {str(e)}")
# 4. Create new signals
signal_matches = re.finditer(r'(.*?)', ai_response, re.DOTALL)
for signal_match in signal_matches:
try:
signal_xml = signal_match.group(1)
pair_match = re.search(r'(.*?)', signal_xml)
type_match = re.search(r'(.*?)', signal_xml)
entry_match = re.search(r'(.*?)', signal_xml)
sl_match = re.search(r'(.*?)', signal_xml)
tp_match = re.search(r'(.*?)', signal_xml)
if all([pair_match, type_match, entry_match, sl_match, tp_match]):
signal_data = {
"pair": pair_match.group(1).strip(),
"type": type_match.group(1).strip(),
"entry": pair_number(entry_match.group(1).strip()),
"stop_loss": pair_number(sl_match.group(1).strip()),
"take_profit": pair_number(tp_match.group(1).strip()),
"timestamps": build_signal_timestamps()
}
auth_token, commit_oid = db_signals.fetch_authenticity_token_and_commit_oid()
if auth_token and commit_oid:
new_signal = json.dumps([signal_data])
result = db_signals.update_user_json_file(auth_token, commit_oid, new_signal)
if result["success"]:
ts = signal_data["timestamps"]
zones = ts.get("zones", {})
times_line = " | ".join([f"{c}: {t}" for c, t in zones.items()])
signal_message = (
f"🚨 إشارة جديدة XAUUSD\n\n"
f"📊 النوع: {signal_data['type']}\n"
f"🎯 الدخول: {signal_data['entry']}\n"
f"🛑 وقف الخسارة: {signal_data['stop_loss']}\n"
f"💰 الهدف: {signal_data['take_profit']}\n"
)
send_result = send_message_to_api(signal_message)
if send_result["success"]:
actions_performed.append("✅ تم إنشاء إشارة جديدة وإرسالها")
else:
actions_performed.append("⚠️ تم إنشاء إشارة لكن فشل الإرسال")
else:
actions_performed.append("❌ فشل في حفظ الإشارة")
else:
missing_fields = []
if not pair_match: missing_fields.append("pair")
if not type_match: missing_fields.append("type")
if not entry_match: missing_fields.append("entry")
if not sl_match: missing_fields.append("stop_loss")
if not tp_match: missing_fields.append("take_profit")
actions_performed.append(f"⚠️ إشارة غير مكتملة (مفقود: {', '.join(missing_fields)})")
except Exception as e:
logger.error(f"Error processing signal: {e}")
actions_performed.append(f"❌ خطأ في معالجة الإشارة: {str(e)}")
# 5. Send user messages
user_msg_matches = re.finditer(r'(.*?)', ai_response, re.DOTALL)
for user_msg_match in user_msg_matches:
try:
user_message = user_msg_match.group(1).strip()
if user_message:
send_result = send_message_to_api(user_message)
if send_result["success"]:
actions_performed.append("✅ تم إرسال رسالة للمستخدم")
else:
actions_performed.append("❌ فشل في إرسال رسالة للمستخدم")
else:
actions_performed.append("⚠️ رسالة فارغة تم تجاهلها")
except Exception as e:
logger.error(f"Error sending user message: {e}")
actions_performed.append(f"❌ خطأ في إرسال الرسالة: {str(e)}")
# 6. Create alerts (NEW FORMAT)
alert_matches = re.finditer(r'(.*?)', ai_response, re.DOTALL)
for alert_match in alert_matches:
try:
alert_xml = alert_match.group(1)
# Parse the new alert format
try:
parsed = parse_alert_block(alert_xml)
except ValueError as ve:
actions_performed.append(f"❌ Alert parse error: {str(ve)}")
continue
# Build final payload for /monitor
alert_payload = build_monitor_payload_from_alert(parsed, symbol="XAUUSD")
# Send to alert API
try:
response = requests.post(ALERT_API_URL, json=alert_payload, timeout=30)
if response.status_code == 200:
pm_count = len(alert_payload.get("price_messages", []))
details = []
if "tp" in alert_payload:
details.append(f"TP: {alert_payload['tp']}")
if "sl" in alert_payload:
details.append(f"SL: {alert_payload['sl']}")
details.append(f"Prices: {pm_count} مستوى/مستويات")
details_text = " | ".join(details)
else:
actions_performed.append(f"❌ فشل في إنشاء المنبه (كود: {response.status_code})")
except Exception as req_e:
actions_performed.append(f"❌ خطأ اتصال أثناء إنشاء المنبه: {str(req_e)}")
except Exception as e:
logger.error(f"Error creating alert (new format): {e}")
actions_performed.append(f"❌ خطأ في إنشاء المنبه (صيغة جديدة): {str(e)}")
logger.info(f"📋 Actions performed in order: {actions_performed}")
return actions_performed
def pair_number(val):
cleaned = re.sub(r'[^0-9\.\-]', '', val)
if cleaned == '' or cleaned == '-' or cleaned == '.':
return val.strip()
try:
num = float(cleaned)
return f"{num:.2f}".rstrip('0').rstrip('.') if '.' in f"{num:.2f}" else f"{num:.2f}"
except:
return val.strip()
def count_xml_tags(ai_response):
counts = {
'signals': len(re.findall(r'', ai_response)),
'user_messages': len(re.findall(r'', ai_response)),
'close_trades': len(re.findall(r'', ai_response)),
'alerts': len(re.findall(r'', ai_response)),
'edits': len(re.findall(r'', ai_response))
}
return counts
def edit_existing_signal(edit_data):
"""Update stop loss and/or take profit of existing signal"""
try:
signals_data = db_signals.fetch_json_from_github()
if not (signals_data["success"] and signals_data["data"]):
return {"success": False, "error": "No active signal found to edit"}
current_signal = signals_data["data"][0]
updates_made = []
if "stop_loss" in edit_data:
old_sl = current_signal.get("stop_loss", "N/A")
current_signal["stop_loss"] = edit_data["stop_loss"]
updates_made.append(f"stop_loss: {old_sl} → {edit_data['stop_loss']}")
if "take_profit" in edit_data:
old_tp = current_signal.get("take_profit", "N/A")
current_signal["take_profit"] = edit_data["take_profit"]
updates_made.append(f"take_profit: {old_tp} → {edit_data['take_profit']}")
if not updates_made:
return {"success": False, "error": "No changes to apply"}
auth_token, commit_oid = db_signals.fetch_authenticity_token_and_commit_oid()
if auth_token and commit_oid:
updated_signal = json.dumps([current_signal])
result = db_signals.update_user_json_file(auth_token, commit_oid, updated_signal)
if result["success"]:
return {"success": True, "updates": updates_made}
else:
return {"success": False, "error": "Failed to update signal"}
else:
return {"success": False, "error": "Failed to get auth tokens"}
except Exception as e:
logger.error(f"Error editing signal: {e}")
return {"success": False, "error": str(e)}
@app.route('/analysis_now', methods=['POST'])
def analysis_now():
try:
data = request.get_json()
alert_message = data.get('message', '') if data else ''
logger.info(f"🔔 Received alert message: {alert_message}")
chat_history = build_chat_history(alert_message)
api_url = AI_API_URL_O1 if has_active_signal() else AI_API_URL
ai_response, updated_chat = call_o1_ai_api(chat_history, api_url=api_url)
tag_counts = count_xml_tags(ai_response)
logger.info(f"📊 XML tags found: {tag_counts}")
has_close_trade = tag_counts['close_trades'] > 0
has_signal = tag_counts['signals'] > 0
if has_close_trade and has_signal:
logger.info("🔄 Both close_trade and signal found - executing close_trade first")
actions = parse_ai_response(ai_response)
return jsonify({
"success": True,
"message": "Analysis completed successfully",
"xml_tags_found": tag_counts,
"execution_note": "close_trade executed before signal" if (has_close_trade and has_signal) else "normal execution",
"actions_performed": actions,
"total_actions": len(actions),
"ai_response_preview": ai_response[:200] + "..." if len(ai_response) > 200 else ai_response
})
except Exception as e:
logger.error(f"Error in analysis_now: {e}")
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/start_analysis', methods=['GET'])
def start_analysis():
try:
logger.info("🚀 Starting initial analysis...")
chat_history = build_chat_history()
api_url = AI_API_URL_O1 if has_active_signal() else AI_API_URL
ai_response, updated_chat = call_o1_ai_api(chat_history, api_url=api_url)
tag_counts = count_xml_tags(ai_response)
logger.info(f"📊 XML tags found: {tag_counts}")
has_close_trade = tag_counts['close_trades'] > 0
has_signal = tag_counts['signals'] > 0
if has_close_trade and has_signal:
logger.info("🔄 Both close_trade and signal found - executing close_trade first")
actions = parse_ai_response(ai_response)
return jsonify({
"success": True,
"message": "Initial analysis completed successfully",
"xml_tags_found": tag_counts,
"execution_note": "close_trade executed before signal" if (has_close_trade and has_signal) else "normal execution",
"actions_performed": actions,
"total_actions": len(actions),
"ai_response_preview": ai_response[:200] + "..." if len(ai_response) > 200 else ai_response
})
except Exception as e:
logger.error(f"Error in start_analysis: {e}")
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/test_actions', methods=['POST'])
def test_actions():
try:
data = request.get_json()
test_response = data.get('test_response', '') if data else ''
if not test_response:
return jsonify({
"success": False,
"error": "Please provide test_response in the request body"
}), 400
tag_counts = count_xml_tags(test_response)
logger.info(f"🧪 Testing with XML tags: {tag_counts}")
has_close_trade = tag_counts['close_trades'] > 0
has_signal = tag_counts['signals'] > 0
if has_close_trade and has_signal:
logger.info("🔄 Test: Both close_trade and signal found - close_trade will execute first")
actions = parse_ai_response(test_response)
return jsonify({
"success": True,
"message": "Test completed successfully",
"xml_tags_found": tag_counts,
"execution_note": "close_trade would execute before signal" if (has_close_trade and has_signal) else "normal execution order",
"actions_performed": actions,
"total_actions": len(actions),
"test_response_preview": test_response[:200] + "..." if len(test_response) > 200 else test_response
})
except Exception as e:
logger.error(f"Error in test_actions: {e}")
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/health', methods=['GET'])
def health_check():
return jsonify({
"status": "healthy",
"timestamp": datetime.now().isoformat(),
"system": "XAUUSD Trading AI",
"execution_order": [
"1. Save analysis (latest 3, UTC timestamps, newest first)",
"2. Close trades (HIGH PRIORITY)",
"3. Create new signals",
"4. Send user messages",
"5. Create alerts"
]
})
@app.route('/', methods=['GET'])
def index():
return jsonify({
"message": "نظام الذكاء الاصطناعي لإشارات تداول XAUUSD",
"endpoints": {
"/start_analysis": "بدء التحليل الأولي (GET)",
"/analysis_now": "webhook للتحليل الفوري (POST)",
"/test_actions": "اختبار معالجة XML tags (POST)",
"/health": "فحص حالة النظام (GET)"
},
"version": "2.4.0",
"execution_priority": "close_trade → signal → send_user → alert"
})
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=7860)