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
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); < 30 oversold
  • 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 / < 20 oversold, more sensitive than RSI
  • 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).