Dooratre commited on
Commit
2c7df70
·
verified ·
1 Parent(s): dd500bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +44 -67
app.py CHANGED
@@ -146,14 +146,13 @@ def build_expired_message(symbol, hi_bid, lo_ask, target_up, target_down, durati
146
  def monitor_price_dual(target_up, target_down, duration_minutes, symbol=SYMBOL_DEFAULT):
147
  """
148
  Monitor both upside and downside targets. Synchronous call.
149
- Returns final JSON result and sends alert message to fixed webhook.
 
150
  """
151
  if duration_minutes <= 0:
152
- msg = "Configuration error: duration must be > 0."
153
- return {"status": "error", "message": msg}, 400
154
  if target_up <= target_down:
155
- msg = "Configuration error: target_up must be greater than target_down."
156
- return {"status": "error", "message": msg}, 400
157
 
158
  # Initial fetch with retry
159
  retries, snap = 0, None
@@ -166,7 +165,7 @@ def monitor_price_dual(target_up, target_down, duration_minutes, symbol=SYMBOL_D
166
  time.sleep(delay)
167
  else:
168
  msg = "Unable to retrieve initial price snapshot."
169
- return {"status": "error", "message": msg}, 502
170
 
171
  t_start = datetime.datetime.now()
172
  t_end = t_start + datetime.timedelta(minutes=duration_minutes)
@@ -181,34 +180,14 @@ def monitor_price_dual(target_up, target_down, duration_minutes, symbol=SYMBOL_D
181
  elapsed_min = (datetime.datetime.now() - t_start).total_seconds() / 60
182
  message = build_upside_message(symbol, bid0, ask0, target_up, target_down, elapsed_min, hi_bid, lo_ask)
183
  send_alert_message(message)
184
- return {
185
- "status": "hit",
186
- "side": "upside",
187
- "message": message,
188
- "symbol": symbol,
189
- "bid": bid0,
190
- "ask": ask0,
191
- "hi_bid": hi_bid,
192
- "lo_ask": lo_ask,
193
- "elapsed_min": round(elapsed_min, 4)
194
- }, 200
195
 
196
  if ask0 <= target_down:
197
  play_alert_sound()
198
  elapsed_min = (datetime.datetime.now() - t_start).total_seconds() / 60
199
  message = build_downside_message(symbol, bid0, ask0, target_up, target_down, elapsed_min, hi_bid, lo_ask)
200
  send_alert_message(message)
201
- return {
202
- "status": "hit",
203
- "side": "downside",
204
- "message": message,
205
- "symbol": symbol,
206
- "bid": bid0,
207
- "ask": ask0,
208
- "hi_bid": hi_bid,
209
- "lo_ask": lo_ask,
210
- "elapsed_min": round(elapsed_min, 4)
211
- }, 200
212
 
213
  ticks = 0
214
  try:
@@ -228,34 +207,14 @@ def monitor_price_dual(target_up, target_down, duration_minutes, symbol=SYMBOL_D
228
  elapsed_min = (datetime.datetime.now() - t_start).total_seconds() / 60
229
  message = build_upside_message(symbol, bid, ask, target_up, target_down, elapsed_min, hi_bid, lo_ask)
230
  send_alert_message(message)
231
- return {
232
- "status": "hit",
233
- "side": "upside",
234
- "message": message,
235
- "symbol": symbol,
236
- "bid": bid,
237
- "ask": ask,
238
- "hi_bid": hi_bid,
239
- "lo_ask": lo_ask,
240
- "elapsed_min": round(elapsed_min, 4)
241
- }, 200
242
 
243
  if ask <= target_down:
244
  play_alert_sound()
245
  elapsed_min = (datetime.datetime.now() - t_start).total_seconds() / 60
246
  message = build_downside_message(symbol, bid, ask, target_up, target_down, elapsed_min, hi_bid, lo_ask)
247
  send_alert_message(message)
248
- return {
249
- "status": "hit",
250
- "side": "downside",
251
- "message": message,
252
- "symbol": symbol,
253
- "bid": bid,
254
- "ask": ask,
255
- "hi_bid": hi_bid,
256
- "lo_ask": lo_ask,
257
- "elapsed_min": round(elapsed_min, 4)
258
- }, 200
259
 
260
  # Optional: light logging cadence
261
  ticks += 1
@@ -268,26 +227,33 @@ def monitor_price_dual(target_up, target_down, duration_minutes, symbol=SYMBOL_D
268
  # Expiry
269
  message = build_expired_message(symbol, hi_bid, lo_ask, target_up, target_down, duration_minutes)
270
  send_alert_message(message)
271
- return {
272
- "status": "expired",
273
- "side": None,
274
- "message": message,
275
- "symbol": symbol,
276
- "hi_bid": hi_bid,
277
- "lo_ask": lo_ask,
278
- "duration_minutes": duration_minutes
279
- }, 200
280
 
281
  except Exception as e:
282
  message = f"Unexpected error: {e}"
283
- return {"status": "error", "message": message}, 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
  # ========== API Endpoints ==========
286
 
287
  @app.route("/monitor", methods=["POST"])
288
  def start_monitor():
289
  """
290
- Start monitoring and block until it hits a target or expires.
291
  Payload:
292
  {
293
  "symbol": "XAUUSD", // optional, default XAUUSD
@@ -295,8 +261,8 @@ def start_monitor():
295
  "target_down": 2475.0, // required
296
  "duration_minutes": 15 // required
297
  }
298
- Response: final result JSON (hit/expired/error)
299
- Also sends POST to ALERT_WEBHOOK_URL with { "message": "..." } on hit/expired.
300
  """
301
  try:
302
  data = request.get_json(force=True)
@@ -311,6 +277,7 @@ def start_monitor():
311
  if target_up is None or target_down is None or duration_minutes is None:
312
  return jsonify({"status": "error", "message": "target_up, target_down, duration_minutes are required"}), 400
313
 
 
314
  try:
315
  target_up = float(target_up)
316
  target_down = float(target_down)
@@ -318,16 +285,26 @@ def start_monitor():
318
  except Exception:
319
  return jsonify({"status": "error", "message": "Invalid types: target_up/target_down must be float, duration_minutes must be int"}), 400
320
 
321
- result, code = monitor_price_dual(target_up, target_down, duration_minutes, symbol=symbol)
322
- return jsonify(result), code
 
 
 
 
 
 
 
 
323
 
324
  @app.route("/", methods=["GET"])
325
  def root():
326
  return jsonify({
327
- "service": "Forex Trade Alert API (single-system)",
328
  "fixed_alert_webhook": ALERT_WEBHOOK_URL,
329
- "endpoint": "POST /monitor"
 
330
  }), 200
331
 
332
  if __name__ == "__main__":
 
333
  app.run(host="0.0.0.0", port=7860, debug=False, threaded=True)
 
146
  def monitor_price_dual(target_up, target_down, duration_minutes, symbol=SYMBOL_DEFAULT):
147
  """
148
  Monitor both upside and downside targets. Synchronous call.
149
+ On hit/expired, sends alert message to fixed webhook.
150
+ Returns a final dict, but in async usage we don't return it to client.
151
  """
152
  if duration_minutes <= 0:
153
+ return {"status": "error", "message": "Configuration error: duration must be > 0."}
 
154
  if target_up <= target_down:
155
+ return {"status": "error", "message": "Configuration error: target_up must be greater than target_down."}
 
156
 
157
  # Initial fetch with retry
158
  retries, snap = 0, None
 
165
  time.sleep(delay)
166
  else:
167
  msg = "Unable to retrieve initial price snapshot."
168
+ return {"status": "error", "message": msg}
169
 
170
  t_start = datetime.datetime.now()
171
  t_end = t_start + datetime.timedelta(minutes=duration_minutes)
 
180
  elapsed_min = (datetime.datetime.now() - t_start).total_seconds() / 60
181
  message = build_upside_message(symbol, bid0, ask0, target_up, target_down, elapsed_min, hi_bid, lo_ask)
182
  send_alert_message(message)
183
+ return {"status": "hit", "side": "upside", "message": message}
 
 
 
 
 
 
 
 
 
 
184
 
185
  if ask0 <= target_down:
186
  play_alert_sound()
187
  elapsed_min = (datetime.datetime.now() - t_start).total_seconds() / 60
188
  message = build_downside_message(symbol, bid0, ask0, target_up, target_down, elapsed_min, hi_bid, lo_ask)
189
  send_alert_message(message)
190
+ return {"status": "hit", "side": "downside", "message": message}
 
 
 
 
 
 
 
 
 
 
191
 
192
  ticks = 0
193
  try:
 
207
  elapsed_min = (datetime.datetime.now() - t_start).total_seconds() / 60
208
  message = build_upside_message(symbol, bid, ask, target_up, target_down, elapsed_min, hi_bid, lo_ask)
209
  send_alert_message(message)
210
+ return {"status": "hit", "side": "upside", "message": message}
 
 
 
 
 
 
 
 
 
 
211
 
212
  if ask <= target_down:
213
  play_alert_sound()
214
  elapsed_min = (datetime.datetime.now() - t_start).total_seconds() / 60
215
  message = build_downside_message(symbol, bid, ask, target_up, target_down, elapsed_min, hi_bid, lo_ask)
216
  send_alert_message(message)
217
+ return {"status": "hit", "side": "downside", "message": message}
 
 
 
 
 
 
 
 
 
 
218
 
219
  # Optional: light logging cadence
220
  ticks += 1
 
227
  # Expiry
228
  message = build_expired_message(symbol, hi_bid, lo_ask, target_up, target_down, duration_minutes)
229
  send_alert_message(message)
230
+ return {"status": "expired", "message": message}
 
 
 
 
 
 
 
 
231
 
232
  except Exception as e:
233
  message = f"Unexpected error: {e}"
234
+ send_alert_message(f"[ERROR] {message}")
235
+ return {"status": "error", "message": message}
236
+
237
+ def start_monitor_async(symbol: str, target_up: float, target_down: float, duration_minutes: int):
238
+ """
239
+ Start monitor_price_dual in a daemon thread (non-blocking).
240
+ """
241
+ def _runner():
242
+ try:
243
+ result = monitor_price_dual(target_up, target_down, duration_minutes, symbol=symbol)
244
+ # Optional: log final result
245
+ print(f"[MONITOR-END] {symbol} result: {result}")
246
+ except Exception as e:
247
+ print(f"[MONITOR-THREAD] Error: {e}")
248
+ t = threading.Thread(target=_runner, daemon=True)
249
+ t.start()
250
 
251
  # ========== API Endpoints ==========
252
 
253
  @app.route("/monitor", methods=["POST"])
254
  def start_monitor():
255
  """
256
+ Start monitoring asynchronously and immediately return 200.
257
  Payload:
258
  {
259
  "symbol": "XAUUSD", // optional, default XAUUSD
 
261
  "target_down": 2475.0, // required
262
  "duration_minutes": 15 // required
263
  }
264
+ Immediate Response:
265
+ { "status": "accepted", "message": "Okay your alert added succes" }
266
  """
267
  try:
268
  data = request.get_json(force=True)
 
277
  if target_up is None or target_down is None or duration_minutes is None:
278
  return jsonify({"status": "error", "message": "target_up, target_down, duration_minutes are required"}), 400
279
 
280
+ # Basic type validation before starting async work
281
  try:
282
  target_up = float(target_up)
283
  target_down = float(target_down)
 
285
  except Exception:
286
  return jsonify({"status": "error", "message": "Invalid types: target_up/target_down must be float, duration_minutes must be int"}), 400
287
 
288
+ if duration_minutes <= 0:
289
+ return jsonify({"status": "error", "message": "duration_minutes must be > 0"}), 400
290
+ if target_up <= target_down:
291
+ return jsonify({"status": "error", "message": "target_up must be greater than target_down"}), 400
292
+
293
+ # Start monitoring in background thread
294
+ start_monitor_async(symbol, target_up, target_down, duration_minutes)
295
+
296
+ # Immediate success response
297
+ return jsonify({"status": "accepted", "message": "Okay your alert added succes"}), 200
298
 
299
  @app.route("/", methods=["GET"])
300
  def root():
301
  return jsonify({
302
+ "service": "Forex Trade Alert API (async monitor)",
303
  "fixed_alert_webhook": ALERT_WEBHOOK_URL,
304
+ "endpoint": "POST /monitor",
305
+ "behavior": "returns immediately; monitoring runs in background"
306
  }), 200
307
 
308
  if __name__ == "__main__":
309
+ # threaded=True is fine; we also use daemon threads for monitors
310
  app.run(host="0.0.0.0", port=7860, debug=False, threaded=True)