from app.bars.models import Bar def sma(values: list[float], period: int) -> list[float | None]: if period <= 0: raise ValueError("period must be positive") output: list[float | None] = [None] * len(values) total = 0.0 for index, value in enumerate(values): total += value if index >= period: total -= values[index - period] if index >= period - 1: output[index] = total / period return output def ema(values: list[float], period: int) -> list[float | None]: if period <= 0: raise ValueError("period must be positive") output: list[float | None] = [None] * len(values) if len(values) < period: return output value = sum(values[:period]) / period output[period - 1] = value multiplier = 2 / (period + 1) for index in range(period, len(values)): value = (values[index] - value) * multiplier + value output[index] = value return output def atr(bars: list[Bar], period: int = 14) -> list[float | None]: if period <= 0: raise ValueError("period must be positive") ranges: list[float] = [] for index, bar in enumerate(bars): previous_close = bars[index - 1].c if index else bar.c ranges.append(max(bar.h - bar.l, abs(bar.h - previous_close), abs(bar.l - previous_close))) return sma(ranges, period)