Charting entries

The charting methods of the Astro class, re-anchoring, and the semantic text projection.

Charting is where everything starts: give a birth date, hour and gender, get an Astrolabe.

from x_iztro import Astro

astro = Astro()

Astro holds no internal state, so instantiate it once and use it everywhere — or construct one per call.

Every entry point raises IztroError on invalid input (it subclasses ValueError, so except ValueError catches it too). Date format and existence, the solar year range and the hour index are validated up front in the core; string values such as gender, language and palace name are validated in the binding layer. The exception carries a machine-readable classification in .code — see Error handling.


ChartConfig

The charting configuration: six switches plus two optional override tables. It is a frozen dataclass in which every field has a default, and the defaults match JS iztro — ChartConfig() is equivalent to passing no config at all.

FieldTypeDefaultValues (matching enum)
year_dividestr"normal"normal lunar New Year's Day / exact Beginning of Spring (YearDivide)
horoscope_dividestr"normal"normal first of the month / exact solar term (HoroscopeDivide)
age_dividestr"normal"normal increments at the turn of the year / birthday increments on the birthday (AgeDivide)
day_dividestr"forward"forward the late Zi hour belongs to the next day / current to the current day (DayDivide)
algorithmstr"default"default / zhongzhou (Algorithm)
astro_typestr"heaven"heaven heaven chart / earth earth chart / human human chart (AstroType)
mutagensdict[str, list[str]] | NoneNoneStem key → four star keys (Lu, Quan, Ke, Ji)
brightnessdict[str, list[str]] | NoneNoneStar key → twelve brightness keys; index 0 is the Yin palace, an empty string means no brightness in that palace

For what the six switches mean and the schools behind them, see Config in depth.

Methods

MethodDescription
to_dict() -> dictConvert to the camelCase config object the binding layer accepts; either table is omitted from the result when it is None

Example

from x_iztro import Astro, ChartConfig, AstroType, HeavenlyStem, MajorStar

cfg = ChartConfig(
    astro_type="earth",
    mutagens={HeavenlyStem.GENG: [MajorStar.TAIYANG, MajorStar.WUQU,
                                  MajorStar.TIANFU, MajorStar.TIANTONG]},
)

print(cfg.to_dict())

chart = Astro().by_solar("2000-8-16", 2, "female", language="en-US", config=cfg)
print(chart.five_elements_class, chart.config.astro_type)
print(chart.palace("soulPalace").mutagen_star_keys)

Output

{'yearDivide': 'normal', 'horoscopeDivide': 'normal', 'ageDivide': 'normal', 'dayDivide': 'forward', 'algorithm': 'default', 'astroType': 'earth', 'mutagens': {'gengHeavenly': ['taiyangMaj', 'wuquMaj', 'tianfuMaj', 'tiantongMaj']}}
earth 5th earth
['tiantongMaj', 'tianjiMaj', 'wenchangMin', 'lianzhenMaj']

The earth chart's Soul palace lands on the original chart's body palace (Career, bing-xu), so the palace-stem mutagens are taken from the bing stem.

Edge cases and pitfalls


by_solar

Purpose Chart a natal chart from a solar date.

Zi Wei meaning Zi Wei Dou Shu computes on the lunar calendar, but most people only remember their solar birthday. This method converts solar to lunar first (including the year, month, day and hour pillars) and places the stars from there. When the year turns over is governed by year_divide — for someone born between lunar New Year and the Beginning of Spring, the two settings give different year pillars, which in turn affects the mutagens, the soul and body stars, and every year-based star.

Signature

def by_solar(
    self,
    solar_date: str,
    time_index: TimeIndexType,
    gender: GenderType,
    *,
    fix_leap: bool = True,
    language: LanguageType = "zh-CN",
    config: ChartConfig | None = None,
) -> Astrolabe

Parameters

ParameterTypeRequiredDefaultDescription
solar_datestrYesSolar date in YYYY-M-D; month and day need no zero padding. Years 1583–9999
time_indexintYesHour index 0–12. 0 is the early Zi hour (00:00–01:00), 12 the late Zi hour (23:00–24:00)
genderstrYes"male" or "female". Sets the direction of the decadal scope and of the Changsheng and Boshi gods
fix_leapboolNoTrueKeyword-only. Whether to correct for lunar leap months. When true, days from the sixteenth of a leap month onward count as the next month (the late Zi hour excepted, see below)
languagestrNo"zh-CN"Output language; affects every translated field. The *_key fields are unaffected
configChartConfig | NoneNoNoneCharting configuration; None takes the defaults

Return value Astrolabe — a complete chart with the twelve palaces, the four pillars, the soul and body stars and the five elements class.

Example

from x_iztro import Astro

chart = Astro().by_solar("2000-8-16", 2, "female", language="en-US")

print(chart.solar_date, "|", chart.lunar_date, "|", chart.chinese_date)
print(chart.sign, chart.zodiac, chart.five_elements_class)
print("soul", chart.soul, "body", chart.body)

Output

2000-8-16 | 二〇〇〇年七月十七 | geng chen - jia shen - bing woo - geng yin
leo dragon wood 3rd
soul rebel body scholar

Edge cases and pitfalls


by_lunar

Purpose Chart a natal chart from a lunar date.

Zi Wei meaning The lunar date is Zi Wei Dou Shu's native input, and this skips the solar conversion. Anyone who knows their lunar birthday can use it directly; the result is identical to calling by_solar with the corresponding solar date.

Signature

def by_lunar(
    self,
    lunar_date: str,
    time_index: TimeIndexType,
    gender: GenderType,
    *,
    is_leap_month: bool = False,
    fix_leap: bool = True,
    language: LanguageType = "zh-CN",
    config: ChartConfig | None = None,
) -> Astrolabe

Parameters Identical to by_solar apart from the following. Everything after gender is keyword-only — is_leap_month and fix_leap sit next to each other, and swapping them positionally would raise no error while silently shifting the chart by a month.

ParameterTypeRequiredDefaultDescription
lunar_datestrYesLunar date in YYYY-M-D; write the month as a positive number (leap months are flagged by the next parameter)
is_leap_monthboolNoFalseKeyword-only. Whether that lunar month is a leap month. Has no effect if that month in that year has no leap month to begin with

Return value Same as by_solar.

Example

a = Astro().by_lunar("2000-7-17", 2, "female", language="en-US")
b = Astro().by_solar("2000-8-16", 2, "female", language="en-US")

print(a.solar_date, a.solar_date == b.solar_date)

Output

2000-8-16 True

Edge cases and pitfalls

The silent no-op of is_leap_month

Pass True when that month is not a leap month and the parameter is silently ignored rather than raising an error. If you need strict validation, confirm the leap month exists for that year and month before calling.


get_horoscope

Purpose Compute the horoscope for a target date, starting from a natal chart.

Signature

def get_horoscope(
    self,
    astrolabe: Astrolabe,
    target_date: str | None = None,
    target_time_index: TimeIndexType | None = None,
) -> Horoscope

Parameters

ParameterTypeRequiredDefaultDescription
astrolabeAstrolabeYesThe natal chart
target_datestr | NoneNoNoneTarget solar date; today when omitted
target_time_indexint | NoneNoNoneTarget hour index; the current hour when omitted

Return value Horoscope, holding the chart passed in. Details on the horoscope object.

Example

chart = Astro().by_solar("2000-8-16", 2, "female", language="en-US")
h = Astro().get_horoscope(chart, "2025-6-1", 0)

print(h.decadal.heavenly_stem + h.decadal.earthly_branch)
print(h.yearly.heavenly_stem + h.yearly.earthly_branch)

Output

gengchen
yisi

Edge cases and pitfalls

There is an equivalent method on the chart

chart.horoscope("2025-6-1", 0) is exactly equivalent and needs no Astro instance in hand. Astro.get_horoscope exists so that "every entry point on one class" also works as a usage style.


rearranged

Purpose Re-anchor the chart on a given stem and branch as the Soul palace and return a new chart; the original is untouched.

Zi Wei meaning The Zhongzhou school reads one set of birth data as three charts: the heaven chart anchors the five elements class on the Soul palace's pillar, the earth chart on the body palace's, the human chart on the Spirit palace's. Change the anchoring pillar and the class changes, and with it the placement of Ziwei and Tianfu, the twelve palace names, the Changsheng gods and the decadal and age scopes are all recomputed. This method opens that capability up to any stem and branch.

Signature

def rearranged(self, from_stem: str, from_branch: str) -> Astrolabe

This is a method on Astrolabe, not on the Astro class.

Parameters

ParameterTypeRequiredDefaultDescription
from_stemstrYesStem key of the new Soul palace, from the HeavenlyStem enum's value set
from_branchstrYesBranch key of the new Soul palace, from the EarthlyBranch enum's value set

Return value A new Astrolabe. Recomputed: the Soul and body palaces, the five elements class, the fourteen major stars, the twelve palace names, the Changsheng gods, the decadal and age scopes, the soul star, plus Tianshang, Tianshi and Tiancai, which follow the Soul palace. Carried over from the original chart: minor stars, the remaining adjective stars, the Boshi gods, the Sui-qian and Jiang-qian gods, and the body star.

On the rearranged chart, patterns(), horoscope queries and the to_text projection all compute from the rearranged layout — the five elements class, soul palace and decadal ranges follow the new starting stem-branch; the birth data (dates and four pillars) stays unchanged.

Example

chart = Astro().by_solar("2000-8-16", 2, "female", language="en-US")

# anchor on the original chart's body palace pillar — equivalent to the earth chart
body = next(p for p in chart.palaces if p.is_body_palace)
earth = chart.rearranged(body.heavenly_stem_key, body.earthly_branch_key)

print("heaven", chart.five_elements_class, "→ earth", earth.five_elements_class)

Output

heaven wood 3rd → earth earth 5th

Edge cases and pitfalls

The three standard charts do not need this method

For the heaven, earth and human charts just chart with ChartConfig(astro_type="earth"); both charting entry points support it. rearranged exists for anchoring on an arbitrary stem and branch.


Semantic text (to_text)

Purpose Project a chart or a horoscope into semantic text — the chart's facts in natural-language form, for a language model or a person. Alongside to_dict/to_json (machine structure) and the translated fields (display), it is the third projection of the same object.

Signature The text projection lives on the objects themselves, not on Astro:

chart.to_text()                            # natal chart; str(chart) is equivalent
chart.horoscope("2025-1-1", 0).to_text()   # horoscope; str(h) is equivalent
chart.palace("soul").to_text()             # one palace
chart.surrounded_palaces("soul").to_text() # surrounded palaces
chart.patterns_to_text()                   # natal patterns

Return value str — sectioned plain text in the chart's own charting language; the natal text closes with a patterns section, and each horoscope scope carries a patterns line and flowing-star lines from its own perspective.

Example

astro = Astro()
chart = astro.by_solar("2000-8-16", 2, "female", language="en-US")

print(chart.to_text()[:77])

Output

=== Basic Info ===
Gender: female
Solar Date: 2000-8-16
Lunar Date: 二〇〇〇年七月十七

Edge cases and pitfalls

The output language follows the chart's language and is not set separately. For English text, chart in English.

On this page