
Volatility
- 1 installs
- 29.6k repo stars
- Updated August 4, 2026
- hkuds/vibe-trading
Trade volatility mean reversion by percentile-ranking historical volatility, going long in low-vol regimes and exiting or shorting in high-vol regimes on any OHLCV data.
About
Implements a volatility mean-reversion strategy using percentile ranking of historical volatility to time entries and exits. A developer uses it to build positions in low-volatility regimes and reduce them when volatility is high.
- Percentile-ranked historical volatility drives long/exit signals
- Configurable HV window, lookback, and annualization for crypto or equities
Volatility by the numbers
- 1 all-time installs (skills.sh)
- Ranked #909 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hkuds/vibe-trading --skill volatilityAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 29.6k |
| Last updated | August 4, 2026 |
| Repository | hkuds/vibe-trading ↗ |
What it does
Trade volatility mean reversion by percentile-ranking historical volatility, going long in low-vol regimes and exiting or shorting in high-vol regimes on any OHLCV data.
Files
Volatility Strategy
Purpose
Uses percentile ranking of historical volatility (HV) to capture volatility mean reversion: build positions in low-volatility regimes while waiting for volatility expansion, and exit or short in high-volatility regimes to capture contraction.
Signal Logic
1. Compute HV: annualized standard deviation of returns over the past hv_window days 2. Percentile ranking: percentile position of HV within the past lookback days (0-100) 3. Signal generation:
- Percentile <
low_pct→ go long (volatility is low, waiting for expansion) - Percentile >
high_pct→ exit / go short (volatility is high, waiting for contraction) - Middle region → keep the current position
Key Implementation Details
- HV =
returns.rolling(hv_window).std() * sqrt(252)(annualized) - Percentile =
hv.rolling(lookback).rank(pct=True) * 100 - For cryptocurrencies, use 365 instead of 252 as the annualization factor
Parameters
| Parameter | Default | Description |
|---|---|---|
| hv_window | 20 | Historical volatility calculation window |
| lookback | 120 | Lookback period for percentile ranking |
| low_pct | 20.0 | Low-volatility threshold (percentile) |
| high_pct | 80.0 | High-volatility threshold (percentile) |
| annualize | 252 | Annualization factor (252 for China A-shares, 365 for crypto) |
Common Pitfalls
- Before the lookback window is filled, there is not enough data to compute percentiles, so the signal should be 0 (
fillna) - Volatility is not direction. Going long in low-volatility regimes does not guarantee price appreciation; it only means volatility expansion is statistically more likely
- Cryptocurrencies trade 7x24, so
annualizeshould be set to 365
Dependencies
pip install pandas numpySignal Convention
1= long (low-volatility regime),-1= short (high-volatility regime),0= stand aside
"""波动率策略信号引擎。
基于历史波动率(HV)百分位排名进行均值回归交易:
低波区间做多等待扩张,高波区间做空等待收缩。纯 pandas 实现。
"""
from typing import Dict
import numpy as np
import pandas as pd
def compute_hv(close: pd.Series, window: int = 20, annualize: int = 252) -> pd.Series:
"""计算年化历史波动率。
Args:
close: 收盘价序列。
window: 波动率计算窗口。
annualize: 年化系数(A股252,加密365)。
Returns:
年化历史波动率序列。
"""
returns = close.pct_change()
return returns.rolling(window).std() * np.sqrt(annualize)
def compute_hv_percentile(hv: pd.Series, lookback: int = 120) -> pd.Series:
"""计算 HV 的滚动百分位排名。
Args:
hv: 历史波动率序列。
lookback: 百分位排名回看期。
Returns:
百分位值(0-100)。
"""
return hv.rolling(lookback).rank(pct=True) * 100
class SignalEngine:
"""波动率均值回归信号引擎。
计算历史波动率的百分位排名,低波做多、高波做空。
Attributes:
hv_window: HV 计算窗口。
lookback: 百分位排名回看期。
low_pct: 低波阈值(百分位)。
high_pct: 高波阈值(百分位)。
annualize: 年化系数。
Example:
>>> engine = SignalEngine(hv_window=20, lookback=120)
>>> signals = engine.generate({"BTC-USDT": df})
>>> signals["BTC-USDT"].value_counts()
"""
def __init__(
self,
hv_window: int = 20,
lookback: int = 120,
low_pct: float = 20.0,
high_pct: float = 80.0,
annualize: int = 252,
):
"""初始化波动率引擎。
Args:
hv_window: HV 计算窗口。
lookback: 百分位排名回看期。
low_pct: 低波阈值(百分位)。
high_pct: 高波阈值(百分位)。
annualize: 年化系数(A股252,加密365)。
"""
self.hv_window = hv_window
self.lookback = lookback
self.low_pct = low_pct
self.high_pct = high_pct
self.annualize = annualize
def generate(self, data_map: Dict[str, pd.DataFrame]) -> Dict[str, pd.Series]:
"""根据波动率百分位生成交易信号。
Args:
data_map: 标的代码到 OHLCV DataFrame 的映射。
Returns:
标的代码到信号 Series 的映射(1=做多, -1=做空, 0=观望)。
"""
result = {}
for code, df in data_map.items():
result[code] = self._generate_one(df)
return result
def _generate_one(self, df: pd.DataFrame) -> pd.Series:
"""对单个标的生成波动率信号。
Args:
df: OHLCV DataFrame。
Returns:
信号 Series。
"""
close = df["close"]
hv = compute_hv(close, self.hv_window, self.annualize)
pct = compute_hv_percentile(hv, self.lookback)
signal = pd.Series(0, index=df.index)
signal[pct < self.low_pct] = 1 # 低波做多
signal[pct > self.high_pct] = -1 # 高波做空
return signal.fillna(0).astype(int)
if __name__ == "__main__":
import requests
def _fetch_okx(inst_id: str, bar: str = "1D", limit: int = 300) -> pd.DataFrame:
"""从 OKX API 获取 K 线数据。
Args:
inst_id: 交易对标识,如 "BTC-USDT"。
bar: K 线周期。
limit: 获取根数。
Returns:
OHLCV DataFrame。
"""
resp = requests.get(
"https://www.okx.com/api/v5/market/candles",
params={"instId": inst_id, "bar": bar, "limit": str(limit)},
)
candles = resp.json()["data"]
columns = ["ts", "open", "high", "low", "close", "vol", "volCcy", "volCcyQuote", "confirm"]
df = pd.DataFrame(reversed(candles), columns=columns)
df["ts"] = pd.to_datetime(df["ts"].astype("int64"), unit="ms")
df = df.set_index("ts")
for col in ["open", "high", "low", "close"]:
df[col] = df[col].astype(float)
df["volume"] = df["vol"].astype(float)
return df
symbols = ["BTC-USDT", "ETH-USDT", "SOL-USDT"]
data_map = {}
for sym in symbols:
print(f"Fetching {sym} ...")
data_map[sym] = _fetch_okx(sym, bar="1D", limit=300)
# 加密货币用 365 年化
engine = SignalEngine(hv_window=20, lookback=120, annualize=365)
signals = engine.generate(data_map)
for sym in symbols:
sig = signals[sym]
hv = compute_hv(data_map[sym]["close"], 20, 365)
pct = compute_hv_percentile(hv, 120)
print(f"\n{sym} ({len(sig)} bars)")
print(f" Current HV(20): {hv.iloc[-1]:.1%}")
print(f" HV Percentile: {pct.iloc[-1]:.0f}%")
print(f" Buy signals: {(sig == 1).sum()}")
print(f" Sell signals: {(sig == -1).sum()}")
print(f" Neutral: {(sig == 0).sum()}")