kulusia commited on
Commit
7da98eb
Β·
verified Β·
1 Parent(s): b08f2ca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -62
app.py CHANGED
@@ -1,17 +1,18 @@
1
  #!/usr/bin/env python3
2
- # BUILD v4 - Smart CSV update from last bar - 2026-05-08
3
  """
4
- SMC AI BOT v9.9 - Hugging Face Deployment (SMART CSV UPDATE)
5
- - Checks where uploaded CSV ends (date/time)
6
- - Fetches only missing bars from Twelve Data
7
- - Updates CSV in Space with new data
8
- - Saves all timeframes to disk
9
- - Signal sent ONLY when new candle data arrives
 
10
  """
11
 
12
- import os, numpy as np, pandas as pd, warnings, requests, time, threading
13
  from datetime import datetime, timedelta, timezone
14
- from flask import Flask
15
  warnings.filterwarnings("ignore")
16
  import xgboost as xgb, joblib
17
 
@@ -25,22 +26,51 @@ def home():
25
  bars = len(data.get('5min', []))
26
  tfs = [tf for tf in ['5min','30min','1h','2h','4h'] if tf in data and len(data[tf]) >= 201]
27
  csv_date = get_csv_last_date(CSV_5MIN_LIVE) if os.path.exists(CSV_5MIN_LIVE) else None
28
- return f"πŸ€– SMC Bot v9.9 β€” {bars:,} bars | Active: {len(tfs)}/5 TFs | CSV ends: {csv_date} | API: {api_call_count}/{MAX_API_CALLS_PER_DAY}"
29
 
30
  @app.route('/health')
31
  def health():
32
- return {
33
- "status": "alive", "time": str(datetime.now(timezone.utc)),
34
- "models": len(models), "active_tfs": len([tf for tf in ['5min','30min','1h','2h','4h'] if tf in data and len(data[tf]) >= 201]),
35
- "api_calls_today": api_call_count, "csv_last_bar": str(get_csv_last_date(CSV_5MIN_LIVE)) if os.path.exists(CSV_5MIN_LIVE) else None,
36
- }
 
 
 
 
 
 
 
 
 
37
 
38
  @app.route('/force-save')
39
  def force_save():
40
  global data
41
- if not data: return "❌ No data", 500
42
  save_all_csvs()
43
- return "πŸ’Ύ Saved:\n" + "\n".join([f"{tf}: {len(data[tf]):,} bars" for tf in data])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  # ============================================================
46
  # LOAD SECRETS
@@ -105,14 +135,12 @@ TF_LABELS = {'5min':'🎯 5min','30min':'πŸ”’ 30min','1h':'πŸ”’ 1H','2h':'πŸ”’ 2
105
  data = {}
106
 
107
  # ============================================================
108
- # SMART CSV FUNCTIONS
109
  # ============================================================
110
 
111
  def get_csv_last_date(filepath):
112
- """Get the last timestamp from a CSV file (fast - reads only last few bytes)"""
113
  if not os.path.exists(filepath): return None
114
  try:
115
- # Fast method: read last 500 bytes
116
  with open(filepath, 'rb') as f:
117
  f.seek(-500, 2)
118
  last_bytes = f.read().decode('utf-8', errors='ignore')
@@ -121,25 +149,21 @@ def get_csv_last_date(filepath):
121
  if ',' in line and not line.startswith('datetime'):
122
  ts_str = line.split(',')[0]
123
  try:
124
- return pd.to_datetime(ts_str).tz_localize('UTC') if 'UTC' not in ts_str and '+' not in ts_str else pd.to_datetime(ts_str)
 
 
125
  except: continue
126
- # Fallback: read full file
127
  df = pd.read_csv(filepath, parse_dates=['datetime'], nrows=5)
128
- return pd.to_datetime(df['datetime'].iloc[-1])
 
 
129
  except: return None
130
 
131
  def update_csv_from_api(filepath):
132
- """
133
- Check where CSV ends, fetch missing bars from API, update CSV.
134
- Returns number of new bars added.
135
- """
136
- if not TWELVEDATA_API_KEY: return 0
137
- if not can_call_api(): return 0
138
-
139
  last_bar = get_csv_last_date(filepath)
140
  now = datetime.now(timezone.utc)
141
 
142
- # If no CSV or it's empty, fetch 5000 bars
143
  if last_bar is None:
144
  print(f" πŸ“‘ No existing data β€” fetching 5000 bars...")
145
  df_new = fetch_5min(5000)
@@ -149,30 +173,25 @@ def update_csv_from_api(filepath):
149
  return len(df_new)
150
  return 0
151
 
152
- # Calculate how many bars are missing
153
  gap = now - last_bar
154
- gap_minutes = gap.total_seconds() / 60
155
- bars_needed = int(gap_minutes / 5) + 10 # 10 extra for safety
156
-
157
  if bars_needed <= 2:
158
- print(f" βœ… CSV is current (last bar: {last_bar})")
159
  return 0
160
 
161
  bars_needed = min(bars_needed, 5000)
162
- print(f" πŸ“‘ CSV ends: {last_bar} | Need ~{bars_needed} bars...")
163
 
164
  df_new = fetch_5min(bars_needed)
165
  if df_new is None or len(df_new) == 0:
 
166
  return 0
167
 
168
- # Filter to only bars after last_bar
169
  df_new = df_new[df_new.index > last_bar]
170
-
171
  if len(df_new) == 0:
172
  print(f" βœ… Already current")
173
  return 0
174
 
175
- # Load existing CSV, merge, save
176
  df_existing = load_csv_data(filepath)
177
  if df_existing is not None and len(df_existing) > 0:
178
  df_combined = pd.concat([df_existing, df_new])
@@ -180,17 +199,13 @@ def update_csv_from_api(filepath):
180
  df_combined.sort_index(inplace=True)
181
  added = len(df_combined) - len(df_existing)
182
  df_combined.to_csv(filepath)
183
- print(f" βœ… +{added} bars | Total: {len(df_combined):,} | Last: {df_combined.index[-1]}")
184
  return added
185
  else:
186
  df_new.to_csv(filepath)
187
  print(f" βœ… Created: {len(df_new):,} bars")
188
  return len(df_new)
189
 
190
- # ============================================================
191
- # DATA PERSISTENCE
192
- # ============================================================
193
-
194
  def save_all_csvs():
195
  global data
196
  if not data: return
@@ -229,7 +244,7 @@ def load_csv_data(filepath):
229
  return None
230
 
231
  # ============================================================
232
- # ALL FEATURE FUNCTIONS
233
  # ============================================================
234
 
235
  def calculate_atr(high, low, close, period=14):
@@ -527,20 +542,9 @@ def run_bot():
527
  print("\nπŸ”„ Bot starting...")
528
  if not models: print("❌ No models"); return
529
 
530
- # STEP 1: Check uploaded CSV and update it
531
  print("\nπŸ“‚ Checking uploaded CSV...")
532
- csv_exists = os.path.exists(CSV_5MIN_LIVE)
533
- last_bar = get_csv_last_date(CSV_5MIN_LIVE)
534
-
535
- if csv_exists:
536
- print(f" βœ… CSV found: {last_bar}")
537
- added = update_csv_from_api(CSV_5MIN_LIVE)
538
- if added > 0:
539
- print(f" πŸ“‘ Updated CSV with +{added} new bars")
540
- else:
541
- print(f" ⚠️ No CSV found β€” fetching fresh data...")
542
 
543
- # STEP 2: Load data from CSV
544
  data = {}
545
  df5 = load_csv_data(CSV_5MIN_LIVE)
546
  if df5 is not None and len(df5) > 0:
@@ -555,8 +559,7 @@ def run_bot():
555
  signal_count = 0
556
 
557
  active_tfs = len([tf for tf in ['5min','30min','1h','2h','4h'] if tf in data and len(data[tf])>=201])
558
- print(f"\nπŸ” Active: {active_tfs}/5 TFs\n")
559
- send_telegram(f"πŸ€– <b>SMC Bot v9.9 LIVE!</b>\nπŸ“Š {len(models)} models | {active_tfs}/5 TFs\nπŸ“‚ CSV: {len(data.get('5min',[])):,} bars\nπŸ” Monitoring XAU/USD")
560
 
561
  while True:
562
  try:
@@ -569,7 +572,6 @@ def run_bot():
569
 
570
  if seconds_since >= fetch_interval and candle_just_closed(5 if active else 15):
571
  if can_call_api():
572
- print(f"⏰ {now.strftime('%H:%M')} β€” fetching...")
573
  nd = fetch_5min(3)
574
  if nd is not None and len(nd) > 0:
575
  if '5min' in data:
@@ -606,9 +608,10 @@ def run_bot():
606
 
607
  time.sleep(10)
608
  except Exception as e:
609
- print(f"⚠️ Error: {e}"); time.sleep(60)
610
 
611
  threading.Thread(target=keep_alive, daemon=True).start()
 
612
  threading.Thread(target=run_bot, daemon=True).start()
613
 
614
  if __name__ == '__main__':
 
1
  #!/usr/bin/env python3
2
+ # BUILD v5 - Complete with /force-save, /health, /data endpoints - 2026-05-08
3
  """
4
+ SMC AI BOT v9.9 - Complete with All Endpoints
5
+ - /health - Full status JSON
6
+ - /force-save - Manual CSV save trigger
7
+ - /data - Data quality check
8
+ - Smart CSV update on startup
9
+ - All feature functions + 31-criteria filters
10
+ - Telegram alerts + API-efficient fetching
11
  """
12
 
13
+ import os, numpy as np, pandas as pd, warnings, requests, time, threading, traceback
14
  from datetime import datetime, timedelta, timezone
15
+ from flask import Flask, jsonify
16
  warnings.filterwarnings("ignore")
17
  import xgboost as xgb, joblib
18
 
 
26
  bars = len(data.get('5min', []))
27
  tfs = [tf for tf in ['5min','30min','1h','2h','4h'] if tf in data and len(data[tf]) >= 201]
28
  csv_date = get_csv_last_date(CSV_5MIN_LIVE) if os.path.exists(CSV_5MIN_LIVE) else None
29
+ return f"πŸ€– SMC Bot v9.9 β€” {bars:,} bars | Active: {len(tfs)}/5 TFs | CSV: {csv_date} | API: {api_call_count}/{MAX_API_CALLS_PER_DAY}"
30
 
31
  @app.route('/health')
32
  def health():
33
+ return jsonify({
34
+ "status": "alive",
35
+ "time": str(datetime.now(timezone.utc)),
36
+ "models_loaded": len(models),
37
+ "active_timeframes": len([tf for tf in ['5min','30min','1h','2h','4h'] if tf in data and len(data[tf]) >= 201]),
38
+ "api_calls_today": api_call_count,
39
+ "api_limit": MAX_API_CALLS_PER_DAY,
40
+ "csv_last_bar": str(get_csv_last_date(CSV_5MIN_LIVE)) if os.path.exists(CSV_5MIN_LIVE) else None,
41
+ "bars_5min": len(data.get('5min', [])),
42
+ "bars_30min": len(data.get('30min', [])),
43
+ "bars_1h": len(data.get('1h', [])),
44
+ "bars_2h": len(data.get('2h', [])),
45
+ "bars_4h": len(data.get('4h', [])),
46
+ })
47
 
48
  @app.route('/force-save')
49
  def force_save():
50
  global data
51
+ if not data: return jsonify({"status": "error", "message": "No data to save"}), 500
52
  save_all_csvs()
53
+ result = {"status": "ok", "saved": {}}
54
+ for tf_name, path in CSV_MAP.items():
55
+ if os.path.exists(path):
56
+ result["saved"][tf_name] = f"{len(data.get(tf_name, [])):,} bars ({os.path.getsize(path)/1024:.1f} KB)"
57
+ return jsonify(result)
58
+
59
+ @app.route('/data')
60
+ def data_status():
61
+ result = {}
62
+ for tf_name in ['5min','30min','1h','2h','4h']:
63
+ if tf_name in data and data[tf_name] is not None and len(data[tf_name]) > 0:
64
+ df = data[tf_name]
65
+ result[tf_name] = {
66
+ "bars": len(df),
67
+ "first": str(df.index[0]),
68
+ "last": str(df.index[-1]),
69
+ "has_201": len(df) >= 201,
70
+ }
71
+ else:
72
+ result[tf_name] = {"bars": 0, "has_201": False}
73
+ return jsonify(result)
74
 
75
  # ============================================================
76
  # LOAD SECRETS
 
135
  data = {}
136
 
137
  # ============================================================
138
+ # CSV FUNCTIONS
139
  # ============================================================
140
 
141
  def get_csv_last_date(filepath):
 
142
  if not os.path.exists(filepath): return None
143
  try:
 
144
  with open(filepath, 'rb') as f:
145
  f.seek(-500, 2)
146
  last_bytes = f.read().decode('utf-8', errors='ignore')
 
149
  if ',' in line and not line.startswith('datetime'):
150
  ts_str = line.split(',')[0]
151
  try:
152
+ dt = pd.to_datetime(ts_str)
153
+ if dt.tzinfo is None: dt = dt.tz_localize('UTC')
154
+ return dt
155
  except: continue
 
156
  df = pd.read_csv(filepath, parse_dates=['datetime'], nrows=5)
157
+ dt = pd.to_datetime(df['datetime'].iloc[-1])
158
+ if dt.tzinfo is None: dt = dt.tz_localize('UTC')
159
+ return dt
160
  except: return None
161
 
162
  def update_csv_from_api(filepath):
163
+ if not TWELVEDATA_API_KEY or not can_call_api(): return 0
 
 
 
 
 
 
164
  last_bar = get_csv_last_date(filepath)
165
  now = datetime.now(timezone.utc)
166
 
 
167
  if last_bar is None:
168
  print(f" πŸ“‘ No existing data β€” fetching 5000 bars...")
169
  df_new = fetch_5min(5000)
 
173
  return len(df_new)
174
  return 0
175
 
 
176
  gap = now - last_bar
177
+ bars_needed = int(gap.total_seconds() / 300) + 10
 
 
178
  if bars_needed <= 2:
179
+ print(f" βœ… CSV is current")
180
  return 0
181
 
182
  bars_needed = min(bars_needed, 5000)
183
+ print(f" πŸ“‘ CSV ends: {last_bar} | Fetching {bars_needed} bars...")
184
 
185
  df_new = fetch_5min(bars_needed)
186
  if df_new is None or len(df_new) == 0:
187
+ print(f" ⚠️ No data returned")
188
  return 0
189
 
 
190
  df_new = df_new[df_new.index > last_bar]
 
191
  if len(df_new) == 0:
192
  print(f" βœ… Already current")
193
  return 0
194
 
 
195
  df_existing = load_csv_data(filepath)
196
  if df_existing is not None and len(df_existing) > 0:
197
  df_combined = pd.concat([df_existing, df_new])
 
199
  df_combined.sort_index(inplace=True)
200
  added = len(df_combined) - len(df_existing)
201
  df_combined.to_csv(filepath)
202
+ print(f" βœ… +{added} bars | Total: {len(df_combined):,}")
203
  return added
204
  else:
205
  df_new.to_csv(filepath)
206
  print(f" βœ… Created: {len(df_new):,} bars")
207
  return len(df_new)
208
 
 
 
 
 
209
  def save_all_csvs():
210
  global data
211
  if not data: return
 
244
  return None
245
 
246
  # ============================================================
247
+ # FEATURE FUNCTIONS (abbreviated - full versions in previous code)
248
  # ============================================================
249
 
250
  def calculate_atr(high, low, close, period=14):
 
542
  print("\nπŸ”„ Bot starting...")
543
  if not models: print("❌ No models"); return
544
 
 
545
  print("\nπŸ“‚ Checking uploaded CSV...")
546
+ added = update_csv_from_api(CSV_5MIN_LIVE)
 
 
 
 
 
 
 
 
 
547
 
 
548
  data = {}
549
  df5 = load_csv_data(CSV_5MIN_LIVE)
550
  if df5 is not None and len(df5) > 0:
 
559
  signal_count = 0
560
 
561
  active_tfs = len([tf for tf in ['5min','30min','1h','2h','4h'] if tf in data and len(data[tf])>=201])
562
+ send_telegram(f"πŸ€– <b>SMC Bot v9.9 LIVE!</b>\nπŸ“Š {len(models)} models | {active_tfs}/5 TFs\nπŸ“‚ {len(data.get('5min',[])):,} bars\nπŸ” Monitoring XAU/USD")
 
563
 
564
  while True:
565
  try:
 
572
 
573
  if seconds_since >= fetch_interval and candle_just_closed(5 if active else 15):
574
  if can_call_api():
 
575
  nd = fetch_5min(3)
576
  if nd is not None and len(nd) > 0:
577
  if '5min' in data:
 
608
 
609
  time.sleep(10)
610
  except Exception as e:
611
+ print(f"⚠️ Error: {e}"); traceback.print_exc(); time.sleep(60)
612
 
613
  threading.Thread(target=keep_alive, daemon=True).start()
614
+ print("πŸ’“ Keep-alive active")
615
  threading.Thread(target=run_bot, daemon=True).start()
616
 
617
  if __name__ == '__main__':