from flask import Flask, request, jsonify import yfinance as yf import pandas as pd import numpy as np import talib import datetime # Bollinger Band Calculation def calculate_bollinger(data, period=20, stddev=2): close = data['close'] upper, middle, lower = talib.BBANDS(close, timeperiod=period, nbdevup=stddev, nbdevdn=stddev, matype=0) return upper, middle, lower #BB Squeeze breakout/fade after low volatility def detect_bb_squeeze(close, upper, lower, middle, lookback=20, perc=20): bandwidth = (upper - lower) / middle # 20th percentile over the lookback window thresh = np.percentile(bandwidth.iloc[-lookback:], perc) if bandwidth.iloc[-1] < thresh: return "Neutral" # otherwise fall back to a breakout rule if close.iloc[-1] > upper.iloc[-1]: return "Bullish" elif close.iloc[-1] < lower.iloc[-1]: return "Bearish" return "Neutral" # BB Breakout Detection def detect_bb_breakout(close, upper, lower): if close.iloc[-1] > upper.iloc[-1]: return "Bullish" elif close.iloc[-1] < lower.iloc[-1]: return "Bearish" return "Neutral" # BB Breakout Reversal def detect_bb_breakout_reversal(data, upper, lower, middle, lookahead=3): i = len(data) - lookahead - 1 if i < 0: return "Neutral" row = data.iloc[i] # Bullish Reversal if row['close'] > upper.iloc[i]: for j in range(1, lookahead + 1): next_row = data.iloc[i + j] if next_row['close'] < upper.iloc[i + j] and next_row['close'] > middle.iloc[i + j]: return "Bullish" # Bearish Reversal elif row['close'] < lower.iloc[i]: for j in range(1, lookahead + 1): next_row = data.iloc[i + j] if next_row['close'] > lower.iloc[i + j] and next_row['close'] < middle.iloc[i + j]: return "Bearish" return "Neutral" # Middle Band Pullback def detect_middle_band_pullback(close, middle, upper, lower, threshold=0.10, trend_lookback=3): band_width = upper.iloc[-1] - lower.iloc[-1] if abs(close.iloc[-1] - middle.iloc[-1]) < band_width * threshold: trend_above = all(close.iloc[-i] > middle.iloc[-i] for i in range(2, 2 + trend_lookback)) trend_below = all(close.iloc[-i] < middle.iloc[-i] for i in range(2, 2 + trend_lookback)) if trend_above: return "Bullish" elif trend_below: return "Bearish" return "Neutral" # Master strategy function def bollinger_strategies(data): upper, middle, lower = calculate_bollinger(data) signals = { "UpperBand": round(upper.iloc[-1], 2), "MiddleBand": round(middle.iloc[-1], 2), "LowerBand": round(lower.iloc[-1], 2), "BB Squeeze": detect_bb_squeeze(data['close'], upper, lower, middle), "BB Breakout": detect_bb_breakout(data['close'], upper, lower), "BB Breakout Reversal": detect_bb_breakout_reversal(data, upper, lower, middle), "Middle Band Pullback": detect_middle_band_pullback(data['close'], middle, upper, lower) } weights = { "BB Squeeze": 30, "BB Breakout": 25, "BB Breakout Reversal": 25, "Middle Band Pullback": 20 } total_score = 0 for strategy, weight in weights.items(): signal = signals[strategy] if "Bullish" in signal or "Breakout Up" in signal or "Squeeze" in signal or "Pullback" in signal: total_score += weight elif "Neutral" in signal or "No Breakout" in signal: total_score += weight * 0.5 overall_percentage = round((total_score / sum(weights.values())) * 100, 2) if overall_percentage >= 60: final_signal = "Buy" elif overall_percentage <= 40: final_signal = "DBuy" else: final_signal = "Neutral" return signals, overall_percentage, final_signal # API-style function def get_bollinger_trade_signal(data): bb_signals, overall_score, final_signal = bollinger_strategies(data) return { "bollinger_signals": bb_signals, "bollinger_score": overall_score, "bollinger_final_signal": final_signal }