{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Streaming API — bar-by-bar live trading\n",
    "\n",
    "The `ferro_ta.streaming` module provides stateful classes that process\n",
    "data bar-by-bar, suitable for real-time feeds and live trading.\n",
    "\n",
    "Install:\n",
    "```bash\n",
    "pip install ferro-ta\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from ferro_ta.streaming import (\n",
    "    StreamingATR,\n",
    "    StreamingBBands,\n",
    "    StreamingEMA,\n",
    "    StreamingMACD,\n",
    "    StreamingRSI,\n",
    "    StreamingSMA,\n",
    ")\n",
    "\n",
    "# Simulate incoming bars\n",
    "np.random.seed(42)\n",
    "n = 50\n",
    "closes = np.cumprod(1 + np.random.randn(n) * 0.01) * 100\n",
    "highs = closes * 1.005\n",
    "lows = closes * 0.995\n",
    "\n",
    "print(f\"Simulated {n} bars\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## StreamingSMA and StreamingEMA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sma = StreamingSMA(period=5)\n",
    "ema = StreamingEMA(period=5)\n",
    "\n",
    "sma_values = [sma.update(c) for c in closes]\n",
    "ema_values = [ema.update(c) for c in closes]\n",
    "\n",
    "print(\n",
    "    \"SMA last 5:\", [f\"{v:.4f}\" if not np.isnan(v) else \"NaN\" for v in sma_values[-5:]]\n",
    ")\n",
    "print(\n",
    "    \"EMA last 5:\", [f\"{v:.4f}\" if not np.isnan(v) else \"NaN\" for v in ema_values[-5:]]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## StreamingRSI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rsi_stream = StreamingRSI(period=14)\n",
    "\n",
    "rsi_values = [rsi_stream.update(c) for c in closes]\n",
    "finite = [(i, v) for i, v in enumerate(rsi_values) if not np.isnan(v)]\n",
    "print(f\"First valid RSI at bar {finite[0][0]}: {finite[0][1]:.2f}\")\n",
    "print(\"RSI last 3:\", [f\"{v:.2f}\" for _, v in finite[-3:]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## StreamingBBands"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bbands = StreamingBBands(period=20)\n",
    "\n",
    "bb_results = [bbands.update(c) for c in closes]\n",
    "# Each result is (upper, middle, lower) or (nan, nan, nan) during warmup\n",
    "valid_bb = [\n",
    "    (i, u, m, lower) for i, (u, m, lower) in enumerate(bb_results) if not np.isnan(m)\n",
    "]\n",
    "if valid_bb:\n",
    "    i, u, m, lower = valid_bb[-1]\n",
    "    print(f\"Latest Bollinger Bands at bar {i}:\")\n",
    "    print(f\"  Upper:  {u:.4f}\")\n",
    "    print(f\"  Middle: {m:.4f}\")\n",
    "    print(f\"  Lower:  {lower:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## StreamingMACD"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "macd_stream = StreamingMACD(fastperiod=12, slowperiod=26, signalperiod=9)\n",
    "\n",
    "macd_results = [macd_stream.update(c) for c in closes]\n",
    "# Each result is (macd_line, signal, histogram)\n",
    "valid_macd = [\n",
    "    (i, m, s, h) for i, (m, s, h) in enumerate(macd_results) if not np.isnan(m)\n",
    "]\n",
    "if valid_macd:\n",
    "    i, m, s, h = valid_macd[-1]\n",
    "    print(f\"Latest MACD at bar {i}:\")\n",
    "    print(f\"  MACD line:  {m:.6f}\")\n",
    "    print(f\"  Signal:     {s:.6f}\")\n",
    "    print(f\"  Histogram:  {h:.6f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## StreamingATR"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "atr_stream = StreamingATR(period=14)\n",
    "\n",
    "atr_values = [atr_stream.update(h, low, c) for h, low, c in zip(highs, lows, closes)]\n",
    "finite_atr = [v for v in atr_values if not np.isnan(v)]\n",
    "if finite_atr:\n",
    "    print(f\"Latest ATR: {finite_atr[-1]:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reset and reuse\n",
    "\n",
    "All streaming classes support `reset()` to clear internal state and start fresh."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sma.reset()\n",
    "print(\"After reset, SMA(5.0):\", sma.update(5.0))  # NaN — warm-up restarted\n",
    "sma.update(6.0)\n",
    "sma.update(7.0)\n",
    "sma.update(8.0)\n",
    "print(\"SMA after 4 bars:\", sma.update(9.0))  # 7.0 = mean of [5,6,7,8,9]"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
