Data model

The type and meaning of every field on Astrolabe, Palace, Star and Horoscope.

For: developers

This page is written against the serialized JSON field names (camelCase), which are the contract shared by all three bindings. Naming translates as follows:

LayerNamingExample
JSON / binding contractcamelCaseisBodyPalace
Pythonsnake_caseis_body_palace
GoPascalCaseIsBodyPalace
Rustsnake_case, values are enumsis_body_palace

Astrolabe

The return value of a charting entry point.

FieldTypeMeaning
genderstringGender, translated text
genderKeystring"male" / "female"
solarDatestringGregorian birthday, echoing the input
lunarDatestringThe lunar birthday written out (always Chinese numerals)
chineseDatestringThe four pillars as a display string
rawDatesRawDatesStructured lunar birthday and four pillars
timestringHour name, e.g. Tiger hour
timeRangestringThe hour's clock range, e.g. 03:00~05:00
signstringZodiac sign
signKeystringZodiac sign key, ariespisces
zodiacstringZodiac animal, from the year branch
zodiacKeystringZodiac animal key, ratpig
earthlyBranchOfSoulPalacestringSoul palace branch
earthlyBranchOfSoulPalaceKeystringSoul palace branch key
earthlyBranchOfBodyPalacestringBody palace branch
earthlyBranchOfBodyPalaceKeystringBody palace branch key
soulstringSoul star
soulKeystringSoul star key
bodystringBody star
bodyKeystringBody star key
fiveElementsClassstringFive Elements class
fiveElementsClassKeystringFive Elements class key, e.g. water2nd
palacesPalace[12]The twelve palaces; index 0 is the Yin palace
timeIndexintHour index of birth, 0–12, keeping the original input value
fixLeapboolWhether leap-month correction is on
languagestringChart language
configConfigThe charting configuration

Palace

FieldTypeMeaning
indexintThe palace's slot on the chart, 0–11; 0 is the Yin palace
namestringPalace name
nameKeystringPalace name key, e.g. soulPalace
isBodyPalaceboolIs it the Body palace
isOriginalPalaceboolIs it the Original palace
heavenlyStemstringPalace stem
heavenlyStemKeystringPalace stem key
earthlyBranchstringPalace branch
earthlyBranchKeystringPalace branch key
majorStarsStar[]Major stars
minorStarsStar[]Minor stars
adjectiveStarsStar[]Adjective stars
changsheng12 / changsheng12KeystringThe twelve Changsheng gods
boshi12 / boshi12KeystringThe twelve Boshi gods
jiangqian12 / jiangqian12KeystringThe twelve Jiang-qian gods
suiqian12 / suiqian12KeystringThe twelve Sui-qian gods
mutagenStarKeysstring[4]Keys of the four stars this palace's stem mutates, in the order Lu, Quan, Ke, Ji; follows a custom mutagen table
decadalDecadalThe decadal this palace governs
agesint[]Nominal ages (虚岁) at which age fortune passes through this palace

Star

FieldTypeMeaning
keystringStar key, e.g. ziweiMaj
namestringStar name
typestringmajor / soft / tough / adjective / flower / helper / lucun / tianma
scopestringorigin / decadal / yearly / monthly / daily / hourly
brightnessstringBrightness as display text. Major and minor stars always have this key, with an empty string where there is no brightness; on adjective and flowing stars the key is absent entirely
brightnessKeystring?Brightness key. Absent when there is no brightness (not an empty string)
mutagenstringMutagen as display text. The 18 mutable stars — the fourteen major stars plus Zuofu, Youbi, Wenchang and Wenqu — always have this key, with an empty string where there is no mutagen; on every other star the key is absent entirely
mutagenKeystring?Mutagen key. Absent when there is no mutagen

Translated fields and key fields default in opposite ways

The translated fields brightness and mutagen have their key present or absent according to the star's category, and when present the value may be an empty string. The key fields brightnessKey and mutagenKey simply omit the key when there is no value.

So "does this star have brightness?" must test whether brightnessKey is present, not whether the brightness key exists — the latter is true for every major and minor star.

Decadal

FieldTypeMeaning
range[int, int]Starting and ending nominal age (虚岁, the East Asian reckoning that starts at 1 on the day of birth), inclusive
heavenlyStem / heavenlyStemKeystringDecadal heavenly stem
earthlyBranch / earthlyBranchKeystringDecadal earthly branch

RawDates

FieldTypeMeaning
lunarDate.lunarYearintLunar year
lunarDate.lunarMonthintLunar month, 1–12
lunarDate.lunarDayintLunar day, 1–30
lunarDate.isLeapboolIs it a leap month
chineseDate.yearly[string, string]Year pillar, [stem, branch]
chineseDate.monthly[string, string]Month pillar
chineseDate.daily[string, string]Day pillar
chineseDate.hourly[string, string]Hour pillar
chineseDate.yearlyKeys[string, string]The year pillar's language-independent keys
chineseDate.monthlyKeys[string, string]The month pillar's keys
chineseDate.dailyKeys[string, string]The day pillar's keys
chineseDate.hourlyKeys[string, string]The hour pillar's keys

The stems and branches inside rawDates.chineseDate are the unlocalized originals — Chinese characters under every chart language — so predicate on *Keys. Hand the *Keys to translate_chinese_date to get a display string translated into any language, character for character identical to the top-level chineseDate field.

Config

FieldAcceptsDefault
yearDividenormal / exactnormal
horoscopeDividenormal / exactnormal
ageDividenormal / birthdaynormal
dayDivideforward / currentforward
algorithmdefault / zhongzhoudefault
astroTypeheaven / earth / humanheaven

There are two further input-only keys, used to replace built-in data tables:

Input keyAccepts
mutagens{stem key: [four star keys]}, in the order Lu, Quan, Ke, Ji; exactly four required
brightness{star key: [twelve brightness keys]}, first item the Yin palace; exactly twelve required, empty string where there is no brightness

These two keys are not echoed in the astrolabe's config. They are charting input rather than part of the chart, and adding them to the DTO would break the field contract with JS iztro. Keep your own copy of the config if you need a record.

For what they mean, see Config in depth.

Horoscope

FieldTypeMeaning
solarDatestringTarget Gregorian date
lunarDatestringTarget lunar date
decadalHoroscopeScopeThe decadal, or the childhood scope before the decadals begin
ageHoroscopeScopeAge fortune, carrying nominalAge
yearlyHoroscopeScopeThe yearly scope, carrying yearlyDecStar
monthlyHoroscopeScopeThe monthly scope
dailyHoroscopeScopeThe daily scope
hourlyHoroscopeScopeThe hourly scope

HoroscopeScope

FieldTypeMeaning
indexintThis scope's slot on the chart, 0–11
namestringScope name, translated text
nameKeystringScope key: decadal / childhood / turn (age fortune) / yearly / monthly / daily / hourly
heavenlyStem / heavenlyStemKeystringThis scope's heavenly stem
earthlyBranch / earthlyBranchKeystringThis scope's earthly branch
palaceNamesstring[12]The palace names re-laid-out with this scope's slot as the Soul palace, in slot order
palaceNameKeysstring[12]The same as keys
mutagenstring[4]Mutagen star names, in the order Lu, Quan, Ke, Ji
mutagenStarKeysstring[4]The same as keys — star keys of the four mutated stars, synonymous with the palace field of the same name; the singular mutagenKey is the mutagen type (sihuaLu etc.)
starsStar[][12]?Flowing stars across the twelve palaces (the outer twelve entries are palaces, each inner list that palace's flowing stars); absent on scopes with no flowing stars
nominalAgeint?Nominal age (虚岁); age fortune only
yearlyDecStarYearlyDecStar?Yearly scope only

Before the decadals begin, nameKey is childhood

When the subject has not yet entered the decadals, the decadal scope's nameKey is childhood rather than decadal — childhood and decadal are different reading semantics. Test nameKey to tell whether the scope is a childhood one; do not compare name translations.

YearlyDecStar

FieldTypeMeaning
suiqian12 / suiqian12Keysstring[12]The twelve Sui-qian gods placed from the yearly branch; the index is the palace slot
jiangqian12 / jiangqian12Keysstring[12]The twelve Jiang-qian gods placed from the yearly branch

The base layer on the Rust side

In Rust the common fields of age and yearly live under .base (AgeItem { base, nominal_age }) and are flattened with #[serde(flatten)] on serialization, so the JSON — and the Python and Go sides — see a flat structure.

A complete JSON example

Real output from by_solar("2000-8-16", 2, female, language="en-US") (top level, with the twelve entries of palaces elided):

{
  "gender": "female",
  "genderKey": "female",
  "solarDate": "2000-8-16",
  "lunarDate": "二〇〇〇年七月十七",
  "chineseDate": "geng chen - jia shen - bing woo - geng yin",
  "rawDates": {
    "lunarDate": { "lunarYear": 2000, "lunarMonth": 7, "lunarDay": 17, "isLeap": false },
    "chineseDate": {
      "yearly": ["庚", "辰"], "monthly": ["甲", "申"],
      "daily": ["丙", "午"], "hourly": ["庚", "寅"],
      "yearlyKeys": ["gengHeavenly", "chenEarthly"],
      "monthlyKeys": ["jiaHeavenly", "shenEarthly"],
      "dailyKeys": ["bingHeavenly", "wuEarthly"],
      "hourlyKeys": ["gengHeavenly", "yinEarthly"]
    }
  },
  "time": "Tiger hour",
  "timeRange": "03:00~05:00",
  "sign": "leo", "signKey": "leo",
  "zodiac": "dragon", "zodiacKey": "dragon",
  "earthlyBranchOfSoulPalace": "woo", "earthlyBranchOfSoulPalaceKey": "wuEarthly",
  "earthlyBranchOfBodyPalace": "xu", "earthlyBranchOfBodyPalaceKey": "xuEarthly",
  "soul": "rebel", "soulKey": "pojunMaj",
  "body": "scholar", "bodyKey": "wenchangMin",
  "fiveElementsClass": "wood 3rd", "fiveElementsClassKey": "wood3rd",
  "palaces": [ /* 12 entries */ ],
  "timeIndex": 2,
  "fixLeap": true,
  "language": "en-US",
  "config": {
    "yearDivide": "normal", "horoscopeDivide": "normal",
    "ageDivide": "normal", "dayDivide": "forward",
    "algorithm": "default", "astroType": "heaven"
  }
}

Two fields stay Chinese in an English chart

lunarDate is rendered with Chinese numerals whatever the language — 二〇〇〇年七月十七 is the 17th day of the 7th lunar month, 2000. And the stems and branches under rawDates.chineseDate are deliberately unlocalized originals; the localized form is the top-level chineseDate, romanized in pinyin for English.

One palace

The Soul palace of the same chart (the palaces entry whose index is 4):

{
  "index": 4,
  "name": "soul", "nameKey": "soulPalace",
  "isBodyPalace": false, "isOriginalPalace": false,
  "heavenlyStem": "ren", "heavenlyStemKey": "renHeavenly",
  "earthlyBranch": "woo", "earthlyBranchKey": "wuEarthly",
  "majorStars": [
    { "key": "ziweiMaj", "name": "emperor", "type": "major", "scope": "origin",
      "brightness": "[+3]", "brightnessKey": "miao", "mutagen": "" }
  ],
  "minorStars": [
    { "key": "wenquMin", "name": "artist", "type": "soft", "scope": "origin",
      "brightness": "[-3]", "brightnessKey": "xian", "mutagen": "" }
  ],
  "adjectiveStars": [
    { "key": "fengge",  "name": "refined",       "type": "adjective", "scope": "origin" },
    { "key": "tianfu",  "name": "lucky",         "type": "adjective", "scope": "origin" },
    { "key": "jielu",   "name": "intercepted",   "type": "adjective", "scope": "origin" },
    { "key": "feilian", "name": "instigated",    "type": "adjective", "scope": "origin" },
    { "key": "nianjie", "name": "considery(Y)",  "type": "helper",    "scope": "origin" }
  ],
  "changsheng12": "weak",       "changsheng12Key": "shuai",
  "boshi12": "dragon",          "boshi12Key": "qinglong",
  "jiangqian12": "disastery",   "jiangqian12Key": "zhaisha",
  "suiqian12": "downcast",      "suiqian12Key": "sangmen",
  "mutagenStarKeys": ["tianliangMaj", "ziweiMaj", "zuofuMin", "wuquMaj"],
  "decadal": {
    "range": [3, 12],
    "heavenlyStem": "ren", "heavenlyStemKey": "renHeavenly",
    "earthlyBranch": "woo", "earthlyBranchKey": "wuEarthly"
  },
  "ages": [5, 17, 29, 41, 53, 65, 77, 89, 101, 113]
}

Both Ziwei and Wenqu carry mutagen: "" — they are mutable stars that happen not to be mutated on this chart, so the key is present with an empty value while mutagenKey is absent entirely. The five adjective stars do not even have a brightness key.

Some en-US names are not English words

considery(Y) and disastery above are the literal en-US renderings of 年解 (nianjie) and 灾煞 (zhaisha), inherited verbatim from iztro's vocabulary. name is display text; never parse it.

What the *Key fields are

Every field that gets translated has a companion field with a Key suffix, valued with iztro's i18n key names and independent of the chart language:

{ "name": "emperor", "key": "ziweiMaj", "brightness": "[+3]", "brightnessKey": "miao" }

The translated field is for people, the key field is for code. The contract is two rules:

  1. Every translated property x has a companion xKey; array-valued ones use the plural Keys (palaceNameKeys, yearlyKeys).
  2. An entity's own identity is simply key — a star's key field is key, not nameKey.

The one naming fork is around mutagens: the singular mutagenKey is the mutagen type (sihuaLu etc.), while the plural mutagenStarKeys holds the star keys of the four mutated stars. The semantic_contract test enforces this contract — every translated field in the DTO must have its key companion.

The *Key / key family, genderKey, timeIndex, fixLeap, language and config are x-iztro's extensions over JS iztro; every other field matches iztro's JSON.stringify output key by key and value by value, held by the binding contract test. Keep predicate logic on the key fields — see The key contract.

Exporting JSON

The Python side has export methods that emit exactly the contract above:

chart.to_dict()          # dict
chart.to_json(indent=2)  # str

Do not use dataclasses.asdict

Astrolabe, Palace and Star hold back-references to each other (a palace holds the astrolabe it belongs to), so asdict() recurses into them until RecursionError. Use to_json() for JSON.

On this page