| from flask import Flask, render_template, request, jsonify, send_file |
| import yfinance as yf |
| import pandas as pd |
| import numpy as np |
| import io |
| import traceback |
| import torch |
| from datetime import datetime |
| import copy |
| from tvDatafeed import TvDatafeed, Interval |
| import os |
| import shutil |
| import requests |
| from model import Kronos, KronosTokenizer, KronosPredictor |
|
|
| app = Flask(__name__) |
|
|
| |
| logo_src = "/Users/kirillpigorev/.gemini/antigravity-ide/brain/0793dfd5-f02b-4fe6-a14e-0608fc119c2f/media__1781258918887.png" |
| logo_dest = "static/logo.png" |
| if os.path.exists(logo_src): |
| os.makedirs("static", exist_ok=True) |
| try: |
| shutil.copy(logo_src, logo_dest) |
| except Exception as e: |
| print("Не удалось скопировать логотип:", e) |
|
|
| |
| print("Загрузка модели Kronos... (пожалуйста, подождите)") |
| tokenizer = KronosTokenizer.from_pretrained("NeoQuasar/Kronos-Tokenizer-base") |
| model = Kronos.from_pretrained("NeoQuasar/Kronos-base") |
| predictor = KronosPredictor(model, tokenizer, max_context=256) |
| print("Модель успешно загружена! Сервер готов к работе.") |
|
|
| PROGRESS = {} |
|
|
| @app.route('/') |
| def index(): |
| return render_template('index.html') |
|
|
| @app.route('/api/search_ticker') |
| def search_ticker(): |
| query = request.args.get('q', '') |
| if not query: |
| return jsonify([]) |
| try: |
| headers = { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', |
| 'Origin': 'https://www.tradingview.com', |
| 'Referer': 'https://www.tradingview.com/' |
| } |
| url = f"https://symbol-search.tradingview.com/symbol_search/v3/?text={query}&hl=0&exchange=&lang=en&domain=production" |
| resp = requests.get(url, headers=headers, timeout=5) |
| if resp.status_code == 200: |
| quotes = resp.json() |
| |
| |
| if isinstance(quotes, dict): |
| quotes = quotes.get('symbols', quotes.get('data', quotes.get('matches', []))) |
| |
| results = [] |
| if isinstance(quotes, list): |
| for q in quotes: |
| if isinstance(q, dict) and 'symbol' in q: |
| results.append({ |
| 'symbol': q['symbol'], |
| 'name': q.get('description', q.get('symbol', '')), |
| 'exchange': q.get('exchange', ''), |
| 'type': q.get('type', '') |
| }) |
| |
| |
| def get_score(item): |
| sym = item['symbol'].upper() |
| q_str = query.upper() |
| score = 0 |
| |
| if sym == q_str: |
| score += 1000 |
| elif sym.startswith(q_str): |
| score += 500 |
| |
| |
| if item['exchange'] in ['BINANCE', 'OANDA', 'NASDAQ', 'NYSE', 'TVC']: |
| score += 200 |
| |
| return score |
| |
| results.sort(key=get_score, reverse=True) |
| return jsonify(results[:20]) |
| return jsonify([]) |
| except Exception as e: |
| print("Ошибка поиска тикера:", e) |
| return jsonify([]) |
|
|
| def prep_df(d): |
| df_prep = d.copy() |
| if df_prep.empty: |
| return df_prep |
| |
| df_prep.columns = [str(c).lower() for c in df_prep.columns] |
| |
| |
| df_prep = df_prep[['open', 'high', 'low', 'close', 'volume']] |
| |
| df_prep['amount'] = df_prep['volume'] |
| |
| |
| if df_prep.isnull().values.any(): |
| raise ValueError("Данные содержат пропуски (NaN)") |
| |
| return df_prep |
|
|
| def tv_to_yf(tv_ticker): |
| tv_ticker = str(tv_ticker).upper().strip() |
| if ':' in tv_ticker: |
| tv_ticker = tv_ticker.split(':')[1] |
| mapping = { |
| 'XAUUSD': 'GC=F', 'GOLD': 'GC=F', 'XAGUSD': 'SI=F', 'SILVER': 'SI=F', |
| 'USOIL': 'CL=F', 'WTI': 'CL=F', 'UKOIL': 'BZ=F', 'BRENT': 'BZ=F', |
| 'SPX': '^GSPC', 'SPX500': '^GSPC', 'SPX500USD': '^GSPC', 'SP500': '^GSPC', |
| 'SP500USD': '^GSPC', 'SP500-USD': '^GSPC', 'US500': '^GSPC', 'GSPC': '^GSPC', |
| 'NDX': '^NDX', 'NAS100USD': '^NDX', 'US100': '^NDX', |
| 'DJI': '^DJI', 'US30': '^DJI', 'VIX': '^VIX', 'DXY': 'DX-Y.NYB', |
| } |
| if tv_ticker in mapping: return mapping[tv_ticker] |
| if tv_ticker.endswith('USDT'): return tv_ticker[:-4] + '-USD' |
| if tv_ticker.endswith('-USD'): return tv_ticker |
| if tv_ticker.endswith('USD'): |
| if tv_ticker in ['EURUSD', 'GBPUSD', 'AUDUSD', 'NZDUSD']: return tv_ticker + '=X' |
| return tv_ticker[:-3] + '-USD' |
| if tv_ticker in ['USDJPY', 'USDCHF', 'USDCAD']: return tv_ticker + '=X' |
| return tv_ticker |
|
|
| |
| tv = TvDatafeed() |
|
|
| @app.route('/api/progress') |
| def progress_api(): |
| task_id = request.args.get('task_id') |
| return jsonify({'progress': PROGRESS.get(task_id, 0)}) |
|
|
| @app.route('/api/forecast') |
| def forecast_api(): |
| raw_ticker = request.args.get('ticker', 'CLSK') |
| tf = request.args.get('tf', '4h') |
| anchor_type = request.args.get('anchor', 'max_vol') |
| task_id = request.args.get('task_id', '') |
| target_date_str = request.args.get('target_date', '') |
| |
| target_datetime = None |
| if target_date_str: |
| try: |
| |
| if len(target_date_str) == 10: |
| target_datetime = pd.to_datetime(target_date_str + " 23:59:59") |
| else: |
| |
| target_datetime = pd.to_datetime(target_date_str) |
| except Exception as e: |
| print("Target date parse error:", e) |
| |
| ticker_symbol = raw_ticker |
| |
| |
| exchange = "" |
| if ':' in ticker_symbol: |
| exchange, ticker_symbol = ticker_symbol.split(':', 1) |
| |
| |
| if not exchange: |
| try: |
| url = f"https://symbol-search.tradingview.com/symbol_search/v3/?text={ticker_symbol}&hl=0&exchange=&lang=en&domain=production" |
| res = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=5) |
| if res.status_code == 200: |
| quotes = res.json() |
| if isinstance(quotes, dict): |
| quotes = quotes.get('symbols', quotes.get('data', quotes.get('matches', []))) |
| if isinstance(quotes, list) and len(quotes) > 0: |
| for q in quotes: |
| if isinstance(q, dict) and q.get('symbol', '').upper() == ticker_symbol.upper(): |
| exchange = q.get('exchange', '') |
| break |
| if not exchange and isinstance(quotes[0], dict): |
| exchange = quotes[0].get('exchange', '') |
| except Exception as e: |
| print("Auto-exchange lookup failed:", e) |
| |
| if not exchange: |
| exchange = "BINANCE" |
| |
| print(f"[{exchange}:{ticker_symbol}] Получен запрос на прогноз (Таймфрейм: {tf})...") |
| |
| if task_id: |
| PROGRESS[task_id] = 10 |
| |
| try: |
| def fetch_tv(symbol, exch, interval_enum, bars): |
| d = tv.get_hist(symbol, exch, interval=interval_enum, n_bars=bars) |
| if d is None or d.empty: |
| d = tv.get_hist(symbol, 'OANDA', interval=interval_enum, n_bars=bars) |
| if d is None or d.empty: |
| d = tv.get_hist(symbol, 'TVC', interval=interval_enum, n_bars=bars) |
| if d is None or d.empty: |
| d = tv.get_hist(symbol, 'CRYPTO', interval=interval_enum, n_bars=bars) |
| if d is None or d.empty: |
| d = tv.get_hist(symbol, 'NASDAQ', interval=interval_enum, n_bars=bars) |
| if d is None or d.empty: |
| d = tv.get_hist(symbol, 'NYSE', interval=interval_enum, n_bars=bars) |
| return d |
| |
| def fetch_yf(symbol, yf_interval, yf_period): |
| yf_ticker = tv_to_yf(symbol) |
| ticker = yf.Ticker(yf_ticker) |
| d = ticker.history(period=yf_period, interval=yf_interval) |
| return d |
|
|
| def get_validated_data(symbol, exch, tv_interval, yf_interval, yf_period, bars=1000): |
| if target_datetime is not None: |
| |
| bars = 5000 |
| |
| result_df = None |
| |
| |
| try: |
| tv_data = fetch_tv(symbol, exch, tv_interval, bars) |
| if tv_data is not None and not tv_data.empty: |
| crypto_exchanges = ['CRYPTO', 'BINANCE', 'COINBASE', 'KUCOIN', 'BYBIT', 'OKX'] |
| if exch.upper() in crypto_exchanges: |
| tv_data.index = tv_data.index.tz_localize('UTC').tz_convert('Europe/Berlin').tz_localize(None) |
| else: |
| tv_data.index = tv_data.index.tz_localize('America/New_York').tz_convert('Europe/Berlin').tz_localize(None) |
| result_df = prep_df(tv_data) |
| except Exception as e: |
| print(f"TV fetch/validate error: {e}") |
| |
| |
| if result_df is None: |
| try: |
| print(f"Fallback to YF for {symbol}...") |
| yf_data = fetch_yf(symbol, yf_interval, yf_period) |
| if yf_data is not None and not yf_data.empty: |
| |
| yf_data.index = yf_data.index.tz_convert('Europe/Berlin').tz_localize(None) |
| result_df = prep_df(yf_data) |
| except Exception as e: |
| print(f"YF fetch/validate error: {e}") |
| |
| if result_df is None: |
| raise ValueError(f"Недостаточно чистых данных для {symbol} (все источники вернули пустые данные или пропуски NaN)") |
| |
| |
| if target_datetime is not None: |
| result_df = result_df[result_df.index <= target_datetime] |
| if result_df.empty: |
| raise ValueError(f"Нет данных для {symbol} до указанной даты {target_date_str}") |
| |
| return result_df |
|
|
| if tf == '5m': |
| data = get_validated_data(ticker_symbol, exchange, Interval.in_5_minute, "5m", "60d") |
| timedelta_step = pd.Timedelta(minutes=5) |
| freq_str = '5min' |
|
|
| elif tf == '15m': |
| data = get_validated_data(ticker_symbol, exchange, Interval.in_15_minute, "15m", "60d") |
| timedelta_step = pd.Timedelta(minutes=15) |
| freq_str = '15min' |
|
|
| elif tf == '1h': |
| data = get_validated_data(ticker_symbol, exchange, Interval.in_1_hour, "1h", "3mo") |
| timedelta_step = pd.Timedelta(hours=1) |
| freq_str = '1h' |
| |
| elif tf == '4h': |
| data = get_validated_data(ticker_symbol, exchange, Interval.in_4_hour, "1h", "6mo") |
| if not data.empty and data.index.freqstr != '4h': |
| data = data.resample('4h').agg({ |
| 'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum' |
| }).dropna() |
| timedelta_step = pd.Timedelta(hours=4) |
| freq_str = '4h' |
| |
| elif tf == '1d': |
| data = get_validated_data(ticker_symbol, exchange, Interval.in_daily, "1d", "2y") |
| timedelta_step = pd.Timedelta(days=1) |
| freq_str = '1d' |
| |
| elif tf == '1w': |
| data = get_validated_data(ticker_symbol, exchange, Interval.in_weekly, "1wk", "max") |
| timedelta_step = pd.Timedelta(weeks=1) |
| freq_str = 'W' |
| |
| elif tf.startswith('mtf'): |
| |
| configs = { |
| 'mtf_3': { |
| 'base_tf': '1h', 'step': pd.Timedelta(hours=1), 'freq': '1h', |
| 'layers': [ |
| {'tf': '1d', 'period': '2y', 'interval': '1d', 'weight': 0.2, 'step': pd.Timedelta(days=1), 'freq': '1d'}, |
| {'tf': '4h', 'period': '6mo', 'interval': '1h', 'resample': '4h', 'weight': 0.3, 'step': pd.Timedelta(hours=4), 'freq': '4h'}, |
| {'tf': '1h', 'period': '3mo', 'interval': '1h', 'weight': 0.5, 'step': pd.Timedelta(hours=1), 'freq': '1h'} |
| ] |
| }, |
| 'mtf_2': { |
| 'base_tf': '15m', 'step': pd.Timedelta(minutes=15), 'freq': '15min', |
| 'layers': [ |
| {'tf': '1d', 'period': '2y', 'interval': '1d', 'weight': 0.1, 'step': pd.Timedelta(days=1), 'freq': '1d'}, |
| {'tf': '4h', 'period': '6mo', 'interval': '1h', 'resample': '4h', 'weight': 0.2, 'step': pd.Timedelta(hours=4), 'freq': '4h'}, |
| {'tf': '1h', 'period': '3mo', 'interval': '1h', 'weight': 0.3, 'step': pd.Timedelta(hours=1), 'freq': '1h'}, |
| {'tf': '15m', 'period': '60d', 'interval': '15m', 'weight': 0.4, 'step': pd.Timedelta(minutes=15), 'freq': '15min'} |
| ] |
| }, |
| 'mtf_1': { |
| 'base_tf': '5m', 'step': pd.Timedelta(minutes=5), 'freq': '5min', |
| 'layers': [ |
| {'tf': '1d', 'period': '2y', 'interval': '1d', 'weight': 0.05, 'step': pd.Timedelta(days=1), 'freq': '1d'}, |
| {'tf': '4h', 'period': '6mo', 'interval': '1h', 'resample': '4h', 'weight': 0.1, 'step': pd.Timedelta(hours=4), 'freq': '4h'}, |
| {'tf': '1h', 'period': '3mo', 'interval': '1h', 'weight': 0.15, 'step': pd.Timedelta(hours=1), 'freq': '1h'}, |
| {'tf': '15m', 'period': '60d', 'interval': '15m', 'weight': 0.3, 'step': pd.Timedelta(minutes=15), 'freq': '15min'}, |
| {'tf': '5m', 'period': '60d', 'interval': '5m', 'weight': 0.4, 'step': pd.Timedelta(minutes=5), 'freq': '5min'} |
| ] |
| }, |
| 'mtf_4': { |
| 'base_tf': '4h', 'step': pd.Timedelta(hours=4), 'freq': '4h', |
| 'layers': [ |
| {'tf': '1w', 'period': 'max', 'interval': '1wk', 'resample': '1w', 'weight': 0.2, 'step': pd.Timedelta(weeks=1), 'freq': 'W'}, |
| {'tf': '1d', 'period': '2y', 'interval': '1d', 'weight': 0.3, 'step': pd.Timedelta(days=1), 'freq': '1d'}, |
| {'tf': '4h', 'period': '6mo', 'interval': '1h', 'resample': '4h', 'weight': 0.5, 'step': pd.Timedelta(hours=4), 'freq': '4h'} |
| ] |
| }, |
| 'mtf_5': { |
| 'base_tf': '1d', 'step': pd.Timedelta(days=1), 'freq': '1d', |
| 'layers': [ |
| {'tf': '1M', 'period': 'max', 'interval': '1mo', 'resample': '1M', 'weight': 0.2, 'step': pd.Timedelta(days=30), 'freq': 'ME'}, |
| {'tf': '1w', 'period': 'max', 'interval': '1wk', 'resample': '1w', 'weight': 0.3, 'step': pd.Timedelta(weeks=1), 'freq': 'W'}, |
| {'tf': '1d', 'period': '2y', 'interval': '1d', 'weight': 0.5, 'step': pd.Timedelta(days=1), 'freq': '1d'} |
| ] |
| } |
| } |
| |
| cfg = configs[tf] |
| pred_len = 24 |
| |
| |
| tv_interval_map = { |
| '1M': (Interval.in_monthly, "1mo", "max"), |
| '1w': (Interval.in_weekly, "1wk", "max"), |
| '1h': (Interval.in_1_hour, "1h", "3mo"), |
| '4h': (Interval.in_4_hour, "1h", "6mo"), |
| '1d': (Interval.in_daily, "1d", "2y"), |
| '15m': (Interval.in_15_minute, "15m", "1mo"), |
| '5m': (Interval.in_5_minute, "5m", "1mo") |
| } |
| |
| super_median = None |
| base_df = None |
| base_pred = None |
| |
| for i, layer in enumerate(cfg['layers']): |
| interval_enum, yf_int, yf_per = tv_interval_map.get(layer['tf'], (Interval.in_1_hour, "1h", "3mo")) |
| |
| try: |
| d = get_validated_data(ticker_symbol, exchange, interval_enum, yf_int, yf_per, bars=2000) |
| except ValueError as ve: |
| return jsonify({'error': str(ve)}), 400 |
| |
| if layer.get('resample'): |
| d = d.resample(layer['resample']).agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'}).dropna() |
| d['amount'] = d['volume'] |
| |
| if layer['tf'] == cfg['base_tf']: |
| base_df = d |
| |
| |
| if len(d) < 256 and not d.empty: |
| pad_size = 256 - len(d) |
| pad_df = pd.concat([d.iloc[0:1]] * pad_size) |
| pad_df['volume'] = 0 |
| pad_dates = pd.date_range(end=d.index[0] - layer['step'], periods=pad_size, freq=layer['freq']) |
| pad_df.index = pad_dates |
| d_input = pd.concat([pad_df, d]).iloc[-256:] |
| else: |
| d_input = d.iloc[-256:] |
| |
| y_ts = pd.Series(pd.date_range(start=d_input.index[-1] + layer['step'], periods=pred_len, freq=layer['freq'])) |
| |
| if task_id: |
| |
| PROGRESS[task_id] = 10 + int(80 * i / len(cfg['layers'])) |
| |
| pred = predictor.predict(df=d_input, x_timestamp=pd.Series(d_input.index), y_timestamp=y_ts, pred_len=pred_len, sample_count=30, return_samples=True) |
| |
| if task_id: |
| PROGRESS[task_id] = 10 + int(80 * (i + 1) / len(cfg['layers'])) |
| |
| if torch.backends.mps.is_available(): |
| torch.mps.empty_cache() |
| elif torch.cuda.is_available(): |
| torch.cuda.empty_cache() |
| |
| if layer['tf'] == cfg['base_tf']: |
| base_pred = pred |
| |
| layer['df'] = d |
| layer['pred'] = pred |
| layer['y_ts'] = y_ts |
| |
| |
| start_price = base_df['close'].iloc[-1] |
| start_time = base_df.index[-1] |
| target_dt_index = pd.date_range(start=start_time + cfg['step'], periods=pred_len, freq=cfg['freq']) |
| target_dates = base_pred['y_ts'] = pd.Series(target_dt_index) |
| |
| final_median = np.zeros(pred_len) |
| |
| for layer in cfg['layers']: |
| median_vals = layer['pred']['median']['close'].values |
| if layer['tf'] == cfg['base_tf']: |
| interp_vals = median_vals |
| else: |
| s = pd.Series(median_vals, index=layer['y_ts'].values) |
| s.loc[start_time] = start_price |
| s.sort_index(inplace=True) |
| |
| interp_vals = s.resample(cfg['freq']).interpolate(method='time').reindex(target_dt_index).ffill().bfill().values |
| if np.isnan(interp_vals).any(): |
| interp_vals = np.nan_to_num(interp_vals, nan=s.iloc[-1]) |
| |
| final_median += interp_vals * layer['weight'] |
| |
| shift = final_median - base_pred['median']['close'].values |
| samples_close = base_pred['raw_preds'][:, :, 3] |
| shifted_samples = samples_close + shift |
| |
| |
| if np.isnan(shifted_samples).any(): |
| shifted_samples = np.nan_to_num(shifted_samples, nan=base_pred['median']['close'].values[-1]) |
| if np.isnan(shift).any(): |
| shift = np.nan_to_num(shift, nan=0.0) |
| |
| p05_arr = np.percentile(shifted_samples, 5, axis=0) |
| p15_arr = np.percentile(shifted_samples, 15, axis=0) |
| p85_arr = np.percentile(shifted_samples, 85, axis=0) |
| p95_arr = np.percentile(shifted_samples, 95, axis=0) |
| |
| df = base_df |
| pred_median_df = base_pred['median'].copy().ffill().bfill() |
| pred_median_df['open'] += shift |
| pred_median_df['high'] += shift |
| pred_median_df['low'] += shift |
| pred_median_df['close'] += shift |
| else: |
| return jsonify({'error': 'Неизвестный таймфрейм'}), 400 |
| |
| if not tf.startswith('mtf'): |
| if data.empty: |
| return jsonify({'error': f'Failed to load data for ticker {ticker_symbol}'}), 400 |
| |
| df = data.copy() |
| |
| |
| if len(df) < 256 and not df.empty: |
| pad_size = 256 - len(df) |
| pad_df = pd.concat([df.iloc[0:1]] * pad_size) |
| pad_df['volume'] = 0 |
| pad_dates = pd.date_range(end=df.index[0] - timedelta_step, periods=pad_size, freq=freq_str) |
| pad_df.index = pad_dates |
| df = pd.concat([pad_df, df]).iloc[-256:] |
| else: |
| df = df.iloc[-256:] |
| |
| x_timestamp = pd.Series(df.index) |
| |
| pred_len = 24 |
| last_time = x_timestamp.iloc[-1] |
| future_dates = pd.date_range(start=last_time + timedelta_step, periods=pred_len, freq=freq_str) |
| y_timestamp = pd.Series(future_dates) |
| |
| if task_id: |
| PROGRESS[task_id] = 50 |
| |
| prediction = predictor.predict( |
| df=df, |
| x_timestamp=x_timestamp, |
| y_timestamp=y_timestamp, |
| pred_len=pred_len, |
| sample_count=30, |
| return_samples=True |
| ) |
| |
| if task_id: |
| PROGRESS[task_id] = 90 |
| |
| pred_median_df = prediction['median'] |
| |
| samples_close = prediction['raw_preds'][:, :, 3] |
| p05_arr = np.percentile(samples_close, 5, axis=0) |
| p15_arr = np.percentile(samples_close, 15, axis=0) |
| p85_arr = np.percentile(samples_close, 85, axis=0) |
| p95_arr = np.percentile(samples_close, 95, axis=0) |
| |
| |
| df['vwap'] = np.nan |
| df['vwap_std'] = np.nan |
| last_cum_vol = 0 |
| last_cum_pv = 0 |
| last_cum_pv2 = 0 |
| |
| if anchor_type != 'none': |
| if anchor_type == 'high': |
| anchor_idx = df['high'].idxmax() |
| elif anchor_type == 'low': |
| anchor_idx = df['low'].idxmin() |
| elif anchor_type == 'gap': |
| gap_series = (df['open'] - df['close'].shift(1)).abs() / df['close'].shift(1) |
| anchor_idx = gap_series.fillna(0).idxmax() |
| else: |
| anchor_idx = df['volume'].idxmax() |
| |
| df_anchored = df.loc[anchor_idx:].copy() |
| |
| cum_vol = df_anchored['volume'].cumsum() |
| cum_pv = (df_anchored['close'] * df_anchored['volume']).cumsum() |
| cum_pv2 = ((df_anchored['close']**2) * df_anchored['volume']).cumsum() |
| |
| vwap_series = cum_pv / cum_vol |
| vwap_var_series = (cum_pv2 / cum_vol) - (vwap_series**2) |
| vwap_var_series = vwap_var_series.clip(lower=0) |
| vwap_std_series = np.sqrt(vwap_var_series) |
| |
| df.loc[anchor_idx:, 'vwap'] = vwap_series |
| df.loc[anchor_idx:, 'vwap_std'] = vwap_std_series |
| |
| last_cum_vol = cum_vol.iloc[-1] if not cum_vol.empty else 0 |
| last_cum_pv = cum_pv.iloc[-1] if not cum_pv.empty else 0 |
| last_cum_pv2 = cum_pv2.iloc[-1] if not cum_pv2.empty else 0 |
| |
| hist_list = [] |
| hist_vwap_list = [] |
| hist_vwap_up1_list = [] |
| hist_vwap_up2_list = [] |
| hist_vwap_dn1_list = [] |
| hist_vwap_dn2_list = [] |
| |
| pred_vwap_list = [] |
| pred_vwap_up1_list = [] |
| pred_vwap_up2_list = [] |
| pred_vwap_dn1_list = [] |
| pred_vwap_dn2_list = [] |
| |
| for i, row in df.iterrows(): |
| hist_list.append({ |
| 'time': int(i.timestamp()), |
| 'open': float(row['open']), |
| 'high': float(row['high']), |
| 'low': float(row['low']), |
| 'close': float(row['close']) |
| }) |
| if not np.isnan(row['vwap']): |
| t = int(i.timestamp()) |
| v = float(row['vwap']) |
| s = float(row['vwap_std']) |
| hist_vwap_list.append({'time': t, 'value': v}) |
| hist_vwap_up1_list.append({'time': t, 'value': v + s}) |
| hist_vwap_up2_list.append({'time': t, 'value': v + 2*s}) |
| hist_vwap_dn1_list.append({'time': t, 'value': v - s}) |
| hist_vwap_dn2_list.append({'time': t, 'value': v - 2*s}) |
| |
| if hist_vwap_list: |
| pred_vwap_list.append(hist_vwap_list[-1]) |
| pred_vwap_up1_list.append(hist_vwap_up1_list[-1]) |
| pred_vwap_up2_list.append(hist_vwap_up2_list[-1]) |
| pred_vwap_dn1_list.append(hist_vwap_dn1_list[-1]) |
| pred_vwap_dn2_list.append(hist_vwap_dn2_list[-1]) |
| |
| pred_list = [] |
| p05_list = [] |
| p15_list = [] |
| p85_list = [] |
| p95_list = [] |
| |
| for idx, (i, row) in enumerate(pred_median_df.iterrows()): |
| if tf.startswith('mtf'): |
| time_val = int(target_dates.iloc[idx].timestamp()) |
| else: |
| time_val = int(i.timestamp()) |
| |
| pred_close = float(row['close']) |
| pred_vol = float(row.get('volume', 0.0)) |
| |
| |
| if last_cum_vol > 0 and pred_vol > 0: |
| last_cum_pv += pred_close * pred_vol |
| last_cum_pv2 += pred_vol * (pred_close**2) |
| last_cum_vol += pred_vol |
| |
| cur_vwap = last_cum_pv / last_cum_vol |
| cur_var = max(0.0, (last_cum_pv2 / last_cum_vol) - cur_vwap**2) |
| cur_std = float(np.sqrt(cur_var)) |
| |
| vwap_list_point = {'time': time_val, 'value': float(cur_vwap)} |
| vwap_up1_point = {'time': time_val, 'value': float(cur_vwap + cur_std)} |
| vwap_up2_point = {'time': time_val, 'value': float(cur_vwap + 2*cur_std)} |
| vwap_dn1_point = {'time': time_val, 'value': float(cur_vwap - cur_std)} |
| vwap_dn2_point = {'time': time_val, 'value': float(cur_vwap - 2*cur_std)} |
| |
| pred_vwap_list.append(vwap_list_point) |
| pred_vwap_up1_list.append(vwap_up1_point) |
| pred_vwap_up2_list.append(vwap_up2_point) |
| pred_vwap_dn1_list.append(vwap_dn1_point) |
| pred_vwap_dn2_list.append(vwap_dn2_point) |
| |
| pred_list.append({ |
| 'time': time_val, |
| 'open': float(row['open']), |
| 'high': float(row['high']), |
| 'low': float(row['low']), |
| 'close': pred_close |
| }) |
| p05_list.append({'time': time_val, 'value': float(p05_arr[idx])}) |
| p15_list.append({'time': time_val, 'value': float(p15_arr[idx])}) |
| p85_list.append({'time': time_val, 'value': float(p85_arr[idx])}) |
| p95_list.append({'time': time_val, 'value': float(p95_arr[idx])}) |
| |
| last_hist = hist_list[-1] |
| |
| |
| for row in pred_list: |
| row['open'] = float(row['open']) |
| row['close'] = float(row['close']) |
| row['high'] = float(row['high']) |
| row['low'] = float(row['low']) |
| |
| pred_list = [p for p in pred_list if p['time'] > last_hist['time']] |
| pred_list.insert(0, last_hist) |
| |
| for lst in [p05_list, p15_list, p85_list, p95_list]: |
| new_lst = [p for p in lst if p['time'] > last_hist['time']] |
| new_lst.insert(0, {'time': last_hist['time'], 'value': last_hist['close']}) |
| lst.clear() |
| lst.extend(new_lst) |
| |
| print(f"[{ticker_symbol}] Прогноз успешно сгенерирован!") |
| return jsonify({ |
| 'success': True, |
| 'history': hist_list, |
| 'prediction': pred_list, |
| 'p05': p05_list, |
| 'p15': p15_list, |
| 'p85': p85_list, |
| 'p95': p95_list, |
| 'hist_vwap': hist_vwap_list, |
| 'hist_vwap_up1': hist_vwap_up1_list, |
| 'hist_vwap_up2': hist_vwap_up2_list, |
| 'hist_vwap_dn1': hist_vwap_dn1_list, |
| 'hist_vwap_dn2': hist_vwap_dn2_list, |
| 'pred_vwap': pred_vwap_list, |
| 'pred_vwap_up1': pred_vwap_up1_list, |
| 'pred_vwap_up2': pred_vwap_up2_list, |
| 'pred_vwap_dn1': pred_vwap_dn1_list, |
| 'pred_vwap_dn2': pred_vwap_dn2_list |
| }) |
| |
| except Exception as e: |
| import traceback |
| traceback.print_exc() |
| print(f"[{ticker_symbol}] Ошибка: {e}") |
| return jsonify({'error': str(e)}), 500 |
|
|
| @app.route('/loading_meme') |
| def loading_meme(): |
| return send_file("/Users/kirillpigorev/.gemini/antigravity-ide/brain/0793dfd5-f02b-4fe6-a14e-0608fc119c2f/trading_doge_pixel_1781280876615.png") |
|
|
| if __name__ == '__main__': |
| app.run(host='0.0.0.0', port=5001, debug=True) |
|
|