Skip to content

Zi Wei Dou Shu (紫微斗数)

A Zi Wei Dou Shu engine that builds the twelve-palace chart with the fourteen major stars from a birth datetime. It converts the birth instant to a lunisolar date, derives the year stem/branch, 命宮 / 身宮, the 五行局 bureau (from the 命宮 stem-branch 納音), and the 紫微 / 天府 star series, then assigns the twelve palaces to the earthly branches.

Each palace selection's symbol is the earthly branch the palace occupies; the palace's stars, its heavenly stem (五虎遁), and the 身宮 flag are recorded as modifiers. Only the fourteen major stars are placed — minor stars and the 四化 transformations (which reference stars beyond the majors and diverge between schools) are out of scope.

from fortune_telling_core import Querent, ReadingRequest
from fortune_telling_core.traditions.zi_wei import ZI_WEI_DECK, ZI_WEI_SPREAD, build_engine

request = ReadingRequest(
    deck_id=ZI_WEI_DECK.id,
    spread_id=ZI_WEI_SPREAD.id,
    querent=Querent(
        id="sample",
        display_name="Sample",
        attributes={"birth_datetime": "1985-04-29T10:00:00+08:00"},
    ),
)
reading = build_engine().cast(request)

Time-varying fortunes (流年 / 大限)

The summary also relocates the 命宮 pointer for a as_of moment (the fourteen major stars do not move):

  • 流年 (annual) — the 流年命宮 sits on the palace whose branch equals the target year's 太歲 branch. Always reported; the year defaults to as_of (or requested_at) and can be pinned with the target_year attribute.
  • 大限 (decade limits) — each palace governs ten years; the first 大限 is the 命宮, starting at the 五行局 number in 虚歳 (數え年, born = 1). It appears only when the request supplies a gender attribute (male / female): 陽年男・ 陰年女 → 順行 (advancing the branch), 陰年男・陽年女 → 逆行 (retreating it). The active decade for the target year is reported with its palace and resident stars.

The moving 流 stars (流昌 / 流曲 …) and the 四化 are minor/divergent and remain out of scope, as in the natal chart.

zi_wei

ZI_WEI_DECK module-attribute

ZI_WEI_DECK = Deck(
    id="zi_wei.deck.branches.v1",
    symbols=tuple(
        (
            Symbol(
                id=f"zi_wei.branch.{slug}",
                name=BRANCH_CJK[index],
                attributes={
                    "cjk": BRANCH_CJK[index],
                    "index": str(index),
                },
            )
        )
        for index, slug in (enumerate(BRANCH_SLUG))
    ),
)

ZI_WEI_SPREAD module-attribute

ZI_WEI_SPREAD = Spread(
    id="zi_wei.spread.twelve_palaces.v1",
    name="Zi Wei Twelve Palaces",
    positions=tuple(
        (Position(slug, cjk, _DESCRIPTIONS[slug]))
        for slug, cjk in PALACES
    ),
)

ZiWeiEngine

ZiWeiEngine(ephemeris: Ephemeris | None = None)

Bases: AbstractEngine

Zi Wei Dou Shu twelve-palace engine.

Parameters:

Name Type Description Default
ephemeris Ephemeris | None

Optional backend implementing the shared ephemeris protocol, used by the lunisolar converter. Defaults to BuiltinEphemeris.

None

Time-varying fortunes: the 流年 (annual 命宮) for request.as_of (or target_year / requested_at) is always reported; the active 大限 (decade limit) is added when the request supplies gender (male/female), which fixes its 順行 / 逆行 direction.

Source code in src/fortune_telling_core/traditions/zi_wei/engine.py
def __init__(self, ephemeris: Ephemeris | None = None) -> None:
    self.ephemeris = ephemeris or BuiltinEphemeris()

id class-attribute instance-attribute

id = 'zi_wei.engine'

version class-attribute instance-attribute

version = '0.1.0'

ephemeris instance-attribute

ephemeris = ephemeris or BuiltinEphemeris()

deck

deck(request: ReadingRequest) -> Deck

Return the Zi Wei branch deck.

Source code in src/fortune_telling_core/traditions/zi_wei/engine.py
def deck(self, request: ReadingRequest) -> Deck:
    """Return the Zi Wei branch deck."""

    if request.deck_id != ZI_WEI_DECK.id:
        raise ValidationError(f"unsupported Zi Wei deck: {request.deck_id}")
    return ZI_WEI_DECK

spread

spread(request: ReadingRequest) -> Spread

Return the Zi Wei twelve-palace spread.

Source code in src/fortune_telling_core/traditions/zi_wei/engine.py
def spread(self, request: ReadingRequest) -> Spread:
    """Return the Zi Wei twelve-palace spread."""

    if request.spread_id != ZI_WEI_SPREAD.id:
        raise ValidationError(f"unsupported Zi Wei spread: {request.spread_id}")
    return ZI_WEI_SPREAD

draw

draw(request: ReadingRequest, rng: Rng) -> Draw

Compute the Zi Wei chart as a deterministic draw.

Source code in src/fortune_telling_core/traditions/zi_wei/engine.py
def draw(self, request: ReadingRequest, rng: Rng) -> Draw:
    """Compute the Zi Wei chart as a deterministic draw."""

    del rng
    birth = self._birth(request)
    jd_tt = jd_tt_from_utc(julian_day_utc(birth.birth_datetime))
    offset = birth.birth_datetime.utcoffset()
    tz_hours = offset.total_seconds() / 3600.0 if offset is not None else 0.0
    lunisolar = to_lunisolar(jd_tt, tz_hours=tz_hours, ephemeris=self.ephemeris)
    hour_branch = ((birth.birth_datetime.hour + 1) // 2) % 12
    chart = compute_chart(lunisolar.year, lunisolar.month, lunisolar.day, hour_branch)
    return _draw_from_chart(
        chart,
        target_year=birth.target_year,
        gender=birth.gender,
        birth_lunar_year=lunisolar.year,
    )

cast

cast(request: ReadingRequest) -> Reading

Compute a Zi Wei reading without a caller RNG.

Source code in src/fortune_telling_core/traditions/zi_wei/engine.py
def cast(self, request: ReadingRequest) -> Reading:
    """Compute a Zi Wei reading without a caller RNG."""

    draw = self.draw(request, _NULL_RNG)
    return self._interpret(request, draw, rng=None)

build_engine

build_engine(
    ephemeris: Ephemeris | None = None,
) -> ZiWeiEngine

Create a Zi Wei Dou Shu engine.

Source code in src/fortune_telling_core/traditions/zi_wei/engine.py
def build_engine(ephemeris: Ephemeris | None = None) -> ZiWeiEngine:
    """Create a Zi Wei Dou Shu engine."""

    return ZiWeiEngine(ephemeris=ephemeris)