Skip to content

Repository files navigation

I Ching Stock Prediction Model

Python implementation of the paper:

"Incorporating I Ching Knowledge Into Prediction Task via Data Mining" Liu et al., Journal of Database Management, Volume 34, Issue 3 DOI: 10.4018/JDM.322097


Overview

The model combines traditional I Ching divination with modern machine learning to predict stock price trends (Positive / Neutral / Negative). It outperforms SVM, XGBoost, RF, GRU, and LSTM-Att in the paper's experiments (Macro F1: 63.33% vs. best baseline 61.08%).

Architecture

All Features
    │
    ▼
Random Forest (Gini importance)
    │  top-30 features
    ▼
Three Vitals Mapping
  Ten (天) — macro/environment
  Chi (地) — size & flow
  Jin (人) — fundamentals
    │  top-2 per Vital = 6 features
    ▼
┌─────────────────────────────┐       ┌──────────────────────┐
│     Hexagram Generator      │       │       Decoder        │
│                             │       │                      │
│  Four Signs (sliding k=8)   │──────▶│  Hexagram Explainer  │
│  LaoYin=6 / ShaoYin=8      │       │  (Algorithm Two)     │
│  ShaoYang=7 / LaoYang=9    │       │                      │
│         ↓                  │       │  Sentiment Classifier │
│  6-bit Original Hexagram   │       └──────────────────────┘
│  6-bit Changed Hexagram    │               │
└─────────────────────────────┘               ▼
                                    Positive / Neutral / Negative

Key Concepts

Three Vitals (三才 — Ten, Chi, Jin)

I Ching's framework for categorising all influencing factors:

Vital Chinese Domain Stock examples
Ten 天 (Heaven) Macro / external Market index, sector ETF, policy
Chi 地 (Earth) Size & flow Market cap, volume, fund flow
Jin 人 (Human) Fundamentals Profit margin, debt ratio, ROE

Four Signs (四象)

Each of the 6 feature values is mapped to a Four Signs code via a sliding window of size 8:

Code Name Yao Moving? Boundary
6 LaoYin (老阴) 0 (Yin) Yes → Yang [min, min+A)
8 ShaoYin (少阴) 0 (Yin) No [min+A, min+A+B)
7 ShaoYang (少阳) 1 (Yang) No [min+A+B, min+A+2B)
9 LaoYang (老阳) 1 (Yang) Yes → Yin [min+A+2B, max]

Where A = (1/8) × range, B = (3/8) × range.

Hexagram Generation

  • 6 Yao values (0/1) form a 6-bit binary string (bottom to top)
  • The Vital ordering (which Vital maps to which Yao positions) is selected by trying all 6 permutations of [Ten, Jin, Chi] during fit() and keeping the one with the best macro F1
  • Original hexagram: raw Yao values
  • Changed hexagram: LaoYang (9) flips 1→0, LaoYin (6) flips 0→1

Hexagram Explainer (Algorithm Two)

Selects the I Ching explanation based on the number of moving Yaos (YbNum):

YbNum Explanation source
0 Original hexagram Gua text
1 Yao text of the single moving Yao
2 Yao text of the higher-position moving Yao
3 Original hexagram Gua text
4 Yao text of the lowest stable Yao
5 Yao text of the single stable Yao
6 Changed hexagram Gua text

Label Construction

Huber regression is fit over a rolling window of closing prices. The slope coefficient a is classified using mean ± std thresholds derived from the normal distribution of all slopes across the dataset:

  • Positive (rising): a > mean + std
  • Neutral (stable): mean − std ≤ a ≤ mean + std (~68% of samples)
  • Negative (falling): a < mean − std

This matches the paper's statement: "the distribution of a is counted, and finally the label category is obtained according to the distribution interval of a."

Huber loss is used for robustness to outliers:

L(y, f(x)) = 0.5*(y-f(x))²          if |y-f(x)| ≤ δ
             δ*(|y-f(x)| - 0.5*δ)   otherwise

File Structure

iching/
├── iching_data.py                   # 64 hexagram dict, 384 Yao explanations, sentiment scores
├── iching_wilhelm_translation.js    # Local copy of adamblvck/iching-wilhelm-dataset (MIT)
├── import_wilhelm.py                # One-shot importer: regenerates iching_data.py from dataset
├── label_construction.py            # Huber regression → {Positive, Neutral, Negative} labels
├── feature_selection.py             # RF Gini importance + Three Vitals mapping → 6 features
├── hexagram_generator.py            # Algorithm One: Four Signs + hexagram generation
├── hexagram_decoder.py              # Algorithm Two: Hexagram Explainer + classifier
├── iching_model.py                  # Algorithm Three: full pipeline (IChingModel class)
├── baselines.py                     # SVM, XGBoost, RF, KNN, GRU, LSTM-Att models
├── benchmark.py                     # Train all 7 models and print comparison table
└── demo.py                          # End-to-end demo (AAPL via yfinance or synthetic data)

Installation

pip install numpy pandas scikit-learn xgboost torch yfinance

Quick Start

from iching_model import IChingModel

# Define which features belong to each Vital
model = IChingModel(
    tian_label=["market_index_return", "sector_etf_return"],   # Ten / macro
    di_label=["volume", "turnover_rate", "fund_flow"],         # Chi / flow
    ren_label=["net_profit_margin", "debt_ratio", "roe"],      # Jin / fundamentals
    label_window=60,      # rolling window for Huber label construction
    window_size=8,        # Four Signs sliding window (paper default)
    n_top_total=30,       # RF top-N feature selection
    n_top_per_vital=2,    # features per Vital (→ 6 total)
)

model.fit(feature_df, close_series)

# Predict with I Ching explanations
results = model.predict_with_explanation(feature_df)
print(results[["label", "explanation"]].tail())

# Evaluate
metrics = model.evaluate(feature_df, close_series)
print(f"F1: {metrics['f1']:.4f}")
print(metrics["report"])

Run the demo

cd iching
python demo.py

Fetches AAPL data from yfinance (2015–2024) and runs the full pipeline. Falls back to synthetic data if yfinance is unavailable.

Run the benchmark

cd iching
python benchmark.py              # AAPL via yfinance
python benchmark.py --ticker TSLA
python benchmark.py --synthetic  # force synthetic data

Baseline Models

Six comparison algorithms are implemented in baselines.py, matching the paper's Table 2 setup:

Class Algorithm Notes
SVMModel Support Vector Machine RBF kernel, probability calibration, balanced class weights
XGBoostModel eXtreme Gradient Boosting 200 estimators, depth 6
RFModel Random Forest 200 estimators, balanced class weights
KNNModel K-Nearest Neighbours k=5, distance-weighted, Euclidean
GRUModel Gated Recurrent Unit 2-layer, hidden=64, 20-day look-back, attention-free
LSTMAttModel LSTM with Attention 2-layer, hidden=64, 20-day look-back, additive attention

All share a unified interface:

from baselines import SVMModel, XGBoostModel, RFModel, KNNModel, GRUModel, LSTMAttModel

model = SVMModel()
model.fit(X_train, y_train)       # y in {-1, 0, 1}
preds = model.predict(X_test)     # returns np.ndarray of {-1, 0, 1}

Benchmark Results

Chronological 80/20 train/test split · 2515 trading days (2015–2024) · 11 features · macro-averaged metrics.

AAPL

Test label distribution: 97 Negative / 195 Neutral / 200 Positive

Model Precision Recall F1
GRU 66.36% 42.83% 37.95%
LSTM-Att 65.18% 39.94% 36.50%
I Ching 34.26% 36.28% 32.25%
KNN 33.26% 32.09% 29.80%
XGBoost 48.73% 33.02% 28.15%
SVM 26.86% 35.61% 27.78%
RF 39.44% 30.07% 26.70%

S&P 500 (^GSPC)

Test label distribution: 36 Negative / 261 Neutral / 195 Positive

Model Precision Recall F1
LSTM-Att 29.32% 31.65% 27.52%
SVM 27.40% 27.55% 27.31%
XGBoost 30.18% 32.26% 24.87%
GRU 26.28% 30.15% 24.83%
KNN 24.35% 31.24% 24.26%
I Ching 29.27% 31.08% 23.93%
RF 17.36% 31.93% 22.49%

All models cluster tightly (23–28% F1) on the index. The Neutral-heavy label distribution (53% of test samples) reflects that the S&P 500 spends most of its time drifting sideways. No model reliably catches Negative (bear) periods — only 36 test samples — because a single-ticker feature set lacks the cross-stock signals needed to separate sharp drawdowns from sideways drift.

Paper results (3000 Chinese stocks, 2010–2020)

Model Precision Recall F1
I Ching 65.41% 61.51% 63.33%
LSTM-Att 61.85% 60.33% 61.08%
GRU 60.15% 60.71% 60.43%
RF 46.46% 68.16% 55.26%
XGBoost 46.31% 68.05% 55.11%
KNN 52.00% 56.77% 54.05%
SVM 45.51% 67.46% 54.35%

Note on differences from the paper: The paper uses 3000 Chinese stocks with full quarterly fundamentals and daily money-flow data. This benchmark uses a single ticker with 11 features, 4 of which are synthetic (fundamentals not available from yfinance). The I Ching model's advantage is strongest in the multi-stock, industry-segmented setting — see Table 3 where industry-specific F1 reaches 76.94% (Power and Energy).


Implementation Audit & Fixes

A full audit against the paper's three algorithms identified and fixed three critical bugs.

Bug 1 — Sliding window element removal (hexagram_generator.py)

Paper: del sortedvec[m] removes the oldest element by its tracked index position m in the sorted array.

Was: sortedvec.remove(old_val) — removes the first occurrence by value, silently producing wrong results when duplicate values exist in the window.

Fix: Uses bisect.bisect_left to locate the exact index of the oldest value, then del sortedvec[m] by position. Also switched to bisect.insort for O(log n) insertion instead of re-sorting each step.

Bug 2 — Label construction thresholds (label_construction.py)

Paper: "the distribution of a is counted, and finally the label category is obtained according to the distribution interval of a" — mean ± std from the normal distribution.

Was: np.percentile(slopes, 33.33) and np.percentile(slopes, 66.67) — always forced exactly ⅓ of samples into each class regardless of the actual distribution shape.

Fix: Thresholds are now mean ± n_std × std (default n_std=1.0), producing a Neutral-heavy label distribution (~68% within 1σ) consistent with the paper's normal distribution assumption.

Bug 3 — Vital ordering permutations (iching_model.py)

Paper: All 6 permutations of the Three Vitals ordering are evaluated; the best-performing Yao sequence arrangement is kept.

Was: The permutation loop regenerated hexagrams from the same fixed six_features_ order on every iteration — all 6 iterations were identical, so the "best" ordering was never actually searched.

Fix: Each permutation now reorders the 6 features by Vital before generating hexagrams. The best ordering is saved as best_six_features_ and used by predict() and predict_with_explanation().


API Reference

IChingModel

Method Description
fit(feature_df, close) Train: build labels, select features, find best Vital ordering
predict(feature_df) Return pd.Series of {1, 0, -1} predictions
predict_with_explanation(feature_df) Return pd.DataFrame with prediction, label, and I Ching text
evaluate(feature_df, close) Return dict with precision, recall, F1, classification report

Module-level functions

Function Module Description
build_labels(close, window, delta, n_std) label_construction Huber regression labels (mean±n_std thresholds)
select_features(X, y, ...) feature_selection RF → Three Vitals → 6 features
four_signs_generator(vec, k) hexagram_generator Algorithm One
build_hexagram_sequences(df, features, k) hexagram_generator Full feature→hexagram pipeline
hexagram_explainer(orig, changed, moving) hexagram_decoder Algorithm Two
decode_sequence(hex_records) hexagram_decoder Batch decode to predictions

References

Liu, W., Chen, S., Huang, G., Lu, L., Li, H., & Sun, G. (2023). Incorporating I Ching Knowledge Into Prediction Task via Data Mining. Journal of Database Management, 34(3). https://doi.org/10.4018/JDM.322097

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages