tmmdev commited on
Commit
22ca14f
·
verified ·
1 Parent(s): 805f7bd

Create api-service.py

Browse files
Files changed (1) hide show
  1. api-service.py +991 -0
api-service.py ADDED
@@ -0,0 +1,991 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, jsonify, request, make_response
2
+ from flask_cors import CORS
3
+ import os
4
+ import threading
5
+ from collections import deque
6
+ import time
7
+ import yfinance as yf
8
+ from tvDatafeed import TvDatafeed, Interval
9
+ from datetime import datetime
10
+ import time
11
+ import sys
12
+ import os
13
+ import numpy as np
14
+ import talib
15
+ import json
16
+ import requests
17
+ import pandas as pd
18
+
19
+ app = Flask(__name__)
20
+ CORS(app)
21
+
22
+ def convert_symbol_format(tv_symbol):
23
+ # Exchange-specific prefixes
24
+ if ':' in tv_symbol:
25
+ exchange, base_symbol = tv_symbol.split(':')
26
+ else:
27
+ base_symbol = tv_symbol
28
+ exchange = ''
29
+
30
+ # Exchange mappings
31
+ exchange_maps = {
32
+ 'NSE': '.NS',
33
+ 'BSE': '.BO',
34
+ 'NYSE': '',
35
+ 'NASDAQ': '',
36
+ 'LSE': '.L',
37
+ 'TSX': '.TO',
38
+ 'HKEX': '.HK',
39
+ 'SSE': '.SS',
40
+ 'SZSE': '.SZ',
41
+ 'ASX': '.AX',
42
+ 'SGX': '.SI',
43
+ 'KRX': '.KS',
44
+ 'KOSDAQ': '.KQ',
45
+ 'JPX': '.T',
46
+ 'FWB': '.F',
47
+ 'SWX': '.SW',
48
+ 'MOEX': '.ME',
49
+ 'BIT': '.MI',
50
+ 'EURONEXT': '.PA'
51
+ }
52
+
53
+ # Futures mappings
54
+ futures_map = {
55
+ 'ES1!': 'ES=F', # S&P 500
56
+ 'NQ1!': 'NQ=F', # NASDAQ
57
+ 'YM1!': 'YM=F', # Dow
58
+ 'RTY1!': 'RTY=F', # Russell
59
+ 'CL1!': 'CL=F', # Crude Oil
60
+ 'GC1!': 'GC=F', # Gold
61
+ 'SI1!': 'SI=F', # Silver
62
+ 'HG1!': 'HG=F', # Copper
63
+ 'NG1!': 'NG=F', # Natural Gas
64
+ 'ZC1!': 'ZC=F', # Corn
65
+ 'ZS1!': 'ZS=F', # Soybean
66
+ 'ZW1!': 'ZW=F', # Wheat
67
+ 'KC1!': 'KC=F', # Coffee
68
+ 'CT1!': 'CT=F', # Cotton
69
+ 'CC1!': 'CC=F', # Cocoa
70
+ 'SB1!': 'SB=F', # Sugar
71
+ '6E1!': '6E=F', # Euro FX
72
+ '6B1!': '6B=F', # British Pound
73
+ '6J1!': '6J=F', # Japanese Yen
74
+ '6C1!': '6C=F', # Canadian Dollar
75
+ '6A1!': '6A=F', # Australian Dollar
76
+ '6N1!': '6N=F', # New Zealand Dollar
77
+ '6S1!': '6S=F' # Swiss Franc
78
+ }
79
+
80
+ # Forex mappings
81
+ forex_map = {
82
+ 'EURUSD': 'EUR=X',
83
+ 'GBPUSD': 'GBP=X',
84
+ 'USDJPY': 'JPY=X',
85
+ 'AUDUSD': 'AUD=X',
86
+ 'USDCAD': 'CAD=X',
87
+ 'NZDUSD': 'NZD=X',
88
+ 'USDCHF': 'CHF=X',
89
+ 'EURGBP': 'EURGBP=X',
90
+ 'EURJPY': 'EURJPY=X',
91
+ 'GBPJPY': 'GBPJPY=X',
92
+ 'AUDJPY': 'AUDJPY=X',
93
+ 'CADJPY': 'CADJPY=X',
94
+ 'NZDJPY': 'NZDJPY=X',
95
+ 'CHFJPY': 'CHFJPY=X'
96
+ }
97
+
98
+ # Crypto mappings
99
+ crypto_map = {
100
+ 'BTCUSDT': 'BTC-USD',
101
+ 'ETHUSDT': 'ETH-USD',
102
+ 'BNBUSDT': 'BNB-USD',
103
+ 'ADAUSDT': 'ADA-USD',
104
+ 'DOGEUSDT': 'DOGE-USD',
105
+ 'XRPUSDT': 'XRP-USD',
106
+ 'DOTUSDT': 'DOT-USD',
107
+ 'UNIUSDT': 'UNI-USD',
108
+ 'LINKUSDT': 'LINK-USD',
109
+ 'SOLUSDT': 'SOL-USD'
110
+ }
111
+
112
+ # Handle different market types
113
+ if any(fut in base_symbol for fut in futures_map.keys()):
114
+ return futures_map.get(base_symbol, base_symbol)
115
+
116
+ if any(x in tv_symbol for x in ['FX:', 'OANDA:', 'FOREX:']):
117
+ clean_symbol = ''.join(filter(str.isalpha, base_symbol))
118
+ return forex_map.get(clean_symbol, f"{clean_symbol}=X")
119
+
120
+ if 'USDT' in base_symbol:
121
+ return crypto_map.get(base_symbol, base_symbol.replace('USDT', '-USD'))
122
+
123
+ if exchange in exchange_maps:
124
+ return f"{base_symbol}{exchange_maps[exchange]}"
125
+
126
+ return base_symbol
127
+
128
+ @app.route('/codellama-chart-model', methods=['GET'])
129
+ def codellama_chart_model():
130
+ try:
131
+ symbol = request.args.get('symbol')
132
+ if not symbol:
133
+ return jsonify({'error': 'Symbol is required'}), 400
134
+
135
+ yf_symbol = convert_symbol_format(symbol)
136
+ print(f"\nCodeLlama Chart Analysis for {symbol} (YF: {yf_symbol})")
137
+
138
+ ticker = yf.Ticker(yf_symbol)
139
+ data = ticker.history(period='2y')[['Open', 'High', 'Low', 'Close', 'Volume']]
140
+
141
+ if data.empty:
142
+ return jsonify({'error': f'No data available for {yf_symbol}'}), 404
143
+
144
+ analysis_results = {
145
+ 'symbol': yf_symbol,
146
+ 'total_candles': len(data),
147
+ 'latest_price': float(data['Close'].iloc[-1]),
148
+ 'high_52w': float(data['High'].max()),
149
+ 'low_52w': float(data['Low'].min()),
150
+ 'avg_volume': float(data['Volume'].mean()),
151
+ 'price_change': float(data['Close'].iloc[-1] - data['Close'].iloc[0]),
152
+ 'price_change_pct': float((data['Close'].iloc[-1] - data['Close'].iloc[0]) / data['Close'].iloc[0] * 100)
153
+ }
154
+
155
+ return jsonify(analysis_results)
156
+
157
+ except Exception as e:
158
+ print(f"Error in analysis: {str(e)}")
159
+ return jsonify({'error': str(e)}), 500
160
+
161
+ # Market mapping and cache setup
162
+ MARKETS = {
163
+ # Stocks with their symbol patterns
164
+ "NYSE": ["", ".US", ".N", "-US"],
165
+ "NASDAQ": ["", ".US", ".O", "-US"],
166
+ "AMEX": [".A", "-AM"],
167
+ "TSX": [".TO", ".V", ".CN"],
168
+ "LSE": [".L", ".IL", "-L", "-LN"],
169
+ "EURONEXT": [".PA", ".AS", ".BR", ".AMS", ".LIS"],
170
+ "XETRA": [".DE", ".F", ".BE", ".HAM", ".HAN", ".MU", ".SG"],
171
+ "ASX": [".AX", "-AU"],
172
+ "NSE": [".NS", "-IN"],
173
+ "BSE": [".BO", "-IN"],
174
+ "HKEX": [".HK", "-HK"],
175
+ "SGX": [".SI", "-SG"],
176
+ "KRX": [".KS", ".KQ", "-KR"],
177
+ "JPX": [".T", ".JP", "-JP"],
178
+
179
+ # Crypto patterns
180
+ "BINANCE": ["USDT", "BUSD", "BTC", "ETH", "BNB"],
181
+ "COINBASE": ["USD", "-USD", "-USDC"],
182
+ "KRAKEN": ["-USD", "-EUR", "-BTC", "-ETH"],
183
+ "BITFINEX": [":USD", ":BTC", ":UST"],
184
+ "BYBIT": [".P", "-PERP"],
185
+
186
+ # Forex patterns
187
+ "FOREX": ["FX:", "FX_IDC:", "OANDA:", "FXCM:"],
188
+
189
+ # Futures
190
+ "CME": ["1!", "ES1!", "NQ1!", "YM1!"],
191
+ "NYMEX": ["CL1!", "NG1!", "GC1!", "SI1!"]
192
+ }
193
+
194
+ def determine_market(symbol):
195
+ """Determine the market based on symbol characteristics"""
196
+ for market, patterns in MARKETS.items():
197
+ if any(pattern in symbol for pattern in patterns):
198
+ return market
199
+
200
+ # Smart fallback based on symbol structure
201
+ if ':' in symbol:
202
+ prefix = symbol.split(':')[0]
203
+ return MARKETS.get(prefix, "NYSE")
204
+
205
+ return "NYSE"
206
+
207
+ def get_symbol_type(symbol: str) -> str:
208
+ if any(crypto_suffix in symbol for market, suffixes in MARKETS.items() if market in ["BINANCE", "COINBASE", "KRAKEN"]):
209
+ return "crypto"
210
+ if any(forex_pattern in symbol for forex_pattern in MARKETS["FOREX"]):
211
+ return "forex"
212
+ if any(futures_pattern in symbol for market, patterns in MARKETS.items() if market in ["CME", "NYMEX"]):
213
+ return "futures"
214
+ return "stock"
215
+
216
+ # Add your TradingView username and password
217
+ TV_USERNAME = "ojasforbusiness2"
218
+ TV_PASSWORD = "APVOm@007!!!"
219
+
220
+ # Initialize TvDatafeed with username and password
221
+ tv = TvDatafeed(username=TV_USERNAME, password=TV_PASSWORD)
222
+
223
+ # Store exchange info from symbol search results
224
+ exchange_info = {}
225
+
226
+ @app.route('/fetch_candles', methods=['GET'])
227
+ def fetch_candles():
228
+ try:
229
+ symbol = request.args.get('symbol')
230
+ timeframe = request.args.get('timeframe', '1D')
231
+
232
+ # Handle default crypto pairs and other symbols
233
+ if ':' not in symbol:
234
+ if 'USDT' in symbol:
235
+ exchange = 'BINANCE'
236
+ base_symbol = symbol
237
+ elif 'USD' in symbol and not symbol.endswith('USD'):
238
+ exchange = 'COINBASE'
239
+ base_symbol = symbol
240
+ else:
241
+ exchange = determine_market(symbol)
242
+ base_symbol = symbol
243
+
244
+ symbol = f"{exchange}:{base_symbol}"
245
+ else:
246
+ exchange, base_symbol = symbol.split(':')
247
+
248
+ print(f"Fetching data for {symbol}")
249
+
250
+ interval_mapping = {
251
+ '1d': Interval.in_daily,
252
+ '1w': Interval.in_weekly,
253
+ '1M': Interval.in_monthly,
254
+ '1h': Interval.in_1_hour,
255
+ '4h': Interval.in_4_hour,
256
+ '15m': Interval.in_15_minute,
257
+ '5m': Interval.in_5_minute,
258
+ '30m': Interval.in_30_minute
259
+ }
260
+
261
+ df = tv.get_hist(
262
+ symbol=symbol,
263
+ exchange=exchange,
264
+ interval=interval_mapping.get(timeframe.lower(), Interval.in_daily),
265
+ n_bars=1000
266
+ )
267
+
268
+ if df is None or df.empty:
269
+ raise ValueError(f"No data available for {symbol}")
270
+
271
+ candles = []
272
+ for index, row in df.iterrows():
273
+ timestamp = int(time.mktime(index.timetuple()) * 1000)
274
+ candle = {
275
+ 'time': timestamp,
276
+ 'open': float(row['open']),
277
+ 'high': float(row['high']),
278
+ 'low': float(row['low']),
279
+ 'close': float(row['close']),
280
+ 'volume': float(row['volume'])
281
+ }
282
+ candles.append(candle)
283
+
284
+ print(f"Successfully returned {len(candles)} candles for {symbol}")
285
+ return jsonify(candles)
286
+
287
+ except Exception as e:
288
+ print(f"Error processing request: {str(e)}")
289
+ return jsonify({'error': str(e)}), 500
290
+
291
+ @app.route('/fetch_segment_data', methods=['GET'])
292
+ def fetch_segment_data():
293
+ country = request.args.get('country', 'IN')
294
+ segment = request.args.get('segment', 'EQ')
295
+ timeframe = request.args.get('timeframe', '1D')
296
+
297
+ interval_mapping = {
298
+ '1D': Interval.in_daily,
299
+ '1W': Interval.in_weekly,
300
+ '1M': Interval.in_monthly,
301
+ '1h': Interval.in_1_hour,
302
+ '4h': Interval.in_4_hour,
303
+ '15m': Interval.in_15_minute
304
+ }
305
+
306
+ interval = interval_mapping.get(timeframe, Interval.in_daily)
307
+ exchanges = segment_data[country][segment]['exchanges']
308
+ segment_tickers_data = {}
309
+
310
+ for exchange in exchanges:
311
+ symbols = tv.search_symbol(exchange)
312
+ for symbol in symbols:
313
+ df = tv.get_hist(
314
+ symbol=symbol,
315
+ exchange=exchange,
316
+ interval=interval,
317
+ n_bars=300
318
+ )
319
+ segment_tickers_data[symbol] = {
320
+ 'open': df['open'].tolist(),
321
+ 'high': df['high'].tolist(),
322
+ 'low': df['low'].tolist(),
323
+ 'close': df['close'].tolist(),
324
+ 'volume': df['volume'].tolist(),
325
+ 'timestamp': df.index.astype(np.int64) // 10**6
326
+ }
327
+
328
+ return jsonify({
329
+ 'country': country,
330
+ 'segment': segment,
331
+ 'exchanges': exchanges,
332
+ 'data': segment_tickers_data
333
+ })
334
+
335
+ def format_symbol(symbol):
336
+ """Format symbol for TradingView"""
337
+ # Add exchange prefix if needed
338
+ if ':' not in symbol:
339
+ return f"BINANCE:{symbol}" # Default to BINANCE, adjust as needed
340
+ return symbol
341
+
342
+
343
+
344
+
345
+ def process_historical_data(data):
346
+ """Process historical data into candle format"""
347
+ candles = []
348
+ for bar in data:
349
+ candle = {
350
+ 'time': int(bar['time']),
351
+ 'open': str(bar['open']),
352
+ 'high': str(bar['high']),
353
+ 'low': str(bar['low']),
354
+ 'close': str(bar['close']),
355
+ 'volume': str(bar['volume'])
356
+ }
357
+ candles.append(candle)
358
+ return candles
359
+
360
+
361
+
362
+
363
+
364
+
365
+ @app.errorhandler(500)
366
+ def internal_error(error):
367
+ print(f"Internal Server Error: {str(error)}")
368
+ return jsonify({'error': 'Internal Server Error'}), 500
369
+
370
+ @app.errorhandler(404)
371
+ def not_found_error(error):
372
+ return jsonify({'error': 'Not Found'}), 404
373
+
374
+ @app.route('/fetch_stock_details', methods=['GET'])
375
+ def fetch_stock_details():
376
+ try:
377
+ symbol = request.args.get('symbol')
378
+
379
+ if not symbol:
380
+ return jsonify({'error': 'Symbol is required'}), 400
381
+
382
+ print(f"Fetching details for symbol: {symbol}")
383
+
384
+ ticker = yf.Ticker(symbol)
385
+ info = ticker.info
386
+
387
+ stock_details = {
388
+ 'symbol': symbol,
389
+ 'price': float(info.get('currentPrice', info.get('regularMarketPrice', 0))),
390
+ 'change': float(info.get('regularMarketChange', 0)),
391
+ 'changePercent': float(info.get('regularMarketChangePercent', 0)),
392
+ 'companyName': info.get('longName', ''),
393
+ 'exchange': info.get('exchange', ''),
394
+ 'industry': info.get('industry', ''),
395
+ 'lastUpdated': str(info.get('regularMarketTime', '')),
396
+
397
+ # Price information
398
+ 'previousClose': float(info.get('previousClose', 0)),
399
+ 'open': float(info.get('open', 0)),
400
+ 'dayLow': float(info.get('dayLow', 0)),
401
+ 'dayHigh': float(info.get('dayHigh', 0)),
402
+
403
+ # Volume information
404
+ 'volume': float(info.get('volume', 0)),
405
+ 'avgVolume': float(info.get('averageVolume', 0)),
406
+ 'avgVolume10days': float(info.get('averageVolume10days', 0)),
407
+
408
+ # Market data
409
+ 'marketCap': float(info.get('marketCap', 0)),
410
+ 'high52Week': float(info.get('fiftyTwoWeekHigh', 0)),
411
+ 'low52Week': float(info.get('fiftyTwoWeekLow', 0)),
412
+
413
+ # Financial ratios
414
+ 'peRatio': float(info.get('trailingPE', 0)) if info.get('trailingPE') else None,
415
+ 'forwardPE': float(info.get('forwardPE', 0)) if info.get('forwardPE') else None,
416
+ 'eps': float(info.get('trailingEps', 0)) if info.get('trailingEps') else None,
417
+ 'forwardEps': float(info.get('forwardEps', 0)) if info.get('forwardEps') else None,
418
+ 'dividend': float(info.get('dividendYield', 0)) if info.get('dividendYield') else None,
419
+ 'beta': float(info.get('beta', 0)) if info.get('beta') else None,
420
+ 'priceToBook': float(info.get('priceToBook', 0)) if info.get('priceToBook') else None,
421
+ 'debtToEquity': float(info.get('debtToEquity', 0)) if info.get('debtToEquity') else None,
422
+ 'returnOnEquity': float(info.get('returnOnEquity', 0)) if info.get('returnOnEquity') else None,
423
+ 'returnOnAssets': float(info.get('returnOnAssets', 0)) if info.get('returnOnAssets') else None,
424
+ 'profitMargins': float(info.get('profitMargins', 0)) if info.get('profitMargins') else None,
425
+ 'operatingMargins': float(info.get('operatingMargins', 0)) if info.get('operatingMargins') else None,
426
+
427
+ # Additional info
428
+ 'sector': info.get('sector', ''),
429
+ 'description': info.get('longBusinessSummary', ''),
430
+ 'website': info.get('website', ''),
431
+ 'employees': int(info.get('fullTimeEmployees', 0)) if info.get('fullTimeEmployees') else None
432
+ }
433
+
434
+ return jsonify(stock_details)
435
+
436
+ except Exception as e:
437
+ print(f"Error fetching stock details: {str(e)}")
438
+ return jsonify({'error': str(e)}), 500
439
+
440
+ watchlists = []
441
+
442
+ @app.route('/watchlists', methods=['GET'])
443
+ def get_watchlists():
444
+ return jsonify(watchlists)
445
+
446
+ @app.route('/watchlist', methods=['POST'])
447
+ def watchlist():
448
+ data = request.get_json()
449
+ if not data:
450
+ return jsonify({'error': 'Request body is required'}), 400
451
+
452
+ if 'name' in data:
453
+ # Creating a new watchlist
454
+ new_watchlist_name = data['name']
455
+ watchlists.append({'name': new_watchlist_name, 'stocks': []})
456
+ return jsonify({'message': f'Watchlist "{new_watchlist_name}" created successfully'}), 201
457
+
458
+ elif 'symbols' in data and isinstance(data['symbols'], list):
459
+ # Adding symbols to a watchlist
460
+ symbol_list = data['symbols']
461
+ stocks_data = []
462
+ for symbol in symbol_list:
463
+ try:
464
+ ticker = yf.Ticker(symbol.strip())
465
+ info = ticker.info
466
+ stock_data = {
467
+ 'symbol': symbol.strip(),
468
+ 'last': str(info.get('currentPrice', info.get('regularMarketPrice', 0))),
469
+ 'chg': str(info.get('regularMarketChange', 0)),
470
+ 'chgPercent': str(info.get('regularMarketChangePercent', 0))
471
+ }
472
+ stocks_data.append(stock_data)
473
+ except Exception as e:
474
+ print(f"Error fetching data for {symbol}: {str(e)}")
475
+ continue
476
+ return jsonify(stocks_data)
477
+
478
+ else:
479
+ return jsonify({'error': 'Invalid request body'}), 400
480
+
481
+ @app.route('/fetch_multiple_stocks', methods=['GET'])
482
+ def fetch_multiple_stocks():
483
+ try:
484
+ symbols = request.args.get('symbols')
485
+ if not symbols:
486
+ return jsonify({'error': 'Symbols are required'}), 400
487
+
488
+ # Split the comma-separated symbols
489
+ symbol_list = symbols.split(',')
490
+
491
+ stocks_data = []
492
+ for symbol in symbol_list:
493
+ try:
494
+ ticker = yf.Ticker(symbol.strip())
495
+ info = ticker.info
496
+
497
+ stock_data = {
498
+ 'symbol': symbol.strip(),
499
+ 'price': str(info.get('currentPrice', info.get('regularMarketPrice', 0))),
500
+ 'change': str(info.get('regularMarketChange', 0)),
501
+ 'changePercent': str(info.get('regularMarketChangePercent', 0)),
502
+ 'companyName': info.get('longName', '')
503
+ }
504
+ stocks_data.append(stock_data)
505
+ except Exception as e:
506
+ print(f"Error fetching data for {symbol}: {str(e)}")
507
+ continue
508
+
509
+ return jsonify(stocks_data)
510
+
511
+ except Exception as e:
512
+ print(f"Error processing request: {str(e)}")
513
+ return jsonify(stocks_data)
514
+
515
+ except Exception as e:
516
+ print(f"Error processing request: {str(e)}")
517
+ return jsonify({'error': str(e)}), 500
518
+
519
+ @app.route('/get_stock_suggestions', methods=['GET'])
520
+ def get_stock_suggestions():
521
+ query = request.args.get('query', '').upper()
522
+
523
+ try:
524
+ search_url = "https://symbol-search.tradingview.com/symbol_search/"
525
+ params = {
526
+ 'text': query,
527
+ 'hl': True,
528
+ 'exchange': '',
529
+ 'lang': 'en',
530
+ 'type': 'stock,crypto,forex,futures' # Added more types for comprehensive search
531
+ }
532
+
533
+ headers = {
534
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
535
+ 'Accept': 'application/json',
536
+ 'Referer': 'https://www.tradingview.com/',
537
+ 'Origin': 'https://www.tradingview.com',
538
+ 'Accept-Language': 'en-US,en;q=0.9'
539
+ }
540
+
541
+ response = requests.get(search_url, params=params, headers=headers, timeout=5)
542
+ data = response.json()
543
+
544
+ # Include the full exchange and symbol info in results
545
+ formatted_results = [{
546
+ 'symbol': item['symbol'].replace('<em>', '').replace('</em>', ''),
547
+ 'name': item['description'].replace('<em>', '').replace('</em>', ''),
548
+ 'exchange': item['exchange'],
549
+ 'fullSymbol': f"{item['exchange']}:{item['symbol'].replace('<em>', '').replace('</em>', '')}"
550
+ } for item in data]
551
+
552
+ return jsonify(formatted_results)
553
+
554
+ except Exception as e:
555
+ print(f"Search error: {str(e)}")
556
+ return jsonify([])
557
+
558
+ @app.route('/fetch_financials', methods=['GET'])
559
+ def fetch_financials():
560
+ try:
561
+ symbol = request.args.get('symbol')
562
+
563
+ if not symbol:
564
+ return jsonify({'error': 'Symbol is required'}), 400
565
+
566
+ print(f"Fetching income statement for symbol: {symbol}")
567
+
568
+ ticker = yf.Ticker(symbol)
569
+
570
+ try:
571
+ # Get income statement data with error handling
572
+ annual_income_stmt = ticker.income_stmt
573
+ print(f"Raw income statement data received for {symbol}")
574
+
575
+ # Validate if we got valid data
576
+ if annual_income_stmt is None or annual_income_stmt.empty:
577
+ return jsonify({'error': f'No financial data available for symbol {symbol}'}), 404
578
+
579
+ # Convert DataFrame to dictionary with proper date handling
580
+ def process_dataframe(df):
581
+ if df.empty:
582
+ return {}
583
+
584
+ data_dict = {}
585
+ try:
586
+ # Iterate through rows (metrics)
587
+ for idx in df.index:
588
+ metric_data = {}
589
+ # Iterate through columns (dates)
590
+ for col in df.columns:
591
+ try:
592
+ # Convert timestamp to string format
593
+ date_key = col.strftime('%Y-%m-%d') if hasattr(col, 'strftime') else str(col)
594
+ value = df.loc[idx, col]
595
+ # Convert numpy/pandas types to native Python types
596
+ if pd.isna(value):
597
+ metric_data[date_key] = None
598
+ else:
599
+ metric_data[date_key] = str(float(value))
600
+ except Exception as e:
601
+ print(f"Error processing column {col} for metric {idx}: {str(e)}")
602
+ metric_data[str(col)] = None
603
+ data_dict[str(idx)] = metric_data
604
+ except Exception as e:
605
+ print(f"Error processing dataframe: {str(e)}")
606
+ return {}
607
+
608
+ return data_dict
609
+
610
+ # Process the income statement
611
+ processed_data = process_dataframe(annual_income_stmt)
612
+
613
+ if not processed_data:
614
+ return jsonify({'error': 'Failed to process financial data'}), 500
615
+
616
+ financials = {
617
+ 'income_statement': processed_data
618
+ }
619
+
620
+ print(f"Successfully processed financial data for {symbol}")
621
+ return jsonify(financials)
622
+
623
+ except Exception as e:
624
+ print(f"Error processing ticker data for {symbol}: {str(e)}")
625
+ return jsonify({'error': f'Failed to fetch financial data: {str(e)}'}), 500
626
+
627
+ except Exception as e:
628
+ print(f"Error in fetch_financials: {str(e)}")
629
+ return jsonify({'error': str(e)}), 500
630
+
631
+ @app.route('/fetch_balance_sheet', methods=['GET'])
632
+ def fetch_balance_sheet():
633
+ try:
634
+ symbol = request.args.get('symbol')
635
+
636
+ if not symbol:
637
+ return jsonify({'error': 'Symbol is required'}), 400
638
+
639
+ print(f"Fetching balance sheet for symbol: {symbol}")
640
+
641
+ ticker = yf.Ticker(symbol)
642
+
643
+ try:
644
+ # Get balance sheet data with error handling
645
+ balance_sheet = ticker.balance_sheet
646
+ print(f"Raw balance sheet data received for {symbol}")
647
+
648
+ # Validate if we got valid data
649
+ if balance_sheet is None or balance_sheet.empty:
650
+ return jsonify({'error': f'No balance sheet data available for symbol {symbol}'}), 404
651
+
652
+ # Convert DataFrame to dictionary with proper date handling
653
+ def process_dataframe(df):
654
+ if df.empty:
655
+ return {}
656
+
657
+ data_dict = {}
658
+ try:
659
+ # Iterate through rows (metrics)
660
+ for idx in df.index:
661
+ metric_data = {}
662
+ # Iterate through columns (dates)
663
+ for col in df.columns:
664
+ try:
665
+ # Convert timestamp to string format
666
+ date_key = col.strftime('%Y-%m-%d') if hasattr(col, 'strftime') else str(col)
667
+ value = df.loc[idx, col]
668
+ # Convert numpy/pandas types to native Python types
669
+ if pd.isna(value):
670
+ metric_data[date_key] = None
671
+ else:
672
+ metric_data[date_key] = str(float(value))
673
+ except Exception as e:
674
+ print(f"Error processing column {col} for metric {idx}: {str(e)}")
675
+ metric_data[str(col)] = None
676
+ data_dict[str(idx)] = metric_data
677
+ except Exception as e:
678
+ print(f"Error processing dataframe: {str(e)}")
679
+ return {}
680
+
681
+ return data_dict
682
+
683
+ # Process the balance sheet
684
+ processed_data = process_dataframe(balance_sheet)
685
+
686
+ if not processed_data:
687
+ return jsonify({'error': 'Failed to process balance sheet data'}), 500
688
+
689
+ balance_sheet_data = {
690
+ 'balance_sheet': processed_data
691
+ }
692
+
693
+ print(f"Successfully processed balance sheet data for {symbol}")
694
+ return jsonify(balance_sheet_data)
695
+
696
+ except Exception as e:
697
+ print(f"Error processing ticker data for {symbol}: {str(e)}")
698
+ return jsonify({'error': f'Failed to fetch balance sheet data: {str(e)}'}), 500
699
+
700
+ except Exception as e:
701
+ print(f"Error in fetch_balance_sheet: {str(e)}")
702
+ return jsonify({'error': str(e)}), 500
703
+
704
+ @app.route('/fetch_cash_flow', methods=['GET'])
705
+ def fetch_cash_flow():
706
+ try:
707
+ symbol = request.args.get('symbol')
708
+
709
+ if not symbol:
710
+ return jsonify({'error': 'Symbol is required'}), 400
711
+
712
+ print(f"Fetching cash flow for symbol: {symbol}")
713
+
714
+ ticker = yf.Ticker(symbol)
715
+
716
+ try:
717
+ # Get cash flow data with error handling
718
+ cash_flow = ticker.cashflow
719
+ print(f"Raw cash flow data received for {symbol}")
720
+
721
+ # Validate if we got valid data
722
+ if cash_flow is None or cash_flow.empty:
723
+ return jsonify({'error': f'No cash flow data available for symbol {symbol}'}), 404
724
+
725
+ # Convert DataFrame to dictionary with proper date handling
726
+ def process_dataframe(df):
727
+ if df.empty:
728
+ return {}
729
+
730
+ data_dict = {}
731
+ try:
732
+ # Iterate through rows (metrics)
733
+ for idx in df.index:
734
+ metric_data = {}
735
+ # Iterate through columns (dates)
736
+ for col in df.columns:
737
+ try:
738
+ # Convert timestamp to string format
739
+ date_key = col.strftime('%Y-%m-%d') if hasattr(col, 'strftime') else str(col)
740
+ value = df.loc[idx, col]
741
+ # Convert numpy/pandas types to native Python types
742
+ if pd.isna(value):
743
+ metric_data[date_key] = None
744
+ else:
745
+ metric_data[date_key] = str(float(value))
746
+ except Exception as e:
747
+ print(f"Error processing column {col} for metric {idx}: {str(e)}")
748
+ metric_data[str(col)] = None
749
+ data_dict[str(idx)] = metric_data
750
+ except Exception as e:
751
+ print(f"Error processing dataframe: {str(e)}")
752
+ return {}
753
+
754
+ return data_dict
755
+
756
+ # Process the cash flow
757
+ processed_data = process_dataframe(cash_flow)
758
+
759
+ if not processed_data:
760
+ return jsonify({'error': 'Failed to process cash flow data'}), 500
761
+
762
+ cash_flow_data = {
763
+ 'cash_flow': processed_data
764
+ }
765
+
766
+ print(f"Successfully processed cash flow data for {symbol}")
767
+ return jsonify(cash_flow_data)
768
+
769
+ except Exception as e:
770
+ print(f"Error processing ticker data for {symbol}: {str(e)}")
771
+ return jsonify({'error': f'Failed to fetch cash flow data: {str(e)}'}), 500
772
+
773
+ except Exception as e:
774
+ print(f"Error in fetch_cash_flow: {str(e)}")
775
+ return jsonify({'error': str(e)}), 500
776
+
777
+ @app.route('/fetch_statistics', methods=['GET'])
778
+ def fetch_statistics():
779
+ try:
780
+ symbol = request.args.get('symbol')
781
+
782
+ if not symbol:
783
+ return jsonify({'error': 'Symbol is required'}), 400
784
+
785
+ print(f"Fetching statistics for symbol: {symbol}")
786
+
787
+ ticker = yf.Ticker(symbol)
788
+ stats = ticker.stats()
789
+
790
+ if not stats:
791
+ return jsonify({'error': f'No statistics data found for symbol {symbol}'}), 404
792
+
793
+ # Include ticker info
794
+ ticker_info = ticker.info
795
+
796
+ statistics_data = {
797
+ 'stats': stats,
798
+ 'ticker_info': ticker_info
799
+ }
800
+
801
+ return jsonify(statistics_data)
802
+
803
+ except Exception as e:
804
+ print(f"Error fetching statistics: {str(e)}")
805
+ return jsonify({'error': str(e)}), 500
806
+
807
+
808
+ @app.route('/market_segments', methods=['GET'])
809
+ def get_market_segments():
810
+ country = request.args.get('country', 'IN')
811
+ segment = request.args.get('segment', 'EQ')
812
+
813
+ tv = TvDatafeed()
814
+ exchanges = segment_data[country][segment]['exchanges']
815
+ symbols = []
816
+
817
+ for exchange in exchanges:
818
+ exchange_symbols = tv.search_symbol(exchange)
819
+ symbols.extend(exchange_symbols)
820
+
821
+ return jsonify({
822
+ 'country': country,
823
+ 'segment': segment,
824
+ 'exchanges': exchanges,
825
+ 'symbols': symbols
826
+ })
827
+
828
+ def fetch_candle_data(symbol, timeframe):
829
+ response = requests.get(f'http://localhost:5000/fetch_candles?symbol={symbol}&timeframe={timeframe}')
830
+ return response.json()
831
+
832
+ def calculate_technicals(candle_data):
833
+ close_prices = np.array([candle['close'] for candle in candle_data])
834
+ high_prices = np.array([candle['high'] for candle in candle_data])
835
+ low_prices = np.array([candle['low'] for candle in candle_data])
836
+ volume = np.array([candle['volume'] for candle in candle_data])
837
+
838
+ def safe_talib_function(func, *args, **kwargs):
839
+ result = func(*args, **kwargs)
840
+ return np.nan_to_num(result).tolist()
841
+
842
+ technicals = {
843
+ 'moving_averages': {
844
+ 'SMA': {
845
+ 'SMA10': safe_talib_function(talib.SMA, close_prices, timeperiod=10),
846
+ 'SMA20': safe_talib_function(talib.SMA, close_prices, timeperiod=20),
847
+ 'SMA30': safe_talib_function(talib.SMA, close_prices, timeperiod=30),
848
+ 'SMA50': safe_talib_function(talib.SMA, close_prices, timeperiod=50),
849
+ 'SMA100': safe_talib_function(talib.SMA, close_prices, timeperiod=100),
850
+ 'SMA200': safe_talib_function(talib.SMA, close_prices, timeperiod=200),
851
+ },
852
+ 'EMA': {
853
+ 'EMA10': safe_talib_function(talib.EMA, close_prices, timeperiod=10),
854
+ 'EMA20': safe_talib_function(talib.EMA, close_prices, timeperiod=20),
855
+ 'EMA30': safe_talib_function(talib.EMA, close_prices, timeperiod=30),
856
+ 'EMA50': safe_talib_function(talib.EMA, close_prices, timeperiod=50),
857
+ 'EMA100': safe_talib_function(talib.EMA, close_prices, timeperiod=100),
858
+ 'EMA200': safe_talib_function(talib.EMA, close_prices, timeperiod=200),
859
+ },
860
+ 'VWMA': {
861
+ 'VWMA20': safe_talib_function(talib.WMA, close_prices, timeperiod=20),
862
+ },
863
+ 'HMA': {
864
+ 'HMA9': safe_talib_function(talib.WMA, close_prices, timeperiod=9),
865
+ },
866
+ },
867
+ 'oscillators': {
868
+ 'RSI': safe_talib_function(talib.RSI, close_prices, timeperiod=14),
869
+ 'MACD': {
870
+ 'macd': safe_talib_function(talib.MACD, close_prices)[0],
871
+ 'signal': safe_talib_function(talib.MACD, close_prices)[1],
872
+ 'hist': safe_talib_function(talib.MACD, close_prices)[2],
873
+ },
874
+ 'Stochastic': {
875
+ 'slowk': safe_talib_function(talib.STOCH, high_prices, low_prices, close_prices)[0],
876
+ 'slowd': safe_talib_function(talib.STOCH, high_prices, low_prices, close_prices)[1],
877
+ },
878
+ 'CCI': safe_talib_function(talib.CCI, high_prices, low_prices, close_prices),
879
+ 'ADX': safe_talib_function(talib.ADX, high_prices, low_prices, close_prices),
880
+ 'Williams%R': safe_talib_function(talib.WILLR, high_prices, low_prices, close_prices),
881
+
882
+ 'Momentum': safe_talib_function(talib.MOM, close_prices, timeperiod=10),
883
+ 'StochRSI': {
884
+ 'fastk': safe_talib_function(talib.STOCHRSI, close_prices)[0],
885
+ 'fastd': safe_talib_function(talib.STOCHRSI, close_prices)[1],
886
+ },
887
+ 'BullBearPower': safe_talib_function(talib.BBANDS, close_prices)[0],
888
+ 'UltimateOscillator': safe_talib_function(talib.ULTOSC, high_prices, low_prices, close_prices, timeperiod1=7, timeperiod2=14, timeperiod3=28),
889
+ },
890
+ 'pivots': {
891
+ 'Classic': safe_talib_function(talib.PIVOT, high_prices, low_prices, close_prices),
892
+ 'Fibonacci': safe_talib_function(talib.PIVOT, high_prices, low_prices, close_prices, type='fibonacci'),
893
+ 'Camarilla': safe_talib_function(talib.PIVOT, high_prices, low_prices, close_prices, type='camarilla'),
894
+ 'Woodie': safe_talib_function(talib.PIVOT, high_prices, low_prices, close_prices, type='woodie'),
895
+ 'DM': safe_talib_function(talib.PIVOT, high_prices, low_prices, close_prices, type='dm'),
896
+ }
897
+ }
898
+
899
+ return technicals
900
+
901
+ @app.route('/fetch_technicals', methods=['GET'])
902
+ def fetch_technicals():
903
+ symbol = request.args.get('symbol')
904
+ timeframe = request.args.get('timeframe', '1d')
905
+
906
+ if not symbol:
907
+ return jsonify({'error': 'Symbol is required'}), 400
908
+
909
+ candle_data = fetch_candle_data(symbol, timeframe)
910
+ if isinstance(candle_data, dict) and 'error' in candle_data:
911
+ return jsonify(candle_data), 500
912
+
913
+ technicals = calculate_technicals(candle_data)
914
+
915
+ return jsonify(technicals)
916
+
917
+ @app.route('/market_news', methods=['GET'])
918
+ def get_market_news():
919
+ try:
920
+ # You can integrate with news APIs like NewsAPI or Financial Modeling Prep
921
+ news_data = requests.get('https://newsapi.org/v2/everything',
922
+ params={
923
+ 'q': 'stock market',
924
+ 'apiKey': 'YOUR_API_KEY',
925
+ 'pageSize': 30
926
+ }
927
+ ).json()
928
+
929
+ return jsonify(news_data)
930
+ except Exception as e:
931
+ return jsonify({'error': str(e)}), 500
932
+
933
+ @app.route('/market_movers', methods=['GET'])
934
+ def get_market_movers():
935
+ try:
936
+ # Get top gainers and losers
937
+ gainers = []
938
+ losers = []
939
+
940
+ # Sample major indices
941
+ indices = ['SPY', 'QQQ', 'DIA', 'IWM']
942
+
943
+ for symbol in indices:
944
+ ticker = yf.Ticker(symbol)
945
+ current_price = ticker.info.get('regularMarketPrice', 0)
946
+ prev_close = ticker.info.get('previousClose', 0)
947
+ change = ((current_price - prev_close) / prev_close) * 100
948
+
949
+ data = {
950
+ 'symbol': symbol,
951
+ 'price': current_price,
952
+ 'change': change
953
+ }
954
+
955
+ if change > 0:
956
+ gainers.append(data)
957
+ else:
958
+ losers.append(data)
959
+
960
+ return jsonify({
961
+ 'gainers': sorted(gainers, key=lambda x: x['change'], reverse=True)[:5],
962
+ 'losers': sorted(losers, key=lambda x: x['change'])[:5]
963
+ })
964
+ except Exception as e:
965
+ return jsonify({'error': str(e)}), 500
966
+
967
+ @app.route('/market_indices', methods=['GET'])
968
+ def get_market_indices():
969
+ try:
970
+ indices = ['^GSPC', '^DJI', '^IXIC', '^RUT']
971
+ index_data = {}
972
+
973
+ for index in indices:
974
+ ticker = yf.Ticker(index)
975
+ hist = ticker.history(period='1d', interval='5m')
976
+
977
+ index_data[index] = {
978
+ 'prices': hist['Close'].tolist(),
979
+ 'times': hist.index.strftime('%H:%M').tolist(),
980
+ 'change': float(hist['Close'][-1] - hist['Close'][0]),
981
+ 'changePercent': float((hist['Close'][-1] - hist['Close'][0]) / hist['Close'][0] * 100)
982
+ }
983
+
984
+ return jsonify(index_data)
985
+ except Exception as e:
986
+ return jsonify({'error': str(e)}), 500
987
+
988
+
989
+
990
+ if __name__ == '__main__':
991
+ app.run(debug=True, port=5000)