Dooratre commited on
Commit
0fb8e13
·
verified ·
1 Parent(s): 5951a34

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -224
app.py CHANGED
@@ -214,12 +214,17 @@ def count_xml_tags(ai_response):
214
  return counts
215
 
216
  def save_latest_final_analysis(final_text):
 
 
 
217
  try:
218
  record = {
219
  "timestamp_utc": datetime.now(timezone.utc).isoformat(),
220
  "response": final_text
221
  }
222
- payload_text = json.dumps(record, ensure_ascii=False)
 
 
223
  auth_token, commit_oid = db_analysis.fetch_authenticity_token_and_commit_oid()
224
  if auth_token and commit_oid:
225
  result = db_analysis.update_user_json_file(auth_token, commit_oid, payload_text)
@@ -248,10 +253,41 @@ def get_chart_screenshot(symbol="XAUUSD", exchange="OANDA", interval="15m", indi
248
  resp.raise_for_status()
249
  return resp.json()
250
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  def fetch_signals_raw():
252
- # Returns dict with shape:
253
- # { "has_active_signal": bool, "active_signal": [... or None],
254
- # "has_scenario": bool, "scenario": {... or None}, "raw": original }
 
 
 
 
 
 
 
 
255
  out = {
256
  "has_active_signal": False,
257
  "active_signal": None,
@@ -264,28 +300,32 @@ def fetch_signals_raw():
264
  if res["success"] and res["data"]:
265
  raw = res["data"][0]
266
  out["raw"] = raw
267
- # Determine if active signal (list with an object containing pair/type)
 
268
  if isinstance(raw, list) and raw and isinstance(raw[0], dict) and "pair" in raw[0] and "type" in raw[0]:
269
  out["has_active_signal"] = True
270
  out["active_signal"] = raw
271
- # Determine if scenario (object with key "scenario")
 
272
  elif isinstance(raw, dict) and "scenario" in raw:
273
  out["has_scenario"] = True
274
  out["scenario"] = raw["scenario"]
 
 
 
 
 
275
  except Exception as e:
276
  logger.error(f"Error fetching signals/scenario: {e}")
277
  return out
278
 
279
  def save_scenario_object(scenario_obj):
280
- # scenario_obj expected shape:
281
- # {
282
- # "Buy": {"at": "...", "SL": "...", "TP": "..."},
283
- # "Sell": {"at": "...", "SL": "...", "TP": "..."},
284
- # "timestamps": {...}
285
- # }
286
  try:
287
- payload = {"scenario": scenario_obj}
288
- payload_text = json.dumps(payload, ensure_ascii=False)
289
  auth_token, commit_oid = db_signals.fetch_authenticity_token_and_commit_oid()
290
  if auth_token and commit_oid:
291
  result = db_signals.update_user_json_file(auth_token, commit_oid, payload_text)
@@ -308,252 +348,126 @@ def post_scenario_to_tracker(buy_at, sell_at):
308
  def build_initial_chat_history(alert_message=None):
309
  chat_history = []
310
 
311
- # System content with new scenario-first instructions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  try:
313
  system_data = db_system.fetch_json_from_github()
314
- system_content = ""
315
  if system_data["success"] and system_data["data"]:
316
- system_content = system_data["data"][0]["response"]
317
-
318
- standard_instruction = (
319
- "أنت تعمل بتحليل متعدد المراحل بالصور ثم تُنهي بـ <final>.\n"
320
- "لطلب صورة استخدم:\n"
321
- "<img><XAUUSD><QQE>//يمكن إضافة مؤشرات مثل <Zerp_Lag>...</QQE><15m></img>\n"
322
- "لا تُنشئ <signal> بعد الآن. بدلاً من ذلك، عندما تُنهي التحليل أرسل:\n"
323
- "<final>\n"
324
- "<Analysis>ملخص</Analysis>\n"
325
- "<wait>سبب</wait>\n"
326
- "<scenario>\n"
327
- "<Buy><@>شرط الدخول/المستوى</@><SL>...</SL><TP>...</TP></Buy>\n"
328
- "<Sell><@>شرط الدخول/المستوى</@><SL>...</SL><TP>...</TP></Sell>\n"
329
- "</scenario>\n"
330
- "<Edit>للتحديث (SL/TP) إذا وُجدت صفقة مفتوحة</Edit>\n"
331
- "<send_group>رسالة قصيرة</send_group>\n"
332
- "<Alert>إن رغبت بمنبه الأسعار بصيغته الجديدة</Alert>\n"
333
- "</final>\n"
334
- "إن كان هناك صفقة مفتوحة بالفعل فلا تُنشئ سيناريو جديد؛ فقط حلّل وتتبع الصفقة وقدّم خطة.\n"
335
- "بعد كل صورة سيتم تزويدك بسعر السوق الحالي ليساعدك على فهم الحركة أثناء المحادثة."
336
- )
337
 
 
 
338
  times = get_time_zones()
339
  time_info = "\n".join([f"{city}: {time}" for city, time in times.items()])
340
-
 
 
 
341
  chat_history.append({
342
  "role": "system",
343
- "content": f'''
344
-
345
- time :
346
- {time_info}
347
- You are AI trading system working in tele group for trade on XAUUSD
348
-
349
- GOAL :
350
-
351
- Your goal is get is day about 500 pip which is 50$ in GOLD system
352
- not 500 pip in one trade but in alot of trades with
353
-
354
- TP about 50 - 70 - 100 pip
355
- SL about 10 - 30 - 50 pip
356
-
357
- NOTE : in gold every 10 pip 1 $
358
-
359
- you can manage it based on your mind
360
-
361
- you can not start trade directly NO , your task is analysis and create scenario BUY / SELL each other we need
362
-
363
- now i will start conversation with you in this conversation you will request images for the chart with Indicators , timeframes You need .
364
-
365
- you can request 1 image chart in message with 1 timeframe and MAX 3 indicators (for make the chart more clear)
366
-
367
- Also you can make the chat like you want , request images and i will fast pring it to you .
368
-
369
- How you gonna request the image you want ?
370
-
371
- it is easy just use this :
372
- <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>
373
- Here All Timeframe :
374
-
375
- "1m"
376
- "3m"
377
- "5m"
378
- "15m"
379
- "30m"
380
- "1h"
381
- "2h"
382
- "4h"
383
- "1D"
384
- "1W"
385
- "1M"
386
-
387
- Here All indicators id :
388
-
389
- 1 . ADX - <ADX>
390
- 2 . VWAP - <VWAP>
391
- 3 . Fibonacci - <FIBO>
392
- 4 . RSI with Divergence - <RSI>
393
- 5 . EMA 20/50/100/200 - <EMA>
394
- 6 . Breakout channle - <BRCH>
395
- maybe num 6 you don't know it here :
396
- This tool draws areas called "Smart Money Breakout Channels" based on the analysis of adjusted price volatility. These channels are identified when price volatility reaches a local low and then rises, indicating the potential presence of smart money activity in that area. A channel is drawn between the highest and lowest prices during that period and continues until the price breaks through the defined boundaries, either by closing a strong candle or by touching the boundaries.
397
-
398
- Okay what if you think that is enough images then you will response with <final></final> Action
399
-
400
- what is <final> Action ?
401
- you will use the final xml response when you get enough images from the chart and then you will response with final and into it the settings
402
- <final>
403
- <Analysis>here put your summrasion of the chat analysis</Analysis>
404
- <wait>this is just using when you are Acually added a scenario and you don't want to change it, put here the reason about why you don't want to change the scenario</wait>
405
- Now the important one (optional):
406
-
407
- <scenario>
408
- <Buy><@>...</@><SL>...</SL><TP>...</TP></Buy> // make sure just put price number no text
409
- <Sell><@>...</@><SL>...</SL><TP>...</TP></Sell> // make sure just put price number no text
410
- </scenario>
411
- we need scalping plans
412
-
413
- TP about 50 - 70 - 100 pip
414
- SL about 10 - 30 - 50 pip
415
-
416
- NOTE : if one of scenarios running mean there trade active don't create new scnario just response without it
417
-
418
-
419
-
420
- after analysis complete from the chat you will decide finally the scenario and we will save it until one of scenario HIT and then gonna tell you what heppen to start new chat with you for maybe we need to
421
-
422
- 📊 Dynamic Trade Editing Feature
423
- <Edit><stop_lose>...</stop_lose><take_profit>...</take_profit></Edit>
424
-
425
- Here Another tool optional using just if you acully need it
426
- that will chage the SL TP for the active trade not the (SCENARIO)
427
- Also you just can change one them like :
428
-
429
- <Edit><take_profit>...</take_profit></Edit>
430
- that will make it just change for TP only (you can use it also for SL like you want)
431
-
432
- Okay to here everything clear but i don't want to manual create message for the tele group and send it , i want you to send message to them using :
433
-
434
- <send_group>what you put here gonna send to the tele group and it is not optional you need to use it every time you use final , to make the group in clear(need to be in arabic becasue all of members Arabic)</send_group>
435
-
436
- okay now the important question How many chats gonna chat with you or how and when you want me back to you to create new chat ?
437
-
438
- Good Q and i have the Good answer we gonna use Alert by time and price also saved messages in the price here we go :
439
-
440
- <Alert>
441
- <price>3...<message>here.......</message></price> //put more than 1 price as you want for next analysis , for messages put the saved messages in the prices for Auto send to user just if the price hit as + 50 pips ....etc of messages (all in arabic)
442
- ..... may more than one price
443
- <duration_min>10(EXP)</duration_min> // that using for if the price still in same place and don't moving alot so the duration if end gonna back to you
444
- </Alert>
445
-
446
- I think it’s very clear. Let me explain only the structure:
447
-
448
- <price>3...<message>here.......</message></price>
449
-
450
-
451
- price → the price level at which you want the next analysis to be triggered.
452
-
453
- message → the auto message that will be sent to the group when that price is hit.
454
-
455
- If the group already has an active trade, you should use the alert as your main tool to track what’s happening with the trade. You can set it at 10, 30, or 50 pips (or however you prefer), but keep it close enough so the group can clearly see how the trade is moving.
456
-
457
- 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.
458
-
459
- You can also give it a time limit (in minutes) in case neither target is reached and the price keeps fluctuating in between.
460
-
461
- ⚠️ 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.
462
- </final>
463
-
464
- NOTE : All XML need to be in the final to work
465
-
466
- Now for the how to request image you will find it in the chat with full IDs for indicators
467
- GOOD LUCK
468
-
469
- i created a summary of news maybe you will find it helpful
470
-
471
- {system_content}
472
-
473
-
474
-
475
-
476
- '''
477
  })
478
  except Exception as e:
479
- logger.error(f"Error fetching system data: {e}")
480
  chat_history.append({
481
  "role": "system",
482
- "content": "ابدأ التحليل بالصيغ الجديدة. استخدم <img> لطلب الصور ثم اختم بـ <final> وضمنه <scenario>."
483
  })
484
 
485
  multipart_content = []
486
 
487
- # Previous analysis (optional)
488
  try:
489
  analysis_data = db_analysis.fetch_json_from_github()
490
  prev_text = ""
491
  if analysis_data["success"] and analysis_data["data"]:
492
- raw_text = analysis_data["data"][0]["response"]
 
 
 
 
 
 
493
  prev_text = str(raw_text)[:1500]
494
  if prev_text:
495
- multipart_content.append({"type": "text", "text": f"التحليلات السابقة (أقرب سجل محفوظ):\n{prev_text}"})
496
  except Exception as e:
497
  logger.error(f"Error fetching previous analysis: {e}")
498
 
499
  # Alert + current context (active signal or scenario or none)
500
  try:
501
- state = fetch_signals_raw()
502
  times = get_time_zones()
503
  time_info = "\n".join([f"{city}: {time}" for city, time in times.items()])
 
504
 
505
  message_content = ""
506
  if alert_message:
507
- message_content += f"رسالة المنبه: {alert_message}\n\n"
508
  else:
509
- message_content += "لا توجد رسالة من المنبه (أول تشغيل)\n\n"
510
-
511
- # Compose context for AI based on state
512
- prices_text = format_live_prices_text(get_live_prices_for_pairs())
513
 
514
- if state["has_active_signal"]:
515
  sig = state["active_signal"][0]
516
  message_content += (
517
- "The user is currently in an active trade (one of the scenarios has been triggered):\n"
518
- f"- Pair: {sig.get('pair','N/A')}\n"
519
- f"- Type: {sig.get('type','N/A')}\n"
520
- f"- Entry: {sig.get('entry','N/A')}\n"
521
- f"- Stop Loss: {sig.get('stop_loss','N/A')}\n"
522
- f"- Take Profit: {sig.get('take_profit','N/A')}\n"
523
- "AI Guidance: Do NOT create any new scenario. Only analyze and track the current trade, providing a plan and instructions.\n\n"
524
- "Important Instructions:\n"
525
- "- Provide only **ONE <img>** at a time in the format:\n"
526
- " <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>\n"
527
- "- Use <final> **only** if you think the conversation already has enough information to conclude.\n\n"
528
- f"Current Time:\n{time_info}\n\n"
529
- f"Live Prices:\n{prices_text}"
530
- )
531
- elif state["has_scenario"]:
532
  sc = state["scenario"]
533
  buy = sc.get("Buy", {})
534
  sell = sc.get("Sell", {})
535
  message_content += (
536
- "There is a previously saved scenario that hasn’t been triggered yet. Creating a new scenario will replace the old one:\n"
537
- f"- Buy: @={buy.get('at','N/A')}, SL={buy.get('SL','N/A')}, TP={buy.get('TP','N/A')}\n"
538
- f"- Sell: @={sell.get('at','N/A')}, SL={sell.get('SL','N/A')}, TP={sell.get('TP','N/A')}\n\n"
539
- "AI Guidance: Continue analyzing. If you want to update the scenario, send a <final> with a new <scenario> to replace it. If no new scenario is created, we will wait for one of the scenarios to be triggered.\n\n"
540
- "Important Instructions:\n"
541
- "- Provide only **ONE <img>** at a time:\n"
542
- " <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>\n"
543
- "- Use <final> **only** if you believe there is enough information in the conversation.\n\n"
544
- f"Current Time:\n{time_info}\n\n"
545
- f"Live Prices:\n{prices_text}"
546
- )
547
  else:
548
  message_content += (
549
- "No scenario or active trade exists (first run). Please analyze and create the first scenario within <final> when done.\n\n"
550
- "Important Instructions:\n"
551
- "- Provide only **ONE <img>** at a time:\n"
552
- " <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>>\n"
553
- "- Use <final> **only** if you believe there is enough information in the conversation to conclude.\n\n"
554
- f"Current Time:\n{time_info}\n\n"
555
- f"Live Prices:\n{prices_text}"
556
- )
557
 
558
  multipart_content.append({"type": "text", "text": message_content})
559
  except Exception as e:
@@ -687,8 +601,8 @@ def build_image_reply_user_turn(png_url):
687
  {"type": "text", "text": (
688
  "Your image is ready for analysis.\n"
689
  "- Request **only ONE <img>** at a time for each chart.\n <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>\n"
690
- "- Use <final> **only** if you think the conversation already has enough information to conclude.\n"
691
- "Do not send multiple images at once. \n\n look i want you to be smart analysis andd choose image and indicators like pro not idiot okay ? go ahead and tell me what next"
692
  )},
693
  {"type": "text", "text": f"Current Time:\n{time_info}"},
694
  {"type": "text", "text": f"Live Prices:\n{prices_text}"}
@@ -868,15 +782,24 @@ def parse_and_execute_final(final_xml_full, final_inner):
868
  return actions_performed
869
 
870
  def edit_existing_signal(edit_data):
 
 
 
871
  try:
872
  signals_data = db_signals.fetch_json_from_github()
873
  if not (signals_data["success"] and signals_data["data"]):
874
  return {"success": False, "error": "No active signal found to edit"}
875
 
876
- current_signal = None
877
  raw = signals_data["data"][0]
 
878
  if isinstance(raw, list) and raw and isinstance(raw[0], dict):
879
  current_signal = raw[0]
 
 
 
 
 
 
880
  else:
881
  return {"success": False, "error": "No active signal found to edit"}
882
 
@@ -897,9 +820,11 @@ def edit_existing_signal(edit_data):
897
 
898
  auth_token, commit_oid = db_signals.fetch_authenticity_token_and_commit_oid()
899
  if auth_token and commit_oid:
900
- updated_signal = json.dumps([current_signal])
901
- result = db_signals.update_user_json_file(auth_token, commit_oid, updated_signal)
902
- if result["success"]:
 
 
903
  return {"success": True, "updates": updates_made}
904
  else:
905
  return {"success": False, "error": "Failed to update signal"}
 
214
  return counts
215
 
216
  def save_latest_final_analysis(final_text):
217
+ """
218
+ Save ONLY the latest final analysis to db_analysis as an array: [ { ... } ]
219
+ """
220
  try:
221
  record = {
222
  "timestamp_utc": datetime.now(timezone.utc).isoformat(),
223
  "response": final_text
224
  }
225
+ # Wrap as array for saving
226
+ payload_list = [record]
227
+ payload_text = json.dumps(payload_list, ensure_ascii=False)
228
  auth_token, commit_oid = db_analysis.fetch_authenticity_token_and_commit_oid()
229
  if auth_token and commit_oid:
230
  result = db_analysis.update_user_json_file(auth_token, commit_oid, payload_text)
 
253
  resp.raise_for_status()
254
  return resp.json()
255
 
256
+ def load_system_prompt_from_files(has_active_signal: bool, has_scenario: bool):
257
+ """
258
+ Returns system prompt string based on current state:
259
+ - If has_active_signal: use prompt_signal.txt
260
+ - Else (no active signal): use prompt_scenario.txt
261
+ If files are missing, fall back to a minimal default in Arabic.
262
+ """
263
+ prompt_file = "prompt_signal.txt" if has_active_signal else "prompt_scenario.txt"
264
+ try:
265
+ with open(prompt_file, "r", encoding="utf-8") as f:
266
+ text = f.read().strip()
267
+ if text:
268
+ return text
269
+ except Exception as e:
270
+ logger.warning(f"Failed to load system prompt from {prompt_file}: {e}")
271
+
272
+ # Fallbacks
273
+ if has_active_signal:
274
+ return "وضع متابعة الصفقة: لا تنشئ سيناريو جديد. حلّل الصفقة الحالية فقط ويمكنك استخدام <Edit> و<send_group> و<Alert>."
275
+ else:
276
+ return "وضع بناء السيناريو: حلّل وأنشئ سيناريو داخل <final> يتضمن <scenario> مع Buy/Sell و(@/SL/TP)."
277
+
278
+
279
  def fetch_signals_raw():
280
+ """
281
+ Returns:
282
+ {
283
+ "has_active_signal": bool,
284
+ "active_signal": list or None, # when active, it's a list with 1 object (normalized)
285
+ "has_scenario": bool,
286
+ "scenario": dict or None,
287
+ "raw": original
288
+ }
289
+ Accepts both legacy object and new array shapes, but normalizes in-memory to arrays when needed.
290
+ """
291
  out = {
292
  "has_active_signal": False,
293
  "active_signal": None,
 
300
  if res["success"] and res["data"]:
301
  raw = res["data"][0]
302
  out["raw"] = raw
303
+
304
+ # If array and first element has pair/type => active signal
305
  if isinstance(raw, list) and raw and isinstance(raw[0], dict) and "pair" in raw[0] and "type" in raw[0]:
306
  out["has_active_signal"] = True
307
  out["active_signal"] = raw
308
+
309
+ # If object with "scenario" => scenario mode
310
  elif isinstance(raw, dict) and "scenario" in raw:
311
  out["has_scenario"] = True
312
  out["scenario"] = raw["scenario"]
313
+
314
+ # Legacy: single signal object (not array) => treat as active signal
315
+ elif isinstance(raw, dict) and "pair" in raw and "type" in raw:
316
+ out["has_active_signal"] = True
317
+ out["active_signal"] = [raw]
318
  except Exception as e:
319
  logger.error(f"Error fetching signals/scenario: {e}")
320
  return out
321
 
322
  def save_scenario_object(scenario_obj):
323
+ """
324
+ Save scenario to db_signals as an array: [ { "scenario": {...} } ]
325
+ """
 
 
 
326
  try:
327
+ payload_list = [{"scenario": scenario_obj}]
328
+ payload_text = json.dumps(payload_list, ensure_ascii=False)
329
  auth_token, commit_oid = db_signals.fetch_authenticity_token_and_commit_oid()
330
  if auth_token and commit_oid:
331
  result = db_signals.update_user_json_file(auth_token, commit_oid, payload_text)
 
348
  def build_initial_chat_history(alert_message=None):
349
  chat_history = []
350
 
351
+ # Determine current state (active signal vs scenario/no state)
352
+ try:
353
+ state = fetch_signals_raw()
354
+ except Exception as e:
355
+ logger.error(f"Error determining state for system prompt: {e}")
356
+ state = {"has_active_signal": False, "has_scenario": False}
357
+
358
+ has_active = state.get("has_active_signal", False)
359
+ has_scen = state.get("has_scenario", False)
360
+
361
+ # Load system prompt from files based on state
362
+ try:
363
+ system_base_prompt = load_system_prompt_from_files(has_active, has_scen)
364
+ except Exception as e:
365
+ logger.error(f"Error loading system prompt: {e}")
366
+ system_base_prompt = "ابدأ التحليل وفق حالتك (صفقة نشطة أو سيناريو)."
367
+
368
+ # Fetch news summary from db_system and name it 'news'
369
+ news = ""
370
  try:
371
  system_data = db_system.fetch_json_from_github()
 
372
  if system_data["success"] and system_data["data"]:
373
+ news = system_data["data"][0].get("response", "") or ""
374
+ except Exception as e:
375
+ logger.error(f"Error fetching news from db_system: {e}")
376
+ news = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
 
378
+ # Build system turn (system prompt + time zones + news)
379
+ try:
380
  times = get_time_zones()
381
  time_info = "\n".join([f"{city}: {time}" for city, time in times.items()])
382
+ parts = [system_base_prompt, f"[Time Zones]\n{time_info}"]
383
+ if news.strip():
384
+ parts.append(f"[News]\n{news.strip()}")
385
+ system_full = "\n\n".join(parts)
386
  chat_history.append({
387
  "role": "system",
388
+ "content": system_full
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  })
390
  except Exception as e:
391
+ logger.error(f"Error building system turn: {e}")
392
  chat_history.append({
393
  "role": "system",
394
+ "content": system_base_prompt
395
  })
396
 
397
  multipart_content = []
398
 
399
+ # Previous analysis (optional) - Read from db_analysis; supports array and legacy object
400
  try:
401
  analysis_data = db_analysis.fetch_json_from_github()
402
  prev_text = ""
403
  if analysis_data["success"] and analysis_data["data"]:
404
+ raw_obj = analysis_data["data"][0]
405
+ if isinstance(raw_obj, list) and raw_obj:
406
+ raw_text = raw_obj[-1].get("response", "")
407
+ elif isinstance(raw_obj, dict):
408
+ raw_text = raw_obj.get("response", "")
409
+ else:
410
+ raw_text = ""
411
  prev_text = str(raw_text)[:1500]
412
  if prev_text:
413
+ multipart_content.append({"type": "text", "text": f"LAST ANALYSIS HAPPEN :\n{prev_text}"})
414
  except Exception as e:
415
  logger.error(f"Error fetching previous analysis: {e}")
416
 
417
  # Alert + current context (active signal or scenario or none)
418
  try:
 
419
  times = get_time_zones()
420
  time_info = "\n".join([f"{city}: {time}" for city, time in times.items()])
421
+ prices_text = format_live_prices_text(get_live_prices_for_pairs())
422
 
423
  message_content = ""
424
  if alert_message:
425
+ message_content += f" ALERT MESSAGE: {alert_message}\n\n"
426
  else:
427
+ message_content += "NO Any Message from ALERT\n\n"
 
 
 
428
 
429
+ if has_active:
430
  sig = state["active_signal"][0]
431
  message_content += (
432
+ "The user is currently in an active trade (one of the scenarios has been triggered):\n"
433
+ f"- Pair: {sig.get('pair','N/A')}\n"
434
+ f"- Type: {sig.get('type','N/A')}\n"
435
+ f"- Entry: {sig.get('entry','N/A')}\n"
436
+ f"- Stop Loss: {sig.get('stop_loss','N/A')}\n"
437
+ f"- Take Profit: {sig.get('take_profit','N/A')}\n"
438
+ "Important Instructions:\n"
439
+ "- Provide only ONE <img> at a time in the format:\n"
440
+ " <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>\n"
441
+ "- Use <final> only if you think the conversation already has enough information to conclude.\n\n"
442
+ f"Current Time:\n{time_info}\n\n"
443
+ f"Live Prices:\n{prices_text}"
444
+ )
445
+ elif has_scen:
 
446
  sc = state["scenario"]
447
  buy = sc.get("Buy", {})
448
  sell = sc.get("Sell", {})
449
  message_content += (
450
+ "There is a previously saved scenario that hasn’t been triggered yet. Creating a new scenario will replace the old one:\n"
451
+ f"- Buy: @={buy.get('at','N/A')}, SL={buy.get('SL','N/A')}, TP={buy.get('TP','N/A')}\n"
452
+ f"- Sell: @={sell.get('at','N/A')}, SL={sell.get('SL','N/A')}, TP={sell.get('TP','N/A')}\n\n"
453
+ "AI Guidance: Continue analyzing. If you want to update the scenario, send a <final> with a new <scenario> to replace it. If no new scenario is created, we will wait for one of the scenarios to be triggered.\n\n"
454
+ "Important Instructions:\n"
455
+ "- Provide only ONE <img> at a time:\n"
456
+ " <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>\n"
457
+ "- Use <final> only if you believe there is enough information in the conversation.\n\n"
458
+ f"Current Time:\n{time_info}\n\n"
459
+ f"Live Prices:\n{prices_text}"
460
+ )
461
  else:
462
  message_content += (
463
+ "No scenario or active trade exists (first run). Please analyze and create the first scenario within <final> when done.\n\n"
464
+ "Important Instructions:\n"
465
+ "- Provide only ONE <img> at a time:\n"
466
+ " <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>\n"
467
+ "- Use <final> only if you believe there is enough information in the conversation to conclude.\n\n"
468
+ f"Current Time:\n{time_info}\n\n"
469
+ f"Live Prices:\n{prices_text}"
470
+ )
471
 
472
  multipart_content.append({"type": "text", "text": message_content})
473
  except Exception as e:
 
601
  {"type": "text", "text": (
602
  "Your image is ready for analysis.\n"
603
  "- Request **only ONE <img>** at a time for each chart.\n <img><XAUUSD><id_for_IND><id_for_IND><id_for_IND>// you can use maxumum 3 indecators in same image<timeframe></img>\n"
604
+ "- Use <final> **only** if you think the conversation already has enough information to conclude. ELSE request new image\n"
605
+ "Do not send multiple images at once. \n\n look i want you to be smart analysis and choose image and indicators like pro not idiot okay ? go ahead and tell me what next"
606
  )},
607
  {"type": "text", "text": f"Current Time:\n{time_info}"},
608
  {"type": "text", "text": f"Live Prices:\n{prices_text}"}
 
782
  return actions_performed
783
 
784
  def edit_existing_signal(edit_data):
785
+ """
786
+ Edit the active signal (assumed stored as an array with a single signal object) and save back as an array.
787
+ """
788
  try:
789
  signals_data = db_signals.fetch_json_from_github()
790
  if not (signals_data["success"] and signals_data["data"]):
791
  return {"success": False, "error": "No active signal found to edit"}
792
 
 
793
  raw = signals_data["data"][0]
794
+ # Expecting current storage shape to be an array; ensure we handle both array and object safely
795
  if isinstance(raw, list) and raw and isinstance(raw[0], dict):
796
  current_signal = raw[0]
797
+ container_is_list = True
798
+ elif isinstance(raw, dict):
799
+ # Legacy/object format, normalize to a single-element list
800
+ current_signal = raw
801
+ container_is_list = False
802
+ logger.warning("Signals DB returned an object; normalizing to array on save.")
803
  else:
804
  return {"success": False, "error": "No active signal found to edit"}
805
 
 
820
 
821
  auth_token, commit_oid = db_signals.fetch_authenticity_token_and_commit_oid()
822
  if auth_token and commit_oid:
823
+ # Always save as array
824
+ updated_signal_list = [current_signal]
825
+ updated_json = json.dumps(updated_signal_list, ensure_ascii=False)
826
+ result = db_signals.update_user_json_file(auth_token, commit_oid, updated_json)
827
+ if result.get("success"):
828
  return {"success": True, "updates": updates_made}
829
  else:
830
  return {"success": False, "error": "Failed to update signal"}