Spaces:
Running
Running
Commit
路
cc50687
1
Parent(s):
8eca612
update pytrade.py
Browse files- pytrade.py +362 -2
pytrade.py
CHANGED
|
@@ -12,7 +12,6 @@ import jwt # PyJWT
|
|
| 12 |
from flask import Flask, request, jsonify, Response, make_response
|
| 13 |
from flask_cors import CORS
|
| 14 |
|
| 15 |
-
# --- Your modules ---
|
| 16 |
# --- Your modules ---
|
| 17 |
from analysestock import analysestock
|
| 18 |
from list import (
|
|
@@ -26,6 +25,7 @@ from signin import get_db_connection, ensure_user_table_exists # <- NOTE: from
|
|
| 26 |
import bcrypt # pip install bcrypt
|
| 27 |
from typing import Tuple
|
| 28 |
from werkzeug.security import generate_password_hash, check_password_hash
|
|
|
|
| 29 |
|
| 30 |
# ------------------------------------------------------------------------------
|
| 31 |
# App, ENV, CORS
|
|
@@ -47,6 +47,7 @@ FRONTEND_ORIGIN = os.environ.get(
|
|
| 47 |
"FRONTEND_ORIGIN",
|
| 48 |
"https://pykara-py-trade.static.hf.space,https://localhost:4200"
|
| 49 |
)
|
|
|
|
| 50 |
allowed = [o.strip() for o in FRONTEND_ORIGIN.split(",") if o.strip()]
|
| 51 |
CORS(
|
| 52 |
app,
|
|
@@ -524,7 +525,7 @@ def create_community_post():
|
|
| 524 |
if not row or row[0] is None:
|
| 525 |
return jsonify({"error": "Failed to retrieve new post id"}), 500
|
| 526 |
|
| 527 |
-
new_id = int(row[0])
|
| 528 |
|
| 529 |
return jsonify({
|
| 530 |
"id": new_id,
|
|
@@ -616,6 +617,365 @@ def list_community_posts():
|
|
| 616 |
except:
|
| 617 |
pass
|
| 618 |
conn.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 619 |
# ------------------------------------------------------------------------------
|
| 620 |
# Run
|
| 621 |
# ------------------------------------------------------------------------------
|
|
|
|
| 12 |
from flask import Flask, request, jsonify, Response, make_response
|
| 13 |
from flask_cors import CORS
|
| 14 |
|
|
|
|
| 15 |
# --- Your modules ---
|
| 16 |
from analysestock import analysestock
|
| 17 |
from list import (
|
|
|
|
| 25 |
import bcrypt # pip install bcrypt
|
| 26 |
from typing import Tuple
|
| 27 |
from werkzeug.security import generate_password_hash, check_password_hash
|
| 28 |
+
import yfinance as yf
|
| 29 |
|
| 30 |
# ------------------------------------------------------------------------------
|
| 31 |
# App, ENV, CORS
|
|
|
|
| 47 |
"FRONTEND_ORIGIN",
|
| 48 |
"https://pykara-py-trade.static.hf.space,https://localhost:4200"
|
| 49 |
)
|
| 50 |
+
|
| 51 |
allowed = [o.strip() for o in FRONTEND_ORIGIN.split(",") if o.strip()]
|
| 52 |
CORS(
|
| 53 |
app,
|
|
|
|
| 525 |
if not row or row[0] is None:
|
| 526 |
return jsonify({"error": "Failed to retrieve new post id"}), 500
|
| 527 |
|
| 528 |
+
new_id = int(row[0]);
|
| 529 |
|
| 530 |
return jsonify({
|
| 531 |
"id": new_id,
|
|
|
|
| 617 |
except:
|
| 618 |
pass
|
| 619 |
conn.close()
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
# ------------------------------------------------------------------------------
|
| 623 |
+
# Market overview values (uses yfinance)
|
| 624 |
+
# ------------------------------------------------------------------------------
|
| 625 |
+
@app.get("/getmarketcards")
|
| 626 |
+
def get_market_cards():
|
| 627 |
+
"""
|
| 628 |
+
Returns a list of market overview items with live prices fetched from yfinance.
|
| 629 |
+
Each item: { title, price, chg, chgPct }
|
| 630 |
+
"""
|
| 631 |
+
# map of friendly title -> yfinance symbol
|
| 632 |
+
tickers = {
|
| 633 |
+
'Gold': 'GC=F',
|
| 634 |
+
'Silver': 'SI=F',
|
| 635 |
+
'Crude Oil (Brent)': 'BZ=F',
|
| 636 |
+
'Crude Oil (WTI)': 'CL=F',
|
| 637 |
+
'Natural Gas': 'NG=F',
|
| 638 |
+
'USD/INR': 'INR=X',
|
| 639 |
+
'EUR/USD': 'EURUSD=X',
|
| 640 |
+
'GBP/USD': 'GBPUSD=X',
|
| 641 |
+
'Bitcoin': 'BTC-USD',
|
| 642 |
+
'Ethereum': 'ETH-USD',
|
| 643 |
+
'S&P 500': '^GSPC',
|
| 644 |
+
'NASDAQ': '^IXIC',
|
| 645 |
+
'DAX': '^GDAXI',
|
| 646 |
+
'Nikkei': '^N225',
|
| 647 |
+
'Copper': 'HG=F'
|
| 648 |
+
}
|
| 649 |
+
|
| 650 |
+
out = []
|
| 651 |
+
for title, symbol in tickers.items():
|
| 652 |
+
try:
|
| 653 |
+
t = yf.Ticker(symbol)
|
| 654 |
+
# try to get recent close prices
|
| 655 |
+
hist = None
|
| 656 |
+
try:
|
| 657 |
+
hist = t.history(period="2d")
|
| 658 |
+
except Exception:
|
| 659 |
+
hist = None
|
| 660 |
+
|
| 661 |
+
price = None
|
| 662 |
+
chg = None
|
| 663 |
+
chg_pct = None
|
| 664 |
+
|
| 665 |
+
if hist is not None and hasattr(hist, 'empty') and not hist.empty:
|
| 666 |
+
closes = list(hist['Close'].values)
|
| 667 |
+
if len(closes) >= 2:
|
| 668 |
+
last = float(closes[-1])
|
| 669 |
+
prev = float(closes[-2])
|
| 670 |
+
else:
|
| 671 |
+
last = float(closes[-1])
|
| 672 |
+
prev = last
|
| 673 |
+
price = last
|
| 674 |
+
chg = last - prev
|
| 675 |
+
chg_pct = (chg / prev * 100) if prev != 0 else 0.0
|
| 676 |
+
else:
|
| 677 |
+
# fallback: try fast info lookup
|
| 678 |
+
info = {}
|
| 679 |
+
try:
|
| 680 |
+
info = t.info or {}
|
| 681 |
+
except Exception:
|
| 682 |
+
info = {}
|
| 683 |
+
# try fields: 'regularMarketPrice', 'previousClose'
|
| 684 |
+
last = info.get('regularMarketPrice') or info.get('currentPrice') or info.get('previousClose')
|
| 685 |
+
prev = info.get('previousClose')
|
| 686 |
+
try:
|
| 687 |
+
if last is not None:
|
| 688 |
+
price = float(last)
|
| 689 |
+
if prev is not None:
|
| 690 |
+
chg = float(last) - float(prev) if last is not None else None
|
| 691 |
+
chg_pct = (chg / float(prev) * 100) if prev and chg is not None else None
|
| 692 |
+
except Exception:
|
| 693 |
+
price = price or None
|
| 694 |
+
|
| 695 |
+
out.append({
|
| 696 |
+
'title': title,
|
| 697 |
+
'symbol': symbol,
|
| 698 |
+
'price': price,
|
| 699 |
+
'chg': chg,
|
| 700 |
+
'chgPct': chg_pct
|
| 701 |
+
})
|
| 702 |
+
except Exception as e:
|
| 703 |
+
app.logger.exception('getmarketcards: failed for %s', symbol)
|
| 704 |
+
out.append({'title': title, 'symbol': symbol, 'price': None, 'chg': None, 'chgPct': None})
|
| 705 |
+
|
| 706 |
+
return jsonify(out), 200
|
| 707 |
+
|
| 708 |
+
|
| 709 |
+
# ------------------------------------------------------------------------------
|
| 710 |
+
# Global indices (uses yfinance)
|
| 711 |
+
# ------------------------------------------------------------------------------
|
| 712 |
+
@app.get("/getglobalindices")
|
| 713 |
+
def get_global_indices():
|
| 714 |
+
"""
|
| 715 |
+
Returns a flat list of global indices for a set of countries. Each item:
|
| 716 |
+
{ id, name, country, region, price, change, changePct, sparkline }
|
| 717 |
+
"""
|
| 718 |
+
# Define representative indices per country with yfinance symbols
|
| 719 |
+
indices_map = {
|
| 720 |
+
'India': [
|
| 721 |
+
('Nifty 50', '^NSEI'),
|
| 722 |
+
('SENSEX', '^BSESN'),
|
| 723 |
+
('Nifty Bank', '^NSEBANK'),
|
| 724 |
+
('Nifty Midcap 100', None),
|
| 725 |
+
('Nifty Smallcap 100', None),
|
| 726 |
+
],
|
| 727 |
+
'United States': [
|
| 728 |
+
('S&P 500', '^GSPC'),
|
| 729 |
+
('Dow Jones', '^DJI'),
|
| 730 |
+
('Nasdaq Composite', '^IXIC'),
|
| 731 |
+
('S&P MidCap 400', None),
|
| 732 |
+
('S&P SmallCap 600', None),
|
| 733 |
+
],
|
| 734 |
+
'United Kingdom': [
|
| 735 |
+
('FTSE 100', '^FTSE'),
|
| 736 |
+
('FTSE 250', None),
|
| 737 |
+
],
|
| 738 |
+
'Germany': [
|
| 739 |
+
('DAX', '^GDAXI'),
|
| 740 |
+
('MDAX', None),
|
| 741 |
+
],
|
| 742 |
+
'Sweden': [
|
| 743 |
+
('OMX Stockholm 30', '^OMXSPI'),
|
| 744 |
+
],
|
| 745 |
+
'Russia': [
|
| 746 |
+
('MOEX Russia', 'IMOEX.ME'),
|
| 747 |
+
('RTS Index', None),
|
| 748 |
+
]
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
def build_spark_from_hist(close_vals, pts=26):
|
| 752 |
+
# normalize to length pts by sampling or padding
|
| 753 |
+
arr = []
|
| 754 |
+
if not close_vals:
|
| 755 |
+
# synthetic series
|
| 756 |
+
base = 100
|
| 757 |
+
from random import random
|
| 758 |
+
arr = [max(1, base + (random() - 0.5) * base * 0.02) for _ in range(pts)]
|
| 759 |
+
return arr
|
| 760 |
+
vals = list(close_vals)
|
| 761 |
+
# if too short, pad with last
|
| 762 |
+
while len(vals) < pts:
|
| 763 |
+
vals.insert(0, vals[0])
|
| 764 |
+
if len(vals) > pts:
|
| 765 |
+
# sample evenly
|
| 766 |
+
step = len(vals) / pts
|
| 767 |
+
sampled = [vals[int(i * step)] for i in range(pts)]
|
| 768 |
+
return [float(v) for v in sampled]
|
| 769 |
+
return [float(v) for v in vals]
|
| 770 |
+
|
| 771 |
+
out = []
|
| 772 |
+
for country, items in indices_map.items():
|
| 773 |
+
for name, symbol in items:
|
| 774 |
+
price = None
|
| 775 |
+
change = None
|
| 776 |
+
change_pct = None
|
| 777 |
+
spark = []
|
| 778 |
+
try:
|
| 779 |
+
if symbol:
|
| 780 |
+
t = yf.Ticker(symbol)
|
| 781 |
+
hist = None
|
| 782 |
+
try:
|
| 783 |
+
hist = t.history(period='30d')
|
| 784 |
+
except Exception:
|
| 785 |
+
hist = None
|
| 786 |
+
if hist is not None and hasattr(hist, 'empty') and not hist.empty:
|
| 787 |
+
closes = list(hist['Close'].values)
|
| 788 |
+
# create spark from most recent values
|
| 789 |
+
spark = build_spark_from_hist(closes[-26:], pts=26)
|
| 790 |
+
last = float(closes[-1])
|
| 791 |
+
prev = float(closes[-2]) if len(closes) >= 2 else last
|
| 792 |
+
price = last
|
| 793 |
+
change = last - prev
|
| 794 |
+
change_pct = (change / prev * 100) if prev != 0 else 0.0
|
| 795 |
+
else:
|
| 796 |
+
# try single-value info
|
| 797 |
+
info = {}
|
| 798 |
+
try:
|
| 799 |
+
info = t.info or {}
|
| 800 |
+
except Exception:
|
| 801 |
+
info = {}
|
| 802 |
+
last = info.get('regularMarketPrice') or info.get('currentPrice') or info.get('previousClose')
|
| 803 |
+
prev = info.get('previousClose')
|
| 804 |
+
if last is not None:
|
| 805 |
+
price = float(last)
|
| 806 |
+
if price is not None and prev is not None:
|
| 807 |
+
change = float(price) - float(prev)
|
| 808 |
+
change_pct = (change / float(prev) * 100) if prev != 0 else 0.0
|
| 809 |
+
else:
|
| 810 |
+
# no symbol: produce demo values (randomized around a base)
|
| 811 |
+
import random
|
| 812 |
+
base = 10000 if 'Nifty' in name or 'Sensex' in name or 'Nifty' in name else 1000
|
| 813 |
+
price = round(base + (random.random() - 0.5) * base * 0.05, 2)
|
| 814 |
+
change = round((random.random() - 0.5) * price * 0.01, 2)
|
| 815 |
+
change_pct = (change / (price - change) * 100) if price - change != 0 else 0.0
|
| 816 |
+
spark = build_spark_from_hist([price for _ in range(26)], pts=26)
|
| 817 |
+
except Exception as e:
|
| 818 |
+
app.logger.exception('get_global_indices: failed for %s', symbol or name)
|
| 819 |
+
|
| 820 |
+
gid = (name or '').replace('\n', ' ').replace(' ', '_') + '_' + (country.replace(' ', '_') if country else 'unknown')
|
| 821 |
+
out.append({
|
| 822 |
+
'id': gid,
|
| 823 |
+
'name': name,
|
| 824 |
+
'country': country,
|
| 825 |
+
'region': country,
|
| 826 |
+
'price': price,
|
| 827 |
+
'change': change,
|
| 828 |
+
'changePct': change_pct,
|
| 829 |
+
'sparkline': spark
|
| 830 |
+
})
|
| 831 |
+
|
| 832 |
+
return jsonify(out), 200
|
| 833 |
+
|
| 834 |
+
@app.get("/getquotes")
|
| 835 |
+
def get_quotes():
|
| 836 |
+
"""
|
| 837 |
+
Returns live quotes for a list of tickers provided via ?tickers=CSV
|
| 838 |
+
Each returned item: { symbol, price, chg, chgPct, high, low }
|
| 839 |
+
"""
|
| 840 |
+
tickers_csv = (request.args.get("tickers") or "").strip()
|
| 841 |
+
if not tickers_csv:
|
| 842 |
+
return jsonify({"error": "Missing ?tickers=..."}), 400
|
| 843 |
+
|
| 844 |
+
tickers = [t.strip() for t in tickers_csv.split(",") if t.strip()]
|
| 845 |
+
out = []
|
| 846 |
+
for symbol in tickers:
|
| 847 |
+
price = None
|
| 848 |
+
chg = None
|
| 849 |
+
chg_pct = None
|
| 850 |
+
high = None
|
| 851 |
+
low = None
|
| 852 |
+
try:
|
| 853 |
+
t = yf.Ticker(symbol)
|
| 854 |
+
hist = None
|
| 855 |
+
try:
|
| 856 |
+
hist = t.history(period="2d")
|
| 857 |
+
except Exception:
|
| 858 |
+
hist = None
|
| 859 |
+
|
| 860 |
+
if hist is not None and hasattr(hist, 'empty') and not hist.empty:
|
| 861 |
+
closes = list(hist['Close'].values)
|
| 862 |
+
if len(closes) >= 2:
|
| 863 |
+
last = float(closes[-1])
|
| 864 |
+
prev = float(closes[-2])
|
| 865 |
+
else:
|
| 866 |
+
last = float(closes[-1])
|
| 867 |
+
prev = last
|
| 868 |
+
price = last
|
| 869 |
+
chg = last - prev
|
| 870 |
+
chg_pct = (chg / prev * 100) if prev != 0 else 0.0
|
| 871 |
+
|
| 872 |
+
# try to extract intraday high/low from history if available
|
| 873 |
+
try:
|
| 874 |
+
highs = list(hist['High'].values)
|
| 875 |
+
lows = list(hist['Low'].values)
|
| 876 |
+
high = float(highs[-1]) if highs else None
|
| 877 |
+
low = float(lows[-1]) if lows else None
|
| 878 |
+
except Exception:
|
| 879 |
+
high = None
|
| 880 |
+
low = None
|
| 881 |
+
else:
|
| 882 |
+
info = {}
|
| 883 |
+
try:
|
| 884 |
+
info = t.info or {}
|
| 885 |
+
except Exception:
|
| 886 |
+
info = {}
|
| 887 |
+
last = info.get('regularMarketPrice') or info.get('currentPrice') or info.get('previousClose')
|
| 888 |
+
prev = info.get('previousClose')
|
| 889 |
+
if last is not None:
|
| 890 |
+
price = float(last)
|
| 891 |
+
if price is not None and prev is not None:
|
| 892 |
+
chg = float(price) - float(prev)
|
| 893 |
+
chg_pct = (chg / float(prev) * 100) if prev != 0 else 0.0
|
| 894 |
+
try:
|
| 895 |
+
high = info.get('dayHigh') or info.get('regularMarketDayRange')
|
| 896 |
+
low = info.get('dayLow')
|
| 897 |
+
except Exception:
|
| 898 |
+
high = None
|
| 899 |
+
low = None
|
| 900 |
+
except Exception as e:
|
| 901 |
+
app.logger.exception('get_quotes: failed for %s', symbol)
|
| 902 |
+
|
| 903 |
+
out.append({
|
| 904 |
+
'symbol': symbol,
|
| 905 |
+
'price': price,
|
| 906 |
+
'chg': chg,
|
| 907 |
+
'chgPct': chg_pct,
|
| 908 |
+
'high': high,
|
| 909 |
+
'low': low
|
| 910 |
+
})
|
| 911 |
+
|
| 912 |
+
return jsonify(out), 200
|
| 913 |
+
|
| 914 |
+
@app.get("/getintraday")
|
| 915 |
+
def get_intraday():
|
| 916 |
+
"""
|
| 917 |
+
Returns intraday series for a given symbol using yfinance.
|
| 918 |
+
Query params: ?symbol=...&period=1d&interval=1m
|
| 919 |
+
Response: { symbol, timestamps: [iso,...], closes: [num,...] }
|
| 920 |
+
"""
|
| 921 |
+
symbol = (request.args.get('symbol') or '').strip()
|
| 922 |
+
period = (request.args.get('period') or '1d').strip()
|
| 923 |
+
interval = (request.args.get('interval') or '1m').strip()
|
| 924 |
+
if not symbol:
|
| 925 |
+
return jsonify({'error': 'Missing ?symbol parameter'}), 400
|
| 926 |
+
|
| 927 |
+
try:
|
| 928 |
+
t = yf.Ticker(symbol)
|
| 929 |
+
hist = None
|
| 930 |
+
try:
|
| 931 |
+
hist = t.history(period=period, interval=interval)
|
| 932 |
+
except Exception:
|
| 933 |
+
hist = None
|
| 934 |
+
|
| 935 |
+
timestamps = []
|
| 936 |
+
closes = []
|
| 937 |
+
if hist is not None and hasattr(hist, 'empty') and not hist.empty:
|
| 938 |
+
# hist.index may be DatetimeIndex
|
| 939 |
+
for idx, row in hist.iterrows():
|
| 940 |
+
try:
|
| 941 |
+
ts = idx.isoformat()
|
| 942 |
+
except Exception:
|
| 943 |
+
try:
|
| 944 |
+
ts = str(idx)
|
| 945 |
+
except Exception:
|
| 946 |
+
ts = None
|
| 947 |
+
if ts is not None:
|
| 948 |
+
timestamps.append(ts)
|
| 949 |
+
try:
|
| 950 |
+
closes.append(float(row['Close']))
|
| 951 |
+
except Exception:
|
| 952 |
+
closes.append(None)
|
| 953 |
+
else:
|
| 954 |
+
# fallback: attempt to get last close from info
|
| 955 |
+
info = {}
|
| 956 |
+
try:
|
| 957 |
+
info = t.info or {}
|
| 958 |
+
except Exception:
|
| 959 |
+
info = {}
|
| 960 |
+
last = info.get('regularMarketPrice') or info.get('currentPrice') or info.get('previousClose')
|
| 961 |
+
if last is not None:
|
| 962 |
+
# return a tiny synthetic series
|
| 963 |
+
from datetime import datetime, timedelta
|
| 964 |
+
now = datetime.utcnow()
|
| 965 |
+
for i in range(60):
|
| 966 |
+
ts = (now - timedelta(minutes=(59 - i))).isoformat()
|
| 967 |
+
timestamps.append(ts)
|
| 968 |
+
closes.append(float(last))
|
| 969 |
+
|
| 970 |
+
return jsonify({
|
| 971 |
+
'symbol': symbol,
|
| 972 |
+
'timestamps': timestamps,
|
| 973 |
+
'closes': closes
|
| 974 |
+
}), 200
|
| 975 |
+
except Exception as e:
|
| 976 |
+
app.logger.exception('get_intraday failed for %s', symbol)
|
| 977 |
+
return jsonify({'symbol': symbol, 'timestamps': [], 'closes': []}), 200
|
| 978 |
+
|
| 979 |
# ------------------------------------------------------------------------------
|
| 980 |
# Run
|
| 981 |
# ------------------------------------------------------------------------------
|