from flask import Blueprint, render_template, request, jsonify import os import pandas as pd from datetime import datetime, timedelta from vnstock import Vnstock from modules.utils import ( detect_candlestick_patterns, calculate_fibonacci_levels, calculate_money_flow, find_double_top_bottom, detect_w_double_bottom, detect_m_double_top, detect_cup_and_handle, plot_candlestick_with_fibo_patterns, get_financial_valuation, fetch_vietstock_news, analyze_news_with_groq ) home_bp = Blueprint('home', __name__) @home_bp.route('/') def home(): symbol = 'VNINDEX' vnindex_csv = os.path.join(os.path.dirname(__file__), '../vnindex.csv') today = datetime.now().date() start_3y = (today - timedelta(days=3*365)).strftime('%Y-%m-%d') end_today = today.strftime('%Y-%m-%d') if not os.path.exists(vnindex_csv): stock = Vnstock().stock(symbol=symbol, source='VCI') df = stock.quote.history(start=start_3y, end=end_today, interval='1D') df.to_csv(vnindex_csv, index=False) else: df_old = pd.read_csv(vnindex_csv) if 'time' in df_old.columns: last_date = pd.to_datetime(df_old['time']).max().date() else: last_date = today - timedelta(days=3*365) if last_date < today: stock = Vnstock().stock(symbol=symbol, source='VCI') df_new = stock.quote.history(start=(last_date + timedelta(days=1)).strftime('%Y-%m-%d'), end=end_today, interval='1D') if not df_new.empty: df_new = df_new[df_new['time'] > str(last_date)] df_full = pd.concat([df_old, df_new], ignore_index=True) df_full = df_full.drop_duplicates(subset=['time']).sort_values('time') df_full.to_csv(vnindex_csv, index=False) else: df_full = df_old else: df_full = df_old df = df_full candlestick_patterns = detect_candlestick_patterns(df) fibonacci_levels = calculate_fibonacci_levels(df) df = calculate_money_flow(df) double_tops, double_bottoms = find_double_top_bottom(df) w_double_bottoms = detect_w_double_bottom(df) m_double_tops = detect_m_double_top(df) CHART_PATH_CANDLE_VNINDEX = "static/images/vnindex_candle.png" CHART_PATH_MONEY_VNINDEX = "static/images/vnindex_money.png" os.makedirs(os.path.dirname(CHART_PATH_CANDLE_VNINDEX), exist_ok=True) plot_candlestick_with_fibo_patterns( df, fibonacci_levels, candlestick_patterns, symbol, chart_path=CHART_PATH_CANDLE_VNINDEX, double_tops=double_tops, double_bottoms=double_bottoms, cup_handle_patterns=None, w_double_bottoms=w_double_bottoms, m_double_tops=m_double_tops ) # Vẽ biểu đồ dòng tiền import matplotlib.pyplot as plt plt.figure(figsize=(10, 6)) plt.plot(df['time'], df['money_flow'], label='Money Flow') plt.plot(df['time'], df['money_flow_20d_avg'], label='20-Day Avg Money Flow', linestyle='--') plt.xlabel('Time') plt.ylabel('Money Flow') plt.title('Money Flow for VNINDEX') plt.legend() plt.grid() plt.savefig(CHART_PATH_MONEY_VNINDEX) plt.close() cup_handle_patterns = detect_cup_and_handle(df) stock = Vnstock().stock(symbol=symbol, source='VCI') financial_valuation = get_financial_valuation(stock) # Lấy 30 phiên gần nhất df_recent = df.tail(30) # Chuẩn bị dữ liệu bảng cho prompt table_rows = [] for _, row in df_recent.iterrows(): table_rows.append(f"{row['time']}: Giá={row['close']}, KL={row['volume']}, Dòng tiền={row.get('money_flow', '')}, MA20 dòng tiền={row.get('money_flow_20d_avg', '')}") data_table = "\n".join(table_rows) # Tổng hợp marker mẫu hình giá pattern_summary = [] if double_tops: pattern_summary.append(f"Đỉnh: {', '.join([str(x['time']) for x in double_tops])}") if double_bottoms: pattern_summary.append(f"Đáy: {', '.join([str(x['time']) for x in double_bottoms])}") if w_double_bottoms: pattern_summary.append(f"W-Double Bottom: {', '.join([str(x['time']) for x in w_double_bottoms])}") if m_double_tops: pattern_summary.append(f"M-Double Top: {', '.join([str(x['time']) for x in m_double_tops])}") if cup_handle_patterns: pattern_summary.append(f"Cup & Handle: {', '.join([str(x['time']) for x in cup_handle_patterns])}") pattern_text = " | ".join(pattern_summary) if pattern_summary else "Không có mẫu hình đặc biệt." # Tạo prompt động cho Groq AI groq_prompt = ( f"Dưới đây là dữ liệu giao dịch VNINDEX 30 phiên gần nhất (thời gian, giá đóng cửa, khối lượng, dòng tiền, MA20 dòng tiền):\n" f"{data_table}\n\n" f"Các mẫu hình nhận diện: {pattern_text}\n\n" "Hãy phân tích dòng tiền theo phương pháp VSA, nhận diện trạng thái hiện tại (đỉnh, đáy, phân phối, tăng tốc/bứt phá), và dự báo xu hướng ngắn hạn. Trình bày ngắn gọn, súc tích, sát thực tế giao dịch." ) # Gọi Groq AI backend để lấy kết quả phân tích khi load trang try: from modules.utils import client, GROQ_MODEL groq_ai_result = client.chat.completions.create( model=GROQ_MODEL, messages=[{"role": "user", "content": groq_prompt}], max_tokens=600, temperature=0.2 ).choices[0].message.content.strip() except Exception as exc: groq_ai_result = f"[Lỗi AI] {exc}" # Fetch and analyze news news_items = fetch_vietstock_news() news_analysis = analyze_news_with_groq(news_items) # Truyền context vào template return render_template( 'home.html', symbol=symbol, chart_path_candle_vnindex=CHART_PATH_CANDLE_VNINDEX, chart_path_money_vnindex=CHART_PATH_MONEY_VNINDEX, vnindex_fibonacci_levels=fibonacci_levels, vnindex_pattern_results=candlestick_patterns, vnindex_double_tops=double_tops, vnindex_double_bottoms=double_bottoms, vnindex_cup_handle_patterns=cup_handle_patterns, financial_valuation=financial_valuation, vnindex_w_double_bottoms=w_double_bottoms, vnindex_m_double_tops=m_double_tops, groq_prompt=groq_prompt, groq_ai_result=groq_ai_result, news_items=news_items, news_analysis=news_analysis, ) @home_bp.route('/api/groq_analyze', methods=['POST']) def groq_analyze(): from flask import jsonify data = request.get_json() prompt = data.get('prompt', '') question = data['question'] if 'question' in data else '' full_prompt = prompt if question: full_prompt += f"\n\nCâu hỏi thêm của người dùng: {question}" try: from modules.utils import client, GROQ_MODEL result = client.chat.completions.create( model=GROQ_MODEL, messages=[{"role": "user", "content": full_prompt}], max_tokens=600, temperature=0.2 ).choices[0].message.content.strip() except Exception as exc: result = "[Lỗi AI] " + str(exc) return jsonify({"result": result})