FreeAstroAPI
Back to Guides
Chinese Astrology
January 9, 2026

Calculate Bazi Four Pillars with Python: A Complete Guide

Learn how to calculate the Four Pillars of Destiny (八字) from a birth date using Python. This guide covers the lunar calendar, Heavenly Stems, Earthly Branches, and practical code examples.

What is Bazi (Four Pillars of Destiny)?

Bazi (八字), literally "Eight Characters," is the Chinese astrological system that maps a person's destiny based on their birth date and time. The system derives four pillars (Year, Month, Day, Hour), each composed of two characters: a Heavenly Stem (天干 / Tiān Gān) and an Earthly Branch (地支 / Dì Zhī).

These eight characters encode the cosmic energy present at the moment of birth, revealing insights about personality, career, relationships, and life cycles.


The Building Blocks

The 10 Heavenly Stems (天干)

StemPinyinElementPolarity
JiǎWoodYang
WoodYin
BǐngFireYang
DīngFireYin
EarthYang
EarthYin
GēngMetalYang
XīnMetalYin
RénWaterYang
GuǐWaterYin

The 12 Earthly Branches (地支)

BranchPinyinZodiacElement
RatWater
ChǒuOxEarth
YínTigerWood
MǎoRabbitWood
ChénDragonEarth
SnakeFire
HorseFire
WèiGoatEarth
ShēnMonkeyMetal
YǒuRoosterMetal
DogEarth
HàiPigWater

The Sexagenary Cycle (六十甲子)

The 10 Stems and 12 Branches combine in sequence to form a 60-unit cycle. This cycle is the foundation of all Chinese calendar systems and is used to represent years, months, days, and hours.

The cycle begins with 甲子 (Jiǎ Zǐ) and ends with 癸亥 (Guǐ Hài). Calculating which Stem-Branch pair applies to a specific moment requires astronomical precision, particularly for the solar terms that define month boundaries.


Calculating Four Pillars in Python

The most reliable way to calculate Bazi in Python is using the lunar-python library. This open-source package handles the complex conversions between the Gregorian calendar, the Chinese lunar calendar, and the sexagenary cycle.

Installation

pip install lunar-python

Basic Calculation

Here's a minimal example to extract the Four Pillars from a birth date and time:

from lunar_python import Solar

# Birth data: May 15, 1990 at 10:30 AM
year, month, day = 1990, 5, 15
hour, minute = 10, 30

# Create a Solar (Gregorian) date object
solar = Solar.fromYmdHms(year, month, day, hour, minute, 0)

# Convert to Lunar and then to BaZi (EightChar)
lunar = solar.getLunar()
eight_char = lunar.getEightChar()

# Extract the Four Pillars
print("Year Pillar:", eight_char.getYear())    # e.g., 庚午
print("Month Pillar:", eight_char.getMonth())  # e.g., 辛巳
print("Day Pillar:", eight_char.getDay())      # e.g., 丙寅
print("Hour Pillar:", eight_char.getTime())    # e.g., 癸巳

Output:

Year Pillar:  庚午
Month Pillar: 辛巳
Day Pillar:   丙寅
Hour Pillar:  癸巳

Extracting Individual Stems and Branches

For more granular analysis, you can extract each pillar's Stem (Gan) and Branch (Zhi) separately:

# Stems (天干)
print("Year Stem:", eight_char.getYearGan())   # 庚
print("Month Stem:", eight_char.getMonthGan()) # 辛
print("Day Stem:", eight_char.getDayGan())     # 丙
print("Hour Stem:", eight_char.getTimeGan())   # 癸

# Branches (地支)
print("Year Branch:", eight_char.getYearZhi())   # 午
print("Month Branch:", eight_char.getMonthZhi()) # 巳
print("Day Branch:", eight_char.getDayZhi())     # 寅
print("Hour Branch:", eight_char.getTimeZhi())   # 巳

Hidden Stems (藏干)

Each Earthly Branch contains "hidden" Heavenly Stems that represent latent energies. The lunar-python library provides access to these:

# Hidden stems for each pillar
print("Year Hidden:", eight_char.getYearHideGan())   # ['丁', '己']
print("Month Hidden:", eight_char.getMonthHideGan()) # ['丙', '庚', '戊']
print("Day Hidden:", eight_char.getDayHideGan())     # ['甲', '丙', '戊']
print("Hour Hidden:", eight_char.getTimeHideGan())   # ['丙', '庚', '戊']

Ten Gods (十神)

The "Ten Gods" system describes the relationship between each pillar's elements and the Day Master (Day Stem). This is essential for Bazi interpretation:

# The Day Master is the Day Stem
day_master = eight_char.getDayGan()
print(f"Day Master: {day_master}")  # 丙 (Bing Fire)

# Ten Gods for each pillar's stem
print("Year Ten God:", eight_char.getYearShiShenGan())   # Direct Wealth
print("Month Ten God:", eight_char.getMonthShiShenGan()) # Indirect Wealth
print("Hour Ten God:", eight_char.getTimeShiShenGan())   # Direct Resource

Calculating the Luck Cycle (大运)

The 10-year Luck Pillars (Da Yun) are calculated based on the birth chart and gender:

# 1 = Male, 0 = Female
sex = 1

yun = eight_char.getYun(sex)
print(f"Luck cycle starts at age: {yun.getStartYear()}")
print(f"Direction: {'Forward' if yun.isForward() else 'Backward'}")

for da_yun in yun.getDaYun():
    if da_yun.getIndex() == 0:
        continue  # Skip the base period
    print(f"Age {da_yun.getStartAge()}-{da_yun.getEndAge()}: {da_yun.getGanZhi()}")

Handling Time Zones and True Solar Time

A common challenge in Bazi calculation is determining the correct local time. Professional practitioners often use True Solar Time (based on the sun's actual position) rather than civil clock time.

The algorithm involves:

  1. Longitude Correction: Adjusting for the position within a timezone
  2. Equation of Time: Compensating for Earth's orbital eccentricity
from datetime import datetime, timedelta

def get_true_solar_time(dt, longitude, timezone_meridian):
    """
    Calculate True Solar Time from civil time.
    
    Args:
        dt: datetime object in local civil time
        longitude: Birth location longitude (degrees, East positive)
        timezone_meridian: Standard meridian for the timezone (e.g., 120 for UTC+8)
    
    Returns:
        Adjusted datetime representing True Solar Time
    """
    # Longitude correction: 4 minutes per degree from standard meridian
    long_correction_minutes = (longitude - timezone_meridian) * 4
    
    # Equation of Time approximation (simplified)
    # E = 9.87 * sin(2B) - 7.53 * cos(B) - 1.5 * sin(B)
    import math
    day_of_year = dt.timetuple().tm_yday
    b = (2 * math.pi / 365) * (day_of_year - 81)
    eot_minutes = 9.87 * math.sin(2*b) - 7.53 * math.cos(b) - 1.5 * math.sin(b)
    
    total_correction = timedelta(minutes=long_correction_minutes + eot_minutes)
    return dt + total_correction

Real-World Example: Complete Chart

Here's a complete example that builds a structured Bazi chart:

from lunar_python import Solar

def calculate_bazi_chart(year, month, day, hour, minute, sex=1):
    """
    Calculate a complete Bazi chart.
    
    Returns a dictionary with all four pillars and their components.
    """
    solar = Solar.fromYmdHms(year, month, day, hour, minute, 0)
    lunar = solar.getLunar()
    eight_char = lunar.getEightChar()
    
    pillars = []
    labels = ["year", "month", "day", "hour"]
    
    stems = [
        eight_char.getYearGan(),
        eight_char.getMonthGan(),
        eight_char.getDayGan(),
        eight_char.getTimeGan()
    ]
    
    branches = [
        eight_char.getYearZhi(),
        eight_char.getMonthZhi(),
        eight_char.getDayZhi(),
        eight_char.getTimeZhi()
    ]
    
    for i, label in enumerate(labels):
        pillars.append({
            "label": label,
            "stem": stems[i],
            "branch": branches[i],
            "pillar": f"{stems[i]}{branches[i]}"
        })
    
    return {
        "day_master": eight_char.getDayGan(),
        "pillars": pillars,
        "lunar_date": f"{lunar.getYearInChinese()}年 {lunar.getMonthInChinese()}月 {lunar.getDayInChinese()}"
    }

# Example usage
chart = calculate_bazi_chart(1990, 5, 15, 10, 30, sex=1)
print(f"Day Master: {chart['day_master']}")
print(f"Lunar Date: {chart['lunar_date']}")
for p in chart['pillars']:
    print(f"{p['label'].capitalize()} Pillar: {p['pillar']}")

DIY vs API: Which Should You Choose?

While calculating Bazi in Python is a great learning exercise, building a production-grade application comes with challenges:

  1. Maintenance: Timezone databases and leap second adjustments change annually.
  2. Accuracy: True Solar Time requires precise geo-coordinates and high-fidelity ephemeris data (like NASA's JPL DE405).
  3. Complexity: Handling edge cases like "Rat hour" (Early vs Late limit) and Blind Years requires deep astrological domain knowledge.

If you need guaranteed accuracy and professional features (like Strength analysis or Yield prediction) without the maintenance burden, using a specialized API is often the smarter choice.


Open Source Libraries

Our API is built on top of the following open-source libraries:

LibraryPurposeLicense
lunar-pythonChinese calendar and Bazi calculationsMIT
pyswissephSwiss Ephemeris for True Solar TimeGPL
pytzTimezone handlingMIT
timezonefinderTimezone lookup from coordinatesMIT

Try Our Free Bazi API

If you want to calculate professional-grade Bazi charts without managing dependencies, infrastructure, or edge cases yourself, try our free API.

Our /api/v1/chinese/bazi endpoint provides:

  • Complete Four Pillars with Pinyin romanization
  • Ten Gods relationships for every stem
  • Hidden Stems with element analysis
  • Symbolic Stars (Shen Sha) including Nobleman, Peach Blossom, and more
  • Branch interactions: Combinations, Clashes, Harmonies, Punishments
  • 10-year Luck Cycle calculation
  • Professional features: Day Master strength, Chart Structure, Yong Shen analysis

Quick Example

import requests

url = "https://astro-api-1qnc.onrender.com/api/v1/chinese/bazi"

payload = {
    "year": 1990,
    "month": 5,
    "day": 15,
    "hour": 10,
    "minute": 30,
    "lat": 28.6139,
    "lng": 77.2090,
    "sex": "M",
    "include_pinyin": True,
    "include_stars": True,
    "include_interactions": True,
    "include_professional": True
}

headers = {
    "Content-Type": "application/json",
    "x-api-key": "YOUR_API_KEY"
}

response = requests.post(url, headers=headers, json=payload)
print(response.json())

Read the Full API Documentation →


Start Building Today

Whether you're building an astrology app, integrating Chinese metaphysics into your platform, or just exploring Bazi for personal research, the tools are available.

  1. DIY: Use lunar-python directly for full control
  2. API: Use our Bazi API for production-ready calculations

Get Your Free API Key →