tomirisai80 commited on
Commit
de7857d
·
verified ·
1 Parent(s): 68dc3cf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +202 -215
app.py CHANGED
@@ -1,12 +1,13 @@
1
  # ============================================
2
  # АВТО-УСТАНОВКА ПАКЕТОВ
3
  # ============================================
4
- import subprocess
5
- import sys
6
- import importlib
7
 
8
  REQUIRED_PACKAGES = {
9
  'numpy': 'numpy',
 
 
 
10
  'requests': 'requests'
11
  }
12
 
@@ -19,176 +20,236 @@ for module_name, pip_name in REQUIRED_PACKAGES.items():
19
  print(f"✅ {pip_name} установлен!")
20
 
21
  # ============================================
22
- # 👑 TOMIRIS SPACE 24 v1.0 — ECOSYSTEM AGGREGATOR
23
  # ============================================
24
- # Агрегирует сигналы от Space 19-23 и выдаёт единый
25
- # экосистемный вердикт для XAU/USD, ETH/USD, SOL/USD.
26
- # ============================================
27
-
28
- from fastapi import FastAPI, Query
29
- from typing import Dict, Any, List, Optional, Tuple
30
- import time
31
- import requests
32
- import threading
33
- import numpy as np
34
- from datetime import datetime
35
  from collections import deque
36
- import warnings
37
- warnings.filterwarnings('ignore')
 
 
 
 
38
 
39
  # ================= КОНФИГУРАЦИЯ =================
40
- SYMBOLS: List[str] = ["XAU/USD", "ETH/USD", "SOL/USD"]
41
 
42
- # URL'ы экосистемных Space'ов (19-23)
43
- ECOSYSTEM_SPACES: Dict[str, Dict[str, Any]] = {
44
  "space_19_sol": {
45
- "url": "https://tomiris-ecosystem-sol-master.hf.space",
46
  "weight": 0.25,
47
  "symbols": ["SOL/USD"],
48
- "description": "SOL Master"
 
49
  },
50
  "space_20_l2_defi": {
51
- "url": "https://tomiris-ecosystem-l2-defi.hf.space",
52
  "weight": 0.25,
53
  "symbols": ["ETH/USD"],
54
- "description": "L2 & DeFi Pulse"
 
55
  },
56
  "space_21_gold_macro": {
57
- "url": "https://tomiris-ecosystem-gold-macro.hf.space",
58
  "weight": 0.25,
59
  "symbols": ["XAU/USD"],
60
- "description": "Gold Macro & Flow"
 
61
  },
62
  "space_22_sentiment": {
63
- "url": "https://tomiris-ecosystem-sentiment.hf.space",
64
  "weight": 0.15,
65
  "symbols": ["XAU/USD", "ETH/USD", "SOL/USD"],
66
- "description": "Cross-Asset Sentiment"
 
67
  },
68
  "space_23_anomaly": {
69
- "url": "https://tomiris-ecosystem-anomaly.hf.space",
70
  "weight": 0.10,
71
  "symbols": ["ETH/USD", "SOL/USD"],
72
- "description": "On-Chain Anomaly"
 
73
  }
74
  }
75
 
76
- SPACE_18_ARBITER: str = "https://tomiris-ai-name6-6.hf.space"
77
-
78
- CACHE: Dict[str, Dict[str, Any]] = {}
79
- CACHE_TIMES: Dict[str, float] = {}
80
- FEATURES_STORE: Dict[str, Dict[str, Any]] = {}
81
- MT5_MAX_AGE_SEC: int = 300
82
-
83
- def send_to_arbiter(signal_data: Dict[str, Any]) -> None:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  try:
85
- requests.post(
86
- f"{SPACE_18_ARBITER}/log_signal",
87
- json={'space': 'space_24_ecosystem', 'symbol': 'ALL', 'signal': signal_data.get('signal', {})},
88
- timeout=5
89
- )
 
 
 
 
 
 
90
  except:
91
  pass
 
 
92
 
93
- # ================= ЗАГРУЗКА СИГНАЛОВ =================
94
- def fetch_space_signal(space_name: str, url: str, symbol: str) -> Optional[Dict[str, Any]]:
95
- cache_key = f"{space_name}_{symbol}"
96
- if cache_key in CACHE and time.time() - CACHE_TIMES.get(cache_key, 0) < 60:
97
- return CACHE[cache_key]
 
 
 
 
98
 
99
  try:
100
- r = requests.get(f"{url}/consilium?symbol={symbol}", timeout=10)
101
  if r.status_code == 200:
102
  data = r.json()
103
- CACHE[cache_key] = data
104
- CACHE_TIMES[cache_key] = time.time()
 
105
  return data
106
- except Exception as e:
107
- print(f"⚠️ {space_name}: {e}")
108
  return None
109
 
110
- def fetch_all_ecosystem_signals(symbol: str) -> Dict[str, Dict[str, Any]]:
111
- signals = {}
112
- errors = []
 
 
 
 
 
 
113
 
114
- for space_name, config in ECOSYSTEM_SPACES.items():
115
- if symbol not in config['symbols']:
116
- continue
117
 
118
- signal = fetch_space_signal(space_name, config['url'], symbol)
119
- if signal:
120
- signals[space_name] = signal
 
 
121
  else:
122
- errors.append(space_name)
123
-
124
- return signals, errors
125
-
126
- # ================= АГРЕГАЦИЯ =================
127
- def aggregate_ecosystem(symbol: str) -> Dict[str, Any]:
128
- signals, errors = fetch_all_ecosystem_signals(symbol)
129
-
130
- if not signals:
131
- return {
132
- "ecosystem_score": 50.0,
133
- "direction": "WAIT",
134
- "confidence": 0.0,
135
- "active_spaces": 0,
136
- "error_spaces": len(errors),
137
- "signals_detail": {},
138
- "votes": {"LONG": 0.0, "SHORT": 0.0}
139
- }
140
 
141
  total_weight = 0.0
142
  long_votes = 0.0
143
  short_votes = 0.0
 
144
  details = {}
145
 
146
- for space_name, data in signals.items():
147
- config = ECOSYSTEM_SPACES.get(space_name, {})
148
- weight = config.get('weight', 0.1)
149
- sig = data.get('signal', {})
150
- direction = sig.get('direction', 'WAIT')
151
- confidence = sig.get('confidence', 0)
152
 
153
- # Взвешенное голосование
154
- if direction == 'LONG':
155
  long_votes += weight * confidence
156
- elif direction == 'SHORT':
157
  short_votes += weight * confidence
158
-
 
159
  total_weight += weight
160
- details[space_name] = {
161
  "direction": direction,
162
  "confidence": confidence,
163
  "weight": weight,
164
  "status": "active",
165
- "description": config.get('description', '')
166
  }
167
 
168
  for name in errors:
169
- config = ECOSYSTEM_SPACES.get(name, {})
170
  details[name] = {
171
  "direction": "ERROR",
172
  "confidence": 0,
173
- "weight": config.get('weight', 0),
174
  "status": "error",
175
- "description": config.get('description', '')
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
177
 
178
- # Определяем направление
179
- if long_votes > short_votes * 1.3:
 
 
 
 
 
 
 
 
 
180
  direction = "LONG"
181
- confidence = min(0.85, long_votes / max(total_weight, 0.1))
182
  elif short_votes > long_votes * 1.3:
183
  direction = "SHORT"
184
- confidence = min(0.85, short_votes / max(total_weight, 0.1))
185
  else:
186
  direction = "WAIT"
187
- confidence = max(long_votes, short_votes) / max(total_weight, 0.1)
188
 
189
- # Экосистемный скор (0-100)
190
- ecosystem_score = ((long_votes + short_votes) / max(total_weight, 0.1)) * 50
191
- ecosystem_score = min(100, max(0, ecosystem_score))
192
 
193
  return {
194
  "ecosystem_score": round(ecosystem_score, 2),
@@ -199,81 +260,15 @@ def aggregate_ecosystem(symbol: str) -> Dict[str, Any]:
199
  "signals_detail": details,
200
  "votes": {
201
  "LONG": round(long_votes, 4),
202
- "SHORT": round(short_votes, 4)
 
203
  }
204
  }
205
 
206
- # ================= АНАЛИЗ ИЗ MT5 =================
207
- def analyze_from_mt5(mt5_features: Dict[str, Any]) -> Optional[Dict[str, Any]]:
208
- try:
209
- score = 50.0
210
-
211
- rsi = mt5_features.get('H1_rsi', 50)
212
- if isinstance(rsi, (int, float)):
213
- if rsi > 70:
214
- score -= 10
215
- elif rsi < 30:
216
- score += 10
217
-
218
- ema = mt5_features.get('H1_price_vs_ema_21', 0)
219
- if isinstance(ema, (int, float)):
220
- if ema > 1.0:
221
- score += 10
222
- elif ema < -1.0:
223
- score -= 10
224
-
225
- score = max(0, min(100, score))
226
-
227
- if score > 60:
228
- direction, confidence = "LONG", score / 100
229
- elif score < 40:
230
- direction, confidence = "SHORT", (100 - score) / 100
231
- else:
232
- direction, confidence = "WAIT", 0.0
233
-
234
- return {
235
- "direction": direction,
236
- "confidence": round(confidence, 4),
237
- "score": score,
238
- "source": "MT5"
239
- }
240
- except:
241
- return None
242
-
243
  # ================= ГЛАВНЫЙ СИГНАЛ =================
244
- def get_ecosystem_aggregate(symbol: str = "XAU/USD") -> Dict[str, Any]:
245
  start = time.time()
246
-
247
- # Проверка MT5
248
- if symbol in FEATURES_STORE:
249
- fs = FEATURES_STORE[symbol]
250
- age = time.time() - fs.get("timestamp", 0)
251
- if age < MT5_MAX_AGE_SEC:
252
- mt5_features = fs.get("features", {})
253
- if mt5_features:
254
- mt5_result = analyze_from_mt5(mt5_features)
255
- if mt5_result:
256
- result = {
257
- "space": "space_24_ecosystem_aggregator",
258
- "timestamp": int(time.time()),
259
- "symbol": symbol,
260
- "signal": {
261
- "direction": mt5_result["direction"],
262
- "confidence": mt5_result["confidence"]
263
- },
264
- "ecosystem_analysis": {
265
- "score": mt5_result["score"],
266
- "active_spaces": 0,
267
- "data_source": "MT5"
268
- },
269
- "meta": {"latency_ms": int((time.time() - start) * 1000)}
270
- }
271
- send_to_arbiter(result)
272
- print(f"🌐 ECO AGG: {mt5_result['direction']} | Score={mt5_result['score']} (MT5)")
273
- return result
274
-
275
- # Fallback: агрегация экосистемных Space'ов
276
- agg = aggregate_ecosystem(symbol)
277
  latency = int((time.time() - start) * 1000)
278
 
279
  result = {
@@ -291,73 +286,65 @@ def get_ecosystem_aggregate(symbol: str = "XAU/USD") -> Dict[str, Any]:
291
  "votes": agg['votes'],
292
  "details": agg['signals_detail']
293
  },
294
- "meta": {"latency_ms": latency}
295
  }
296
 
297
- send_to_arbiter(result)
298
- print(f"🌐 ECO AGG v1: {symbol} | {agg['direction']} | Active={agg['active_spaces']}/{len(ECOSYSTEM_SPACES)}")
 
 
 
 
 
 
 
 
 
 
299
  return result
300
 
301
- # ================= KEEP-ALIVE =================
302
- def keep_alive():
303
- while True:
304
- time.sleep(840)
305
- try:
306
- requests.get("http://localhost:7860/health", timeout=5)
307
- except:
308
- pass
309
 
310
- threading.Thread(target=keep_alive, daemon=True).start()
 
 
311
 
312
- # ================= FASTAPI =================
313
- app = FastAPI(title="TOMIRIS SPACE 24 v1.0 — ECOSYSTEM AGGREGATOR")
 
314
 
315
  @app.get("/health")
316
- @app.head("/health")
317
  async def health():
318
  return {
319
- "space": "Space 24 - Ecosystem Aggregator v1.0",
320
  "status": "operational",
 
321
  "symbols": SYMBOLS,
322
- "ecosystem_spaces": {k: v['description'] for k, v in ECOSYSTEM_SPACES.items()},
323
- "total_spaces": len(ECOSYSTEM_SPACES)
 
324
  }
325
 
326
  @app.get("/consilium")
327
  async def consilium(symbol: str = Query("XAU/USD")):
328
  if symbol not in SYMBOLS:
329
- return {"error": f"Unsupported: {symbol}"}
330
- return get_ecosystem_aggregate(symbol)
331
 
332
  @app.get("/breakdown/{symbol}")
333
  async def breakdown(symbol: str):
334
- """Детальный разбор всех экосистемных Space'ов."""
335
- agg = aggregate_ecosystem(symbol)
336
- return {
337
- "symbol": symbol,
338
- "aggregate": agg,
339
- "spaces_detail": agg.get('signals_detail', {})
340
- }
341
 
342
  @app.get("/all")
343
  async def all_signals():
344
- """Сигналы по всем трём активам."""
345
  results = {}
346
  for sym in SYMBOLS:
347
- results[sym] = aggregate_ecosystem(sym)
348
  return results
349
 
350
- @app.post("/features")
351
- async def receive_features(data: Dict[str, Any]):
352
- symbol = data.get("symbol", "XAU/USD")
353
- FEATURES_STORE[symbol] = {
354
- "features": data.get("features", {}),
355
- "price": data.get("price", 0.0),
356
- "timestamp": time.time()
357
- }
358
- print(f"📥 MT5 {symbol}: {len(data.get('features', {}))} признаков")
359
- return {"status": "ok"}
360
 
361
- print(f"🚀 SPACE 24 v1.0 — ECOSYSTEM AGGREGATOR ЗАПУЩЕН!")
362
- print(f"🌐 Агрегирует Space 19-23 для XAU, ETH, SOL")
363
- print(f"✅ Готов к бою!")
 
1
  # ============================================
2
  # АВТО-УСТАНОВКА ПАКЕТОВ
3
  # ============================================
4
+ import subprocess, sys, importlib
 
 
5
 
6
  REQUIRED_PACKAGES = {
7
  'numpy': 'numpy',
8
+ 'httpx': 'httpx',
9
+ 'fastapi': 'fastapi',
10
+ 'uvicorn': 'uvicorn',
11
  'requests': 'requests'
12
  }
13
 
 
20
  print(f"✅ {pip_name} установлен!")
21
 
22
  # ============================================
23
+ # 👑 TOMIRIS SPACE 24 v2.0 — ECOSYSTEM AGGREGATOR PRO (Async, Dynamic Weights, No MT5)
24
  # ============================================
25
+ import os, time, json, logging, asyncio
26
+ from typing import Dict, Any, List, Optional
27
+ from datetime import datetime, timezone
 
 
 
 
 
 
 
 
28
  from collections import deque
29
+ import numpy as np
30
+ import httpx
31
+ from fastapi import FastAPI, Query
32
+
33
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
34
+ logger = logging.getLogger("Space24_EcosystemAggregator")
35
 
36
  # ================= КОНФИГУРАЦИЯ =================
37
+ SYMBOLS = ["XAU/USD", "ETH/USD", "SOL/USD"]
38
 
39
+ # Реальные URL'ы экосистемных Space'ов
40
+ ECOSYSTEM_SPACES = {
41
  "space_19_sol": {
42
+ "url": "https://tomirisai80-tomirisanal.hf.space",
43
  "weight": 0.25,
44
  "symbols": ["SOL/USD"],
45
+ "description": "SOL Master",
46
+ "cache_ttl": 300
47
  },
48
  "space_20_l2_defi": {
49
+ "url": "https://tomirisai80-tomirisanal2.hf.space",
50
  "weight": 0.25,
51
  "symbols": ["ETH/USD"],
52
+ "description": "L2 & DeFi Pulse",
53
+ "cache_ttl": 300
54
  },
55
  "space_21_gold_macro": {
56
+ "url": "https://tomirisai80-tomirisanal3.hf.space",
57
  "weight": 0.25,
58
  "symbols": ["XAU/USD"],
59
+ "description": "Gold Macro & Flow",
60
+ "cache_ttl": 600
61
  },
62
  "space_22_sentiment": {
63
+ "url": "https://tomirisai80-tomirisanal4.hf.space",
64
  "weight": 0.15,
65
  "symbols": ["XAU/USD", "ETH/USD", "SOL/USD"],
66
+ "description": "Cross-Asset Sentiment",
67
+ "cache_ttl": 300
68
  },
69
  "space_23_anomaly": {
70
+ "url": "https://tomirisai80-tomirisai5.hf.space",
71
  "weight": 0.10,
72
  "symbols": ["ETH/USD", "SOL/USD"],
73
+ "description": "On-Chain Anomaly",
74
+ "cache_ttl": 60
75
  }
76
  }
77
 
78
+ HUB_URL = os.getenv("SPACE17_URL", "https://tomiris-ai-name5-5.hf.space")
79
+ ARBITER_URL = os.getenv("SPACE18_URL", "https://tomiris-ai-name6-6.hf.space")
80
+ SPACE31_URL = os.getenv("SPACE31_URL", "https://nuxotetotmailsvoboden-tomiris-perf.hf.space")
81
+
82
+ # ================= HTTP КЛИЕНТ =================
83
+ http_client = httpx.AsyncClient(timeout=15.0)
84
+
85
+ # ================= ГЛОБАЛЬНЫЙ КЭШ И BREAKER =================
86
+ cache_store = {}
87
+ cache_times = {}
88
+ CIRCUIT_BREAKER = {}
89
+
90
+ def breaker_open(name: str) -> bool:
91
+ info = CIRCUIT_BREAKER.get(name)
92
+ if not info: return False
93
+ if info["fails"] < 3: return False
94
+ if time.time() - info["last_fail"] > 600:
95
+ CIRCUIT_BREAKER[name] = {"fails": 0, "last_fail": 0}
96
+ return False
97
+ return True
98
+
99
+ def breaker_record(name: str, success: bool):
100
+ info = CIRCUIT_BREAKER.get(name, {"fails": 0, "last_fail": 0})
101
+ if success:
102
+ info["fails"] = 0
103
+ else:
104
+ info["fails"] += 1
105
+ info["last_fail"] = time.time()
106
+ CIRCUIT_BREAKER[name] = info
107
+
108
+ # ================= ДИНАМИЧЕСКИЕ ВЕСА =================
109
+ async def fetch_dynamic_weights() -> Dict[str, float]:
110
+ """Пытается получить веса из Space 31, затем из Space 17. Если нет — базовые."""
111
+ # Space 31
112
+ if SPACE31_URL:
113
+ try:
114
+ r = await http_client.get(f"{SPACE31_URL}/weights", timeout=8)
115
+ if r.status_code == 200:
116
+ data = r.json()
117
+ if data:
118
+ return {k: v.get("weight", 0.1) for k, v in data.items() if k in ECOSYSTEM_SPACES}
119
+ except:
120
+ pass
121
+ # Space 17 (analyst_metrics)
122
  try:
123
+ r = await http_client.get(f"{HUB_URL}/metrics", timeout=8)
124
+ if r.status_code == 200:
125
+ metrics = r.json()
126
+ weights = {}
127
+ for m in metrics:
128
+ name = m.get("space_name", "")
129
+ if name in ECOSYSTEM_SPACES:
130
+ acc = m.get("accuracy", 0.5)
131
+ pf = m.get("profit_factor", 1.0)
132
+ weights[name] = acc * min(pf, 3.0) / 3.0
133
+ return weights
134
  except:
135
  pass
136
+ # Базовые
137
+ return {name: cfg["weight"] for name, cfg in ECOSYSTEM_SPACES.items()}
138
 
139
+ # ================= ОПРОС ОДНОГО SPACE =================
140
+ async def fetch_space_signal(name: str, config: Dict, symbol: str) -> Optional[Dict]:
141
+ if breaker_open(name):
142
+ return None
143
+ cache_key = f"{name}_{symbol}"
144
+ ttl = config.get("cache_ttl", 60)
145
+ now = time.time()
146
+ if cache_key in cache_store and (now - cache_times.get(cache_key, 0)) < ttl:
147
+ return cache_store[cache_key]
148
 
149
  try:
150
+ r = await http_client.get(f"{config['url']}/consilium?symbol={symbol}", timeout=10)
151
  if r.status_code == 200:
152
  data = r.json()
153
+ breaker_record(name, True)
154
+ cache_store[cache_key] = data
155
+ cache_times[cache_key] = time.time()
156
  return data
157
+ except:
158
+ breaker_record(name, False)
159
  return None
160
 
161
+ # ================= АГРЕГАЦИЯ =================
162
+ async def aggregate_ecosystem(symbol: str) -> Dict[str, Any]:
163
+ dynamic_weights = await fetch_dynamic_weights()
164
+ tasks = []
165
+ space_names = []
166
+ for name, cfg in ECOSYSTEM_SPACES.items():
167
+ if symbol in cfg["symbols"]:
168
+ tasks.append(fetch_space_signal(name, cfg, symbol))
169
+ space_names.append(name)
170
 
171
+ results = await asyncio.gather(*tasks)
 
 
172
 
173
+ signals = {}
174
+ errors = []
175
+ for name, res in zip(space_names, results):
176
+ if res and isinstance(res, dict):
177
+ signals[name] = res
178
  else:
179
+ errors.append(name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  total_weight = 0.0
182
  long_votes = 0.0
183
  short_votes = 0.0
184
+ wait_votes = 0.0
185
  details = {}
186
 
187
+ for name, data in signals.items():
188
+ weight = dynamic_weights.get(name, ECOSYSTEM_SPACES[name]["weight"])
189
+ sig = data.get("signal", {})
190
+ direction = sig.get("direction", "WAIT")
191
+ confidence = sig.get("confidence", 0)
 
192
 
193
+ if direction == "LONG":
 
194
  long_votes += weight * confidence
195
+ elif direction == "SHORT":
196
  short_votes += weight * confidence
197
+ else:
198
+ wait_votes += weight * confidence
199
  total_weight += weight
200
+ details[name] = {
201
  "direction": direction,
202
  "confidence": confidence,
203
  "weight": weight,
204
  "status": "active",
205
+ "description": ECOSYSTEM_SPACES[name]["description"]
206
  }
207
 
208
  for name in errors:
209
+ weight = dynamic_weights.get(name, ECOSYSTEM_SPACES[name]["weight"])
210
  details[name] = {
211
  "direction": "ERROR",
212
  "confidence": 0,
213
+ "weight": weight,
214
  "status": "error",
215
+ "description": ECOSYSTEM_SPACES[name]["description"]
216
+ }
217
+
218
+ if total_weight == 0:
219
+ # Ни одного активного Space
220
+ return {
221
+ "ecosystem_score": 50.0,
222
+ "direction": "WAIT",
223
+ "confidence": 0.0,
224
+ "active_spaces": 0,
225
+ "error_spaces": len(errors),
226
+ "signals_detail": details,
227
+ "votes": {"LONG": 0.0, "SHORT": 0.0, "WAIT": 0.0}
228
  }
229
 
230
+ # Баланс голосов
231
+ bias = (long_votes - short_votes) / total_weight
232
+ ecosystem_score = 50.0 + bias * 50.0
233
+ ecosystem_score = max(0, min(100, ecosystem_score))
234
+
235
+ # Определение направления с учётом WAIT
236
+ wait_ratio = wait_votes / total_weight
237
+ if wait_ratio > 0.6:
238
+ direction = "WAIT"
239
+ confidence = wait_ratio
240
+ elif long_votes > short_votes * 1.3:
241
  direction = "LONG"
242
+ confidence = min(0.9, long_votes / total_weight)
243
  elif short_votes > long_votes * 1.3:
244
  direction = "SHORT"
245
+ confidence = min(0.9, short_votes / total_weight)
246
  else:
247
  direction = "WAIT"
248
+ confidence = max(long_votes, short_votes) / total_weight
249
 
250
+ # Снижаем confidence при малом числе активных Space
251
+ active_ratio = len(signals) / len(space_names) if space_names else 0
252
+ confidence *= 0.5 + 0.5 * active_ratio
253
 
254
  return {
255
  "ecosystem_score": round(ecosystem_score, 2),
 
260
  "signals_detail": details,
261
  "votes": {
262
  "LONG": round(long_votes, 4),
263
+ "SHORT": round(short_votes, 4),
264
+ "WAIT": round(wait_votes, 4)
265
  }
266
  }
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  # ================= ГЛАВНЫЙ СИГНАЛ =================
269
+ async def get_ecosystem_aggregate(symbol: str = "XAU/USD") -> Dict[str, Any]:
270
  start = time.time()
271
+ agg = await aggregate_ecosystem(symbol)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  latency = int((time.time() - start) * 1000)
273
 
274
  result = {
 
286
  "votes": agg['votes'],
287
  "details": agg['signals_detail']
288
  },
289
+ "latency_ms": latency
290
  }
291
 
292
+ # Отправка в Arbiter
293
+ try:
294
+ await http_client.post(f"{ARBITER_URL}/log_signal", json={
295
+ "space": "space_24_ecosystem",
296
+ "symbol": symbol,
297
+ "signal": result["signal"],
298
+ "ecosystem_details": result["ecosystem_analysis"]
299
+ })
300
+ except:
301
+ pass
302
+
303
+ logger.info(f"🌐 Ecosystem Agg: {symbol} {agg['direction']} conf={agg['confidence']:.3f} score={agg['ecosystem_score']:.1f} active={agg['active_spaces']}")
304
  return result
305
 
306
+ # ================= FASTAPI =================
307
+ app = FastAPI(title="Tomiris Space 24 v2.0 — Ecosystem Aggregator Pro")
 
 
 
 
 
 
308
 
309
+ @app.on_event("startup")
310
+ async def startup():
311
+ pass
312
 
313
+ @app.on_event("shutdown")
314
+ async def shutdown():
315
+ await http_client.aclose()
316
 
317
  @app.get("/health")
 
318
  async def health():
319
  return {
 
320
  "status": "operational",
321
+ "version": "2.0",
322
  "symbols": SYMBOLS,
323
+ "ecosystem_spaces": list(ECOSYSTEM_SPACES.keys()),
324
+ "no_mt5": True,
325
+ "async": True
326
  }
327
 
328
  @app.get("/consilium")
329
  async def consilium(symbol: str = Query("XAU/USD")):
330
  if symbol not in SYMBOLS:
331
+ return {"error": "Invalid symbol"}
332
+ return await get_ecosystem_aggregate(symbol)
333
 
334
  @app.get("/breakdown/{symbol}")
335
  async def breakdown(symbol: str):
336
+ agg = await aggregate_ecosystem(symbol)
337
+ return {"symbol": symbol, "aggregate": agg, "spaces_detail": agg.get('signals_detail', {})}
 
 
 
 
 
338
 
339
  @app.get("/all")
340
  async def all_signals():
 
341
  results = {}
342
  for sym in SYMBOLS:
343
+ results[sym] = await aggregate_ecosystem(sym)
344
  return results
345
 
346
+ if __name__ == "__main__":
347
+ import uvicorn
348
+ uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
349
 
350
+ print("🚀 SPACE 24 v2.0 — ECOSYSTEM AGGREGATOR PRO ЗАПУЩЕН!")