MYTHOSLIVE / indicators /indicators.py
3VVM's picture
Upload MYTHOSLIVE project from A2 ZIP
9831ced verified
Raw History Blame Contribute Delete
4.9 kB
import numpy as np
import pandas as pd
def sma(series: pd.Series, window: int) -> pd.Series:
return series.rolling(window=window).mean()
def ema(series: pd.Series, window: int) -> pd.Series:
return series.ewm(span=window, adjust=False).mean()
def _wilder_rma(values: pd.Series, window: int) -> pd.Series:
if isinstance(window, bool) or not isinstance(window, (int, np.integer)) or window < 1:
raise ValueError('Wilder period must be a positive integer.')
arr = values.to_numpy(dtype=float)
n = len(arr)
out = np.full(n, np.nan)
seed, prev = [], None
for i, value in enumerate(arr):
if not np.isfinite(value):
seed, prev = [], None
continue
if prev is None:
seed.append(value)
if len(seed) == window:
prev = float(np.mean(seed))
out[i] = prev
else:
prev = (prev * (window - 1) + value) / window
out[i] = prev
return pd.Series(out, index=values.index)
def rsi(series: pd.Series, window: int=14) -> pd.Series:
delta = series.diff()
gain = delta.clip(lower=0)
loss = -delta.clip(upper=0)
avg_gain = _wilder_rma(gain, window)
avg_loss = _wilder_rma(loss, window)
rs = avg_gain / avg_loss.replace(0, np.nan)
out = 100 - 100 / (1 + rs)
out = out.mask((avg_loss == 0) & (avg_gain > 0), 100.0)
return out.mask((avg_loss == 0) & (avg_gain == 0), 50.0)
def macd(series: pd.Series, fast: int=12, slow: int=26, signal: int=9):
fast_ema = ema(series, fast)
slow_ema = ema(series, slow)
macd_line = fast_ema - slow_ema
signal_line = ema(macd_line, signal)
histogram = macd_line - signal_line
return (macd_line, signal_line, histogram)
def bollinger_bands(series: pd.Series, window: int=20, num_std: float=2.0):
mid = sma(series, window)
std = series.rolling(window=window).std(ddof=0)
return (mid + num_std * std, mid, mid - num_std * std)
def stochastic_oscillator(high: pd.Series, low: pd.Series, close: pd.Series, k_window: int=14, d_window: int=3):
lowest_low = low.rolling(window=k_window).min()
highest_high = high.rolling(window=k_window).max()
span = highest_high - lowest_low
percent_k = 100 * (close - lowest_low) / span.replace(0, np.nan)
percent_k = percent_k.mask(span == 0, 50.0)
percent_d = percent_k.rolling(window=d_window).mean()
return (percent_k, percent_d)
def add_all_indicators(df: pd.DataFrame) -> pd.DataFrame:
out = df.copy()
out['SMA_20'] = sma(out['Close'], 20)
out['EMA_20'] = ema(out['Close'], 20)
out['RSI_14'] = rsi(out['Close'], 14)
macd_line, signal_line, hist = macd(out['Close'])
out['MACD'] = macd_line
out['MACD_Signal'] = signal_line
out['MACD_Hist'] = hist
upper, mid, lower = bollinger_bands(out['Close'])
out['BB_Upper'] = upper
out['BB_Mid'] = mid
out['BB_Lower'] = lower
k, d = stochastic_oscillator(out['High'], out['Low'], out['Close'])
out['Stoch_%K'] = k
out['Stoch_%D'] = d
return out
def _round(value, digits):
return round(float(value), digits) if pd.notna(value) else 'n/a'
def latest_signals(df_with_indicators: pd.DataFrame) -> dict:
if df_with_indicators.empty:
raise ValueError('No candles available for latest indicators.')
last = df_with_indicators.iloc[-1]
signals = {}
rsi_value = last['RSI_14']
signals['RSI (14)'] = {'value': _round(rsi_value, 2), 'signal': 'Insufficient data' if pd.isna(rsi_value) else 'Overbought' if rsi_value > 70 else 'Oversold' if rsi_value < 30 else 'Neutral'}
macd_ready = pd.notna(last['MACD']) and pd.notna(last['MACD_Signal'])
signals['MACD'] = {'value': _round(last['MACD'], 5), 'signal': 'Insufficient data' if not macd_ready else 'Bullish (above signal)' if last['MACD'] > last['MACD_Signal'] else 'Bearish (below signal)' if last['MACD'] < last['MACD_Signal'] else 'Neutral'}
bb_ready = pd.notna(last['BB_Upper']) and pd.notna(last['BB_Lower'])
signals['Bollinger Bands'] = {'value': _round(last['Close'], 5), 'signal': 'Insufficient data' if not bb_ready else 'Above Upper Band' if last['Close'] > last['BB_Upper'] else 'Below Lower Band' if last['Close'] < last['BB_Lower'] else 'Inside Bands'}
sma_ready = pd.notna(last['SMA_20'])
signals['Moving Averages'] = {'value': f"SMA20={_round(last['SMA_20'], 5)}, EMA20={_round(last['EMA_20'], 5)}", 'signal': 'Insufficient data for SMA' if not sma_ready else 'Price Above MA (Uptrend)' if last['Close'] > last['SMA_20'] else 'Price Below MA (Downtrend)' if last['Close'] < last['SMA_20'] else 'Neutral'}
stoch_value = last['Stoch_%K']
signals['Stochastic (14,3)'] = {'value': _round(stoch_value, 2), 'signal': 'Insufficient data' if pd.isna(stoch_value) else 'Overbought' if stoch_value > 80 else 'Oversold' if stoch_value < 20 else 'Neutral'}
return signals