Update app.py
Browse files
app.py
CHANGED
|
@@ -79,30 +79,22 @@ ACTIVE_LOCK = threading.Lock()
|
|
| 79 |
|
| 80 |
# ================ db_signals reader helper ================
|
| 81 |
# If you already have an official reader, replace this function with it.
|
| 82 |
-
def read_user_json_file(authenticity_token: str, commit_oid: str)
|
| 83 |
"""
|
| 84 |
-
|
| 85 |
-
Your db_signals module can implement this properly. Here we show a pattern:
|
| 86 |
-
- It likely needs the file URL or the API route you already use in update_user_json_file.
|
| 87 |
"""
|
| 88 |
-
|
| 89 |
-
# For demonstration, we assume you can call a function db_signals.get_user_json_file()
|
| 90 |
-
# that returns {"success": True, "content": "..."} (JSON string).
|
| 91 |
-
# If you don't have it, implement it there and call it here.
|
| 92 |
-
from db_signals import get_user_json_file # implement this helper in your module
|
| 93 |
res = get_user_json_file(authenticity_token, commit_oid)
|
| 94 |
if not res.get("success"):
|
| 95 |
raise RuntimeError(f"Failed to read user json file: {res}")
|
| 96 |
content = res.get("content", "")
|
|
|
|
|
|
|
| 97 |
try:
|
| 98 |
-
return json.loads(content)
|
| 99 |
except Exception:
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
parsed = json.loads(content)
|
| 103 |
-
return parsed
|
| 104 |
-
except Exception:
|
| 105 |
-
return {}
|
| 106 |
|
| 107 |
# ================ Utilities ================
|
| 108 |
def safe_float_join(whole, decimal):
|
|
@@ -263,19 +255,21 @@ def call_analysis_now(text: str):
|
|
| 263 |
# ================ GitHub signal writers/readers ================
|
| 264 |
def write_active_scenario_to_github(pair: str, side: str, entry: str, sl: str, tp: str, dt_utc: datetime.datetime, preserve_timestamps: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
| 265 |
"""
|
| 266 |
-
|
| 267 |
-
|
| 268 |
"""
|
| 269 |
timestamps = preserve_timestamps if preserve_timestamps else format_zone_times(dt_utc)
|
| 270 |
-
|
| 271 |
"pair": pair,
|
| 272 |
"type": side,
|
| 273 |
"entry": str(entry),
|
| 274 |
"stop_loss": str(sl),
|
| 275 |
"take_profit": str(tp),
|
| 276 |
"timestamps": timestamps
|
| 277 |
-
}
|
| 278 |
-
|
|
|
|
|
|
|
| 279 |
try:
|
| 280 |
authenticity_token, commit_oid = fetch_authenticity_token_and_commit_oid()
|
| 281 |
if not authenticity_token or not commit_oid:
|
|
@@ -284,7 +278,7 @@ def write_active_scenario_to_github(pair: str, side: str, entry: str, sl: str, t
|
|
| 284 |
return {"success": False, "message": msg}
|
| 285 |
result = update_user_json_file(authenticity_token, commit_oid, new_content)
|
| 286 |
if result.get("success"):
|
| 287 |
-
logger.info("[GITHUB] signals updated with active scenario.")
|
| 288 |
return {"success": True}
|
| 289 |
else:
|
| 290 |
logger.error(f"[GITHUB] Update failed: {result}")
|
|
@@ -312,30 +306,45 @@ def clear_github_signals_to_empty_list() -> Dict[str, Any]:
|
|
| 312 |
|
| 313 |
def read_current_scenario_from_github() -> Optional[Dict[str, Any]]:
|
| 314 |
"""
|
| 315 |
-
Reads the pre-activation scenario JSON
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
"
|
| 319 |
-
|
| 320 |
-
|
|
|
|
|
|
|
| 321 |
}
|
| 322 |
-
|
|
|
|
| 323 |
"""
|
| 324 |
try:
|
| 325 |
authenticity_token, commit_oid = fetch_authenticity_token_and_commit_oid()
|
| 326 |
if not authenticity_token or not commit_oid:
|
| 327 |
logger.error("[GITHUB] Missing auth tokens to read scenario")
|
| 328 |
return None
|
|
|
|
| 329 |
content = read_user_json_file(authenticity_token, commit_oid)
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
if isinstance(content, dict) and "scenario" in content:
|
| 332 |
return content
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
return None
|
| 336 |
except Exception as e:
|
| 337 |
logger.warning(f"[SCENARIO] fetch failed: {e}")
|
| 338 |
return None
|
|
|
|
| 339 |
|
| 340 |
# ================ Activation helpers ================
|
| 341 |
class CancelToken:
|
|
|
|
| 79 |
|
| 80 |
# ================ db_signals reader helper ================
|
| 81 |
# If you already have an official reader, replace this function with it.
|
| 82 |
+
def read_user_json_file(authenticity_token: str, commit_oid: str):
|
| 83 |
"""
|
| 84 |
+
Returns parsed JSON: list or dict, or [] if empty/unparseable.
|
|
|
|
|
|
|
| 85 |
"""
|
| 86 |
+
from db_signals import get_user_json_file
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
res = get_user_json_file(authenticity_token, commit_oid)
|
| 88 |
if not res.get("success"):
|
| 89 |
raise RuntimeError(f"Failed to read user json file: {res}")
|
| 90 |
content = res.get("content", "")
|
| 91 |
+
if not content:
|
| 92 |
+
return []
|
| 93 |
try:
|
| 94 |
+
return json.loads(content)
|
| 95 |
except Exception:
|
| 96 |
+
logger.warning("[GITHUB] JSON parse failed; treating as empty []")
|
| 97 |
+
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
# ================ Utilities ================
|
| 100 |
def safe_float_join(whole, decimal):
|
|
|
|
| 255 |
# ================ GitHub signal writers/readers ================
|
| 256 |
def write_active_scenario_to_github(pair: str, side: str, entry: str, sl: str, tp: str, dt_utc: datetime.datetime, preserve_timestamps: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
| 257 |
"""
|
| 258 |
+
Always writes signals.json as a single-element array [ { active_trade } ].
|
| 259 |
+
If preserve_timestamps provided, keep it; else compute fresh timestamps.
|
| 260 |
"""
|
| 261 |
timestamps = preserve_timestamps if preserve_timestamps else format_zone_times(dt_utc)
|
| 262 |
+
active_obj = {
|
| 263 |
"pair": pair,
|
| 264 |
"type": side,
|
| 265 |
"entry": str(entry),
|
| 266 |
"stop_loss": str(sl),
|
| 267 |
"take_profit": str(tp),
|
| 268 |
"timestamps": timestamps
|
| 269 |
+
}
|
| 270 |
+
payload_array = [active_obj]
|
| 271 |
+
new_content = json.dumps(payload_array, ensure_ascii=False)
|
| 272 |
+
|
| 273 |
try:
|
| 274 |
authenticity_token, commit_oid = fetch_authenticity_token_and_commit_oid()
|
| 275 |
if not authenticity_token or not commit_oid:
|
|
|
|
| 278 |
return {"success": False, "message": msg}
|
| 279 |
result = update_user_json_file(authenticity_token, commit_oid, new_content)
|
| 280 |
if result.get("success"):
|
| 281 |
+
logger.info("[GITHUB] signals updated with active scenario (array).")
|
| 282 |
return {"success": True}
|
| 283 |
else:
|
| 284 |
logger.error(f"[GITHUB] Update failed: {result}")
|
|
|
|
| 306 |
|
| 307 |
def read_current_scenario_from_github() -> Optional[Dict[str, Any]]:
|
| 308 |
"""
|
| 309 |
+
Reads the pre-activation scenario JSON when signals.json is an array, e.g.:
|
| 310 |
+
[
|
| 311 |
+
{
|
| 312 |
+
"scenario": {
|
| 313 |
+
"Buy": {"at": "...", "SL": "...", "TP": "..."},
|
| 314 |
+
"Sell": {"at": "...", "SL": "...", "TP": "..."},
|
| 315 |
+
"timestamps": {...}
|
| 316 |
+
}
|
| 317 |
}
|
| 318 |
+
]
|
| 319 |
+
Returns {"scenario": {...}} or None if not found.
|
| 320 |
"""
|
| 321 |
try:
|
| 322 |
authenticity_token, commit_oid = fetch_authenticity_token_and_commit_oid()
|
| 323 |
if not authenticity_token or not commit_oid:
|
| 324 |
logger.error("[GITHUB] Missing auth tokens to read scenario")
|
| 325 |
return None
|
| 326 |
+
|
| 327 |
content = read_user_json_file(authenticity_token, commit_oid)
|
| 328 |
+
|
| 329 |
+
# Handle [] formats
|
| 330 |
+
if isinstance(content, list):
|
| 331 |
+
if not content:
|
| 332 |
+
return None
|
| 333 |
+
first = content[0]
|
| 334 |
+
if isinstance(first, dict) and "scenario" in first:
|
| 335 |
+
return first # {"scenario": {...}}
|
| 336 |
+
else:
|
| 337 |
+
return None
|
| 338 |
+
|
| 339 |
+
# Fallback for older dict format
|
| 340 |
if isinstance(content, dict) and "scenario" in content:
|
| 341 |
return content
|
| 342 |
+
|
| 343 |
+
return None
|
|
|
|
| 344 |
except Exception as e:
|
| 345 |
logger.warning(f"[SCENARIO] fetch failed: {e}")
|
| 346 |
return None
|
| 347 |
+
|
| 348 |
|
| 349 |
# ================ Activation helpers ================
|
| 350 |
class CancelToken:
|