Astrolabe object

The fields of Astrolabe, its lookup methods, and the surrounded and flanking palaces.

Astrolabe is what charting produces and the entry point for every query. It is a frozen=True dataclass holding all the data of the twelve palaces along with chart-level information such as the four pillars, the soul and body stars and the five elements class.

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

The examples on this page all chart in en-US, so the display values in the output are the English translations. Charting in another language changes those strings and nothing else — the *_key identifiers and the results of every predicate method stay the same.

Fields


palace

Purpose Fetch a palace by index, by name, or as the body or original palace.

Zi Wei meaning The twelve palaces are the skeleton of a chart. Once the Soul palace is fixed the other eleven follow counterclockwise in a fixed order. The "body palace" is whichever of the twelve also carries that flag, marking where acquired effort concentrates; the "palace of origin" is the one whose stem matches the birth-year stem, marking where matters originate.

Signature

def palace(self, index_or_name: int | PalaceName | str) -> Palace | None

Parameters

ParameterTypeRequiredDefaultDescription
index_or_nameint | strYes—Four spellings, see the table below
SpellingExampleMeaning
Indexchart.palace(0)Palace index 0–11, where 0 is the Yin palace
Name keychart.palace("soulPalace")One of the twelve palace-name keys, i.e. the value set of PalaceName
Palace name in the charting languagechart.palace("soul")The palace-name text as translated for the chart's language
Body palacechart.palace("bodyPalace")Whichever palace carries the body-palace flag
Palace of originchart.palace("originalPalace")The palace whose stem matches the birth-year stem

Return value Palace | None. An out-of-range index or a misspelled name returns None; the name, body-palace and origin-palace spellings all resolve on any chart as long as they are spelled correctly.

Example

soul = chart.palace("soulPalace")
print(soul.name, soul.heavenly_stem + soul.earthly_branch)

print("body palace falls in", chart.palace("bodyPalace").name)
print("palace of origin is", chart.palace("originalPalace").name)
print("the Yin palace is", chart.palace(0).name)

Output

soul renwoo
body palace falls in career
palace of origin is spouse
the Yin palace is wealth

Edge cases and pitfalls


star / star_in_palace

Purpose Find a star by key, or get it together with the palace it sits in.

Signature

def star(self, star: str) -> Star | None
def star_in_palace(self, star: str) -> tuple[Star, Palace] | None

Parameters

ParameterTypeRequiredDefaultDescription
starstrYes—A star key (such as "ziweiMaj"), or the star name in the chart's charting language (such as "emperor")

Return value None when the star is not on this chart. star_in_palace returns a (star, palace) tuple, sparing a further call to star.palace().

Example

ziwei = chart.star("ziweiMaj")

print(ziwei.name, "sits in", ziwei.palace().name)
print("its opposite palace is", ziwei.opposite_palace().name)
print("brightness", ziwei.brightness, "mutagen", ziwei.mutagen)

star, palace = chart.star_in_palace("ziweiMaj")
print(star.key, palace.name_key)

Output

emperor sits in soul
its opposite palace is surface
brightness [+3] mutagen None
ziweiMaj soulPalace

Edge cases and pitfalls

The search covers only the three groups of major, minor and adjective stars. The Changsheng, Boshi, Sui-qian and Jiang-qian gods are one-per-palace marks rather than star lists — read them from fields like palace.changsheng12_key.


surrounded_palaces

Purpose Fetch the surrounded palaces of a target palace.

Zi Wei meaning The surrounded set is the most commonly used reading scope in Zi Wei Dou Shu: the palace itself, its opposite (index +6), the career position (+4) and the wealth position (+8). The four are read together rather than the palace alone, because the stars of the opposite and trine palaces bear on the palace's affairs just as much.

Signature

def surrounded_palaces(self, index_or_name: int | PalaceName | str) -> SurroundedPalaces | None

Parameters Same as palace; all four spellings are supported.

Return value SurroundedPalaces | None, holding the four Palaces target / opposite / wealth / career. Returns None when the palace cannot be located (out-of-range index or misspelled name). Its predicates are on Surrounded palaces.

Example

sp = chart.surrounded_palaces("soulPalace")

print(sp.target.name, sp.opposite.name, sp.wealth.name, sp.career.name)
print("Ziwei in the surrounded set:", sp.have(["ziweiMaj"]))

Output

soul surface wealth career
Ziwei in the surrounded set: True

is_surrounded / is_surrounded_one_of / not_surrounded

Purpose Test the surrounded palaces of a palace straight from the chart, skipping the step of fetching the set first.

Signature

def is_surrounded(self, index_or_name: int | PalaceName | str, stars: list[str]) -> bool
def is_surrounded_one_of(self, index_or_name: int | PalaceName | str, stars: list[str]) -> bool
def not_surrounded(self, index_or_name: int | PalaceName | str, stars: list[str]) -> bool

Parameters

ParameterTypeRequiredDefaultDescription
index_or_nameint | strYes—Located the same way as in palace
starslist[str]Yes—A list of star keys

Return value

MethodMeaning
is_surroundedEvery star in the list is in the surrounded set
is_surrounded_one_ofAt least one star in the list is in the surrounded set
not_surroundedNone of the stars in the list is in the surrounded set

Example

print(chart.is_surrounded("soulPalace", ["ziweiMaj", "tianxiangMaj"]))
print(chart.is_surrounded_one_of("soulPalace", ["qishaMaj", "pojunMaj"]))
print(chart.not_surrounded("soulPalace", ["huoxingMin"]))

Output

True
False
True

The Soul palace holds only Ziwei, while Tianxiang sits in the Wealth palace, one of the trine — hence the first line is true. Neither Qisha nor Pojun is in any of the four, hence the second is false.

Edge cases and pitfalls

What an empty list returns

With an empty stars list, is_surrounded and not_surrounded return True ("all elements satisfy" and "no element fails" both hold vacuously) while is_surrounded_one_of returns False. Make sure the list is non-empty before calling.


flanking_palaces

Purpose Get a palace's flanking pair: the two palaces immediately before and after it on the chart.

Zi Wei meaning Phrases like "Qing Yang and Tuo Luo flank Ji" or "Sun and Moon flank the Soul palace" are about exactly this pair. Flanking and the surrounded set are two non-overlapping lines of evidence: the surrounded set asks how one group of energies echoes itself, flanking asks what stands on either side.

Signature

def flanking_palaces(self, index_or_name: int | PalaceName | str) -> FlankingPalaces | None

Parameters Same as palace; all four spellings are supported.

Return value FlankingPalaces | None, holding two Palace fields. Returns None when the palace cannot be located.

FieldOffsetDescription
previous-1The preceding palace
next+1The following palace

The twelve palaces form a ring — indices wrap modulo 12, so palace 0 is preceded by palace 11. astrolabe() gives back the chart the pair belongs to.

The five predicates are named and defined exactly as on the surrounded set; only the scope changes to these two palaces:

MethodMeaning
have(stars: list[str]) -> boolThe two palaces together hold every star in the list
not_have(stars: list[str]) -> boolNeither palace holds any of them
have_one_of(stars: list[str]) -> boolThe two together hold at least one
have_mutagen(mutagen: Mutagen) -> boolEither palace carries that natal mutagen
not_have_mutagen(mutagen: Mutagen) -> boolNeither one does

Stars may be written as MajorStar / MinorStar enum members or as star names in the chart's language.

Example

f = chart.flanking_palaces("soulPalace")

print(f.previous.name, "/", f.next.name)
print(f.have(["tianjiMaj", "tuoluoMin"]))
print(f.have_one_of(["huoxingMin"]))

w = chart.flanking_palaces("wealthPalace")
print(w.previous.name, "/", w.next.name)
print(w.have_mutagen("sihuaLu"), w.have_mutagen("sihuaJi"))

Output

siblings / parents
True
False
health / children
True True

The Soul palace sits in woo, flanked by siblings (si) and parents (wei). Tianji sits in siblings and Tuoluo in parents — different palaces, yet have still holds. Huoxing sits in spouse, outside the pair, so have_one_of is False. Wealth sits in yin, flanked by health (Tiantong) and children (Taiyang); this chart's natal stem geng turns Taiyang to Lu and Tiantong to Ji, so both mutagen questions are True.

Edge cases and pitfalls


horoscope

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

Signature

def horoscope(
    self,
    target_date: str | None = None,
    target_time_index: int | None = None,
) -> Horoscope

Parameters

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

Return value Horoscope — a horoscope object holding this chart, so palace lookups across the six scopes need not be passed the astrolabe again. Details on the horoscope object.

Example

h = chart.horoscope("2025-6-1", 0)

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

# both parameters can be omitted for right now
now = chart.horoscope()

Output

decadal gengchen
yearly  yisi

to_text

Purpose The chart's semantic text: a complete description for language models and people; str(chart) is equivalent.

Signature

def to_text(
    self,
    *,
    knowledge: bool | KnowledgePack | None = None,
    config: PatternConfig | None = None,
) -> str

Parameters

ParameterTypeRequiredDefaultDescription
knowledgebool | KnowledgePack | NonenoNoneReading material: True takes the bundled default pack for the charting language, a KnowledgePack uses that pack (custom or merged); when given, readings are inlined after the facts — pattern readings (with their conditions) follow the pattern list, each palace's star readings follow that palace's facts (same-palace major-star combinations first), and a ## Mutagen Notes section closes the text
configPatternConfig | NonenoNonePattern criteria, the same argument as patterns(config); acts on both the text's pattern section and the pattern readings. None takes the default criteria

Return value str — a Markdown subset in the charting language: a # Natal Chart … title, ## Basic Info, ## Palace Overview (a table), ## Patterns, ## Palaces (one ### section per palace from the Soul palace on). The full format is on Semantic text; where readings are inserted is on Text with readings.

Example

print(chart.to_text().splitlines()[0])

zh = astro.by_solar("2000-8-16", 2, "female")          # the bundled pack is zh-CN
text = zh.to_text(knowledge=True)
print(len(zh.to_text()), len(text))
print(" ".join(l for l in text.splitlines() if l.startswith("## ")))

Output

# Natal Chart 2000-8-16 Tiger hour female
3389 20767
## 基本信息 ## 十二宫总览 ## 格局 ## 十二宫 ## 四化释义

Edge cases and pitfalls

knowledge=True on a chart whose language has no bundled pack (only zh-CN has one) raises IztroError (invalid_argument) rather than silently dropping the readings; on an English chart pass a KnowledgePack explicitly.

For single-palace and surrounded-palace text see palace(...).to_text() and surrounded_palaces(...).to_text(); for pattern text see patterns_to_text on Patterns.


to_dict / to_json

Purpose Export the chart as JSON matching the field contract of JS iztro.

Signature

def to_dict(self) -> dict[str, Any]
def to_json(self, **kwargs: Any) -> str

Parameters

ParameterTypeRequiredDefaultDescription
kwargs—No—to_json only: forwarded to json.dumps, e.g. indent=2, sort_keys=True

to_json defaults to ensure_ascii=False, so non-ASCII characters land in the output directly rather than as \uXXXX.

Return value to_dict returns a deep copy of the underlying DTO — camelCase keys, values translated for the charting language, plus the *Key language-independent identifiers and the charting context. Mutating it does not affect the chart. to_json returns the same data as a JSON string, matching iztro's JSON.stringify(astrolabe) key for key and value for value.

Example

d = chart.to_dict()
print(d["solarDate"], d["palaces"][4]["nameKey"])
print(d["config"]["yearDivide"], d["genderKey"], d["timeIndex"])

import json
print(json.dumps({k: d[k] for k in ("gender", "solarDate", "lunarDate")},
                 ensure_ascii=False))
print(len(chart.to_json()) > 10000, chart.to_json()[:1])

Output

2000-8-16 soulPalace
normal female 2
{"gender": "female", "solarDate": "2000-8-16", "lunarDate": "二〇〇〇年七月十七"}
True {

The key order is lexicographic

When the native extension converts the underlying DTO into a Python dict the keys come out sorted by name, so the top-level keys of to_dict() / to_json() run body, bodyKey, chineseDate, … rather than in iztro's declaration order. The names and values correspond one for one; only the arrangement differs. For a fixed order, pick out the keys you need yourself.

Edge cases and pitfalls

Do not export with dataclasses.asdict

Astrolabe, Palace and Star are all dataclasses, but palaces and stars each hold a reference back to the chart (_astrolabe / _palace). dataclasses.asdict(chart) follows that back-reference into infinite recursion and ends in RecursionError.

Always export through to_dict() / to_json() — they take the underlying DTO directly, so they neither recurse nor lose fields.

The override tables are not in the export

config echoes only the six switches. The custom mutagen and brightness tables passed in at charting time are input rather than result and do not enter the DTO — matching the field contract of JS iztro. To record which tables were used, keep your ChartConfig on your own call site.

Horoscope carries a pair of methods by the same names and the same shape; see the horoscope object.

On this page