OpenAlgo Indicator Expert Skill
Environment
- Python 3.12+ (required by openalgo 2.x) with openalgo, pandas, numpy, plotly, dash, streamlit
- Data sources: OpenAlgo (Indian markets via
client.history(), client.quotes(), client.depth()), yfinance (US/Global)
- Real-time: OpenAlgo WebSocket (
client.connect(), subscribe_ltp, subscribe_quote, subscribe_depth)
- Indicators: openalgo.ta (ALWAYS — 100+ indicators computed by a compiled Rust core, full speed from the first call)
- Charts: Plotly with
template="plotly_dark"
- Dashboards: Plotly Dash with
dash-bootstrap-components (default) OR Streamlit with st.plotly_chart() — use Streamlit only when the user explicitly asks for it
- Custom indicators: vectorized NumPy composed from openalgo.ta primitives (Rust core — no JIT, no warmup)
- API keys loaded from single root
.env via python-dotenv + find_dotenv() — never hardcode keys
- Scripts go in appropriate directories (charts/, dashboards/, custom_indicators/, scanners/) created on-demand
- Never use icons/emojis in code or logger output
Critical Rules
- ALWAYS use openalgo.ta for ALL technical indicators. Never reimplement what already exists in the library.
- Data normalization: Always convert DataFrame index to datetime, sort, and strip timezone after fetching.
- Signal cleaning: Always use
ta.exrem() after generating raw buy/sell signals. Always .fillna(False) before exrem.
- Plotly dark theme: All charts use
template="plotly_dark" with xaxis type="category" for candlesticks.
- Custom indicators: Compose from openalgo.ta primitives (
ta.sma, ta.stdev, ta.bbands, ...) plus vectorized NumPy. Never reimplement built-ins; no JIT or warmup is needed.
- Input flexibility: openalgo.ta accepts numpy arrays, pandas Series, or lists. Output matches input type.
- WebSocket feeds: Use
client.connect(), client.subscribe_ltp() / subscribe_quote() / subscribe_depth() for real-time data.
- Environment: Load
.env from project root via find_dotenv() — never hardcode API keys.
- Market detection: If symbol looks Indian (SBIN, RELIANCE, NIFTY), use OpenAlgo. If US (AAPL, MSFT), use yfinance.
- Always explain chart outputs in plain language so traders understand what the indicator shows.
Data Source Priority
| Market |
Data Source |
Method |
Example Symbols |
| India (equity) |
OpenAlgo |
client.history() |
SBIN, RELIANCE, INFY |
| India (index) |
OpenAlgo |
client.history(exchange="NSE_INDEX") |
NIFTY, BANKNIFTY |
| India (F&O) |
OpenAlgo |
client.history(exchange="NFO") |
NIFTY30DEC25FUT |
| US/Global |
yfinance |
yf.download() |
AAPL, MSFT, SPY |
OpenAlgo API Methods for Data
| Method |
Purpose |
Returns |
client.history(symbol, exchange, interval, start_date, end_date) |
OHLCV candles |
DataFrame (timestamp, open, high, low, close, volume) |
client.quotes(symbol, exchange) |
Real-time snapshot |
Dict (open, high, low, ltp, bid, ask, prev_close, volume) |
client.multiquotes(symbols=[...]) |
Multi-symbol quotes |
List of quote dicts |
client.depth(symbol, exchange) |
Market depth (L5) |
Dict (bids, asks, ohlc, volume, oi) |
client.intervals() |
Available intervals |
Dict (minutes, hours, days, weeks, months) |
client.optionchain(underlying, exchange, expiry_date, strike_count) |
Option chain around ATM |
Dict (underlying_ltp, atm_strike, chain with ce/pe per strike) |
client.optiongreeks(symbol, exchange, interest_rate, ...) |
Option greeks + IV |
Dict (greeks: delta/gamma/theta/vega/rho, implied_volatility, days_to_expiry) |
client.expiry(symbol, exchange, instrumenttype) |
Expiry dates list |
Dict (data: list of expiry dates) |
client.connect() |
WebSocket connect |
None (sets up WS connection) |
client.subscribe_ltp(instruments, callback) |
Live LTP stream |
Callback with {symbol, exchange, ltp} |
client.subscribe_quote(instruments, callback) |
Live quote stream |
Callback with {symbol, exchange, ohlc, ltp, volume} |
client.subscribe_depth(instruments, callback) |
Live depth stream |
Callback with {symbol, exchange, bids, asks} |
Indicator Library Reference
All indicators accessed via from openalgo import ta:
Trend (20)
ta.sma, ta.ema, ta.wma, ta.dema, ta.tema, ta.hma, ta.vwma, ta.alma, ta.kama, ta.zlema, ta.t3, ta.frama, ta.supertrend, ta.ichimoku, ta.chande_kroll_stop, ta.trima, ta.mcginley, ta.vidya, ta.alligator, ta.ma_envelopes
Momentum (9)
ta.rsi, ta.macd, ta.stochastic, ta.cci, ta.williams_r, ta.bop, ta.elder_ray, ta.fisher, ta.crsi
Volatility (16)
ta.atr, ta.bbands, ta.keltner, ta.donchian, ta.chaikin_volatility, ta.natr, ta.rvi, ta.ultimate_oscillator, ta.true_range, ta.massindex, ta.bb_percent, ta.bb_width, ta.chandelier_exit, ta.historical_volatility, ta.ulcer_index, ta.starc
Volume (15)
ta.obv, ta.obv_smoothed, ta.vwap, ta.mfi, ta.adl, ta.cmf, ta.emv, ta.force_index, ta.nvi, ta.pvi, ta.volosc, ta.vroc, ta.kvo, ta.pvt, ta.rvol
Oscillators (20+)
ta.cmo, ta.trix, ta.uo_oscillator, ta.awesome_oscillator, ta.accelerator_oscillator, ta.ppo, ta.po, ta.dpo, ta.aroon_oscillator, ta.stoch_rsi, ta.rvi_oscillator, ta.cho, ta.chop, ta.kst, ta.tsi, ta.vortex, ta.gator_oscillator, ta.stc, ta.coppock, ta.roc
Statistical (9)
ta.linreg, ta.lrslope, ta.correlation, ta.beta, ta.variance, ta.tsf, ta.median, ta.mode, ta.median_bands
Hybrid (6+)
ta.adx, ta.dmi, ta.aroon, ta.pivot_points, ta.sar, ta.williams_fractals, ta.rwi
TA-Lib Compatible (18, new in openalgo 2.0)
ta.mom, ta.rocp, ta.rocr, ta.rocr100, ta.apo, ta.midpoint, ta.midprice, ta.avgprice, ta.medprice, ta.typprice, ta.wclprice, ta.plus_dm, ta.minus_dm, ta.dx, ta.adxr, ta.stochf, ta.linregangle, ta.linregintercept
Utilities
ta.crossover, ta.crossunder, ta.cross, ta.highest, ta.lowest, ta.change, ta.roc, ta.stdev, ta.exrem, ta.flip, ta.valuewhen, ta.rising, ta.falling
Modular Rule Files
Detailed reference for each topic is in rules/:
| Rule File |
Topic |
| indicator-catalog |
Complete 100+ indicator reference with signatures and parameters |
| data-fetching |
OpenAlgo history/quotes/depth, yfinance, data normalization |
| plotting |
Plotly candlestick, overlay, subplot, multi-panel charts |
| custom-indicators |
Building custom indicators with vectorized NumPy + ta primitives |
| websocket-feeds |
Real-time LTP/Quote/Depth streaming via WebSocket |
| performance |
Rust core performance, O(n) guarantees, benchmarking |
| dashboard-patterns |
Plotly Dash web applications with callbacks |
| streamlit-patterns |
Streamlit web applications with sidebar, metrics, plotly charts |
| multi-timeframe |
Multi-timeframe indicator analysis |
| signal-generation |
Signal generation, cleaning, crossover/crossunder |
| indicator-combinations |
Combining indicators for confluence analysis |
| symbol-format |
OpenAlgo symbol format, exchange codes, index symbols |
Chart Templates (in rules/assets/)
| Template |
Path |
Description |
| EMA Chart |
assets/ema_chart/chart.py |
EMA overlay on candlestick |
| RSI Chart |
assets/rsi_chart/chart.py |
RSI with overbought/oversold zones |
| MACD Chart |
assets/macd_chart/chart.py |
MACD line, signal, histogram |
| Supertrend |
assets/supertrend_chart/chart.py |
Supertrend overlay with direction coloring |
| Bollinger |
assets/bollinger_chart/chart.py |
Bollinger Bands with squeeze detection |
| Multi-Indicator |
assets/multi_indicator/chart.py |
Candlestick + EMA + RSI + MACD + Volume |
| Basic Dashboard |
assets/dashboard_basic/app.py |
Single-symbol Plotly Dash app |
| Multi Dashboard |
assets/dashboard_multi/app.py |
Multi-symbol multi-timeframe dashboard |
| Streamlit Basic |
assets/streamlit_basic/app.py |
Single-symbol Streamlit app |
| Streamlit Multi |
assets/streamlit_multi/app.py |
Multi-timeframe Streamlit app |
| Custom Indicator |
assets/custom_indicator/template.py |
NumPy custom indicator template (composes ta primitives) |
| Live Feed |
assets/live_feed/template.py |
WebSocket real-time indicator |
| Scanner |
assets/scanner/template.py |
Multi-symbol indicator scanner |
Quick Template: Standard Indicator Chart Script
import os
from datetime import datetime, timedelta
from pathlib import Path
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from dotenv import find_dotenv, load_dotenv
from openalgo import api, ta
# --- Config ---
script_dir = Path(__file__).resolve().parent
load_dotenv(find_dotenv(), override=False)
SYMBOL = "SBIN"
EXCHANGE = "NSE"
INTERVAL = "D"
# --- Fetch Data ---
client = api(
api_key=os.getenv("OPENALGO_API_KEY"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
end_date = datetime.now().date()
start_date = end_date - timedelta(days=365)
df = client.history(
symbol=SYMBOL, exchange=EXCHANGE, interval=INTERVAL,
start_date=start_date.strftime("%Y-%m-%d"),
end_date=end_date.strftime("%Y-%m-%d"),
)
if "timestamp" in df.columns:
df["timestamp"] = pd.to_datetime(df["timestamp"])
df = df.set_index("timestamp")
else:
df.index = pd.to_datetime(df.index)
df = df.sort_index()
if df.index.tz is not None:
df.index = df.index.tz_convert(None)
close = df["close"]
high = df["high"]
low = df["low"]
volume = df["volume"]
# --- Compute Indicators ---
ema_20 = ta.ema(close, 20)
rsi_14 = ta.rsi(close, 14)
# --- Chart ---
fig = make_subplots(
rows=2, cols=1, shared_xaxes=True,
row_heights=[0.7, 0.3], vertical_spacing=0.03,
subplot_titles=[f"{SYMBOL} Price + EMA(20)", "RSI(14)"],
)
# Candlestick
x_labels = df.index.strftime("%Y-%m-%d")
fig.add_trace(go.Candlestick(
x=x_labels, open=df["open"], high=high, low=low, close=close,
name="Price",
), row=1, col=1)
# EMA overlay
fig.add_trace(go.Scatter(
x=x_labels, y=ema_20, mode="lines",
name="EMA(20)", line=dict(color="cyan", width=1.5),
), row=1, col=1)
# RSI subplot
fig.add_trace(go.Scatter(
x=x_labels, y=rsi_14, mode="lines",
name="RSI(14)", line=dict(color="yellow", width=1.5),
), row=2, col=1)
fig.add_hline(y=70, line_dash="dash", line_color="red", row=2, col=1)
fig.add_hline(y=30, line_dash="dash", line_color="green", row=2, col=1)
fig.update_layout(
template="plotly_dark", title=f"{SYMBOL} Technical Analysis",
xaxis_rangeslider_visible=False, xaxis_type="category",
xaxis2_type="category", height=700,
)
fig.show()
1---2name: indicator-expert3description: OpenAlgo indicator expert. Use when user asks about technical indicators, charting, plotting indicators, creating custom indicators, building dashboards, real-time feeds, scanning stocks, indicator combinations, or using openalgo.ta. Also triggers for indicator functions (sma, ema, rsi, macd, supertrend, bollinger, atr, adx, ichimoku, stochastic, obv, vwap, crossover, crossunder, exrem).4---5
6# OpenAlgo Indicator Expert Skill
7
8## Environment
9
10- Python 3.12+ (required by openalgo 2.x) with openalgo, pandas, numpy, plotly, dash, streamlit
11- Data sources: OpenAlgo (Indian markets via `client.history()`, `client.quotes()`, `client.depth()`), yfinance (US/Global)
12- Real-time: OpenAlgo WebSocket (`client.connect()`, `subscribe_ltp`, `subscribe_quote`, `subscribe_depth`)
13- Indicators: **openalgo.ta** (ALWAYS — 100+ indicators computed by a compiled Rust core, full speed from the first call)
14- Charts: Plotly with `template="plotly_dark"`
15- Dashboards: Plotly Dash with `dash-bootstrap-components` (default) OR Streamlit with `st.plotly_chart()` — use Streamlit only when the user explicitly asks for it
16- Custom indicators: vectorized NumPy composed from openalgo.ta primitives (Rust core — no JIT, no warmup)
17- API keys loaded from single root `.env` via `python-dotenv` + `find_dotenv()` — never hardcode keys
18- Scripts go in appropriate directories (charts/, dashboards/, custom_indicators/, scanners/) created on-demand
19- Never use icons/emojis in code or logger output
20
21## Critical Rules
22
231. **ALWAYS use openalgo.ta** for ALL technical indicators. Never reimplement what already exists in the library.
242. **Data normalization**: Always convert DataFrame index to datetime, sort, and strip timezone after fetching.
253. **Signal cleaning**: Always use `ta.exrem()` after generating raw buy/sell signals. Always `.fillna(False)` before exrem.
264. **Plotly dark theme**: All charts use `template="plotly_dark"` with `xaxis type="category"` for candlesticks.
275. **Custom indicators**: Compose from openalgo.ta primitives (`ta.sma`, `ta.stdev`, `ta.bbands`, ...) plus vectorized NumPy. Never reimplement built-ins; no JIT or warmup is needed.
286. **Input flexibility**: openalgo.ta accepts numpy arrays, pandas Series, or lists. Output matches input type.
297. **WebSocket feeds**: Use `client.connect()`, `client.subscribe_ltp()` / `subscribe_quote()` / `subscribe_depth()` for real-time data.
308. **Environment**: Load `.env` from project root via `find_dotenv()` — never hardcode API keys.
319. **Market detection**: If symbol looks Indian (SBIN, RELIANCE, NIFTY), use OpenAlgo. If US (AAPL, MSFT), use yfinance.
3210. **Always explain** chart outputs in plain language so traders understand what the indicator shows.
33
34## Data Source Priority
35
36| Market | Data Source | Method | Example Symbols |
37|--------|------------|--------|-----------------|
38| India (equity) | OpenAlgo | `client.history()` | SBIN, RELIANCE, INFY |
39| India (index) | OpenAlgo | `client.history(exchange="NSE_INDEX")` | NIFTY, BANKNIFTY |
40| India (F&O) | OpenAlgo | `client.history(exchange="NFO")` | NIFTY30DEC25FUT |
41| US/Global | yfinance | `yf.download()` | AAPL, MSFT, SPY |
42
43## OpenAlgo API Methods for Data
44
45| Method | Purpose | Returns |
46|--------|---------|---------|
47| `client.history(symbol, exchange, interval, start_date, end_date)` | OHLCV candles | DataFrame (timestamp, open, high, low, close, volume) |
48| `client.quotes(symbol, exchange)` | Real-time snapshot | Dict (open, high, low, ltp, bid, ask, prev_close, volume) |
49| `client.multiquotes(symbols=[...])` | Multi-symbol quotes | List of quote dicts |
50| `client.depth(symbol, exchange)` | Market depth (L5) | Dict (bids, asks, ohlc, volume, oi) |
51| `client.intervals()` | Available intervals | Dict (minutes, hours, days, weeks, months) |
52| `client.optionchain(underlying, exchange, expiry_date, strike_count)` | Option chain around ATM | Dict (underlying_ltp, atm_strike, chain with ce/pe per strike) |
53| `client.optiongreeks(symbol, exchange, interest_rate, ...)` | Option greeks + IV | Dict (greeks: delta/gamma/theta/vega/rho, implied_volatility, days_to_expiry) |
54| `client.expiry(symbol, exchange, instrumenttype)` | Expiry dates list | Dict (data: list of expiry dates) |
55| `client.connect()` | WebSocket connect | None (sets up WS connection) |
56| `client.subscribe_ltp(instruments, callback)` | Live LTP stream | Callback with `{symbol, exchange, ltp}` |
57| `client.subscribe_quote(instruments, callback)` | Live quote stream | Callback with `{symbol, exchange, ohlc, ltp, volume}` |
58| `client.subscribe_depth(instruments, callback)` | Live depth stream | Callback with `{symbol, exchange, bids, asks}` |
59
60## Indicator Library Reference
61
62All indicators accessed via `from openalgo import ta`:
63
64### Trend (20)
65`ta.sma`, `ta.ema`, `ta.wma`, `ta.dema`, `ta.tema`, `ta.hma`, `ta.vwma`, `ta.alma`, `ta.kama`, `ta.zlema`, `ta.t3`, `ta.frama`, `ta.supertrend`, `ta.ichimoku`, `ta.chande_kroll_stop`, `ta.trima`, `ta.mcginley`, `ta.vidya`, `ta.alligator`, `ta.ma_envelopes`
66
67### Momentum (9)
68`ta.rsi`, `ta.macd`, `ta.stochastic`, `ta.cci`, `ta.williams_r`, `ta.bop`, `ta.elder_ray`, `ta.fisher`, `ta.crsi`
69
70### Volatility (16)
71`ta.atr`, `ta.bbands`, `ta.keltner`, `ta.donchian`, `ta.chaikin_volatility`, `ta.natr`, `ta.rvi`, `ta.ultimate_oscillator`, `ta.true_range`, `ta.massindex`, `ta.bb_percent`, `ta.bb_width`, `ta.chandelier_exit`, `ta.historical_volatility`, `ta.ulcer_index`, `ta.starc`
72
73### Volume (15)
74`ta.obv`, `ta.obv_smoothed`, `ta.vwap`, `ta.mfi`, `ta.adl`, `ta.cmf`, `ta.emv`, `ta.force_index`, `ta.nvi`, `ta.pvi`, `ta.volosc`, `ta.vroc`, `ta.kvo`, `ta.pvt`, `ta.rvol`
75
76### Oscillators (20+)
77`ta.cmo`, `ta.trix`, `ta.uo_oscillator`, `ta.awesome_oscillator`, `ta.accelerator_oscillator`, `ta.ppo`, `ta.po`, `ta.dpo`, `ta.aroon_oscillator`, `ta.stoch_rsi`, `ta.rvi_oscillator`, `ta.cho`, `ta.chop`, `ta.kst`, `ta.tsi`, `ta.vortex`, `ta.gator_oscillator`, `ta.stc`, `ta.coppock`, `ta.roc`
78
79### Statistical (9)
80`ta.linreg`, `ta.lrslope`, `ta.correlation`, `ta.beta`, `ta.variance`, `ta.tsf`, `ta.median`, `ta.mode`, `ta.median_bands`
81
82### Hybrid (6+)
83`ta.adx`, `ta.dmi`, `ta.aroon`, `ta.pivot_points`, `ta.sar`, `ta.williams_fractals`, `ta.rwi`
84
85### TA-Lib Compatible (18, new in openalgo 2.0)
86`ta.mom`, `ta.rocp`, `ta.rocr`, `ta.rocr100`, `ta.apo`, `ta.midpoint`, `ta.midprice`, `ta.avgprice`, `ta.medprice`, `ta.typprice`, `ta.wclprice`, `ta.plus_dm`, `ta.minus_dm`, `ta.dx`, `ta.adxr`, `ta.stochf`, `ta.linregangle`, `ta.linregintercept`
87
88### Utilities
89`ta.crossover`, `ta.crossunder`, `ta.cross`, `ta.highest`, `ta.lowest`, `ta.change`, `ta.roc`, `ta.stdev`, `ta.exrem`, `ta.flip`, `ta.valuewhen`, `ta.rising`, `ta.falling`
90
91## Modular Rule Files
92
93Detailed reference for each topic is in `rules/`:
94
95| Rule File | Topic |
96|-----------|-------|
97| [indicator-catalog](rules/indicator-catalog.md) | Complete 100+ indicator reference with signatures and parameters |
98| [data-fetching](rules/data-fetching.md) | OpenAlgo history/quotes/depth, yfinance, data normalization |
99| [plotting](rules/plotting.md) | Plotly candlestick, overlay, subplot, multi-panel charts |
100| [custom-indicators](rules/custom-indicators.md) | Building custom indicators with vectorized NumPy + ta primitives |
101| [websocket-feeds](rules/websocket-feeds.md) | Real-time LTP/Quote/Depth streaming via WebSocket |
102| [performance](rules/performance.md) | Rust core performance, O(n) guarantees, benchmarking |
103| [dashboard-patterns](rules/dashboard-patterns.md) | Plotly Dash web applications with callbacks |
104| [streamlit-patterns](rules/streamlit-patterns.md) | Streamlit web applications with sidebar, metrics, plotly charts |
105| [multi-timeframe](rules/multi-timeframe.md) | Multi-timeframe indicator analysis |
106| [signal-generation](rules/signal-generation.md) | Signal generation, cleaning, crossover/crossunder |
107| [indicator-combinations](rules/indicator-combinations.md) | Combining indicators for confluence analysis |
108| [symbol-format](rules/symbol-format.md) | OpenAlgo symbol format, exchange codes, index symbols |
109
110## Chart Templates (in rules/assets/)
111
112| Template | Path | Description |
113|----------|------|-------------|
114| EMA Chart | `assets/ema_chart/chart.py` | EMA overlay on candlestick |
115| RSI Chart | `assets/rsi_chart/chart.py` | RSI with overbought/oversold zones |
116| MACD Chart | `assets/macd_chart/chart.py` | MACD line, signal, histogram |
117| Supertrend | `assets/supertrend_chart/chart.py` | Supertrend overlay with direction coloring |
118| Bollinger | `assets/bollinger_chart/chart.py` | Bollinger Bands with squeeze detection |
119| Multi-Indicator | `assets/multi_indicator/chart.py` | Candlestick + EMA + RSI + MACD + Volume |
120| Basic Dashboard | `assets/dashboard_basic/app.py` | Single-symbol Plotly Dash app |
121| Multi Dashboard | `assets/dashboard_multi/app.py` | Multi-symbol multi-timeframe dashboard |
122| Streamlit Basic | `assets/streamlit_basic/app.py` | Single-symbol Streamlit app |
123| Streamlit Multi | `assets/streamlit_multi/app.py` | Multi-timeframe Streamlit app |
124| Custom Indicator | `assets/custom_indicator/template.py` | NumPy custom indicator template (composes ta primitives) |
125| Live Feed | `assets/live_feed/template.py` | WebSocket real-time indicator |
126| Scanner | `assets/scanner/template.py` | Multi-symbol indicator scanner |
127
128## Quick Template: Standard Indicator Chart Script
129
130```python
131import os
132from datetime import datetime, timedelta
133from pathlib import Path
134
135import numpy as np
136import pandas as pd
137import plotly.graph_objects as go
138from plotly.subplots import make_subplots
139from dotenv import find_dotenv, load_dotenv
140from openalgo import api, ta
141
142# --- Config ---
143script_dir = Path(__file__).resolve().parent
144load_dotenv(find_dotenv(), override=False)
145
146SYMBOL = "SBIN"
147EXCHANGE = "NSE"
148INTERVAL = "D"
149
150# --- Fetch Data ---
151client = api(
152 api_key=os.getenv("OPENALGO_API_KEY"),
153 host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
154)
155
156end_date = datetime.now().date()
157start_date = end_date - timedelta(days=365)
158
159df = client.history(
160 symbol=SYMBOL, exchange=EXCHANGE, interval=INTERVAL,
161 start_date=start_date.strftime("%Y-%m-%d"),
162 end_date=end_date.strftime("%Y-%m-%d"),
163)
164if "timestamp" in df.columns:
165 df["timestamp"] = pd.to_datetime(df["timestamp"])
166 df = df.set_index("timestamp")
167else:
168 df.index = pd.to_datetime(df.index)
169df = df.sort_index()
170if df.index.tz is not None:
171 df.index = df.index.tz_convert(None)
172
173close = df["close"]
174high = df["high"]
175low = df["low"]
176volume = df["volume"]
177
178# --- Compute Indicators ---
179ema_20 = ta.ema(close, 20)
180rsi_14 = ta.rsi(close, 14)
181
182# --- Chart ---
183fig = make_subplots(
184 rows=2, cols=1, shared_xaxes=True,
185 row_heights=[0.7, 0.3], vertical_spacing=0.03,
186 subplot_titles=[f"{SYMBOL} Price + EMA(20)", "RSI(14)"],
187)
188
189# Candlestick
190x_labels = df.index.strftime("%Y-%m-%d")
191fig.add_trace(go.Candlestick(
192 x=x_labels, open=df["open"], high=high, low=low, close=close,
193 name="Price",
194), row=1, col=1)
195
196# EMA overlay
197fig.add_trace(go.Scatter(
198 x=x_labels, y=ema_20, mode="lines",
199 name="EMA(20)", line=dict(color="cyan", width=1.5),
200), row=1, col=1)
201
202# RSI subplot
203fig.add_trace(go.Scatter(
204 x=x_labels, y=rsi_14, mode="lines",
205 name="RSI(14)", line=dict(color="yellow", width=1.5),
206), row=2, col=1)
207fig.add_hline(y=70, line_dash="dash", line_color="red", row=2, col=1)
208fig.add_hline(y=30, line_dash="dash", line_color="green", row=2, col=1)
209
210fig.update_layout(
211 template="plotly_dark", title=f"{SYMBOL} Technical Analysis",
212 xaxis_rangeslider_visible=False, xaxis_type="category",
213 xaxis2_type="category", height=700,
214)
215fig.show()
216```