> Source: https://ashareapi.com/en/docs/guides/kline-technical-indicators/  ·  Markdown version for LLMs / AI agents

Guide

# Computing technical indicators from K-line data
 With raw K-lines, indicators are easy to compute yourself - no expensive tools needed. This guide uses the free endpooint for forward-adjusted daily bars and writes MA / MACD / RSI / KDJ / BOLL in pure Python + pandas, no TA-Lib.

## 1. Get the data first (free endpoint)
 `/v1/kline` is a **free endpoint (no Key)**. Returns **forward-adjusted** daily/weekly/monthly bars:

 Python: daily bars (forward-adjusted)
```
import requests
import pandas as pd

r = requests.get("https://api.ashareapi.com/v1/kline",
 params={"code": "sh600667", "period": "day", "count": 60},
 timeout=30)
bars = r.json()["data"] # list[dict]
df = pd.DataFrame(bars)
print(df[["date", "open", "last", "high", "low", "volume"]].head())
```

-
 **Forward-adjusted**: adjusted for dividends/splits (no gap on ex-dividend days). To get raw prices, adjust back yourself (not covered here)

-
 **`last` is the close**, not the live price — computing indicators off the close is standard

 |
| | Field | Meaning | Note

| | date | Trade date | Day = that day; week/month = period end date

| | open / last | Open / close | last is the close (not live price)

| | high / low | High / low |
| --- | --- | --- |

| | volume | Volume | unit: lots (×100 = shares)

| | amount | Turnover value | unit: CNY

| | turnover | Turnover rate | %

 date
 Trade date
 Day = that day; week/month = period end date

 open / last
 Open / close
 last is the close (not live price)

 high / low
 High / low

 volume
 Volume
 unit: lots (×100 = shares)

 amount
 Turnover value
 unit: CNY

 turnover
 Turnover rate
 %

## 2. MA (simple moving average, the most basic)
 MA(N) = mean of last N closes. It is the "smoothing filter" for trend and an input to other indicators.

 Python: MA5 / MA10 / MA20 / MA60
```
def ma(close, n):
 return close.rolling(n).mean()

df["ma5"] = ma(df["last"], 5)
df["ma10"] = ma(df["last"], 10)
df["ma20"] = ma(df["last"], 20)
df["ma60"] = ma(df["last"], 60)
print(df[["date", "last", "ma5", "ma20"]].tail())
```

-
 **Golden cross**: MA5 crosses above MA20 (short turns stronger)

-
 **Death cross**: MA5 crosses below MA20 (weaker)

-
 **Bull alignment**: MA5 > MA10 > MA20 (uptrend); **bear alignment** the reverse

## 3. MACD (trend + momentum, most used)
 MACD = difference of fast/slow EMAs (DIF) smoothed again (DEA), standard 12/26/9.

 Python: MACD (12, 26, 9)
```
def macd(close, fast=12, slow=26, sig=9):
 ema_f = close.ewm(span=fast).mean()
 ema_s = close.ewm(span=slow).mean()
 dif = ema_f - ema_s # fast - slow
 dea = dif.ewm(span=sig).mean() # EMA of DIF
 hist = (dif - dea) * 2 # bar (common ×2)
 return dif, dea, hist

df["dif"], df["dea"], df["macd"] = macd(df["last"])
```

-
 **Golden cross**: DIF crosses above DEA (momentum turns positive); **death cross**: below

-
 **Bar**: red growing = bullish momentum, green growing = bearish

-
 **Divergence**: price new high but MACD not (top divergence, warning); price new low but MACD not (bottom divergence)

## 4. RSI (overbought / oversold)
 RSI(N) measures recent up/down strength, used to spot overbought/oversold. Classic 14.

 Python: RSI(14)
```
def rsi(close, n=14):
 delta = close.diff()
 gain = delta.clip(lower=0).rolling(n).mean()
 loss = (-delta.clip(upper=0)).rolling(n).mean()
 rs = gain / loss
 return 100 - 100 / (1 + rs)

df["rsi14"] = rsi(df["last"])
```

-
 **> 70** usually overbought (watch for pullback); **

-
 **Divergence** applies too: price new high but RSI not = top divergence

-
 **Not absolute**: in strong trends RSI can stay overbought a long time (not necessarily a pullback)

## 5. KDJ (stochastic, short-term favorite)
 KDJ uses high/low/close relative position; sensitive but noisy. Classic 9,3,3.

 Python: KDJ(9,3,3)
```
def kdj(high, low, close, n=9):
 low_n = low.rolling(n).min()
 high_n = high.rolling(n).max()
 rsv = (close - low_n) / (high_n - low_n) * 100
 k = rsv.ewm(alpha=1/3).mean()
 d = k.ewm(alpha=1/3).mean()
 j = 3 * k - 2 * d
 return k, d, j

df["k"], df["d"], df["j"] = kdj(df["high"], df["low"], df["last"])
```

-
 **K crosses above D** = golden cross (short-term stronger); below = death cross

-
 **> 80 overbought /

-
 **J value** can exceed 0/100 (extremes), used to detect saturation

## 6. BOLL (Bollinger Bands, volatility range)
 BOLL = mid-band MA20 ± 2×std, describing the "normal range" of price.

 Python: BOLL(20, 2)
```
def boll(close, n=20, k=2):
 mid = close.rolling(n).mean()
 std = close.rolling(n).std()
 upper = mid + k * std
 lower = mid - k * std
 return upper, mid, lower

df["boll_up"], df["boll_mid"], df["boll_low"] = boll(df["last"])
```

-
 **Squeeze** (band narrows) = volatility about to expand (pre-breakout); **expansion** = volatility up

-
 **Touch upper band** = strong (can ride along in a trend); **touch lower band** = weak

-
 **Break below lower then reclaim** = possible oversold bounce; **break above upper** = possible acceleration (but in a strong trend price rides the band, not necessarily falling back)

## 7. Combining the five (more is not better)

-
 **Trend + momentum**: MA (direction) + MACD (momentum) — the most common combination

-
 **Overbought/oversold**: RSI / KDJ (pick one; both short-term, noisy)

-
 **Volatility range**: BOLL (gives MA a "where it should be" band)

-
 **Discipline**: indicators are **confirmation aids**, not standalone buy/sell. Two or more agreeing + your own logic is more reliable (this guide only covers data retrieval and computation, not investment advice)

## 8. Common misreads (important)

-
 **① Adjustment basis**: the API returns forward-adjusted — computing indicators off it is **correct** (keeps continuity). But compare against raw price when reconciling with your broker

-
 **② Parameters are configurable**: MACD(12,26,9) / RSI(14) / KDJ(9,3,3) / BOLL(20,2) are "common", not "only". Changing them = a different indicator, possibly different conclusion

-
 **③ Technical indicators lag**: they are all historical averages/smoothing — they **reflect the past**. In strong trends they can saturate; do not mechanically trade on crosses

## FAQ
 Is this paid?
 No. kline is one of the 5 free endpoints (quote / kline / hot / market-overview / changedist), no Key needed.

 Is the data forward-adjusted?
 Yes, the API always returns forward-adjusted (no gap on ex-dividend days). Computing indicators off forward-adjusted is correct (keeps the series continuous). For raw unadjusted prices, adjust back using dividend/split factors.

 Why not use TA-Lib?
 TA-Lib is powerful but awkward to install (needs compilation). This guide implements 5 common indicators in pure pandas, zero extra dependencies, ideal for teaching and light use. For large backtests, consider TA-Lib for speed.

 Can I get minute-level K-lines?
 No. kline currently offers only daily / weekly / monthly — no minute bars. Intraday data is elsewhere (e.g. orderbook live quote), but no minute K-lines.

 Last updated: 2026-09-29
 Data from live ashareapi /v1/kline responses (measured 2026-09-29: forward-adjusted daily bars with date/open/last/high/low/volume/amount/turnover); indicators implemented with standard algorithms (pandas, no TA-Lib).

 Related

-
[Endpoint reference (kline)](/en/docs/endpoints/kline)

-
[Market breadth via changedist](/en/docs/guides/market-breadth)

-
[Factor screening basics (incl. health check)](/en/docs/guides/factor-screening)

-
[Error codes & rate limits](/en/docs/errors)

 [← All tutorials](/en/docs/guides)
