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 | NoneParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
index_or_name | int | str | Yes | — | Four spellings, see the table below |
| Spelling | Example | Meaning |
|---|---|---|
| Index | chart.palace(0) | Palace index 0–11, where 0 is the Yin palace |
| Name key | chart.palace("soulPalace") | One of the twelve palace-name keys, i.e. the value set of PalaceName |
| Palace name in the charting language | chart.palace("soul") | The palace-name text as translated for the chart's language |
| Body palace | chart.palace("bodyPalace") | Whichever palace carries the body-palace flag |
| Palace of origin | chart.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 wealthEdge 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] | NoneParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
star | str | Yes | — | 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 soulPalaceEdge 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 | NoneParameters 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: Trueis_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]) -> boolParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
index_or_name | int | str | Yes | — | Located the same way as in palace |
stars | list[str] | Yes | — | A list of star keys |
Return value
| Method | Meaning |
|---|---|
is_surrounded | Every star in the list is in the surrounded set |
is_surrounded_one_of | At least one star in the list is in the surrounded set |
not_surrounded | None 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
TrueThe 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 | NoneParameters Same as palace; all four spellings are supported.
Return value FlankingPalaces | None, holding two Palace fields. Returns None when the palace
cannot be located.
| Field | Offset | Description |
|---|---|---|
previous | -1 | The preceding palace |
next | +1 | The 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:
| Method | Meaning |
|---|---|
have(stars: list[str]) -> bool | The two palaces together hold every star in the list |
not_have(stars: list[str]) -> bool | Neither palace holds any of them |
have_one_of(stars: list[str]) -> bool | The two together hold at least one |
have_mutagen(mutagen: Mutagen) -> bool | Either palace carries that natal mutagen |
not_have_mutagen(mutagen: Mutagen) -> bool | Neither 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 TrueThe 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,
) -> HoroscopeParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
target_date | str | None | No | None | Target solar date; today when omitted |
target_time_index | int | None | No | None | Target 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 yisito_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,
) -> strParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
knowledge | bool | KnowledgePack | None | no | None | Reading 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 |
config | PatternConfig | None | no | None | Pattern 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) -> strParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
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.