Patterns

Pattern hits on natal and horoscope charts, the PatternConfig readings, the PatternKey enum, and the predicate methods.

A pattern (格局) is the recognition of a named star arrangement on a chart. The same 64 rules are judged on natal charts and on horoscope views. For what patterns are and each rule's condition and source, see the concept page.

from x_iztro import Astro

chart = Astro().by_solar("1985-5-3", 9, "male", language="en-US")
hits = chart.patterns()

The examples on this page all start from an en-US natal chart, so the display values in the output are the English translations.

Types

PatternHit, PatternStar and PatternConfig are frozen dataclasses; PatternKey and BrightnessSource are StrEnums. All of them are exported from the x_iztro top level.

PatternHit

FieldTypeMeaning
keystrLanguage-independent pattern key; its value domain is PatternKey
namestrPattern name, translated to the chart's language
scopestrThe view it was judged in: "origin" for natal, otherwise that level
palace_indexintSlot of the palace where the pattern formed (0-11, Yin palace is 0)
palace_namestrThat palace's name in this view
palace_name_keystrThe palace key; its value domain is PalaceName
variantstr | NoneWhich reading matched; None for single-reading patterns
brokenboolWhether the "spoiled by malefics" condition fired. The hit is reported either way; this is only a flag
starslist[PatternStar]The stars evidencing the pattern, with their palaces

Three methods:

MethodMeaning
is_(key)Whether this is the given pattern; takes a PatternKey or a string
in_palace(name)Whether the forming palace is the given palace in this view; takes a PalaceName, a palace key, or the name in the chart's language
to_dict()The raw DTO the binding returned (camelCase keys)

PatternStar

FieldTypeMeaning
keystrLanguage-independent star key
namestrStar name, translated to the chart's language
palace_indexintThe slot the star actually occupies (when borrowed, not the borrowing palace)
brightness / brightness_keystr | NoneBrightness display text and key; None when the star has none
mutagen / mutagen_keystr | NoneThe mutagen in this view, and its key; None when there is none

Two methods, has_brightness(b) and has_mutagen(m), both comparing keys and therefore independent of the chart's language.

PatternConfig

The reading switches. Anything that is merely a second form of the same pattern goes through PatternHit.variant; only data readings that change the finding of fact itself live here, which is why there are just three fields.

from x_iztro import PatternConfig, BrightnessSource

PatternConfig(
    brightness_source=BrightnessSource.TABLE,  # default
    borrow=True,                               # default
    flow_stars=True,                           # default
)
FieldDefaultEffect
brightness_sourceBrightnessSource.TABLEBasis for Sun and Moon brightness
borrowTrueWhether an empty palace borrows the opposite palace's majors
flow_starsTrueWhether flowing stars count as their natal counterparts in horoscope views

BrightnessSource has two members: TABLE follows the chart's brightness table (Miao and Wang bright, Xian and Bu dim — matching iztro value for value), POSITIONAL follows the traditional placement (Sun bright Yin–Wu, dim You–Chou; Moon bright You–Chou, dim Mao–Wei). The trade-off is explained on the concept page.

PatternConfig is a frozen dataclass; change one field with dataclasses.replace, or just build a new one — all three fields have defaults, so name only the one you are changing.

PatternKey

The language-independent key of each of the 64 patterns, as a StrEnum, so it compares directly against PatternHit.key:

from x_iztro import PatternKey

print(len(list(PatternKey)))
print(PatternKey.SHA_PO_LANG)
print(PatternKey("sha_po_lang").name)
64
sha_po_lang
SHA_PO_LANG

patterns

Purpose Every pattern hit on the natal chart.

In Zi Wei terms Lists every named star arrangement that holds on this chart, together with the palace it formed in and the stars that evidence it.

Signature

def patterns(self, config: PatternConfig | None = None) -> list[PatternHit]

Parameters

ParameterTypeRequiredDefaultMeaning
configPatternConfig | NonenoNoneThe reading; omit for the default

Returns list[PatternHit] in the source page's entry order; an empty list when nothing holds. The two transit patterns (禄衰马困 lu_shuai_ma_kun, 风云际会 feng_yun_ji_hui) never appear on a natal chart.

Example

chart = Astro().by_solar("1985-5-3", 9, "male", language="en-US")

for hit in chart.patterns():
    print(f"{hit.name} {hit.palace_index} {hit.palace_name} broken={hit.broken}")

Output

General and Wolf Together 11 surface broken=False
Empress and Minister Facing the Palace 5 soul broken=False
Marshal, Rebel and Wolf 11 surface broken=False
Money and Horse Galloping Together 5 soul broken=False
Officer and Helper Flanking Life 5 soul broken=False
Literary Nobility and Brilliance 11 surface broken=False
Literary Stars Facing Life 5 soul broken=True
Literary Stars in Hidden Support 5 soul broken=False
Literary Stars in Hidden Support 5 soul broken=False

Taking one hit and reading its evidence:

from x_iztro import PatternKey, PalaceName

hit = next(h for h in chart.patterns() if h.is_(PatternKey.FU_XIANG_CHAO_YUAN))

print(hit.name, hit.variant, hit.in_palace(PalaceName.SOUL))
for s in hit.stars:
    print(" ", s.name, s.palace_index, s.brightness, s.brightness_key)
Empress and Minister Facing the Palace soul_empty True
  empress 9 [+1] de
  minister 1 [-3] xian

Judging under an explicit reading:

from x_iztro import PatternConfig, BrightnessSource

chart = Astro().by_solar("1985-1-5", 11, "female", language="en-US")

print([h.name for h in chart.patterns()])
print([h.name for h in chart.patterns(
    PatternConfig(brightness_source=BrightnessSource.POSITIONAL))])
['Money and Horse Galloping Together', 'Officer and Helper Flanking Life', 'Sitting on and Facing Nobility']
['Sun and Moon Both Bright', 'Money and Horse Galloping Together', 'Officer and Helper Flanking Life', 'Sitting on and Facing Nobility']

Edges and traps


Horoscope.patterns

Purpose Pattern hits in the view of one horoscope level.

In Zi Wei terms Takes that level's palace as the Soul palace, merges in that level's flowing stars and mutagens, and runs every rule again. This is how "if the natal chart has the arrangement and the decadal then arrives at it, its benefit is enjoyed" is computed.

Signature

def patterns(
    self,
    scope: Scope | ScopeLiteral,
    config: PatternConfig | None = None,
    astrolabe: Astrolabe | None = None,
) -> list[PatternHit]

Parameters

ParameterTypeRequiredDefaultMeaning
scopeScope | stryesThe level whose view to judge in
configPatternConfig | NonenoNoneThe reading
astrolabeAstrolabe | NonenoNoneThe chart; omitted, it uses the one this horoscope came from

Returns list[PatternHit], each carrying the level passed in as its scope. Passing Scope.ORIGIN gives exactly what patterns() on the astrolabe gives.

Raises ValueError — when no astrolabe was passed and this horoscope did not come from a chart. A horoscope obtained through chart.horoscope(...) never hits this.

Example

from x_iztro import Scope

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

for hit in h.patterns(Scope.DECADAL):
    print(hit.name, hit.scope, hit.variant)

print(h.patterns("origin") == chart.patterns())

Output

Marshal, Rebel and Wolf decadal None
Meeting of Wind and Cloud decadal None
Meeting of Wind and Cloud decadal yearly
True

The natal view of that same chart holds only "Empress and Minister Facing the Palace" — the Marshal-Rebel-Wolf pattern holds at this level only because the decadal moved the Soul palace.

Edges and traps


to_dict

Purpose The raw DTO the binding returned: camelCase keys, values translated into the chart's language, alongside the language-independent keys. Use it to store hits verbatim, ship them to a frontend, or feed them to a model.

Signature

def to_dict(self) -> dict[str, Any]

Example

import json

chart = Astro().by_solar("1985-5-3", 9, "male", language="en-US")
hit = next(h for h in chart.patterns() if h.is_(PatternKey.FU_XIANG_CHAO_YUAN))
print(json.dumps(hit.to_dict(), ensure_ascii=False, indent=2))

Output

{
  "broken": false,
  "key": "fu_xiang_chao_yuan",
  "name": "Empress and Minister Facing the Palace",
  "palaceIndex": 5,
  "palaceName": "soul",
  "palaceNameKey": "soulPalace",
  "scope": "origin",
  "stars": [
    {
      "brightness": "[+1]",
      "brightnessKey": "de",
      "key": "tianfuMaj",
      "name": "empress",
      "palaceIndex": 9
    },
    {
      "brightness": "[-3]",
      "brightnessKey": "xian",
      "key": "tianxiangMaj",
      "name": "minister",
      "palaceIndex": 1
    }
  ],
  "variant": "soul_empty"
}

Optional keys with no value (variant, brightness, mutagen) are omitted from the DTO entirely; they never come back as null.


patterns_to_text

Purpose The pattern hits as semantic text, one per line: pattern name, landing palace, forming stars, with broken patterns marked [Broken].

Signature

def patterns_to_text(self, config: PatternConfig | None = None) -> str          # Astrolabe
def patterns_to_text(self, scope, config=None, astrolabe=None) -> str           # Horoscope

The same judgment as patterns (including re-anchoring context and judging criteria); the horoscope version writes palace names as re-laid out at that scope.

Example

print(chart.patterns_to_text(), end="")

Output

- Empress and Minister Facing the Palace(soul): empress([+3]), minister([+3])

The chart's and the horoscope's to_text each already carry a patterns section; the standalone call suits cases that want only the pattern summary.

On this page